diff options
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/GeckoJavaSampler.java')
-rw-r--r-- | mobile/android/base/java/org/mozilla/gecko/GeckoJavaSampler.java | 211 |
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; + } + } +} |