Profiles

Stack trace sampling at regular intervals for flamecharts and performance bottleneck analysis.

Statusstable
Version2.5.0(changelog)

Profiling captures stack traces at regular intervals during program execution, enabling Sentry to reconstruct flamecharts and identify performance bottlenecks. SDKs collect profile data and send it to Sentry as profile_chunk envelope items using the V2 sample format.

  • V2 (Continuous): Profile chunks exist independently and are sent as profile_chunk envelope items. Used for continuous and UI profiling. This is the current format — all new SDK implementations SHOULD use V2.
  • V1 (Transaction-based): (Deprecated) Profile is bound to a transaction and sent in the same envelope. SDKs SHOULD migrate to V2.

Related specs:


Two sample format versions exist:

  • V2 ("version": "2") — A profile chunk exists independently of transactions. It is sent in its own envelope using the profile_chunk item type. Used for continuous and UI profiling. This is the current format.
  • V1 ("version": "1") — (Deprecated since 2.5.0) A profile is always associated with a transaction. It is sent in the same envelope as the associated transaction using the profile item type. SDKs SHOULD migrate to V2.

Both formats share a common profile data structure consisting of:

  • Samples — timestamps with corresponding thread and stack references
  • Stacks — lists of frame indices forming stack traces
  • Frames — function/line information for each position in a stacktrace
  • Thread metadata — names and priorities for profiled threads

Continuous profiling operates in one of two lifecycle modes:

  • Manual mode (profile_lifecycle: 'manual') — The profiler is controlled via explicit start_profiler() and stop_profiler() calls.
  • Trace mode (profile_lifecycle: 'trace') — The profiler starts/stops automatically based on active root spans.

Stablespecified since 1.0.0

SDKs SHOULD collect profile samples at a frequency of 101Hz (roughly once every 10 milliseconds).

The 101Hz value is intentional to avoid lockstep sampling — a condition where profiling samples occur at the same frequency as a loop in the application. (since 2.1.1) 101 was chosen for its primality; 99 (1 below 100) is evenly divisible by several smaller numbers, which could lead to similar lockstep behavior.

Stablespecified since 2.3.0

SDKs implementing continuous profiling MUST support two lifecycle modes:

Manual mode (profile_lifecycle: 'manual'):

  • The profiler is controlled via start_profiler() and stop_profiler().
  • Profiling only respects profile_session_sample_rate (independent of spans).
  • If the session is sampled, start_profiler() starts the profiler.

Trace mode (profile_lifecycle: 'trace'):

  • This mode requires tracing to be enabled. SDKs SHOULD log a warning if profile_lifecycle is set to 'trace' but tracing is disabled.
  • The profiler starts automatically when there is at least one active root span and stops when there are no active root spans (letting the current chunk finish).
  • Profiling respects both profile_session_sample_rate and the tracing sampling configuration (traces_sample_rate or traces_sampler).
  • profile_session_sample_rate is checked first. If the session is not sampled, no profiling will occur.
  • (since 2.3.1) Only if a root span is sampled (based on traces_sample_rate / traces_sampler), profiling will start. Profile sampling is re-evaluated for each root span.
  • The profiler runs as long as there is at least one sampled root span. If multiple root spans overlap, profiling continues until the last sampled root span finishes.
Stablespecified since 2.1.0

For continuous profiling (V2), additional context MUST be attached to transaction and span payloads to link them to profile data.

Profile context (.contexts.profile): SDKs MUST include the profiler_id in the profile context of transactions so the profile can be associated back to the transaction.

Trace context (.contexts.trace.data): SDKs MUST include thread.id in the trace context data to identify which thread is associated with the transaction. thread.name MAY be included.

Span data (.spans[].data): SDKs MUST include thread.id in span data. thread.name and profiler_id (to override the trace context value) MAY be included.


Deprecatedspecified since 1.0.0Superseded by #v2-sample-format

