提交 43cad5c9 authored 作者: Thomas Mueller's avatar Thomas Mueller

The built-in Profiler now also lists ticks per package.

上级 c2b3b4c2
...@@ -17,6 +17,7 @@ import org.h2.engine.Constants; ...@@ -17,6 +17,7 @@ import org.h2.engine.Constants;
* A simple CPU profiling tool similar to java -Xrunhprof. * A simple CPU profiling tool similar to java -Xrunhprof.
*/ */
public class Profiler implements Runnable { public class Profiler implements Runnable {
private static Instrumentation instrumentation; private static Instrumentation instrumentation;
private static final int MAX_ELEMENTS = 1000; private static final int MAX_ELEMENTS = 1000;
...@@ -25,15 +26,21 @@ public class Profiler implements Runnable { ...@@ -25,15 +26,21 @@ public class Profiler implements Runnable {
public boolean paused; public boolean paused;
private String[] ignoreLines = StringUtils.arraySplit("", ',', true); private String[] ignoreLines = StringUtils.arraySplit("", ',', true);
private String[] ignorePackages = StringUtils.arraySplit(
"java," +
"sun," +
"com.sun.,"
, ',', true);
private String[] ignoreThreads = StringUtils.arraySplit( private String[] ignoreThreads = StringUtils.arraySplit(
"java.lang.Object.wait," +
"java.lang.Thread.dumpThreads," + "java.lang.Thread.dumpThreads," +
"java.lang.Thread.getThreads," + "java.lang.Thread.getThreads," +
"java.net.PlainSocketImpl.socketAccept," +
"java.net.SocketInputStream.socketRead0," +
"java.net.SocketOutputStream.socketWrite0," +
"java.lang.UNIXProcess.waitForProcessExit," +
"java.lang.Object.wait," +
"java.lang.Thread.sleep," + "java.lang.Thread.sleep," +
"java.lang.UNIXProcess.waitForProcessExit," +
"java.net.PlainSocketImpl.accept," +
"java.net.PlainSocketImpl.socketAccept," +
"java.net.SocketInputStream.socketRead," +
"java.net.SocketOutputStream.socketWrite," +
"sun.awt.windows.WToolkit.eventLoop," + "sun.awt.windows.WToolkit.eventLoop," +
"sun.misc.Unsafe.park," + "sun.misc.Unsafe.park," +
"dalvik.system.VMStack.getThreadStackTrace," + "dalvik.system.VMStack.getThreadStackTrace," +
...@@ -41,6 +48,7 @@ public class Profiler implements Runnable { ...@@ -41,6 +48,7 @@ public class Profiler implements Runnable {
, ',', true); , ',', true);
private volatile boolean stop; private volatile boolean stop;
private HashMap<String, Integer> counts = new HashMap<String, Integer>(); private HashMap<String, Integer> counts = new HashMap<String, Integer>();
private HashMap<String, Integer> packages = new HashMap<String, Integer>();
private int minCount = 1; private int minCount = 1;
private int total; private int total;
private Thread thread; private Thread thread;
...@@ -120,62 +128,74 @@ public class Profiler implements Runnable { ...@@ -120,62 +128,74 @@ public class Profiler implements Runnable {
continue; continue;
} }
StackTraceElement[] dump = entry.getValue(); StackTraceElement[] dump = entry.getValue();
if (dump.length == 0) { if (dump == null || dump.length == 0) {
continue; continue;
} }
boolean ignoreThis = false; if (startsWithAny(dump[0].toString(), ignoreThreads)) {
for (String ig : ignoreThreads) {
if (ig.length() > 0 && dump[0].toString().startsWith(ig)) {
ignoreThis = true;
break;
}
}
if (ignoreThis) {
continue; continue;
} }
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
// simple recursive calls are ignored // simple recursive calls are ignored
String last = null; String last = null;
boolean packageCounts = false;
for (int j = 0, i = 0; i < dump.length && j < depth; i++) { for (int j = 0, i = 0; i < dump.length && j < depth; i++) {
String el = dump[i].toString(); String el = dump[i].toString();
ignoreThis = false; if (!el.equals(last) && !startsWithAny(el, ignoreLines)) {
for (String ig : ignoreLines) { last = el;
if (ig.length() > 0 && el.startsWith(ig)) { buff.append("at ").append(el).append(SysProperties.LINE_SEPARATOR);
ignoreThis = true; if (!packageCounts && !startsWithAny(el, ignorePackages)) {
packageCounts = true;
int index = 0;
for (; index < el.length(); index++) {
char c = el.charAt(index);
if (Character.isUpperCase(c) || c == '(') {
break; break;
} }
} }
if (!ignoreThis && !el.equals(last)) { if (index > 0 && el.charAt(index - 1) == '.') {
last = el; index--;
buff.append("at ").append(el).append(SysProperties.LINE_SEPARATOR); }
String packageName = el.substring(0, index);
increment(packages, packageName, 0);
}
j++; j++;
} }
} }
if (buff.length() > 0) { if (buff.length() > 0) {
increment(buff.toString()); minCount = increment(counts, buff.toString().trim(), minCount);
total++;
} }
} }
} }
private void increment(String trace) { private boolean startsWithAny(String s, String[] prefixes) {
total++; for (String p : prefixes) {
Integer oldCount = counts.get(trace); if (p.length() > 0 && s.startsWith(p)) {
return true;
}
}
return false;
}
private static int increment(HashMap<String, Integer> map, String trace, int minCount) {
Integer oldCount = map.get(trace);
if (oldCount == null) { if (oldCount == null) {
counts.put(trace, 1); map.put(trace, 1);
} else { } else {
counts.put(trace, oldCount + 1); map.put(trace, oldCount + 1);
} }
if (counts.size() > MAX_ELEMENTS) { while (map.size() > MAX_ELEMENTS) {
for (Iterator<Map.Entry<String, Integer>> ei = counts.entrySet().iterator(); ei.hasNext();) { for (Iterator<Map.Entry<String, Integer>> ei = map.entrySet().iterator(); ei.hasNext();) {
Map.Entry<String, Integer> e = ei.next(); Map.Entry<String, Integer> e = ei.next();
if (e.getValue() <= minCount) { if (e.getValue() <= minCount) {
ei.remove(); ei.remove();
} }
} }
if (counts.size() > MAX_ELEMENTS) { if (map.size() > MAX_ELEMENTS) {
minCount++; minCount++;
} }
} }
return minCount;
} }
/** /**
...@@ -190,12 +210,20 @@ public class Profiler implements Runnable { ...@@ -190,12 +210,20 @@ public class Profiler implements Runnable {
buff.append("Profiler: top ").append(count).append(" stack trace(s) of ").append(time). buff.append("Profiler: top ").append(count).append(" stack trace(s) of ").append(time).
append(" ms [build-").append(Constants.BUILD_ID).append("]:").append(SysProperties.LINE_SEPARATOR); append(" ms [build-").append(Constants.BUILD_ID).append("]:").append(SysProperties.LINE_SEPARATOR);
if (counts.size() == 0) { if (counts.size() == 0) {
buff.append("(none)"); buff.append("(none)").append(SysProperties.LINE_SEPARATOR);
}
appendTop(buff, counts, count, total, false);
buff.append("packages:").append(SysProperties.LINE_SEPARATOR);
appendTop(buff, packages, count, total, true);
buff.append('.');
return buff.toString();
} }
private static void appendTop(StringBuilder buff, HashMap<String, Integer> map, int count, int total, boolean table) {
for (int x = 0, min = 0;;) { for (int x = 0, min = 0;;) {
int highest = 0; int highest = 0;
Map.Entry<String, Integer> best = null; Map.Entry<String, Integer> best = null;
for (Map.Entry<String, Integer> el : counts.entrySet()) { for (Map.Entry<String, Integer> el : map.entrySet()) {
if (el.getValue() > highest) { if (el.getValue() > highest) {
best = el; best = el;
highest = el.getValue(); highest = el.getValue();
...@@ -204,18 +232,29 @@ public class Profiler implements Runnable { ...@@ -204,18 +232,29 @@ public class Profiler implements Runnable {
if (best == null) { if (best == null) {
break; break;
} }
counts.remove(best.getKey()); map.remove(best.getKey());
if (++x >= count) { if (++x >= count) {
if (best.getValue() < min) { if (best.getValue() < min) {
break; break;
} }
min = best.getValue(); min = best.getValue();
} }
buff.append(best.getValue()).append('/').append(total). int c = best.getValue();
append(SysProperties.LINE_SEPARATOR).append(best.getKey()); int percent = 100 * c / Math.max(total, 1);
if (table) {
if (percent > 1) {
buff.append(percent).
append("%: ").append(best.getKey()).
append(SysProperties.LINE_SEPARATOR);
}
} else {
buff.append(c).append('/').append(total).append(" (").
append(percent).
append("%):").append(SysProperties.LINE_SEPARATOR).
append(best.getKey()).
append(SysProperties.LINE_SEPARATOR);
}
} }
buff.append('.');
return buff.toString();
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论