diff --git a/scripts/loader.py b/scripts/loader.py index 9b25a2cf27151b90d817d13fcae45ba89569c15e..e56656a48b6d51de4523b31958664862f94c713c 100644 --- a/scripts/loader.py +++ b/scripts/loader.py @@ -21,7 +21,7 @@ external = os.path.join(osv_dir, 'external', arch) sys.path.append(os.path.join(osv_dir, 'scripts')) from osv.trace import Trace,TracePoint,BacktraceFormatter,format_time,format_duration -from osv import trace +from osv import trace, debug virtio_driver_type = gdb.lookup_type('virtio::virtio_driver') @@ -73,38 +73,49 @@ def load_elf(path, base): gdb.execute('add-symbol-file %s %s %s' % (path, text_addr, args)) -class syminfo(object): +class syminfo_resolver(object): cache = dict() - def __init__(self, addr): - if addr in syminfo.cache: - cobj = syminfo.cache[addr] - self.func = cobj.func - self.source = cobj.source - return + + def __call__(self, addr): + if addr in syminfo_resolver.cache: + return self.cache[addr] infosym = gdb.execute('info symbol 0x%x' % addr, False, True) - self.func = infosym[:infosym.find(" + ")] + func = infosym[:infosym.find(" + ")] sal = gdb.find_pc_line(addr) try : # prefer (filename:line), - self.source = '%s:%s' % (sal.symtab.filename, sal.line) + filename = sal.symtab.filename + line = sal.line except : # but if can't get it, at least give the name of the object - if infosym.startswith("No symbol matches") : - self.source = None - else: - self.source = infosym[infosym.rfind("/")+1:].rstrip() - if self.source and self.source.startswith('../../'): - self.source = self.source[6:] - syminfo.cache[addr] = self - def __str__(self): - ret = self.func - if self.source: - ret += ' (%s)' % (self.source,) - return ret + if not infosym.startswith("No symbol matches") : + filename = infosym[infosym.rfind("/")+1:].rstrip() + + if filename and filename.startswith('../../'): + filename = filename[6:] + result = [debug.SourceAddress(addr, name=func, filename=filename, line=line)] + syminfo_resolver.cache[addr] = result + return result + @classmethod def clear_cache(clazz): clazz.cache.clear() +symbol_resolver = syminfo_resolver() + +def symbol_formatter(src_addr): + ret = src_addr.name + if src_addr.filename or src_addr.line: + ret += ' (' + ret += src_addr.filename + if src_addr.line: + ret += ':' + str(src_addr.line) + ret += ')' + return ret + +def syminfo(addr): + return symbol_formatter(syminfo_resolver(addr)) + def translate(path): '''given a path, try to find it on the host OS''' name = os.path.basename(path) @@ -449,7 +460,7 @@ class osv_syms(gdb.Command): gdb.Command.__init__(self, 'osv syms', gdb.COMMAND_USER, gdb.COMPLETE_NONE) def invoke(self, arg, from_tty): - syminfo.clear_cache() + syminfo_resolver.clear_cache() p = gdb.lookup_global_symbol('elf::program::s_objs').value() p = p.dereference().address while p.dereference(): @@ -939,7 +950,7 @@ def make_symbolic(addr): def dump_trace(out_func): indents = defaultdict(int) - bt_formatter = BacktraceFormatter(make_symbolic) + bt_formatter = BacktraceFormatter(symbol_resolver, symbol_formatter) def lookup_tp(name): return gdb.lookup_global_symbol(name).value().dereference() diff --git a/scripts/osv/debug.py b/scripts/osv/debug.py index 536c68bb6583921775f17db4ac4f3f4aa6432aac..006dc4fe00a8e0ab838176ca82c9819daa4ba854 100644 --- a/scripts/osv/debug.py +++ b/scripts/osv/debug.py @@ -1,6 +1,9 @@ import os import re import subprocess +import threading +import itertools +import select class SourceAddress: def __init__(self, addr, name=None, filename=None, line=None): @@ -19,56 +22,81 @@ class DummyResolver(object): self.cache = {} def __call__(self, addr): - src_addr = self.cache.get(addr, None) - if not src_addr: - src_addr = SourceAddress(addr) - self.cache[addr] = src_addr - return src_addr + result = self.cache.get(addr, None) + if not result: + result = [SourceAddress(addr)] + self.cache[addr] = result + return result class SymbolResolver(object): - def __init__(self, object_path): + inline_prefix = ' (inlined by) ' + + def __init__(self, object_path, show_inline=True): if not os.path.exists(object_path): raise Exception('File not found: ' + object_path) - self.addr2line = subprocess.Popen(['addr2line', '-e', object_path, '-Cfp'], + self.show_inline = show_inline + flags = '-Cfp' + if show_inline: + flags += 'i' + self.addr2line = subprocess.Popen(['addr2line', '-e', object_path, flags], stdin=subprocess.PIPE, stdout=subprocess.PIPE) self.cache = {} + def next_line(self): + return self.addr2line.stdout.readline().rstrip('\n') + + def parse_line(self, addr, line): + # addr2line ver. 2.23.2 (Ubuntu) + m = re.match(r'^\?\?$', line) + if m: + line = self.next_line() + if not re.match(r'^\?\?:0$', line): + raise Exception('Unexpected response: ' + line) + return SourceAddress(addr) + + # addr2line ver. 2.23.52.0.1-9.fc19 + m = re.match(r'^\?\? \?\?:0$', line) + if m: + return SourceAddress(addr) + + m = re.match(r'(?P<name>.*) at ((?P<file>.*?)|\?+):((?P<line>\d+)|\?+)', line) + if not m: + raise Exception('addr2line response not matched: ' + line) + return SourceAddress(addr, m.group('name'), m.group('file'), m.group('line')) + def __call__(self, addr): """ - Returns SourceAddress for given addr. + Returns an iterable of SourceAddress objects for given addr. """ - src_addr = self.cache.get(addr, None) - if src_addr: - return src_addr + result = self.cache.get(addr, None) + if result: + return result self.addr2line.stdin.write('0x%x\n' % addr) - response = self.addr2line.stdout.readline().rstrip('\n') - # addr2line ver. 2.23.2 (Ubuntu) - m = re.match(r'^\?\?$', response) - if m: - response = self.addr2line.stdout.readline().rstrip('\n') - if not re.match(r'^\?\?:0$', response): - raise Exception('Unexpected response: ' + response) - src_addr = SourceAddress(addr) - self.cache[addr] = src_addr - return src_addr + if self.show_inline: + self.addr2line.stdin.write('0\n') - # addr2line ver. 2.23.52.0.1-9.fc19 - m = re.match(r'^\?\? \?\?:0$', response) - if m: - src_addr = SourceAddress(addr) - self.cache[addr] = src_addr - return src_addr + result = [self.parse_line(addr, self.next_line())] - m = re.match(r'(?P<name>.*) at ((?P<file>.*?)|\?+):((?P<line>\d+)|\?+)', response) - if not m: - raise Exception('addr2line response not matched: ' + response) - src_addr = SourceAddress(addr, m.group('name'), m.group('file'), m.group('line')) - self.cache[addr] = src_addr - return src_addr + if self.show_inline: + line = self.next_line() + while line.startswith(self.inline_prefix): + result.append(self.parse_line(addr, line[len(self.inline_prefix):])) + line = self.next_line() + + self.cache[addr] = result + return result def close(): self.addr2line.stdin.close() self.addr2line.wait() + +def resolve_all(resolver, raw_addresses): + """ + Returns iterable of SourceAddress objects for given list of raw addresses + using supplied resolver. + + """ + return itertools.chain.from_iterable(map(resolver, raw_addresses)) diff --git a/scripts/osv/prof.py b/scripts/osv/prof.py index 73cfe2a9845942ee05719ba5c4770392faacfdbb..d8b02a0d71d9e83c03e9e3c3f1f163e62f00a8fe 100644 --- a/scripts/osv/prof.py +++ b/scripts/osv/prof.py @@ -1,6 +1,7 @@ import sys from operator import attrgetter from osv import trace, tree, debug +import itertools class ProfNode(tree.TreeNode): def __init__(self, key): @@ -171,7 +172,7 @@ def print_profile(samples, symbol_resolver, caller_oriented=False, if not sample: continue - frames = [symbol_resolver(addr - 1) for addr in sample.backtrace] + frames = list(debug.resolve_all(symbol_resolver, (addr - 1 for addr in sample.backtrace))) frames = strip_garbage(frames) if caller_oriented: frames.reverse() diff --git a/scripts/osv/trace.py b/scripts/osv/trace.py index 2d11f66ab9d81ed4597a43920ed3a4b805d33b7c..64869d28bf27fdbaaabb382cdb449ed2cea35796 100644 --- a/scripts/osv/trace.py +++ b/scripts/osv/trace.py @@ -2,6 +2,7 @@ import os import mmap import struct import sys +from osv import debug # version 2 introduced thread_name # version 3 introduced variable-length arguments (blob) @@ -20,22 +21,25 @@ def format_time(time): return "%12.9f" % nanos_to_seconds(time) class BacktraceFormatter: - def __init__(self, resolver): + def __init__(self, resolver, formatter): self.resolver = resolver + self.formatter = formatter def __call__(self, backtrace): if not backtrace: return '' - while self.resolver(backtrace[0] - 1).startswith("tracepoint"): - backtrace.pop(0) + frames = list(debug.resolve_all(self.resolver, (x - 1 for x in backtrace if x))) - return ' [' + ', '.join((str(self.resolver(x - 1)) for x in backtrace if x)) + ']' + while frames[0].name and frames[0].name.startswith("tracepoint"): + frames.pop(0) -def simple_symbol_formatter(addr): - return '0x%x' % addr + return ' [' + ', '.join(map(self.formatter, frames)) + ']' -default_backtrace_formatter = BacktraceFormatter(simple_symbol_formatter) +def simple_symbol_formatter(src_addr): + return '0x%x' % src_addr.addr + +default_backtrace_formatter = BacktraceFormatter(debug.DummyResolver, simple_symbol_formatter) class TimeRange(object): """ diff --git a/scripts/trace.py b/scripts/trace.py index e82e44765c42e70f0f918b7657196d8d1042fa2b..99e9198234f40c620869d929a983895d7c8a967c 100755 --- a/scripts/trace.py +++ b/scripts/trace.py @@ -56,6 +56,7 @@ def add_symbol_resolution_options(parser): group.add_argument("-d", "--debug", action="store_true", help="use loader.elf from debug build") group.add_argument("-e", "--exe", action="store", help="path to the object file used for symbol resolution") group.add_argument("-x", "--no-resolve", action='store_true', help="do not resolve symbols") + group.add_argument("--no-inlined-by", action='store_true', help="do not show inlined-by functions") group.add_argument("-L", "--show-line-number", action='store_true', help="show line numbers") group.add_argument("-A", "--show-address", action='store_true', help="show raw addresses") group.add_argument("-F", "--show-file-name", action='store_true', help="show file names") @@ -65,13 +66,14 @@ class BeautifyingResolver(object): self.delegate = delegate def __call__(self, addr): - src_addr = self.delegate(addr) - if src_addr.name: - if src_addr.name.startswith("void sched::thread::do_wait_until<"): - src_addr.name = "sched::thread::do_wait_until" - elif src_addr.name.startswith("do_wait_until<"): - src_addr.name = "do_wait_until" - return src_addr + resolution = self.delegate(addr) + for src_addr in resolution: + if src_addr.name: + if src_addr.name.startswith("void sched::thread::do_wait_until<"): + src_addr.name = "sched::thread::do_wait_until" + elif src_addr.name.startswith("do_wait_until<"): + src_addr.name = "do_wait_until" + return resolution def symbol_resolver(args): if args.no_resolve: @@ -84,14 +86,14 @@ def symbol_resolver(args): else: elf_path = 'build/release/loader.elf' - return BeautifyingResolver(debug.SymbolResolver(elf_path)) + return BeautifyingResolver(debug.SymbolResolver(elf_path, show_inline=not args.no_inlined_by)) def get_backtrace_formatter(args): if not args.backtrace: return lambda backtrace: '' return trace.BacktraceFormatter( - symbol_printer(symbol_resolver(args), src_addr_formatter(args))) + symbol_resolver(args), src_addr_formatter(args)) def list_trace(args): def data_formatter(sample):