---
title: Correlate RUM and Profiling
description: >-
  Use profiling with RUM to understand application performance issues affecting
  user experience.
breadcrumbs: >-
  Docs > RUM & Session Replay > Correlate RUM Events with Other Telemetry >
  Correlate RUM and Profiling
---

# Correlate RUM and Profiling

## Overview{% #overview %}

Datadog RUM supports profiling for browser, iOS, and Android applications. Use profiling data to identify performance bottlenecks, optimize slow code paths, and improve rendering performance at both the system and code level.

{% section displayed-if="SDK is Browser" %}
This section only applies to users who meet the following criteria: SDK is Browser

{% callout %}
##### Join the Preview!


Browser Profiling is in Preview.




[Request Access](https://www.datadoghq.com/product-preview/browser-profiler/)
{% /callout %}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler.f3c90929d45047a8f0ab89034d64e50f.png?auto=format"
   alt="Browser profiling example when analyzing an event sample." /%}

Browser profiling provides visibility into how your application behaves in your users' browsers, helping you understand root causes behind unresponsive applications at page load or during the page life cycle. Use profiling data alongside RUM insights to identify which code executes during a [Long Animation Frame (LoAF)](https://docs.datadoghq.com/real_user_monitoring/guide/browser-sdk-upgrade.md#collect-long-animation-frames-as-long-tasks) and how JavaScript execution and rendering tasks impact user-perceived performance.

To get started, enable browser profiling in your RUM SDK configuration. After enabling it, click on a profiled event sample to see detailed profiling data.

## Setup{% #setup %}

### Step 1 - Set up RUM{% #step-1--set-up-rum %}

{% alert level="info" %}
Browser SDK version 6.12 or later is required.
{% /alert %}

To start collecting data, set up [RUM Browser Monitoring](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/browser/setup.md).

### Step 2 - Configure the profiling sampling rate{% #step-2--configure-the-profiling-sampling-rate %}

1. Initialize the RUM SDK and configure `profilingSampleRate`, which determines the percentage of sessions that are profiled (for example, 25% means profiling runs on 25 out of 100 sessions).

   ```
   import { datadogRum } from '@datadog/browser-rum'
   
   datadogRum.init({
     clientToken: '<CLIENT_TOKEN>',
     applicationId: '<APPLICATION_ID>',
     site: 'datadoghq.com',
     //  service: 'my-web-application',
     //  env: 'production',
     //  version: '1.0.0',
     profilingSampleRate: 25,
     trackLongTasks: true,
     trackUserInteractions: true,
   })
   ```

1. Configure your web servers to serve HTML pages with the HTTP response header `Document-Policy: js-profiling`:

   ```
       app.get("/", (request, response) => {
           … 
           response.set("Document-Policy", "js-profiling");
           …
       });
   ```

1. Set up Cross-Origin Resource Sharing (CORS) if needed.

This step is required only if your JavaScript files are served from a different origin than your HTML. For example, if your HTML is served from `cdn.com` and JavaScript files from `static.cdn.com`, you must enable CORS to make JavaScript files visible to the profiler. For more information, see the Browser profiling and CORS section.

To enable CORS:

   - Add a `crossorigin="anonymous"` attribute to `<script/>` tags

   - Make sure that JavaScript response includes the `Access-Control-Allow-Origin: *` HTTP header (or the proper origin value)

     ```
     app.get("/", (request, response) => {
         … 
         response.header("Access-Control-Allow-Origin", "*");
         response.header("Access-Control-Allow-Headers",
         …
     });
     ```

{% collapsible-section %}
### Browser profiling and CORS

#### Requirements for Cross-Origin Scripts (CORS){% #requirements-for-cross-origin-scripts--cors %}

If a script's execution or attribution information is to be surfaced in performance entries (and thus captured in browser profiling), the resource (for example, a JavaScript file) needs to be fetched with CORS headers that explicitly allow it to be shared with the origin making the measurement (your application).

