EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.util]

COVERAGE SUMMARY FOR SOURCE FILE [Profiler.java]

nameclass, %method, %block, %line, %
Profiler.java100% (1/1)67%  (14/21)61%  (591/963)58%  (135/233)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Profiler100% (1/1)67%  (14/21)61%  (591/963)58%  (135/233)
copyInThread (InputStream, OutputStream): void 0%   (0/1)0%   (0/8)0%   (0/2)
exec (String []): String 0%   (0/1)0%   (0/53)0%   (0/13)
main (String []): void 0%   (0/1)0%   (0/6)0%   (0/2)
premain (String, Instrumentation): void 0%   (0/1)0%   (0/3)0%   (0/2)
readRunnableStackTraces (int): List 0%   (0/1)0%   (0/36)0%   (0/5)
readStackTrace (LineNumberReader): List 0%   (0/1)0%   (0/79)0%   (0/28)
run (String []): void 0%   (0/1)0%   (0/162)0%   (0/37)
tick (): void 100% (1/1)79%  (26/33)75%  (9/12)
run (): void 100% (1/1)89%  (16/18)75%  (6/8)
processList (List): void 100% (1/1)91%  (139/152)94%  (29/31)
stopCollecting (): Profiler 100% (1/1)94%  (15/16)86%  (6/7)
increment (HashMap, String, int): int 100% (1/1)96%  (53/55)93%  (13/14)
<static initializer> 100% (1/1)100% (5/5)100% (1/1)
Profiler (): void 100% (1/1)100% (37/37)100% (9/9)
appendTop (StringBuilder, HashMap, int, int, boolean): void 100% (1/1)100% (112/112)100% (23/23)
getInstrumentation (): Instrumentation 100% (1/1)100% (2/2)100% (1/1)
getRunnableStackTraces (): List 100% (1/1)100% (43/43)100% (12/12)
getTop (int): String 100% (1/1)100% (7/7)100% (2/2)
getTopTraces (int): String 100% (1/1)100% (93/93)100% (16/16)
startCollecting (): Profiler 100% (1/1)100% (16/16)100% (4/4)
startsWithAny (String, String []): boolean 100% (1/1)100% (27/27)100% (4/4)

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

[all classes][org.h2.util]
EMMA 2.0.5312 (C) Vladimir Roubtsov