| #!/usr/bin/env python3 |
| # Copyright Microsoft and CHERIoT Contributors. |
| # SPDX-License-Identifier: MIT |
| |
| import optparse, re, bisect, sys, subprocess |
| |
| nm_re=re.compile('([0-9a-f]+) . (.+)') |
| trace_re=re.compile('(?P<pc>[0-9a-fA-F]{8})') |
| |
| def usage(msg): |
| sys.stderr.write(msg + "\n") |
| sys.exit(1) |
| |
| def get_names(options): |
| if options.exe_file: |
| p = subprocess.Popen(['nm','--demangle', options.exe_file],stdout=subprocess.PIPE,text=True) |
| nm_file = p.stdout |
| elif options.nm_file: |
| nm_file = open(options.nm_file, 'r') |
| else: |
| usage("Error: please provide either exe file or nm output.") |
| print("Parsing nm file...") |
| names=[] |
| for line in nm_file: |
| # line=str(line) |
| m=nm_re.match(line) |
| if not m: |
| sys.stderr.write(f"Warning: unrecognized line in nm file: {line}") |
| continue |
| names.append((int(m.group(1), 16), m.group(2))) |
| # Sort by address |
| names.sort(key=lambda x: x[0]) |
| # Got the data, now merge duplicate symbols into one. |
| print("Removing duplicate symbols...") |
| unique_names=[] |
| prev_addr, prev_name=0, 'null' |
| for (addr, name) in names: |
| if addr != prev_addr: |
| unique_names.append((prev_addr, prev_name)) |
| prev_name = name |
| prev_addr = addr |
| else: |
| prev_name = prev_name + '/' + name |
| unique_names.append((prev_addr, prev_name)) |
| print("Loaded names.") |
| return unique_names |
| |
| def annotate_trace(options): |
| names = get_names(options) |
| non_local_names = filter(lambda x: not x[1].startswith('.'), names) |
| (addresses, syms) = list(zip(*names)) |
| (non_local_addresses, non_local_syms) = list(zip(*non_local_names)) |
| if options.trace_file is None: |
| trace_file=sys.stdin |
| else: |
| trace_file=open(options.trace_file, 'r') |
| for line in trace_file: |
| m = trace_re.search(line) |
| if m: |
| addr=int(m.group('pc'), 16) |
| i = bisect.bisect_right(addresses, addr)-1 |
| sym = syms[i] |
| i2 = bisect.bisect_right(non_local_addresses, addr) - 1 |
| non_local_sym = non_local_syms[i2] |
| if non_local_sym != sym: |
| sym = f"{non_local_sym} / {sym}" |
| print(f"{line[:-1]:{options.width}s} | {sym}") |
| else: |
| print(line[:-1]) |
| |
| if __name__=="__main__": |
| parser = optparse.OptionParser("""%prog --exe <EXE> --trace <TRACE> |
| Simple script to annotate an instruction trace with function names. |
| Assumes the first 8 digit hex number in each input line is the program counter.""") |
| parser.add_option('-e','--exe', dest="exe_file", help="Elf file containing symbol names") |
| parser.add_option('-N','--nm', dest="nm_file", help="File containing output of nm (alternative to elf file)") |
| parser.add_option('-t','--trace', dest="trace_file", help="Trace file to annotate (default stdin)", default=None) |
| parser.add_option('-w','--width', help="Width to pad input lines to before annotating", type=int, default=70) |
| (opts, args) = parser.parse_args() |
| annotate_trace(opts) |