react-native-nitro-tracing 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +21 -0
  3. package/NitroTracing.podspec +32 -0
  4. package/README.md +75 -0
  5. package/android/CMakeLists.txt +49 -0
  6. package/android/build.gradle +133 -0
  7. package/android/fix-prefab.gradle +51 -0
  8. package/android/gradle.properties +5 -0
  9. package/android/proguard-rules.pro +5 -0
  10. package/android/src/main/AndroidManifest.xml +2 -0
  11. package/android/src/main/cpp/cpp-adapter.cpp +47 -0
  12. package/android/src/main/java/com/margelo/nitro/nitrotracing/HermesSamplingGuard.kt +43 -0
  13. package/android/src/main/java/com/margelo/nitro/nitrotracing/NitroTracingFrameSource.kt +36 -0
  14. package/android/src/main/java/com/margelo/nitro/nitrotracing/NitroTracingPackage.kt +31 -0
  15. package/client.ts +15 -0
  16. package/cpp/HermesSampling.cpp +26 -0
  17. package/cpp/HermesSampling.hpp +10 -0
  18. package/cpp/HybridRecording.cpp +66 -0
  19. package/cpp/HybridRecording.hpp +30 -0
  20. package/cpp/HybridTraceSpan.cpp +10 -0
  21. package/cpp/HybridTraceSpan.hpp +17 -0
  22. package/cpp/HybridTracing.cpp +10 -0
  23. package/cpp/HybridTracing.hpp +10 -0
  24. package/cpp/NativeConversions.cpp +77 -0
  25. package/cpp/NativeConversions.hpp +15 -0
  26. package/cpp/core/NativeSampler.cpp +152 -0
  27. package/cpp/core/NativeSampler.hpp +56 -0
  28. package/cpp/core/Recorder.cpp +218 -0
  29. package/cpp/core/Recorder.hpp +60 -0
  30. package/cpp/core/Retention.cpp +26 -0
  31. package/cpp/core/Retention.hpp +7 -0
  32. package/cpp/core/TraceJson.cpp +214 -0
  33. package/cpp/core/TraceJson.hpp +7 -0
  34. package/cpp/core/TraceTypes.hpp +62 -0
  35. package/expo.ts +2 -0
  36. package/ios/NitroTracingFrameSource.mm +35 -0
  37. package/lib/commonjs/client.cjs +767 -0
  38. package/lib/commonjs/client.cjs.map +7 -0
  39. package/lib/commonjs/expo.cjs +88 -0
  40. package/lib/commonjs/expo.cjs.map +7 -0
  41. package/lib/commonjs/index.cjs +28 -0
  42. package/lib/commonjs/index.cjs.map +7 -0
  43. package/lib/commonjs/performance.cjs +112 -0
  44. package/lib/commonjs/performance.cjs.map +7 -0
  45. package/lib/commonjs/plugins.cjs +477 -0
  46. package/lib/commonjs/plugins.cjs.map +7 -0
  47. package/lib/commonjs/react.cjs +3833 -0
  48. package/lib/commonjs/react.cjs.map +7 -0
  49. package/lib/commonjs/release-profiler.cjs +142 -0
  50. package/lib/commonjs/release-profiler.cjs.map +7 -0
  51. package/lib/commonjs/sentry.cjs +188 -0
  52. package/lib/commonjs/sentry.cjs.map +7 -0
  53. package/lib/module/client.mjs +768 -0
  54. package/lib/module/client.mjs.map +7 -0
  55. package/lib/module/expo.mjs +62 -0
  56. package/lib/module/expo.mjs.map +7 -0
  57. package/lib/module/index.mjs +7 -0
  58. package/lib/module/index.mjs.map +7 -0
  59. package/lib/module/performance.mjs +89 -0
  60. package/lib/module/performance.mjs.map +7 -0
  61. package/lib/module/plugins.mjs +461 -0
  62. package/lib/module/plugins.mjs.map +7 -0
  63. package/lib/module/react.mjs +3851 -0
  64. package/lib/module/react.mjs.map +7 -0
  65. package/lib/module/release-profiler.mjs +137 -0
  66. package/lib/module/release-profiler.mjs.map +7 -0
  67. package/lib/module/sentry.mjs +165 -0
  68. package/lib/module/sentry.mjs.map +7 -0
  69. package/lib/typescript/client.d.ts +4 -0
  70. package/lib/typescript/expo.d.ts +2 -0
  71. package/lib/typescript/performance.d.ts +2 -0
  72. package/lib/typescript/plugins.d.ts +12 -0
  73. package/lib/typescript/react.d.ts +5 -0
  74. package/lib/typescript/release-profiler.d.ts +2 -0
  75. package/lib/typescript/sentry.d.ts +2 -0
  76. package/lib/typescript/src/client/TracingSink.d.ts +10 -0
  77. package/lib/typescript/src/client/createTraceClient.d.ts +82 -0
  78. package/lib/typescript/src/client/playground.d.ts +12 -0
  79. package/lib/typescript/src/expo/ExpoAdapter.d.ts +15 -0
  80. package/lib/typescript/src/expo/index.d.ts +2 -0
  81. package/lib/typescript/src/index.d.ts +6 -0
  82. package/lib/typescript/src/plugins/TracePlugin.d.ts +22 -0
  83. package/lib/typescript/src/plugins/errors.d.ts +15 -0
  84. package/lib/typescript/src/plugins/nativeMetrics.d.ts +13 -0
  85. package/lib/typescript/src/plugins/navigation.d.ts +29 -0
  86. package/lib/typescript/src/plugins/network.d.ts +23 -0
  87. package/lib/typescript/src/plugins/orderSpans.d.ts +3 -0
  88. package/lib/typescript/src/plugins/performance.d.ts +13 -0
  89. package/lib/typescript/src/plugins/releaseProfiler.d.ts +25 -0
  90. package/lib/typescript/src/plugins/runtimeMetrics.d.ts +17 -0
  91. package/lib/typescript/src/plugins/sentry.d.ts +15 -0
  92. package/lib/typescript/src/plugins/startPlugins.d.ts +4 -0
  93. package/lib/typescript/src/react/DetailView.d.ts +11 -0
  94. package/lib/typescript/src/react/ExplorerFilters.d.ts +9 -0
  95. package/lib/typescript/src/react/ExplorerView.d.ts +9 -0
  96. package/lib/typescript/src/react/InspectorControls.d.ts +229 -0
  97. package/lib/typescript/src/react/InspectorHeader.d.ts +8 -0
  98. package/lib/typescript/src/react/InspectorSheet.d.ts +16 -0
  99. package/lib/typescript/src/react/MetricsView.d.ts +7 -0
  100. package/lib/typescript/src/react/SummaryView.d.ts +11 -0
  101. package/lib/typescript/src/react/TimelineView.d.ts +8 -0
  102. package/lib/typescript/src/react/TraceInspector.d.ts +11 -0
  103. package/lib/typescript/src/react/TraceOverlay.d.ts +17 -0
  104. package/lib/typescript/src/react/explorerModel.d.ts +38 -0
  105. package/lib/typescript/src/react/labels.d.ts +203 -0
  106. package/lib/typescript/src/react/native.d.ts +42 -0
  107. package/lib/typescript/src/react/readSnapshot.d.ts +4 -0
  108. package/lib/typescript/src/react/store.d.ts +9 -0
  109. package/lib/typescript/src/react/topics.d.ts +80 -0
  110. package/lib/typescript/src/react/useAppReadyMetric.d.ts +3 -0
  111. package/lib/typescript/src/react/useLiveRecording.d.ts +4 -0
  112. package/lib/typescript/src/react/useTraceViewer.d.ts +140 -0
  113. package/lib/typescript/src/react/viewerModel.d.ts +59 -0
  114. package/lib/typescript/src/specs/Recording.nitro.d.ts +31 -0
  115. package/lib/typescript/src/specs/TraceSpan.nitro.d.ts +14 -0
  116. package/lib/typescript/src/specs/Tracing.nitro.d.ts +17 -0
  117. package/lib/typescript/src/types/CompletedSpanOptions.d.ts +15 -0
  118. package/lib/typescript/src/types/EventContext.d.ts +8 -0
  119. package/lib/typescript/src/types/MarkEvent.d.ts +4 -0
  120. package/lib/typescript/src/types/MarkOptions.d.ts +6 -0
  121. package/lib/typescript/src/types/MetricEvent.d.ts +8 -0
  122. package/lib/typescript/src/types/MetricOptions.d.ts +10 -0
  123. package/lib/typescript/src/types/NativeSamplingOptions.d.ts +7 -0
  124. package/lib/typescript/src/types/ReadOptions.d.ts +7 -0
  125. package/lib/typescript/src/types/RecordingOptions.d.ts +11 -0
  126. package/lib/typescript/src/types/RecordingStats.d.ts +25 -0
  127. package/lib/typescript/src/types/SpanEvent.d.ts +13 -0
  128. package/lib/typescript/src/types/SpanOptions.d.ts +6 -0
  129. package/lib/typescript/src/types/SpanOutcome.d.ts +2 -0
  130. package/lib/typescript/src/types/TraceAttribute.d.ts +7 -0
  131. package/lib/typescript/src/types/TraceAttributes.d.ts +3 -0
  132. package/lib/typescript/src/types/TraceContext.d.ts +10 -0
  133. package/lib/typescript/src/types/TracePage.d.ts +18 -0
  134. package/lib/typescript/src/types.d.ts +17 -0
  135. package/nitro.json +20 -0
  136. package/nitrogen/generated/.gitattributes +1 -0
  137. package/nitrogen/generated/android/NitroTracing+autolinking.cmake +83 -0
  138. package/nitrogen/generated/android/NitroTracing+autolinking.gradle +27 -0
  139. package/nitrogen/generated/android/NitroTracingOnLoad.cpp +49 -0
  140. package/nitrogen/generated/android/NitroTracingOnLoad.hpp +34 -0
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrotracing/NitroTracingOnLoad.kt +35 -0
  142. package/nitrogen/generated/ios/NitroTracing+autolinking.rb +62 -0
  143. package/nitrogen/generated/ios/NitroTracing-Swift-Cxx-Bridge.cpp +17 -0
  144. package/nitrogen/generated/ios/NitroTracing-Swift-Cxx-Bridge.hpp +27 -0
  145. package/nitrogen/generated/ios/NitroTracing-Swift-Cxx-Umbrella.hpp +38 -0
  146. package/nitrogen/generated/ios/NitroTracingAutolinking.mm +35 -0
  147. package/nitrogen/generated/ios/NitroTracingAutolinking.swift +16 -0
  148. package/nitrogen/generated/shared/c++/CompletedSpanOptions.hpp +122 -0
  149. package/nitrogen/generated/shared/c++/HybridRecordingSpec.cpp +31 -0
  150. package/nitrogen/generated/shared/c++/HybridRecordingSpec.hpp +100 -0
  151. package/nitrogen/generated/shared/c++/HybridTraceSpanSpec.cpp +23 -0
  152. package/nitrogen/generated/shared/c++/HybridTraceSpanSpec.hpp +65 -0
  153. package/nitrogen/generated/shared/c++/HybridTracingSpec.cpp +22 -0
  154. package/nitrogen/generated/shared/c++/HybridTracingSpec.hpp +68 -0
  155. package/nitrogen/generated/shared/c++/MarkEvent.hpp +102 -0
  156. package/nitrogen/generated/shared/c++/MarkOptions.hpp +99 -0
  157. package/nitrogen/generated/shared/c++/MetricEvent.hpp +110 -0
  158. package/nitrogen/generated/shared/c++/MetricOptions.hpp +107 -0
  159. package/nitrogen/generated/shared/c++/NativeSamplingOptions.hpp +87 -0
  160. package/nitrogen/generated/shared/c++/ReadOptions.hpp +87 -0
  161. package/nitrogen/generated/shared/c++/RecordingOptions.hpp +95 -0
  162. package/nitrogen/generated/shared/c++/RecordingStats.hpp +123 -0
  163. package/nitrogen/generated/shared/c++/SpanEvent.hpp +121 -0
  164. package/nitrogen/generated/shared/c++/SpanOptions.hpp +99 -0
  165. package/nitrogen/generated/shared/c++/SpanOutcome.hpp +84 -0
  166. package/nitrogen/generated/shared/c++/TraceAttribute.hpp +87 -0
  167. package/nitrogen/generated/shared/c++/TracePage.hpp +111 -0
  168. package/package.json +236 -0
  169. package/performance.ts +2 -0
  170. package/plugins.ts +24 -0
  171. package/react-native.config.js +16 -0
  172. package/react.ts +10 -0
  173. package/release-profiler.ts +6 -0
  174. package/sentry.ts +2 -0
  175. package/src/client/TracingSink.ts +25 -0
  176. package/src/client/createTraceClient.ts +439 -0
  177. package/src/client/playground.ts +143 -0
  178. package/src/expo/ExpoAdapter.ts +71 -0
  179. package/src/expo/index.ts +2 -0
  180. package/src/index.ts +8 -0
  181. package/src/plugins/TracePlugin.ts +22 -0
  182. package/src/plugins/errors.ts +69 -0
  183. package/src/plugins/nativeMetrics.ts +26 -0
  184. package/src/plugins/navigation.ts +111 -0
  185. package/src/plugins/network.ts +178 -0
  186. package/src/plugins/orderSpans.ts +22 -0
  187. package/src/plugins/performance.ts +120 -0
  188. package/src/plugins/releaseProfiler.ts +123 -0
  189. package/src/plugins/runtimeMetrics.ts +131 -0
  190. package/src/plugins/sentry.ts +176 -0
  191. package/src/plugins/startPlugins.ts +58 -0
  192. package/src/react/DetailView.tsx +514 -0
  193. package/src/react/ExplorerFilters.tsx +151 -0
  194. package/src/react/ExplorerView.tsx +243 -0
  195. package/src/react/InspectorControls.tsx +651 -0
  196. package/src/react/InspectorHeader.tsx +171 -0
  197. package/src/react/InspectorSheet.tsx +89 -0
  198. package/src/react/MetricsView.tsx +182 -0
  199. package/src/react/SummaryView.tsx +121 -0
  200. package/src/react/TimelineView.tsx +87 -0
  201. package/src/react/TraceInspector.tsx +196 -0
  202. package/src/react/TraceOverlay.tsx +585 -0
  203. package/src/react/explorerModel.ts +285 -0
  204. package/src/react/labels.ts +234 -0
  205. package/src/react/native.tsx +159 -0
  206. package/src/react/readSnapshot.ts +58 -0
  207. package/src/react/store.ts +31 -0
  208. package/src/react/topics.ts +427 -0
  209. package/src/react/useAppReadyMetric.ts +10 -0
  210. package/src/react/useLiveRecording.ts +43 -0
  211. package/src/react/useTraceViewer.ts +333 -0
  212. package/src/react/viewerModel.ts +172 -0
  213. package/src/specs/Recording.nitro.ts +40 -0
  214. package/src/specs/TraceSpan.nitro.ts +14 -0
  215. package/src/specs/Tracing.nitro.ts +14 -0
  216. package/src/types/CompletedSpanOptions.ts +15 -0
  217. package/src/types/EventContext.ts +8 -0
  218. package/src/types/MarkEvent.ts +3 -0
  219. package/src/types/MarkOptions.ts +6 -0
  220. package/src/types/MetricEvent.ts +8 -0
  221. package/src/types/MetricOptions.ts +10 -0
  222. package/src/types/NativeSamplingOptions.ts +7 -0
  223. package/src/types/ReadOptions.ts +7 -0
  224. package/src/types/RecordingOptions.ts +11 -0
  225. package/src/types/RecordingStats.ts +25 -0
  226. package/src/types/SpanEvent.ts +13 -0
  227. package/src/types/SpanOptions.ts +6 -0
  228. package/src/types/SpanOutcome.ts +2 -0
  229. package/src/types/TraceAttribute.ts +7 -0
  230. package/src/types/TraceAttributes.ts +4 -0
  231. package/src/types/TraceContext.ts +10 -0
  232. package/src/types/TracePage.ts +18 -0
  233. package/src/types.ts +17 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ ## 0.6.0
