[Lldb-commits] [lldb] r218315 - android: add a command script to help display mixed Java/native backtraces.

Todd Fiala todd.fiala at gmail.com
Tue Sep 23 09:28:02 PDT 2014


Author: tfiala
Date: Tue Sep 23 11:28:01 2014
New Revision: 218315

URL: http://llvm.org/viewvc/llvm-project?rev=218315&view=rev
Log:
android: add a command script to help display mixed Java/native backtraces.

This script supports displaying developer-focused backtraces when working
with mixed Java and C/C++ stack frames within lldb.  On Android, this represents
just about every app, since all apps start in Java code.

The script currently supports the Art JVM when run on host-side x86_64 and x86,
but does require a patch not yet accepted in AOSP:
AOSP patch: https://android-review.googlesource.com/#/c/106523/

The backtraces will hide Art VM machinery for interpreted and AOT code
and display the Java file/line numbers for Java code, while displaying
native backtrace info for native frames.  Effectively the developer will
get an app-centric view of the call stack.

This script is not yet tested on device-side Art nor is it tested on
any architecture other than x86_64 or x86 32-bit.  Several changes were
needed on the AOSP side to enable it to work properly for x86_64 and x86, 
so it is quite likely we'll need to do something similar for other cpu
architectures as well.

Change by Tong Shen

Added:
    lldb/trunk/scripts/Python/android/
    lldb/trunk/scripts/Python/android/host_art_bt.py

