1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.os;
18
19
20import java.util.ArrayList;
21
22/**
23 * Collects performance data between two function calls in Bundle objects and
24 * outputs the results using writer of type {@link PerformanceResultsWriter}.
25 * <p>
26 * {@link #beginSnapshot(String)} and {@link #endSnapshot()} functions collect
27 * memory usage information and measure runtime between calls to begin and end.
28 * These functions logically wrap around an entire test, and should be called
29 * with name of test as the label, e.g. EmailPerformanceTest.
30 * <p>
31 * {@link #startTiming(String)} and {@link #stopTiming(String)} functions
32 * measure runtime between calls to start and stop. These functions logically
33 * wrap around a single test case or a small block of code, and should be called
34 * with the name of test case as the label, e.g. testSimpleSendMailSequence.
35 * <p>
36 * {@link #addIteration(String)} inserts intermediate measurement point which
37 * can be labeled with a String, e.g. Launch email app, compose, send, etc.
38 * <p>
39 * Snapshot and timing functions do not interfere with each other, and thus can
40 * be called in any order. The intended structure is to wrap begin/endSnapshot
41 * around calls to start/stopTiming, for example:
42 * <p>
43 * <code>beginSnapshot("EmailPerformanceTest");
44 * startTiming("testSimpleSendSequence");
45 * addIteration("Launch email app");
46 * addIteration("Compose");
47 * stopTiming("Send");
48 * startTiming("testComplexSendSequence");
49 * stopTiming("");
50 * startTiming("testAddLabel");
51 * stopTiming("");
52 * endSnapshot();</code>
53 * <p>
54 * Structure of results output is up to implementor of
55 * {@link PerformanceResultsWriter }.
56 *
57 * {@hide} Pending approval for public API.
58 */
59public class PerformanceCollector {
60
61    /**
62     * Interface for reporting performance data.
63     */
64    public interface PerformanceResultsWriter {
65
66        /**
67         * Callback invoked as first action in
68         * PerformanceCollector#beginSnapshot(String) for reporting the start of
69         * a performance snapshot.
70         *
71         * @param label description of code block between beginSnapshot and
72         *              PerformanceCollector#endSnapshot()
73         * @see PerformanceCollector#beginSnapshot(String)
74         */
75        public void writeBeginSnapshot(String label);
76
77        /**
78         * Callback invoked as last action in PerformanceCollector#endSnapshot()
79         * for reporting performance data collected in the snapshot.
80         *
81         * @param results memory and runtime metrics stored as key/value pairs,
82         *        in the same structure as returned by
83         *        PerformanceCollector#endSnapshot()
84         * @see PerformanceCollector#endSnapshot()
85         */
86        public void writeEndSnapshot(Bundle results);
87
88        /**
89         * Callback invoked as first action in
90         * PerformanceCollector#startTiming(String) for reporting the start of
91         * a timing measurement.
92         *
93         * @param label description of code block between startTiming and
94         *              PerformanceCollector#stopTiming(String)
95         * @see PerformanceCollector#startTiming(String)
96         */
97        public void writeStartTiming(String label);
98
99        /**
100         * Callback invoked as last action in
101         * {@link PerformanceCollector#stopTiming(String)} for reporting the
102         * sequence of timings measured.
103         *
104         * @param results runtime metrics of code block between calls to
105         *                startTiming and stopTiming, in the same structure as
106         *                returned by PerformanceCollector#stopTiming(String)
107         * @see PerformanceCollector#stopTiming(String)
108         */
109        public void writeStopTiming(Bundle results);
110
111        /**
112         * Callback invoked as last action in
113         * {@link PerformanceCollector#addMeasurement(String, long)} for
114         * reporting an integer type measurement.
115         *
116         * @param label short description of the metric that was measured
117         * @param value long value of the measurement
118         */
119        public void writeMeasurement(String label, long value);
120
121        /**
122         * Callback invoked as last action in
123         * {@link PerformanceCollector#addMeasurement(String, float)} for
124         * reporting a float type measurement.
125         *
126         * @param label short description of the metric that was measured
127         * @param value float value of the measurement
128         */
129        public void writeMeasurement(String label, float value);
130
131        /**
132         * Callback invoked as last action in
133         * {@link PerformanceCollector#addMeasurement(String, String)} for
134         * reporting a string field.
135         *
136         * @param label short description of the metric that was measured
137         * @param value string summary of the measurement
138         */
139        public void writeMeasurement(String label, String value);
140    }
141
142    /**
143     * In a results Bundle, this key references a List of iteration Bundles.
144     */
145    public static final String METRIC_KEY_ITERATIONS = "iterations";
146    /**
147     * In an iteration Bundle, this key describes the iteration.
148     */
149    public static final String METRIC_KEY_LABEL = "label";
150    /**
151     * In a results Bundle, this key reports the cpu time of the code block
152     * under measurement.
153     */
154    public static final String METRIC_KEY_CPU_TIME = "cpu_time";
155    /**
156     * In a results Bundle, this key reports the execution time of the code
157     * block under measurement.
158     */
159    public static final String METRIC_KEY_EXECUTION_TIME = "execution_time";
160    /**
161     * In a snapshot Bundle, this key reports the number of received
162     * transactions from the binder driver before collection started.
163     */
164    public static final String METRIC_KEY_PRE_RECEIVED_TRANSACTIONS = "pre_received_transactions";
165    /**
166     * In a snapshot Bundle, this key reports the number of transactions sent by
167     * the running program before collection started.
168     */
169    public static final String METRIC_KEY_PRE_SENT_TRANSACTIONS = "pre_sent_transactions";
170    /**
171     * In a snapshot Bundle, this key reports the number of received
172     * transactions from the binder driver.
173     */
174    public static final String METRIC_KEY_RECEIVED_TRANSACTIONS = "received_transactions";
175    /**
176     * In a snapshot Bundle, this key reports the number of transactions sent by
177     * the running program.
178     */
179    public static final String METRIC_KEY_SENT_TRANSACTIONS = "sent_transactions";
180    /**
181     * In a snapshot Bundle, this key reports the number of garbage collection
182     * invocations.
183     */
184    public static final String METRIC_KEY_GC_INVOCATION_COUNT = "gc_invocation_count";
185    /**
186     * In a snapshot Bundle, this key reports the amount of allocated memory
187     * used by the running program.
188     */
189    public static final String METRIC_KEY_JAVA_ALLOCATED = "java_allocated";
190    /**
191     * In a snapshot Bundle, this key reports the amount of free memory
192     * available to the running program.
193     */
194    public static final String METRIC_KEY_JAVA_FREE = "java_free";
195    /**
196     * In a snapshot Bundle, this key reports the number of private dirty pages
197     * used by dalvik.
198     */
199    public static final String METRIC_KEY_JAVA_PRIVATE_DIRTY = "java_private_dirty";
200    /**
201     * In a snapshot Bundle, this key reports the proportional set size for
202     * dalvik.
203     */
204    public static final String METRIC_KEY_JAVA_PSS = "java_pss";
205    /**
206     * In a snapshot Bundle, this key reports the number of shared dirty pages
207     * used by dalvik.
208     */
209    public static final String METRIC_KEY_JAVA_SHARED_DIRTY = "java_shared_dirty";
210    /**
211     * In a snapshot Bundle, this key reports the total amount of memory
212     * available to the running program.
213     */
214    public static final String METRIC_KEY_JAVA_SIZE = "java_size";
215    /**
216     * In a snapshot Bundle, this key reports the amount of allocated memory in
217     * the native heap.
218     */
219    public static final String METRIC_KEY_NATIVE_ALLOCATED = "native_allocated";
220    /**
221     * In a snapshot Bundle, this key reports the amount of free memory in the
222     * native heap.
223     */
224    public static final String METRIC_KEY_NATIVE_FREE = "native_free";
225    /**
226     * In a snapshot Bundle, this key reports the number of private dirty pages
227     * used by the native heap.
228     */
229    public static final String METRIC_KEY_NATIVE_PRIVATE_DIRTY = "native_private_dirty";
230    /**
231     * In a snapshot Bundle, this key reports the proportional set size for the
232     * native heap.
233     */
234    public static final String METRIC_KEY_NATIVE_PSS = "native_pss";
235    /**
236     * In a snapshot Bundle, this key reports the number of shared dirty pages
237     * used by the native heap.
238     */
239    public static final String METRIC_KEY_NATIVE_SHARED_DIRTY = "native_shared_dirty";
240    /**
241     * In a snapshot Bundle, this key reports the size of the native heap.
242     */
243    public static final String METRIC_KEY_NATIVE_SIZE = "native_size";
244    /**
245     * In a snapshot Bundle, this key reports the number of objects allocated
246     * globally.
247     */
248    public static final String METRIC_KEY_GLOBAL_ALLOC_COUNT = "global_alloc_count";
249    /**
250     * In a snapshot Bundle, this key reports the size of all objects allocated
251     * globally.
252     */
253    public static final String METRIC_KEY_GLOBAL_ALLOC_SIZE = "global_alloc_size";
254    /**
255     * In a snapshot Bundle, this key reports the number of objects freed
256     * globally.
257     */
258    public static final String METRIC_KEY_GLOBAL_FREED_COUNT = "global_freed_count";
259    /**
260     * In a snapshot Bundle, this key reports the size of all objects freed
261     * globally.
262     */
263    public static final String METRIC_KEY_GLOBAL_FREED_SIZE = "global_freed_size";
264    /**
265     * In a snapshot Bundle, this key reports the number of private dirty pages
266     * used by everything else.
267     */
268    public static final String METRIC_KEY_OTHER_PRIVATE_DIRTY = "other_private_dirty";
269    /**
270     * In a snapshot Bundle, this key reports the proportional set size for
271     * everything else.
272     */
273    public static final String METRIC_KEY_OTHER_PSS = "other_pss";
274    /**
275     * In a snapshot Bundle, this key reports the number of shared dirty pages
276     * used by everything else.
277     */
278    public static final String METRIC_KEY_OTHER_SHARED_DIRTY = "other_shared_dirty";
279
280    private PerformanceResultsWriter mPerfWriter;
281    private Bundle mPerfSnapshot;
282    private Bundle mPerfMeasurement;
283    private long mSnapshotCpuTime;
284    private long mSnapshotExecTime;
285    private long mCpuTime;
286    private long mExecTime;
287
288    public PerformanceCollector() {
289    }
290
291    public PerformanceCollector(PerformanceResultsWriter writer) {
292        setPerformanceResultsWriter(writer);
293    }
294
295    public void setPerformanceResultsWriter(PerformanceResultsWriter writer) {
296        mPerfWriter = writer;
297    }
298
299    /**
300     * Begin collection of memory usage information.
301     *
302     * @param label description of code block between beginSnapshot and
303     *              endSnapshot, used to label output
304     */
305    public void beginSnapshot(String label) {
306        if (mPerfWriter != null)
307            mPerfWriter.writeBeginSnapshot(label);
308        startPerformanceSnapshot();
309    }
310
311    /**
312     * End collection of memory usage information. Returns collected data in a
313     * Bundle object.
314     *
315     * @return Memory and runtime metrics stored as key/value pairs. Values are
316     *         of type long, and keys include:
317     *         <ul>
318     *         <li>{@link #METRIC_KEY_CPU_TIME cpu_time}
319     *         <li>{@link #METRIC_KEY_EXECUTION_TIME execution_time}
320     *         <li>{@link #METRIC_KEY_PRE_RECEIVED_TRANSACTIONS
321     *         pre_received_transactions}
322     *         <li>{@link #METRIC_KEY_PRE_SENT_TRANSACTIONS
323     *         pre_sent_transactions}
324     *         <li>{@link #METRIC_KEY_RECEIVED_TRANSACTIONS
325     *         received_transactions}
326     *         <li>{@link #METRIC_KEY_SENT_TRANSACTIONS sent_transactions}
327     *         <li>{@link #METRIC_KEY_GC_INVOCATION_COUNT gc_invocation_count}
328     *         <li>{@link #METRIC_KEY_JAVA_ALLOCATED java_allocated}
329     *         <li>{@link #METRIC_KEY_JAVA_FREE java_free}
330     *         <li>{@link #METRIC_KEY_JAVA_PRIVATE_DIRTY java_private_dirty}
331     *         <li>{@link #METRIC_KEY_JAVA_PSS java_pss}
332     *         <li>{@link #METRIC_KEY_JAVA_SHARED_DIRTY java_shared_dirty}
333     *         <li>{@link #METRIC_KEY_JAVA_SIZE java_size}
334     *         <li>{@link #METRIC_KEY_NATIVE_ALLOCATED native_allocated}
335     *         <li>{@link #METRIC_KEY_NATIVE_FREE native_free}
336     *         <li>{@link #METRIC_KEY_NATIVE_PRIVATE_DIRTY native_private_dirty}
337     *         <li>{@link #METRIC_KEY_NATIVE_PSS native_pss}
338     *         <li>{@link #METRIC_KEY_NATIVE_SHARED_DIRTY native_shared_dirty}
339     *         <li>{@link #METRIC_KEY_NATIVE_SIZE native_size}
340     *         <li>{@link #METRIC_KEY_GLOBAL_ALLOC_COUNT global_alloc_count}
341     *         <li>{@link #METRIC_KEY_GLOBAL_ALLOC_SIZE global_alloc_size}
342     *         <li>{@link #METRIC_KEY_GLOBAL_FREED_COUNT global_freed_count}
343     *         <li>{@link #METRIC_KEY_GLOBAL_FREED_SIZE global_freed_size}
344     *         <li>{@link #METRIC_KEY_OTHER_PRIVATE_DIRTY other_private_dirty}
345     *         <li>{@link #METRIC_KEY_OTHER_PSS other_pss}
346     *         <li>{@link #METRIC_KEY_OTHER_SHARED_DIRTY other_shared_dirty}
347     *         </ul>
348     */
349    public Bundle endSnapshot() {
350        endPerformanceSnapshot();
351        if (mPerfWriter != null)
352            mPerfWriter.writeEndSnapshot(mPerfSnapshot);
353        return mPerfSnapshot;
354    }
355
356    /**
357     * Start measurement of user and cpu time.
358     *
359     * @param label description of code block between startTiming and
360     *        stopTiming, used to label output
361     */
362    public void startTiming(String label) {
363        if (mPerfWriter != null)
364            mPerfWriter.writeStartTiming(label);
365        mPerfMeasurement = new Bundle();
366        mPerfMeasurement.putParcelableArrayList(
367                METRIC_KEY_ITERATIONS, new ArrayList<Parcelable>());
368        mExecTime = SystemClock.uptimeMillis();
369        mCpuTime = Process.getElapsedCpuTime();
370    }
371
372    /**
373     * Add a measured segment, and start measuring the next segment. Returns
374     * collected data in a Bundle object.
375     *
376     * @param label description of code block between startTiming and
377     *              addIteration, and between two calls to addIteration, used
378     *              to label output
379     * @return Runtime metrics stored as key/value pairs. Values are of type
380     *         long, and keys include:
381     *         <ul>
382     *         <li>{@link #METRIC_KEY_LABEL label}
383     *         <li>{@link #METRIC_KEY_CPU_TIME cpu_time}
384     *         <li>{@link #METRIC_KEY_EXECUTION_TIME execution_time}
385     *         </ul>
386     */
387    public Bundle addIteration(String label) {
388        mCpuTime = Process.getElapsedCpuTime() - mCpuTime;
389        mExecTime = SystemClock.uptimeMillis() - mExecTime;
390
391        Bundle iteration = new Bundle();
392        iteration.putString(METRIC_KEY_LABEL, label);
393        iteration.putLong(METRIC_KEY_EXECUTION_TIME, mExecTime);
394        iteration.putLong(METRIC_KEY_CPU_TIME, mCpuTime);
395        mPerfMeasurement.getParcelableArrayList(METRIC_KEY_ITERATIONS).add(iteration);
396
397        mExecTime = SystemClock.uptimeMillis();
398        mCpuTime = Process.getElapsedCpuTime();
399        return iteration;
400    }
401
402    /**
403     * Stop measurement of user and cpu time.
404     *
405     * @param label description of code block between addIteration or
406     *              startTiming and stopTiming, used to label output
407     * @return Runtime metrics stored in a bundle, including all iterations
408     *         between calls to startTiming and stopTiming. List of iterations
409     *         is keyed by {@link #METRIC_KEY_ITERATIONS iterations}.
410     */
411    public Bundle stopTiming(String label) {
412        addIteration(label);
413        if (mPerfWriter != null)
414            mPerfWriter.writeStopTiming(mPerfMeasurement);
415        return mPerfMeasurement;
416    }
417
418    /**
419     * Add an integer type measurement to the collector.
420     *
421     * @param label short description of the metric that was measured
422     * @param value long value of the measurement
423     */
424    public void addMeasurement(String label, long value) {
425        if (mPerfWriter != null)
426            mPerfWriter.writeMeasurement(label, value);
427    }
428
429    /**
430     * Add a float type measurement to the collector.
431     *
432     * @param label short description of the metric that was measured
433     * @param value float value of the measurement
434     */
435    public void addMeasurement(String label, float value) {
436        if (mPerfWriter != null)
437            mPerfWriter.writeMeasurement(label, value);
438    }
439
440    /**
441     * Add a string field to the collector.
442     *
443     * @param label short description of the metric that was measured
444     * @param value string summary of the measurement
445     */
446    public void addMeasurement(String label, String value) {
447        if (mPerfWriter != null)
448            mPerfWriter.writeMeasurement(label, value);
449    }
450
451    /*
452     * Starts tracking memory usage, binder transactions, and real & cpu timing.
453     */
454    private void startPerformanceSnapshot() {
455        // Create new snapshot
456        mPerfSnapshot = new Bundle();
457
458        // Add initial binder counts
459        Bundle binderCounts = getBinderCounts();
460        for (String key : binderCounts.keySet()) {
461            mPerfSnapshot.putLong("pre_" + key, binderCounts.getLong(key));
462        }
463
464        // Force a GC and zero out the performance counters. Do this
465        // before reading initial CPU/wall-clock times so we don't include
466        // the cost of this setup in our final metrics.
467        startAllocCounting();
468
469        // Record CPU time up to this point, and start timing. Note: this
470        // must happen at the end of this method, otherwise the timing will
471        // include noise.
472        mSnapshotExecTime = SystemClock.uptimeMillis();
473        mSnapshotCpuTime = Process.getElapsedCpuTime();
474    }
475
476    /*
477     * Stops tracking memory usage, binder transactions, and real & cpu timing.
478     * Stores collected data as type long into Bundle object for reporting.
479     */
480    private void endPerformanceSnapshot() {
481        // Stop the timing. This must be done first before any other counting is
482        // stopped.
483        mSnapshotCpuTime = Process.getElapsedCpuTime() - mSnapshotCpuTime;
484        mSnapshotExecTime = SystemClock.uptimeMillis() - mSnapshotExecTime;
485
486        stopAllocCounting();
487
488        long nativeMax = Debug.getNativeHeapSize() / 1024;
489        long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
490        long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
491
492        Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
493        Debug.getMemoryInfo(memInfo);
494
495        Runtime runtime = Runtime.getRuntime();
496
497        long dalvikMax = runtime.totalMemory() / 1024;
498        long dalvikFree = runtime.freeMemory() / 1024;
499        long dalvikAllocated = dalvikMax - dalvikFree;
500
501        // Add final binder counts
502        Bundle binderCounts = getBinderCounts();
503        for (String key : binderCounts.keySet()) {
504            mPerfSnapshot.putLong(key, binderCounts.getLong(key));
505        }
506
507        // Add alloc counts
508        Bundle allocCounts = getAllocCounts();
509        for (String key : allocCounts.keySet()) {
510            mPerfSnapshot.putLong(key, allocCounts.getLong(key));
511        }
512
513        mPerfSnapshot.putLong(METRIC_KEY_EXECUTION_TIME, mSnapshotExecTime);
514        mPerfSnapshot.putLong(METRIC_KEY_CPU_TIME, mSnapshotCpuTime);
515
516        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_SIZE, nativeMax);
517        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_ALLOCATED, nativeAllocated);
518        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_FREE, nativeFree);
519        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_PSS, memInfo.nativePss);
520        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_PRIVATE_DIRTY, memInfo.nativePrivateDirty);
521        mPerfSnapshot.putLong(METRIC_KEY_NATIVE_SHARED_DIRTY, memInfo.nativeSharedDirty);
522
523        mPerfSnapshot.putLong(METRIC_KEY_JAVA_SIZE, dalvikMax);
524        mPerfSnapshot.putLong(METRIC_KEY_JAVA_ALLOCATED, dalvikAllocated);
525        mPerfSnapshot.putLong(METRIC_KEY_JAVA_FREE, dalvikFree);
526        mPerfSnapshot.putLong(METRIC_KEY_JAVA_PSS, memInfo.dalvikPss);
527        mPerfSnapshot.putLong(METRIC_KEY_JAVA_PRIVATE_DIRTY, memInfo.dalvikPrivateDirty);
528        mPerfSnapshot.putLong(METRIC_KEY_JAVA_SHARED_DIRTY, memInfo.dalvikSharedDirty);
529
530        mPerfSnapshot.putLong(METRIC_KEY_OTHER_PSS, memInfo.otherPss);
531        mPerfSnapshot.putLong(METRIC_KEY_OTHER_PRIVATE_DIRTY, memInfo.otherPrivateDirty);
532        mPerfSnapshot.putLong(METRIC_KEY_OTHER_SHARED_DIRTY, memInfo.otherSharedDirty);
533    }
534
535    /*
536     * Starts allocation counting. This triggers a gc and resets the counts.
537     */
538    private static void startAllocCounting() {
539        // Before we start trigger a GC and reset the debug counts. Run the
540        // finalizers and another GC before starting and stopping the alloc
541        // counts. This will free up any objects that were just sitting around
542        // waiting for their finalizers to be run.
543        Runtime.getRuntime().gc();
544        Runtime.getRuntime().runFinalization();
545        Runtime.getRuntime().gc();
546
547        Debug.resetAllCounts();
548
549        // start the counts
550        Debug.startAllocCounting();
551    }
552
553    /*
554     * Stops allocation counting.
555     */
556    private static void stopAllocCounting() {
557        Runtime.getRuntime().gc();
558        Runtime.getRuntime().runFinalization();
559        Runtime.getRuntime().gc();
560        Debug.stopAllocCounting();
561    }
562
563    /*
564     * Returns a bundle with the current results from the allocation counting.
565     */
566    private static Bundle getAllocCounts() {
567        Bundle results = new Bundle();
568        results.putLong(METRIC_KEY_GLOBAL_ALLOC_COUNT, Debug.getGlobalAllocCount());
569        results.putLong(METRIC_KEY_GLOBAL_ALLOC_SIZE, Debug.getGlobalAllocSize());
570        results.putLong(METRIC_KEY_GLOBAL_FREED_COUNT, Debug.getGlobalFreedCount());
571        results.putLong(METRIC_KEY_GLOBAL_FREED_SIZE, Debug.getGlobalFreedSize());
572        results.putLong(METRIC_KEY_GC_INVOCATION_COUNT, Debug.getGlobalGcInvocationCount());
573        return results;
574    }
575
576    /*
577     * Returns a bundle with the counts for various binder counts for this
578     * process. Currently the only two that are reported are the number of send
579     * and the number of received transactions.
580     */
581    private static Bundle getBinderCounts() {
582        Bundle results = new Bundle();
583        results.putLong(METRIC_KEY_SENT_TRANSACTIONS, Debug.getBinderSentTransactions());
584        results.putLong(METRIC_KEY_RECEIVED_TRANSACTIONS, Debug.getBinderReceivedTransactions());
585        return results;
586    }
587}
588