diff --git a/apps/common-app/src/apps/reanimated/examples/SystraceSectionExample.tsx b/apps/common-app/src/apps/reanimated/examples/SystraceSectionExample.tsx
new file mode 100644
index 000000000000..8eed6bf1158d
--- /dev/null
+++ b/apps/common-app/src/apps/reanimated/examples/SystraceSectionExample.tsx
@@ -0,0 +1,39 @@
+import { Button, StyleSheet, Text, View } from 'react-native';
+
+import React from 'react';
+import { scheduleOnUI } from 'react-native-worklets';
+
+declare global {
+ var _beginSection: (name: string) => void;
+ var _endSection: () => void;
+}
+
+function handlePress() {
+ scheduleOnUI(() => {
+ globalThis._beginSection('SystraceSectionExample');
+ const start = performance.now();
+ // eslint-disable-next-line no-empty
+ while (performance.now() - start < 1000) {}
+ globalThis._endSection();
+ });
+}
+
+export default function SystraceSectionExample() {
+ return (
+
+ Hello world!
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+});
diff --git a/apps/common-app/src/apps/reanimated/examples/index.ts b/apps/common-app/src/apps/reanimated/examples/index.ts
index 717df60cf76c..e38b49e84b3e 100644
--- a/apps/common-app/src/apps/reanimated/examples/index.ts
+++ b/apps/common-app/src/apps/reanimated/examples/index.ts
@@ -144,6 +144,7 @@ import SvgExample from './SvgExample';
import SwipeableListExample from './SwipeableListExample';
import SynchronizablePerformanceExample from './SynchronizableExample';
import SynchronousPropsExample from './SynchronousPropsExample';
+import SystraceSectionExample from './SystraceSectionExample';
import ThirdPartyComponentsExample from './ThirdPartyComponentsExample';
import TransformExample from './TransformExample';
import TransformOriginExample from './TransformOriginExample';
@@ -198,6 +199,11 @@ export const EXAMPLES: Record = {
title: 'FPS',
screen: FpsExample,
},
+ SystraceSectionExample: {
+ icon: '📊',
+ title: 'Systrace section',
+ screen: SystraceSectionExample,
+ },
SyncBackToReactExample: {
icon: '🔄',
title: 'Sync back to React',
diff --git a/packages/react-native-reanimated/src/privateGlobals.d.ts b/packages/react-native-reanimated/src/privateGlobals.d.ts
index d55fa739592a..a6d714d3d8f2 100644
--- a/packages/react-native-reanimated/src/privateGlobals.d.ts
+++ b/packages/react-native-reanimated/src/privateGlobals.d.ts
@@ -24,6 +24,8 @@ declare global {
var _REANIMATED_VERSION_JS: string | undefined;
var __reanimatedModuleProxy: ReanimatedModuleProxy | undefined;
var _log: (value: unknown) => void;
+ var _beginSection: (name: string) => void;
+ var _endSection: () => void;
var _notifyAboutProgress: (
tag: number,
value: Record
diff --git a/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.cpp b/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.cpp
index 025795293d9e..81438dc8e1d0 100644
--- a/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.cpp
+++ b/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntimeDecorator.cpp
@@ -7,11 +7,36 @@
#include
#include
+#ifdef ANDROID
+#include
+#endif
+
+#if defined(__APPLE__)
+#include
+#if OS_LOG_TARGET_HAS_10_15_FEATURES
+#include
+#include
+#endif
+#endif
+
#include
#include
#include
#include
+#if defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES
+static os_log_t workletsInstrumentsLogHandle = nullptr;
+static thread_local os_signpost_id_t tls_signpostId = OS_SIGNPOST_ID_INVALID;
+static thread_local std::string tls_signpostName;
+
+static os_log_t getWorkletsInstrumentsLogHandle() {
+ if (!workletsInstrumentsLogHandle) {
+ workletsInstrumentsLogHandle = os_log_create("dev.worklets.instruments", OS_LOG_CATEGORY_POINTS_OF_INTEREST);
+ }
+ return workletsInstrumentsLogHandle;
+}
+#endif
+
namespace worklets {
static inline double performanceNow() {
@@ -87,6 +112,33 @@ void WorkletRuntimeDecorator::decorate(
return jsi::String::createFromUtf8(rt, stringifyJSIValue(rt, value));
});
+ jsi_utils::installJsiFunction(rt, "_beginSection", [](jsi::Runtime &rt, const jsi::Value &nameValue) {
+#ifdef ANDROID
+ ATrace_beginSection(nameValue.asString(rt).utf8(rt).c_str());
+#elif defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES
+ os_log_t logHandle = getWorkletsInstrumentsLogHandle();
+ if (os_signpost_enabled(logHandle)) {
+ tls_signpostName = nameValue.asString(rt).utf8(rt);
+ tls_signpostId = os_signpost_id_make_with_pointer(logHandle, &tls_signpostId);
+ os_signpost_interval_begin(logHandle, tls_signpostId, "Worklets", "%s", tls_signpostName.c_str());
+ }
+#endif
+ return jsi::Value::undefined();
+ });
+
+ jsi_utils::installJsiFunction(rt, "_endSection", [](jsi::Runtime &rt) {
+#ifdef ANDROID
+ ATrace_endSection();
+#elif defined(__APPLE__) && OS_LOG_TARGET_HAS_10_15_FEATURES
+ os_log_t logHandle = getWorkletsInstrumentsLogHandle();
+ if (os_signpost_enabled(logHandle) && tls_signpostId != OS_SIGNPOST_ID_INVALID) {
+ os_signpost_interval_end(logHandle, tls_signpostId, "Worklets", "%s end", tls_signpostName.c_str());
+ tls_signpostId = OS_SIGNPOST_ID_INVALID;
+ }
+#endif
+ return jsi::Value::undefined();
+ });
+
jsi_utils::installJsiFunction(
rt, "_createSerializable", [](jsi::Runtime &rt, const jsi::Value &value, const jsi::Value &nativeStateSource) {
auto shouldRetainRemote = jsi::Value::undefined();
diff --git a/packages/react-native-worklets/android/CMakeLists.txt b/packages/react-native-worklets/android/CMakeLists.txt
index 40a5fa103cdf..0c958f58eb0b 100644
--- a/packages/react-native-worklets/android/CMakeLists.txt
+++ b/packages/react-native-worklets/android/CMakeLists.txt
@@ -99,7 +99,7 @@ target_include_directories(
# build shared lib
set_target_properties(worklets PROPERTIES LINKER_LANGUAGE CXX)
-target_link_libraries(worklets log ReactAndroid::reactnative ReactAndroid::jsi
+target_link_libraries(worklets android log ReactAndroid::reactnative ReactAndroid::jsi
fbjni::fbjni)
if(${JS_RUNTIME} STREQUAL "hermes")
diff --git a/packages/react-native-worklets/src/privateGlobals.d.ts b/packages/react-native-worklets/src/privateGlobals.d.ts
index 03f38af8d4ad..460f33a66c7e 100644
--- a/packages/react-native-worklets/src/privateGlobals.d.ts
+++ b/packages/react-native-worklets/src/privateGlobals.d.ts
@@ -73,6 +73,8 @@ declare global {
var __flushAnimationFrame: (timestamp: number) => void;
var __frameTimestamp: number | undefined;
var _log: (value: unknown) => void;
+ var _beginSection: (name: string) => void;
+ var _endSection: () => void;
var _getAnimationTimestamp: () => number;
var _scheduleOnRuntime: (
runtime: WorkletRuntime,