From f9e5c2108338f768398a299e751468237432b4a6 Mon Sep 17 00:00:00 2001 From: orbiter Date: Mon, 14 Mar 2011 20:46:04 +0000 Subject: [PATCH] update to thread dump logs git-svn-id: https://svn.berlios.de/svnroot/repos/yacy/trunk@7590 6c8d7289-2bf4-0310-a012-ef5d649a1542 --- htroot/Threaddump_p.java | 38 +-- .../net/yacy/kelondro/logging/ThreadDump.java | 238 +++++++++++------- source/net/yacy/kelondro/util/OS.java | 11 + 3 files changed, 175 insertions(+), 112 deletions(-) diff --git a/htroot/Threaddump_p.java b/htroot/Threaddump_p.java index 743186eb2..47e5e282b 100644 --- a/htroot/Threaddump_p.java +++ b/htroot/Threaddump_p.java @@ -27,13 +27,14 @@ import java.io.File; +import java.io.IOException; import java.util.Date; import java.util.Map; import java.util.ArrayList; import net.yacy.cora.protocol.RequestHeader; import net.yacy.kelondro.logging.ThreadDump; -import net.yacy.kelondro.util.MemoryControl; +import net.yacy.kelondro.util.OS; import de.anomic.search.Switchboard; import de.anomic.server.serverObjects; @@ -73,27 +74,28 @@ public class Threaddump_p { multipleCount = post.getInt("count", multipleCount); final ArrayList> traces = new ArrayList>(); for (int i = 0; i < multipleCount; i++) { - traces.add(Thread.getAllStackTraces()); - if (MemoryControl.available() < 20 * 1024 * 1024) break; + try { + traces.add(ThreadDump.getAllStackTraces()); + } catch (OutOfMemoryError e) { + break; + } } ThreadDump.appendStackTraceStats(appPath, buffer, traces, plain, null); - /* - ThreadDumpGenerator.appendStackTraceStats(appPath, buffer, traces, plain, Thread.State.BLOCKED); - ThreadDumpGenerator.appendStackTraceStats(appPath, buffer, traces, plain, Thread.State.RUNNABLE); - ThreadDumpGenerator.appendStackTraceStats(appPath, buffer, traces, plain, Thread.State.TIMED_WAITING); - ThreadDumpGenerator.appendStackTraceStats(appPath, buffer, traces, plain, Thread.State.WAITING); - ThreadDumpGenerator.appendStackTraceStats(appPath, buffer, traces, plain, Thread.State.NEW); - ThreadDumpGenerator.appendStackTraceStats(appPath, buffer, traces, plain, Thread.State.TERMINATED); - */ } else { + // write a thread dump to standard error output + if (OS.canExecUnix) { + int pid = OS.getPID(); + if (pid >= 0) try {OS.execSynchronous("kill -3 " + pid);} catch (IOException e) {} + } + // generate a single thread dump - final Map stackTraces = Thread.getAllStackTraces(); - ThreadDump.appendStackTraces(appPath, buffer, stackTraces, plain, Thread.State.BLOCKED); - ThreadDump.appendStackTraces(appPath, buffer, stackTraces, plain, Thread.State.RUNNABLE); - ThreadDump.appendStackTraces(appPath, buffer, stackTraces, plain, Thread.State.TIMED_WAITING); - ThreadDump.appendStackTraces(appPath, buffer, stackTraces, plain, Thread.State.WAITING); - ThreadDump.appendStackTraces(appPath, buffer, stackTraces, plain, Thread.State.NEW); - ThreadDump.appendStackTraces(appPath, buffer, stackTraces, plain, Thread.State.TERMINATED); + final Map stackTraces = ThreadDump.getAllStackTraces(); + new ThreadDump(appPath, stackTraces, plain, Thread.State.BLOCKED).appendStackTraces(buffer, plain, Thread.State.BLOCKED); + new ThreadDump(appPath, stackTraces, plain, Thread.State.RUNNABLE).appendStackTraces(buffer, plain, Thread.State.RUNNABLE); + new ThreadDump(appPath, stackTraces, plain, Thread.State.TIMED_WAITING).appendStackTraces(buffer, plain, Thread.State.TIMED_WAITING); + new ThreadDump(appPath, stackTraces, plain, Thread.State.WAITING).appendStackTraces(buffer, plain, Thread.State.WAITING); + new ThreadDump(appPath, stackTraces, plain, Thread.State.NEW).appendStackTraces(buffer, plain, Thread.State.NEW); + new ThreadDump(appPath, stackTraces, plain, Thread.State.TERMINATED).appendStackTraces(buffer, plain, Thread.State.TERMINATED); } ThreadDump.bufferappend(buffer, plain, "************* End Thread Dump " + dt + " *******************"); diff --git a/source/net/yacy/kelondro/logging/ThreadDump.java b/source/net/yacy/kelondro/logging/ThreadDump.java index 5bd912590..b47b2f997 100644 --- a/source/net/yacy/kelondro/logging/ThreadDump.java +++ b/source/net/yacy/kelondro/logging/ThreadDump.java @@ -35,18 +35,24 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.SortedSet; -import java.util.TreeSet; + +import com.sun.jdi.Bootstrap; +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.VirtualMachineManager; +import com.sun.jdi.connect.AttachingConnector; +import com.sun.jdi.connect.Connector; +import com.sun.jdi.connect.IllegalConnectorArgumentsException; import net.yacy.document.parser.html.CharacterCoding; import net.yacy.kelondro.util.FileUtils; +import net.yacy.kelondro.util.OS; import de.anomic.tools.nxTools; public class ThreadDump extends HashMap> implements Map> { private static final long serialVersionUID = -5587850671040354397L; - public class Thread { + public static class Thread { public String name; public Thread(final String name) { this.name = name; @@ -68,7 +74,7 @@ public class ThreadDump extends HashMap> impleme } } - public class Lock { + public static class Lock { public String id; public Lock(final String name) { this.id = name; @@ -94,12 +100,54 @@ public class ThreadDump extends HashMap> impleme return java.lang.Thread.getAllStackTraces(); } + public ThreadDump() { + super(); + + VirtualMachineManager manager = Bootstrap.virtualMachineManager(); + AttachingConnector ac = manager.attachingConnectors().get(0); + Map env = ac.defaultArguments(); + env.get("port").setValue("8066"); + env.get("hostname").setValue("localhost"); + VirtualMachine vm = null; + try { + vm = ac.attach(env); + } catch (IOException e1) { + e1.printStackTrace(); + } catch (IllegalConnectorArgumentsException e1) { + e1.printStackTrace(); + } + + Process proc = vm.process(); + InputStream is = proc.getInputStream(); + + // get the current process PID + int pid = OS.getPID(); + + // call kill -3 on the pid + if (pid >= 0) try {OS.execSynchronous("kill -3 " + pid);} catch (IOException e) {} + + //byte[] b = os.toByteArray(); + //System.err.println("dump size: " + b.length + ", log out lines = " + o.size()); + + // import the thread dump; + try { + importText(is); + } catch (IOException e) { + e.printStackTrace(); + } + + } + public ThreadDump(final File f) throws IOException { this(new FileInputStream(f)); } public ThreadDump(final InputStream is) throws IOException { super(); + importText(is); + } + + private void importText(final InputStream is) throws IOException { final BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line; String thread = null; @@ -123,35 +171,94 @@ public class ThreadDump extends HashMap> impleme list.add(line); } } + // recognize last thread + if (thread != null) { + this.put(new ThreadDump.Thread(thread), list); + } } - public static void appendStackTraces(final File rootPath, - final StringBuilder buffer, + public ThreadDump( + final File appPath, final Map stackTraces, final boolean plain, - final java.lang.Thread.State stateIn) - { + final java.lang.Thread.State stateIn) { + super(); + final File classPath = new File(appPath, "source"); + + java.lang.Thread thread; + // collect single dumps + for (final Entry entry: stackTraces.entrySet()) { + thread = entry.getKey(); + final StackTraceElement[] stackTraceElements = entry.getValue(); + StackTraceElement ste; + String line; + String tracename = ""; + File classFile; + if ((stateIn == null || stateIn.equals(thread.getState())) && stackTraceElements.length > 0) { + StringBuilder sb = new StringBuilder(3000); + if (plain) { + classFile = getClassFile(classPath, stackTraceElements[stackTraceElements.length - 1].getClassName()); + tracename = classFile.getName(); + if (tracename.endsWith(".java")) tracename = tracename.substring(0, tracename.length() - 5); + if (tracename.length() > 20) tracename = tracename.substring(0, 20); + while (tracename.length() < 20) tracename = tracename + "_"; + tracename = "[" + tracename + "] "; + } + String threadtitle = tracename + "Thread= " + thread.getName() + " " + (thread.isDaemon()?"daemon":"") + " id=" + thread.getId() + " " + thread.getState().toString(); + String className; + boolean cutcore = true; + for (int i = 0; i < stackTraceElements.length; i++) { + ste = stackTraceElements[i]; + className = ste.getClassName(); + if (cutcore && (className.startsWith("java.") || className.startsWith("sun."))) { + sb.setLength(0); + bufferappend(sb, plain, tracename + "at " + CharacterCoding.unicode2html(ste.toString(), true)); + } else { + cutcore = false; + if (i == 0) { + line = getLine(getClassFile(classPath, className), ste.getLineNumber()); + } else { + line = null; + } + if ((line != null) && (line.length() > 0)) { + bufferappend(sb, plain, tracename + "at " + CharacterCoding.unicode2html(ste.toString(), true) + " [" + line.trim() + "]"); + } else { + bufferappend(sb, plain, tracename + "at " + CharacterCoding.unicode2html(ste.toString(), true)); + } + } + } + final String threaddump = sb.toString(); + List threads = this.get(threaddump); + if (threads == null) threads = new ArrayList(); + threads.add(threadtitle); + this.put(new Thread(threaddump), threads); + } + } + } + + public void appendStackTraces( + final StringBuilder buffer, + final boolean plain, + final java.lang.Thread.State stateIn) { bufferappend(buffer, plain, "THREADS WITH STATES: " + stateIn.toString()); bufferappend(buffer, plain, ""); - // collect single dumps - final Map> dumps = dumpCollection(rootPath, stackTraces, plain, stateIn); // write dumps - for (final Entry> entry: dumps.entrySet()) { - SortedSet threads = entry.getValue(); + for (final Entry> entry: this.entrySet()) { + List threads = entry.getValue(); for (final String t: threads) bufferappend(buffer, plain, t); - bufferappend(buffer, plain, entry.getKey()); + bufferappend(buffer, plain, entry.getKey().name); bufferappend(buffer, plain, ""); } bufferappend(buffer, plain, ""); } - public static void appendStackTraceStats(final File rootPath, + public static void appendStackTraceStats( + final File rootPath, final StringBuilder buffer, final List> traces, final boolean plain, - final java.lang.Thread.State stateIn) - { + final java.lang.Thread.State stateIn) { if (stateIn != null) { bufferappend(buffer, plain, "THREADS WITH STATES: " + stateIn.toString()); bufferappend(buffer, plain, ""); @@ -164,7 +271,6 @@ public class ThreadDump extends HashMap> impleme final Entry e = removeMax(dumps); bufferappend(buffer, plain, "Occurrences: " + e.getValue()); bufferappend(buffer, plain, e.getKey()); - //bufferappend(buffer, plain, ""); } bufferappend(buffer, plain, ""); } @@ -180,89 +286,29 @@ public class ThreadDump extends HashMap> impleme return max; } - private static Map dumpStatistic(final File rootPath, + private static Map dumpStatistic( + final File rootPath, final List> stackTraces, final boolean plain, - final java.lang.Thread.State stateIn) - { + final java.lang.Thread.State stateIn) { final Map result = new HashMap(); - Map> x; + ThreadDump x; int count; for (final Map trace: stackTraces) { - x = dumpCollection(rootPath, trace, plain, stateIn); - for (final Entry> e: x.entrySet()) { + x = new ThreadDump(rootPath, trace, plain, stateIn); + for (final Entry> e: x.entrySet()) { Integer c = result.get(e.getKey()); count = e.getValue().size(); - if (c == null) result.put(e.getKey(), Integer.valueOf(count)); + if (c == null) result.put(e.getKey().name, Integer.valueOf(count)); else { c = Integer.valueOf(c.intValue() + count); - result.put(e.getKey(), c); + result.put(e.getKey().name, c); } } } return result; } - private static Map> dumpCollection(final File appPath, - final Map stackTraces, - final boolean plain, - final java.lang.Thread.State stateIn) - { - final File classPath = new File(appPath, "source"); - - java.lang.Thread thread; - // collect single dumps - final Map> dumps = new HashMap>(); - for (final Entry entry: stackTraces.entrySet()) { - thread = entry.getKey(); - final StackTraceElement[] stackTraceElements = entry.getValue(); - StackTraceElement ste; - String line; - String tracename = ""; - File classFile; - if ((stateIn == null || stateIn.equals(thread.getState())) && stackTraceElements.length > 0) { - StringBuilder sb = new StringBuilder(3000); - if (plain) { - classFile = getClassFile(classPath, stackTraceElements[stackTraceElements.length - 1].getClassName()); - tracename = classFile.getName(); - if (tracename.endsWith(".java")) tracename = tracename.substring(0, tracename.length() - 5); - if (tracename.length() > 20) tracename = tracename.substring(0, 20); - while (tracename.length() < 20) tracename = tracename + "_"; - tracename = "[" + tracename + "] "; - } - String threadtitle = tracename + "Thread= " + thread.getName() + " " + (thread.isDaemon()?"daemon":"") + " id=" + thread.getId() + " " + thread.getState().toString(); - String className; - boolean cutcore = true; - for (int i = 0; i < stackTraceElements.length; i++) { - ste = stackTraceElements[i]; - className = ste.getClassName(); - if (cutcore && (className.startsWith("java.") || className.startsWith("sun."))) { - sb.setLength(0); - bufferappend(sb, plain, tracename + "at " + CharacterCoding.unicode2html(ste.toString(), true)); - } else { - cutcore = false; - if (i == 0) { - line = getLine(getClassFile(classPath, className), ste.getLineNumber()); - } else { - line = null; - } - if ((line != null) && (line.length() > 0)) { - bufferappend(sb, plain, tracename + "at " + CharacterCoding.unicode2html(ste.toString(), true) + " [" + line.trim() + "]"); - } else { - bufferappend(sb, plain, tracename + "at " + CharacterCoding.unicode2html(ste.toString(), true)); - } - } - } - final String threaddump = sb.toString(); - SortedSet threads = dumps.get(threaddump); - if (threads == null) threads = new TreeSet(); - threads.add(threadtitle); - dumps.put(threaddump, threads); - } - } - return dumps; - } - private static File getClassFile(final File sourcePath, final String classname) { final String classPath = classname.replace('.', '/') + ".java"; final File file = new File(sourcePath, classPath); @@ -346,22 +392,26 @@ public class ThreadDump extends HashMap> impleme } public static void main(String[] args) { + ThreadDump dump = null; + if (args.length == 0) { + dump = new ThreadDump(); + } if (args.length == 2 && args[0].equals("-f")) { File dumpfile = new File(args[1]); - ThreadDump dump = null; try { dump = new ThreadDump(dumpfile); } catch (IOException e) { e.printStackTrace(); } - //dump.print(); - Map locks = dump.countLocks(); - for (int i = 0; i < dump.size() + 10; i++) { - for (Map.Entry entry: locks.entrySet()) { - if (entry.getValue().intValue() == i) { - System.out.println("holds lock for " + i + " threads:"); - dump.print(entry.getKey()); - } + } + //dump.print(); + Map locks = dump.countLocks(); + System.out.println("*** Thread Dump Lock report; dump size = " + dump.size() + ", locks = " + locks.size()); + for (int i = 0; i < dump.size() + 10; i++) { + for (Map.Entry entry: locks.entrySet()) { + if (entry.getValue().intValue() == i) { + System.out.println("holds lock for " + i + " threads:"); + dump.print(entry.getKey()); } } } diff --git a/source/net/yacy/kelondro/util/OS.java b/source/net/yacy/kelondro/util/OS.java index aecc7e146..bc822c10d 100644 --- a/source/net/yacy/kelondro/util/OS.java +++ b/source/net/yacy/kelondro/util/OS.java @@ -25,6 +25,7 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; +import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; @@ -161,6 +162,16 @@ public final class OS { } } + /** + * use a hack to get the current process PID + * @return the PID of the current java process or -1 if the PID cannot be obtained + */ + public static int getPID() { + String pids = ManagementFactory.getRuntimeMXBean().getName(); + int p = pids.indexOf('@'); + return p >= 0 ? Integer.parseInt(pids.substring(0, p)) : -1; + } + public static void execAsynchronous(final File scriptFile) throws IOException { // runs a script as separate thread String starterFileExtension = null;