import collections
import os
import re
import shutil
import subprocess
import sys
PANIC = re.compile(
r"core(::|\d+)(panicking|str\d*slice_error_fail|slice\d+index|"
r"option\d+(unwrap|expect)_failed|result\d+unwrap_failed)"
)
SRC = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "src") + os.sep
def function_name(symbol):
symbol = re.sub(r"17h[0-9a-f]{16}E$", "", symbol) for m in re.finditer(r"\d+", symbol):
rest = symbol[m.end():]
for start in range(m.start(), m.end()):
if int(symbol[start:m.end()]) == len(rest) and rest:
return rest
return symbol
def binutils_prefix():
candidates = [os.environ["DISC_V_BINUTILS_PREFIX"]] if "DISC_V_BINUTILS_PREFIX" in os.environ \
else ["riscv64-elf-", "riscv64-unknown-elf-", "riscv64-linux-gnu-"]
for prefix in candidates:
if shutil.which(prefix + "objdump") and shutil.which(prefix + "addr2line"):
return prefix
sys.exit("RISC-V binutils not found")
def main():
elf = sys.argv[1]
prefix = binutils_prefix()
dis = subprocess.run([prefix + "objdump", "-d", "--no-show-raw-insn", elf],
capture_output=True, text=True, check=True).stdout
function, sites = None, []
for line in dis.splitlines():
m = re.match(r"[0-9a-f]+ <(.*)>:", line)
if m:
function = m.group(1)
continue
m = re.match(r"\s*([0-9a-f]+):\s.*# [0-9a-f]+ <(.*)>", line)
if m and PANIC.search(m.group(2)) and not PANIC.search(function or ""):
sites.append((m.group(1), m.group(2)))
if not sites:
print("no panics")
return
lines = subprocess.run([prefix + "addr2line", "-e", elf, "-a", "-i", "-f", "-C"]
+ [a for a, _ in sites], capture_output=True, text=True,
check=True).stdout.splitlines()
frames, current = {}, None
for line in lines:
if re.fullmatch(r"0x[0-9a-f]+", line):
current = int(line, 16)
frames[current] = []
else:
frames[current].append(line)
counts = collections.Counter()
for address, target in sites:
stack = frames[int(address, 16)]
where = "outside disc-v: " + (stack[-2] if stack else "?")
for function, location in zip(stack[0::2], stack[1::2]):
location = re.sub(r" \(discriminator \d+\)", "", location)
if location.startswith(SRC):
where = location[len(SRC) - len("src/"):]
break
if re.match(r"<?disc_v::", function):
where = function
break
kind = function_name(target)
counts[(where, kind)] += 1
for (where, kind), n in sorted(counts.items()):
print(f"{n:3} {where} ({kind})")
print(f"{sum(counts.values())} calls to panic functions")
sys.exit(1)
if __name__ == "__main__":
main()