diff --git a/dotrace_decode.py b/dotrace_decode.py index 2916bbd..6c35fc3 100755 --- a/dotrace_decode.py +++ b/dotrace_decode.py @@ -1,11 +1,31 @@ #! /usr/bin/env python3 +""" A python script used to decode the addresses in traces generated by +the dotrace instrumentation. +Requires addr2line to be installed. The script works by collecting a +given number of address from the trace file and then calling addr2line +with such a block of addresses. +""" + +import os import subprocess import collections +import sys from itertools import chain import argparse def decode(bin, traces): + """Decodes a list of address using addr2line. + + Args: + bin: The executable used for retrieving the debug symbols. + traces: A list of lists of the form [c, a, b] where a and + b are addresses as string. + + Returns: + Generates lists [c, call targed name, call site name, + call site source location] + """ if len(traces) == 0: return [] addrs = chain.from_iterable([x[1:] for x in traces]) @@ -20,6 +40,15 @@ def decode(bin, traces): yield [line[0],target_name,site_name,site_line] def processBlock(args, traces, out_file): + """Decodes a block of trace lines. + + Args: + args: The program arguments. + traces: A list of lists of the form [c, a, b] where a and + b are addresses as string and c is eiter 'e' or 'x' + indicating if the line is a function entry or exit. + out_file: The output file to write into. In binary mode. + """ decoded = decode(args.binary, traces) for decoded_line in decoded: if decoded_line[0] == 'e': @@ -37,6 +66,7 @@ def processBlock(args, traces, out_file): out_file.write(decoded_line[1]) out_file.write(b"\n") +# Parser configuration parser = argparse.ArgumentParser(description='Decode a trace file generated by dotrace.') parser.add_argument('-b', '--binary', help='The binary file.', required=True, metavar="FILE") @@ -54,12 +84,29 @@ parser.add_argument('-s', '--block-size', type=int, default=10000, if __name__ == "__main__": args = parser.parse_args() + # Check if addr2line is installed + DEVNULL = open(os.devnull, 'w') + try: + subprocess.call(["addr2line", "--version"], + stdout=DEVNULL, stderr=subprocess.STDOUT) + except FileNotFoundError as e: + print("Error: addr2line not installed.", file=sys.stderr) + sys.exit(1) + + # Check if the files referenced by the options exist + for f in [args.binary, args.trace]: + if not os.path.exists(f): + print("Error: File {} does not exist.".format(f) ,file=sys.stderr) + sys.exit(1) + lines_to_read = args.block_size / 2 accumulator = [] i = 0 total = 1 with open(args.trace, 'r') as trace_file: with open(args.output, 'bw') as out_file: + # read lines_to_read lines from the file, each line constains two + # addresses for line in trace_file: i = i + 1 accumulator.append(line.split())