To summarize:

- If a script is loaded from a same-origin source, then attribution is allowed, and you can see profiling data attributed to this script.
- If a script is loaded cross-origin *without* a permissive CORS policy (like `Access-Control-Allow-Origin` allowing the page origin), then attribution is blocked, and you do not see profiling data attributed to this script.

This CORS policy restricts profiling to only scripts that are explicitly intended to be profiled by other origins.

#### How does CORS relate to browser profiling?{% #how-does-cors-relate-to-browser-profiling %}

When you start Datadog's browser profiler (which uses the [JS Self-Profiling API](https://developer.mozilla.org/en-US/docs/Web/API/JS_Self-Profiling_API)), the profiler can capture stack traces of JavaScript execution—but it only includes *attribution* (function names, URLs, etc.) for the following scripts:

- Scripts that have the same origin as the page initiating the profiling
- Cross-origin scripts that explicitly opt-in using CORS

This protects third-party content and users from leaking execution details across security boundaries.

#### Why is the crossorigin="anonymous" attribute needed?{% #why-is-the-crossorigin-anonymous-attribute-needed %}

Without the `crossorigin="anonymous"` attribute, the browser does not make a CORS-enabled request for the script. The browser fetches the script without CORS, meaning:

- No CORS policy applies.
- No credentials (cookies, HTTP auth, etc.) are sent.
- The fetched script is not eligible for detailed attribution in performance entries or stack traces. These stack frames are displayed as "(anonymous)" or with no attribution.

To protect cross-origin script privacy, *both* sides must agree to share information:

- The page must explicitly request a CORS-enabled fetch, with `crossorigin="anonymous"`.
- The server must permit this, with an `Access-Control-Allow-Origin` header in the response.

A script is eligible for attribution in the JS Self-Profiling API only when both of these conditions are met.
{% /collapsible-section %}

## Explore profiling{% #explore-profiling %}

### Within the Sessions Explorer{% #within-the-sessions-explorer %}

Profiling data is captured on long tasks and rolls up to actions, views, vitals, and sessions. Use `@profiling.has_profile` to filter to profiled events and understand what code ran and how it affected the user's experience. This is available for sessions, views, actions, vitals, and long tasks.

- **View panel**: Profiling data in a new tab.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer_view_panel.b4f1c7f16aa63a5e28385526a3d5e901.png?auto=format"
   alt="Browser profiling tab in the View panel." /%}

- **Long Task panel**: Profiling data in the performance tab.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer.6c37c17ba4843996619bf6feae6a69d2.png?auto=format"
   alt="Browser profiling troubleshoot section example within the Optimization page." /%}

- **Vitals panel**: Profiling data in a new tab.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer_vitals_panel.b4f1c7f16aa63a5e28385526a3d5e901.png?auto=format"
   alt="Browser profiling tab in the Vitals panel." /%}

- **Action panel**: Profiling data in a new tab.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer_action_panel.f02c564a02bac6017751d52fc940c7c8.png?auto=format"
   alt="Browser profiling tab in the Action panel." /%}

### Within the Profiling page{% #within-the-profiling-page %}

{% video
   url="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_aggregate_exprience.mp4" /%}

The Profiling page, found thorugh the top bar navigation, lets you analyze profiling data across sessions in one place. Use it to spot system level patterns, compare top-consuming functions, and prioritize optimizations instead of inspecting profiled sessions one by one. The guided experience walks you through:

1. **Focus on views**: Choose the views you'd like to analyze.
1. **Select a measurement**: Pick a Core Web Vital, custom vital, or RUM action to dive into.
1. **Refine your selection**: Narrow to the most relevant slice of data by percentile or time range so you focus on the worst-performing or most critical segment.
1. **Investigate slowest functions**: Review which functions consume the most time in the aggregated profile so you can prioritize what to optimize first.
1. **View the flame graph**: Explore the call hierarchy to see how those functions relate and where time is spent across the stack.