A V1 profile MUST be associated with a transaction and sent in the same envelope.

FieldTypeRequiredSinceDescription
versionStringREQUIRED1.0.0MUST be "1".
event_idStringREQUIRED1.0.0Hexadecimal UUID v4, exactly 32 characters, lowercase, no dashes.
platformStringREQUIRED1.0.0Platform identifier (e.g., cocoa, node, python, rust).
releaseStringREQUIRED1.0.0Release version of the application.
deviceObjectREQUIRED1.0.0Device information. See V1 Device Object.
osObjectREQUIRED1.0.0OS information: name (required), version (required), build_number (optional, cocoa only).
profileObjectREQUIRED1.0.0Profile data. See Profile Data.
transactionObjectREQUIRED1.0.0Associated transaction: id (required), name (required), trace_id (required), active_thread_id (required, uint64 as string).
timestampStringRECOMMENDED1.0.0RFC 3339 timestamp of when the profile was captured. SHOULD match the transaction's start_timestamp.
environmentStringRECOMMENDED1.0.0Environment name. Default: production.
debug_metaObjectREQUIRED (native)1.0.0Debug metadata for symbolication. Required on native platforms (cocoa, rust). Same payload as the debug_meta interface.
measurementsObjectOPTIONAL1.0.0Metrics collected during profiling (e.g., frozen_frame_renders, slow_frame_renders). See Measurements.
runtimeObjectOPTIONAL1.0.0Runtime information (name, version). Used for platforms with multiple runtimes.

FieldTypeRequiredSinceDescription
architectureStringREQUIRED1.0.0CPU architecture (e.g., arm64e).
is_emulatorBooleanOPTIONAL1.0.0Whether running in an emulator. Usually mobile only.
localeStringOPTIONAL1.0.0System locale. Usually mobile only.
manufacturerStringOPTIONAL1.0.0Device manufacturer. Usually mobile only.
modelStringOPTIONAL1.0.0Device model. Usually mobile only.
Stablespecified since 2.0.0

A V2 profile chunk exists independently and is sent as a profile_chunk envelope item.

FieldTypeRequiredSinceDescription
versionStringREQUIRED2.0.0MUST be "2".
profiler_idStringREQUIRED2.0.0Hexadecimal UUID v4, exactly 32 characters, lowercase, no dashes. Random UUID for each profiler session.
chunk_idStringREQUIRED2.0.0Hexadecimal UUID v4, exactly 32 characters, lowercase, no dashes. Random UUID identifying this chunk.
platformStringREQUIRED2.0.0Platform identifier.
releaseStringREQUIRED2.0.0Release version of the application.
client_sdkObjectREQUIRED2.0.0SDK information: name and version. (since 2.2.0) Changed from optional to required.
profileObjectREQUIRED2.0.0Profile data. See Profile Data.
environmentStringRECOMMENDED2.0.0Environment name. Default: production.
debug_metaObjectREQUIRED (native)2.0.0Debug metadata for symbolication. Required on native platforms. Same payload as the debug_meta interface.
measurementsObjectOPTIONAL2.0.0Metrics collected during profiling. See Measurements.
Stablespecified since 1.0.0

Both V1 and V2 formats share the same profile object structure:

FieldTypeRequiredSinceDescription
samplesListREQUIRED1.0.0List of sample objects. Each sample contains stack_id (integer), thread_id (string), and a timestamp — V1 uses elapsed_since_start_ns (uint64 as string, wall time nanoseconds since profile start); V2 uses timestamp (float64, Unix seconds with microsecond precision).
stacksListREQUIRED1.0.0List of frame index arrays forming stack traces. Frames SHOULD be ordered from leaf to root (main frame at end). SDKs SHOULD deduplicate stacks.
framesListREQUIRED1.0.0List of frame objects. (since 1.1.0) Each frame MUST contain at least one of filename, function, or instruction_addr. For native platforms, instruction_addr is REQUIRED for symbolication. module or package is used for frame grouping.
thread_metadataObjectREQUIRED1.0.0Object keyed by thread ID. Each entry MAY contain name (string) and priority (number). Used in the flamechart thread selector.

