summaryrefslogtreecommitdiffstats
path: root/dalvik/src
diff options
context:
space:
mode:
authorBob Lee <crazybob@crazybob.org>2009-09-04 18:28:16 -0700
committerBob Lee <crazybob@crazybob.org>2009-09-06 16:40:34 -0700
commit6c24f973cb7b0de1069b20daf0371b6e73ca3fd1 (patch)
tree53d1918f1e59f5219a8bc3cb7f78c9b3cca171ed /dalvik/src
parent2d97a2b8ca7cd4993f9f917bdffd83d19e1e4f6c (diff)
downloadlibcore-6c24f973cb7b0de1069b20daf0371b6e73ca3fd1.zip
libcore-6c24f973cb7b0de1069b20daf0371b6e73ca3fd1.tar.gz
libcore-6c24f973cb7b0de1069b20daf0371b6e73ca3fd1.tar.bz2
Modified Thread.c to allow sampling profiler thread in zygote. Added a function that
queries the native thread status. Modified the profiler to sample every thread at a fixed interval instead of adjusting the delay based on the number of threads. This will make it easier to compare results across apps. Added ability to shut down the profiler thread. Added code to track whether the method was a leaf or a caller.
Diffstat (limited to 'dalvik/src')
-rw-r--r--dalvik/src/main/java/dalvik/system/SamplingProfiler.java198
1 files changed, 146 insertions, 52 deletions
diff --git a/dalvik/src/main/java/dalvik/system/SamplingProfiler.java b/dalvik/src/main/java/dalvik/system/SamplingProfiler.java
index 694df38..65e56f2 100644
--- a/dalvik/src/main/java/dalvik/system/SamplingProfiler.java
+++ b/dalvik/src/main/java/dalvik/system/SamplingProfiler.java
@@ -15,62 +15,78 @@ public class SamplingProfiler {
private static final Logger logger = Logger.getLogger(
SamplingProfiler.class.getName());
+ static final boolean DEBUG = false;
+
+ enum State {
+ /** The sampling thread hasn't started or is waiting to resume. */
+ PAUSED,
+ /** The sampling thread is collecting samples. */
+ RUNNING,
+ /** The sampling thread is shutting down. */
+ SHUTTING_DOWN
+ }
+
/** Pointer to native state. */
int pointer = 0;
/** The thread that collects samples. */
Thread samplingThread;
- /** Whether or not the profiler is running. */
- boolean running = false;
- int delayPerThread; // ms
+ /** Time between samples. */
+ volatile int delay; // ms
- /** Number of samples taken. */
- int sampleCount = 0;
+ /** Number of samples taken (~samples per second * number of threads). */
+ int totalThreadsSampled = 0;
/** Total time spent collecting samples. */
long totalSampleTime = 0;
+ /** The state of the profiler. */
+ volatile State state = State.PAUSED;
+
private SamplingProfiler() {}
/**
* Returns true if the profiler is running.
*/
public boolean isRunning() {
- return running;
+ return state == State.RUNNING;
}
/**
* Starts collecting samples.
*
- * @param threadsPerSecond number of threads to sample per second
+ * @param samplesPerSecond number of times to sample each thread per second
+ * @throws IllegalStateException if the profiler is
+ * {@linkplain #shutDown()} shutting down}
*/
- public synchronized void start(int threadsPerSecond) {
- if (threadsPerSecond < 1) {
- throw new IllegalArgumentException("threadsPerSecond < 1");
+ public synchronized void start(int samplesPerSecond) {
+ if (samplesPerSecond < 1) {
+ throw new IllegalArgumentException("samplesPerSecond < 1");
}
- if (!running) {
- logger.info("Starting profiler.");
- running = true;
+ ensureNotShuttingDown();
+ delay = 1000 / samplesPerSecond;
+ if (!isRunning()) {
+ if (DEBUG) logger.info("Starting profiler...");
+ state = State.RUNNING;
if (samplingThread == null) {
// TODO: Priority?
- samplingThread = new Thread(new Sampler());
+ samplingThread = new Thread(new Sampler(), "SamplingProfiler");
samplingThread.setDaemon(true);
samplingThread.start();
} else {
notifyAll();
}
}
- delayPerThread = 1000 / threadsPerSecond;
}
/**
- * Stops sample collection.
+ * Pauses sample collection.
*/
- public synchronized void stop() {
- if (running) {
- logger.info("Stopping profiler.");
- running = false;
+ public synchronized void pause() {
+ if (isRunning()) {
+ if (DEBUG) logger.info("Pausing profiler...");
+ state = State.PAUSED;
}
}
@@ -80,49 +96,116 @@ public class SamplingProfiler {
*
* <p>Note: The exact format is not documented because it's not set in
* stone yet.
+ *
+ * @throws IllegalStateException if the profiler is
+ * {@linkplain #shutDown()} shutting down}
*/
public synchronized byte[] snapshot() {
- if (pointer == 0 || sampleCount == 0) {
+ ensureNotShuttingDown();
+ if (pointer == 0 || totalThreadsSampled == 0) {
return null;
}
- int size = size(pointer);
- int collisions = collisions(pointer);
-
- long start = System.nanoTime();
- byte[] bytes = snapshot(pointer);
- long elapsed = System.nanoTime() - start;
-
- logger.info("Grabbed snapshot in " + (elapsed / 1000) + "us."
- + " Samples collected: " + sampleCount
- + ", Average sample time (per thread): "
- + (((totalSampleTime / sampleCount) << 10) / 1000) + "us"
- + ", Set size: " + size
- + ", Collisions: " + collisions);
- sampleCount = 0;
- totalSampleTime = 0;
-
- return bytes;
+ if (DEBUG) {
+ int size = size(pointer);
+ int collisions = collisions(pointer);
+
+ long start = System.nanoTime();
+ byte[] bytes = snapshot(pointer);
+ long elapsed = System.nanoTime() - start;
+
+ long averageSampleTime = ((totalSampleTime / totalThreadsSampled)
+ << 10) / 1000;
+ logger.info("Grabbed snapshot in " + (elapsed / 1000) + "us."
+ + " Samples collected: " + totalThreadsSampled
+ + ", Average sample time (per thread): "
+ + averageSampleTime + "us"
+ + ", Set size: " + size
+ + ", Collisions: " + collisions);
+ totalThreadsSampled = 0;
+ totalSampleTime = 0;
+
+ return bytes;
+ } else {
+ totalThreadsSampled = 0;
+ return snapshot(pointer);
+ }
}
/**
* Identifies the "event thread". For a user-facing application, this
* might be the UI thread. For a background process, this might be the
* thread that processes incoming requests.
+ *
+ * @throws IllegalStateException if the profiler is
+ * {@linkplain #shutDown()} shutting down}
*/
public synchronized void setEventThread(Thread eventThread) {
+ ensureNotShuttingDown();
if (pointer == 0) {
pointer = allocate();
}
setEventThread(pointer, eventThread);
}
+ private void ensureNotShuttingDown() {
+ if (state == State.SHUTTING_DOWN) {
+ throw new IllegalStateException("Profiler is shutting down.");
+ }
+ }
+
+ /**
+ * Shuts down the profiler thread and frees native memory. The profiler
+ * will recreate the thread the next time {@link #start(int)} is called.
+ *
+ * @throws IllegalStateException if the profiler is already shutting down
+ */
+ public void shutDown() {
+ Thread toStop;
+ synchronized (this) {
+ ensureNotShuttingDown();
+
+ toStop = samplingThread;
+ if (toStop == null) {
+ // The profiler hasn't started yet.
+ return;
+ }
+
+ state = State.SHUTTING_DOWN;
+ samplingThread = null;
+ notifyAll();
+ }
+
+ // Release lock to 'this' so background thread can grab it and stop.
+ boolean successful = false;
+ while (!successful) {
+ try {
+ toStop.join();
+ successful = true;
+ } catch (InterruptedException e) { /* ignore */ }
+ }
+
+ synchronized (this) {
+ if (pointer != 0) {
+ free(pointer);
+ pointer = 0;
+ }
+
+ totalThreadsSampled = 0;
+ totalSampleTime = 0;
+ state = State.PAUSED;
+ }
+ }
+
/** Collects some data. Returns number of threads sampled. */
private static native int sample(int pointer);
/** Allocates native state. */
private static native int allocate();
+ /** Frees native state. */
+ private static native void free(int pointer);
+
/** Gets the number of methods in the sample set. */
private static native int size(int pointer);
@@ -142,11 +225,15 @@ public class SamplingProfiler {
public void run() {
boolean firstSample = true;
while (true) {
- int threadsSampled;
synchronized (SamplingProfiler.this) {
- if (!running) {
- logger.info("Stopped profiler.");
- while (!running) {
+ if (!isRunning()) {
+ if (DEBUG) logger.info("Paused profiler.");
+ while (!isRunning()) {
+ if (state == State.SHUTTING_DOWN) {
+ // Stop thread.
+ return;
+ }
+
try {
SamplingProfiler.this.wait();
} catch (InterruptedException e) { /* ignore */ }
@@ -159,20 +246,24 @@ public class SamplingProfiler {
}
if (firstSample) {
- logger.info("Started profiler.");
+ if (DEBUG) logger.info("Started profiler.");
firstSample = false;
}
- long start = System.nanoTime();
- threadsSampled = sample(pointer);
- long elapsed = System.nanoTime() - start;
+ if (DEBUG) {
+ long start = System.nanoTime();
+ int threadsSampled = sample(pointer);
+ long elapsed = System.nanoTime() - start;
- sampleCount += threadsSampled;
- totalSampleTime += elapsed >> 10; // shift avoids overflow.
+ totalThreadsSampled += threadsSampled;
+ totalSampleTime += elapsed >> 10; // avoids overflow.
+ } else {
+ totalThreadsSampled += sample(pointer);
+ }
}
try {
- Thread.sleep(delayPerThread * threadsSampled);
+ Thread.sleep(delay);
} catch (InterruptedException e) { /* ignore */ }
}
}
@@ -211,9 +302,12 @@ public class SamplingProfiler {
private static void appendCounts(DataInputStream in, StringBuilder sb)
throws IOException {
- sb.append(" running: ").append(in.readShort()).append('\n');
- sb.append(" native: ").append(in.readShort()).append('\n');
- sb.append(" suspended: ").append(in.readShort()).append('\n');
+ sb.append(" running:\n");
+ sb.append(" caller: ").append(in.readShort()).append('\n');
+ sb.append(" leaf: ").append(in.readShort()).append('\n');
+ sb.append(" suspended:\n");
+ sb.append(" caller: ").append(in.readShort()).append('\n');
+ sb.append(" leaf: ").append(in.readShort()).append('\n');
}
/** This will be allocated when the user calls getInstance(). */