package de.twenty11.unitprofile.callback;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.twenty11.unitprofile.agent.Agent;
import de.twenty11.unitprofile.domain.MethodDescriptor;
import de.twenty11.unitprofile.domain.MethodInvocation;
import de.twenty11.unitprofile.output.OutputGenerator;
/**
* the class called from the instrumented methods.
*
*/
public class ProfilerCallback {
private static final Logger logger = LoggerFactory.getLogger(ProfilerCallback.class);
/**
* contains all profiling information after profiling
*/
private static List<MethodInvocation> invocations = new ArrayList<MethodInvocation>();
/**
* a stack keeping track of the nested method calls. Starts empty and stops with the top-level (root) invocation.
*/
private static ArrayDeque<MethodInvocation> callstack = new ArrayDeque<MethodInvocation>();
/**
* the first invocation for this profiling session.
*
* @param objectName
* @param methodName
* @return
*/
public static MethodInvocation start(String objectName, String methodName, int lineNumber) {
bigMessage("Starting profiling... " + objectName + "#" + methodName + " (" + lineNumber + ")");
if (profiling()) {
logger.error("Profiling was already started for '{}'", callstack.getFirst().getCls() + "#"
+ callstack.getFirst().getMethod());
throw new IllegalStateException();
}
MethodDescriptor methodDescriptor = new MethodDescriptor(objectName, methodName, lineNumber);
MethodInvocation rootInvocation = new MethodInvocation(methodDescriptor);
invocations.add(rootInvocation);
callstack.add(rootInvocation);
Agent.setRootInvocation(rootInvocation);
return rootInvocation;
}
public static void stop(String objectName, String methodName) {
bigMessage("Profiling... done.");
long now = System.currentTimeMillis();
invocations.get(invocations.size() - 1).setEnd(now);
MethodInvocation last = callstack.pollLast();
logger.info("Calculating data...");
last.calc();
logger.info("Profiling output:\n");
logger.info(last.dump());
logger.info("Creating files...");
new OutputGenerator().renderFromBootstrapTemplate(last);
new OutputGenerator().renderDebugInfo();
logger.info("Instrumentation execution... done.");
}
public static void before(String objectName, String methodName, int lineNumber) { // , int depth) {
if (!profiling()) {
return;
}
handleInvocation(objectName, methodName, lineNumber);
}
public static void after(String objectName, String methodName) {
if (!profiling()) {
return;
}
long now = System.currentTimeMillis();
MethodInvocation pollLast = callstack.pollLast();
pollLast.setEnd(now);
}
public static boolean profiling() {
return callstack.size() > 0;
}
public static List<MethodInvocation> getInvocations() {
return invocations;
}
private static void handleInvocation(String objectName, String methodName, int lineNumber) {
MethodInvocation existingInvocation = getInvocation(callstack.peekLast(), objectName, methodName);
if (existingInvocation != null) {
// logger.debug("invocation '{}' exists, incrementing count", existingInvocation);
existingInvocation.increment();
callstack.add(existingInvocation);
return;
}
MethodDescriptor methodDescriptor = new MethodDescriptor(objectName, methodName, lineNumber);
MethodInvocation invocation = new MethodInvocation(callstack.peekLast(), methodDescriptor);
// logger.info("invocation of '{}'", invocation);
invocations.add(invocation);
callstack.add(invocation);
}
private static MethodInvocation getInvocation(MethodInvocation peekLast, String objectName, String methodName) {
for (MethodInvocation invocation : invocations) {
if (invocation.getParent() == null && peekLast != null) {
continue;
}
if (invocation.getParent().equals(peekLast) && invocation.getCls().equals(objectName)
&& invocation.getMethod().equals(methodName)) {
return invocation;
}
}
return null;
}
private static void bigMessage(String msg) {
logger.info("");
logger.info("=====================");
logger.info(msg);
logger.info("=====================");
logger.info("");
}
}