Measurement values differ between V1 and V2:

  • V1: Uses elapsed_since_start_ns (nanoseconds since profile start, uint64 as string) and value (float64).
  • V2: Uses timestamp (Unix timestamp in seconds with microsecond precision, float64) and value (float64).

Currently supported measurements: frozen_frame_renders and slow_frame_renders.

Accepted measurement units: nanosecond, ns, hertz, hz, byte, percent.

Deprecatedspecified since 1.1.0Superseded by #v2-validation

Relay rejects a V1 profile if any of these conditions are true:

  • Profile data is missing (no frames, no samples, no stacks)
  • Fewer than 2 samples
  • No transaction associated with the profile
  • Required metadata is missing
  • Profile size exceeds 50 MB
  • Profile duration exceeds 30 seconds (difference between last and first sample timestamp)

SDKs SHOULD validate these conditions before sending.

SDKs SHOULD remove unnecessary data like thread entries without samples from thread_metadata.

Stablespecified since 2.0.0

Relay rejects a V2 profile chunk if any of these conditions are true:

  • Chunk data is missing (no frames, no samples, no stacks)
  • Required metadata is missing
  • Chunk size exceeds 50 MB

SDKs SHOULD validate these conditions before sending.

SDKs SHOULD remove unnecessary data like thread entries without samples from thread_metadata.

Deprecatedspecified since 1.0.0Superseded by #v2-envelope

A V1 profile MUST be serialized as JSON and sent in the same envelope as the associated transaction with item type profile. An envelope MUST contain at most one profile item.

Copied
{"event_id":"a229377b82ad4898be7c3a6272d052d9"}
{"type":"transaction"}
{ /* transaction JSON payload */ }
{"type":"profile"}
{ /* profile JSON payload */ }
Stablespecified since 2.0.0

A V2 profile chunk MUST be serialized as JSON and sent in an envelope with item type profile_chunk.

(since 2.4.0) The platform item header is REQUIRED for rate limiting and categorization and MUST match the platform field in the payload.

Copied
{"event_id":"a229377b82ad4898be7c3a6272d052d9"}
{"type":"profile_chunk", "platform":"node"}
{ /* profile_chunk JSON payload */ }
Stablespecified since 2.1.0

For V2 continuous profiling, SDKs MUST modify transaction and span payloads to enable profile linking.

Profile context (.contexts.profile):

FieldTypeRequiredSinceDescription
profiler_idStringREQUIRED2.1.0UUID matching the profiler session.

Trace context data (.contexts.trace.data):

FieldTypeRequiredSinceDescription
thread.idStringREQUIRED2.1.0Thread ID matching thread_metadata in the profile chunk. Often the thread the transaction was started on.
thread.nameStringOPTIONAL2.1.0Thread name matching thread_metadata in the profile chunk.

Span data (.spans[].data):

FieldTypeRequiredSinceDescription
thread.idStringREQUIRED2.1.0Thread ID matching thread_metadata in the profile chunk.
thread.nameStringOPTIONAL2.1.0Thread name matching thread_metadata in the profile chunk.
profiler_idStringOPTIONAL2.1.0Override for the profile context profiler_id.

Stablespecified since 2.3.0

SDKs implementing continuous profiling MUST accept a profileSessionSampleRate configuration option (number, 0.0–1.0, default 0). The sampling decision is made once when the SDK is initialized.

The definition of a profiling session depends on the profiling type:

  • Continuous Profiling: The session starts when the SDK is configured and stops when the service terminates. profileSessionSampleRate controls the percentage of service instances with profiling enabled. Sampling is re-evaluated on restart or redeployment.
  • UI Profiling: The session corresponds to a user session (SDK initialization to tab close or session end). On mobile, backgrounding and foregrounding starts a new session with re-evaluated sampling. If a trace is active when the app is foregrounded, the existing profiling session continues until the last root span in that trace finishes.

