File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\util\Profiler.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.util;
8
9 import java.io.ByteArrayOutputStream;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.io.LineNumberReader;
13 import java.io.OutputStream;
14 import java.io.StringReader;
15 import java.util.ArrayList;
16 import java.lang.instrument.Instrumentation;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21
22 /**
23 * A simple CPU profiling tool similar to java -Xrunhprof. It can be used
24 * in-process (to profile the current application) or as a standalone program
25 * (to profile a different process).
26 */
27 public class Profiler implements Runnable {
28
29 private static Instrumentation instrumentation;
30 private static final String LINE_SEPARATOR = System.getProperty("line.separator", "\n");
31 private static final int MAX_ELEMENTS = 1000;
32
33 public int interval = 2;
34 public int depth = 48;
35 public boolean paused;
36 public boolean sumClasses;
37
38 private int pid;
39
40 private final String[] ignoreLines = {};
41 private final String[] ignorePackages = (
42 "java," +
43 "sun," +
44 "com.sun."
45 ).split(",");
46 private final String[] ignoreThreads = (
47 "java.lang.Object.wait," +
48 "java.lang.Thread.dumpThreads," +
49 "java.lang.Thread.getThreads," +
50 "java.lang.Thread.sleep," +
51 "java.lang.UNIXProcess.waitForProcessExit," +
52 "java.net.PlainSocketImpl.accept," +
53 "java.net.PlainSocketImpl.socketAccept," +
54 "java.net.SocketInputStream.socketRead," +
55 "java.net.SocketOutputStream.socketWrite," +
56 "sun.awt.windows.WToolkit.eventLoop," +
57 "sun.misc.Unsafe.park," +
58 "dalvik.system.VMStack.getThreadStackTrace," +
59 "dalvik.system.NativeStart.run"
60 ).split(",");
61
62 private volatile boolean stop;
63 private final HashMap<String, Integer> counts = new HashMap<String, Integer>();
64
65 /**
66 * The summary (usually one entry per package, unless sumClasses is enabled,
67 * in which case it's one entry per class).
68 */
69 private final HashMap<String, Integer> summary = new HashMap<String, Integer>();
70 private int minCount = 1;
71 private int total;
72 private Thread thread;
73 private long start;
74 private long time;
75
76 /**
77 * This method is called when the agent is installed.
78 *
79 * @param agentArgs the agent arguments
80 * @param inst the instrumentation object
81 */
82 public static void premain(String agentArgs, Instrumentation inst) {
83 instrumentation = inst;
84 }
85
86 /**
87 * Get the instrumentation object if started as an agent.
88 *
89 * @return the instrumentation, or null
90 */
91 public static Instrumentation getInstrumentation() {
92 return instrumentation;
93 }
94
95 /**
96 * Run the command line version of the profiler. The JDK (jps and jstack)
97 * need to be in the path.
98 *
99 * @param args the process id of the process - if not set the java processes
100 * are listed
101 */
102 public static void main(String... args) {
103 new Profiler().run(args);
104 }
105
106 private void run(String... args) {
107 if (args.length == 0) {
108 System.out.println("Show profiling data");
109 System.out.println("Usage: " + getClass().getName() + " <pid>");
110 System.out.println("Processes:");
111 String processes = exec("jps", "-l");
112 System.out.println(processes);
113 return;
114 }
115 pid = Integer.parseInt(args[0]);
116 start = System.currentTimeMillis();
117 long last = 0;
118 while (true) {
119 tick();
120 long t = System.currentTimeMillis();
121 if (t - last > 5000) {
122 time = System.currentTimeMillis() - start;
123 System.out.println(getTopTraces(3));
124 last = t;
125 }
126 }
127 }
128
129 private static List<Object[]> getRunnableStackTraces() {
130 ArrayList<Object[]> list = new ArrayList<Object[]>();
131 Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
132 for (Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
133 Thread t = entry.getKey();
134 if (t.getState() != Thread.State.RUNNABLE) {
135 continue;
136 }
137 StackTraceElement[] dump = entry.getValue();
138 if (dump == null || dump.length == 0) {
139 continue;
140 }
141 list.add(dump);
142 }
143 return list;
144 }
145
146 private static List<Object[]> readRunnableStackTraces(int pid) {
147 ArrayList<Object[]> list = new ArrayList<Object[]>();
148 try {
149 String jstack = exec("jstack", "" + pid);
150 LineNumberReader r = new LineNumberReader(new StringReader(jstack));
151 while (true) {
152 String line = r.readLine();
153 if (line == null) {
154 break;
155 }
156 if (!line.startsWith("\"")) {
157 // not a thread
158 continue;
159 }
160 line = r.readLine();
161 if (line == null) {
162 break;
163 }
164 line = line.trim();
165 if (!line.startsWith("java.lang.Thread.State: RUNNABLE")) {
166 continue;
167 }
168 ArrayList<String> stack = new ArrayList<String>();
169 while (true) {
170 line = r.readLine();
171 if (line == null) {
172 break;
173 }
174 line = line.trim();
175 if (line.startsWith("- ")) {
176 continue;
177 }
178 if (!line.startsWith("at ")) {
179 break;
180 }
181 line = line.substring(3).trim();
182 stack.add(line);
183 }
184 if (stack.size() > 0) {
185 String[] s = stack.toArray(new String[stack.size()]);
186 list.add(s);
187 }
188 }
189 return list;
190 } catch (IOException e) {
191 throw new RuntimeException(e);
192 }
193 }
194
195 private static String exec(String... args) {
196 ByteArrayOutputStream err = new ByteArrayOutputStream();
197 ByteArrayOutputStream out = new ByteArrayOutputStream();
198 try {
199 Process p = Runtime.getRuntime().exec(args);
200 copyInThread(p.getInputStream(), out);
201 copyInThread(p.getErrorStream(), err);
202 p.waitFor();
203 String e = new String(err.toByteArray(), "UTF-8");
204 if (e.length() > 0) {
205 throw new RuntimeException(e);
206 }
207 String output = new String(out.toByteArray(), "UTF-8");
208 return output;
209 } catch (Exception e) {
210 throw new RuntimeException(e);
211 }
212 }
213
214 private static void copyInThread(final InputStream in, final OutputStream out) {
215 new Thread("Profiler stream copy") {
216 public void run() {
217 byte[] buffer = new byte[4096];
218 try {
219 while (true) {
220 int len = in.read(buffer, 0, buffer.length);
221 if (len < 0) {
222 break;
223 }
224 out.write(buffer, 0, len);
225 }
226 } catch (Exception e) {
227 throw new RuntimeException(e);
228 }
229 }
230 }.run();
231 }
232
233 /**
234 * Start collecting profiling data.
235 */
236 public void startCollecting() {
237 thread = new Thread(this, "Profiler");
238 thread.setDaemon(true);
239 thread.start();
240 }
241
242 /**
243 * Stop collecting.
244 */
245 public void stopCollecting() {
246 stop = true;
247 if (thread != null) {
248 try {
249 thread.join();
250 } catch (InterruptedException e) {
251 // ignore
252 }
253 thread = null;
254 }
255 }
256
257 public void run() {
258 start = System.currentTimeMillis();
259 while (!stop) {
260 try {
261 tick();
262 } catch (Throwable t) {
263 break;
264 }
265 }
266 time = System.currentTimeMillis() - start;
267 }
268
269 private void tick() {
270 if (interval > 0) {
271 if (paused) {
272 return;
273 }
274 try {
275 Thread.sleep(interval);
276 } catch (Exception e) {
277 // ignore
278 }
279 }
280
281 List<Object[]> list;
282 if (pid != 0) {
283 list = readRunnableStackTraces(pid);
284 } else {
285 list = getRunnableStackTraces();
286 }
287 for (Object[] dump : list) {
288 if (startsWithAny(dump[0].toString(), ignoreThreads)) {
289 continue;
290 }
291 StringBuilder buff = new StringBuilder();
292 // simple recursive calls are ignored
293 String last = null;
294 boolean packageCounts = false;
295 for (int j = 0, i = 0; i < dump.length && j < depth; i++) {
296 String el = dump[i].toString();
297 if (!el.equals(last) && !startsWithAny(el, ignoreLines)) {
298 last = el;
299 buff.append("at ").append(el).append(LINE_SEPARATOR);
300 if (!packageCounts && !startsWithAny(el, ignorePackages)) {
301 packageCounts = true;
302 int index = 0;
303 for (; index < el.length(); index++) {
304 char c = el.charAt(index);
305 if (c == '(' || Character.isUpperCase(c)) {
306 break;
307 }
308 }
309 if (index > 0 && el.charAt(index - 1) == '.') {
310 index--;
311 }
312 if (sumClasses) {
313 int m = el.indexOf('.', index + 1);
314 index = m >= 0 ? m : index;
315 }
316 String groupName = el.substring(0, index);
317 increment(summary, groupName, 0);
318 }
319 j++;
320 }
321 }
322 if (buff.length() > 0) {
323 minCount = increment(counts, buff.toString().trim(), minCount);
324 total++;
325 }
326 }
327 }
328
329 private static boolean startsWithAny(String s, String[] prefixes) {
330 for (String p : prefixes) {
331 if (p.length() > 0 && s.startsWith(p)) {
332 return true;
333 }
334 }
335 return false;
336 }
337
338 private static int increment(HashMap<String, Integer> map, String trace, int minCount) {
339 Integer oldCount = map.get(trace);
340 if (oldCount == null) {
341 map.put(trace, 1);
342 } else {
343 map.put(trace, oldCount + 1);
344 }
345 while (map.size() > MAX_ELEMENTS) {
346 for (Iterator<Map.Entry<String, Integer>> ei = map.entrySet().iterator(); ei.hasNext();) {
347 Map.Entry<String, Integer> e = ei.next();
348 if (e.getValue() <= minCount) {
349 ei.remove();
350 }
351 }
352 if (map.size() > MAX_ELEMENTS) {
353 minCount++;
354 }
355 }
356 return minCount;
357 }
358
359 /**
360 * Get the top stack traces.
361 *
362 * @param count the maximum number of stack traces
363 * @return the stack traces.
364 */
365 public String getTop(int count) {
366 stopCollecting();
367 return getTopTraces(count);
368 }
369
370 private String getTopTraces(int count) {
371 StringBuilder buff = new StringBuilder();
372 buff.append("Profiler: top ").append(count).append(" stack trace(s) of ").append(time).
373 append(" ms:").append(LINE_SEPARATOR);
374 if (counts.size() == 0) {
375 buff.append("(none)").append(LINE_SEPARATOR);
376 }
377 HashMap<String, Integer> copy = new HashMap<String, Integer>(counts);
378 appendTop(buff, copy, count, total, false);
379 buff.append("summary:").append(LINE_SEPARATOR);
380 copy = new HashMap<String, Integer>(summary);
381 appendTop(buff, copy, count, total, true);
382 buff.append('.');
383 return buff.toString();
384 }
385
386 private static void appendTop(StringBuilder buff, HashMap<String, Integer> map, int count, int total, boolean table) {
387 for (int x = 0, min = 0;;) {
388 int highest = 0;
389 Map.Entry<String, Integer> best = null;
390 for (Map.Entry<String, Integer> el : map.entrySet()) {
391 if (el.getValue() > highest) {
392 best = el;
393 highest = el.getValue();
394 }
395 }
396 if (best == null) {
397 break;
398 }
399 map.remove(best.getKey());
400 if (++x >= count) {
401 if (best.getValue() < min) {
402 break;
403 }
404 min = best.getValue();
405 }
406 int c = best.getValue();
407 int percent = 100 * c / Math.max(total, 1);
408 if (table) {
409 if (percent > 1) {
410 buff.append(percent).
411 append("%: ").append(best.getKey()).
412 append(LINE_SEPARATOR);
413 }
414 } else {
415 buff.append(c).append('/').append(total).append(" (").
416 append(percent).
417 append("%):").append(LINE_SEPARATOR).
418 append(best.getKey()).
419 append(LINE_SEPARATOR);
420 }
421 }
422 }
423
424 }
425