test_payload_full.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import re
  2. import zipfile
  3. from io import BytesIO
  4. from unittest.mock import patch
  5. import pytest
  6. from django.core.files.uploadedfile import SimpleUploadedFile
  7. from django.urls import reverse
  8. from sentry import eventstore
  9. from sentry.models import File, ProjectDebugFile
  10. from sentry.testutils import RelayStoreHelper, TransactionTestCase
  11. from sentry.testutils.factories import get_fixture_path
  12. from sentry.testutils.helpers.datetime import before_now, iso_format
  13. from tests.symbolicator import insta_snapshot_stacktrace_data
  14. # IMPORTANT:
  15. # For these tests to run, write `symbolicator.enabled: true` into your
  16. # `~/.sentry/config.yml` and run `sentry devservices up`
  17. REAL_RESOLVING_EVENT_DATA = {
  18. "platform": "cocoa",
  19. "debug_meta": {
  20. "images": [
  21. {
  22. "type": "apple",
  23. "arch": "x86_64",
  24. "uuid": "502fc0a5-1ec1-3e47-9998-684fa139dca7",
  25. "image_vmaddr": "0x0000000100000000",
  26. "image_size": 4096,
  27. "image_addr": "0x0000000100000000",
  28. "name": "Foo.app/Contents/Foo",
  29. }
  30. ],
  31. "sdk_info": {
  32. "dsym_type": "macho",
  33. "sdk_name": "macOS",
  34. "version_major": 10,
  35. "version_minor": 12,
  36. "version_patchlevel": 4,
  37. },
  38. },
  39. "exception": {
  40. "values": [
  41. {
  42. "stacktrace": {
  43. "frames": [
  44. {"platform": "foobar", "function": "hi"},
  45. {"function": "unknown", "instruction_addr": "0x0000000100000fa0"},
  46. ]
  47. },
  48. "type": "Fail",
  49. "value": "fail",
  50. }
  51. ]
  52. },
  53. "timestamp": iso_format(before_now(seconds=1)),
  54. }
  55. class SymbolicatorResolvingIntegrationTest(RelayStoreHelper, TransactionTestCase):
  56. # For these tests to run, write `symbolicator.enabled: true` into your
  57. # `~/.sentry/config.yml` and run `sentry devservices up`
  58. @pytest.fixture(autouse=True)
  59. def initialize(self, live_server):
  60. self.project.update_option("sentry:builtin_symbol_sources", [])
  61. new_prefix = live_server.url
  62. with patch("sentry.auth.system.is_internal_ip", return_value=True), self.options(
  63. {"system.url-prefix": new_prefix}
  64. ):
  65. # Run test case:
  66. yield
  67. def get_event(self, event_id):
  68. return eventstore.get_event_by_id(self.project.id, event_id)
  69. def test_real_resolving(self):
  70. url = reverse(
  71. "sentry-api-0-dsym-files",
  72. kwargs={
  73. "organization_slug": self.project.organization.slug,
  74. "project_slug": self.project.slug,
  75. },
  76. )
  77. self.login_as(user=self.user)
  78. out = BytesIO()
  79. f = zipfile.ZipFile(out, "w")
  80. f.write(get_fixture_path("native", "hello.dsym"), "dSYM/hello")
  81. f.close()
  82. response = self.client.post(
  83. url,
  84. {
  85. "file": SimpleUploadedFile(
  86. "symbols.zip", out.getvalue(), content_type="application/zip"
  87. )
  88. },
  89. format="multipart",
  90. )
  91. assert response.status_code == 201, response.content
  92. assert len(response.data) == 1
  93. with self.feature({"organizations:images-loaded-v2": False}):
  94. event = self.post_and_retrieve_event(REAL_RESOLVING_EVENT_DATA)
  95. assert event.data["culprit"] == "main"
  96. insta_snapshot_stacktrace_data(self, event.data)
  97. def test_debug_id_resolving(self):
  98. file = File.objects.create(
  99. name="crash.pdb", type="default", headers={"Content-Type": "text/x-breakpad"}
  100. )
  101. path = get_fixture_path("native", "windows.sym")
  102. with open(path, "rb") as f:
  103. file.putfile(f)
  104. ProjectDebugFile.objects.create(
  105. file=file,
  106. object_name="crash.pdb",
  107. cpu_name="x86",
  108. project_id=self.project.id,
  109. debug_id="3249d99d-0c40-4931-8610-f4e4fb0b6936-1",
  110. code_id="5AB380779000",
  111. )
  112. self.login_as(user=self.user)
  113. event_data = {
  114. "contexts": {
  115. "device": {"arch": "x86"},
  116. "os": {"build": "", "name": "Windows", "type": "os", "version": "10.0.14393"},
  117. },
  118. "debug_meta": {
  119. "images": [
  120. {
  121. "id": "3249d99d-0c40-4931-8610-f4e4fb0b6936-1",
  122. "image_addr": "0x2a0000",
  123. "image_size": 36864,
  124. "name": "C:\\projects\\breakpad-tools\\windows\\Release\\crash.exe",
  125. "type": "symbolic",
  126. }
  127. ]
  128. },
  129. "exception": {
  130. "stacktrace": {
  131. "frames": [
  132. {
  133. "function": "<unknown>",
  134. "instruction_addr": "0x2a2a3d",
  135. "package": "C:\\projects\\breakpad-tools\\windows\\Release\\crash.exe",
  136. }
  137. ]
  138. },
  139. "thread_id": 1636,
  140. "type": "EXCEPTION_ACCESS_VIOLATION_WRITE",
  141. "value": "Fatal Error: EXCEPTION_ACCESS_VIOLATION_WRITE",
  142. },
  143. "platform": "native",
  144. "timestamp": iso_format(before_now(seconds=1)),
  145. }
  146. with self.feature({"organizations:images-loaded-v2": False}):
  147. event = self.post_and_retrieve_event(event_data)
  148. assert event.data["culprit"] == "main"
  149. insta_snapshot_stacktrace_data(self, event.data)
  150. def test_missing_dsym(self):
  151. self.login_as(user=self.user)
  152. with self.feature({"organizations:images-loaded-v2": False}):
  153. event = self.post_and_retrieve_event(REAL_RESOLVING_EVENT_DATA)
  154. assert event.data["culprit"] == "unknown"
  155. insta_snapshot_stacktrace_data(self, event.data)
  156. def test_missing_debug_images(self):
  157. self.login_as(user=self.user)
  158. payload = dict(project=self.project.id, **REAL_RESOLVING_EVENT_DATA)
  159. del payload["debug_meta"]
  160. with self.feature({"organizations:images-loaded-v2": False}):
  161. event = self.post_and_retrieve_event(payload)
  162. assert event.data["culprit"] == "unknown"
  163. insta_snapshot_stacktrace_data(self, event.data)
  164. def test_resolving_with_candidates_sentry_source(self):
  165. # Checks the candidates with a sentry source URI for location
  166. file = File.objects.create(
  167. name="crash.pdb", type="default", headers={"Content-Type": "text/x-breakpad"}
  168. )
  169. path = get_fixture_path("native", "windows.sym")
  170. with open(path, "rb") as f:
  171. file.putfile(f)
  172. ProjectDebugFile.objects.create(
  173. file=file,
  174. object_name="crash.pdb",
  175. cpu_name="x86",
  176. project_id=self.project.id,
  177. debug_id="3249d99d-0c40-4931-8610-f4e4fb0b6936-1",
  178. code_id="5AB380779000",
  179. )
  180. self.login_as(user=self.user)
  181. event_data = {
  182. "contexts": {
  183. "device": {"arch": "x86"},
  184. },
  185. "debug_meta": {
  186. "images": [
  187. {
  188. "id": "3249d99d-0c40-4931-8610-f4e4fb0b6936-1",
  189. "image_addr": "0x2a0000",
  190. "image_size": 36864,
  191. "name": "C:\\projects\\breakpad-tools\\windows\\Release\\crash.exe",
  192. "type": "symbolic",
  193. }
  194. ]
  195. },
  196. "exception": {
  197. "stacktrace": {
  198. "frames": [
  199. {
  200. "instruction_addr": "0x2a2a3d",
  201. }
  202. ]
  203. },
  204. "type": "EXCEPTION_ACCESS_VIOLATION_WRITE",
  205. "value": "Fatal Error: EXCEPTION_ACCESS_VIOLATION_WRITE",
  206. },
  207. "platform": "native",
  208. "timestamp": iso_format(before_now(seconds=1)),
  209. }
  210. with self.feature("organizations:images-loaded-v2"):
  211. event = self.post_and_retrieve_event(event_data)
  212. assert event.data["culprit"] == "main"
  213. candidates = event.data["debug_meta"]["images"][0]["candidates"]
  214. redact_location(candidates)
  215. self.insta_snapshot(candidates)
  216. def redact_location(candidates):
  217. """Redacts the sentry location URI to be independent of the specific ID.
  218. This modifies the data passed in, returns None.
  219. """
  220. location_re = re.compile("^sentry://project_debug_file/[0-9]+$")
  221. for candidate in candidates:
  222. try:
  223. location = candidate["location"]
  224. except KeyError:
  225. continue
  226. else:
  227. if location_re.search(location):
  228. candidate["location"] = "sentry://project_debug_file/x"