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

Side by Side Diff: appengine/findit/crash/stacktrace.py

Issue 2557423003: Making Stacktrace immutable, so it can be hashable (Closed)
Patch Set: rebase Created 4 years 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 2016 The Chromium Authors. All rights reserved. 1 # Copyright 2016 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 from collections import namedtuple 5 from collections import namedtuple
6 import copy 6 import copy
7 import logging 7 import logging
8 import re 8 import re
9 9
10 from crash import parse_util 10 from crash import parse_util
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 return CallStack(self.priority, 252 return CallStack(self.priority,
253 format_type=self.format_type, 253 format_type=self.format_type,
254 language_type=self.language_type, 254 language_type=self.language_type,
255 frame_list=self.frames[low_index:high_index]) 255 frame_list=self.frames[low_index:high_index])
256 256
257 257
258 # N.B., because ``list`` is mutable it isn't hashable, thus cannot be 258 # N.B., because ``list`` is mutable it isn't hashable, thus cannot be
259 # used as a key in a dict. Because we want to usecallstacks as keys (for 259 # used as a key in a dict. Because we want to usecallstacks as keys (for
260 # memoization) we has-a tuple rather than is-a list. 260 # memoization) we has-a tuple rather than is-a list.
261 # TODO(http://crbug.com/644476): this class needs a better name. 261 # TODO(http://crbug.com/644476): this class needs a better name.
262 class Stacktrace(object): 262 class Stacktrace(namedtuple('Stacktrace',
263 ['stacks', 'crash_stack', 'signature_parts'])):
263 """A collection of callstacks which together provide a trace of what happened. 264 """A collection of callstacks which together provide a trace of what happened.
264 265
265 For instance, when doing memory debugging we will have callstacks for 266 For instance, when doing memory debugging we will have callstacks for
266 (1) when the crash occurred, (2) when the object causing the crash 267 (1) when the crash occurred, (2) when the object causing the crash
267 was allocated, (3) when the object causing the crash was freed (for 268 was allocated, (3) when the object causing the crash was freed (for
268 use-after-free crashes), etc. What callstacks are included in the 269 use-after-free crashes), etc. What callstacks are included in the
269 trace is unspecified, since this differs for different tools.""" 270 trace is unspecified, since this differs for different tools."""
270 def __init__(self, stack_list=None, signature=None): 271 __slots__ = ()
271 self.stacks = stack_list or [] 272
272 self._crash_stack = None 273 def __new__(cls, stack_list=None, signature=None):
273 self._signature_parts = None 274 stacks = tuple(stack_list or [])
275
276 parts = None
274 if signature: 277 if signature:
275 # Filter out the types of signature, for example [Out of Memory]. 278 # Filter out the types of signature, for example [Out of Memory].
276 signature = re.sub('[[][^]]*[]]\s*', '', signature) 279 signature = re.sub('[[][^]]*[]]\s*', '', signature)
277 # For clusterfuzz crash, the signature is crash state. It is 280 # For clusterfuzz crash, the signature is crash state. It is
278 # usually the top 3 important stack frames separated by '\n'. 281 # usually the top 3 important stack frames separated by '\n'.
279 self._signature_parts = signature.split('\n') 282 parts = signature.split('\n')
280 283
281 def __getitem__(self, i): # pragma: no cover 284 # Get the callstack with the highest priority (i.e., whose priority
282 return self.stacks[i] 285 # field is numerically the smallest) in the stacktrace.
286 crash_stack = None
287 if not stacks:
288 logging.warning('Cannot get crash stack for empty stacktrace '
289 '(with signature: %s)' % signature)
290 else:
291 if parts:
292 def _IsSignatureCallstack(callstack):
293 for index, frame in enumerate(callstack):
294 for signature_part in parts:
295 if signature_part in frame.function:
296 return True, index
297
298 return False, 0
299
300 # Set the crash stack using signature callstack.
301 for callstack in stacks:
302 is_signature_callstack, index = _IsSignatureCallstack(callstack)
303 if is_signature_callstack:
304 # Filter all the stack frames before signature.
305 crash_stack = callstack.SliceFrames(index, None)
306 break
307
308 # If there is no signature callstack, fall back to set crash stack using
309 # the first least priority callstack.
310 if crash_stack is None:
311 crash_stack = sorted(stacks, key=lambda stack: stack.priority)[0]
312
313 return super(cls, Stacktrace).__new__(cls, stacks, crash_stack, parts)
283 314
284 def __len__(self): 315 def __len__(self):
316 """Returns the number of stacks in this trace."""
285 return len(self.stacks) 317 return len(self.stacks)
286 318
287 def __bool__(self): # pragma: no cover 319 def __bool__(self): # pragma: no cover
320 """Returns whether this trace is empty."""
288 return bool(self.stacks) 321 return bool(self.stacks)
289 322
290 def __iter__(self): 323 def __iter__(self):
324 """Iterator over the stacks in this trace."""
291 return iter(self.stacks) 325 return iter(self.stacks)
292
293 @property
294 def crash_stack(self):
295 """Get the callstack with the highest priority (i.e., whose priority
296 field is numerically the smallest) in the stacktrace."""
297 if not self.stacks:
298 logging.warning('Cannot get crash stack for empty stacktrace: %s', self)
299 return None
300
301 if self._crash_stack is None and self._signature_parts:
302 def _IsSignatureCallstack(callstack):
303 for index, frame in enumerate(callstack):
304 for signature_part in self._signature_parts:
305 if signature_part in frame.function:
306 return True, index
307
308 return False, 0
309
310 # Set the crash stack using signature callstack.
311 for callstack in self.stacks:
312 is_signature_callstack, index = _IsSignatureCallstack(callstack)
313 if is_signature_callstack:
314 # Filter all the stack frames before signature.
315 self._crash_stack = callstack.SliceFrames(index, None)
316 break
317
318 # If there is no signature callstack, fall back to set crash stack using
319 # the first least priority callstack.
320 if self._crash_stack is None:
321 self._crash_stack = sorted(self.stacks,
322 key=lambda stack: stack.priority)[0]
323
324 return self._crash_stack
OLDNEW
« no previous file with comments | « appengine/findit/crash/crash_report.py ('k') | appengine/findit/crash/test/changelist_classifier_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698