| #!/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) |