OLD | NEW |
---|---|
(Empty) | |
1 # Copyright (c) 2014 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import os | |
6 import re | |
7 | |
8 import crash_utils | |
9 | |
10 | |
11 class Stacktrace(object): | |
12 """Represents Stacktrace object. | |
13 | |
14 Contains a list of callstacks, because one stacktrace might have more than | |
15 one callstacks. | |
16 """ | |
17 | |
18 def __init__(self, stacktrace, chrome_build): | |
19 self.stack_list = [] | |
20 self.ParseStacktrace(stacktrace, chrome_build) | |
21 | |
22 def ParseStacktrace(self, stacktrace, chrome_build): | |
23 """Parses stacktrace and normalizes it. | |
24 | |
25 If there are multiple callstacks within the stacktrace, | |
26 it will parse each of them separately, and store them in the stack_list | |
27 variable. | |
28 | |
29 Args: | |
30 stacktrace: A string containing stacktrace. | |
31 chrome_build: A string containing the build type of the crash. | |
32 """ | |
33 # If the passed in string is empty, the object does not represent anything. | |
34 if not stacktrace: | |
35 self.stack_list = None | |
36 return | |
37 | |
38 # Reset the stack list, and assume we start from main thread. | |
39 self.stack_list = [] | |
40 stack_priority = 0 | |
41 reached_new_callstack = False | |
42 # Note that we do not need exact stack frame index, we only need relative | |
43 # position of a frame within a callstack. The reason for not extracting | |
44 # index from a line is that some stack frames do not have index. | |
45 stack_frame_index = 0 | |
46 current_stack = CallStack(stack_priority) | |
47 | |
48 for line in stacktrace: | |
49 # Check if current line is the start of new callstack | |
Martin Barbella
2014/08/06 21:12:25
This comment isn't needed. The next line could sta
jeun
2014/08/06 23:36:25
Done.
| |
50 is_new_callstack = self.__CheckIfNewCallStack(line, chrome_build) | |
Martin Barbella
2014/08/06 21:12:24
__CheckIfNewCallStack is a slightly awkward name.
jeun
2014/08/06 23:36:25
Done.
| |
51 if is_new_callstack: | |
52 | |
53 # If this callstack is from main thread, update the boolean. | |
54 if not reached_new_callstack: | |
55 reached_new_callstack = True | |
56 | |
57 # If this is from freed or prev-alloc, add the callstack we have | |
58 # to the list of callstacks, and increment the stack priority. | |
59 else: | |
60 stack_frame_index = 0 | |
61 if current_stack.frame_list: | |
62 self.stack_list.append(current_stack) | |
63 stack_priority += 1 | |
Martin Barbella
2014/08/06 21:12:25
A while ago, I thought we discussed that free and
jeun
2014/08/06 23:36:25
i have fixed so that it would look at the marker w
| |
64 current_stack = CallStack(stack_priority) | |
65 | |
66 # Parse function name, file path and line number from the line. | |
67 parsed_stack_frame_line = self.__ExtractFromStackFrame( | |
Martin Barbella
2014/08/06 21:12:24
This is more of a general comment, but try to name
jeun
2014/08/06 23:36:25
Done.
| |
68 line, chrome_build) | |
69 | |
70 # If the line is malformed, ignore this line. Else, get the info. | |
71 if not parsed_stack_frame_line: | |
72 continue | |
73 | |
74 (function, file_path, crashed_line_number) = parsed_stack_frame_line | |
Martin Barbella
2014/08/06 21:12:24
Instead of dealing with multiple return values fro
jeun
2014/08/06 23:36:25
Done.
| |
75 | |
76 # Normalize the file path so that it can be compared to repository path. | |
77 file_name = os.path.basename(file_path) | |
78 (component, file_path) = crash_utils.NormalizePathLinux(file_path) | |
79 | |
80 # Currently supports only blink and chromium. | |
81 if component == 'blink' or component == 'chromium': | |
82 current_stack.Add( | |
83 StackFrame(stack_frame_index, component, file_name, | |
84 function, file_path, crashed_line_number)) | |
85 | |
86 stack_frame_index += 1 | |
87 | |
88 # Add the current callstack only if there are frames in it. | |
89 if current_stack.frame_list: | |
90 self.stack_list.append(current_stack) | |
91 | |
92 def __CheckIfNewCallStack(self, line, chrome_build): | |
93 """Check if this line is the start of the new callstack. | |
94 | |
95 Since each builds have different format of stacktrace, the logic for | |
96 checking the line for all builds is handled in here. | |
97 | |
98 Args: | |
99 line: Line to check for. | |
100 chrome_build: The name of the build. | |
101 | |
102 Returns: | |
103 True if the line is the start of new callstack, False otherwise. | |
104 """ | |
105 # Currently not supported. | |
106 if 'android' in chrome_build: | |
107 return False | |
108 | |
109 if 'syzyasan' in chrome_build: | |
110 # In syzyasan build, new stack starts with 'crash stack:', | |
111 # 'freed stack:', etc. | |
112 callstack_start_pattern = re.compile(r'^.* stack:$') | |
113 return callstack_start_pattern.match(line) | |
114 | |
115 # Stack frame pattern for asan, lsan, tsan, etc. | |
116 stack_frame_index_pattern = re.compile(r'#(\d+)') | |
Martin Barbella
2014/08/06 21:12:25
Make sure that there is only whitespace before the
jeun
2014/08/06 23:36:25
This line is deleted.
| |
117 | |
118 # Match the line to see if it is a valid stack frame. | |
119 stack_frame_index_match = stack_frame_index_pattern.match(line) | |
120 | |
121 # If this line does not represent the crashing information, return False. | |
122 if not stack_frame_index_match: | |
123 return False | |
124 | |
125 # Get index from the frame. If the index is 0, it means that new callstack | |
126 # starts in this line. | |
127 stack_frame_index = int(stack_frame_index_match.group(1)) | |
128 return stack_frame_index == 0 | |
129 | |
130 def __ExtractFromStackFrame(self, line, chrome_build): | |
131 """Extracts information from a line in stacktrace. | |
132 | |
133 Args: | |
134 line: A stacktrace string to extract data from. | |
135 chrome_build: A string containing the build type | |
136 of this crash (e.g. linux_asan_chrome_mp). | |
137 | |
138 Returns: | |
139 A triple containing the name of the function, the path of the file and | |
140 the crashed line number. | |
141 """ | |
142 line_parts = line.split() | |
143 try: | |
144 # Tsan has different stack frame style from other builds. | |
145 if chrome_build.startswith('linux_tsan'): | |
146 # Filter out lines that are not stack frame. | |
147 stack_frame_index_pattern = re.compile(r'#(\d+)') | |
Martin Barbella
2014/08/06 21:12:25
These three lines seem to be the same in both case
jeun
2014/08/06 23:36:25
Done.
| |
148 if not stack_frame_index_pattern.match(line_parts[0]): | |
149 return None | |
150 | |
151 file_path_and_line = line_parts[-2] | |
152 function = ' '.join(line_parts[1:-2]) | |
153 | |
154 else: | |
155 # Filter out lines that are not stack frame. | |
156 stack_frame_index_pattern = re.compile(r'#(\d+)') | |
157 if not stack_frame_index_pattern.match(line_parts[0]): | |
158 return None | |
159 | |
160 file_path_and_line = line_parts[-1] | |
161 function = ' '.join(line_parts[3:-1]) | |
162 | |
163 # Get file path and line info from the line. | |
164 file_path_and_line = file_path_and_line.split(':') | |
165 file_path = file_path_and_line[0] | |
166 crashed_line_number = int(file_path_and_line[1]) | |
167 return (function, file_path, crashed_line_number) | |
168 | |
169 # Return None if the line is malformed. | |
170 except IndexError: | |
171 return None | |
172 except ValueError: | |
173 return None | |
174 | |
175 def __getitem__(self, index): | |
176 return self.stack_list[index] | |
177 | |
178 def GetStackFromMainThread(self): | |
Martin Barbella
2014/08/06 21:12:25
It makes more sense to call this the crash stack t
jeun
2014/08/06 23:36:25
Done.
| |
179 return self.stack_list[0] | |
180 | |
181 | |
182 class CallStack(object): | |
183 """Represents a call stack within a stacktrace. | |
184 | |
185 It is a list of StackFrame object, and the stack represented by | |
186 this object is from main thread, freed thread or previously-allocated thread. | |
187 """ | |
188 | |
189 def __init__(self, stack_priority): | |
190 self.frame_list = [] | |
191 self.priority = stack_priority | |
192 | |
193 def Add(self, stacktrace_line): | |
194 self.frame_list.append(stacktrace_line) | |
195 | |
196 def GetFirstN(self, n): | |
Martin Barbella
2014/08/06 21:12:24
Consider renaming to something like GetTopNFrames.
jeun
2014/08/06 23:36:25
Done.
| |
197 return self.frame_list[:n] | |
198 | |
199 | |
200 class StackFrame(object): | |
201 """Represents a frame in stacktrace. | |
202 | |
203 Attributes: | |
204 index: An index of the stack frame. | |
205 component: A component this line represents, such as blink, chrome, etc. | |
206 file_name: The name of the file that crashed. | |
207 function: The function that caused the crash. | |
208 file_path: The path of the crashed file. | |
209 crashed_line_number: The line of the file that caused the crash. | |
210 """ | |
211 | |
212 def __init__(self, stack_frame_index, component, file_name, | |
213 function, file_path, crashed_line_number): | |
214 self.index = stack_frame_index | |
215 self.component = component | |
216 self.file_name = file_name | |
217 self.function = function | |
218 self.file_path = file_path | |
219 self.crashed_line_number = crashed_line_number | |
OLD | NEW |