diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 576b454cb..4cdd9e939 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -91,10 +91,12 @@ test:native-android: - /opt/homebrew/opt/openjdk@17/bin/java --version - echo "org.gradle.java.home=/opt/homebrew/opt/openjdk@17" >> packages/core/android/gradle.properties - echo "org.gradle.java.home=/opt/homebrew/opt/openjdk@17" >> packages/react-native-session-replay/android/gradle.properties + - echo "org.gradle.java.home=/opt/homebrew/opt/openjdk@17" >> packages/react-native-profiling/android/gradle.properties - echo "org.gradle.java.home=/opt/homebrew/opt/openjdk@17" >> packages/internal-testing-tools/android/gradle.properties - yarn - (cd packages/core/android && ./gradlew build -PDdSdkReactNative_minSdkVersion=24) - (cd packages/react-native-session-replay/android && ./gradlew build -PDdSdkReactNative_minSdkVersion=24 -PDatadogSDKReactNativeSessionReplay_minSdkVersion=24) + - (cd packages/react-native-profiling/android && ./gradlew build -PDdSdkReactNative_minSdkVersion=24 -PDatadogSDKReactNativeProfiling_minSdkVersion=24) - (cd packages/internal-testing-tools/android && ./gradlew build -PDdSdkReactNative_minSdkVersion=24 -PDatadogInternalTesting_minSdkVersion=24) test:native-ios: @@ -119,6 +121,17 @@ test:native-ios-sr: - (cd example/ios && RCT_NEW_ARCH_ENABLED=0 pod install --repo-update) - set -o pipefail && xcodebuild -workspace example/ios/DdSdkReactNativeExample.xcworkspace -scheme DatadogSDKReactNativeSessionReplay test -destination "$IOS_SIMULATOR_DESTINATION" -derivedDataPath "$IOS_DERIVED_DATA_PATH" | xcbeautify +test:native-ios-profiling: + tags: ['macos:sequoia-arm64', 'specific:true'] + stage: test + rules: + - if: '$BUILD_BENCHMARK != "true"' + timeout: 1h + script: + - yarn + - (cd example/ios && RCT_NEW_ARCH_ENABLED=0 pod install --repo-update) + - set -o pipefail && xcodebuild -workspace example/ios/DdSdkReactNativeExample.xcworkspace -scheme DatadogSDKReactNativeProfiling test -destination "$IOS_SIMULATOR_DESTINATION" -derivedDataPath "$IOS_DERIVED_DATA_PATH" | xcbeautify + test:native-ios-newarch: tags: ['macos:sequoia-arm64', 'specific:true'] stage: test diff --git a/bump-native-dd-sdk.sh b/bump-native-dd-sdk.sh index fabaf9600..0e2c2efae 100755 --- a/bump-native-dd-sdk.sh +++ b/bump-native-dd-sdk.sh @@ -13,6 +13,7 @@ new_version="$2" build_gradle_files=( "packages/core/android/build.gradle" "packages/react-native-session-replay/android/build.gradle" + "packages/react-native-profiling/android/build.gradle" "packages/react-native-webview/android/build.gradle" "packages/internal-testing-tools/android/build.gradle" "benchmarks/android/app/build.gradle" @@ -21,6 +22,7 @@ build_gradle_files=( podspec_files=( "packages/core/DatadogSDKReactNative.podspec" "packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec" + "packages/react-native-profiling/DatadogSDKReactNativeProfiling.podspec" "packages/react-native-webview/DatadogSDKReactNativeWebView.podspec" ) diff --git a/example-new-architecture/App.tsx b/example-new-architecture/App.tsx index db326f6d4..693670d5f 100644 --- a/example-new-architecture/App.tsx +++ b/example-new-architecture/App.tsx @@ -39,6 +39,7 @@ import { LearnMoreLinks, ReloadInstructions, } from 'react-native/Libraries/NewAppScreen'; +import {DdProfiling} from '@datadog/mobile-react-native-profiling'; // @ts-ignore import {APPLICATION_ID, CLIENT_TOKEN, ENVIRONMENT} from './ddCredentials'; @@ -73,6 +74,11 @@ import {APPLICATION_ID, CLIENT_TOKEN, ENVIRONMENT} from './ddCredentials'; // Initialize the Datadog SDK. await DdSdkReactNative.initialize(config); + await DdProfiling.enable({ + applicationLaunchSampleRate: 100, + continuousSampleRate: 100, + }); + // Enable Datadog Flags feature. await DdFlags.enable(); diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index 368a3840c..ff1216974 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -11,6 +11,8 @@ PODS: - DatadogInternal (3.16.1) - DatadogLogs (3.16.1): - DatadogInternal (= 3.16.1) + - DatadogProfiling (3.16.1): + - DatadogInternal (= 3.16.1) - DatadogRUM (3.16.1): - DatadogInternal (= 3.16.1) - DatadogSDKReactNative (3.11.0): @@ -69,6 +71,29 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - DatadogSDKReactNativeProfiling (3.11.0): + - DatadogProfiling (= 3.16.1) + - DatadogSDKReactNative + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.10.14.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga - DatadogTrace (3.16.1): - DatadogInternal (= 3.16.1) - OpenTelemetry-Swift-Api (~> 2.5.0) @@ -1651,6 +1676,7 @@ DEPENDENCIES: - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - DatadogSDKReactNative (from `../../packages/core/DatadogSDKReactNative.podspec`) - DatadogSDKReactNative/Tests (from `../../packages/core/DatadogSDKReactNative.podspec`) + - "DatadogSDKReactNativeProfiling (from `../node_modules/@datadog/mobile-react-native-profiling`)" - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - fast_float (from `../node_modules/react-native/third-party-podspecs/fast_float.podspec`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) @@ -1724,6 +1750,7 @@ SPEC REPOS: - DatadogFlags - DatadogInternal - DatadogLogs + - DatadogProfiling - DatadogRUM - DatadogTrace - DatadogWebViewTracking @@ -1736,6 +1763,8 @@ EXTERNAL SOURCES: :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" DatadogSDKReactNative: :path: "../../packages/core/DatadogSDKReactNative.podspec" + DatadogSDKReactNativeProfiling: + :path: "../node_modules/@datadog/mobile-react-native-profiling" DoubleConversion: :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" fast_float: @@ -1871,8 +1900,10 @@ SPEC CHECKSUMS: DatadogFlags: 65cb325168aed2f7beb24ebef716227c6c2a8d1a DatadogInternal: 152be7f0a389684161fa642e55509163c99757d5 DatadogLogs: 0c145bb072308903df8a7edd9c1125f4b479fa5f + DatadogProfiling: 03a1ac3f8fbeee901aceb5f176c2ce1918e6c826 DatadogRUM: dcffd64699184ef38f63d42bd331cc1eba967661 DatadogSDKReactNative: 5279edc70099a2ab35585bdc99995cd08b0a8d33 + DatadogSDKReactNativeProfiling: 93c93a534ddcd62b015bf15df187ad003522bc6a DatadogTrace: f16ae4f5aa847aa3ed194c50f6145fac70e54962 DatadogWebViewTracking: 2ba64c950bb40bf9dfab42d122c426788b5de2f0 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 diff --git a/example-new-architecture/package.json b/example-new-architecture/package.json index 83a990a29..b21767cca 100644 --- a/example-new-architecture/package.json +++ b/example-new-architecture/package.json @@ -10,6 +10,7 @@ "dependencies": { "@datadog/mobile-react-native": "workspace:packages/core", "@datadog/mobile-react-native-openfeature": "workspace:packages/react-native-openfeature", + "@datadog/mobile-react-native-profiling": "workspace:packages/react-native-profiling", "@openfeature/react-sdk": "^1.1.0", "react": "18.3.1", "react-native": "0.76.9" diff --git a/example/ios/Podfile b/example/ios/Podfile index 0c9182f55..9229dd357 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -19,6 +19,7 @@ end target 'ddSdkReactnativeExample' do pod 'DatadogSDKReactNative', :path => '../../packages/core/DatadogSDKReactNative.podspec', :testspecs => ['Tests'] pod 'DatadogSDKReactNativeSessionReplay', :path => '../../packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec', :testspecs => ['Tests'] + pod 'DatadogSDKReactNativeProfiling', :path => '../../packages/react-native-profiling/DatadogSDKReactNativeProfiling.podspec', :testspecs => ['Tests'] pod 'DatadogSDKReactNativeWebView', :path => '../../packages/react-native-webview/DatadogSDKReactNativeWebView.podspec', :testspecs => ['Tests'] config = use_native_modules! diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e29f53a10..72bec5dcd 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -11,6 +11,8 @@ PODS: - DatadogInternal (3.16.1) - DatadogLogs (3.16.1): - DatadogInternal (= 3.16.1) + - DatadogProfiling (3.16.1): + - DatadogInternal (= 3.16.1) - DatadogRUM (3.16.1): - DatadogInternal (= 3.16.1) - DatadogSDKReactNative (3.11.0): @@ -31,6 +33,14 @@ PODS: - DatadogTrace (= 3.16.1) - DatadogWebViewTracking (= 3.16.1) - React-Core + - DatadogSDKReactNativeProfiling (3.11.0): + - DatadogProfiling (= 3.16.1) + - DatadogSDKReactNative + - React-Core + - DatadogSDKReactNativeProfiling/Tests (3.11.0): + - DatadogProfiling (= 3.16.1) + - DatadogSDKReactNative + - React-Core - DatadogSDKReactNativeSessionReplay (3.11.0): - DatadogSDKReactNative - DatadogSessionReplay (= 3.16.1) @@ -1720,6 +1730,8 @@ DEPENDENCIES: - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - DatadogSDKReactNative (from `../../packages/core/DatadogSDKReactNative.podspec`) - DatadogSDKReactNative/Tests (from `../../packages/core/DatadogSDKReactNative.podspec`) + - DatadogSDKReactNativeProfiling (from `../../packages/react-native-profiling/DatadogSDKReactNativeProfiling.podspec`) + - DatadogSDKReactNativeProfiling/Tests (from `../../packages/react-native-profiling/DatadogSDKReactNativeProfiling.podspec`) - DatadogSDKReactNativeSessionReplay (from `../../packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec`) - DatadogSDKReactNativeSessionReplay/Tests (from `../../packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec`) - DatadogSDKReactNativeWebView (from `../../packages/react-native-webview/DatadogSDKReactNativeWebView.podspec`) @@ -1804,6 +1816,7 @@ SPEC REPOS: - DatadogFlags - DatadogInternal - DatadogLogs + - DatadogProfiling - DatadogRUM - DatadogSessionReplay - DatadogTrace @@ -1818,6 +1831,8 @@ EXTERNAL SOURCES: :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" DatadogSDKReactNative: :path: "../../packages/core/DatadogSDKReactNative.podspec" + DatadogSDKReactNativeProfiling: + :path: "../../packages/react-native-profiling/DatadogSDKReactNativeProfiling.podspec" DatadogSDKReactNativeSessionReplay: :path: "../../packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec" DatadogSDKReactNativeWebView: @@ -1971,8 +1986,10 @@ SPEC CHECKSUMS: DatadogFlags: 65cb325168aed2f7beb24ebef716227c6c2a8d1a DatadogInternal: 152be7f0a389684161fa642e55509163c99757d5 DatadogLogs: 0c145bb072308903df8a7edd9c1125f4b479fa5f + DatadogProfiling: 03a1ac3f8fbeee901aceb5f176c2ce1918e6c826 DatadogRUM: dcffd64699184ef38f63d42bd331cc1eba967661 DatadogSDKReactNative: c05bfd84ba1e81a84d0b45839619f5311bbbe8ab + DatadogSDKReactNativeProfiling: d23f6ad7fbe4a06e08871efe00d617695aec78a7 DatadogSDKReactNativeSessionReplay: 1e3a975282b75df6ae82c93598d837cb490c5af8 DatadogSDKReactNativeWebView: d6c56cefa61a2476c98b41f3a095c6d89ec0ea3b DatadogSessionReplay: 137621c077cfb039224ecb3955a8d9a015d065a0 @@ -2053,6 +2070,6 @@ SPEC CHECKSUMS: SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: feb4910aba9742cfedc059e2b2902e22ffe9954a -PODFILE CHECKSUM: a2729cbf6c0a559932ef8f49d27110873b77ff7d +PODFILE CHECKSUM: 47138531a1c82b754b0b3598ba2f51c2f0c36642 COCOAPODS: 1.16.2 diff --git a/example/package.json b/example/package.json index 9dd1f105c..6d444b91c 100644 --- a/example/package.json +++ b/example/package.json @@ -13,6 +13,7 @@ "@datadog/mobile-react-native": "workspace:packages/core", "@datadog/mobile-react-native-navigation": "workspace:packages/react-native-navigation", "@datadog/mobile-react-native-openfeature": "workspace:packages/react-native-openfeature", + "@datadog/mobile-react-native-profiling": "workspace:packages/react-native-profiling", "@datadog/mobile-react-native-session-replay": "workspace:packages/react-native-session-replay", "@datadog/mobile-react-native-webview": "workspace:packages/react-native-webview", "@datadog/mobile-react-navigation": "workspace:packages/react-navigation", diff --git a/example/src/App.tsx b/example/src/App.tsx index 95546c6df..d9e808db9 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -16,6 +16,7 @@ import { TextAndInputPrivacyLevel, TouchPrivacyLevel, } from '@datadog/mobile-react-native-session-replay'; +import { DdProfiling } from '@datadog/mobile-react-native-profiling'; import { Route } from "@react-navigation/native"; import { NestedNavigator } from './screens/NestedNavigator/NestedNavigator'; @@ -89,6 +90,11 @@ const handleDatadogInitialization = async () => { enableHeatmaps: true, }); + await DdProfiling.enable({ + applicationLaunchSampleRate: 100, + continuousSampleRate: 100, + }); + // Enable Datadog Flags feature. await DdFlags.enable(); diff --git a/example/src/ddUtils.tsx b/example/src/ddUtils.tsx index d4acbe069..777ff7683 100644 --- a/example/src/ddUtils.tsx +++ b/example/src/ddUtils.tsx @@ -37,6 +37,13 @@ export function getDatadogConfig(trackingConsent: TrackingConsent) { trackErrors: true, sessionSampleRate: 100, nativeCrashReportEnabled: true, + // Report JavaScript long tasks. These are forwarded to the native RUM + // monitor, which is what continuous profiling correlates profiles against: + // a continuous profile is only uploaded if the profiling window captured a + // long task, an app hang/ANR, or a vital. Leaving this at its default of 0 + // disables JS long task reporting, so profiles would be collected and then + // discarded. See the "Trigger JS long task" button on the main screen. + longTaskThresholdMs: 200, firstPartyHosts: [{ match: "example.com", propagatorTypes: [PropagatorType.B3MULTI, PropagatorType.TRACECONTEXT] diff --git a/example/src/screens/MainScreen.tsx b/example/src/screens/MainScreen.tsx index 37f14b8b0..8a4ae4d38 100644 --- a/example/src/screens/MainScreen.tsx +++ b/example/src/screens/MainScreen.tsx @@ -26,6 +26,7 @@ interface MainScreenState { resultTouchableOpacityAction: string, resultTouchableWithoutFeedback: string, resultTouchableNativeFeedback: string, + resultLongTaskAction: string, trackingConsent: TrackingConsent, trackingConsentModalVisible: boolean } @@ -93,6 +94,25 @@ export default class MainScreen extends Component { .then((response) => response.data); } + /** + * Blocks the JavaScript thread synchronously, which the SDK reports as a JS long task + * (see `longTaskThresholdMs` in ddUtils.tsx). The long task is forwarded to the native + * RUM monitor, and continuous profiling attaches the in-flight profile to it: a + * continuous profile is only uploaded when its window captured a long task, an app + * hang/ANR, or a vital. Without an event like this, profiles are collected and dropped. + */ + triggerJsLongTask(durationMs: number = 500) { + const start = Date.now(); + // Busy-wait on purpose: yielding would let the JS thread breathe and no long task + // would be recorded. + while (Date.now() - start < durationMs) { + // no-op + } + this.setState({ + resultLongTaskAction: `Blocked the JS thread for ${durationMs}ms` + } as MainScreenState); + } + componentDidMount() { this.updateTrackingConsent() DdLogs.debug("[DATADOG SDK] Test React Native Debug Log"); @@ -203,6 +223,12 @@ export default class MainScreen extends Component { Click me + {this.state.resultLongTaskAction} +