From 5d55591adeb756e9f52166d1c4157577dfd991ca Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Fri, 5 May 2023 22:30:16 +0200 Subject: [PATCH 01/40] Add profiling start and stop to RNSentry Android module --- .../io/sentry/react/RNSentryModuleImpl.java | 50 +++++++++++++++++++ .../java/io/sentry/react/RNSentryPackage.java | 3 +- .../java/io/sentry/react/RNSentryModule.java | 11 ++++ .../MainApplication.java | 17 +++++++ .../src/Screens/HomeScreen.tsx | 16 ++++++ src/js/NativeRNSentry.ts | 2 + src/js/index.ts | 2 + src/js/wrapper.ts | 28 +++++++++++ 8 files changed, 128 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java b/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java index daa1b94b0..5fdba78b9 100644 --- a/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +++ b/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java @@ -14,6 +14,7 @@ import androidx.core.app.FrameMetricsAggregator; import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.JavaScriptExecutorFactory; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReadableArray; @@ -26,9 +27,12 @@ import com.facebook.react.bridge.WritableNativeMap; import java.io.BufferedInputStream; +import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; import java.util.HashMap; @@ -69,6 +73,8 @@ public class RNSentryModuleImpl { public static final String NAME = "RNSentry"; + private static JavaScriptExecutorFactory javaScriptExecutorFactory = null; + private static final ILogger logger = new AndroidLogger(NAME); private static final BuildInfoProvider buildInfo = new BuildInfoProvider(logger); private static final String modulesPath = "modules.json"; @@ -88,6 +94,10 @@ public class RNSentryModuleImpl { private static final int SCREENSHOT_TIMEOUT_SECONDS = 2; + public static void setJavaScriptExecutorFactory(JavaScriptExecutorFactory javaScriptExecutorFactory) { + RNSentryModuleImpl.javaScriptExecutorFactory = javaScriptExecutorFactory; + } + public RNSentryModuleImpl(ReactApplicationContext reactApplicationContext) { packageInfo = getPackageInfo(reactApplicationContext); this.reactApplicationContext = reactApplicationContext; @@ -615,6 +625,46 @@ public void disableNativeFramesTracking() { } } + public void startProfiling(Promise promise) { + final WritableMap result = new WritableNativeMap(); + try { + RNSentryModuleImpl.javaScriptExecutorFactory.startSamplingProfiler(); + } catch (UnsupportedOperationException e) { + String error = javaScriptExecutorFactory.toString() + "does not support Sampling Profiler"; + logger.log(SentryLevel.ERROR, error); + result.putString("error", error); + } + promise.resolve(result); + } + + public void stopProfiling(Promise promise) { + final WritableMap result = new WritableNativeMap(); + try { + final File output = File.createTempFile( + "sampling-profiler-trace", ".cpuprofile", reactApplicationContext.getCacheDir()); + RNSentryModuleImpl.javaScriptExecutorFactory.stopSamplingProfiler(output.getPath()); + + StringBuilder text = new StringBuilder(); + BufferedReader br = new BufferedReader(new FileReader(output)); + String line; + while ((line = br.readLine()) != null) { + text.append(line); + text.append('\n'); + } + + result.putString("data", text.toString()); + } catch (IOException e) { + final String error = "Could not create temporary file for saving results from Sampling Profiler"; + logger.log(SentryLevel.ERROR, error); + result.putString("error", error); + } catch (UnsupportedOperationException e) { + final String error = javaScriptExecutorFactory.toString() + "does not support Sampling Profiler"; + logger.log(SentryLevel.ERROR, error); + result.putString("error", error); + } + promise.resolve(result); + } + private void setEventOriginTag(SentryEvent event) { SdkVersion sdk = event.getSdk(); if (sdk != null) { diff --git a/android/src/main/java/io/sentry/react/RNSentryPackage.java b/android/src/main/java/io/sentry/react/RNSentryPackage.java index eb2fd90f2..c9ccdbde9 100644 --- a/android/src/main/java/io/sentry/react/RNSentryPackage.java +++ b/android/src/main/java/io/sentry/react/RNSentryPackage.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; +import com.facebook.react.bridge.JavaScriptExecutorFactory; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.module.model.ReactModuleInfo; @@ -17,7 +18,7 @@ public class RNSentryPackage extends TurboReactPackage { @Override public NativeModule getModule(String name, ReactApplicationContext reactContext) { if (name.equals(RNSentryModuleImpl.NAME)) { - return new RNSentryModule(reactContext); + return new io.sentry.react.RNSentryModule(reactContext); } else { return null; } diff --git a/android/src/newarch/java/io/sentry/react/RNSentryModule.java b/android/src/newarch/java/io/sentry/react/RNSentryModule.java index 40eb787b1..b67c50e15 100644 --- a/android/src/newarch/java/io/sentry/react/RNSentryModule.java +++ b/android/src/newarch/java/io/sentry/react/RNSentryModule.java @@ -2,6 +2,7 @@ import androidx.annotation.NonNull; +import com.facebook.react.bridge.JavaScriptExecutorFactory; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; @@ -121,4 +122,14 @@ public void fetchNativeDeviceContexts(Promise promise) { public void fetchNativeSdkInfo(Promise promise) { // Not used on android } + + @Override + public void startProfiling(Promise promise) { + this.impl.startProfiling(promise); + } + + @Override + public void stopProfiling(Promise promise) { + this.impl.stopProfiling(promise); + } } diff --git a/sample-new-architecture/android/app/src/main/java/com/samplenewarchitecture/MainApplication.java b/sample-new-architecture/android/app/src/main/java/com/samplenewarchitecture/MainApplication.java index cae11eb38..444d6418d 100644 --- a/sample-new-architecture/android/app/src/main/java/com/samplenewarchitecture/MainApplication.java +++ b/sample-new-architecture/android/app/src/main/java/com/samplenewarchitecture/MainApplication.java @@ -1,21 +1,32 @@ package com.samplenewarchitecture; import android.app.Application; + +import com.facebook.hermes.reactexecutor.HermesExecutorFactory; import com.facebook.react.PackageList; import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.JavaScriptExecutor; +import com.facebook.react.bridge.JavaScriptExecutorFactory; +import com.facebook.react.common.JavascriptException; import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; import com.facebook.react.defaults.DefaultReactNativeHost; import com.facebook.soloader.SoLoader; import java.util.List; +import javax.annotation.Nullable; + +import io.sentry.react.RNSentryModuleImpl; import io.sentry.react.RNSentryPackage; public class MainApplication extends Application implements ReactApplication { + private final JavaScriptExecutorFactory mJavaScriptExecutorFactory = new HermesExecutorFactory(); + private final ReactNativeHost mReactNativeHost = new DefaultReactNativeHost(this) { + @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; @@ -52,6 +63,12 @@ protected boolean isNewArchEnabled() { protected Boolean isHermesEnabled() { return BuildConfig.IS_HERMES_ENABLED; } + + @Override + protected @Nullable JavaScriptExecutorFactory getJavaScriptExecutorFactory() { + RNSentryModuleImpl.setJavaScriptExecutorFactory(mJavaScriptExecutorFactory); + return mJavaScriptExecutorFactory; + } }; @Override diff --git a/sample-new-architecture/src/Screens/HomeScreen.tsx b/sample-new-architecture/src/Screens/HomeScreen.tsx index 7a576e7e6..a2779ed3b 100644 --- a/sample-new-architecture/src/Screens/HomeScreen.tsx +++ b/sample-new-architecture/src/Screens/HomeScreen.tsx @@ -121,6 +121,22 @@ const HomeScreen = (props: Props) => { +