# 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")