/*******************************************************************************
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.kato.katoview.commands.infocommands;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Stack;
import java.util.Iterator;
import java.util.Enumeration;
import java.util.Properties;
import javax.tools.diagnostics.image.CorruptData;
import javax.tools.diagnostics.image.CorruptDataException;
import javax.tools.diagnostics.image.DataUnavailable;
import javax.tools.diagnostics.image.Image;
import javax.tools.diagnostics.image.ImageAddressSpace;
import javax.tools.diagnostics.image.ImageProcess;
import javax.tools.diagnostics.image.ImageRegister;
import javax.tools.diagnostics.image.ImageSection;
import javax.tools.diagnostics.image.ImageStackFrame;
import javax.tools.diagnostics.image.ImageThread;
import javax.tools.diagnostics.image.DiagnosticException;
import javax.tools.diagnostics.runtime.ManagedRuntime;
import javax.tools.diagnostics.runtime.java.JavaLocation;
import javax.tools.diagnostics.runtime.java.JavaMonitor;
import javax.tools.diagnostics.runtime.java.JavaObject;
import javax.tools.diagnostics.runtime.java.JavaRuntime;
import javax.tools.diagnostics.runtime.java.JavaStackFrame;
import javax.tools.diagnostics.runtime.java.JavaThread;
import org.apache.kato.katoview.Output;
import org.apache.kato.katoview.commands.Command;
import org.apache.kato.katoview.commands.helpers.Exceptions;
import org.apache.kato.katoview.commands.helpers.MonitorState;
import org.apache.kato.katoview.commands.helpers.StateToString;
import org.apache.kato.katoview.commands.helpers.ThreadData;
import org.apache.kato.katoview.commands.helpers.Utils;
public class InfoThreadCommand extends Command{
private int pointerSize;
public InfoThreadCommand(Output o){
super(o, "thread", "displays information about Java and native threads",
"parameters: none, \"*\", or thread name\n\n" +
"prints the following information about the current thread, " +
"all threads, or the specified thread, respectively:\n" +
" - thread id\n" +
" - registers\n" +
" - stack sections\n" +
" - thread frames: procedure name and base pointer\n" +
" - the thread properties\n" +
" - associated Java thread (if applicable):\n" +
" - name of Java thread\n" +
" - address of associated java.lang.Thread object\n" +
" - current state according to JVMTI specification\n" +
" - current state relative to java.lang.Thread.State\n" +
" - the Java thread priority\n" +
" - the monitor the thread is waiting to enter or waiting on notify\n" +
" - thread frames: base pointer, method, and filename:line\n"
);
child_commands = null;
}
public void doCommand(Stack args, Image loadedImage, HashMap properties){
if (args.isEmpty())
{
ImageAddressSpace ias = (ImageAddressSpace)properties.get("current_address_space");
String id;
try {
ImageThread it = ias.getCurrentProcess().getCurrentThread();
if (null != it)
id = it.getID();
else
{
out.print("\n\tNo current (failing) thread, try specifying a native thread ID or '*'\n");
ImageProcess ip = ias.getCurrentProcess();
if (ip!=null){
printThreadSummary(ip , out);
}
return;
}
} catch (CorruptDataException e) {
out.error("exception encountered while getting information about " +
"current thread");
return;
}
args.push(id);
}
String param = (String)args.pop();
if (!args.isEmpty())
{
out.error("\"info thread\" takes at most one parameter, which, if " +
"specified, must be a thread ID or \"*\"");
return;
}
if (param.equals("*"))
{
out.print("\n");
printAddressSpaceInfo(loadedImage, out, null, getJavaThreads(loadedImage, null));
out.print("\n");
}
else
{
out.print("\n");
printAddressSpaceInfo(loadedImage, out, param, getJavaThreads(loadedImage, param));
out.print("\n");
}
}
private void printAddressSpaceInfo(Image loadedImage, Output out, String id, Map threads)
{
Iterator itAddressSpace;
ImageAddressSpace ias;
int asnum = 0;
itAddressSpace = loadedImage.getAddressSpaces().iterator();
while (itAddressSpace.hasNext()) {
ias = (ImageAddressSpace)itAddressSpace.next();
out.print("\n\tnative threads for address space # " + asnum + "\n");
printProcessInfo(ias, out, id, threads);
asnum++;
}
if (!threads.isEmpty()) {
out.print("\n\tJava threads not associated with known native threads:\n\n");
// retrieve any orphaned Java threads from the hashmap
ArrayList ta = (ArrayList)threads.remove(null);
if ( ta != null ) {
for ( int i=0; i<ta.size(); i++) {
ThreadData td = (ThreadData)ta.get(i);
printJavaThreadInfo(out, td.getThread(), getMonitors(td.getRuntime()));
}
}
}
}
private void printProcessInfo(ImageAddressSpace ias, Output out, String id, Map threads)
{
Iterator itProcess;
ImageProcess ip;
boolean found = false;
itProcess = ias.getProcesses().iterator();
while (itProcess.hasNext())
{
ip = (ImageProcess)itProcess.next();
pointerSize = ip.getPointerSize();
out.print("\t process id: ");
try {
out.print(ip.getID());
} catch (DataUnavailable d) {
out.print(Exceptions.getDataUnavailableString());
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print("\n\n");
found = printThreadInfo(ip, out, id, threads);
}
if (!found)
{
out.print("\t no native threads found with specified id\n");
}
}
private boolean printThreadInfo(ImageProcess ip, Output out, String id, Map threads)
{
Iterator itThread;
ImageThread it;
boolean found = false;
itThread = ip.getThreads().iterator();
while (itThread.hasNext())
{
Object next = itThread.next();
if (next instanceof CorruptData) {
continue;
}
it = (ImageThread)next;
String currID;
try {
currID = it.getID();
} catch (CorruptDataException e) {
currID = null;
}
if (null == id || id.equals(currID))
{
out.print("\t thread id: ");
out.print(currID);
out.print("\n");
printRegisters(it);
out.print("\t stack sections:");
out.print("\n");
Iterator itStackSection = it.getStackSections().iterator();
while (itStackSection.hasNext()) {
Object nextStackSection = itStackSection.next();
if (nextStackSection instanceof CorruptData) {
out.print("\t " + Exceptions.getCorruptDataExceptionString() + "\n");
continue;
}
ImageSection is = (ImageSection)nextStackSection;
printStackSection(is);
}
printStackFrameInfo(it, out);
out.print("\t properties:");
out.print("\n");
printProperties(it, out);
out.print("\n");
out.print("\t associated Java thread: ");
ThreadData td = (ThreadData)threads.remove(currID);
if (null != td) {
out.print("\n");
printJavaThreadInfo(out, td.getThread(), getMonitors(td.getRuntime()));
} else {
out.print("<no associated Java thread>\n");
}
out.print("\n");
found = true;
}
}
return found;
}
public void printRegisters(ImageThread it)
{
out.print("\t registers:");
out.print("\n");
int count = 0;
Iterator itImageRegister = it.getRegisters().iterator();
while (itImageRegister.hasNext())
{
if (count % 4 == 0)
{
if (0 != count)
out.print("\n");
out.print("\t ");
}
ImageRegister ir = (ImageRegister)itImageRegister.next();
printRegisterInfo(ir);
count++;
}
out.print("\n");
}
public void printRegisterInfo(ImageRegister ir)
{
out.print(Utils.padWithSpaces(ir.getName(), 8) + " = ");
try {
out.print(toAdjustedHex(ir.getValue().longValue()));
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print(" ");
}
public void printStackSection(ImageSection is)
{
long startAddr = is.getBaseAddress().getAddress();
long size = is.getSize();
long endAddr = startAddr + size;
out.print("\t ");
out.print(Utils.toHex(startAddr));
out.print(" to ");
out.print(Utils.toHex(endAddr));
out.print(" (length ");
out.print(Utils.toHex(size));
out.print(")\n");
}
private void printStackFrameInfo(ImageThread it, Output out)
{
Iterator itStackFrame;
ImageStackFrame isf;
try {
itStackFrame = it.getStackFrames().iterator();
} catch (DataUnavailable d) {
out.print("\t error with stack frames: " + Exceptions.getDataUnavailableString() + "\n");
return;
}
out.print("\t stack frames:");
out.print("\n");
while (itStackFrame.hasNext())
{
isf = (ImageStackFrame)itStackFrame.next();
out.print("\t bp: ");
try {
out.print(toAdjustedHex(isf.getBasePointer().getAddress()));
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print(" pc: ");
try {
out.print(toAdjustedHex(isf.getProcedureAddress().getAddress()));
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print(" ");
try {
out.print(isf.getProcedureName());
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print("\n");
}
}
private Map getJavaThreads(Image loadedImage, String id)
{
ManagedRuntime mr;
Iterator itRuntime = Utils.getRuntimes(loadedImage);
Map threads = new HashMap();
while (itRuntime.hasNext()) {
mr = (ManagedRuntime)itRuntime.next();
if (mr instanceof JavaRuntime)
{
JavaRuntime jr = (JavaRuntime)mr;
Iterator itThread = jr.getThreads().iterator();
while (itThread.hasNext()) {
JavaThread jt = (JavaThread)itThread.next();
String currID;
try {
ImageThread it = jt.getImageThread();
currID = it.getID();
} catch (DiagnosticException e) {
currID = null;
}
if (null == id) {
// thread id set to null means we want all the threads, and we
// save all orphaned java threads in a list within the hashmap
if ( null == currID) {
if (threads.containsKey(null)) {
ArrayList ta = (ArrayList)threads.get(null);
ta.add(new ThreadData(jt, jr));
} else {
ArrayList ta = new ArrayList(1);
ta.add(new ThreadData(jt, jr));
threads.put(null, ta);
}
} else {
threads.put(currID, new ThreadData(jt, jr));
}
} else if (id.equals(currID)) {
// we just want the specific java thread that matches the native one
threads.put(currID, new ThreadData(jt, jr));
}
}
}
}
return threads;
}
private Map getMonitors(JavaRuntime jr)
{
Map monitors = new HashMap();
Iterator itMonitor = jr.getMonitors().iterator();
while (itMonitor.hasNext()) {
JavaMonitor jm = (JavaMonitor)itMonitor.next();
Iterator itEnterWaiter = jm.getEnterWaiters().iterator();
while (itEnterWaiter.hasNext()) {
JavaThread jt = (JavaThread)itEnterWaiter.next();
monitors.put(jt, new MonitorState(jm, MonitorState.WAITING_TO_ENTER));
}
Iterator itNotifyWaiter = jm.getNotifyWaiters().iterator();
while (itNotifyWaiter.hasNext()) {
JavaThread jt = (JavaThread)itNotifyWaiter.next();
monitors.put(jt, new MonitorState(jm, MonitorState.WAITING_TO_BE_NOTIFIED_ON));
}
}
return monitors;
}
private void printJavaThreadInfo(Output out, JavaThread jt, Map monitors)
{
out.print("\t name: ");
try {
out.print(jt.getName());
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print("\n");
out.print("\t Thread object: ");
try {
JavaObject threadObj = jt.getObject();
if (null == threadObj) {
out.print("<no associated Thread object>");
} else {
out.print(Utils.toHex(threadObj.getID().getAddress()));
}
} catch (CorruptDataException cde) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print("\n");
out.print("\t JVMTI state: ");
try {
out.print(StateToString.getJVMTIStateString(jt.getState()));
} catch (CorruptDataException cde) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print("\n");
out.print("\t Thread.State: ");
try {
out.print(StateToString.getThreadStateString(jt.getState()));
} catch (CorruptDataException cde) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print("\n");
out.print("\t Priority: ");
try {
Integer pri = new Integer(jt.getPriority());
out.print(pri.toString());
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print("\n");
out.print("\t monitor: ");
MonitorState ms = (MonitorState)monitors.get(jt);
if (null == ms) {
out.print("<no associated monitor>");
} else {
try {
String name = ms.getMonitor().getName();
if (name.equals("")) {
name = "<unnamed>";
}
out.print(ms.getStatusString() + " \"" + name + "\"");
} catch (CorruptDataException cde) {
out.print(Exceptions.getCorruptDataExceptionString());
}
JavaObject jo = ms.getMonitor().getObject();
if (null == jo) {
// working with a raw monitor
out.print(" with ID ");
// FIXME: once Kato implements IDs, output ID
out.print("<unavailable>");
} else {
// working with a Java monitor
out.print(" with object ");
out.print(Utils.toHex(jo.getID().getAddress()));
}
}
out.print("\n");
out.print("\t thread frames: ");
printJavaStackFrameInfo(jt, out);
out.print("\n");
}
private void printJavaStackFrameInfo(JavaThread jt, Output out)
{
Iterator itStackFrame;
JavaStackFrame jsf;
JavaLocation jl;
itStackFrame = jt.getStackFrames().iterator();
if (!itStackFrame.hasNext()) {
out.print("<no frames to print>\n");
return;
} else {
out.print("\n");
}
while (itStackFrame.hasNext()) {
// this iterator can contain JavaStackFrame or CorruptData objects
Object next = itStackFrame.next();
if (next instanceof CorruptData) {
out.print("\t " + Exceptions.getCorruptDataExceptionString() + "\n");
return;
} else {
jsf = (JavaStackFrame)next;
}
try {
jl = jsf.getLocation();
} catch (CorruptDataException e) {
out.print("\t " + Exceptions.getCorruptDataExceptionString()+ "\n");
return;
}
out.print("\t bp: ");
try {
out.print(toAdjustedHex(jsf.getBasePointer().getAddress()));
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print(" method: ");
try {
String signature = jl.getMethod().getSignature();
out.print(Utils.getReturnValueName(signature) + " " +
jl.getMethod().getDeclaringClass().getName() + "." +
jl.getMethod().getName() +
Utils.getMethodSignatureName(signature)
);
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
} catch (DataUnavailable e) {
out.print(Exceptions.getDataUnavailableString());
}
out.print(" filename:line: ");
try {
out.print(jl.getFilename());
} catch (DataUnavailable d) {
out.print(Exceptions.getDataUnavailableString());
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print(":");
try {
out.print(Integer.toString(jl.getLineNumber()));
} catch (DataUnavailable d) {
out.print(Exceptions.getDataUnavailableString());
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
out.print("\n");
}
}
private String toAdjustedHex(long l)
{
if (pointerSize > 32) {
return "0x" + Utils.toFixedWidthHex(l);
} else if (31 == pointerSize) {
return "0x" + Utils.toFixedWidthHex((int)(l & (((long)1) << pointerSize) - 1));
} else {
return "0x" + Utils.toFixedWidthHex((int)l);
}
}
private void printThreadSummary(ImageProcess ip, Output out)
{
// Prints a summary list of native thread IDs
int count = 0;
Iterator itThread = ip.getThreads().iterator();
while (itThread.hasNext()) {
Object next = itThread.next();
if (next instanceof CorruptData)
continue;
ImageThread it = (ImageThread)next;
if (count % 8 == 0) {
if (0 == count)
out.print("\n\n\tNative thread IDs for current process:");
out.print("\n\t ");
}
try {
out.print(Utils.padWithSpaces(it.getID(), 8));
} catch (CorruptDataException e) {
out.print(Exceptions.getCorruptDataExceptionString());
}
count++;
}
out.print("\n");
}
private void printProperties(ImageThread it, Output out)
{
Properties jtp = it.getProperties();
Enumeration keys = jtp.propertyNames();
while (keys.hasMoreElements())
{
String propertyKey = keys.nextElement().toString();
out.print("\t " + propertyKey + ": " + jtp.getProperty(propertyKey));
out.print("\n");
}
}
}