|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import sys |
|
|
import re |
|
|
|
|
|
def scan_exclusions(srcpath): |
|
|
"""Return a set of line numbers to exclude for this source file.""" |
|
|
with open(srcpath, "r", encoding="utf-8") as fh: |
|
|
text = fh.readlines() |
|
|
excl = set() |
|
|
in_block = False |
|
|
for i, line in enumerate(text, start=1): |
|
|
if "LCOV_EXCL_LINE" in line: |
|
|
excl.add(i) |
|
|
if "LCOV_EXCL_START" in line: |
|
|
in_block = True |
|
|
excl.add(i) |
|
|
continue |
|
|
if "LCOV_EXCL_STOP" in line: |
|
|
excl.add(i) |
|
|
in_block = False |
|
|
continue |
|
|
if in_block: |
|
|
excl.add(i) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if re.match(r'^\s*#(if|endif|else|elif|ifdef|ifndef)\b', line): |
|
|
excl.add(i) |
|
|
|
|
|
|
|
|
|
|
|
if re.match(r'^\s*}\s*(/\*.*?\*/)?$', line): |
|
|
excl.add(i) |
|
|
return excl |
|
|
|
|
|
DA_RE = re.compile(r'^\s*DA:(\d+),(\d+)(,.*)?\s*$') |
|
|
LF_RE = re.compile(r'^\s*LF:(\d+)\s*$') |
|
|
LH_RE = re.compile(r'^\s*LH:(\d+)\s*$') |
|
|
BRDA_RE = re.compile(r'^\s*BRDA:(\d+),([e\d]+),(.*),([-\d]+)\s*$') |
|
|
BRF_RE = re.compile(r'^\s*BRF:(\d+)\s*$') |
|
|
BRH_RE = re.compile(r'^\s*BRH:(\d+)\s*$') |
|
|
FN_RE = re.compile(r'^\s*FN:(\d+),([^,\s]*)\s*$') |
|
|
FNDA_RE = re.compile(r'^\s*FNDA:(\d+),([^,\s]*)\s*$') |
|
|
FNF_RE = re.compile(r'^\s*FNF:(\d+)\s*$') |
|
|
FNH_RE = re.compile(r'^\s*FNH:(\d+)\s*$') |
|
|
|
|
|
def process_block(block_lines): |
|
|
"""Return processed block lines with excluded DA/BRDA removed and LF/LH fixed.""" |
|
|
if not block_lines: |
|
|
return block_lines |
|
|
|
|
|
first = block_lines[0] |
|
|
assert first.lstrip().startswith('SF:') |
|
|
sf_path = first.split(':', 1)[1].strip() |
|
|
exclusions = scan_exclusions(sf_path) |
|
|
|
|
|
new_lines = [] |
|
|
da_orig_found = 0 |
|
|
da_orig_hit = 0 |
|
|
da_new_found = 0 |
|
|
da_new_hit = 0 |
|
|
brda_orig_found = 0 |
|
|
brda_orig_hit = 0 |
|
|
brda_new_found = 0 |
|
|
brda_new_hit = 0 |
|
|
fnda_orig_found = 0 |
|
|
fnda_orig_hit = 0 |
|
|
fnda_new_found = 0 |
|
|
fnda_new_hit = 0 |
|
|
|
|
|
fn_exclusions = set() |
|
|
|
|
|
|
|
|
for line in block_lines: |
|
|
m_fn = FN_RE.match(line) |
|
|
assert (m_fn is not None) == line.lstrip().startswith('FN:') |
|
|
if m_fn: |
|
|
fn_line = int(m_fn.group(1)) |
|
|
fn_name = m_fn.group(2) |
|
|
if fn_line in exclusions: |
|
|
fn_exclusions.add(fn_name) |
|
|
|
|
|
|
|
|
for line in block_lines: |
|
|
m_da = DA_RE.match(line) |
|
|
assert (m_da is not None) == line.lstrip().startswith('DA:') |
|
|
if m_da: |
|
|
line_num = int(m_da.group(1)) |
|
|
execution_count = int(m_da.group(2)) |
|
|
da_orig_found += 1 |
|
|
if execution_count > 0: |
|
|
da_orig_hit += 1 |
|
|
if line_num in exclusions: |
|
|
|
|
|
continue |
|
|
da_new_found += 1 |
|
|
if execution_count > 0: |
|
|
da_new_hit += 1 |
|
|
new_lines.append(line) |
|
|
continue |
|
|
m_brda = BRDA_RE.match(line) |
|
|
assert (m_brda is not None) == line.lstrip().startswith('BRDA:') |
|
|
if m_brda: |
|
|
brda_orig_found += 1 |
|
|
taken = m_brda.group(4) |
|
|
if taken != '-' and int(taken) > 0: |
|
|
brda_orig_hit += 1 |
|
|
if int(m_brda.group(1)) in exclusions: |
|
|
|
|
|
continue |
|
|
brda_new_found += 1 |
|
|
if taken != '-' and int(taken) > 0: |
|
|
brda_new_hit += 1 |
|
|
new_lines.append(line) |
|
|
continue |
|
|
m_fnda = FNDA_RE.match(line) |
|
|
assert (m_fnda is not None) == line.lstrip().startswith('FNDA:') |
|
|
if m_fnda: |
|
|
fnda_orig_found += 1 |
|
|
fn_name = m_fnda.group(2) |
|
|
count = int(m_fnda.group(1)) |
|
|
if count > 0: |
|
|
fnda_orig_hit += 1 |
|
|
if fn_name in fn_exclusions: |
|
|
|
|
|
continue |
|
|
fnda_new_found += 1 |
|
|
if count > 0: |
|
|
fnda_new_hit += 1 |
|
|
new_lines.append(line) |
|
|
continue |
|
|
m_fn = FN_RE.match(line) |
|
|
assert (m_fn is not None) == line.lstrip().startswith('FN:') |
|
|
if m_fn: |
|
|
fn_line = int(m_fn.group(1)) |
|
|
fn_name = m_fn.group(2) |
|
|
if fn_name in fn_exclusions: |
|
|
|
|
|
continue |
|
|
new_lines.append(line) |
|
|
continue |
|
|
|
|
|
new_lines.append(line) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for i, line in enumerate(new_lines): |
|
|
|
|
|
m_lf = LF_RE.match(line) |
|
|
assert (m_lf is not None) == line.lstrip().startswith('LF:') |
|
|
if m_lf: |
|
|
|
|
|
leading = re.match(r'^(\s*)', line).group(1) |
|
|
|
|
|
new_lines[i] = f"{leading}LF:{da_new_found}\n" |
|
|
|
|
|
try: |
|
|
lf_orig = int(m_lf.group(1)) |
|
|
if lf_orig != da_orig_found: |
|
|
print(f"warning: original LF ({lf_orig}) != counted DA entries ({da_orig_found}) for {sf_path}", file=sys.stderr) |
|
|
except Exception: |
|
|
pass |
|
|
continue |
|
|
|
|
|
|
|
|
m_lh = LH_RE.match(line) |
|
|
assert (m_lh is not None) == line.lstrip().startswith('LH:') |
|
|
if m_lh: |
|
|
leading = re.match(r'^(\s*)', line).group(1) |
|
|
new_lines[i] = f"{leading}LH:{da_new_hit}\n" |
|
|
try: |
|
|
lh_orig = int(m_lh.group(1)) |
|
|
if lh_orig != da_orig_hit: |
|
|
print(f"warning: original LH ({lh_orig}) != counted DA hits ({da_orig_hit}) for {sf_path}", file=sys.stderr) |
|
|
except Exception: |
|
|
pass |
|
|
continue |
|
|
|
|
|
|
|
|
m_brf = BRF_RE.match(line) |
|
|
assert (m_brf is not None) == line.lstrip().startswith('BRF:') |
|
|
if m_brf: |
|
|
leading = re.match(r'^(\s*)', line).group(1) |
|
|
|
|
|
new_lines[i] = f"{leading}BRF:{brda_new_found}\n" |
|
|
try: |
|
|
brf_orig = int(m_brf.group(1)) |
|
|
if brf_orig != brda_orig_found: |
|
|
print(f"warning: original BRF ({brf_orig}) != counted BRDA entries ({brda_orig_found}) for {sf_path}", file=sys.stderr) |
|
|
except Exception: |
|
|
pass |
|
|
continue |
|
|
|
|
|
|
|
|
m_brh = BRH_RE.match(line) |
|
|
assert (m_brh is not None) == line.lstrip().startswith('BRH:') |
|
|
if m_brh: |
|
|
leading = re.match(r'^(\s*)', line).group(1) |
|
|
new_lines[i] = f"{leading}BRH:{brda_new_hit}\n" |
|
|
try: |
|
|
brh_orig = int(m_brh.group(1)) |
|
|
if brh_orig != brda_orig_hit: |
|
|
print(f"warning: original BRH ({brh_orig}) != counted BRDA hits ({brda_orig_hit}) for {sf_path}", file=sys.stderr) |
|
|
except Exception: |
|
|
pass |
|
|
continue |
|
|
|
|
|
|
|
|
m_fnf = FNF_RE.match(line) |
|
|
assert (m_fnf is not None) == line.lstrip().startswith('FNF:') |
|
|
if m_fnf: |
|
|
leading = re.match(r'^(\s*)', line).group(1) |
|
|
new_lines[i] = f"{leading}FNF:{fnda_new_found}\n" |
|
|
try: |
|
|
fnf_orig = int(m_fnf.group(1)) |
|
|
if fnf_orig != fnda_orig_found: |
|
|
print(f"warning: original FNF ({fnf_orig}) != counted FNDA entries ({fnda_orig_found}) for {sf_path}", file=sys.stderr) |
|
|
except Exception: |
|
|
pass |
|
|
continue |
|
|
|
|
|
|
|
|
m_fnh = FNH_RE.match(line) |
|
|
assert (m_fnh is not None) == line.lstrip().startswith('FNH:') |
|
|
if m_fnh: |
|
|
leading = re.match(r'^(\s*)', line).group(1) |
|
|
new_lines[i] = f"{leading}FNH:{fnda_new_hit}\n" |
|
|
try: |
|
|
fnh_orig = int(m_fnh.group(1)) |
|
|
if fnh_orig != fnda_orig_hit: |
|
|
print(f"warning: original FNH ({fnh_orig}) != counted FNDA hits ({fnda_orig_hit}) for {sf_path}", file=sys.stderr) |
|
|
except Exception: |
|
|
pass |
|
|
continue |
|
|
|
|
|
return new_lines |
|
|
|
|
|
def filter_lcov(in_fh, out_fh): |
|
|
lines = in_fh.readlines() |
|
|
|
|
|
i = 0 |
|
|
out_lines = [] |
|
|
while i < len(lines): |
|
|
line = lines[i] |
|
|
if line.lstrip().startswith('SF:'): |
|
|
|
|
|
block = [] |
|
|
while i < len(lines): |
|
|
block.append(lines[i]) |
|
|
if lines[i].strip() == 'end_of_record': |
|
|
i += 1 |
|
|
break |
|
|
i += 1 |
|
|
processed = process_block(block) |
|
|
out_lines.extend(processed) |
|
|
else: |
|
|
out_lines.append(line) |
|
|
i += 1 |
|
|
|
|
|
out_fh.writelines(out_lines) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
if len(sys.argv) > 3: |
|
|
print("Usage: python3 FilterCoverage.py [infile [outfile]]", file=sys.stderr) |
|
|
sys.exit(1) |
|
|
if len(sys.argv) > 2: |
|
|
with open(sys.argv[2], "w", encoding="utf-8") as out_fh: |
|
|
with open(sys.argv[1], "r", encoding="utf-8") as in_fh: |
|
|
filter_lcov(in_fh, out_fh) |
|
|
elif len(sys.argv) > 1: |
|
|
with open(sys.argv[1], "r", encoding="utf-8") as fh: |
|
|
filter_lcov(fh, sys.stdout) |
|
|
else: |
|
|
filter_lcov(sys.stdin, sys.stdout) |
|
|
|