From c718181cdac4196288d034d737a8ed545ddf2c20 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Mon, 2 Mar 2020 20:04:41 +0100 Subject: [PATCH] Add runtime trace debuggers Since the runtime tracing prints out object addresses, it's possible to track which objects are which and propagate that information through applications. Since creating lambdas prints the function pointer address, it's possible to resolve the symbols and print all of the applications as symbolic. The trace tool runs the output under lldb since lldb disables ASLR, which makes resolving function addresses much more straightforward. For an example, run: cargo run --release ivy-examples/fibonacci.vy python3 tools/trace.py --debug-only a.out And it will print out the numbers instead of an opaque debug representation. --- .gitignore | 1 + tools/format_trace.py | 80 +++++++++++++++++++++++++++++++++++++++++++ tools/trace.py | 37 ++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 tools/format_trace.py create mode 100644 tools/trace.py diff --git a/.gitignore b/.gitignore index e0acd43..fac2830 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ a.out *.s !rt/*.s +*.pyc diff --git a/tools/format_trace.py b/tools/format_trace.py new file mode 100644 index 0000000..a9732d2 --- /dev/null +++ b/tools/format_trace.py @@ -0,0 +1,80 @@ +import argparse +import sys +import subprocess + + +def get_syms(program): + result = subprocess.run( + ["nm", "-defined-only", "-demangle", program], + check=True, + capture_output=True, + encoding="utf8", + ) + syms = {} + for line in result.stdout.split("\n"): + if not line.strip(): + continue + addr, _, name = line.split(' ', 2) + if name.startswith("ivy_builtin$"): + name = name.split("$", 2)[1] + syms[addr] = name + return syms + + +def handle_trace(lines, syms={}, only_debug=False): + objs = {} + def obj(x): + if int(x, 16) % 2 == 1: + return int(x, 16) >> 1 + if x in objs: + return objs[x] + elif x in syms: + return syms[x] + return x + + for line in lines: + kind, *args = line.split() + if kind == "MAKE": + objs[args[0]] = syms.get(args[1], f"fn@{args[1]}") + if not only_debug: + print("MAKE", objs[args[0]]) + elif kind == "COPY": + objs[args[1]] = obj(args[0]) + elif kind == "APP": + if int(args[1], 16) == 0: + objs[args[0]] = f"({obj(args[0])})" + else: + objs[args[0]] = f"({obj(args[0])} {obj(args[1])})" + if not only_debug: + print("APP", objs[args[0]]) + elif kind == "DEBUG": + if only_debug: + print(obj(args[0])) + else: + print("DEBUG", obj(args[0])) + elif kind == "UPD8": + pass # who cares :P + else: + if not only_debug: + print(kind, *(obj(x) for x in args)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Make an Ivy runtime trace more readable", + ) + parser.add_argument("program", help="The program that produced the trace") + parser.add_argument("trace", help="A file containing the trace") + parser.add_argument( + "-d", "--only-debug", action="store_true", + help="If set, only print the DEBUG outputs", + ) + if len(sys.argv) == 1: + parser.print_help() + sys.exit(0) + args = parser.parse_args() + + syms = get_syms(args.program) + with open(args.trace, "r") as f: + lines = [line.strip() for line in f if line.strip()] + handle_trace(lines, syms, ags.only_debug) diff --git a/tools/trace.py b/tools/trace.py new file mode 100644 index 0000000..9e1987e --- /dev/null +++ b/tools/trace.py @@ -0,0 +1,37 @@ +import argparse +import format_trace +import subprocess +import sys + + +def get_trace(command): + result = subprocess.run( + ["lldb", "-b", "-o", "run", command], + env={"IVY_RT_TRACE": "1"}, + capture_output=True, + encoding="utf8", + ) + lines = result.stdout.split("\n") + return lines[3:-4] + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description="Run an Ivy executable and format its runtime trace.", + ) + parser.add_argument( + "command", + help="The command to execute.", + ) + parser.add_argument( + "-d", "--only-debug", action="store_true", + help="If set, only print the DEBUG outputs", + ) + if len(sys.argv) <= 1: + parser.print_help() + sys.exit(0) + args = parser.parse_args() + + syms = format_trace.get_syms(args.command) + lines = get_trace(args.command) + format_trace.handle_trace(lines, syms, args.only_debug) -- 2.43.2