stacktrace_processor.py 5.6 KB

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