Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/sentry/seer/explorer/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,11 @@ def rpc_get_profile_flamegraph(profile_id: str, organization_id: int) -> dict[st
)
return {"error": "Failed to generate execution tree from profile data"}

# Extract thread_id from profile data
profile = profile_data.get("profile") or profile_data.get("chunk", {}).get("profile")
samples = profile.get("samples", []) if profile else []
thread_id = str(samples[0]["thread_id"]) if samples else None

return {
"execution_tree": execution_tree,
"metadata": {
Expand All @@ -590,6 +595,7 @@ def rpc_get_profile_flamegraph(profile_id: str, organization_id: int) -> dict[st
"is_continuous": is_continuous,
"start_ts": min_start_ts,
"end_ts": max_end_ts,
"thread_id": thread_id,
},
}

Expand Down
15 changes: 6 additions & 9 deletions src/sentry/seer/explorer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,12 @@ def _convert_profile_to_execution_tree(profile_data: dict) -> list[dict]:
if not all([frames, stacks, samples]):
return []

# Find the MainThread ID
thread_metadata = profile.get("thread_metadata", {}) or {}
main_thread_id = next(
(key for key, value in thread_metadata.items() if value.get("name") == "MainThread"), None
)
if (
not main_thread_id and len(thread_metadata) == 1
): # if there is only one thread, use that as the main thread
main_thread_id = list(thread_metadata.keys())[0]
# Use the thread ID from the first sample as this should be the one with the most application logic
if samples:
main_thread_id = str(samples[0]["thread_id"])
else:
# No samples - can't determine thread
main_thread_id = None

def _get_elapsed_since_start_ns(
sample: dict[str, Any], all_samples: list[dict[str, Any]]
Expand Down
8 changes: 5 additions & 3 deletions tests/sentry/seer/autofix/test_autofix.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def test_convert_profile_to_execution_tree(self) -> None:
assert len(child["children"]) == 0 # No children for the last in_app frame

def test_convert_profile_to_execution_tree_non_main_thread(self) -> None:
"""Test that non-MainThread samples are excluded from execution tree"""
"""Test that the first sample's thread is used (even if not MainThread)"""
profile_data = {
"profile": {
"frames": [
Expand All @@ -101,8 +101,10 @@ def test_convert_profile_to_execution_tree_non_main_thread(self) -> None:

execution_tree = _convert_profile_to_execution_tree(profile_data)

# Should be empty since no MainThread samples
assert len(execution_tree) == 0
# Should include the worker thread since it's the first sample's thread
assert len(execution_tree) == 1
assert execution_tree[0]["function"] == "worker"
assert execution_tree[0]["filename"] == "worker.py"

def test_convert_profile_to_execution_tree_merges_duplicate_frames(self) -> None:
"""Test that duplicate frames in different samples are merged correctly"""
Expand Down
Loading