### Within the Optimization page{% #within-the-optimization-page %}

The **Optimization page** surfaces profiling data in several contexts:

- In the **Troubleshoot section**, Datadog samples long tasks across multiple views to identify your top contributing functions. Use this overview to find where JavaScript execution time is spent and which functions block the main thread, then optimize those functions to improve responsiveness.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_troubleshoot_section.7fa9f72cce3e38b5e6785291423be06b.png?auto=format"
   alt="Browser profiling troubleshoot section example within the Optimization page." /%}

- Within the **Event Waterfall**, any long task that includes profiling data is marked with a yellow profiling icon. Click one of these long task events to open a Long Task view panel with detailed profiling data. Use this panel to identify blocking functions, trace their call stacks, and understand how script execution contributes to poor responsiveness.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_event_waterfall.9d598bde1aad74b87583fea24edf2bb9.png?auto=format"
   alt="Browser profiling event waterfall example within the Optimization page." /%}
{% /section %}

{% section displayed-if="SDK is Android" %}
This section only applies to users who meet the following criteria: SDK is Android

{% callout %}
##### Join the Preview!


Android Profiling is in Preview.




[Request Access](https://www.datadoghq.com/product-preview/android-profiler/)
{% /callout %}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/android/android-profiling-ttid.42a859be355425903315687a4f4d8871.png?auto=format"
   alt="Android profiling data in a time to initial display vital event." /%}

Android profiling captures detailed data about your application's performance during launch, helping you identify slow methods and optimize startup time. Android profiling is built on top of the [ProfilingManager Android API](https://developer.android.com/topic/performance/tracing/profiling-manager/overview) and samples the device's CPU to collect method call stacks from the application's process.

{% alert level="warning" %}
Only devices running Android 15 (API level 35) or higher generate profiling data.
{% /alert %}

## Prerequisites{% #prerequisites %}

