PageRenderTime 41ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/socorro/processor/signatureUtilities.py

https://github.com/hfeeki/socorro
Python | 205 lines | 183 code | 8 blank | 14 comment | 7 complexity | b9aafc87fc3104639620bd5773613707 MD5 | raw file
  1. # This Source Code Form is subject to the terms of the Mozilla Public
  2. # License, v. 2.0. If a copy of the MPL was not distributed with this
  3. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. import re
  5. #==============================================================================
  6. class SignatureTool (object):
  7. #--------------------------------------------------------------------------
  8. def __init__(self, config):
  9. self.config = config
  10. self.max_len = config.setdefault('signature_max_len', 255)
  11. self.escape_single_quote = \
  12. config.setdefault('signature_escape_single_quote', True)
  13. #--------------------------------------------------------------------------
  14. def generate(self,
  15. source_list,
  16. hang_type=0,
  17. crashed_thread=None,
  18. delimiter=' | '):
  19. signature, signature_notes = self._do_generate(source_list,
  20. hang_type,
  21. crashed_thread,
  22. delimiter)
  23. if len(signature) > self.max_len:
  24. signature = "%s..." % signature[:self.max_len - 3]
  25. signature_notes.append('SignatureTool: signature truncated due to '
  26. 'length')
  27. if self.escape_single_quote:
  28. signature = signature.replace("'", "''")
  29. return signature, signature_notes
  30. #==============================================================================
  31. class CSignatureTool (SignatureTool):
  32. hang_prefixes = {-1: "hang",
  33. 1: "chromehang"
  34. }
  35. #--------------------------------------------------------------------------
  36. def __init__(self, config):
  37. super(CSignatureTool, self).__init__(config)
  38. self.irrelevantSignatureRegEx = \
  39. re.compile(self.config.irrelevantSignatureRegEx)
  40. self.prefixSignatureRegEx = \
  41. re.compile(self.config.prefixSignatureRegEx)
  42. self.signaturesWithLineNumbersRegEx = \
  43. re.compile(self.config.signaturesWithLineNumbersRegEx)
  44. self.fixupSpace = re.compile(r' (?=[\*&,])')
  45. self.fixupComma = re.compile(r',(?! )')
  46. self.fixupInteger = re.compile(r'(<|, )(\d+)([uUlL]?)([^\w])')
  47. #--------------------------------------------------------------------------
  48. def normalize_signature(self, module_name, function, source, source_line,
  49. instruction):
  50. """ returns a structured conglomeration of the input parameters to
  51. serve as a signature
  52. """
  53. #if function is not None:
  54. if function:
  55. if self.signaturesWithLineNumbersRegEx.match(function):
  56. function = "%s:%s" % (function, source_line)
  57. # Remove spaces before all stars, ampersands, and commas
  58. function = self.fixupSpace.sub('', function)
  59. # Ensure a space after commas
  60. function = self.fixupComma.sub(', ', function)
  61. # normalize template signatures with manifest const integers to
  62. #'int': Bug 481445
  63. function = self.fixupInteger.sub(r'\1int\4', function)
  64. return function
  65. #if source is not None and source_line is not None:
  66. if source and source_line:
  67. filename = source.rstrip('/\\')
  68. if '\\' in filename:
  69. source = filename.rsplit('\\')[-1]
  70. else:
  71. source = filename.rsplit('/')[-1]
  72. return '%s#%s' % (source, source_line)
  73. if not module_name:
  74. module_name = '' # might have been None
  75. return '%s@%s' % (module_name, instruction)
  76. #--------------------------------------------------------------------------
  77. def _do_generate(self,
  78. source_list,
  79. hang_type,
  80. crashed_thread,
  81. delimiter=' | '):
  82. """
  83. each element of signatureList names a frame in the crash stack; and is:
  84. - a prefix of a relevant frame: Append this element to the signature
  85. - a relevant frame: Append this element and stop looking
  86. - irrelevant: Append this element only after seeing a prefix frame
  87. The signature is a ' | ' separated string of frame names
  88. """
  89. signature_notes = []
  90. # shorten source_list to the first signatureSentinel
  91. sentinel_locations = []
  92. for a_sentinel in self.config.signatureSentinels:
  93. if type(a_sentinel) == tuple:
  94. a_sentinel, condition_fn = a_sentinel
  95. if not condition_fn(source_list):
  96. continue
  97. try:
  98. sentinel_locations.append(source_list.index(a_sentinel))
  99. except ValueError:
  100. pass
  101. if sentinel_locations:
  102. source_list = source_list[min(sentinel_locations):]
  103. newSignatureList = []
  104. for aSignature in source_list:
  105. if self.irrelevantSignatureRegEx.match(aSignature):
  106. continue
  107. newSignatureList.append(aSignature)
  108. if not self.prefixSignatureRegEx.match(aSignature):
  109. break
  110. if hang_type:
  111. newSignatureList.insert(0, self.hang_prefixes[hang_type])
  112. signature = delimiter.join(newSignatureList)
  113. if signature == '' or signature is None:
  114. if crashed_thread is None:
  115. signature_notes.append("CSignatureTool: No signature could be "
  116. "created because we do not know which "
  117. "thread crashed")
  118. signature = "EMPTY: no crashing thread identified"
  119. else:
  120. signature_notes.append("CSignatureTool: No proper signature "
  121. "could be created because no good data "
  122. "for the crashing thread (%s) was found"
  123. % crashed_thread)
  124. try:
  125. signature = source_list[0]
  126. except IndexError:
  127. signature = "EMPTY: no frame data available"
  128. return signature, signature_notes
  129. #==============================================================================
  130. class JavaSignatureTool (SignatureTool):
  131. java_line_number_killer = re.compile(r'\.java\:\d+\)$')
  132. java_hex_addr_killer = re.compile(r'@[0-9a-f]{8}\s')
  133. #--------------------------------------------------------------------------
  134. @staticmethod
  135. def join_ignore_empty(delimiter, list_of_strings):
  136. return delimiter.join(x for x in list_of_strings if x)
  137. #--------------------------------------------------------------------------
  138. def _do_generate(self,
  139. source,
  140. hang_type_unused=0,
  141. crashed_thread_unused=None,
  142. delimiter=' '):
  143. signature_notes = []
  144. try:
  145. source_list = source.split('\n')
  146. source_list = [x.strip() for x in source_list]
  147. except AttributeError:
  148. signature_notes.append('JavaSignatureTool: stack trace not '
  149. 'in expected format')
  150. return ("EMPTY: Java stack trace not in expected format",
  151. signature_notes)
  152. try:
  153. java_exception_class, description = source_list[0].split(':', 1)
  154. java_exception_class = java_exception_class.strip() + ':'
  155. description = description.strip()
  156. except ValueError:
  157. java_exception_class = source_list[0] + ':'
  158. description = ''
  159. signature_notes.append('JavaSignatureTool: stack trace line 1 is '
  160. 'not in the expected format')
  161. try:
  162. java_method = re.sub(self.java_line_number_killer,
  163. '.java)',
  164. source_list[1])
  165. if not java_method:
  166. signature_notes.append('JavaSignatureTool: stack trace line 2 '
  167. 'is empty')
  168. except IndexError:
  169. signature_notes.append('JavaSignatureTool: stack trace line 2 is '
  170. 'missing')
  171. java_method = ''
  172. signature = self.join_ignore_empty(delimiter,
  173. (java_exception_class,
  174. description,
  175. java_method))
  176. # relace all hex addresses by the string <addr>
  177. signature = self.java_hex_addr_killer.sub(r'@<addr>: ', signature)
  178. if len(signature) > self.max_len:
  179. signature = delimiter.join((java_exception_class,
  180. java_method))
  181. # must reapply the address masking
  182. signature = self.java_hex_addr_killer.sub(r'@<addr>: ',
  183. signature)
  184. signature_notes.append('JavaSignatureTool: dropped Java exception '
  185. 'description due to length')
  186. return signature, signature_notes