• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 
17 package com.android.gallery3d.util;
18 
19 import android.os.Handler;
20 import android.os.HandlerThread;
21 import android.os.Process;
22 
23 import java.util.ArrayList;
24 import java.util.Random;
25 
26 // The Profile class is used to collect profiling information for a thread. It
27 // samples stack traces for a thread periodically. enable() and disable() is
28 // used to enable and disable profiling for the calling thread. The profiling
29 // information can then be dumped to a file using the dumpToFile() method.
30 //
31 // The disableAll() method can be used to disable profiling for all threads and
32 // can be called in onPause() to ensure all profiling is disabled when an
33 // activity is paused.
34 public class Profile {
35     private static final String TAG = "Profile";
36     private static final int NS_PER_MS = 1000000;
37 
38     // This is a watchdog entry for one thread.
39     // For every cycleTime period, we dump the stack of the thread.
40     private static class WatchEntry {
41         Thread thread;
42 
43         // Both are in milliseconds
44         int cycleTime;
45         int wakeTime;
46 
47         boolean isHolding;
48         ArrayList<String[]> holdingStacks = new ArrayList<String[]>();
49     }
50 
51     // This is a watchdog thread which dumps stacks of other threads periodically.
52     private static Watchdog sWatchdog = new Watchdog();
53 
54     private static class Watchdog {
55         private ArrayList<WatchEntry> mList = new ArrayList<WatchEntry>();
56         private HandlerThread mHandlerThread;
57         private Handler mHandler;
58         private Runnable mProcessRunnable = new Runnable() {
59             public void run() {
60                 synchronized (Watchdog.this) {
61                     processList();
62                 }
63             }
64         };
65         private Random mRandom = new Random();
66         private ProfileData mProfileData = new ProfileData();
67 
Watchdog()68         public Watchdog() {
69             mHandlerThread = new HandlerThread("Watchdog Handler",
70                     Process.THREAD_PRIORITY_FOREGROUND);
71             mHandlerThread.start();
72             mHandler = new Handler(mHandlerThread.getLooper());
73         }
74 
addWatchEntry(Thread thread, int cycleTime)75         public synchronized void addWatchEntry(Thread thread, int cycleTime) {
76             WatchEntry e = new WatchEntry();
77             e.thread = thread;
78             e.cycleTime = cycleTime;
79             int firstDelay = 1 + mRandom.nextInt(cycleTime);
80             e.wakeTime = (int) (System.nanoTime() / NS_PER_MS) + firstDelay;
81             mList.add(e);
82             processList();
83         }
84 
removeWatchEntry(Thread thread)85         public synchronized void removeWatchEntry(Thread thread) {
86             for (int i = 0; i < mList.size(); i++) {
87                 if (mList.get(i).thread == thread) {
88                     mList.remove(i);
89                     break;
90                 }
91             }
92             processList();
93         }
94 
removeAllWatchEntries()95         public synchronized void removeAllWatchEntries() {
96             mList.clear();
97             processList();
98         }
99 
processList()100         private void processList() {
101             mHandler.removeCallbacks(mProcessRunnable);
102             if (mList.size() == 0) return;
103 
104             int currentTime = (int) (System.nanoTime() / NS_PER_MS);
105             int nextWakeTime = 0;
106 
107             for (WatchEntry entry : mList) {
108                 if (currentTime > entry.wakeTime) {
109                     entry.wakeTime += entry.cycleTime;
110                     Thread thread = entry.thread;
111                     sampleStack(entry);
112                 }
113 
114                 if (entry.wakeTime > nextWakeTime) {
115                     nextWakeTime = entry.wakeTime;
116                 }
117             }
118 
119             long delay = nextWakeTime - currentTime;
120             mHandler.postDelayed(mProcessRunnable, delay);
121         }
122 
sampleStack(WatchEntry entry)123         private void sampleStack(WatchEntry entry) {
124             Thread thread = entry.thread;
125             StackTraceElement[] stack = thread.getStackTrace();
126             String[] lines = new String[stack.length];
127             for (int i = 0; i < stack.length; i++) {
128                 lines[i] = stack[i].toString();
129             }
130             if (entry.isHolding) {
131                 entry.holdingStacks.add(lines);
132             } else {
133                 mProfileData.addSample(lines);
134             }
135         }
136 
findEntry(Thread thread)137         private WatchEntry findEntry(Thread thread) {
138             for (int i = 0; i < mList.size(); i++) {
139                 WatchEntry entry = mList.get(i);
140                 if (entry.thread == thread) return entry;
141             }
142             return null;
143         }
144 
dumpToFile(String filename)145         public synchronized void dumpToFile(String filename) {
146             mProfileData.dumpToFile(filename);
147         }
148 
reset()149         public synchronized void reset() {
150             mProfileData.reset();
151         }
152 
hold(Thread t)153         public synchronized void hold(Thread t) {
154             WatchEntry entry = findEntry(t);
155 
156             // This can happen if the profiling is disabled (probably from
157             // another thread). Same check is applied in commit() and drop()
158             // below.
159             if (entry == null) return;
160 
161             entry.isHolding = true;
162         }
163 
commit(Thread t)164         public synchronized void commit(Thread t) {
165             WatchEntry entry = findEntry(t);
166             if (entry == null) return;
167             ArrayList<String[]> stacks = entry.holdingStacks;
168             for (int i = 0; i < stacks.size(); i++) {
169                 mProfileData.addSample(stacks.get(i));
170             }
171             entry.isHolding = false;
172             entry.holdingStacks.clear();
173         }
174 
drop(Thread t)175         public synchronized void drop(Thread t) {
176             WatchEntry entry = findEntry(t);
177             if (entry == null) return;
178             entry.isHolding = false;
179             entry.holdingStacks.clear();
180         }
181     }
182 
183     // Enable profiling for the calling thread. Periodically (every cycleTimeInMs
184     // milliseconds) sample the stack trace of the calling thread.
enable(int cycleTimeInMs)185     public static void enable(int cycleTimeInMs) {
186         Thread t = Thread.currentThread();
187         sWatchdog.addWatchEntry(t, cycleTimeInMs);
188     }
189 
190     // Disable profiling for the calling thread.
disable()191     public static void disable() {
192         sWatchdog.removeWatchEntry(Thread.currentThread());
193     }
194 
195     // Disable profiling for all threads.
disableAll()196     public static void disableAll() {
197         sWatchdog.removeAllWatchEntries();
198     }
199 
200     // Dump the profiling data to a file.
dumpToFile(String filename)201     public static void dumpToFile(String filename) {
202         sWatchdog.dumpToFile(filename);
203     }
204 
205     // Reset the collected profiling data.
reset()206     public static void reset() {
207         sWatchdog.reset();
208     }
209 
210     // Hold the future samples coming from current thread until commit() or
211     // drop() is called, and those samples are recorded or ignored as a result.
212     // This must called after enable() to be effective.
hold()213     public static void hold() {
214         sWatchdog.hold(Thread.currentThread());
215     }
216 
commit()217     public static void commit() {
218         sWatchdog.commit(Thread.currentThread());
219     }
220 
drop()221     public static void drop() {
222         sWatchdog.drop(Thread.currentThread());
223     }
224 }
225