- Your Android application must use the Datadog Android SDK version 3.6.0+.
- [RUM without Limits](https://docs.datadoghq.com/real_user_monitoring/rum_without_limits.md) must be enabled in your organization.

## Setup{% #setup-2 %}

### Step 1 - Set up RUM{% #step-1--set-up-rum-2 %}

To start collecting data, set up [Mobile RUM for Android](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/android.md).

### Step 2 - Configure the profiling sampling rate{% #step-2--configure-the-profiling-sampling-rate-2 %}

1. Initialize the RUM SDK and configure the `applicationLaunchSampleRate`, which determines the percentage of application launches that are profiled (for example, 15% means profiling runs on 15 out of 100 launches).
Important alert (level: danger): 
If no value is specified, the default `applicationLaunchSampleRate` is 15 percent.

   ```
     class SampleApplication : Application() {
         override fun onCreate() {
             super.onCreate()
             val configuration = Configuration.Builder(
                 clientToken = "<CLIENT_TOKEN>",
                 env = "<ENV_NAME>",
                 variant = "<APP_VARIANT_NAME>"
             ).build()
   
             Datadog.initialize(this, configuration, trackingConsent)
   
             // Enable RUM (required for Profiling)
             val rumConfig = RumConfiguration.Builder(applicationId)
                 .build()
             Rum.enable(rumConfig)
   
             // Enable Profiling
             val profilingConfig = ProfilingConfiguration.Builder()
               .setApplicationLaunchSampleRate(15) // default is 15%
               .build()
   
             Profiling.enable(profilingConfig)
         }
     }
   ```
Important alert (level: warning): 
The total volume of profiles may not match the percentage configured in `applicationLaunchSampleRate`. This variation results from [rate limitations](https://developer.android.com/topic/performance/tracing/profiling-manager/will-my-profile-always-be-collected#how-rate-limiting-works) within the data collector, including profiling support on older devices and the maximum profiling frequency per device.

The [ProfilingManager API](https://developer.android.com/topic/performance/tracing/profiling-manager/debug-mode) also supports disabling rate limiting during debug builds.

## Explore profiling data{% #explore-profiling-data %}

Profiling data is captured on vitals and rolls up to views and sessions. Use `@profiling.has_profile` in the Sessions Explorer to filter to profiled events and investigate which code ran and how it affected the user's experience. This is available for sessions, views, and vitals.

### During the time to initial display{% #during-the-time-to-initial-display %}

Android application launch profiling data is attached to the [time to initial display](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/android/application_launch_monitoring.md?tab=kotlin) vital event in a RUM session. You can access the time to initial display from the session side panel, view side panel, or directly from the time to initial display vital side panel.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/android/android-profiling-session.e48d508d104271ec303e5840408a7bee.png?auto=format"
   alt="Android profiling data in RUM session." /%}

Use the **flame graph** to identify which methods consume the most CPU time during launch, the **thread timeline** to see parallel execution patterns, and the **call graph** to trace method dependencies. You can also download the profiling data for external analysis or deeper investigation.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/android/android-profiling-thread-timeline.8968aa60db0150e82a44291125c771e7.png?auto=format"
   alt="Android profiling data for the time to initial display in a thread timeline." /%}
{% /section %}

{% section displayed-if="SDK is iOS" %}
This section only applies to users who meet the following criteria: SDK is iOS

{% callout %}
##### Join the Preview!


iOS Profiling is in Preview.




[Request Access](https://www.datadoghq.com/product-preview/ios-profiler/)
{% /callout %}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/ios/ios-profiling-ttid.e923ca487faca19586289c468f07e6ee.png?auto=format"
   alt="iOS profiling data in a time to initial display vital event." /%}

iOS profiling captures detailed data about your application's performance during launch, helping you identify slow functions and optimize startup time. iOS profiling is built on top of the [mach Kernel API](https://developer.apple.com/documentation/kernel/mach) and periodically samples all application threads to collect call stacks.

## Prerequisites{% #prerequisites-2 %}

- Your iOS application must use the Datadog iOS SDK version 3.6.0+.
- [RUM without Limits](https://docs.datadoghq.com/real_user_monitoring/rum_without_limits.md) must be enabled in your organization.

## Setup{% #setup-3 %}

### Step 1 - Set up RUM{% #step-1--set-up-rum-3 %}

To start collecting data, set up [Mobile RUM for iOS](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/ios.md).

### Step 2 - Configure the profiling sampling rate{% #step-2--configure-the-profiling-sampling-rate-3 %}

Initialize the RUM SDK and configure the `applicationLaunchSampleRate`, which determines the percentage of application launches that are profiled (for example, 5% means profiling runs on 5 out of 100 launches).

{% alert level="danger" %}
If no value is specified, the default `applicationLaunchSampleRate` is 5 percent.
{% /alert %}

```
    import DatadogCore
    import DatadogRUM
    import DatadogProfiling

    // Initialize Datadog SDK with your configuration
    Datadog.initialize(
      with: Datadog.Configuration(
        clientToken: "<client token>",  // From Datadog UI
        env: "<environment>",           // for example, "production", "staging"
        service: "<service name>"       // Your app's service name
      ),
      trackingConsent: trackingConsent  // GDPR compliance setting
    )

    // Enable RUM feature
    RUM.enable(
      with: RUM.Configuration(
        applicationID: "<rum application id>"
      )
    )

    // Enable Profiling feature
    Profiling.enable() // default is 5%
```

## Explore profiling data{% #explore-profiling-data-2 %}

Profiling data is captured on vitals and rolls up to views and sessions. Use `@profiling.has_profile` in the Sessions Explorer to filter to profiled events and investigate which code ran and how it affected the user's experience. This is available for sessions, views, and vitals.

### During the time to initial display{% #during-the-time-to-initial-display-2 %}

iOS application launch profiling data is attached to the [time to initial display](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/ios/application_launch_monitoring.md?tab=swift) vital event in a RUM session. You can access the time to initial display from the session side panel, view side panel, or directly from the time to initial display vital side panel.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/ios/ios-profiling-session.7f28ff5e724223b62a4b620edd957b88.png?auto=format"
   alt="iOS profiling data in a view event to initial display vital event." /%}

Use the **flame graph** to identify which functions consume the most Wall time during launch, the **thread timeline** to see parallel execution patterns, and the **call graph** to trace function dependencies. You can also download the profiling data for external analysis or deeper investigation.

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/ios/ios-profiling-thread-timeline.6defacce896d471fda276ace3c6a8c9c.png?auto=format"
   alt="iOS profiling data for the time to initial display in a thread timeline." /%}
{% /section %}

