summaryrefslogtreecommitdiffstats
path: root/mobile/android/base/java/org/mozilla/gecko/GeckoJavaSampler.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/GeckoJavaSampler.java')
-rw-r--r--mobile/android/base/java/org/mozilla/gecko/GeckoJavaSampler.java211
1 files changed, 211 insertions, 0 deletions
diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoJavaSampler.java b/mobile/android/base/java/org/mozilla/gecko/GeckoJavaSampler.java
new file mode 100644
index 000000000..319eccec1
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoJavaSampler.java
@@ -0,0 +1,211 @@
+/* -*- 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.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+
+import org.mozilla.gecko.annotation.WrapForJNI;
+
+import java.lang.Thread;
+import java.util.Set;
+
+public class GeckoJavaSampler {
+ private static final String LOGTAG = "JavaSampler";
+ private static Thread sSamplingThread;
+ private static SamplingThread sSamplingRunnable;
+ private static Thread sMainThread;
+
+ // Use the same timer primitive as the profiler
+ // to get a perfect sample syncing.
+ @WrapForJNI
+ private static native double getProfilerTime();
+
+ private static class Sample {
+ public Frame[] mFrames;
+ public double mTime;
+ public long mJavaTime; // non-zero if Android system time is used
+ public Sample(StackTraceElement[] aStack) {
+ mFrames = new Frame[aStack.length];
+ if (GeckoThread.isStateAtLeast(GeckoThread.State.LIBS_READY)) {
+ mTime = getProfilerTime();
+ }
+ if (mTime == 0.0d) {
+ // getProfilerTime is not available yet; either libs are not loaded,
+ // or profiling hasn't started on the Gecko side yet
+ mJavaTime = SystemClock.elapsedRealtime();
+ }
+ 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;
+ private boolean mStopSampler;
+
+ private final SparseArray<Sample[]> mSamples = new SparseArray<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;
+ }
+
+ @Override
+ 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;
+ }
+ }
+
+
+ @WrapForJNI
+ 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);
+ }
+
+ @WrapForJNI
+ public synchronized static double getSampleTime(int aThreadId, int aSampleId) {
+ Sample sample = getSample(aThreadId, aSampleId);
+ if (sample != null) {
+ if (sample.mJavaTime != 0) {
+ return (sample.mJavaTime -
+ SystemClock.elapsedRealtime()) + getProfilerTime();
+ }
+ System.out.println("Sample: " + sample.mTime);
+ return sample.mTime;
+ }
+ return 0;
+ }
+
+ @WrapForJNI
+ 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;
+ }
+
+ @WrapForJNI
+ public static void start(int aInterval, int aSamples) {
+ synchronized (GeckoJavaSampler.class) {
+ if (sSamplingRunnable != null) {
+ return;
+ }
+ sSamplingRunnable = new SamplingThread(aInterval, aSamples);
+ sSamplingThread = new Thread(sSamplingRunnable, "Java Sampler");
+ sSamplingThread.start();
+ }
+ }
+
+ @WrapForJNI
+ public static void pause() {
+ synchronized (GeckoJavaSampler.class) {
+ sSamplingRunnable.mPauseSampler = true;
+ }
+ }
+
+ @WrapForJNI
+ public static void unpause() {
+ synchronized (GeckoJavaSampler.class) {
+ sSamplingRunnable.mPauseSampler = false;
+ }
+ }
+
+ @WrapForJNI
+ 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;
+ }
+ }
+}