Enhance tracing/profiling tool by adding a new -S option that instructs
trace.py to use tracefile.symbols file for symbol resolution instead of 
addr2line utility:

./scripts/trace.py prof -FLS

In essence, this new patch enhances loader.py to generate new file 
tracefile.symbols
along the trace file with an information like this:

0x100000119277 /usr/lib/golang/src/runtime/mheap.go 1950 runtime.newMarkBits
0x100000114a04 /usr/lib/golang/src/runtime/mgcsweep.go 471 
runtime.(*mspan).sweep
0x10000010824a /usr/lib/golang/src/runtime/mcentral.go 214 
runtime.(*mcentral).uncacheSpan
0x100000107c3b /usr/lib/golang/src/runtime/mcache.go 276 
runtime.(*mcache).releaseAll
0x40351825 core/mempool.cc 356 memory::pool::free(void*)
0x402403b6 ./bsd/sys/sys/mbuf.h 609 m_freem
0x40388884 /usr/include/c++/11/bits/std_mutex.h 228 
epoll_file::wait(epoll_event*, int, int)

Each line above contains white-space separated address, filename,
file number (if any) and name of all referenced symbols of both kernel and 
application.

Please note the 'trace.py extract' calls 'osv syms' to load all application 
objects.
But this only helps if the objects are still loaded by OSv. For example, if one 
runs some
server app to profile under load and uses 'trace.py extract', then it will work 
because
the app would be still running as OSv has not entered into a shutdown phase.

However, if one tests it with any app that simply completes like 
native-example, then
it will not work because objects are already unloaded. In this case, one has to 
invoke run.py
with -w option, set a breakpoint in OSv code after it loads objects but before 
it starts
the app (for example at core/app.cc:220) and then manually run 'osv syms' to 
force resolution
of application symbols.

The extra benefit of this new approach is that 'trace.py prof' works
much faster as it simply reads new file .symbols instead of calling
addr2line against loader.elf for each symbol which can take half a
minute or longer.

Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>
---
 scripts/loader.py    | 21 +++++++++++++++++++++
 scripts/osv/debug.py | 36 ++++++++++++++++++++++++++++++++++++
 scripts/trace.py     |  6 ++++++
 3 files changed, 63 insertions(+)

diff --git a/scripts/loader.py b/scripts/loader.py
index 97c831e9..a7f82e6c 100755
--- a/scripts/loader.py
+++ b/scripts/loader.py
@@ -101,6 +101,15 @@ class syminfo_resolver(object):
     def clear_cache(clazz):
         clazz.cache.clear()
 
+    @classmethod
+    def output_cache(clazz, output_func):
+        for source_addr in clazz.cache.values():
+            addr = source_addr[0]
+            if addr.line:
+                output_func("0x%x %s %d %s\n" % (addr.addr, addr.filename, 
addr.line, addr.name))
+            else:
+                output_func("0x%x %s <?> %s\n" % (addr.addr, addr.filename, 
addr.name))
+
 symbol_resolver = syminfo_resolver()
 
 def symbol_formatter(src_addr):
@@ -1304,6 +1313,17 @@ def all_traces():
 def save_traces_to_file(filename):
     trace.write_to_file(filename, list(all_traces()))
 
+def save_backtrace_symbols_to_file(filename):
+    # Iterate over all traces and force resolution of symbols in
+    # included backtrace if any
+    for trace in all_traces():
+        if trace.backtrace:
+            for address in list(x - 1 for x in trace.backtrace if x):
+                symbol_resolver(address)
+    # Save resolved symbol information from cache into a file
+    with open(filename, 'wt') as sout:
+        syminfo_resolver.output_cache(sout.write)
+
 def make_symbolic(addr):
     return str(syminfo(addr))
 
@@ -1503,6 +1523,7 @@ class osv_trace_save(gdb.Command):
 
         gdb.write('Saving traces to %s ...\n' % arg)
         save_traces_to_file(arg)
+        save_backtrace_symbols_to_file("%s.symbols" % arg)
 
 class osv_trace_file(gdb.Command):
     def __init__(self):
diff --git a/scripts/osv/debug.py b/scripts/osv/debug.py
index 83372ada..d7d34637 100644
--- a/scripts/osv/debug.py
+++ b/scripts/osv/debug.py
@@ -99,6 +99,42 @@ class SymbolResolver(object):
         self.addr2line.stdin.close()
         self.addr2line.wait()
 
+class SymbolsFileResolver(object):
+    def __init__(self, symbols_file, fallback_resolver=DummyResolver()):
+        if not os.path.exists(symbols_file):
+            raise Exception('File not found: ' + object_path)
+        self.fallback_resolver = fallback_resolver
+        self.cache = dict()
+
+        try:
+            symbol_lines = open(symbols_file).read().split('\n')
+        except IOError:
+            symbol_lines = []
+
+        for symbol_line in symbol_lines:
+            tokens = symbol_line.split(maxsplit=3)
+            if len(tokens) > 0:
+                addr = int(tokens[0], 16)
+                filename = tokens[1]
+                if tokens[2] == '<?>':
+                    line = None
+                else:
+                    line = int(tokens[2])
+                name = tokens[3]
+                self.cache[addr] = [SourceAddress(addr, name=name, 
filename=filename, line=line)]
+
+    def __call__(self, addr):
+        """
+        Returns an iterable of SourceAddress objects for given addr.
+
+        """
+
+        result = self.cache.get(addr, None)
+        if result:
+            return result
+        else:
+            return self.fallback_resolver(addr)
+
 def resolve_all(resolver, raw_addresses):
     """
     Returns iterable of SourceAddress objects for given list of raw addresses
diff --git a/scripts/trace.py b/scripts/trace.py
index 3d3d0dce..17a6faeb 100755
--- a/scripts/trace.py
+++ b/scripts/trace.py
@@ -63,6 +63,7 @@ def add_symbol_resolution_options(parser):
     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")
+    group.add_argument("-S", "--use-symbols-file", action='store_true', 
help="use <tracefile>.symbols to resolve symbols")
 
 class BeautifyingResolver(object):
     def __init__(self, delegate):
@@ -82,6 +83,10 @@ def symbol_resolver(args):
     if args.no_resolve:
         return debug.DummyResolver()
 
+    if args.use_symbols_file:
+        symbols_file = "%s.symbols" % args.tracefile
+        return BeautifyingResolver(debug.SymbolsFileResolver(symbols_file))
+
     if args.exe:
         elf_path = args.exe
     elif args.debug:
@@ -281,6 +286,7 @@ def extract(args):
             cmdline.extend(['-ex', 'target remote ' + args.remote])
         else:
             cmdline.extend(['-ex', 'conn'])
+        cmdline.extend(['-ex', 'osv syms'])
         cmdline.extend(['-ex', 'osv trace save ' + args.tracefile])
         proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE,
             stderr=subprocess.STDOUT)
-- 
2.35.1

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/20230404024308.169022-4-jwkozaczuk%40gmail.com.

Reply via email to