If profilesSampleRate or profilesSampler are configured for transaction-based profiling, profileSessionSampleRate has no effect. SDKs SHOULD log a warning in this case.

Naming SHOULD follow the SDK's language conventions:

  • profileSessionSampleRate (JavaScript)
  • profile_session_sample_rate (Python)
Stablespecified since 2.3.0

SDKs implementing continuous profiling MUST accept a profileLifecycle configuration option with values 'trace' or 'manual' (default: 'manual').

See Profile Lifecycle Modes for behavior details.

Naming SHOULD follow the SDK's language conventions:

  • profileLifecycle (JavaScript)
  • profile_lifecycle (Python)
Stablespecified since 2.3.0

Mobile SDKs MAY accept a startProfilerOnAppStart configuration option (boolean, default false).

When true, profiling starts as early as possible during app startup, before startProfiler() can be manually called. The profileSessionSampleRate for app start profiling is evaluated on the previous app launch and applied on the next launch, since the sample rate cannot be evaluated at launch time.

Behavior with profileLifecycle:

  • 'manual': Profiling starts on startup. The developer MUST call stopProfiler() when startup is complete.
  • 'trace': Profiling starts on startup and stops automatically when the root span associated with app startup ends.

Naming SHOULD follow the SDK's language conventions:

  • start_profiler_on_app_start (Python)
Stablespecified since 2.3.0

SDKs implementing continuous profiling SHOULD expose a function to start the profiler:

Copied
startProfiler() -> void

This is a no-op if any of the following conditions are met:

  • The profiling session is sampled and the profiler is already running.
  • The profiling session is not sampled.
  • profileLifecycle is set to 'trace' (profiler lifecycle is bound to trace collection).

SDKs SHOULD log a warning in debug mode if this function is called but results in a no-op.

Naming SHOULD follow the SDK's language conventions:

  • Sentry.startProfiler() (JavaScript)
  • sentry_sdk.start_profiler() (Python)
Stablespecified since 2.3.0

SDKs implementing continuous profiling SHOULD expose a function to stop the profiler:

Copied
stopProfiler() -> void

Behavior depends on profileLifecycle:

  • 'manual': Stops the current profiling session. The profiler can be started again by calling startProfiler().
  • 'trace': This is a no-op. The profiler stops automatically when there are no active root spans.

SDKs SHOULD log a warning in debug mode if this function is called while profileLifecycle is 'trace'.

Naming SHOULD follow the SDK's language conventions:

  • Sentry.stopProfiler() (JavaScript)
  • sentry_sdk.stop_profiler() (Python)

Copied
{
  "debug_meta": {
    "images": [
      {
        "debug_id": "32420279-25E2-34E6-8BC7-8A006A8F2425",
        "image_addr": "0x000000010258c000",
        "code_file": "/private/var/containers/Bundle/Application/C3511752-DD67-4FE8-9DA2-ACE18ADFAA61/TrendingMovies.app/TrendingMovies",
        "type": "macho",
        "image_size": 1720320,
        "image_vmaddr": "0x0000000100000000"
      }
    ]
  },
  "device": {
    "architecture": "arm64e",
    "is_emulator": true,
    "locale": "en_US",
    "manufacturer": "Apple",
    "model": "iPhone14,8"
  },
  "environment": "development",
  "event_id": "41fed0925670468bb0457f61a74688ec",
  "os": {
    "build_number": "20D47",
    "name": "iOS",
    "version": "16.3"
  },
  "platform": "cocoa",
  "release": "1.0 (9999)",
  "runtime": {
    "name": "",
    "version": ""
  },
  "timestamp": "2023-01-01T00:00:00.000Z",
  "transaction": {
    "active_thread_id": "259",
    "id": "30976f2ddbe04ac9b6bffe6e35d4710c",
    "name": "example_ios_movies_sources.MoviesViewController",
    "trace_id": "4b25bc58f14243d8b208d1e22a054164"
  },
  "version": "1",
  "profile": {
    "samples": [
      {
        "elapsed_since_start_ns": "1234567890",
        "stack_id": 0,
        "thread_id": "259"
      }
    ],
    "stacks": [[0]],
    "frames": [
      {
        "instruction_addr": "0xa722447ffffffffc"
      }
    ],
    "thread_metadata": {
      "259": {
        "priority": 31
      }
    }
  }
}

