stacktrace_processor.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import copy
  2. import logging
  3. from symbolic import (
  4. Archive, SymCache, parse_addr, ProguardMapper
  5. )
  6. from symbolic.demangle import demangle_name
  7. alternative_arch = {
  8. "x86": ["x86", "x86_64"]
  9. }
  10. class ResolvedStacktrace:
  11. def __init__(self, score=0, frames=[]):
  12. self.score = score
  13. self.frames = frames
  14. def find_arch_object(archive, arch):
  15. if arch in alternative_arch:
  16. arch_list = alternative_arch[arch]
  17. else:
  18. arch_list = [arch]
  19. for arch in arch_list:
  20. try:
  21. object = archive.get_object(arch=arch)
  22. return object
  23. except Exception:
  24. pass
  25. def digest_symbol(symbol):
  26. try:
  27. item = symbol[0]
  28. if item.lang == "unknown":
  29. return None
  30. return item
  31. except Exception:
  32. pass
  33. def getLogger():
  34. return logging.getLogger("glitchtip.difs")
  35. class StacktraceProcessor:
  36. """
  37. This class process an event with exceptions. Try to load DIF and resolve
  38. the stacktrace
  39. """
  40. def __init__(self):
  41. pass
  42. @classmethod
  43. def is_supported(cls, event_json, dif):
  44. if cls.is_android_event(event_json):
  45. return dif.is_proguard_mapping()
  46. # It need more data to determine the exact condition
  47. return True
  48. @classmethod
  49. def resolve_stacktrace(cls, event, symbol_file):
  50. # Process event
  51. try:
  52. contexts = event.get("contexts")
  53. if contexts is None:
  54. # Nodejs crash report doesn't contain this field.
  55. # In future, we need to support.
  56. return
  57. arch = contexts.get("device").get("arch")
  58. # Process the first exception only.
  59. exceptions = event.get("exception").get("values")
  60. stacktrace = exceptions[0].get("stacktrace")
  61. except Exception:
  62. getLogger().error(f"StacktraceProcessor: Invalid event: {event}")
  63. return
  64. if cls.is_android_event(event):
  65. return cls.resolve_proguard_stacktrace(stacktrace, symbol_file)
  66. return cls.resolve_native_stacktrace(
  67. stacktrace,
  68. symbol_file,
  69. arch=arch
  70. )
  71. @classmethod
  72. def resolve_proguard_stacktrace(cls, stacktrace, symbol_file):
  73. try:
  74. mapper = ProguardMapper.open(symbol_file)
  75. except Exception as e:
  76. getLogger().error(
  77. f"StacktraceProcessor: Open symbol file failed: {e}")
  78. return
  79. try:
  80. frames = stacktrace.get("frames")
  81. score = 0
  82. resolved_frames = copy.copy(frames)
  83. for index, frame in enumerate(frames):
  84. frame = copy.copy(frame)
  85. module = frame.get("module")
  86. function = frame.get("function")
  87. lineno = frame.get("lineno")
  88. if lineno is None:
  89. continue
  90. result = mapper.remap_frame(module, function, lineno)
  91. if len(result) > 0:
  92. remapped_frame = result[0]
  93. frame["resolved"] = True
  94. frame["filename"] = remapped_frame.file
  95. frame["lineNo"] = remapped_frame.line
  96. frame["function"] = remapped_frame.method
  97. frame["module"] = remapped_frame.class_name
  98. score = score + 1
  99. resolved_frames[index] = frame
  100. return ResolvedStacktrace(
  101. score=score,
  102. frames=resolved_frames
  103. )
  104. except Exception as e:
  105. getLogger().error(f"StacktraceProcessor: Unexpected error: {e}")
  106. @classmethod
  107. def resolve_native_stacktrace(cls, stacktrace, symbol_file, arch=None):
  108. # Open symbol file
  109. try:
  110. archive = Archive.open(symbol_file)
  111. archive.open(symbol_file)
  112. obj = find_arch_object(archive, arch)
  113. sym_cache = SymCache.from_object(obj)
  114. except Exception as e:
  115. getLogger().error(
  116. f"StacktraceProcessor: Open symbol file failed: {e}")
  117. return
  118. try:
  119. frames = stacktrace.get("frames")
  120. score = 0
  121. resolved_frames = []
  122. for frame in frames:
  123. frame = copy.copy(frame)
  124. image_addr = parse_addr(frame.get("image_addr"))
  125. instruction_addr = parse_addr(frame.get("instruction_addr"))
  126. function = frame.get("function")
  127. addr = instruction_addr - image_addr
  128. symbol = sym_cache.lookup(addr)
  129. digested_symbol = digest_symbol(symbol)
  130. if (
  131. digested_symbol is not None and
  132. digested_symbol.symbol == function
  133. ):
  134. frame["resolved"] = True
  135. frame["filename"] = digested_symbol.filename
  136. frame["lineNo"] = digested_symbol.line
  137. frame["function"] = demangle_name(digested_symbol.symbol)
  138. score = score + 1
  139. resolved_frames.append(frame)
  140. return ResolvedStacktrace(
  141. score=score,
  142. frames=resolved_frames
  143. )
  144. except Exception as e:
  145. getLogger().error(f"StacktraceProcessor: Unexpected error: {e}")
  146. @classmethod
  147. def update_frames(cls, event, frames):
  148. try:
  149. data = event.data
  150. exceptions = data.get("exception").get("values")
  151. stacktrace = exceptions[0].get("stacktrace")
  152. stacktrace["frames"] = frames
  153. event.data = data
  154. except Exception as e:
  155. getLogger().error(f"StacktraceProcessor: Unexpected error: {e}")
  156. pass
  157. @classmethod
  158. def is_android_event(cls, event):
  159. try:
  160. return event["contexts"]["os"]["name"] == "Android"
  161. except Exception:
  162. return False