| OLD | NEW |
| 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 Loading... |
| 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 | |
| OLD | NEW |