Skip to content

Commit 9d3538d

Browse files
committed
feat(integrations): Resolve stack trace links at a frame's file revision
Native frames symbolicated with source server info carry a per-file revision (a Perforce changelist for srcsrv-indexed PDBs). Accept it as fileRevision on the stacktrace-link and stacktrace-source-context endpoints and prefer it over the release's commitId, so links and fetched source context point at the revision the binary was built from.
1 parent 58e2eee commit 9d3538d

3 files changed

Lines changed: 26 additions & 1 deletion

File tree

‎src/sentry/issues/endpoints/project_stacktrace_link.py‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ def generate_context(parameters: QueryDict) -> StacktraceLinkContext:
7272
"file": parameters.get("file", ""),
7373
# XXX: Temp change to support try_path_munging until refactored
7474
"filename": parameters.get("file", ""),
75-
"commit_id": parameters.get("commitId"),
75+
# A frame's source server revision pins its file more precisely than the release commit.
76+
"commit_id": parameters.get("fileRevision") or parameters.get("commitId"),
7677
"platform": parameters.get("platform"),
7778
"sdk_name": parameters.get("sdkName"),
7879
"abs_path": parameters.get("absPath"),
@@ -175,6 +176,8 @@ class ProjectStacktraceLinkEndpoint(ProjectEndpoint):
175176
`file`: The file path from the stack trace
176177
`commitId` (optional): The commit_id for the last commit of the
177178
release associated to the stack trace's event
179+
`fileRevision` (optional): The source server revision of the frame's file.
180+
Takes precedence over `commitId`.
178181
`sdkName` (optional): The sdk.name associated with the event
179182
`absPath` (optional): The abs_path field value of the relevant stack frame
180183
`module` (optional): The module field value of the relevant stack frame
@@ -209,6 +212,14 @@ class ProjectStacktraceLinkEndpoint(ProjectEndpoint):
209212
OpenApiParameter(
210213
"commitId", type=str, description="The commit ID associated with the event."
211214
),
215+
OpenApiParameter(
216+
"fileRevision",
217+
type=str,
218+
description=(
219+
"The source server revision of the frame's file, if known. "
220+
"Takes precedence over `commitId`."
221+
),
222+
),
212223
OpenApiParameter(
213224
"sdkName", type=str, description="The SDK name associated with the event."
214225
),

‎src/sentry/issues/endpoints/project_stacktrace_source_context.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class ProjectStacktraceSourceContextEndpoint(ProjectEndpoint):
2929
`file`: The file path from the stack trace (required)
3030
`lineNo`: The line number to fetch context around (required)
3131
`commitId` (optional): The commit_id for the last commit of the release
32+
`fileRevision` (optional): The source server revision of the frame's file.
33+
Takes precedence over `commitId`.
3234
`platform` (optional): The platform of the event
3335
`sdkName` (optional): The sdk.name associated with the event
3436
`absPath` (optional): The abs_path field value of the relevant stack frame

‎tests/sentry/issues/endpoints/test_project_stacktrace_link.py‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,18 @@ def test_config_and_source_url(self) -> None:
209209
assert response.data["sourceUrl"] == "https://sourceurl.com/"
210210
assert response.data["integrations"] == [serialized_integration(self.integration)]
211211

212+
@patch.object(ExampleIntegration, "get_stacktrace_link", return_value="https://sourceurl.com/")
213+
def test_file_revision_takes_precedence_over_commit_id(
214+
self, mock_get_stacktrace_link: MagicMock
215+
) -> None:
216+
self._create_code_mapping(stack_root="usr/src/getsentry/", source_root="")
217+
self.get_success_response(
218+
self.organization.slug,
219+
self.project.slug,
220+
qs_params={"file": self.filepath, "commitId": "abc123", "fileRevision": "12345"},
221+
)
222+
assert mock_get_stacktrace_link.call_args.args[3] == "12345"
223+
212224
@patch.object(ExampleIntegration, "get_stacktrace_link")
213225
def test_file_no_stack_root_match(self, mock_integration: MagicMock) -> None:
214226
# Pretend that the file was not found in the repository

0 commit comments

Comments
 (0)