Copied
{
  "debug_meta": {
    "images": [
      {
        "debug_id": "5819FF25-01CB-3D32-B84F-0634B37D3BBC",
        "image_addr": "0x00000001023a8000",
        "type": "macho",
        "image_size": 16384,
        "code_file": "/Library/Developer/CoreSimulator/Volumes/iOS_21C62/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.2.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libLogRedirect.dylib"
      }
    ]
  },
  "profiler_id": "71bba98d90b545c39f2ae73f702d7ef4",
  "chunk_id": "3e11a5c9831f4e49939c0a81944ea2cb",
  "client_sdk": {
    "name": "sentry.cocoa",
    "version": "8.36.0"
  },
  "platform": "cocoa",
  "release": "io.sentry.sample.iOS-Swift@8.36.0+1",
  "environment": "simulator",
  "version": "2",
  "profile": {
    "samples": [
      {
        "thread_id": "259",
        "stack_id": 0,
        "timestamp": 1724777211.5037799
      }
    ],
    "stacks": [[0]],
    "frames": [
      {
        "instruction_addr": "0x000000010232d144",
        "function": "_ZNK5dyld311MachOLoaded17findClosestSymbolEyPPKcPy"
      }
    ],
    "thread_metadata": {
      "259": {
        "name": "main"
      }
    }
  }
}

Copied
{"event_id":"a229377b82ad4898be7c3a6272d052d9"}
{"type":"transaction"}
{ /* transaction JSON payload */ }
{"type":"profile"}
{ /* V1 profile JSON payload */ }

Copied
{"event_id":"a229377b82ad4898be7c3a6272d052d9"}
{"type":"profile_chunk", "platform":"node"}
{ /* V2 profile_chunk JSON payload */ }

Profile context on transaction:

Copied
{
  "contexts": {
    "profile": {
      "profiler_id": "42928c7ee9174231956f077581145489"
    },
    "trace": {
      "data": {
        "thread.id": "259",
        "thread.name": "com.apple.main-thread"
      }
    }
  }
}

Span data:

Copied
{
  "data": {
    "thread.id": "259",
    "thread.name": "com.apple.main-thread",
    "profiler_id": "42928c7ee9174231956f077581145489"
  }
}

VersionDateSummary
2.5.02026-02-20Deprecated V1 transaction-based profiling in favor of V2 continuous profiling
2.4.02026-01-23Added platform item header requirement for profile_chunk envelope
2.3.12025-11-27Clarified trace mode profiling behavior for UI profiling
2.3.02025-10-22Added Continuous/UI Profiling public API (start_profiler, stop_profiler, configuration)
2.2.12025-06-27Fixed V2 sample timestamp documentation
2.2.02025-06-27Tightened client_sdk to required in V2 format
2.1.12024-11-21Added lockstep sampling explanation (101Hz frequency rationale)
2.1.02024-09-11Added transaction/span context requirements for linking continuous profiles to traces
2.0.02024-09-03Added V2 sample format for continuous profiling
1.1.12023-03-31Clarified stack ordering and clock type for V1 format
1.1.02023-03-15Added frame details and validation requirements to V1 format
1.0.12023-03-13Fixed typo in V1 format documentation
1.0.02023-03-07Initial spec — V1 transaction-based profiling sample format
Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").