4
+
5
+ First public release. In-app performance tracing for React Native, for development, custom and UAT builds.
6
+
7
+ ### Features
8
+
9
+ - **native:** bounded C++ recorder with a monotonic clock, explicit parent IDs, and source span IDs that link parents completing after their children.
10
+ - **native:** native sampler thread for UI fps, max frame gap, slow and frozen frames (CADisplayLink / Choreographer), process CPU and memory.
11
+ - **native:** `Recording.exportTraceEvents()` writes Chrome Trace Event / Perfetto JSON: a lane per source, instants for marks, and counters for metrics.
12
+ - **native:** metric samples may use at most half of the buffer and roll over within it, so long sessions keep their spans. Only lost spans and marks count as dropped history.
13
+ - **native:** stats split lost history by kind (`droppedSpans`, `droppedMarks`, `droppedMetrics`). The inspector says "Lost 12 spans, 3 marks" instead of a bare count.
14
+ - **client:** `createTraceClient` owns the recording lifecycle, plugins, the `trace` facade (`mark`, `metric`, `measure`, `measureSince`) and sharing.
15
+ - **client:** `shareTrace('perfetto')` shares a trace without stopping the recording.
16
+ - **client:** `flag(note?)` marks a problem and captures a bounded Hermes CPU profile (`flagProfileMs`, default 10 s). The profile is linked from the export.
17
+ - **client:** keeps 10000 events / 6 MB by default for multi-hour UAT sessions.
18
+ - **plugins:** collectors for native metrics, navigation and screens (`createScreenTracker` for custom tabs), network (XHR and native fetch), JS errors, runtime metrics and the Performance API.
19
+ - **plugins:** Sentry as an alternative measurement layer: frames and app start map onto the same metric names. Optional span export.
20
+ - **inspector:**
21
+ - Summary triages issues by topic: startup, screens, network, responsiveness, resources, errors.
22
+ - Timeline groups events by screen visit.
23
+ - Explore has search, sort and draft filters, with trace waterfalls.
24
+ - Metrics carry units.
25
+ - **inspector:** native tab bar with Liquid Glass and native menus on iOS; React Native fallbacks elsewhere. Labels are localizable in the package.
26
+ - **inspector:** live overlay with a draggable FPS bubble, a non-modal sheet for the current screen, and Flag and Share buttons.
27
+ - **example:** Instruments-style activity generators for screens, network, stalls and errors.
28
+
29
+ ### Bug Fixes
30
+
31
+ - **native:** stopping a CPU profile really stops Hermes sampling on Android. React Native 0.85 binds `HermesSamplingProfiler.disable()` to the native enable, so `react-native-release-profiler`'s stop restarted the sampler. It then ran until the process died, and a dev reload aborted the app. `Tracing.disableHermesSampling()` stops it through the Hermes C++ API after every stop, and a ReactHost before-destroy guard stops it before reloads.
32
+ - **native:** armeabi-v7a builds compile (size_t narrowing).
33
+ - **inspector:** lists key rows by event sequence, so collectors that reuse a span ID cannot corrupt them.
34
+ - **inspector:** Android no longer crashes on AppCompat hosts: native tabs are iOS-only.
35
+
36
+ ### Documentation
37
+
38
+ - Guides for getting started, collectors, inspector and overlay, exporting, native API, architecture, troubleshooting and releasing.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 gugell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,32 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "NitroTracing"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported, :visionos => 1.0 }
14
+ s.source = { :http => "https://registry.npmjs.org/#{package["name"]}/-/#{package["name"]}-#{s.version}.tgz" }
15
+
16
+ s.source_files = [
17
+ # Implementation (Swift)
18
+ "ios/**/*.{swift}",
19
+ # Autolinking/Registration (Objective-C++)
20
+ "ios/**/*.{m,mm}",
21
+ # Implementation (C++ objects)
22
+ "cpp/**/*.{hpp,cpp}",
23
+ ]
24
+
25
+ load 'nitrogen/generated/ios/NitroTracing+autolinking.rb'
26
+ add_nitrogen_files(s)
27
+
28
+ s.frameworks = 'QuartzCore'
29
+ s.dependency 'React-jsi'
30
+ s.dependency 'React-callinvoker'
31
+ install_modules_dependencies(s)
32
+ end
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # react-native-nitro-tracing
2
+
3
+ In-app performance tracing for React Native, for development, custom and UAT builds. It records spans, marks and metrics into a bounded C++ recorder. Native collectors measure frames, CPU and memory, and a live inspector runs inside the app. Recordings export to [Perfetto](https://ui.perfetto.dev).
4
+
5
+ Requires the New Architecture, Hermes and [`react-native-nitro-modules`](https://nitro.margelo.com) ^0.37.1, in a development or native build. Expo Go cannot load it.
6
+
7
+ ```sh
8
+ npx expo install react-native-nitro-tracing react-native-nitro-modules
9
+ # inspector and overlay:
10
+ npx expo install react-native-safe-area-context @legendapp/list
11
+ ```
12
+
13
+ ```ts
14
+ import { createTraceClient } from 'react-native-nitro-tracing/client'
15
+ import { createExpoTraceSharing } from 'react-native-nitro-tracing/expo'
16
+ import {
17
+ createErrorsPlugin,
18
+ createNativeMetricsPlugin,
19
+ createNavigationPlugin,
20
+ createNetworkPlugin,
21
+ } from 'react-native-nitro-tracing/plugins'
22
+
23
+ export const tracing = createTraceClient({
24
+ ...createExpoTraceSharing(),
25
+ runtimeMetrics: true,
26
+ plugins: [
27
+ createNativeMetricsPlugin(),
28
+ createNavigationPlugin(navigationRef),
29
+ createNetworkPlugin(),
30
+ createErrorsPlugin(),
31
+ ],
32
+ })
33
+ await tracing.start()
34
+ ```
35
+
36
+ ```tsx
37
+ import { TraceInspector, TraceOverlay } from 'react-native-nitro-tracing/react'
38
+
39
+ {
40
+ showTracing && (
41
+ <>
42
+ <TraceOverlay client={tracing} />
43
+ <TraceInspector client={tracing} />
44
+ </>
45
+ )
46
+ }
47
+ ```
48
+
49
+ The package never gates itself on `__DEV__`; the app decides which builds include it. Tap **Flag** when something feels slow: it marks the moment and captures a CPU profile. **Share trace** sends a Perfetto file without stopping the recording.
50
+
51
+ ## Entry points
52
+
53
+ | Import | Contains | Peers |
54
+ | --------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
55
+ | `react-native-nitro-tracing` | Native `Tracing` API | `react-native-nitro-modules` |
56
+ | `react-native-nitro-tracing/client` | `createTraceClient`: lifecycle, `trace` facade, share, flag | — |
57
+ | `react-native-nitro-tracing/plugins` | Native metrics, navigation, network, errors, runtime metrics, `TracePlugin` | — |
58
+ | `react-native-nitro-tracing/react` | `TraceInspector`, `TraceOverlay`, `useAppReadyMetric`, `defaultLabels` | `react-native-safe-area-context`, `@legendapp/list`; optional `react-native-bottom-tabs`, `expo-glass-effect` |
59
+ | `react-native-nitro-tracing/expo` | Share sheet adapter, dev menu item | `expo-file-system`, `expo-sharing`, `expo-dev-client` |
60
+ | `react-native-nitro-tracing/performance` | Performance API marks and measures | optional `react-native-performance` |
61
+ | `react-native-nitro-tracing/sentry` | Sentry as a measurement layer, and span export | `@sentry/react-native` |
62
+ | `react-native-nitro-tracing/release-profiler` | Hermes CPU profiles for Flag and Tools | `react-native-release-profiler` |
63
+
64
+ ## Documentation
65
+
66
+ [Getting started](https://github.com/gugell/react-native-nitro-tracing/blob/main/docs/getting-started.md) ·
67
+ [Collectors](https://github.com/gugell/react-native-nitro-tracing/blob/main/docs/collectors.md) ·
68
+ [Inspector and overlay](https://github.com/gugell/react-native-nitro-tracing/blob/main/docs/inspector.md) ·
69
+ [Exporting traces](https://github.com/gugell/react-native-nitro-tracing/blob/main/docs/exporting.md) ·
70
+ [Native API](https://github.com/gugell/react-native-nitro-tracing/blob/main/docs/native-api.md) ·
71
+ [Architecture](https://github.com/gugell/react-native-nitro-tracing/blob/main/docs/architecture.md) ·
72
+ [Troubleshooting](https://github.com/gugell/react-native-nitro-tracing/blob/main/docs/troubleshooting.md) ·
73
+ [Changelog](https://github.com/gugell/react-native-nitro-tracing/blob/main/packages/react-native-nitro-tracing/CHANGELOG.md)
74
+
75
+ MIT
@@ -0,0 +1,49 @@
1
+ project(NitroTracing)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set (PACKAGE_NAME NitroTracing)
5
+ set (CMAKE_VERBOSE_MAKEFILE ON)
6
+ set (CMAKE_CXX_STANDARD 20)
7
+
8
+ # Define C++ library and add all sources
9
+ add_library(${PACKAGE_NAME} SHARED
10
+ src/main/cpp/cpp-adapter.cpp
11
+ ../cpp/HybridTracing.cpp
12
+ ../cpp/HybridRecording.cpp
13
+ ../cpp/HybridTraceSpan.cpp
14
+ ../cpp/HermesSampling.cpp
15
+ ../cpp/NativeConversions.cpp
16
+ ../cpp/core/Recorder.cpp
17
+ ../cpp/core/Retention.cpp
18
+ ../cpp/core/TraceJson.cpp
19
+ ../cpp/core/NativeSampler.cpp
20
+ )
21
+
22
+ # Add Nitrogen specs :)
23
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroTracing+autolinking.cmake)
24
+
25
+ # Set up local includes
26
+ include_directories(
27
+ "src/main/cpp"
28
+ "../cpp"
29
+ )
30
+
31
+ find_library(LOG_LIB log)
32
+
33
+ # Link all libraries together
34
+ target_link_libraries(
35
+ ${PACKAGE_NAME}
36
+ ${LOG_LIB}
37
+ android # <-- Android core
38
+ )
39
+
40
+ # Hermes C++ API, to stop the sampling profiler correctly (see cpp/HermesSampling.cpp).
41
+ # Optional: without Hermes the library builds and disableHermesSampling() returns false.
42
+ find_package(hermes-engine CONFIG)
43
+ if(TARGET hermes-engine::hermesvm)
44
+ target_link_libraries(${PACKAGE_NAME} hermes-engine::hermesvm)
45
+ target_compile_definitions(${PACKAGE_NAME} PRIVATE NITRO_TRACING_HERMES=1)
46
+ elseif(TARGET hermes-engine::libhermes)
47
+ target_link_libraries(${PACKAGE_NAME} hermes-engine::libhermes)
48
+ target_compile_definitions(${PACKAGE_NAME} PRIVATE NITRO_TRACING_HERMES=1)
49
+ endif()
@@ -0,0 +1,133 @@
1
+ buildscript {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+
7
+ dependencies {
8
+ classpath "com.android.tools.build:gradle:9.2.1"
9
+ }
10
+ }
11
+
12
+ def reactNativeArchitectures() {
13
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
14
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
15
+ }
16
+
17
+ def isNewArchitectureEnabled() {
18
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
19
+ }
20
+
21
+ apply plugin: "com.android.library"
22
+ apply plugin: 'org.jetbrains.kotlin.android'
23
+ apply from: '../nitrogen/generated/android/NitroTracing+autolinking.gradle'
24
+ apply from: "./fix-prefab.gradle"
25
+
26
+ def getExtOrDefault(name) {
27
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NitroTracing_" + name]
28
+ }
29
+
30
+ def getExtOrIntegerDefault(name) {
31
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NitroTracing_" + name]).toInteger()
32
+ }
33
+
34
+ android {
35
+ namespace "com.margelo.nitro.nitrotracing"
36
+
37
+ ndkVersion getExtOrDefault("ndkVersion")
38
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
39
+
40
+ defaultConfig {
41
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
42
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
43
+ consumerProguardFiles "proguard-rules.pro"
44
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
45
+
46
+ externalNativeBuild {
47
+ cmake {
48
+ cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all"
49
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
50
+ abiFilters (*reactNativeArchitectures())
51
+
52
+ buildTypes {
53
+ debug {
54
+ cppFlags "-O1 -g"
55
+ }
56
+ release {
57
+ cppFlags "-O2"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ externalNativeBuild {
65
+ cmake {
66
+ path "CMakeLists.txt"
67
+ }
68
+ }
69
+
70
+ packagingOptions {
71
+ excludes = [
72
+ "META-INF",
73
+ "META-INF/**",
74
+ "**/libc++_shared.so",
75
+ "**/libNitroModules.so",
76
+ "**/libfbjni.so",
77
+ "**/libjsi.so",
78
+ "**/libfolly_json.so",
79
+ "**/libfolly_runtime.so",
80
+ "**/libglog.so",
81
+ "**/libhermes.so",
82
+ "**/libhermesvm.so",
83
+ "**/libhermes-executor-debug.so",
84
+ "**/libhermes_executor.so",
85
+ "**/libreactnative.so",
86
+ "**/libreactnativejni.so",
87
+ "**/libturbomodulejsijni.so",
88
+ "**/libreact_nativemodule_core.so",
89
+ "**/libjscexecutor.so"
90
+ ]
91
+ }
92
+
93
+ buildFeatures {
94
+ buildConfig true
95
+ prefab true
96
+ }
97
+
98
+ buildTypes {
99
+ release {
100
+ minifyEnabled false
101
+ }
102
+ }
103
+
104
+ lintOptions {
105
+ disable "GradleCompatible"
106
+ }
107
+
108
+ compileOptions {
109
+ sourceCompatibility JavaVersion.VERSION_1_8
110
+ targetCompatibility JavaVersion.VERSION_1_8
111
+ }
112
+
113
+ }
114
+
115
+ repositories {
116
+ mavenCentral()
117
+ google()
118
+ }
119
+
120
+
121
+ dependencies {
122
+ // React Native is provided by the consuming app. Keeping this compileOnly
123
+ // prevents this library AAR from dexing RN classes a second time in release.
124
+ compileOnly "com.facebook.react:react-android"
125
+
126
+ // NitroModules is provided by the consuming app. Keeping this compileOnly
127
+ // avoids bundling NitroModules' runtime graph, including react-android, into
128
+ // this library AAR during release dexing.
129
+ compileOnly project(":react-native-nitro-modules")
130
+
131
+ // Hermes prefab headers and library, provided by the app at runtime.
132
+ compileOnly "com.facebook.react:hermes-android"
133
+ }
@@ -0,0 +1,51 @@
1
+ tasks.configureEach { task ->
2
+ // Make sure that we generate our prefab publication file only after having built the native library
3
+ // so that not a header publication file, but a full configuration publication will be generated, which
4
+ // will include the .so file
5
+
6
+ def prefabConfigurePattern = ~/^prefab(.+)ConfigurePackage$/
7
+ def matcher = task.name =~ prefabConfigurePattern
8
+ if (matcher.matches()) {
9
+ def variantName = matcher[0][1]
10
+ task.outputs.upToDateWhen { false }
11
+ task.dependsOn("externalNativeBuild${variantName}")
12
+ }
13
+ }
14
+
15
+ afterEvaluate {
16
+ def abis = reactNativeArchitectures()
17
+ rootProject.allprojects.each { proj ->
18
+ if (proj === rootProject) return
19
+
20
+ def dependsOnThisLib = proj.configurations.findAll { it.canBeResolved }.any { config ->
21
+ config.dependencies.any { dep ->
22
+ dep.group == project.group && dep.name == project.name
23
+ }
24
+ }
25
+ if (!dependsOnThisLib && proj != project) return
26
+
27
+ if (!proj.plugins.hasPlugin('com.android.application') && !proj.plugins.hasPlugin('com.android.library')) {
28
+ return
29
+ }
30
+
31
+ def variants = proj.android.hasProperty('applicationVariants') ? proj.android.applicationVariants : proj.android.libraryVariants
32
+ // Touch the prefab_config.json files to ensure that in ExternalNativeJsonGenerator.kt we will re-trigger the prefab CLI to
33
+ // generate a libnameConfig.cmake file that will contain our native library (.so).
34
+ // See this condition: https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/ExternalNativeJsonGenerator.kt;l=207-219?q=createPrefabBuildSystemGlue
35
+ variants.all { variant ->
36
+ def variantName = variant.name
37
+ abis.each { abi ->
38
+ def searchDir = new File(proj.projectDir, ".cxx/${variantName}")
39
+ if (!searchDir.exists()) return
40
+ def matches = []
41
+ searchDir.eachDir { randomDir ->
42
+ def prefabFile = new File(randomDir, "${abi}/prefab_config.json")
43
+ if (prefabFile.exists()) matches << prefabFile
44
+ }
45
+ matches.each { prefabConfig ->
46
+ prefabConfig.setLastModified(System.currentTimeMillis())
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,5 @@
1
+ NitroTracing_kotlinVersion=2.1.21
2
+ NitroTracing_minSdkVersion=23
3
+ NitroTracing_targetSdkVersion=36
4
+ NitroTracing_compileSdkVersion=36
5
+ NitroTracing_ndkVersion=29.0.14206865
@@ -0,0 +1,5 @@
1
+ # Called only from C++ over JNI; keep it through R8 in release builds.
2
+ -keep class com.margelo.nitro.nitrotracing.NitroTracingFrameSource { *; }
3
+ # Reload guard: instantiated from ReactModuleInfo by class name; its JNI bridge is native.
4
+ -keep class com.margelo.nitro.nitrotracing.HermesSamplingGuard { *; }
5
+ -keep class com.margelo.nitro.nitrotracing.HermesSampling { *; }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,47 @@
1
+ #include "HermesSampling.hpp"
2
+ #include "NitroTracingOnLoad.hpp"
3
+ #include "core/NativeSampler.hpp"
4
+ #include <fbjni/fbjni.h>
5
+ #include <jni.h>
6
+
7
+ using margelo::nitro::tracingcore::FrameMonitor;
8
+
9
+ namespace {
10
+ // Resolved on the loader thread: FindClass from other threads cannot see app classes.
11
+ jclass frameSourceClass = nullptr;
12
+ jmethodID setEnabled = nullptr;
13
+
14
+ void registerFrameSource(JNIEnv *env) {
15
+ jclass local = env->FindClass("com/margelo/nitro/nitrotracing/NitroTracingFrameSource");
16
+ if (!local) {
17
+ env->ExceptionClear();
18
+ return; // Frame metrics are optional; CPU and memory sampling still work.
19
+ }
20
+ frameSourceClass = static_cast<jclass>(env->NewGlobalRef(local));
21
+ env->DeleteLocalRef(local);
22
+ setEnabled = env->GetStaticMethodID(frameSourceClass, "setEnabled", "(Z)V");
23
+ FrameMonitor::instance().setToggle([](bool enabled) {
24
+ JNIEnv *env = facebook::jni::Environment::ensureCurrentThreadIsAttached();
25
+ env->CallStaticVoidMethod(frameSourceClass, setEnabled, static_cast<jboolean>(enabled));
26
+ if (env->ExceptionCheck())
27
+ env->ExceptionClear();
28
+ });
29
+ }
30
+ } // namespace
31
+
32
+ extern "C" JNIEXPORT void JNICALL
33
+ Java_com_margelo_nitro_nitrotracing_NitroTracingFrameSource_nativeOnFrame(JNIEnv *, jclass, jlong frameTimeNanos) {
34
+ // Choreographer frame times share the monotonic clock; 0 lets the monitor infer the interval.
35
+ FrameMonitor::instance().onFrame(static_cast<double>(frameTimeNanos) / 1e9, 0);
36
+ }
37
+
38
+ extern "C" JNIEXPORT jboolean JNICALL Java_com_margelo_nitro_nitrotracing_HermesSampling_disable(JNIEnv *, jclass) {
39
+ return margelo::nitro::nitrotracing::disableHermesSampling() ? JNI_TRUE : JNI_FALSE;
40
+ }
41
+
42
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
43
+ return facebook::jni::initialize(vm, [] {
44
+ margelo::nitro::nitrotracing::registerAllNatives();
45
+ registerFrameSource(facebook::jni::Environment::current());
46
+ });
47
+ }
@@ -0,0 +1,43 @@
1
+ package com.margelo.nitro.nitrotracing
2
+
3
+ import com.facebook.react.ReactApplication
4
+ import com.facebook.react.ReactHost
5
+ import com.facebook.react.bridge.BaseJavaModule
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.turbomodule.core.interfaces.TurboModule
8
+ import java.util.Collections
9
+ import java.util.WeakHashMap
10
+
11
+ /**
12
+ * Stops Hermes' sampling profiler before a reload tears down the JS runtime.
13
+ *
14
+ * The sampler is process-wide. If a reload destroys the JS thread while it samples, the
15
+ * sampler signals a dead thread and the app aborts (`invalid pthread_t` on `hermes-sampling`).
16
+ * React Native quits the JS thread before invalidating modules, so this registers a
17
+ * ReactHost before-destroy listener, which runs while the thread is still alive.
18
+ * Eagerly initialized; JS never calls it.
19
+ */
20
+ internal class HermesSamplingGuard(context: ReactApplicationContext) :
21
+ BaseJavaModule(context), TurboModule {
22
+ init {
23
+ (context.applicationContext as? ReactApplication)?.reactHost?.let(::watch)
24
+ }
25
+
26
+ override fun getName() = NAME
27
+
28
+ companion object {
29
+ const val NAME = "NitroTracingHermesSamplingGuard"
30
+ // One listener per host; a new module instance is created on every reload.
31
+ private val watched = Collections.newSetFromMap(WeakHashMap<ReactHost, Boolean>())
32
+
33
+ private fun watch(host: ReactHost) {
34
+ synchronized(watched) { if (!watched.add(host)) return }
35
+ host.addBeforeDestroyListener { runCatching { HermesSampling.disable() } }
36
+ }
37
+ }
38
+ }
39
+
40
+ /** JNI bridge to cpp/HermesSampling.cpp. */
41
+ internal object HermesSampling {
42
+ @JvmStatic external fun disable(): Boolean
43
+ }
@@ -0,0 +1,36 @@
1
+ package com.margelo.nitro.nitrotracing
2
+
3
+ import android.os.Handler
4
+ import android.os.Looper
5
+ import android.view.Choreographer
6
+
7
+ /**
8
+ * Main-thread Choreographer callbacks, started only while a native sampler needs frames.
9
+ * A callback delayed past its vsync means the main thread was busy; the C++ FrameMonitor
10
+ * infers the display interval from the shortest recent gap.
11
+ */
12
+ object NitroTracingFrameSource : Choreographer.FrameCallback {
13
+ private val main = Handler(Looper.getMainLooper())
14
+ private var running = false
15
+
16
+ @JvmStatic
17
+ fun setEnabled(enabled: Boolean) {
18
+ main.post {
19
+ if (enabled && !running) {
20
+ running = true
21
+ Choreographer.getInstance().postFrameCallback(this)
22
+ } else if (!enabled && running) {
23
+ running = false
24
+ Choreographer.getInstance().removeFrameCallback(this)
25
+ }
26
+ }
27
+ }
28
+
29
+ override fun doFrame(frameTimeNanos: Long) {
30
+ if (!running) return
31
+ nativeOnFrame(frameTimeNanos)
32
+ Choreographer.getInstance().postFrameCallback(this)
33
+ }
34
+
35
+ @JvmStatic private external fun nativeOnFrame(frameTimeNanos: Long)
36
+ }
@@ -0,0 +1,31 @@
1
+ package com.margelo.nitro.nitrotracing
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfo
7
+ import com.facebook.react.module.model.ReactModuleInfoProvider
8
+
9
+ class NitroTracingPackage : BaseReactPackage() {
10
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
11
+ if (name == HermesSamplingGuard.NAME) HermesSamplingGuard(reactContext) else null
12
+
13
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
14
+ mapOf(
15
+ HermesSamplingGuard.NAME to ReactModuleInfo(
16
+ HermesSamplingGuard.NAME,
17
+ HermesSamplingGuard::class.java.name,
18
+ false, // canOverrideExistingModule
19
+ true, // needsEagerInit: registers the reload guard at startup
20
+ false, // isCxxModule
21
+ true, // isTurboModule
22
+ ),
23
+ )
24
+ }
25
+
26
+ companion object {
27
+ init {
28
+ NitroTracingOnLoad.initializeNative()
29
+ }
30
+ }
31
+ }
package/client.ts ADDED
@@ -0,0 +1,15 @@
1
+ export { createTraceClient } from './src/client/createTraceClient'
2
+ export type {
3
+ TraceClient,
4
+ TraceClientOptions,
5
+ TraceClientSnapshot,
6
+ ClientAttributes,
7
+ TraceFormat,
8
+ } from './src/client/createTraceClient'
9
+
10
+ export type {
11
+ PlaygroundReport,
12
+ PlaygroundCheckId,
13
+ } from './src/client/playground'
14
+
15
+ export type { TracingEntry, TracingSink } from './src/client/TracingSink'
@@ -0,0 +1,26 @@
1
+ #include "HermesSampling.hpp"
2
+
3
+ #if defined(NITRO_TRACING_HERMES)
4
+ #include <hermes/hermes.h>
5
+ #endif
6
+
7
+ namespace margelo::nitro::nitrotracing {
8
+ bool disableHermesSampling() noexcept {
9
+ #if defined(NITRO_TRACING_HERMES)
10
+ // React Native 0.85's HermesSamplingProfiler binds its Java disable() to the native
11
+ // enable(), so on Android "stop profiling" leaves the sampler running. This is the call
12
+ // the binding should have made.
13
+ try {
14
+ auto *api = facebook::jsi::castInterface<facebook::hermes::IHermesRootAPI>(facebook::hermes::makeHermesRootAPI());
15
+ if (api == nullptr)
16
+ return false;
17
+ api->disableSamplingProfiler();
18
+ return true;
19
+ } catch (...) {
20
+ return false;
21
+ }
22
+ #else
23
+ return false;
24
+ #endif
25
+ }
26
+ } // namespace margelo::nitro::nitrotracing
@@ -0,0 +1,10 @@
1
+ #pragma once
2
+
3
+ namespace margelo::nitro::nitrotracing {
4
+ /**
5
+ * Stops Hermes' process-wide sampling profiler through the Hermes C++ API.
6
+ * Returns false when this build does not link Hermes (the iOS build, where the
7
+ * profiler's own stop works) or when Hermes refuses the call.
8
+ */
9
+ bool disableHermesSampling() noexcept;
10
+ } // namespace margelo::nitro::nitrotracing