diff --git a/mobile/android/base/GeckoJavaSampler.java b/mobile/android/base/GeckoJavaSampler.java new file mode 100644 index 00000000000..130d5643f97 --- /dev/null +++ b/mobile/android/base/GeckoJavaSampler.java @@ -0,0 +1,184 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko; + +import android.util.Log; +import java.lang.Thread; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class GeckoJavaSampler { + private static final String LOGTAG = "JavaSampler"; + private static Thread sSamplingThread = null; + private static SamplingThread sSamplingRunnable = null; + private static Thread sMainThread = null; + + // Use the same timer primitive as the profiler + // to get a perfect sample syncing. + private static native double getProfilerTime(); + + private static class Sample { + public Frame[] mFrames; + public double mTime; + public Sample(StackTraceElement[] aStack) { + mFrames = new Frame[aStack.length]; + mTime = getProfilerTime(); + for (int i = 0; i < aStack.length; i++) { + mFrames[aStack.length - 1 - i] = new Frame(); + mFrames[aStack.length - 1 - i].fileName = aStack[i].getFileName(); + mFrames[aStack.length - 1 - i].lineNo = aStack[i].getLineNumber(); + mFrames[aStack.length - 1 - i].methodName = aStack[i].getMethodName(); + mFrames[aStack.length - 1 - i].className = aStack[i].getClassName(); + } + } + } + private static class Frame { + public String fileName; + public int lineNo; + public String methodName; + public String className; + } + + private static class SamplingThread implements Runnable { + private final int mInterval; + private final int mSampleCount; + + private boolean mPauseSampler = false; + private boolean mStopSampler = false; + + private Map mSamples = new HashMap(); + private int mSamplePos; + + public SamplingThread(final int aInterval, final int aSampleCount) { + // If we sample faster then 10ms we get to many missed samples + mInterval = Math.max(10, aInterval); + mSampleCount = aSampleCount; + } + + public void run() { + synchronized (GeckoJavaSampler.class) { + mSamples.put(0, new Sample[mSampleCount]); + mSamplePos = 0; + + // Find the main thread + Set threadSet = Thread.getAllStackTraces().keySet(); + for (Thread t : threadSet) { + if (t.getName().compareToIgnoreCase("main") == 0) { + sMainThread = t; + break; + } + } + + if (sMainThread == null) { + Log.e(LOGTAG, "Main thread not found"); + return; + } + } + + while (true) { + try { + Thread.sleep(mInterval); + } catch (InterruptedException e) { + e.printStackTrace(); + } + synchronized (GeckoJavaSampler.class) { + if (!mPauseSampler) { + StackTraceElement[] bt = sMainThread.getStackTrace(); + mSamples.get(0)[mSamplePos] = new Sample(bt); + mSamplePos = (mSamplePos+1) % mSamples.get(0).length; + } + if (mStopSampler) { + break; + } + } + } + } + + private Sample getSample(int aThreadId, int aSampleId) { + if (aThreadId < mSamples.size() && aSampleId < mSamples.get(aThreadId).length && + mSamples.get(aThreadId)[aSampleId] != null) { + int startPos = 0; + if (mSamples.get(aThreadId)[mSamplePos] != null) { + startPos = mSamplePos; + } + int readPos = (startPos + aSampleId) % mSamples.get(aThreadId).length; + return mSamples.get(aThreadId)[readPos]; + } + return null; + } + } + + public synchronized static String getThreadName(int aThreadId) { + if (aThreadId == 0 && sMainThread != null) { + return sMainThread.getName(); + } + return null; + } + + private synchronized static Sample getSample(int aThreadId, int aSampleId) { + return sSamplingRunnable.getSample(aThreadId, aSampleId); + } + public synchronized static double getSampleTime(int aThreadId, int aSampleId) { + Sample sample = getSample(aThreadId, aSampleId); + if (sample != null) { + System.out.println("Sample: " + sample.mTime); + return sample.mTime; + } + return 0; + } + public synchronized static String getFrameName(int aThreadId, int aSampleId, int aFrameId) { + Sample sample = getSample(aThreadId, aSampleId); + if (sample != null && aFrameId < sample.mFrames.length) { + Frame frame = sample.mFrames[aFrameId]; + if (frame == null) { + return null; + } + return frame.className + "." + frame.methodName + "()"; + } + return null; + } + + public static void start(int aInterval, int aSamples) { + synchronized (GeckoJavaSampler.class) { + sSamplingRunnable = new SamplingThread(aInterval, aSamples); + sSamplingThread = new Thread(sSamplingRunnable, "Java Sampler"); + sSamplingThread.start(); + } + } + + public static void pause() { + synchronized (GeckoJavaSampler.class) { + sSamplingRunnable.mPauseSampler = true; + } + } + + public static void unpause() { + synchronized (GeckoJavaSampler.class) { + sSamplingRunnable.mPauseSampler = false; + } + } + + public static void stop() { + synchronized (GeckoJavaSampler.class) { + if (sSamplingThread == null) { + return; + } + + sSamplingRunnable.mStopSampler = true; + try { + sSamplingThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + sSamplingThread = null; + sSamplingRunnable = null; + } + } +} + + + diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 2877d6de239..caca727613f 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -107,6 +107,7 @@ FENNEC_JAVA_FILES = \ GeckoPopupMenu.java \ GeckoSmsManager.java \ GeckoThread.java \ + GeckoJavaSampler.java \ GlobalHistory.java \ GeckoViewsFactory.java \ HeightChangeAnimation.java \ @@ -1152,6 +1153,7 @@ jars: CLASSES_WITH_JNI= \ org.mozilla.gecko.GeckoAppShell \ + org.mozilla.gecko.GeckoJavaSampler \ $(NULL) ifdef MOZ_WEBSMS_BACKEND diff --git a/mobile/android/base/jni-generator.py b/mobile/android/base/jni-generator.py index 15ccee4758e..c7988152b52 100644 --- a/mobile/android/base/jni-generator.py +++ b/mobile/android/base/jni-generator.py @@ -69,7 +69,7 @@ class Generator: returnValue = '' elif returnType == 'jobject': returnValue = 'NULL' - elif returnType in ('jint', 'jfloat'): + elif returnType in ('jint', 'jfloat', 'jdouble'): returnValue = '0' else: raise Exception(('Unsupported JNI return type %s found; ' diff --git a/mozglue/android/jni-stubs.inc b/mozglue/android/jni-stubs.inc index 33a8a2208ea..67e6444e371 100644 --- a/mozglue/android/jni-stubs.inc +++ b/mozglue/android/jni-stubs.inc @@ -379,3 +379,22 @@ Java_org_mozilla_gecko_GeckoAppShell_notifyFilePickerResult(JNIEnv * arg0, jclas xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_notifyFilePickerResult", &f_Java_org_mozilla_gecko_GeckoAppShell_notifyFilePickerResult); #endif +#ifdef JNI_STUBS + +typedef jdouble (*Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime_t)(JNIEnv *, jclass); +static Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime_t f_Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime; +extern "C" NS_EXPORT jdouble JNICALL +Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv * arg0, jclass arg1) { + if (!f_Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime) { + arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"), + "JNI Function called before it was loaded"); + return 0; + } + return f_Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(arg0, arg1); +} +#endif + +#ifdef JNI_BINDINGS + xul_dlsym("Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime", &f_Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime); +#endif + diff --git a/tools/profiler/BreakpadSampler.cpp b/tools/profiler/BreakpadSampler.cpp index adc82df480b..25a9952fd02 100644 --- a/tools/profiler/BreakpadSampler.cpp +++ b/tools/profiler/BreakpadSampler.cpp @@ -245,7 +245,7 @@ void TableTicker::UnwinderTick(TickSample* sample) } if (sample) { - TimeDuration delta = sample->timestamp - mStartTime; + TimeDuration delta = sample->timestamp - sStartTime; utb__addEntry( utb, ProfileEntry('t', delta.ToMilliseconds()) ); } diff --git a/tools/profiler/GeckoProfiler.h b/tools/profiler/GeckoProfiler.h index 7e40705d001..07a922e3964 100644 --- a/tools/profiler/GeckoProfiler.h +++ b/tools/profiler/GeckoProfiler.h @@ -142,6 +142,8 @@ static inline void profiler_unregister_thread() {} // profiling on auxilerary threads. static inline void profiler_js_operation_callback() {} +static inline double profiler_time() { return 0; } + #else #include "GeckoProfilerImpl.h" diff --git a/tools/profiler/GeckoProfilerFunc.h b/tools/profiler/GeckoProfilerFunc.h index 41134bfb5ce..61d051be389 100644 --- a/tools/profiler/GeckoProfilerFunc.h +++ b/tools/profiler/GeckoProfilerFunc.h @@ -61,6 +61,8 @@ void mozilla_sampler_unlock(); bool mozilla_sampler_register_thread(const char* name); void mozilla_sampler_unregister_thread(); +double mozilla_sampler_time(); + /* Returns true if env var SPS_NEW is set to anything, else false. */ extern bool sps_version2(); diff --git a/tools/profiler/GeckoProfilerImpl.h b/tools/profiler/GeckoProfilerImpl.h index b5c20626c61..11f092de972 100644 --- a/tools/profiler/GeckoProfilerImpl.h +++ b/tools/profiler/GeckoProfilerImpl.h @@ -163,6 +163,12 @@ void profiler_js_operation_callback() stack->jsOperationCallback(); } +static inline +double profiler_time() +{ + return mozilla_sampler_time(); +} + // we want the class and function name but can't easily get that using preprocessor macros // __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters diff --git a/tools/profiler/TableTicker.cpp b/tools/profiler/TableTicker.cpp index 052cd4a189c..9288e4093af 100644 --- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -34,6 +34,10 @@ #include "mozilla/Services.h" #include "PlatformMacros.h" +#ifdef ANDROID + #include "AndroidBridge.h" +#endif + // JS #include "jsdbgapi.h" @@ -160,6 +164,54 @@ JSObject* TableTicker::ToJSObject(JSContext *aCx) return jsProfile; } +#ifdef ANDROID +static +JSCustomObject* BuildJavaThreadJSObject(JSAObjectBuilder& b) +{ + JSCustomObject* javaThread = b.CreateObject(); + b.DefineProperty(javaThread, "name", "Java Main Thread"); + + JSCustomArray *samples = b.CreateArray(); + b.DefineProperty(javaThread, "samples", samples); + + int sampleId = 0; + while (true) { + int frameId = 0; + JSCustomObject *sample = nullptr; + JSCustomArray *frames = nullptr; + while (true) { + nsCString result; + bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result); + if (!hasFrame) { + if (frames) { + b.DefineProperty(sample, "frames", frames); + } + break; + } + if (!sample) { + sample = b.CreateObject(); + frames = b.CreateArray(); + b.DefineProperty(sample, "frames", frames); + b.ArrayPush(samples, sample); + + double sampleTime = AndroidBridge::Bridge()->GetSampleTimeJavaProfiling(0, sampleId); + b.DefineProperty(sample, "time", sampleTime); + } + JSCustomObject *frame = b.CreateObject(); + b.DefineProperty(frame, "location", result.BeginReading()); + b.ArrayPush(frames, frame); + frameId++; + } + if (frameId == 0) { + break; + } + sampleId++; + } + + return javaThread; +} +#endif + void TableTicker::BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile) { // Put shared library info @@ -191,8 +243,19 @@ void TableTicker::BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile) } } +#ifdef ANDROID + if (ProfileJava()) { + AndroidBridge::Bridge()->PauseJavaProfiling(); + + JSCustomObject* javaThread = BuildJavaThreadJSObject(b); + b.ArrayPush(threads, javaThread); + + AndroidBridge::Bridge()->UnpauseJavaProfiling(); + } +#endif + SetPaused(false); -} +} // END SaveProfileTask et al //////////////////////////////////////////////////////////////////////// @@ -452,7 +515,7 @@ void TableTicker::InplaceTick(TickSample* sample) } if (sample) { - TimeDuration delta = sample->timestamp - mStartTime; + TimeDuration delta = sample->timestamp - sStartTime; currThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds())); } diff --git a/tools/profiler/TableTicker.h b/tools/profiler/TableTicker.h index 25379a966e7..b0baef98377 100644 --- a/tools/profiler/TableTicker.h +++ b/tools/profiler/TableTicker.h @@ -30,7 +30,6 @@ class TableTicker: public Sampler { const char** aFeatures, uint32_t aFeatureCount) : Sampler(aInterval, true, aEntrySize) , mPrimaryThreadProfile(nullptr) - , mStartTime(TimeStamp::Now()) , mSaveRequested(false) , mUnwinderThread(false) { @@ -39,10 +38,13 @@ class TableTicker: public Sampler { //XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank"); mProfileJS = hasFeature(aFeatures, aFeatureCount, "js"); - mProfileThreads = true || hasFeature(aFeatures, aFeatureCount, "threads"); + mProfileJava = hasFeature(aFeatures, aFeatureCount, "java"); + mProfileThreads = hasFeature(aFeatures, aFeatureCount, "threads"); mUnwinderThread = hasFeature(aFeatures, aFeatureCount, "unwinder") || sps_version2(); mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf"); + sStartTime = TimeStamp::Now(); + { mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); @@ -123,6 +125,7 @@ class TableTicker: public Sampler { bool HasUnwinderThread() const { return mUnwinderThread; } bool ProfileJS() const { return mProfileJS; } + bool ProfileJava() const { return mProfileJava; } bool ProfileThreads() const { return mProfileThreads; } protected: @@ -139,7 +142,6 @@ protected: // This represent the application's main thread (SAMPLER_INIT) ThreadProfile* mPrimaryThreadProfile; - TimeStamp mStartTime; bool mSaveRequested; bool mAddLeafAddresses; bool mUseStackWalk; @@ -147,5 +149,6 @@ protected: bool mProfileJS; bool mProfileThreads; bool mUnwinderThread; + bool mProfileJava; }; diff --git a/tools/profiler/platform.cpp b/tools/profiler/platform.cpp index 15373332f2f..60d5373bd96 100644 --- a/tools/profiler/platform.cpp +++ b/tools/profiler/platform.cpp @@ -20,6 +20,10 @@ #include "mozilla/Services.h" #include "nsThreadUtils.h" +#ifdef ANDROID + #include "AndroidBridge.h" +#endif + mozilla::ThreadLocal tlsPseudoStack; mozilla::ThreadLocal tlsTicker; // We need to track whether we've been initialized otherwise @@ -29,6 +33,7 @@ mozilla::ThreadLocal tlsTicker; bool stack_key_initialized; TimeStamp sLastTracerEvent; // is raced on +TimeStamp sStartTime; int sFrameNumber = 0; int sLastFrameNumber = 0; int sInitCount = 0; // Each init must have a matched shutdown. @@ -383,6 +388,7 @@ const char** mozilla_sampler_get_features() // Use a seperate thread of walking the stack. "unwinder", #endif + "java", // Only record samples during periods of bad responsiveness "jank", // Tell the JS engine to emmit pseudostack entries in the @@ -445,6 +451,17 @@ void mozilla_sampler_start(int aProfileEntries, int aInterval, } } +#ifdef ANDROID + if (t->ProfileJava()) { + int javaInterval = aInterval; + // Java sampling doesn't accuratly keep up with 1ms sampling + if (javaInterval < 10) { + aInterval = 10; + } + mozilla::AndroidBridge::Bridge()->StartJavaProfiling(javaInterval, 1000); + } +#endif + sIsProfiling = true; nsCOMPtr os = mozilla::services::GetObserverService(); @@ -560,6 +577,12 @@ void mozilla_sampler_unregister_thread() Sampler::UnregisterCurrentThread(); } +double mozilla_sampler_time() +{ + TimeDuration delta = TimeStamp::Now() - sStartTime; + return delta.ToMilliseconds(); +} + // END externally visible functions //////////////////////////////////////////////////////////////////////// diff --git a/tools/profiler/platform.h b/tools/profiler/platform.h index f78c6911f9d..68d2a3a2223 100644 --- a/tools/profiler/platform.h +++ b/tools/profiler/platform.h @@ -80,6 +80,8 @@ #define ENABLE_SPS_LEAF_DATA #endif +extern mozilla::TimeStamp sStartTime; + typedef uint8_t* Address; // ---------------------------------------------------------------------------- diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index c24d65a0ab8..ded1e35a4c9 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -176,6 +176,15 @@ AndroidBridge::Init(JNIEnv *jEnv, jLockScreenOrientation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "lockScreenOrientation", "(I)V"); jUnlockScreenOrientation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "unlockScreenOrientation", "()V"); + jGeckoJavaSamplerClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("org/mozilla/gecko/GeckoJavaSampler")); + jStart = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "start", "(II)V"); + jStop = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "stop", "()V"); + jPause = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "pause", "()V"); + jUnpause = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "unpause", "()V"); + jGetThreadName = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "getThreadName", "(I)Ljava/lang/String;"); + jGetFrameName = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "getFrameName", "(III)Ljava/lang/String;"); + jGetSampleTime = jEnv->GetStaticMethodID(jGeckoJavaSamplerClass, "getSampleTime", "(II)D"); + jThumbnailHelperClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("org/mozilla/gecko/ThumbnailHelper")); jNotifyThumbnail = jEnv->GetStaticMethodID(jThumbnailHelperClass, "notifyThumbnail", "(Ljava/nio/ByteBuffer;IZ)V"); @@ -2413,6 +2422,120 @@ __attribute__ ((visibility("default"))) jobject JNICALL Java_org_mozilla_gecko_GeckoAppShell_allocateDirectBuffer(JNIEnv *env, jclass, jlong size); +void +AndroidBridge::StartJavaProfiling(int aInterval, int aSamples) +{ + JNIEnv* env = GetJNIForThread(); + if (!env) + return; + + AutoLocalJNIFrame jniFrame(env); + + env->CallStaticVoidMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass, + AndroidBridge::Bridge()->jStart, + aInterval, aSamples); +} + +void +AndroidBridge::StopJavaProfiling() +{ + JNIEnv* env = GetJNIForThread(); + if (!env) + return; + + AutoLocalJNIFrame jniFrame(env); + + env->CallStaticVoidMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass, + AndroidBridge::Bridge()->jStop); +} + +void +AndroidBridge::PauseJavaProfiling() +{ + JNIEnv* env = GetJNIForThread(); + if (!env) + return; + + AutoLocalJNIFrame jniFrame(env); + + env->CallStaticVoidMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass, + AndroidBridge::Bridge()->jPause); +} + +void +AndroidBridge::UnpauseJavaProfiling() +{ + JNIEnv* env = GetJNIForThread(); + if (!env) + return; + + AutoLocalJNIFrame jniFrame(env); + + env->CallStaticVoidMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass, + AndroidBridge::Bridge()->jUnpause); +} + +bool +AndroidBridge::GetThreadNameJavaProfiling(uint32_t aThreadId, nsCString & aResult) +{ + JNIEnv* env = GetJNIForThread(); + if (!env) + return false; + + AutoLocalJNIFrame jniFrame(env); + + jstring jstrThreadName = static_cast( + env->CallStaticObjectMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass, + AndroidBridge::Bridge()->jGetThreadName, + aThreadId)); + + if (!jstrThreadName) + return false; + + nsJNIString jniStr(jstrThreadName, env); + CopyUTF16toUTF8(jniStr.get(), aResult); + return true; +} + +bool +AndroidBridge::GetFrameNameJavaProfiling(uint32_t aThreadId, uint32_t aSampleId, + uint32_t aFrameId, nsCString & aResult) +{ + JNIEnv* env = GetJNIForThread(); + if (!env) + return false; + + AutoLocalJNIFrame jniFrame(env); + + jstring jstrSampleName = static_cast( + env->CallStaticObjectMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass, + AndroidBridge::Bridge()->jGetFrameName, + aThreadId, aSampleId, aFrameId)); + + if (!jstrSampleName) + return false; + + nsJNIString jniStr(jstrSampleName, env); + CopyUTF16toUTF8(jniStr.get(), aResult); + return true; +} + +double +AndroidBridge::GetSampleTimeJavaProfiling(uint32_t aThreadId, uint32_t aSampleId) +{ + JNIEnv* env = GetJNIForThread(); + if (!env) + return 0; + + AutoLocalJNIFrame jniFrame(env); + + jdouble jSampleTime = + env->CallStaticDoubleMethod(AndroidBridge::Bridge()->jGeckoJavaSamplerClass, + AndroidBridge::Bridge()->jGetSampleTime, + aThreadId, aSampleId); + + return jSampleTime; +} void AndroidBridge::SendThumbnail(jobject buffer, int32_t tabId, bool success) { diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index a5cba23303c..2db4bf4027c 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -152,6 +152,14 @@ public: static void NotifyIMEChange(const PRUnichar *aText, uint32_t aTextLen, int aStart, int aEnd, int aNewEnd); + void StartJavaProfiling(int aInterval, int aSamples); + void StopJavaProfiling(); + void PauseJavaProfiling(); + void UnpauseJavaProfiling(); + bool GetThreadNameJavaProfiling(uint32_t aThreadId, nsCString & aResult); + bool GetFrameNameJavaProfiling(uint32_t aThreadId, uint32_t aSampleId, uint32_t aFrameId, nsCString & aResult); + double GetSampleTimeJavaProfiling(uint32_t aThreadId, uint32_t aSampleId); + nsresult CaptureThumbnail(nsIDOMWindow *window, int32_t bufW, int32_t bufH, int32_t tabId, jobject buffer); void SendThumbnail(jobject buffer, int32_t tabId, bool success); nsresult GetDisplayPort(bool aPageSizeUpdate, bool aIsBrowserContentDisplayed, int32_t tabId, nsIAndroidViewport* metrics, nsIAndroidDisplayport** displayPort); @@ -360,6 +368,15 @@ public: void RegisterSurfaceTextureFrameListener(jobject surfaceTexture, int id); void UnregisterSurfaceTextureFrameListener(jobject surfaceTexture); + jclass jGeckoJavaSamplerClass; + jmethodID jStart; + jmethodID jStop; + jmethodID jPause; + jmethodID jUnpause; + jmethodID jGetThreadName; + jmethodID jGetFrameName; + jmethodID jGetSampleTime; + void GetGfxInfoData(nsACString& aRet); nsresult GetProxyForURI(const nsACString & aSpec, const nsACString & aScheme, diff --git a/widget/android/AndroidJNI.cpp b/widget/android/AndroidJNI.cpp index cf239d3e9ef..fa55853e071 100644 --- a/widget/android/AndroidJNI.cpp +++ b/widget/android/AndroidJNI.cpp @@ -38,6 +38,7 @@ #include "nsIMobileMessageDatabaseService.h" #include "nsPluginInstanceOwner.h" #include "nsSurfaceTexture.h" +#include "GeckoProfiler.h" using namespace mozilla; using namespace mozilla::dom; @@ -876,4 +877,10 @@ Java_org_mozilla_gecko_GeckoAppShell_onSurfaceTextureFrameAvailable(JNIEnv* jenv st->NotifyFrameAvailable(); } +NS_EXPORT jdouble JNICALL +Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv *jenv, jclass jc) +{ + return profiler_time(); +} + }