author | Benoit Girard <b56girard@gmail.com> |
Tue, 23 Apr 2013 13:10:29 -0400 | |
changeset 140601 | 42f859a219d647f6bc5b297add90ca31ae59a737 |
parent 140600 | 57af1961439a359356a05b391cf8d29a8783e34a |
child 140602 | f8077e8edd44d68177aa90d969733562d916de4c |
push id | 2579 |
push user | akeybl@mozilla.com |
push date | Mon, 24 Jun 2013 18:52:47 +0000 |
treeherder | mozilla-beta@b69b7de8a05a [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | snorp, kats |
bugs | 788022 |
milestone | 23.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /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<Integer,Sample[]> mSamples = new HashMap<Integer,Sample[]>(); + 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<Thread> 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; + } + } +} + + +
--- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -102,16 +102,17 @@ FENNEC_JAVA_FILES = \ GeckoMessageReceiver.java \ GeckoSubMenu.java \ GeckoPreferences.java \ GeckoPreferenceFragment.java \ GeckoProfile.java \ GeckoPopupMenu.java \ GeckoSmsManager.java \ GeckoThread.java \ + GeckoJavaSampler.java \ GlobalHistory.java \ GeckoViewsFactory.java \ HeightChangeAnimation.java \ InputMethods.java \ JavaAddonManager.java \ LightweightTheme.java \ LightweightThemeDrawable.java \ LinkPreference.java \ @@ -1147,16 +1148,17 @@ jars/webrtc.jar: $(addprefix $(srcdir)/, endif jars: @echo "MKDIR jars" $(NSINSTALL) -D jars CLASSES_WITH_JNI= \ org.mozilla.gecko.GeckoAppShell \ + org.mozilla.gecko.GeckoJavaSampler \ $(NULL) ifdef MOZ_WEBSMS_BACKEND # Note: if you are building with MOZ_WEBSMS_BACKEND turned on, then # you will get a build error because the generated jni-stubs.inc will # be different than the one checked in (i.e. it will have the sms-related # JNI stubs as well). Just copy the generated file to mozglue/android/ # like the error message says and rebuild. All should be well after that.
--- a/mobile/android/base/jni-generator.py +++ b/mobile/android/base/jni-generator.py @@ -64,17 +64,17 @@ class Generator: match = re.match(paramsRegex, line) if match: paramTypes = re.split('\s*,\s*', match.group(1)) paramNames = ['arg%d' % i for i in range(0, len(paramTypes))] if returnType == 'void': 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; ' + 'please update mobile/android/base/' + 'jni-generator.py to handle this case!') % returnType) self.write('JNI_STUBS', STUB_TEMPLATE % {
--- a/mozglue/android/jni-stubs.inc +++ b/mozglue/android/jni-stubs.inc @@ -374,8 +374,27 @@ Java_org_mozilla_gecko_GeckoAppShell_not f_Java_org_mozilla_gecko_GeckoAppShell_notifyFilePickerResult(arg0, arg1, arg2, arg3); } #endif #ifdef JNI_BINDINGS 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 +
--- a/tools/profiler/BreakpadSampler.cpp +++ b/tools/profiler/BreakpadSampler.cpp @@ -240,17 +240,17 @@ void TableTicker::UnwinderTick(TickSampl // Add any extras if (!sLastTracerEvent.IsNull() && sample) { TimeDuration delta = sample->timestamp - sLastTracerEvent; utb__addEntry( utb, ProfileEntry('r', delta.ToMilliseconds()) ); } if (sample) { - TimeDuration delta = sample->timestamp - mStartTime; + TimeDuration delta = sample->timestamp - sStartTime; utb__addEntry( utb, ProfileEntry('t', delta.ToMilliseconds()) ); } if (sLastFrameNumber != sFrameNumber) { utb__addEntry( utb, ProfileEntry('f', sFrameNumber) ); sLastFrameNumber = sFrameNumber; }
--- a/tools/profiler/GeckoProfiler.h +++ b/tools/profiler/GeckoProfiler.h @@ -137,16 +137,18 @@ static inline void profiler_unlock() {} static inline void profiler_register_thread(const char* name) {} static inline void profiler_unregister_thread() {} // Call by the JSRuntime's operation callback. This is used to enable // profiling on auxilerary threads. static inline void profiler_js_operation_callback() {} +static inline double profiler_time() { return 0; } + #else #include "GeckoProfilerImpl.h" #endif class GeckoProfilerInitRAII { public:
--- a/tools/profiler/GeckoProfilerFunc.h +++ b/tools/profiler/GeckoProfilerFunc.h @@ -56,13 +56,15 @@ void mozilla_sampler_lock(); // Unlock the profiler, leaving it stopped and fires profiler-unlocked. void mozilla_sampler_unlock(); // Register/unregister threads with the profiler 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(); #endif
--- a/tools/profiler/GeckoProfilerImpl.h +++ b/tools/profiler/GeckoProfilerImpl.h @@ -158,16 +158,22 @@ void profiler_js_operation_callback() PseudoStack *stack = tlsPseudoStack.get(); if (!stack) { return; } 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 #define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line #define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) #define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__) #define PROFILER_LABEL(name_space, info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, __LINE__)
--- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -29,16 +29,20 @@ #include "nsIXULRuntime.h" #include "nsIXULAppInfo.h" #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" #include "nsIObserverService.h" #include "mozilla/Services.h" #include "PlatformMacros.h" +#ifdef ANDROID + #include "AndroidBridge.h" +#endif + // JS #include "jsdbgapi.h" // we eventually want to make this runtime switchable #if defined(MOZ_PROFILING) && (defined(XP_UNIX) && !defined(XP_MACOSX)) #ifndef ANDROID #define USE_BACKTRACE #endif @@ -155,16 +159,64 @@ JSObject* TableTicker::ToJSObject(JSCont JSObjectBuilder b(aCx); JSCustomObject* profile = b.CreateObject(); BuildJSObject(b, profile); JSObject* jsProfile = b.GetJSObject(profile); 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 b.DefineProperty(profile, "libs", GetSharedLibraryInfoString().c_str()); // Put meta data JSCustomObject *meta = GetMetaJSCustomObject(b); b.DefineProperty(profile, "meta", meta); @@ -186,18 +238,29 @@ void TableTicker::BuildJSObject(JSAObjec MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex()); JSCustomObject* threadSamples = b.CreateObject(); sRegisteredThreads->at(i)->Profile()->BuildJSObject(b, threadSamples); b.ArrayPush(threads, threadSamples); } } +#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 //////////////////////////////////////////////////////////////////////// static void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr) { aProfile.addTag(ProfileEntry(aTagName, "")); @@ -447,17 +510,17 @@ void TableTicker::InplaceTick(TickSample currThreadProfile.flush(); if (!sLastTracerEvent.IsNull() && sample && currThreadProfile.IsMainThread()) { TimeDuration delta = sample->timestamp - sLastTracerEvent; currThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds())); } if (sample) { - TimeDuration delta = sample->timestamp - mStartTime; + TimeDuration delta = sample->timestamp - sStartTime; currThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds())); } if (sLastFrameNumber != sFrameNumber) { currThreadProfile.addTag(ProfileEntry('f', sFrameNumber)); sLastFrameNumber = sFrameNumber; } }
--- a/tools/profiler/TableTicker.h +++ b/tools/profiler/TableTicker.h @@ -25,29 +25,31 @@ extern unsigned int sLastSampledEventGen class BreakpadSampler; class TableTicker: public Sampler { public: TableTicker(int aInterval, int aEntrySize, const char** aFeatures, uint32_t aFeatureCount) : Sampler(aInterval, true, aEntrySize) , mPrimaryThreadProfile(nullptr) - , mStartTime(TimeStamp::Now()) , mSaveRequested(false) , mUnwinderThread(false) { mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk"); //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); // Create ThreadProfile for each registered thread for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { ThreadInfo* info = sRegisteredThreads->at(i); if (!info->IsMainThread() && !mProfileThreads) @@ -118,34 +120,35 @@ class TableTicker: public Sampler { } void ToStreamAsJSON(std::ostream& stream); virtual JSObject *ToJSObject(JSContext *aCx); JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b); bool HasUnwinderThread() const { return mUnwinderThread; } bool ProfileJS() const { return mProfileJS; } + bool ProfileJava() const { return mProfileJava; } bool ProfileThreads() const { return mProfileThreads; } protected: // Called within a signal. This function must be reentrant virtual void UnwinderTick(TickSample* sample); // Called within a signal. This function must be reentrant virtual void InplaceTick(TickSample* sample); // Not implemented on platforms which do not support backtracing void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample); void BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile); // This represent the application's main thread (SAMPLER_INIT) ThreadProfile* mPrimaryThreadProfile; - TimeStamp mStartTime; bool mSaveRequested; bool mAddLeafAddresses; bool mUseStackWalk; bool mJankOnly; bool mProfileJS; bool mProfileThreads; bool mUnwinderThread; + bool mProfileJava; };
--- a/tools/profiler/platform.cpp +++ b/tools/profiler/platform.cpp @@ -15,25 +15,30 @@ #include "TableTicker.h" #include "UnwinderThread2.h" #include "nsIObserverService.h" #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" #include "mozilla/Services.h" #include "nsThreadUtils.h" +#ifdef ANDROID + #include "AndroidBridge.h" +#endif + mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack; mozilla::ThreadLocal<TableTicker *> tlsTicker; // We need to track whether we've been initialized otherwise // we end up using tlsStack without initializing it. // Because tlsStack is totally opaque to us we can't reuse // it as the flag itself. 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. static bool sIsProfiling = false; // is raced on /* used to keep track of the last event that we sampled during */ unsigned int sLastSampledEventGeneration = 0; @@ -378,16 +383,17 @@ const char** mozilla_sampler_get_feature // Include the C++ leaf node if not stackwalking. DevTools // profiler doesn't want the native addresses. "leaf", #endif #if !defined(SPS_OS_windows) // 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 // pro/epilogue. "js", // Profile the registered secondary threads. "threads", NULL @@ -440,16 +446,27 @@ void mozilla_sampler_start(int aProfileE ThreadProfile* thread_profile = info->Profile(); if (!thread_profile) { continue; } thread_profile->GetPseudoStack()->enableJSSampling(); } } +#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<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-started", nullptr); } void mozilla_sampler_stop() @@ -555,12 +572,18 @@ bool mozilla_sampler_register_thread(con return Sampler::RegisterCurrentThread(aName, stack, false); } void mozilla_sampler_unregister_thread() { Sampler::UnregisterCurrentThread(); } +double mozilla_sampler_time() +{ + TimeDuration delta = TimeStamp::Now() - sStartTime; + return delta.ToMilliseconds(); +} + // END externally visible functions ////////////////////////////////////////////////////////////////////////
--- a/tools/profiler/platform.h +++ b/tools/profiler/platform.h @@ -75,16 +75,18 @@ } while (0) #endif #if defined(XP_MACOSX) || defined(XP_WIN) #define ENABLE_SPS_LEAF_DATA #endif +extern mozilla::TimeStamp sStartTime; + typedef uint8_t* Address; // ---------------------------------------------------------------------------- // Mutex // // Mutexes are used for serializing access to non-reentrant sections of code. // The implementations of mutex should allow for nested/recursive locking.
--- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -171,16 +171,25 @@ AndroidBridge::Init(JNIEnv *jEnv, jDisableNetworkNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableNetworkNotifications", "()V"); jGetScreenOrientation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getScreenOrientation", "()S"); jEnableScreenOrientationNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableScreenOrientationNotifications", "()V"); jDisableScreenOrientationNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableScreenOrientationNotifications", "()V"); 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"); jStringClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("java/lang/String")); jSurfaceClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("android/view/Surface")); if (!GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &mAPIVersion, jEnv)) @@ -2408,16 +2417,130 @@ AndroidBridge::RemovePluginView(jobject env->CallStaticVoidMethod(mGeckoAppShellClass, jRemovePluginView, view, isFullScreen); } extern "C" __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<jstring>( + 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<jstring>( + 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) { // Regardless of whether we successfully captured a thumbnail, we need to // send a response to process the remaining entries in the queue. If we // don't get an env here, we'll stall the thumbnail loop, but there isn't // much we can do about it. JNIEnv* env = GetJNIEnv();
--- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -147,16 +147,24 @@ public: /* These are all implemented in Java */ static void NotifyIME(int aType); static void NotifyIMEContext(int aState, const nsAString& aTypeHint, const nsAString& aModeHint, const nsAString& aActionHint); 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); bool ProgressiveUpdateCallback(bool aHasPendingNewThebesContent, const gfx::Rect& aDisplayPort, float aDisplayResolution, bool aDrawingCritical, gfx::Rect& aViewport, float& aScaleX, float& aScaleY); void AcknowledgeEvent(); @@ -355,16 +363,25 @@ public: int GetAPIVersion() { return mAPIVersion; } bool IsHoneycomb() { return mAPIVersion >= 11 && mAPIVersion <= 13; } void ScheduleComposite(); 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, const nsACString & aHost, const int32_t aPort, nsACString & aResult); protected: static AndroidBridge *sBridge;
--- a/widget/android/AndroidJNI.cpp +++ b/widget/android/AndroidJNI.cpp @@ -33,16 +33,17 @@ #include "mozilla/dom/SmsMessage.h" #include "mozilla/dom/mobilemessage/Constants.h" #include "mozilla/dom/mobilemessage/Types.h" #include "mozilla/dom/mobilemessage/PSms.h" #include "mozilla/dom/mobilemessage/SmsParent.h" #include "nsIMobileMessageDatabaseService.h" #include "nsPluginInstanceOwner.h" #include "nsSurfaceTexture.h" +#include "GeckoProfiler.h" using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::dom::mobilemessage; /* Forward declare all the JNI methods as extern "C" */ extern "C" { @@ -871,9 +872,15 @@ Java_org_mozilla_gecko_GeckoAppShell_onS if (!st) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "Failed to find nsSurfaceTexture with id %d", id); return; } st->NotifyFrameAvailable(); } +NS_EXPORT jdouble JNICALL +Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv *jenv, jclass jc) +{ + return profiler_time(); } + +}