Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: tools/findit/stacktrace.py

Issue 478763003: [Findit] Bug fixing and implemented some feature requests. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixed a bug in intersection Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright (c) 2014 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import os
6 import re 5 import re
7 6
8 import crash_utils 7 import crash_utils
9 8
10 9
10 SYZYASAN_STACK_FRAME_PATTERN = re.compile(
11 r'(CF: )?(.*?)( \(FPO: .*\) )?( \(CONV: .*\) )?\[(.*) @ (\d+)\]')
12 FILE_PATH_AND_LINE_PATTERN = re.compile(r'(.*?):(\d+)(:\d+)?')
13
14
11 class StackFrame(object): 15 class StackFrame(object):
12 """Represents a frame in stacktrace. 16 """Represents a frame in stacktrace.
13 17
14 Attributes: 18 Attributes:
15 index: An index of the stack frame. 19 index: An index of the stack frame.
16 component_path: The path of the component this frame represents. 20 component_path: The path of the component this frame represents.
17 component_name: The name of the component this frame represents. 21 component_name: The name of the component this frame represents.
18 file_name: The name of the file that crashed. 22 file_name: The name of the file that crashed.
19 function: The function that caused the crash. 23 function: The function that caused the crash.
20 file_path: The path of the crashed file. 24 file_path: The path of the crashed file.
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 56
53 class Stacktrace(object): 57 class Stacktrace(object):
54 """Represents Stacktrace object. 58 """Represents Stacktrace object.
55 59
56 Contains a list of callstacks, because one stacktrace might have more than 60 Contains a list of callstacks, because one stacktrace might have more than
57 one callstacks. 61 one callstacks.
58 """ 62 """
59 63
60 def __init__(self, stacktrace, build_type, parsed_deps): 64 def __init__(self, stacktrace, build_type, parsed_deps):
61 self.stack_list = None 65 self.stack_list = None
62 self.parsed_deps = parsed_deps 66 self.ParseStacktrace(stacktrace, build_type, parsed_deps)
63 self.ParseStacktrace(stacktrace, build_type)
64 67
65 def ParseStacktrace(self, stacktrace, build_type): 68 def ParseStacktrace(self, stacktrace, build_type, parsed_deps):
66 """Parses stacktrace and normalizes it. 69 """Parses stacktrace and normalizes it.
67 70
68 If there are multiple callstacks within the stacktrace, 71 If there are multiple callstacks within the stacktrace,
69 it will parse each of them separately, and store them in the stack_list 72 it will parse each of them separately, and store them in the stack_list
70 variable. 73 variable.
71 74
72 Args: 75 Args:
73 stacktrace: A string containing stacktrace. 76 stacktrace: A string containing stacktrace.
74 build_type: A string containing the build type of the crash. 77 build_type: A string containing the build type of the crash.
78 parsed_deps: A parsed DEPS file to normalize path with.
75 """ 79 """
76 # If the passed in string is empty, the object does not represent anything. 80 # If the passed in string is empty, the object does not represent anything.
77 if not stacktrace: 81 if not stacktrace:
78 return 82 return
79
80 # Reset the stack list. 83 # Reset the stack list.
81 self.stack_list = [] 84 self.stack_list = []
82 reached_new_callstack = False 85 reached_new_callstack = False
83 # Note that we do not need exact stack frame index, we only need relative 86 # Note that we do not need exact stack frame index, we only need relative
84 # position of a frame within a callstack. The reason for not extracting 87 # position of a frame within a callstack. The reason for not extracting
85 # index from a line is that some stack frames do not have index. 88 # index from a line is that some stack frames do not have index.
86 stack_frame_index = 0 89 stack_frame_index = 0
87 current_stack = None 90 current_stack = CallStack(-1)
Martin Barbella 2014/08/22 02:24:14 This seems pretty hacky. What was the issue here?
jeun 2014/08/22 22:58:44 This is to bypass UBSan (and possibly some other b
88 91
89 for line in stacktrace: 92 for line in stacktrace:
93 line = line.strip()
90 (is_new_callstack, stack_priority) = self.__IsStartOfNewCallStack( 94 (is_new_callstack, stack_priority) = self.__IsStartOfNewCallStack(
91 line, build_type) 95 line, build_type)
92
93 if is_new_callstack: 96 if is_new_callstack:
94 97
95 # If this callstack is crash stack, update the boolean. 98 # If this callstack is crash stack, update the boolean.
96 if not reached_new_callstack: 99 if not reached_new_callstack:
97 reached_new_callstack = True 100 reached_new_callstack = True
98 current_stack = CallStack(stack_priority) 101 current_stack = CallStack(stack_priority)
99 102
100 # If this is from freed or allocation, add the callstack we have 103 # If this is from freed or allocation, add the callstack we have
101 # to the list of callstacks, and increment the stack priority. 104 # to the list of callstacks, and increment the stack priority.
102 else: 105 else:
103 stack_frame_index = 0 106 stack_frame_index = 0
104 if current_stack and current_stack.frame_list: 107 if current_stack and current_stack.frame_list:
105 self.stack_list.append(current_stack) 108 self.stack_list.append(current_stack)
106 current_stack = CallStack(stack_priority) 109 current_stack = CallStack(stack_priority)
107 110
108 # Generate stack frame object from the line. 111 # Generate stack frame object from the line.
109 parsed_stack_frame = self.__GenerateStackFrame( 112 parsed_stack_frame = self.__GenerateStackFrame(
110 stack_frame_index, line, build_type) 113 stack_frame_index, line, build_type, parsed_deps)
111 114
112 # If the line does not represent the stack frame, ignore this line. 115 # If the line does not represent the stack frame, ignore this line.
113 if not parsed_stack_frame: 116 if not (parsed_stack_frame and current_stack):
Martin Barbella 2014/08/22 02:24:14 With the other change, would the "and current_stac
jeun 2014/08/22 22:58:44 No, removed.
114 continue 117 continue
115 118
116 # Add the parsed stack frame object to the current stack. 119 # Add the parsed stack frame object to the current stack.
117 current_stack.Add(parsed_stack_frame) 120 current_stack.Add(parsed_stack_frame)
118 stack_frame_index += 1 121 stack_frame_index += 1
119 122
120 # Add the current callstack only if there are frames in it. 123 # Add the current callstack only if there are frames in it.
121 if current_stack and current_stack.frame_list: 124 if current_stack and current_stack.frame_list:
122 self.stack_list.append(current_stack) 125 self.stack_list.append(current_stack)
123 126
124 def __IsStartOfNewCallStack(self, line, build_type): 127 def __IsStartOfNewCallStack(self, line, build_type):
125 """Check if this line is the start of the new callstack. 128 """Check if this line is the start of the new callstack.
126 129
127 Since each builds have different format of stacktrace, the logic for 130 Since each builds have different format of stacktrace, the logic for
128 checking the line for all builds is handled in here. 131 checking the line for all builds is handled in here.
129 132
130 Args: 133 Args:
131 line: Line to check for. 134 line: Line to check for.
132 build_type: The name of the build. 135 build_type: The name of the build.
133 136
134 Returns: 137 Returns:
135 True if the line is the start of new callstack, False otherwise. If True, 138 True if the line is the start of new callstack, False otherwise. If True,
136 it also returns the priority of the line. 139 it also returns the priority of the line.
137 """ 140 """
138 # Currently not supported. 141 if 'syzyasan' in build_type:
139 if 'android' in build_type:
140 pass
141
142 elif 'syzyasan' in build_type:
143 # In syzyasan build, new stack starts with 'crash stack:', 142 # In syzyasan build, new stack starts with 'crash stack:',
144 # 'freed stack:', etc. 143 # 'freed stack:', etc.
145 callstack_start_pattern = re.compile(r'^(.*) stack:$') 144 callstack_start_pattern = re.compile(r'^(.*) stack:$')
146 match = callstack_start_pattern.match(line) 145 match = callstack_start_pattern.match(line)
147 146
148 # If the line matches the callstack start pattern. 147 # If the line matches the callstack start pattern.
149 if match: 148 if match:
150 # Check the type of the new match. 149 # Check the type of the new match.
151 stack_type = match.group(1) 150 stack_type = match.group(1)
152 151
153 # Crash stack gets priority 0. 152 # Crash stack gets priority 0.
154 if stack_type == 'Crash': 153 if stack_type == 'Crash':
155 return (True, 0) 154 return (True, 0)
156 155
157 # Other callstacks all get priority 1. 156 # Other callstacks all get priority 1.
158 else: 157 else:
159 return (True, 1) 158 return (True, 1)
160 159
161 elif 'tsan' in build_type: 160 elif 'tsan' in build_type:
162 # Create patterns for each callstack type. 161 # Create patterns for each callstack type.
163 crash_callstack_start_pattern = re.compile( 162 crash_callstack_start_pattern1 = re.compile(
164 r'^(Read|Write) of size \d+') 163 r'^(Read|Write) of size \d+')
165 164
165 crash_callstack_start_pattern2 = re.compile(
166 r'^[A-Z]+: ThreadSanitizer')
167
166 allocation_callstack_start_pattern = re.compile( 168 allocation_callstack_start_pattern = re.compile(
167 r'^Previous (write|read) of size \d+') 169 r'^Previous (write|read) of size \d+')
168 170
169 location_callstack_start_pattern = re.compile( 171 location_callstack_start_pattern = re.compile(
170 r'^Location is heap block of size \d+') 172 r'^Location is heap block of size \d+')
171 173
172 # Crash stack gets priority 0. 174 # Crash stack gets priority 0.
173 if crash_callstack_start_pattern.match(line): 175 if (crash_callstack_start_pattern1.match(line) or
176 crash_callstack_start_pattern2.match(line)):
174 return (True, 0) 177 return (True, 0)
175 178
176 # All other stacks get priority 1. 179 # All other stacks get priority 1.
177 if allocation_callstack_start_pattern.match(line): 180 if allocation_callstack_start_pattern.match(line):
178 return (True, 1) 181 return (True, 1)
179 182
180 if location_callstack_start_pattern.match(line): 183 if location_callstack_start_pattern.match(line):
181 return (True, 1) 184 return (True, 1)
182 185
183 else: 186 else:
184 # In asan and other build types, crash stack can start 187 # In asan and other build types, crash stack can start
185 # in two different ways. 188 # in two different ways.
186 crash_callstack_start_pattern1 = re.compile(r'^==\d+== ?ERROR:') 189 crash_callstack_start_pattern1 = re.compile(r'^==\d+== ?[A-Z]+:')
187 crash_callstack_start_pattern2 = re.compile( 190 crash_callstack_start_pattern2 = re.compile(
188 r'^(READ|WRITE) of size \d+ at') 191 r'^(READ|WRITE) of size \d+ at')
192 crash_callstack_start_pattern3 = re.compile(r'^backtrace:')
189 193
190 freed_callstack_start_pattern = re.compile( 194 freed_callstack_start_pattern = re.compile(
191 r'^freed by thread T\d+ (.* )?here:') 195 r'^freed by thread T\d+ (.* )?here:')
192 196
193 allocation_callstack_start_pattern = re.compile( 197 allocation_callstack_start_pattern = re.compile(
194 r'^previously allocated by thread T\d+ (.* )?here:') 198 r'^previously allocated by thread T\d+ (.* )?here:')
195 199
196 other_callstack_start_pattern = re.compile( 200 other_callstack_start_pattern = re.compile(
197 r'^Thread T\d+ (.* )?created by') 201 r'^Thread T\d+ (.* )?created by')
198 202
199 # Crash stack gets priority 0. 203 # Crash stack gets priority 0.
200 if (crash_callstack_start_pattern1.match(line) or 204 if (crash_callstack_start_pattern1.match(line) or
201 crash_callstack_start_pattern2.match(line)): 205 crash_callstack_start_pattern2.match(line) or
206 crash_callstack_start_pattern3.match(line)):
202 return (True, 0) 207 return (True, 0)
203 208
204 # All other callstack gets priority 1. 209 # All other callstack gets priority 1.
205 if freed_callstack_start_pattern.match(line): 210 if freed_callstack_start_pattern.match(line):
206 return (True, 1) 211 return (True, 1)
207 212
208 if allocation_callstack_start_pattern.match(line): 213 if allocation_callstack_start_pattern.match(line):
209 return (True, 1) 214 return (True, 1)
210 215
211 if other_callstack_start_pattern.match(line): 216 if other_callstack_start_pattern.match(line):
212 return (True, 1) 217 return (True, 1)
213 218
214 # If the line does not match any pattern, return false and a dummy for 219 # If the line does not match any pattern, return false and a dummy for
215 # stack priority. 220 # stack priority.
216 return (False, -1) 221 return (False, -1)
217 222
218 def __GenerateStackFrame(self, stack_frame_index, line, build_type): 223 def __GenerateStackFrame(self, stack_frame_index, line, build_type,
224 parsed_deps):
219 """Extracts information from a line in stacktrace. 225 """Extracts information from a line in stacktrace.
220 226
221 Args: 227 Args:
222 stack_frame_index: A stack frame index of this line. 228 stack_frame_index: A stack frame index of this line.
223 line: A stacktrace string to extract data from. 229 line: A stacktrace string to extract data from.
224 build_type: A string containing the build type 230 build_type: A string containing the build type
225 of this crash (e.g. linux_asan_chrome_mp). 231 of this crash (e.g. linux_asan_chrome_mp).
232 parsed_deps: A parsed DEPS file to normalize path with.
226 233
227 Returns: 234 Returns:
228 A triple containing the name of the function, the path of the file and 235 A triple containing the name of the function, the path of the file and
229 the crashed line number. 236 the crashed line number.
230 """ 237 """
231 line_parts = line.split() 238 line_parts = line.split()
239 try:
232 240
233 try: 241 if 'syzyasan' in build_type:
234 # Filter out lines that are not stack frame. 242 stack_frame_match = SYZYASAN_STACK_FRAME_PATTERN.match(line)
235 stack_frame_index_pattern = re.compile(r'#(\d+)')
236 if not stack_frame_index_pattern.match(line_parts[0]):
237 return None
238 243
239 # Tsan has different stack frame style from other builds. 244 if not stack_frame_match:
240 if build_type.startswith('linux_tsan'): 245 return None
241 file_path_and_line = line_parts[-2] 246 file_path = stack_frame_match.group(5)
242 function = ' '.join(line_parts[1:-2]) 247 crashed_line_number = [int(stack_frame_match.group(6))]
Martin Barbella 2014/08/22 02:24:14 Maybe rename this here and in StackFrame to crashe
jeun 2014/08/22 22:58:44 Done.
248 function = stack_frame_match.group(2)
243 249
244 else: 250 else:
245 file_path_and_line = line_parts[-1] 251 if not line_parts[0].startswith('#'):
246 function = ' '.join(line_parts[3:-1]) 252 return None
247 253
248 # Get file path and line info from the line. 254 if 'tsan' in build_type:
249 file_path_and_line = file_path_and_line.split(':') 255 file_path_and_line = line_parts[-2]
250 file_path = file_path_and_line[0] 256 function = ' '.join(line_parts[1:-2])
251 crashed_line_number = int(file_path_and_line[1]) 257 else:
258 file_path_and_line = line_parts[-1]
259 function = ' '.join(line_parts[3:-1])
260
261 # Get file path and line info from the line.
262 file_path_and_line_match = FILE_PATH_AND_LINE_PATTERN.match(
263 file_path_and_line)
264
265 # Return None if the file path information is not available
266 if not file_path_and_line_match:
267 return None
268
269 file_path = file_path_and_line_match.group(1)
270
271 # Get the crashed line range. For example, file_path:line_number:range.
272 crashed_line_range = file_path_and_line_match.group(3)
273
274 if crashed_line_range:
275 # Strip ':' prefix.
276 crashed_line_range = int(crashed_line_range[1:])
277 else:
278 crashed_line_range = 0
279
280 crashed_line_number = int(file_path_and_line_match.group(2))
281 # For example, 655:1 has crashed lines 655 and 656.
stgao 2014/08/22 06:50:54 How did we know ":1" mean a range? Is it a functio
jeun 2014/08/22 22:58:44 Done.
282 crashed_line_number = \
283 range(crashed_line_number,
284 crashed_line_number + crashed_line_range + 1)
252 285
253 # Return None if the line is malformed. 286 # Return None if the line is malformed.
254 except IndexError: 287 except IndexError:
255 return None 288 return None
256 except ValueError: 289 except ValueError:
257 return None 290 return None
258 291
259 # Normalize the file path so that it can be compared to repository path. 292 # Normalize the file path so that it can be compared to repository path.
260 file_name = os.path.basename(file_path)
261 (component_path, component_name, file_path) = ( 293 (component_path, component_name, file_path) = (
262 crash_utils.NormalizePathLinux(file_path, self.parsed_deps)) 294 crash_utils.NormalizePath(file_path, parsed_deps))
263
264 # If this component is not supported, ignore this line.
265 if not component_path:
266 return None
267 295
268 # Return a new stack frame object with the parsed information. 296 # Return a new stack frame object with the parsed information.
297 file_name = file_path.split('/')[-1]
stgao 2014/08/22 06:50:54 os.path.basename
jeun 2014/08/22 22:58:44 This changed to split('/')[-1] so that the code wi
269 return StackFrame(stack_frame_index, component_path, component_name, 298 return StackFrame(stack_frame_index, component_path, component_name,
270 file_name, function, file_path, crashed_line_number) 299 file_name, function, file_path, crashed_line_number)
271 300
272 def __getitem__(self, index): 301 def __getitem__(self, index):
273 return self.stack_list[index] 302 return self.stack_list[index]
274 303
275 def GetCrashStack(self): 304 def GetCrashStack(self):
276 """Returns the callstack with the highest priority. 305 """Returns the callstack with the highest priority.
277 306
278 Crash stack has priority 0, and allocation/freed/other thread stacks 307 Crash stack has priority 0, and allocation/freed/other thread stacks
279 get priority 1. 308 get priority 1.
280 309
281 Returns: 310 Returns:
282 The highest priority callstack in the stacktrace. 311 The highest priority callstack in the stacktrace.
283 """ 312 """
284 sorted_stacklist = sorted(self.stack_list, 313 sorted_stacklist = sorted(self.stack_list,
285 key=lambda callstack: callstack.priority) 314 key=lambda callstack: callstack.priority)
286 return sorted_stacklist[0] 315 return sorted_stacklist[0]
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698