Added: lldb/trunk/scripts/Python/android/host_art_bt.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/scripts/Python/android/host_art_bt.py?rev=218315&view=auto
==============================================================================
--- lldb/trunk/scripts/Python/android/host_art_bt.py (added)
+++ lldb/trunk/scripts/Python/android/host_art_bt.py Tue Sep 23 11:28:01 2014
@@ -0,0 +1,192 @@
+# Usage:
+#   art/test/run-test --host --gdb [--64] [--interpreter] 004-JniTest
+#   'b Java_Main_shortMethod'
+#   'r'
+#   'command script import host_art_bt.py'
+#   'host_art_bt'
+
+import sys
+import re
+
+import lldb
+
+def host_art_bt(debugger, command, result, internal_dict):
+  prettified_frames = []
+  lldb_frame_index = 0
+  art_frame_index = 0
+  target = debugger.GetSelectedTarget()
+  process = target.GetProcess()
+  thread = process.GetSelectedThread()
+  while lldb_frame_index < thread.GetNumFrames():
+    frame = thread.GetFrameAtIndex(lldb_frame_index)
+    if frame.GetModule() and re.match(r'JIT\(.*?\)', frame.GetModule().GetFileSpec().GetFilename()):
+      # Compiled Java frame
+
+      # Get function/filename/lineno from symbol context
+      symbol = frame.GetSymbol()
+      if not symbol:
+        print 'No symbol info for compiled Java frame: ', frame
+        sys.exit(1)
+      line_entry = frame.GetLineEntry()
+      prettified_frames.append({
+        'function': symbol.GetName(),
+        'file'    : str(line_entry.GetFileSpec()) if line_entry else None,
+        'line'    : line_entry.GetLine() if line_entry else -1
+      })
+
+      # Skip art frames
+      while True:
+        art_stack_visitor = frame.EvaluateExpression("""struct GetStackVisitor : public StackVisitor { GetStackVisitor(int depth_) : StackVisitor(Thread::Current(), NULL), depth(depth_) {} bool VisitFrame() { if (cur_depth_ == depth) { return false; } else { return true; } } int depth; }; GetStackVisitor visitor(""" + str(art_frame_index) + """); visitor.WalkStack(true); visitor""")
+        art_method = frame.EvaluateExpression(art_stack_visitor.GetName() + """.GetMethod()""")
+        if art_method.GetValueAsUnsigned() != 0:
+          art_method_name = frame.EvaluateExpression("""art::PrettyMethod(""" + art_method.GetName() + """, true)""")
+          art_method_name_data = frame.EvaluateExpression(art_method_name.GetName() + """.c_str()""").GetValueAsUnsigned()
+          art_method_name_size = frame.EvaluateExpression(art_method_name.GetName() + """.length()""").GetValueAsUnsigned()
+          error = lldb.SBError()
+          art_method_name = process.ReadCStringFromMemory(art_method_name_data, art_method_name_size + 1, error)
+          if not error.Success:
+            print 'Failed to read method name'
+            sys.exit(1)
+          if art_method_name != symbol.GetName():
+            print 'Function names in native symbol and art runtime stack do not match: ', symbol.GetName(), ' != ', art_method_name
+          art_frame_index = art_frame_index + 1
+          break
+        art_frame_index = art_frame_index + 1
+
+      # Skip native frames
+      lldb_frame_index = lldb_frame_index + 1
+      if lldb_frame_index < thread.GetNumFrames():
+        frame = thread.GetFrameAtIndex(lldb_frame_index)
+        if frame.GetModule() and re.match(r'JIT\(.*?\)', frame.GetModule().GetFileSpec().GetFilename()):
+          # Another compile Java frame
+          # Don't skip; leave it to the next iteration
+          continue
+        elif frame.GetSymbol() and (frame.GetSymbol().GetName() == 'art_quick_invoke_stub' or frame.GetSymbol().GetName() == 'art_quick_invoke_static_stub'):
+          # art_quick_invoke_stub / art_quick_invoke_static_stub
+          # Skip until we get past the next ArtMethod::Invoke()
+          while True:
+            lldb_frame_index = lldb_frame_index + 1
+            if lldb_frame_index >= thread.GetNumFrames():
+              print 'ArtMethod::Invoke not found below art_quick_invoke_stub/art_quick_invoke_static_stub'
+              sys.exit(1)
+            frame = thread.GetFrameAtIndex(lldb_frame_index)
+            if frame.GetSymbol() and frame.GetSymbol().GetName() == 'art::mirror::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)':
+              lldb_frame_index = lldb_frame_index + 1
+              break
+        else:
+          print 'Invalid frame below compiled Java frame: ', frame
+    elif frame.GetSymbol() and frame.GetSymbol().GetName() == 'art_quick_generic_jni_trampoline':
+      # Interpreted JNI frame for x86_64
+
+      # Skip art frames
+      while True:
+        art_stack_visitor = frame.EvaluateExpression("""struct GetStackVisitor : public StackVisitor { GetStackVisitor(int depth_) : StackVisitor(Thread::Current(), NULL), depth(depth_) {} bool VisitFrame() { if (cur_depth_ == depth) { return false; } else { return true; } } int depth; }; GetStackVisitor visitor(""" + str(art_frame_index) + """); visitor.WalkStack(true); visitor""")
+        art_method = frame.EvaluateExpression(art_stack_visitor.GetName() + """.GetMethod()""")
+        if art_method.GetValueAsUnsigned() != 0:
+          # Get function/filename/lineno from ART runtime
+          art_method_name = frame.EvaluateExpression("""art::PrettyMethod(""" + art_method.GetName() + """, true)""")
+          art_method_name_data = frame.EvaluateExpression(art_method_name.GetName() + """.c_str()""").GetValueAsUnsigned()
+          art_method_name_size = frame.EvaluateExpression(art_method_name.GetName() + """.length()""").GetValueAsUnsigned()
+          error = lldb.SBError()
+          function = process.ReadCStringFromMemory(art_method_name_data, art_method_name_size + 1, error)
+
+          prettified_frames.append({
+            'function': function,
+            'file'    : None,
+            'line'    : -1
+          })
+
+          art_frame_index = art_frame_index + 1
+          break
+        art_frame_index = art_frame_index + 1
+
+      # Skip native frames
+      lldb_frame_index = lldb_frame_index + 1
+      if lldb_frame_index < thread.GetNumFrames():
+        frame = thread.GetFrameAtIndex(lldb_frame_index)
+        if frame.GetSymbol() and (frame.GetSymbol().GetName() == 'art_quick_invoke_stub' or frame.GetSymbol().GetName() == 'art_quick_invoke_static_stub'):
+          # art_quick_invoke_stub / art_quick_invoke_static_stub
+          # Skip until we get past the next ArtMethod::Invoke()
+          while True:
+            lldb_frame_index = lldb_frame_index + 1
+            if lldb_frame_index >= thread.GetNumFrames():
+              print 'ArtMethod::Invoke not found below art_quick_invoke_stub/art_quick_invoke_static_stub'
+              sys.exit(1)
+            frame = thread.GetFrameAtIndex(lldb_frame_index)
+            if frame.GetSymbol() and frame.GetSymbol().GetName() == 'art::mirror::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)':
+              lldb_frame_index = lldb_frame_index + 1
+              break
+        else:
+          print 'Invalid frame below compiled Java frame: ', frame
+    elif frame.GetSymbol() and re.search(r'art::interpreter::', frame.GetSymbol().GetName()):
+      # Interpreted Java frame
+
+      while True:
+        lldb_frame_index = lldb_frame_index + 1
+        if lldb_frame_index >= thread.GetNumFrames():
+          print 'art::interpreter::Execute not found in interpreter frame'
+          sys.exit(1)
+        frame = thread.GetFrameAtIndex(lldb_frame_index)
+        if frame.GetSymbol() and frame.GetSymbol().GetName() == 'art::interpreter::Execute(art::Thread*, art::MethodHelper&, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)':
+          break
+
+      # Skip art frames
+      while True:
+        art_stack_visitor = frame.EvaluateExpression("""struct GetStackVisitor : public StackVisitor { GetStackVisitor(int depth_) : StackVisitor(Thread::Current(), NULL), depth(depth_) {} bool VisitFrame() { if (cur_depth_ == depth) { return false; } else { return true; } } int depth; }; GetStackVisitor visitor(""" + str(art_frame_index) + """); visitor.WalkStack(true); visitor""")
+        art_method = frame.EvaluateExpression(art_stack_visitor.GetName() + """.GetMethod()""")
+        if art_method.GetValueAsUnsigned() != 0:
+          # Get function/filename/lineno from ART runtime
+          art_method_name = frame.EvaluateExpression("""art::PrettyMethod(""" + art_method.GetName() + """, true)""")
+          art_method_name_data = frame.EvaluateExpression(art_method_name.GetName() + """.c_str()""").GetValueAsUnsigned()
+          art_method_name_size = frame.EvaluateExpression(art_method_name.GetName() + """.length()""").GetValueAsUnsigned()
+          error = lldb.SBError()
+          function = process.ReadCStringFromMemory(art_method_name_data, art_method_name_size + 1, error)
+
+          line = frame.EvaluateExpression(art_stack_visitor.GetName() + """.GetMethod()->GetLineNumFromDexPC(""" + art_stack_visitor.GetName() + """.GetDexPc(true))""").GetValueAsUnsigned()
+          
+          file_name = frame.EvaluateExpression(art_method.GetName() + """->GetDeclaringClassSourceFile()""")
+          file_name_data = file_name.GetValueAsUnsigned()
+          file_name_size = frame.EvaluateExpression("""(size_t)strlen(""" + file_name.GetName() + """)""").GetValueAsUnsigned()
+          error = lldb.SBError()
+          file_name = process.ReadCStringFromMemory(file_name_data, file_name_size + 1, error)
+          if not error.Success():
+            print 'Failed to read source file name'
+            sys.exit(1)
+
+          prettified_frames.append({
+            'function': function,
+            'file'    : file_name,
+            'line'    : line
+          })
+
+          art_frame_index = art_frame_index + 1
+          break
+        art_frame_index = art_frame_index + 1
+
+      # Skip native frames
+      while True:
+        lldb_frame_index = lldb_frame_index + 1
+        if lldb_frame_index >= thread.GetNumFrames():
+          print 'Can not get past interpreter native frames'
+          sys.exit(1)
+        frame = thread.GetFrameAtIndex(lldb_frame_index)
+        if frame.GetSymbol() and not re.search(r'art::interpreter::', frame.GetSymbol().GetName()):
+          break
+    else:
+      # Other frames. Add them as-is.
+      frame = thread.GetFrameAtIndex(lldb_frame_index)
+      lldb_frame_index = lldb_frame_index + 1
+      if frame.GetModule():
+        module_name = frame.GetModule().GetFileSpec().GetFilename()
+        if not module_name in ['libartd.so', 'dalvikvm32', 'dalvikvm64', 'libc.so.6']:
+          prettified_frames.append({
+            'function': frame.GetSymbol().GetName() if frame.GetSymbol() else None,
+            'file'    : str(frame.GetLineEntry().GetFileSpec()) if frame.GetLineEntry() else None,
+            'line'    : frame.GetLineEntry().GetLine() if frame.GetLineEntry() else -1
+          })
+
+  for prettified_frame in prettified_frames:
+    print prettified_frame['function'], prettified_frame['file'], prettified_frame['line']
+
+def __lldb_init_module(debugger, internal_dict):
+    debugger.HandleCommand('command script add -f host_art_bt.host_art_bt host_art_bt')





More information about the lldb-commits mailing list