Implement Kotlin API for PerformanceTracer in RNDT (#54316)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/54316

Changelog: [internal]

This defines an internal class in Kotlin to access `PerformanceTracer` methods, necessary for platform-specific integrations on Android.

Reviewed By: sbuggay

Differential Revision: D85689126

fbshipit-source-id: 84cb675984b90c677c3a322b191e24cc6b58c74f
This commit is contained in:
Rubén Norte
2025-10-29 15:35:35 -07:00
committed by meta-codesync[bot]
parent 712c544be6
commit b68329f7ca
4 changed files with 429 additions and 0 deletions
@@ -0,0 +1,109 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react.internal.tracing
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.bridge.ReadableNativeMap
import com.facebook.soloader.SoLoader
/**
* JNI interface to access PerformanceTracer methods from React Native Inspector Modern.
*
* This provides access to reportMark and reportMeasure methods which are used for performance
* tracing in the React Native DevTools.
*/
@DoNotStrip
public object PerformanceTracer {
init {
SoLoader.loadLibrary("react_performancetracerjni")
}
/** Callback interface for tracing state changes. */
@DoNotStrip
public interface TracingStateCallback {
/**
* Called when tracing state changes.
*
* @param isTracing true if tracing has started, false if tracing has stopped
*/
@DoNotStrip public fun onTracingStateChanged(isTracing: Boolean)
}
/**
* Report a Performance.mark() event - a labelled timestamp.
*
* @param name The name/label of the mark
* @param timestampNanos The timestamp in nanoseconds (monotonic time)
* @param detail Optional map with additional detail (pass null if not needed)
*/
@DoNotStrip
@JvmStatic
public external fun reportMark(name: String, timestampNanos: Long, detail: ReadableNativeMap?)
/**
* Report a Performance.measure() event - a labelled duration.
*
* @param name The name/label of the measure
* @param startTimestampNanos The start timestamp in nanoseconds (monotonic time)
* @param durationNanos The duration in nanoseconds
* @param detail Optional map with additional detail (pass null if not needed)
*/
@DoNotStrip
@JvmStatic
public external fun reportMeasure(
name: String,
startTimestampNanos: Long,
durationNanos: Long,
detail: ReadableNativeMap?,
)
/**
* Report a TimeStamp Trace Event - a labelled entry on Performance timeline.
*
* @param name The name/label of the timestamp
* @param startTimeNanos Start timestamp in nanoseconds (monotonic time)
* @param endTimeNanos End timestamp in nanoseconds (monotonic time)
* @param trackName Optional track name for organizing the timestamp
* @param trackGroup Optional track group for organizing the timestamp
* @param color Optional color name (e.g., "primary", "secondary", "error", "warning")
*/
@DoNotStrip
@JvmStatic
public external fun reportTimeStamp(
name: String,
startTimeNanos: Long,
endTimeNanos: Long,
trackName: String?,
trackGroup: String?,
color: String?,
)
/**
* Check if the tracer is currently tracing.
*
* @return true if tracing is active, false otherwise
*/
@DoNotStrip @JvmStatic public external fun isTracing(): Boolean
/**
* Subscribe to tracing state changes (start/stop events).
*
* @param callback The callback to invoke when tracing starts or stops
* @return A subscription ID that can be used to unsubscribe
*/
@DoNotStrip
@JvmStatic
public external fun subscribeToTracingStateChanges(callback: TracingStateCallback): Int
/**
* Unsubscribe from tracing state changes.
*
* @param subscriptionId The subscription ID returned from subscribeToTracingStateChanges
*/
@DoNotStrip @JvmStatic public external fun unsubscribeFromTracingStateChanges(subscriptionId: Int)
}
@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "PerformanceTracerCxxInterop.h"
#include <fbjni/fbjni.h>
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*unused*/) {
return facebook::jni::initialize(vm, [] {
facebook::react::PerformanceTracerCxxInterop::registerNatives();
});
}
@@ -0,0 +1,241 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "PerformanceTracerCxxInterop.h"
#include <folly/dynamic.h>
#include <jsinspector-modern/tracing/PerformanceTracer.h>
#include <react/jni/NativeMap.h>
#include <react/jni/ReadableNativeMap.h>
#include <react/timing/primitives.h>
#include <map>
#include <memory>
#include <mutex>
namespace facebook::react {
void TracingStateCallback::onTracingStateChanged(bool isTracing) const {
static const auto method =
javaClassStatic()->getMethod<void(jboolean)>("onTracingStateChanged");
method(self(), static_cast<jboolean>(isTracing));
}
namespace {
class TracingCallbackRegistry {
public:
static TracingCallbackRegistry& getInstance() {
static TracingCallbackRegistry instance;
return instance;
}
void registerCallback(
uint32_t subscriptionId,
jni::global_ref<jobject> callback) {
std::lock_guard<std::mutex> lock(mutex_);
callbacks_[subscriptionId] = std::move(callback);
}
void unregisterCallback(uint32_t subscriptionId) {
std::lock_guard<std::mutex> lock(mutex_);
callbacks_.erase(subscriptionId);
}
jni::global_ref<jobject> getCallback(uint32_t subscriptionId) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = callbacks_.find(subscriptionId);
if (it != callbacks_.end()) {
return it->second;
}
return nullptr;
}
private:
std::mutex mutex_;
std::map<uint32_t, jni::global_ref<jobject>> callbacks_;
};
} // namespace
void PerformanceTracerCxxInterop::reportMark(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/,
jni::alias_ref<jstring> name,
jlong timestampNanos,
jni::alias_ref<ReadableNativeMap::javaobject> detail) {
auto& tracer = jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (!tracer.isTracing()) {
return;
}
std::string nameStr = name->toStdString();
HighResTimeStamp timestamp = HighResTimeStamp::fromChronoSteadyClockTimePoint(
std::chrono::steady_clock::time_point(
std::chrono::nanoseconds(timestampNanos)));
folly::dynamic detailDynamic = nullptr;
if (detail) {
detailDynamic = detail->cthis()->consume();
}
tracer.reportMark(nameStr, timestamp, std::move(detailDynamic));
}
void PerformanceTracerCxxInterop::reportMeasure(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/,
jni::alias_ref<jstring> name,
jlong startTimestampNanos,
jlong durationNanos,
jni::alias_ref<ReadableNativeMap::javaobject> detail) {
auto& tracer = jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (!tracer.isTracing()) {
return;
}
std::string nameStr = name->toStdString();
HighResTimeStamp startTimestamp =
HighResTimeStamp::fromChronoSteadyClockTimePoint(
std::chrono::steady_clock::time_point(
std::chrono::nanoseconds(startTimestampNanos)));
HighResDuration duration = HighResDuration::fromNanoseconds(durationNanos);
folly::dynamic detailDynamic = nullptr;
if (detail) {
detailDynamic = detail->cthis()->consume();
}
tracer.reportMeasure(
nameStr, startTimestamp, duration, std::move(detailDynamic));
}
void PerformanceTracerCxxInterop::reportTimeStamp(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/,
jni::alias_ref<jstring> name,
jlong startTimeNanos,
jlong endTimeNanos,
jni::alias_ref<jstring> trackName,
jni::alias_ref<jstring> trackGroup,
jni::alias_ref<jstring> color) {
auto& tracer = jsinspector_modern::tracing::PerformanceTracer::getInstance();
if (!tracer.isTracing()) {
return;
}
std::string nameStr = name->toStdString();
// Convert start timestamp
HighResTimeStamp startTimestamp =
HighResTimeStamp::fromChronoSteadyClockTimePoint(
std::chrono::steady_clock::time_point(
std::chrono::nanoseconds(startTimeNanos)));
// Convert end timestamp
HighResTimeStamp endTimestamp =
HighResTimeStamp::fromChronoSteadyClockTimePoint(
std::chrono::steady_clock::time_point(
std::chrono::nanoseconds(endTimeNanos)));
// Handle optional string parameters
std::optional<std::string> trackNameOpt = std::nullopt;
if (trackName) {
trackNameOpt = trackName->toStdString();
}
std::optional<std::string> trackGroupOpt = std::nullopt;
if (trackGroup) {
trackGroupOpt = trackGroup->toStdString();
}
// Handle optional color parameter
std::optional<jsinspector_modern::tracing::ConsoleTimeStampColor> colorOpt =
std::nullopt;
if (color) {
colorOpt = jsinspector_modern::tracing::getConsoleTimeStampColorFromString(
color->toStdString());
}
tracer.reportTimeStamp(
nameStr,
startTimestamp,
endTimestamp,
trackNameOpt,
trackGroupOpt,
colorOpt);
}
jboolean PerformanceTracerCxxInterop::isTracing(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/) {
return static_cast<jboolean>(
jsinspector_modern::tracing::PerformanceTracer::getInstance()
.isTracing());
}
jint PerformanceTracerCxxInterop::subscribeToTracingStateChanges(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/,
jni::alias_ref<TracingStateCallback::javaobject> callback) {
auto globalCallback = jni::make_global(callback);
auto& tracer = jsinspector_modern::tracing::PerformanceTracer::getInstance();
auto callbackWrapper = std::make_shared<uint32_t>(0);
uint32_t subscriptionId =
tracer.subscribeToTracingStateChanges([callbackWrapper](bool isTracing) {
auto callback = TracingCallbackRegistry::getInstance().getCallback(
*callbackWrapper);
if (callback) {
try {
jni::ThreadScope::WithClassLoader([&] {
auto callbackClass = callback->getClass();
auto onTracingStateChangedMethod =
callbackClass->getMethod<void(jboolean)>(
"onTracingStateChanged");
onTracingStateChangedMethod(
callback, static_cast<jboolean>(isTracing));
});
} catch (const std::exception& e) {
}
}
});
*callbackWrapper = subscriptionId;
TracingCallbackRegistry::getInstance().registerCallback(
subscriptionId, globalCallback);
return static_cast<jint>(subscriptionId);
}
void PerformanceTracerCxxInterop::unsubscribeFromTracingStateChanges(
jni::alias_ref<PerformanceTracerCxxInterop> /*jthis*/,
jint subscriptionId) {
auto& tracer = jsinspector_modern::tracing::PerformanceTracer::getInstance();
tracer.unsubscribeFromTracingStateChanges(
static_cast<uint32_t>(subscriptionId));
TracingCallbackRegistry::getInstance().unregisterCallback(
static_cast<uint32_t>(subscriptionId));
}
void PerformanceTracerCxxInterop::registerNatives() {
javaClassLocal()->registerNatives(
{makeNativeMethod("reportMark", PerformanceTracerCxxInterop::reportMark),
makeNativeMethod(
"reportMeasure", PerformanceTracerCxxInterop::reportMeasure),
makeNativeMethod(
"reportTimeStamp", PerformanceTracerCxxInterop::reportTimeStamp),
makeNativeMethod("isTracing", PerformanceTracerCxxInterop::isTracing),
makeNativeMethod(
"subscribeToTracingStateChanges",
PerformanceTracerCxxInterop::subscribeToTracingStateChanges),
makeNativeMethod(
"unsubscribeFromTracingStateChanges",
PerformanceTracerCxxInterop::unsubscribeFromTracingStateChanges)});
}
} // namespace facebook::react
@@ -0,0 +1,63 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <fbjni/fbjni.h>
#include <jni.h>
#include <react/jni/ReadableNativeMap.h>
namespace facebook::react {
class TracingStateCallback : public facebook::jni::JavaClass<TracingStateCallback> {
public:
static constexpr auto kJavaDescriptor =
"Lcom/facebook/react/internal/tracing/PerformanceTracer$TracingStateCallback;";
void onTracingStateChanged(bool isTracing) const;
};
class PerformanceTracerCxxInterop : public facebook::jni::JavaClass<PerformanceTracerCxxInterop> {
public:
static constexpr auto kJavaDescriptor = "Lcom/facebook/react/internal/tracing/PerformanceTracer;";
static void reportMark(
facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis,
facebook::jni::alias_ref<jstring> name,
jlong timestampNanos,
facebook::jni::alias_ref<ReadableNativeMap::javaobject> detail);
static void reportMeasure(
facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis,
facebook::jni::alias_ref<jstring> name,
jlong startTimestampNanos,
jlong durationNanos,
facebook::jni::alias_ref<ReadableNativeMap::javaobject> detail);
static void reportTimeStamp(
facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis,
facebook::jni::alias_ref<jstring> name,
jlong startTimeNanos,
jlong endTimeNanos,
facebook::jni::alias_ref<jstring> trackName,
facebook::jni::alias_ref<jstring> trackGroup,
facebook::jni::alias_ref<jstring> color);
static jboolean isTracing(facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis);
static jint subscribeToTracingStateChanges(
facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis,
facebook::jni::alias_ref<TracingStateCallback::javaobject> callback);
static void unsubscribeFromTracingStateChanges(
facebook::jni::alias_ref<PerformanceTracerCxxInterop> jthis,
jint subscriptionId);
static void registerNatives();
};
} // namespace facebook::react