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