package samples.callgraph;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Stack;
import javax.swing.JFrame;
import org.apache.log4j.Category;
import org.apache.log4j.PropertyConfigurator;
import salvo.jesus.graph.DirectedEdgeImpl;
import salvo.jesus.graph.EdgeImpl;
import salvo.jesus.graph.Graph;
// import salvo.jesus.graph.GraphImpl;
import salvo.jesus.graph.Tree;
import salvo.jesus.graph.TreeImpl;
import salvo.jesus.graph.Vertex;
import salvo.jesus.graph.visual.GraphEditor;
import salvo.jesus.graph.visual.VisualGraph;
//import salvo.jesus.graph.visual.layout.ForceDirectedLayout;
import salvo.jesus.graph.visual.layout.GraphLayoutManager;
import salvo.jesus.graph.visual.layout.LayeredTreeLayout;
//import salvo.jesus.graph.visual.layout.OrthogonalLineLayout;
import alt.jiapi.InstrumentationContext;
import alt.jiapi.InstrumentationDescriptor;
import alt.jiapi.event.MethodEvent;
import alt.jiapi.event.MethodEventProducer;
import alt.jiapi.event.MethodListener;
import alt.jiapi.reflect.Loader;
import alt.jiapi.util.InstrumentingClassLoader;
public class CallGraphMonitor extends JFrame implements MethodListener {
private static Category log = Category.getInstance(CallGraphMonitor.class.getName());
private Stack callStack;
private Map createdClasses;
private int count = 1;
// private Graph graph;
private Tree tree;
private VisualGraph visual;
private GraphEditor editor;
//private ForceDirectedLayout layout;
// private OrthogonalLineLayout layout;
private GraphLayoutManager layout;
public CallGraphMonitor(Properties props) throws Exception {
PropertyConfigurator.configure(props);
editor = new GraphEditor();
// graph = new GraphImpl();
tree = new TreeImpl();
visual = editor.getVisualGraph();
callStack = new Stack();
createdClasses = new HashMap();
// visual.setGraph(graph);
editor.setGraph(tree);
this.getContentPane().setLayout(new GridLayout(1,2));
this.getContentPane().add(editor);
//layout = new OrthogonalLineLayout(visual);
// layout = new ForceDirectedLayout(visual);
// layout.setIncrement(0.001);
layout = new LayeredTreeLayout(visual);
editor.setGraphLayoutManager(layout);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }
});
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = new Dimension(screenSize.width - 80,
screenSize.height - 200);
this.setSize(frameSize);
this.setLocation((int)(screenSize.getWidth() - frameSize.getWidth()) / 2, (int)(screenSize.getHeight() - frameSize.getHeight()) / 2);
// layout.layout();
}
// MethodListener callbacks
public void constructorEntered(MethodEvent event) {
}
public void constructorExited(MethodEvent event) {
}
public void methodEntered(MethodEvent event) {
enterMethod(event.getClassName(), event.getMethodName());
}
public void methodExited(MethodEvent event){
exitMethod(event.getClassName(), event.getMethodName());
}
private void enterMethod(String className, String methodName) {
// try {
// Thread.currentThread().sleep(1000);
// } catch (Exception e) {
// }
log.debug(className + "." + methodName + " entered");
// ClassVertex classVertex = (ClassVertex) createdClasses.get(className);
// if (classVertex == null) {
// createdClasses.put(className, classVertex);
// try {
// tree.add(classVertex);
// } catch (Exception e) {
// log.error(e.getMessage(), e);
// }
// }
ClassVertex classVertex = null;
if (!callStack.empty()) {
ClassVertex parent = (ClassVertex) callStack.peek();
if (parent.hasChild(className)) {
classVertex = parent.getChild(className);
MethodEdge edge = parent.getEdge(className);
edge.addMethodCall(methodName, count);
// NOTE! A kludge to refresh edge name.
try {
tree.removeEdge(edge);
tree.addEdge(edge);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
else {
classVertex = new ClassVertex(className);
MethodEdge edge = null;
try {
tree.add(classVertex);
edge = new MethodEdge(parent, classVertex);
edge.addMethodCall(methodName, count);
tree.addEdge(edge);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
parent.addChild(classVertex, edge);
}
log.debug("added edge from " + parent.getName() +
" to " + classVertex.getName());
}
else {
try {
classVertex = new ClassVertex(className);
tree.addNode(null, classVertex);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
callStack.push(classVertex);
count++;
visual.layout();
}
private void exitMethod(String className, String methodName) {
log.debug(className + "." + methodName + " exited");
// This cannot be null since it is impossible to exit a method
// without first entering it.
// ClassVertex classVertex = (ClassVertex) createdClasses.get(className);
try {
ClassVertex parent = (ClassVertex) callStack.pop();
// if (parent != null) {
// parent.finishMethodCall();
// }
} catch (EmptyStackException ese) {
}
}
public static void main(String[] args) throws Exception {
InputStream config = new BufferedInputStream(new FileInputStream(args[0]));
Properties props = new Properties();
props.load(config);
CallGraphMonitor monitor = new CallGraphMonitor(props);
monitor.setTitle("CallGraphMonitor");
monitor.setVisible(true);
log.debug("created CallGraphMonitor");
try {
Thread.currentThread().sleep(1000);
}
catch (Exception e) {
}
log.debug("creating the InstrumentationDescriptor");
InstrumentationContext ctx = new InstrumentationContext();
InstrumentationDescriptor id = new InstrumentationDescriptor();
id.addInclusionRule("test.callgraph");
MethodEventProducer eventProducer = new MethodEventProducer(id);
eventProducer.addMethodListener(monitor);
ctx.addInstrumentationDescriptor(id);
log.debug("creating a class loader");
ClassLoader cl = InstrumentingClassLoader.createClassLoader(ctx);
log.debug("loading classes");
Class clazz = cl.loadClass("test.callgraph.Main");
log.debug("starting program");
java.lang.reflect.Method m =
clazz.getMethod("main", new Class[] {String[].class});
String []restArgs = new String[args.length - 1];
for (int i = 1; i < args.length; i++) {
restArgs[i - 1] = args[i];
}
for (int i = 0; i < restArgs.length; i++) {
System.out.println(i + ": " + restArgs[i]);
}
m.invoke(clazz, new Object[] {restArgs});
log.debug("main thread dying");
}
}