/*******************************************************************************
* 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.Map;
import java.util.HashMap;
import java.util.Stack;
import java.util.Iterator;
import javax.tools.diagnostics.image.CorruptData;
import javax.tools.diagnostics.image.CorruptDataException;
import javax.tools.diagnostics.image.Image;
import javax.tools.diagnostics.runtime.ManagedRuntime;
import javax.tools.diagnostics.runtime.java.JavaClass;
import javax.tools.diagnostics.runtime.java.JavaClassLoader;
import javax.tools.diagnostics.runtime.java.JavaHeap;
import javax.tools.diagnostics.runtime.java.JavaObject;
import javax.tools.diagnostics.runtime.java.JavaRuntime;
import org.apache.kato.katoview.Output;
import org.apache.kato.katoview.commands.Command;
import org.apache.kato.katoview.commands.helpers.ClassOutput;
import org.apache.kato.katoview.commands.helpers.Exceptions;
import org.apache.kato.katoview.commands.helpers.Utils;
public class InfoClassCommand extends Command{
public InfoClassCommand(Output o){
super(o, "class", "prints inheritance chain and other data for a given class",
"parameters: none, class name\n\n" +
"if no parameters are passed to \"info class\", it prints the " +
"number of instances of each class and the total size of all " +
"instances of each class as well as the total number of instances " +
"of all classes and the total size of all objects.\n\n" +
"if a class name is passed to \"info class\", it prints the " +
"following information about that class:\n" +
" - name\n" +
" - ID\n" +
" - superclass ID\n" +
" - class loader ID\n" +
" - modifiers\n" +
" - number of instances and total size of instances\n" +
" - inheritance chain\n" +
" - fields with modifiers (and values for static fields)\n" +
" - methods with modifiers\n"
);
child_commands = null;
}
public void doCommand(Stack args, Image loadedImage, HashMap systemProperties){
if (args.isEmpty())
{
printImageClasses(loadedImage);
return;
}
String className = (String)args.pop();
if (!args.isEmpty())
{
out.error("\"info class\" takes at most one parameter, which, if " +
"specified, must be a class name");
return;
}
printImageClass(loadedImage, className);
}
private void printImageClass(Image loadedImage, String className)
{
ManagedRuntime mr;
Iterator itRuntime = Utils.getRuntimes(loadedImage);
int count = 1;
out.print("\n");
while (itRuntime.hasNext()) {
mr = (ManagedRuntime)itRuntime.next();
if (mr instanceof JavaRuntime)
{
out.print("Runtime #" + count + ": \n\n");
printRuntimeClass((JavaRuntime)mr, className);
}
count++;
}
}
private void printRuntimeClass(JavaRuntime jr, String className)
{
JavaClass jc = Utils.getClassGivenName(className, jr, out);
// if we couldn't find a class of that name, return; the passed in class name could
// still be an array type or it might not exist
if (null == jc)
{
out.print("\t could not find class with name \"" + className + "\"\n\n");
return;
}
String spaces = " ";
String cdeInfo = "N/A (CorruptDataException occurred)";
out.print("name = " + className);
out.print(spaces);
out.print("\n\n\t");
out.print("ID = 0x" + Long.toHexString(jc.getID().getAddress()));
String superClassInfo;
try{
JavaClass superClass = jc.getSuperclass();
if (null == superClass) {
superClassInfo = "<no superclass>";
} else {
superClassInfo = "0x" + Long.toHexString(superClass.getID().getAddress());
}
}catch (CorruptDataException dce){
superClassInfo = cdeInfo;
}
out.print(spaces);
out.print("superID = " + superClassInfo);
/*
* Omitting size of class because that might differ between instances (ie. arrays)
*
sb.append(spaces);
sb.append("instance size = " + datum.getSize());
*/
String classLoaderInfo;
try{
JavaClassLoader jClassLoader = jc.getClassLoader();
classLoaderInfo = "0x" + Long.toHexString(jClassLoader.getObject().getJavaClass().getID().getAddress());
}catch (CorruptDataException cde){
classLoaderInfo = cdeInfo;
}
out.print(spaces);
out.print("\n\t");
out.print("classLoader = " + classLoaderInfo);
String modifiersInfo;
try{
int modifiers = jc.getModifiers();
modifiersInfo = Utils.getModifierString(modifiers);
}catch (CorruptDataException cde){
modifiersInfo = cdeInfo;
}
out.print(spaces);
out.print("modifers: " + modifiersInfo);
out.print("\n\n");
Map classMap = new HashMap();
classMap.put(jc, new Datum());
updateInstanceCount(jr, classMap);
Datum d = (Datum)classMap.get(jc);
out.print("\tnumber of instances: " + d.getCount() + "\n");
out.print("\ttotal size of instances: " + d.getSize() + " bytes");
out.print("\n\n");
printClassHierarchy(jc);
out.print("\n");
printFields(jc);
out.print("\n");
printMethods(jc);
}
private void printClassHierarchy(JavaClass jClass) {
Stack stack = new Stack();
while (null != jClass){
try{
stack.add(jClass.getName());
jClass = jClass.getSuperclass();
}catch(CorruptDataException cde){
stack.add("N/A (CorruptDataException occurred)");
break;
}
}
printStack(stack);
}
private void printStack(Stack stack){
out.print("Inheritance chain....\n\n");
String tab = "\t";
String spaces = "";
while(stack.size() > 0){
out.print(tab + spaces + (String)stack.pop() + "\n");
spaces += " ";
}
}
private void printFields(JavaClass jClass) {
out.print("Fields......\n\n");
ClassOutput.printStaticFields(jClass, out);
ClassOutput.printNonStaticFields(jClass, out);
}
private void printMethods(JavaClass jClass){
out.print("Methods......\n\n");
ClassOutput.printMethods(jClass.getDeclaredMethods().iterator(), out);
out.print("\n");
}
// below methods are for "info class" (with no parameters)
private void printImageClasses(Image loadedImage) {
ManagedRuntime mr;
Iterator itRuntime = Utils.getRuntimes(loadedImage);
int count = 1;
out.print("\n");
while (itRuntime.hasNext()) {
mr = (ManagedRuntime)itRuntime.next();
if (mr instanceof JavaRuntime)
{
out.print("Runtime #" + count + ": \n\n");
printRuntimeClasses((JavaRuntime)mr);
out.print("\n\n");
}
count++;
}
}
private void printRuntimeClasses(JavaRuntime jr) {
Map javaClasses = new HashMap();
Iterator itClassLoader = jr.getJavaClassLoaders().iterator();
// create Map of all classes in this JavaRuntime's class loaders
while (itClassLoader.hasNext()) {
JavaClassLoader jcl = (JavaClassLoader)itClassLoader.next();
// Check for any corrupt data for this classloader
Iterator itCache = jcl.getCachedClasses().iterator();
while (itCache.hasNext()) {
Object next = itCache.next();
if (next instanceof CorruptData){
// Warn the user that the classloader data is corrupt
try {
long jclAddress = jcl.getObject().getID().getAddress();
out.print("\t classloader ID: " + Utils.toHex(jclAddress) + " " + Exceptions.getCorruptDataExceptionString() + "\n");
} catch (CorruptDataException e) {
out.print("\t classloader ID: <unknown> " + Exceptions.getCorruptDataExceptionString() + "\n");
}
break;
}
}
Iterator itClass = jcl.getDefinedClasses().iterator();
while (itClass.hasNext()) {
javaClasses.put((JavaClass)itClass.next(), new Datum());
}
}
// update count of objects and sizes in Map of classes
updateInstanceCount(jr, javaClasses);
// print out results of object counting
long objCount = 0;
long totalSize = 0;
Iterator itClass = javaClasses.keySet().iterator();
if (itClass.hasNext()) {
printClassListHeader();
} else {
out.print("\n\t No information found for loaded classes\n");
}
while (itClass.hasNext()) {
JavaClass jc = (JavaClass)itClass.next();
String className;
try {
className = jc.getName();
} catch (CorruptDataException cde) {
className = Exceptions.getCorruptDataExceptionString();
}
Datum d = (Datum)javaClasses.get(jc);
totalSize += d.getSize();
objCount += d.getCount();
printOneClass(className, d);
}
out.print("\n");
if ( objCount > 0) {
out.print("\t Total number of objects: " + objCount + "\n");
out.print("\t Total size of objects: " + totalSize + "\n");
}
}
private void updateInstanceCount(JavaRuntime jr, Map javaClasses) {
Iterator itHeap = jr.getHeaps().iterator();
while (itHeap.hasNext()) {
JavaHeap jh = (JavaHeap)itHeap.next();
Iterator itObject = jh.getObjects().iterator();
while (itObject.hasNext()) {
Object next = itObject.next();
// Check that this is a JavaObject (there may be CorruptData objects in the
// JavaHeap, we don't attempt to count these as instances of known classes).
if (next instanceof JavaObject) {
JavaObject jo = (JavaObject)next;
Datum d = null;
JavaClass jc;
try {
jc = jo.getJavaClass();
} catch (CorruptDataException cde) {
jc = null;
}
if (null != jc) {
long size;
d = (Datum)javaClasses.get(jc);
if (null == d) {
d = new Datum();
javaClasses.put(jc, d);
// FIXME: the below is for debugging purposes
// this should not be needed when Kato reports array classes
// in the class loaders
/* try {
out.print("\"" + jo.getJavaClass().getName() + "\"\n");
} catch (CorruptDataException cde) {
out.print(Exceptions.getCorruptDataExceptionString() + "\n");
}
*/
}
try {
size = jo.getSize();
} catch (CorruptDataException cde) {
size = 0;
}
d.addToSize(size);
d.incrementCount();
} else { // FIXME: DEBUG
try {
out.print(jo.getJavaClass().getName() + "\n");
} catch (CorruptDataException cde) {
out.print(Exceptions.getCorruptDataExceptionString() + "\n");
}
}
}
}
}
}
private void printClassListHeader() {
out.print("\t");
out.print(Utils.prePadWithSpaces("instances", 16));
out.print(Utils.prePadWithSpaces("total size", 16));
out.print(" class name");
out.print("\n");
}
private void printOneClass(String className, Datum datum){
out.print("\t");
out.print(Utils.prePadWithSpaces(String.valueOf(datum.getCount()), 16));
out.print(Utils.prePadWithSpaces(String.valueOf(datum.getSize()), 16));
out.print(" " + className);
out.print("\n");
}
/*
private void printOneClass(String className, Datum datum){
out.print("\t " + className + "\t"
+ "has " + datum.getCount() + " instances "
+ "(total size = " + (long)datum.getSize()*datum.getCount() + ")"
+ "\n");
}
*/
public class Datum{
private int count;
private long size;
public Datum(){
this.count = 0;
this.size = 0;
}
public int getCount(){
return this.count;
}
public long getSize(){
return this.size;
}
public void incrementCount(){
this.count++;
}
public void addToSize(long sizeToAdd){
this.size += sizeToAdd;
}
}
}