提交 126fd829 authored 作者: Thomas Mueller's avatar Thomas Mueller

The profiler tool can now process files with full thread dumps.

上级 dd38db03
...@@ -18,11 +18,16 @@ Change Log ...@@ -18,11 +18,16 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>MVStore mode: the CLOB and BLOB storage was re-implemented and is <ul><li>The profiler tool can now process files with full thread dumps.
</li><li>MVStore mode: the CLOB and BLOB storage was re-implemented and is
now much faster than with the PageStore (which is still the default storage). now much faster than with the PageStore (which is still the default storage).
</li><li>Various bugs in the MVStore storage and have been fixed. </li><li>MVStore mode: creating indexes is now much faster
(in many cases faster than with the default PageStore).
</li><li>Various bugs in the MVStore storage and have been fixed,
including a bug in the R-tree implementation.
</li><li>The method org.h2.expression.Function.getCost could throw a NullPointException. </li><li>The method org.h2.expression.Function.getCost could throw a NullPointException.
</li><li>Storing LOBs in separate files (outside of the database) is no longer supported for new databases. </li><li>Storing LOBs in separate files (outside of the main database file)
is no longer supported for new databases.
</li><li>Lucene 2 is no longer supported. </li><li>Lucene 2 is no longer supported.
</li><li>Fix bug in calculating default MIN and MAX values for SEQUENCE. </li><li>Fix bug in calculating default MIN and MAX values for SEQUENCE.
</li></ul> </li></ul>
......
...@@ -7,13 +7,16 @@ ...@@ -7,13 +7,16 @@
package org.h2.util; package org.h2.util;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader; import java.io.LineNumberReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList;
import java.lang.instrument.Instrumentation; import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
...@@ -22,7 +25,7 @@ import java.util.Map; ...@@ -22,7 +25,7 @@ import java.util.Map;
/** /**
* A simple CPU profiling tool similar to java -Xrunhprof. It can be used * A simple CPU profiling tool similar to java -Xrunhprof. It can be used
* in-process (to profile the current application) or as a standalone program * in-process (to profile the current application) or as a standalone program
* (to profile a different process). * (to profile a different process, or files containing full thread dumps).
*/ */
public class Profiler implements Runnable { public class Profiler implements Runnable {
...@@ -37,11 +40,19 @@ public class Profiler implements Runnable { ...@@ -37,11 +40,19 @@ public class Profiler implements Runnable {
private int pid; private int pid;
private final String[] ignoreLines = {}; private final String[] ignoreLines = (
"java," +
"sun," +
"com.sun.," +
"com.google.common.," +
"com.mongodb."
).split(",");
private final String[] ignorePackages = ( private final String[] ignorePackages = (
"java," + "java," +
"sun," + "sun," +
"com.sun." "com.sun.," +
"com.google.common.," +
"com.mongodb."
).split(","); ).split(",");
private final String[] ignoreThreads = ( private final String[] ignoreThreads = (
"java.lang.Object.wait," + "java.lang.Object.wait," +
...@@ -49,6 +60,7 @@ public class Profiler implements Runnable { ...@@ -49,6 +60,7 @@ public class Profiler implements Runnable {
"java.lang.Thread.getThreads," + "java.lang.Thread.getThreads," +
"java.lang.Thread.sleep," + "java.lang.Thread.sleep," +
"java.lang.UNIXProcess.waitForProcessExit," + "java.lang.UNIXProcess.waitForProcessExit," +
"java.net.PlainDatagramSocketImpl.receive0," +
"java.net.PlainSocketImpl.accept," + "java.net.PlainSocketImpl.accept," +
"java.net.PlainSocketImpl.socketAccept," + "java.net.PlainSocketImpl.socketAccept," +
"java.net.SocketInputStream.socketRead," + "java.net.SocketInputStream.socketRead," +
...@@ -72,6 +84,7 @@ public class Profiler implements Runnable { ...@@ -72,6 +84,7 @@ public class Profiler implements Runnable {
private Thread thread; private Thread thread;
private long start; private long start;
private long time; private long time;
private int threadDumps;
/** /**
* This method is called when the agent is installed. * This method is called when the agent is installed.
...@@ -106,24 +119,50 @@ public class Profiler implements Runnable { ...@@ -106,24 +119,50 @@ public class Profiler implements Runnable {
private void run(String... args) { private void run(String... args) {
if (args.length == 0) { if (args.length == 0) {
System.out.println("Show profiling data"); System.out.println("Show profiling data");
System.out.println("Usage: java " + getClass().getName() + " <pid>"); System.out.println("Usage: java " + getClass().getName() + " <pid> | <stackTraceFileNames>");
System.out.println("Processes:"); System.out.println("Processes:");
String processes = exec("jps", "-l"); String processes = exec("jps", "-l");
System.out.println(processes); System.out.println(processes);
return; return;
} }
pid = Integer.parseInt(args[0]);
start = System.currentTimeMillis(); start = System.currentTimeMillis();
long last = 0; if (args[0].matches("\\d+")) {
while (true) { pid = Integer.parseInt(args[0]);
tick(); long last = 0;
long t = System.currentTimeMillis(); while (true) {
if (t - last > 5000) { tick();
time = System.currentTimeMillis() - start; long t = System.currentTimeMillis();
System.out.println(getTopTraces(3)); if (t - last > 5000) {
last = t; time = System.currentTimeMillis() - start;
System.out.println(getTopTraces(3));
last = t;
}
} }
} }
try {
for (String file : args) {
Reader reader;
LineNumberReader r;
reader = new InputStreamReader(new FileInputStream(file), "CP1252");
r = new LineNumberReader(reader);
while (true) {
String line = r.readLine();
if (line == null) {
break;
} else if (line.startsWith("Full thread dump")) {
threadDumps++;
}
}
reader.close();
reader = new InputStreamReader(new FileInputStream(file), "CP1252");
r = new LineNumberReader(reader);
processList(readStackTrace(r));
reader.close();
}
System.out.println(getTopTraces(3));
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
private static List<Object[]> getRunnableStackTraces() { private static List<Object[]> getRunnableStackTraces() {
...@@ -144,52 +183,56 @@ public class Profiler implements Runnable { ...@@ -144,52 +183,56 @@ public class Profiler implements Runnable {
} }
private static List<Object[]> readRunnableStackTraces(int pid) { private static List<Object[]> readRunnableStackTraces(int pid) {
ArrayList<Object[]> list = new ArrayList<Object[]>();
try { try {
String jstack = exec("jstack", "" + pid); String jstack = exec("jstack", "" + pid);
LineNumberReader r = new LineNumberReader(new StringReader(jstack)); LineNumberReader r = new LineNumberReader(new StringReader(jstack));
return readStackTrace(r);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static List<Object[]> readStackTrace(LineNumberReader r) throws IOException {
ArrayList<Object[]> list = new ArrayList<Object[]>();
while (true) {
String line = r.readLine();
if (line == null) {
break;
}
if (!line.startsWith("\"")) {
// not a thread
continue;
}
line = r.readLine();
if (line == null) {
break;
}
line = line.trim();
if (!line.startsWith("java.lang.Thread.State: RUNNABLE")) {
continue;
}
ArrayList<String> stack = new ArrayList<String>();
while (true) { while (true) {
String line = r.readLine();
if (line == null) {
break;
}
if (!line.startsWith("\"")) {
// not a thread
continue;
}
line = r.readLine(); line = r.readLine();
if (line == null) { if (line == null) {
break; break;
} }
line = line.trim(); line = line.trim();
if (!line.startsWith("java.lang.Thread.State: RUNNABLE")) { if (line.startsWith("- ")) {
continue; continue;
} }
ArrayList<String> stack = new ArrayList<String>(); if (!line.startsWith("at ")) {
while (true) { break;
line = r.readLine();
if (line == null) {
break;
}
line = line.trim();
if (line.startsWith("- ")) {
continue;
}
if (!line.startsWith("at ")) {
break;
}
line = line.substring(3).trim();
stack.add(line);
}
if (stack.size() > 0) {
String[] s = stack.toArray(new String[stack.size()]);
list.add(s);
} }
line = line.substring(3).trim();
stack.add(line);
}
if (stack.size() > 0) {
String[] s = stack.toArray(new String[stack.size()]);
list.add(s);
} }
return list;
} catch (IOException e) {
throw new RuntimeException(e);
} }
return list;
} }
private static String exec(String... args) { private static String exec(String... args) {
...@@ -292,6 +335,11 @@ public class Profiler implements Runnable { ...@@ -292,6 +335,11 @@ public class Profiler implements Runnable {
} else { } else {
list = getRunnableStackTraces(); list = getRunnableStackTraces();
} }
threadDumps++;
processList(list);
}
private void processList(List<Object[]> list) {
for (Object[] dump : list) { for (Object[] dump : list) {
if (startsWithAny(dump[0].toString(), ignoreThreads)) { if (startsWithAny(dump[0].toString(), ignoreThreads)) {
continue; continue;
...@@ -377,8 +425,14 @@ public class Profiler implements Runnable { ...@@ -377,8 +425,14 @@ public class Profiler implements Runnable {
private String getTopTraces(int count) { private String getTopTraces(int count) {
StringBuilder buff = new StringBuilder(); StringBuilder buff = new StringBuilder();
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(" ms:").append(LINE_SEPARATOR); if (time > 0) {
buff.append(" of ").append(time).append(" ms");
}
if (threadDumps > 0) {
buff.append(" of ").append(threadDumps).append(" thread dumps");
}
buff.append(":").append(LINE_SEPARATOR);
if (counts.size() == 0) { if (counts.size() == 0) {
buff.append("(none)").append(LINE_SEPARATOR); buff.append("(none)").append(LINE_SEPARATOR);
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论