Skip to content
Merged
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
41 changes: 22 additions & 19 deletions develop-docs/sdk/telemetry/attachments/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ When using `Crashpad` as the crash-capturing backend in the Native SDK, the file

SDKs **MAY** attach a `flamegraph.json` file containing aggregated call trees, such as those captured for application hangs. The attachment **MUST** use `attachment_type: "event.flamegraph"` and `content_type: "application/json"`.

The format reuses profiling frame objects and debug metadata to support symbolication. Tree nodes describe aggregated call paths and sample counts.
The format reuses profiling frame objects and debug metadata to support symbolication. A flat node table describes aggregated call paths and sample counts. Child references use node indices to avoid deeply nested JSON and allow iterative processing without recursive traversal.

#### Flame Graph Attachment Transport

Expand All @@ -190,13 +190,14 @@ Event context such as release, environment, SDK identity, device, OS, and hang d

#### Flame Graph Payload

The top-level object describes one collection of call trees and their shared frame table.
The top-level object describes one collection of call trees and their shared frame and node tables.

| Field | Type | Requirement | Description |
| ------------ | ---------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `version` | String | **REQUIRED** | `"1"` for this wire format. |
| `platform` | String | **REQUIRED** | Existing Sentry platform identifier. Used as the default platform for frames. Native Apple frames use `"cocoa"`. |
| `frames` | Array of frame objects | **REQUIRED** | Non-empty frame table using the same frame objects as a [V2 profile chunk](/sdk/telemetry/profiles/#profile-data). |
| `nodes` | Array of node objects | **REQUIRED** | Non-empty flat node table. Child and root references are zero-based indices into this array. |
| `trees` | Array of tree objects | **REQUIRED** | Non-empty collection of independent call trees. |
| `debug_meta` | Debug Meta object | **REQUIRED** for native frames with known images | Existing debug metadata for the process that produced the diagnostic. See [Native Symbolication](#flame-graph-native-symbolication). |

Expand All @@ -216,21 +217,23 @@ A tree object groups root nodes that share the same sample population. Each obje

| Field | Type | Requirement | Description |
| ------------------- | -------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `roots` | Array of nodes | **REQUIRED** | Non-empty forest of call paths. Roots are the oldest retained callers. |
| `roots` | Array of integers | **REQUIRED** | Non-empty list of zero-based indices into `nodes`. Roots are the oldest retained callers. |
| `thread_id` | String | **OPTIONAL** | Actual source thread identifier, if known. Uses the string representation used by profiling samples. |
| `thread_attributed` | Boolean | **OPTIONAL** | Whether the diagnostic source identifies this tree's thread as responsible for the reported issue, such as a hang. Omit when unknown. |

Each tree is an independent sample population. Producers **MUST** include `thread_id` only when the source provides a thread identifier.

##### Flame Graph Nodes

Each node describes one frame occurrence in a call path.
Each entry in the top-level `nodes` array describes one frame occurrence in a call path.

| Field | Type | Requirement | Description |
| -------------- | -------------- | ------------ | -------------------------------------------------------------------------------- |
| `frame_id` | Integer | **REQUIRED** | Zero-based index into `frames`. **MUST** be between `0` and `frames.length - 1`. |
| `sample_count` | Integer | **REQUIRED** | Positive inclusive count of samples containing this call-path prefix. |
| `children` | Array of nodes | **OPTIONAL** | Direct callees. Defaults to `[]` when omitted. |
| `children` | Array of integers | **OPTIONAL** | Zero-based indices into `nodes` for direct callees. Defaults to `[]` when omitted. |

Root and child indices **MUST** be between `0` and `nodes.length - 1`. Node references **MUST** form independent trees without cycles or shared nodes. Recursive calls use separate nodes that **MAY** reference the same `frame_id`.

Paths **MUST** run from caller to callee. Each root-to-leaf path is one observed call path, potentially ending before the true bottom or top of the stack if the source was incomplete.

Expand All @@ -248,13 +251,13 @@ Frame addresses **MUST** follow the existing [`instruction_addr` and `addr_mode`

Symbolication **MUST** preserve captured addresses, addressing modes, and debug images. Resolved function names and source locations **MUST** populate the standard profiling frame fields. Unresolved frames **MUST** retain their original data.

Consumers **MUST** track symbolication state and skip symbolication for already-processed payloads. If symbolication is rerun, it **MUST** start from the original, unexpanded frames and trees.
Consumers **MUST** track symbolication state and skip symbolication for already-processed payloads. If symbolication is rerun, it **MUST** start from the original, unexpanded frames, nodes, and trees.

##### Flame Graph Inline Frames

Compiler-inlined functions share a physical stack frame with their caller. [Native symbolication](/ingestion/symbolicator/#native-symbolication) can recover these functions as separate logical frames.

A processor that expands inline frames **MUST** replace each affected node occurrence with a caller-to-callee chain, assign the original `sample_count` to every node in that chain, and attach the original children to its innermost node. The expansion **MUST** preserve the tree's total sample count. Frame-table changes **MUST** update all affected indices.
A processor that expands inline frames **MUST** replace each affected node occurrence with a caller-to-callee chain, assign the original `sample_count` to every node in that chain, and attach the original children to its innermost node. The expansion **MUST** preserve the tree's total sample count. Frame-table changes **MUST** update all affected `frame_id` values. Node-table changes **MUST** update all affected `roots` and `children` indices.

#### Flame Graph Example

Expand Down Expand Up @@ -290,21 +293,21 @@ This example contains 10 samples. Six continue from `main` into `process` and fo
"lineno": 42
}
],
"nodes": [
{
"frame_id": 0,
"sample_count": 10,
"children": [1]
},
{
"frame_id": 1,
"sample_count": 6
}
],
"trees": [
{
"thread_attributed": true,
"roots": [
{
"frame_id": 0,
"sample_count": 10,
"children": [
{
"frame_id": 1,
"sample_count": 6
}
]
}
]
"roots": [0]
}
]
}
Expand Down
Loading