/*******************************************************************************
* 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.helpers;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
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.DiagnosticException;
import javax.tools.diagnostics.runtime.java.JavaClass;
import javax.tools.diagnostics.runtime.java.JavaClassLoader;
import javax.tools.diagnostics.runtime.java.JavaField;
import javax.tools.diagnostics.runtime.java.JavaHeap;
import javax.tools.diagnostics.runtime.java.JavaLocation;
import javax.tools.diagnostics.runtime.java.JavaMethod;
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;
/**
* Provides utility classes for retrieving things from dumps
*
*
*/
public class KatoSearch {
/**
* Passed JavaRuntimes to a visitor given an image.
*
* @param image
* @param visitor
*/
public static void visitRuntimes(Image image, JavaRuntimeVisitor visitor) throws DiagnosticException {
List spaces = image.getAddressSpaces();
for(Object nextSpace : spaces) {
if(nextSpace instanceof CorruptData) {
continue;
}
ImageAddressSpace space = (ImageAddressSpace) nextSpace;
List processes = space.getProcesses();
for (Object nextProcess : processes) {
if (nextProcess instanceof CorruptData) {
continue;
}
ImageProcess process = (ImageProcess) nextProcess;
List runtimes = process.getRuntimes();
for (Object nextRuntime : runtimes) {
if (nextRuntime instanceof CorruptData) {
continue;
}
if (nextRuntime instanceof JavaRuntime) {
JavaRuntime runtime = (JavaRuntime) nextRuntime;
visitor.visit(runtime);
}
}
}
}
}
/**
* Finds the first instance of a class.
* Best used for classes loaded in one class loader.
*
* @param runtime JavaRuntime to search for class
* @param name name of class to search for
* @return JavaClass or null if class not found.
*/
public static JavaClass findClass(JavaRuntime runtime, String name) {
List classLoaders = runtime.getJavaClassLoaders();
for (Object nextLoader : classLoaders) {
if (nextLoader instanceof CorruptData) {
continue;
}
JavaClassLoader loader = (JavaClassLoader) nextLoader;
List classes = loader.getDefinedClasses();
for (Object nextClazz : classes) {
if (nextClazz instanceof CorruptData) {
continue;
}
JavaClass clazz = (JavaClass) nextClazz;
try {
if (name.equals(clazz.getName())) {
return clazz;
}
} catch (CorruptDataException e) {
// IGNORE
}
}
}
return null;
}
/**
* Visits all classes in a passed runtime.
*
* @param runtime
* @param visitor
* @throws DiagnosticException
*/
public static void visitClasses(JavaRuntime runtime, JavaClassVisitor visitor) {
List classLoaders = runtime.getJavaClassLoaders();
CLASSLOADERS:
for (Object nextLoader : classLoaders) {
if (nextLoader instanceof CorruptData) {
continue;
}
JavaClassLoader loader = (JavaClassLoader) nextLoader;
List classes = loader.getDefinedClasses();
for (Object nextClazz : classes) {
if (nextClazz instanceof CorruptData) {
continue;
}
JavaClass clazz = (JavaClass) nextClazz;
try {
boolean rc = visitor.visit(clazz);
if (rc == false) {
break CLASSLOADERS;
}
} catch (DiagnosticException e) {
}
}
}
}
/**
* Returns a field of a given name from a class.
* Also tries superclasses.
*
*
* @param clazz JavaClass to search for field
* @param name name of field to retrieve.
* @return JavaField if found, else returns null.
*/
public static JavaField getField(JavaClass clazz, String name) {
while(clazz != null) {
List fields = clazz.getDeclaredFields();
for (Object nextField : fields) {
if (nextField instanceof CorruptData) {
break;
}
JavaField field = (JavaField) nextField;
try {
if (name.equals(field.getName())) {
return field;
}
} catch (CorruptDataException e) {
// Try again.
}
}
try {
clazz = clazz.getSuperclass();
} catch (CorruptDataException e) {
break;
}
}
return null;
}
/**
* Finds all instances of a given class and returns them in a list.
* Only suitable for small numbers.
*
* @param runtime
* @param clazz
* @return
*/
public static List<JavaObject> findInstances(JavaRuntime runtime, JavaClass clazz) {
LinkedList<JavaObject> instances = new LinkedList<JavaObject>();
InstanceFinder finder = new InstanceFinder(instances, clazz);
visitObjects(runtime, finder);
return instances;
}
/**
* Helper class for finding JavaObjects of a certain JavaClass.
*
*
*/
private static class InstanceFinder implements JavaObjectVisitor {
private JavaClass clazz;
private List<JavaObject> list;
public InstanceFinder(List<JavaObject> list, JavaClass clazz) {
this.list = list;
this.clazz = clazz;
}
public boolean visit(JavaObject object) throws DiagnosticException {
if (clazz.equals(object.getJavaClass())) {
list.add(object);
}
return true;
}
}
/**
* Find all instances of a JavaClass with a passed name, and all of it's
* subclasses.
*
* @param runtime JavaRuntime
* @param className name o
* @return List of JavaClasses
*/
public static List<JavaClass> findClasses(JavaRuntime runtime, String className) {
LinkedList<JavaClass> list = new LinkedList<JavaClass>();
SubclassFinder finder = new SubclassFinder(className, list);
visitClasses(runtime, finder);
return list;
}
/**
* Supports findClasses method.
*
*/
private static class SubclassFinder implements JavaClassVisitor {
private String clazzName;
private List<JavaClass> list;
public SubclassFinder(String name, List<JavaClass> list) {
if (name == null) {
throw new IllegalArgumentException("Cannot accept null class name");
}
this.clazzName = name;
this.list = list;
}
public boolean visit(JavaClass clazz) throws DiagnosticException {
JavaClass subClass = clazz;
while((clazz != null)
&& !(clazzName.equals(clazz.getName()))) {
clazz = clazz.getSuperclass();
}
if (clazz != null) {
list.add(subClass);
}
return true;
}
}
/**
* Executes the passed JavaObjectVisitor over all objects on all the heaps in
* the given JavaRuntime.
* CorruptData objects are not passed.
*
* @param runtime JavaRuntime whose objects are being visited
* @param visitor Visitor to execute against each object.
*/
public static void visitObjects(JavaRuntime runtime, JavaObjectVisitor visitor) {
Iterator heaps = runtime.getHeaps().iterator();
while (heaps.hasNext()) {
Object nextHeap = heaps.next();
if (nextHeap instanceof CorruptData) {
continue;
}
JavaHeap heap = (JavaHeap) nextHeap;
Iterator objects = heap.getObjects().iterator();
while (objects.hasNext()) {
Object nextObject = objects.next();
if (nextObject instanceof CorruptData) {
continue;
}
JavaObject object = (JavaObject) nextObject;
try {
boolean rc = visitor.visit(object);
if (rc == false) {
return;
}
} catch (DiagnosticException e) {
e.printStackTrace();
}
}
}
}
/**
* Given a JavaClass, find all threads that are executing a method in that class.
*
* @param runtime JavaRuntime to search
* @param clazz JavaClass to look for
* @return List of JavaThreads
*/
public static List<JavaThread> findClassInThreads(JavaRuntime runtime, JavaClass clazz) {
List<JavaThread> list = new LinkedList<JavaThread>();
for (Object nextThread : runtime.getThreads()) {
if (nextThread instanceof CorruptData) {
continue;
}
JavaThread thread = (JavaThread) nextThread;
// Iterate over all frames. If method is in passed class, add it to the list.
FRAMES: for (Object nextFrame : thread.getStackFrames()) {
if (nextFrame instanceof CorruptData) {
continue;
}
JavaStackFrame frame = (JavaStackFrame) nextFrame;
JavaLocation location;
try {
location = frame.getLocation();
} catch (CorruptDataException e) {
continue FRAMES;
}
if (location != null) {
try {
JavaMethod method = location.getMethod();
if (clazz.equals(method.getDeclaringClass())) {
list.add(thread);
break FRAMES;
}
} catch (CorruptDataException e) {
continue FRAMES;
} catch (DataUnavailable e) {
}
}
}
}
return list;
}
}