OSDN Git Service

A sampling profiler for Dalvik.
[android-x86/dalvik.git] / libcore / dalvik / src / main / java / dalvik / system / SamplingProfiler.java
1 package dalvik.system;
2
3 import java.util.logging.Logger;
4 import java.io.DataInputStream;
5 import java.io.ByteArrayInputStream;
6 import java.io.IOException;
7
8 /**
9  * A sampling profiler.
10  *
11  * @hide
12  */
13 public class SamplingProfiler {
14
15     private static final Logger logger = Logger.getLogger(
16             SamplingProfiler.class.getName());
17
18     /** Pointer to native state. */
19     int pointer = 0;
20
21     /** The thread that collects samples. */
22     Thread samplingThread;
23
24     /** Whether or not the profiler is running. */
25     boolean running = false;
26     int delayPerThread; // ms
27
28     /** Number of samples taken. */
29     int sampleCount = 0;
30
31     /** Total time spent collecting samples. */
32     long totalSampleTime = 0;
33
34     private SamplingProfiler() {}
35
36     /**
37      * Returns true if the profiler is running.
38      */
39     public boolean isRunning() {
40         return running;
41     }
42
43     /**
44      * Starts collecting samples.
45      *
46      * @param threadsPerSecond number of threads to sample per second
47      */
48     public synchronized void start(int threadsPerSecond) {
49         if (threadsPerSecond < 1) {
50             throw new IllegalArgumentException("threadsPerSecond < 1");
51         }
52         if (!running) {
53             logger.info("Starting profiler.");
54             running = true;
55             if (samplingThread == null) {
56                 // TODO: Priority?
57                 samplingThread = new Thread(new Sampler());
58                 samplingThread.setDaemon(true);
59                 samplingThread.start();
60             } else {
61                 notifyAll();
62             }
63         }
64         delayPerThread = 1000 / threadsPerSecond;
65     }
66
67     /**
68      * Stops sample collection.
69      */
70     public synchronized void stop() {
71         if (running) {
72             logger.info("Stopping profiler.");
73             running = false;
74         }
75     }
76
77     /**
78      * Captures collected samples and clears the sample set. Returns null
79      * if no data has been captured.
80      *
81      * <p>Note: The exact format is not documented because it's not set in
82      * stone yet.
83      */
84     public synchronized byte[] snapshot() {
85         if (pointer == 0 || sampleCount == 0) {
86             return null;
87         }
88
89         int size = size(pointer);
90         int collisions = collisions(pointer);
91
92         long start = System.nanoTime();
93         byte[] bytes = snapshot(pointer);
94         long elapsed = System.nanoTime() - start;
95
96         logger.info("Grabbed snapshot in " + (elapsed / 1000) + "us."
97                 + " Samples collected: " + sampleCount
98                 + ", Average sample time (per thread): "
99                     + (((totalSampleTime / sampleCount) << 10) / 1000) + "us"
100                 + ", Set size: " + size
101                 + ", Collisions: " + collisions);
102         sampleCount = 0;
103         totalSampleTime = 0;
104
105         return bytes;
106     }
107
108     /**
109      * Identifies the "event thread". For a user-facing application, this
110      * might be the UI thread. For a background process, this might be the
111      * thread that processes incoming requests.
112      */
113     public synchronized void setEventThread(Thread eventThread) {
114         if (pointer == 0) {
115             pointer = allocate();
116         }
117         setEventThread(pointer, eventThread);
118     }
119
120     /** Collects some data. Returns number of threads sampled. */
121     private static native int sample(int pointer);
122
123     /** Allocates native state. */
124     private static native int allocate();
125
126     /** Gets the number of methods in the sample set. */
127     private static native int size(int pointer);
128
129     /** Gets the number of collisions in the sample set. */
130     private static native int collisions(int pointer);
131
132     /** Captures data. */
133     private static native byte[] snapshot(int pointer);
134
135     /** Identifies the "event thread". */
136     private static native void setEventThread(int pointer, Thread thread);
137
138     /**
139      * Background thread that collects samples.
140      */
141     class Sampler implements Runnable {
142         public void run() {
143             boolean firstSample = true;
144             while (true) {
145                 int threadsSampled;
146                 synchronized (SamplingProfiler.this) {
147                     if (!running) {
148                         logger.info("Stopped profiler.");
149                         while (!running) {
150                             try {
151                                 SamplingProfiler.this.wait();
152                             } catch (InterruptedException e) { /* ignore */ }
153                         }
154                         firstSample = true;
155                     }
156
157                     if (pointer == 0) {
158                         pointer = allocate();
159                     }
160
161                     if (firstSample) {
162                         logger.info("Started profiler.");
163                         firstSample = false;
164                     }
165
166                     long start = System.nanoTime();
167                     threadsSampled = sample(pointer);
168                     long elapsed = System.nanoTime() - start;
169
170                     sampleCount += threadsSampled;
171                     totalSampleTime += elapsed >> 10; // shift avoids overflow.
172                 }
173
174                 try {
175                     Thread.sleep(delayPerThread * threadsSampled);
176                 } catch (InterruptedException e) { /* ignore */ }
177             }
178         }
179     }
180
181     /**
182      * Dumps a snapshot to the log. Useful for debugging.
183      */
184     public static void logSnapshot(byte[] snapshot) {
185         DataInputStream in = new DataInputStream(
186                 new ByteArrayInputStream(snapshot));
187         try {
188             int version = in.readUnsignedShort();
189             int classCount = in.readUnsignedShort();
190             StringBuilder sb = new StringBuilder();
191             sb.append("version=").append(version).append(' ')
192                     .append("classes=").append(classCount).append('\n');
193             logger.info(sb.toString());
194             for (int i = 0; i < classCount; i++) {
195                 sb = new StringBuilder();
196                 sb.append("class ").append(in.readUTF()).append('\n');
197                 int methodCount = in.readUnsignedShort();
198                 for (int m = 0; m < methodCount; m++) {
199                     sb.append("  ").append(in.readUTF()).append(":\n");
200                     sb.append("    event:\n");
201                     appendCounts(in, sb);
202                     sb.append("    other:\n");
203                     appendCounts(in, sb);
204                 }
205                 logger.info(sb.toString());
206             }
207         } catch (IOException e) {
208             logger.warning(e.toString());
209         }
210     }
211
212     private static void appendCounts(DataInputStream in, StringBuilder sb)
213             throws IOException {
214         sb.append("      running: ").append(in.readShort()).append('\n');
215         sb.append("      native: ").append(in.readShort()).append('\n');
216         sb.append("      suspended: ").append(in.readShort()).append('\n');
217     }
218
219     /** This will be allocated when the user calls getInstance(). */
220     private static final SamplingProfiler instance = new SamplingProfiler();
221
222     /**
223      * Gets the profiler. The profiler is not running by default. Start it
224      * with {@link #start(int)}.
225      */
226     public static synchronized SamplingProfiler getInstance() {
227         return instance;
228     }
229 }