1# Usage:
2#   art/test/run-test --host --gdb [--64] [--interpreter] 004-JniTest
3#   'b Java_Main_shortMethod'
4#   'r'
5#   'command script import host_art_bt.py'
6#   'host_art_bt'
7
8from __future__ import print_function
9
10import sys
11import re
12
13import lldb
14
15
16def host_art_bt(debugger, command, result, internal_dict):
17    prettified_frames = []
18    lldb_frame_index = 0
19    art_frame_index = 0
20    target = debugger.GetSelectedTarget()
21    process = target.GetProcess()
22    thread = process.GetSelectedThread()
23    while lldb_frame_index < thread.GetNumFrames():
24        frame = thread.GetFrameAtIndex(lldb_frame_index)
25        if frame.GetModule() and re.match(r'JIT\(.*?\)',
26                                          frame.GetModule().GetFileSpec().GetFilename()):
27            # Compiled Java frame
28
29            # Get function/filename/lineno from symbol context
30            symbol = frame.GetSymbol()
31            if not symbol:
32                print('No symbol info for compiled Java frame: ', frame)
33                sys.exit(1)
34            line_entry = frame.GetLineEntry()
35            prettified_frames.append({
36                'function': symbol.GetName(),
37                'file': str(line_entry.GetFileSpec()) if line_entry else None,
38                'line': line_entry.GetLine() if line_entry else -1
39            })
40
41            # Skip art frames
42            while True:
43                art_stack_visitor = frame.EvaluateExpression(
44                    """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(""" +
45                    str(art_frame_index) +
46                    """); visitor.WalkStack(true); visitor""")
47                art_method = frame.EvaluateExpression(
48                    art_stack_visitor.GetName() + """.GetMethod()""")
49                if art_method.GetValueAsUnsigned() != 0:
50                    art_method_name = frame.EvaluateExpression(
51                        """art::PrettyMethod(""" + art_method.GetName() + """, true)""")
52                    art_method_name_data = frame.EvaluateExpression(
53                        art_method_name.GetName() + """.c_str()""").GetValueAsUnsigned()
54                    art_method_name_size = frame.EvaluateExpression(
55                        art_method_name.GetName() + """.length()""").GetValueAsUnsigned()
56                    error = lldb.SBError()
57                    art_method_name = process.ReadCStringFromMemory(
58                        art_method_name_data, art_method_name_size + 1, error)
59                    if not error.Success:
60                        print('Failed to read method name')
61                        sys.exit(1)
62                    if art_method_name != symbol.GetName():
63                        print('Function names in native symbol and art runtime stack do not match: ', symbol.GetName(), ' != ', art_method_name)
64                    art_frame_index = art_frame_index + 1
65                    break
66                art_frame_index = art_frame_index + 1
67
68            # Skip native frames
69            lldb_frame_index = lldb_frame_index + 1
70            if lldb_frame_index < thread.GetNumFrames():
71                frame = thread.GetFrameAtIndex(lldb_frame_index)
72                if frame.GetModule() and re.match(
73                        r'JIT\(.*?\)', frame.GetModule().GetFileSpec().GetFilename()):
74                    # Another compile Java frame
75                    # Don't skip; leave it to the next iteration
76                    continue
77                elif frame.GetSymbol() and (frame.GetSymbol().GetName() == 'art_quick_invoke_stub' or frame.GetSymbol().GetName() == 'art_quick_invoke_static_stub'):
78                    # art_quick_invoke_stub / art_quick_invoke_static_stub
79                    # Skip until we get past the next ArtMethod::Invoke()
80                    while True:
81                        lldb_frame_index = lldb_frame_index + 1
82                        if lldb_frame_index >= thread.GetNumFrames():
83                            print('ArtMethod::Invoke not found below art_quick_invoke_stub/art_quick_invoke_static_stub')
84                            sys.exit(1)
85                        frame = thread.GetFrameAtIndex(lldb_frame_index)
86                        if frame.GetSymbol() and frame.GetSymbol().GetName(
87                        ) == 'art::mirror::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)':
88                            lldb_frame_index = lldb_frame_index + 1
89                            break
90                else:
91                    print('Invalid frame below compiled Java frame: ', frame)
92        elif frame.GetSymbol() and frame.GetSymbol().GetName() == 'art_quick_generic_jni_trampoline':
93            # Interpreted JNI frame for x86_64
94
95            # Skip art frames
96            while True:
97                art_stack_visitor = frame.EvaluateExpression(
98                    """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(""" +
99                    str(art_frame_index) +
100                    """); visitor.WalkStack(true); visitor""")
101                art_method = frame.EvaluateExpression(
102                    art_stack_visitor.GetName() + """.GetMethod()""")
103                if art_method.GetValueAsUnsigned() != 0:
104                    # Get function/filename/lineno from ART runtime
105                    art_method_name = frame.EvaluateExpression(
106                        """art::PrettyMethod(""" + art_method.GetName() + """, true)""")
107                    art_method_name_data = frame.EvaluateExpression(
108                        art_method_name.GetName() + """.c_str()""").GetValueAsUnsigned()
109                    art_method_name_size = frame.EvaluateExpression(
110                        art_method_name.GetName() + """.length()""").GetValueAsUnsigned()
111                    error = lldb.SBError()
112                    function = process.ReadCStringFromMemory(
113                        art_method_name_data, art_method_name_size + 1, error)
114
115                    prettified_frames.append({
116                        'function': function,
117                        'file': None,
118                        'line': -1
119                    })
120
121                    art_frame_index = art_frame_index + 1
122                    break
123                art_frame_index = art_frame_index + 1
124
125            # Skip native frames
126            lldb_frame_index = lldb_frame_index + 1
127            if lldb_frame_index < thread.GetNumFrames():
128                frame = thread.GetFrameAtIndex(lldb_frame_index)
129                if frame.GetSymbol() and (frame.GetSymbol().GetName() ==
130                                          'art_quick_invoke_stub' or frame.GetSymbol().GetName() == 'art_quick_invoke_static_stub'):
131                    # art_quick_invoke_stub / art_quick_invoke_static_stub
132                    # Skip until we get past the next ArtMethod::Invoke()
133                    while True:
134                        lldb_frame_index = lldb_frame_index + 1
135                        if lldb_frame_index >= thread.GetNumFrames():
136                            print('ArtMethod::Invoke not found below art_quick_invoke_stub/art_quick_invoke_static_stub')
137                            sys.exit(1)
138                        frame = thread.GetFrameAtIndex(lldb_frame_index)
139                        if frame.GetSymbol() and frame.GetSymbol().GetName(
140                        ) == 'art::mirror::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)':
141                            lldb_frame_index = lldb_frame_index + 1
142                            break
143                else:
144                    print('Invalid frame below compiled Java frame: ', frame)
145        elif frame.GetSymbol() and re.search(r'art::interpreter::', frame.GetSymbol().GetName()):
146            # Interpreted Java frame
147
148            while True:
149                lldb_frame_index = lldb_frame_index + 1
150                if lldb_frame_index >= thread.GetNumFrames():
151                    print('art::interpreter::Execute not found in interpreter frame')
152                    sys.exit(1)
153                frame = thread.GetFrameAtIndex(lldb_frame_index)
154                if frame.GetSymbol() and frame.GetSymbol().GetName(
155                ) == 'art::interpreter::Execute(art::Thread*, art::MethodHelper&, art::DexFile::CodeItem const*, art::ShadowFrame&, art::JValue)':
156                    break
157
158            # Skip art frames
159            while True:
160                art_stack_visitor = frame.EvaluateExpression(
161                    """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(""" +
162                    str(art_frame_index) +
163                    """); visitor.WalkStack(true); visitor""")
164                art_method = frame.EvaluateExpression(
165                    art_stack_visitor.GetName() + """.GetMethod()""")
166                if art_method.GetValueAsUnsigned() != 0:
167                    # Get function/filename/lineno from ART runtime
168                    art_method_name = frame.EvaluateExpression(
169                        """art::PrettyMethod(""" + art_method.GetName() + """, true)""")
170                    art_method_name_data = frame.EvaluateExpression(
171                        art_method_name.GetName() + """.c_str()""").GetValueAsUnsigned()
172                    art_method_name_size = frame.EvaluateExpression(
173                        art_method_name.GetName() + """.length()""").GetValueAsUnsigned()
174                    error = lldb.SBError()
175                    function = process.ReadCStringFromMemory(
176                        art_method_name_data, art_method_name_size + 1, error)
177
178                    line = frame.EvaluateExpression(
179                        art_stack_visitor.GetName() +
180                        """.GetMethod()->GetLineNumFromDexPC(""" +
181                        art_stack_visitor.GetName() +
182                        """.GetDexPc(true))""").GetValueAsUnsigned()
183
184                    file_name = frame.EvaluateExpression(
185                        art_method.GetName() + """->GetDeclaringClassSourceFile()""")
186                    file_name_data = file_name.GetValueAsUnsigned()
187                    file_name_size = frame.EvaluateExpression(
188                        """(size_t)strlen(""" + file_name.GetName() + """)""").GetValueAsUnsigned()
189                    error = lldb.SBError()
190                    file_name = process.ReadCStringFromMemory(
191                        file_name_data, file_name_size + 1, error)
192                    if not error.Success():
193                        print('Failed to read source file name')
194                        sys.exit(1)
195
196                    prettified_frames.append({
197                        'function': function,
198                        'file': file_name,
199                        'line': line
200                    })
201
202                    art_frame_index = art_frame_index + 1
203                    break
204                art_frame_index = art_frame_index + 1
205
206            # Skip native frames
207            while True:
208                lldb_frame_index = lldb_frame_index + 1
209                if lldb_frame_index >= thread.GetNumFrames():
210                    print('Can not get past interpreter native frames')
211                    sys.exit(1)
212                frame = thread.GetFrameAtIndex(lldb_frame_index)
213                if frame.GetSymbol() and not re.search(
214                        r'art::interpreter::', frame.GetSymbol().GetName()):
215                    break
216        else:
217            # Other frames. Add them as-is.
218            frame = thread.GetFrameAtIndex(lldb_frame_index)
219            lldb_frame_index = lldb_frame_index + 1
220            if frame.GetModule():
221                module_name = frame.GetModule().GetFileSpec().GetFilename()
222                if not module_name in [
223                    'libartd.so',
224                    'dalvikvm32',
225                    'dalvikvm64',
226                        'libc.so.6']:
227                    prettified_frames.append({
228                        'function': frame.GetSymbol().GetName() if frame.GetSymbol() else None,
229                        'file': str(frame.GetLineEntry().GetFileSpec()) if frame.GetLineEntry() else None,
230                        'line': frame.GetLineEntry().GetLine() if frame.GetLineEntry() else -1
231                    })
232
233    for prettified_frame in prettified_frames:
234        print(prettified_frame['function'], prettified_frame['file'], prettified_frame['line'])
235
236
237def __lldb_init_module(debugger, internal_dict):
238    debugger.HandleCommand(
239        'command script add -f host_art_bt.host_art_bt host_art_bt')
240