/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.command.system; import java.io.PrintWriter; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Comparator; import java.util.TreeSet; import org.jnode.shell.AbstractCommand; import org.jnode.shell.syntax.Argument; import org.jnode.shell.syntax.FlagArgument; import org.jnode.shell.syntax.ThreadNameArgument; import org.jnode.vm.scheduler.VmThread; /** * Shell command to print information about all threads or a specific thread. * * @author Ewout Prangsma (epr@users.sourceforge.net) * @author Martin Husted Hartvig (hagar@jnode.org) * @author crawley@jnode.org * @author Levente S\u00e1ntha */ public class ThreadCommand extends AbstractCommand { private static final String help_name = "the name of a specific thread to be printed"; private static final String help_group = "output a ThreadGroup dump"; private static final String help_verbose = "show all threads in thread groups"; private static final String help_super = "View info about all threads, or a specific thread"; private static final String SEPARATOR = ", "; private static final String SLASH_T = "\t"; private static final String GROUP = "Group "; private static final String TRACE = "Stack trace"; private final ThreadNameArgument argName; private final FlagArgument argDump; private final FlagArgument argVerbose; public ThreadCommand() { super(help_super); argName = new ThreadNameArgument("threadName", Argument.OPTIONAL, help_name); argDump = new FlagArgument("groupDump", Argument.OPTIONAL, help_group); argVerbose = new FlagArgument("verbose", Argument.OPTIONAL, help_verbose); registerArguments(argName, argVerbose, argDump); } public static void main(String[] args) throws Exception { new ThreadCommand().execute(args); } /** * Execute this command */ public void execute() throws Exception { // If threadName is null, we'll print all threads String threadName = (argName.isSet()) ? argName.getValue() : null; boolean dump = argDump.isSet(); // Find the root of the ThreadGroup tree ThreadGroup grp = Thread.currentThread().getThreadGroup(); while (grp.getParent() != null) { grp = grp.getParent(); } if (dump) { // Produce an ugly (but useful) ThreadGroup dump. Unfortunately, // it goes to System.out, and we cannot fix it w/o changing a Java // standard API. grp.list(); } else { if (!argVerbose.isSet() && !argName.isSet()) { showDefaultInfo(grp); } else { // Show the threads in the ThreadGroup tree. showThreads(grp, getOutput().getPrintWriter(), threadName); } } } private void showDefaultInfo(ThreadGroup grp) { TreeSet<Thread> threadSet = new TreeSet<Thread>(new Comparator<Thread>() { @Override public int compare(Thread t1, Thread t2) { return Long.valueOf(t1.getId()).compareTo(t2.getId()); } }); findThreads(grp, threadSet); PrintWriter out = getOutput().getPrintWriter(); for (final Thread thread : threadSet) { VmThread vmThread = AccessController.doPrivileged(new PrivilegedAction<VmThread>() { public VmThread run() { return ThreadHelper.getVmThread(thread); } }); out.println(" " + thread.getId() + SEPARATOR + thread.getName() + SEPARATOR + thread.getPriority() + SEPARATOR + vmThread.getThreadStateName()); } } private void findThreads(ThreadGroup grp, TreeSet<Thread> threadSet) { final int max = grp.activeCount() * 2; final Thread[] ts = new Thread[max]; grp.enumerate(ts); for (int i = 0; i < max; i++) { final Thread t = ts[i]; if (t != null) { threadSet.add(t); } } final int gmax = grp.activeGroupCount() * 2; final ThreadGroup[] tgs = new ThreadGroup[gmax]; grp.enumerate(tgs); for (int i = 0; i < gmax; i++) { final ThreadGroup tg = tgs[i]; if (tg != null) { findThreads(tg, threadSet); } } } /** * Traverse the ThreadGroups threads and its child ThreadGroups printing * information for each thread found. If 'threadName' is non-null, only * print information for the thread that matches the name. * * @param grp the ThreadGroup to traverse * @param out the destination for output * @param threadName if non-null, only display this thread. */ private void showThreads(ThreadGroup grp, PrintWriter out, String threadName) { if (threadName == null) { out.println(GROUP + grp.getName()); } final int max = grp.activeCount() * 2; final Thread[] ts = new Thread[max]; grp.enumerate(ts); for (int i = 0; i < max; i++) { final Thread t = ts[i]; if (t != null) { if ((threadName == null) || threadName.equals(t.getName())) { VmThread vmThread = AccessController .doPrivileged(new PrivilegedAction<VmThread>() { public VmThread run() { return ThreadHelper.getVmThread(t); } }); out.println(SLASH_T + t.getId() + SEPARATOR + t.getName() + SEPARATOR + t.getPriority() + SEPARATOR + vmThread.getThreadStateName()); if (threadName != null) { final Object[] trace = VmThread.getStackTrace(vmThread); final int traceLen = trace.length; out.println(SLASH_T + SLASH_T + TRACE); for (int k = 0; k < traceLen; k++) { out.println(SLASH_T + SLASH_T + trace[k]); } return; } } } } final int gmax = grp.activeGroupCount() * 2; final ThreadGroup[] tgs = new ThreadGroup[gmax]; grp.enumerate(tgs); for (int i = 0; i < gmax; i++) { final ThreadGroup tg = tgs[i]; if (tg != null) { showThreads(tg, out, threadName); } } } }