michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko; michael@0: michael@0: import android.os.SystemClock; michael@0: import android.util.Log; michael@0: import android.util.SparseArray; michael@0: michael@0: import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI; michael@0: michael@0: import java.lang.Thread; michael@0: import java.util.Set; michael@0: michael@0: public class GeckoJavaSampler { michael@0: private static final String LOGTAG = "JavaSampler"; michael@0: private static Thread sSamplingThread = null; michael@0: private static SamplingThread sSamplingRunnable = null; michael@0: private static Thread sMainThread = null; michael@0: private static volatile boolean sLibsLoaded = false; michael@0: michael@0: // Use the same timer primitive as the profiler michael@0: // to get a perfect sample syncing. michael@0: private static native double getProfilerTime(); michael@0: michael@0: private static class Sample { michael@0: public Frame[] mFrames; michael@0: public double mTime; michael@0: public long mJavaTime; // non-zero if Android system time is used michael@0: public Sample(StackTraceElement[] aStack) { michael@0: mFrames = new Frame[aStack.length]; michael@0: if (sLibsLoaded) { michael@0: mTime = getProfilerTime(); michael@0: } michael@0: if (mTime == 0.0d) { michael@0: // getProfilerTime is not available yet; either libs are not loaded, michael@0: // or profiling hasn't started on the Gecko side yet michael@0: mJavaTime = SystemClock.elapsedRealtime(); michael@0: } michael@0: for (int i = 0; i < aStack.length; i++) { michael@0: mFrames[aStack.length - 1 - i] = new Frame(); michael@0: mFrames[aStack.length - 1 - i].fileName = aStack[i].getFileName(); michael@0: mFrames[aStack.length - 1 - i].lineNo = aStack[i].getLineNumber(); michael@0: mFrames[aStack.length - 1 - i].methodName = aStack[i].getMethodName(); michael@0: mFrames[aStack.length - 1 - i].className = aStack[i].getClassName(); michael@0: } michael@0: } michael@0: } michael@0: private static class Frame { michael@0: public String fileName; michael@0: public int lineNo; michael@0: public String methodName; michael@0: public String className; michael@0: } michael@0: michael@0: private static class SamplingThread implements Runnable { michael@0: private final int mInterval; michael@0: private final int mSampleCount; michael@0: michael@0: private boolean mPauseSampler = false; michael@0: private boolean mStopSampler = false; michael@0: michael@0: private SparseArray mSamples = new SparseArray(); michael@0: private int mSamplePos; michael@0: michael@0: public SamplingThread(final int aInterval, final int aSampleCount) { michael@0: // If we sample faster then 10ms we get to many missed samples michael@0: mInterval = Math.max(10, aInterval); michael@0: mSampleCount = aSampleCount; michael@0: } michael@0: michael@0: public void run() { michael@0: synchronized (GeckoJavaSampler.class) { michael@0: mSamples.put(0, new Sample[mSampleCount]); michael@0: mSamplePos = 0; michael@0: michael@0: // Find the main thread michael@0: Set threadSet = Thread.getAllStackTraces().keySet(); michael@0: for (Thread t : threadSet) { michael@0: if (t.getName().compareToIgnoreCase("main") == 0) { michael@0: sMainThread = t; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (sMainThread == null) { michael@0: Log.e(LOGTAG, "Main thread not found"); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: while (true) { michael@0: try { michael@0: Thread.sleep(mInterval); michael@0: } catch (InterruptedException e) { michael@0: e.printStackTrace(); michael@0: } michael@0: synchronized (GeckoJavaSampler.class) { michael@0: if (!mPauseSampler) { michael@0: StackTraceElement[] bt = sMainThread.getStackTrace(); michael@0: mSamples.get(0)[mSamplePos] = new Sample(bt); michael@0: mSamplePos = (mSamplePos+1) % mSamples.get(0).length; michael@0: } michael@0: if (mStopSampler) { michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: private Sample getSample(int aThreadId, int aSampleId) { michael@0: if (aThreadId < mSamples.size() && aSampleId < mSamples.get(aThreadId).length && michael@0: mSamples.get(aThreadId)[aSampleId] != null) { michael@0: int startPos = 0; michael@0: if (mSamples.get(aThreadId)[mSamplePos] != null) { michael@0: startPos = mSamplePos; michael@0: } michael@0: int readPos = (startPos + aSampleId) % mSamples.get(aThreadId).length; michael@0: return mSamples.get(aThreadId)[readPos]; michael@0: } michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: michael@0: @WrapElementForJNI(allowMultithread = true, stubName = "GetThreadNameJavaProfilingWrapper") michael@0: public synchronized static String getThreadName(int aThreadId) { michael@0: if (aThreadId == 0 && sMainThread != null) { michael@0: return sMainThread.getName(); michael@0: } michael@0: return null; michael@0: } michael@0: michael@0: private synchronized static Sample getSample(int aThreadId, int aSampleId) { michael@0: return sSamplingRunnable.getSample(aThreadId, aSampleId); michael@0: } michael@0: michael@0: @WrapElementForJNI(allowMultithread = true, stubName = "GetSampleTimeJavaProfiling") michael@0: public synchronized static double getSampleTime(int aThreadId, int aSampleId) { michael@0: Sample sample = getSample(aThreadId, aSampleId); michael@0: if (sample != null) { michael@0: if (sample.mJavaTime != 0) { michael@0: return (double)(sample.mJavaTime - michael@0: SystemClock.elapsedRealtime()) + getProfilerTime(); michael@0: } michael@0: System.out.println("Sample: " + sample.mTime); michael@0: return sample.mTime; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: @WrapElementForJNI(allowMultithread = true, stubName = "GetFrameNameJavaProfilingWrapper") michael@0: public synchronized static String getFrameName(int aThreadId, int aSampleId, int aFrameId) { michael@0: Sample sample = getSample(aThreadId, aSampleId); michael@0: if (sample != null && aFrameId < sample.mFrames.length) { michael@0: Frame frame = sample.mFrames[aFrameId]; michael@0: if (frame == null) { michael@0: return null; michael@0: } michael@0: return frame.className + "." + frame.methodName + "()"; michael@0: } michael@0: return null; michael@0: } michael@0: michael@0: @WrapElementForJNI(allowMultithread = true, stubName = "StartJavaProfiling") michael@0: public static void start(int aInterval, int aSamples) { michael@0: synchronized (GeckoJavaSampler.class) { michael@0: if (sSamplingRunnable != null) { michael@0: return; michael@0: } michael@0: sSamplingRunnable = new SamplingThread(aInterval, aSamples); michael@0: sSamplingThread = new Thread(sSamplingRunnable, "Java Sampler"); michael@0: sSamplingThread.start(); michael@0: } michael@0: } michael@0: michael@0: @WrapElementForJNI(allowMultithread = true, stubName = "PauseJavaProfiling") michael@0: public static void pause() { michael@0: synchronized (GeckoJavaSampler.class) { michael@0: sSamplingRunnable.mPauseSampler = true; michael@0: } michael@0: } michael@0: michael@0: @WrapElementForJNI(allowMultithread = true, stubName = "UnpauseJavaProfiling") michael@0: public static void unpause() { michael@0: synchronized (GeckoJavaSampler.class) { michael@0: sSamplingRunnable.mPauseSampler = false; michael@0: } michael@0: } michael@0: michael@0: @WrapElementForJNI(allowMultithread = true, stubName = "StopJavaProfiling") michael@0: public static void stop() { michael@0: synchronized (GeckoJavaSampler.class) { michael@0: if (sSamplingThread == null) { michael@0: return; michael@0: } michael@0: michael@0: sSamplingRunnable.mStopSampler = true; michael@0: try { michael@0: sSamplingThread.join(); michael@0: } catch (InterruptedException e) { michael@0: e.printStackTrace(); michael@0: } michael@0: sSamplingThread = null; michael@0: sSamplingRunnable = null; michael@0: } michael@0: } michael@0: michael@0: public static void setLibsLoaded() { michael@0: sLibsLoaded = true; michael@0: } michael@0: } michael@0: michael@0: michael@0: