/socorro/processor/signatureUtilities.py
Python | 205 lines | 183 code | 8 blank | 14 comment | 7 complexity | b9aafc87fc3104639620bd5773613707 MD5 | raw file
- # This Source Code Form is subject to the terms of the Mozilla Public
- # License, v. 2.0. If a copy of the MPL was not distributed with this
- # file, You can obtain one at http://mozilla.org/MPL/2.0/.
- import re
- #==============================================================================
- class SignatureTool (object):
- #--------------------------------------------------------------------------
- def __init__(self, config):
- self.config = config
- self.max_len = config.setdefault('signature_max_len', 255)
- self.escape_single_quote = \
- config.setdefault('signature_escape_single_quote', True)
- #--------------------------------------------------------------------------
- def generate(self,
- source_list,
- hang_type=0,
- crashed_thread=None,
- delimiter=' | '):
- signature, signature_notes = self._do_generate(source_list,
- hang_type,
- crashed_thread,
- delimiter)
- if len(signature) > self.max_len:
- signature = "%s..." % signature[:self.max_len - 3]
- signature_notes.append('SignatureTool: signature truncated due to '
- 'length')
- if self.escape_single_quote:
- signature = signature.replace("'", "''")
- return signature, signature_notes
- #==============================================================================
- class CSignatureTool (SignatureTool):
- hang_prefixes = {-1: "hang",
- 1: "chromehang"
- }
- #--------------------------------------------------------------------------
- def __init__(self, config):
- super(CSignatureTool, self).__init__(config)
- self.irrelevantSignatureRegEx = \
- re.compile(self.config.irrelevantSignatureRegEx)
- self.prefixSignatureRegEx = \
- re.compile(self.config.prefixSignatureRegEx)
- self.signaturesWithLineNumbersRegEx = \
- re.compile(self.config.signaturesWithLineNumbersRegEx)
- self.fixupSpace = re.compile(r' (?=[\*&,])')
- self.fixupComma = re.compile(r',(?! )')
- self.fixupInteger = re.compile(r'(<|, )(\d+)([uUlL]?)([^\w])')
- #--------------------------------------------------------------------------
- def normalize_signature(self, module_name, function, source, source_line,
- instruction):
- """ returns a structured conglomeration of the input parameters to
- serve as a signature
- """
- #if function is not None:
- if function:
- if self.signaturesWithLineNumbersRegEx.match(function):
- function = "%s:%s" % (function, source_line)
- # Remove spaces before all stars, ampersands, and commas
- function = self.fixupSpace.sub('', function)
- # Ensure a space after commas
- function = self.fixupComma.sub(', ', function)
- # normalize template signatures with manifest const integers to
- #'int': Bug 481445
- function = self.fixupInteger.sub(r'\1int\4', function)
- return function
- #if source is not None and source_line is not None:
- if source and source_line:
- filename = source.rstrip('/\\')
- if '\\' in filename:
- source = filename.rsplit('\\')[-1]
- else:
- source = filename.rsplit('/')[-1]
- return '%s#%s' % (source, source_line)
- if not module_name:
- module_name = '' # might have been None
- return '%s@%s' % (module_name, instruction)
- #--------------------------------------------------------------------------
- def _do_generate(self,
- source_list,
- hang_type,
- crashed_thread,
- delimiter=' | '):
- """
- each element of signatureList names a frame in the crash stack; and is:
- - a prefix of a relevant frame: Append this element to the signature
- - a relevant frame: Append this element and stop looking
- - irrelevant: Append this element only after seeing a prefix frame
- The signature is a ' | ' separated string of frame names
- """
- signature_notes = []
- # shorten source_list to the first signatureSentinel
- sentinel_locations = []
- for a_sentinel in self.config.signatureSentinels:
- if type(a_sentinel) == tuple:
- a_sentinel, condition_fn = a_sentinel
- if not condition_fn(source_list):
- continue
- try:
- sentinel_locations.append(source_list.index(a_sentinel))
- except ValueError:
- pass
- if sentinel_locations:
- source_list = source_list[min(sentinel_locations):]
- newSignatureList = []
- for aSignature in source_list:
- if self.irrelevantSignatureRegEx.match(aSignature):
- continue
- newSignatureList.append(aSignature)
- if not self.prefixSignatureRegEx.match(aSignature):
- break
- if hang_type:
- newSignatureList.insert(0, self.hang_prefixes[hang_type])
- signature = delimiter.join(newSignatureList)
- if signature == '' or signature is None:
- if crashed_thread is None:
- signature_notes.append("CSignatureTool: No signature could be "
- "created because we do not know which "
- "thread crashed")
- signature = "EMPTY: no crashing thread identified"
- else:
- signature_notes.append("CSignatureTool: No proper signature "
- "could be created because no good data "
- "for the crashing thread (%s) was found"
- % crashed_thread)
- try:
- signature = source_list[0]
- except IndexError:
- signature = "EMPTY: no frame data available"
- return signature, signature_notes
- #==============================================================================
- class JavaSignatureTool (SignatureTool):
- java_line_number_killer = re.compile(r'\.java\:\d+\)$')
- java_hex_addr_killer = re.compile(r'@[0-9a-f]{8}\s')
- #--------------------------------------------------------------------------
- @staticmethod
- def join_ignore_empty(delimiter, list_of_strings):
- return delimiter.join(x for x in list_of_strings if x)
- #--------------------------------------------------------------------------
- def _do_generate(self,
- source,
- hang_type_unused=0,
- crashed_thread_unused=None,
- delimiter=' '):
- signature_notes = []
- try:
- source_list = source.split('\n')
- source_list = [x.strip() for x in source_list]
- except AttributeError:
- signature_notes.append('JavaSignatureTool: stack trace not '
- 'in expected format')
- return ("EMPTY: Java stack trace not in expected format",
- signature_notes)
- try:
- java_exception_class, description = source_list[0].split(':', 1)
- java_exception_class = java_exception_class.strip() + ':'
- description = description.strip()
- except ValueError:
- java_exception_class = source_list[0] + ':'
- description = ''
- signature_notes.append('JavaSignatureTool: stack trace line 1 is '
- 'not in the expected format')
- try:
- java_method = re.sub(self.java_line_number_killer,
- '.java)',
- source_list[1])
- if not java_method:
- signature_notes.append('JavaSignatureTool: stack trace line 2 '
- 'is empty')
- except IndexError:
- signature_notes.append('JavaSignatureTool: stack trace line 2 is '
- 'missing')
- java_method = ''
- signature = self.join_ignore_empty(delimiter,
- (java_exception_class,
- description,
- java_method))
- # relace all hex addresses by the string <addr>
- signature = self.java_hex_addr_killer.sub(r'@<addr>: ', signature)
- if len(signature) > self.max_len:
- signature = delimiter.join((java_exception_class,
- java_method))
- # must reapply the address masking
- signature = self.java_hex_addr_killer.sub(r'@<addr>: ',
- signature)
- signature_notes.append('JavaSignatureTool: dropped Java exception '
- 'description due to length')
- return signature, signature_notes