## Further reading{% #further-reading %}

- [Real User Monitoring](https://www.datadoghq.com/blog/real-user-monitoring-with-datadog/)
- [Start monitoring single-page applications](https://www.datadoghq.com/blog/modern-frontend-monitoring/)
- [Start monitoring Android applications](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/android.md)
- [Start monitoring iOS applications](https://docs.datadoghq.com/real_user_monitoring/application_monitoring/ios.md)
- [APM and Distributed Tracing](https://docs.datadoghq.com/tracing.md)



{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler.f3c90929d45047a8f0ab89034d64e50f.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler.f3c90929d45047a8f0ab89034d64e50f.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer_view_panel.b4f1c7f16aa63a5e28385526a3d5e901.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer_view_panel.b4f1c7f16aa63a5e28385526a3d5e901.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer.6c37c17ba4843996619bf6feae6a69d2.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer.6c37c17ba4843996619bf6feae6a69d2.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer_vitals_panel.b4f1c7f16aa63a5e28385526a3d5e901.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer_vitals_panel.b4f1c7f16aa63a5e28385526a3d5e901.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer_action_panel.f02c564a02bac6017751d52fc940c7c8.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_sessions_explorer_action_panel.f02c564a02bac6017751d52fc940c7c8.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_aggregate_exprience.db82d2a9d1a8cc27a27ace630a5a0f80.mp4?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_aggregate_exprience.db82d2a9d1a8cc27a27ace630a5a0f80.mp4?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_troubleshoot_section.7fa9f72cce3e38b5e6785291423be06b.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_troubleshoot_section.7fa9f72cce3e38b5e6785291423be06b.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_event_waterfall.9d598bde1aad74b87583fea24edf2bb9.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/browser/optimizing_performance/browser_profiler_event_waterfall.9d598bde1aad74b87583fea24edf2bb9.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/android/android-profiling-ttid.42a859be355425903315687a4f4d8871.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/android/android-profiling-ttid.42a859be355425903315687a4f4d8871.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/android/android-profiling-session.e48d508d104271ec303e5840408a7bee.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/android/android-profiling-session.e48d508d104271ec303e5840408a7bee.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/android/android-profiling-thread-timeline.8968aa60db0150e82a44291125c771e7.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/android/android-profiling-thread-timeline.8968aa60db0150e82a44291125c771e7.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/ios/ios-profiling-ttid.e923ca487faca19586289c468f07e6ee.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/ios/ios-profiling-ttid.e923ca487faca19586289c468f07e6ee.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/ios/ios-profiling-session.7f28ff5e724223b62a4b620edd957b88.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/ios/ios-profiling-session.7f28ff5e724223b62a4b620edd957b88.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}

{% image
   source="https://docs.dd-static.net/images/real_user_monitoring/ios/ios-profiling-thread-timeline.6defacce896d471fda276ace3c6a8c9c.png?auto=format&fit=max&w=850 1x, https://docs.dd-static.net/images/real_user_monitoring/ios/ios-profiling-thread-timeline.6defacce896d471fda276ace3c6a8c9c.png?auto=format&fit=max&w=850&dpr=2 2x"
   alt="" /%}


