package org.ntlab.reverseDebugger; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jface.viewers.TreeNode; import org.ntlab.onlineAccessor.JDIInstanceMethodCaller; import org.ntlab.onlineAccessor.JDIStaticMethodCaller; import com.sun.jdi.BooleanValue; import com.sun.jdi.ClassNotLoadedException; import com.sun.jdi.IncompatibleThreadStateException; import com.sun.jdi.IntegerValue; import com.sun.jdi.InvalidTypeException; import com.sun.jdi.InvocationException; import com.sun.jdi.ObjectReference; import com.sun.jdi.StringReference; import com.sun.jdi.ThreadReference; import com.sun.jdi.Value; import com.sun.jdi.VirtualMachine; @SuppressWarnings("restriction") public class CallStackModels { private String debuggingThreadId = ""; private List<CallStackModel> debuggingThreadCallStacks = new ArrayList<>(); private Map<String, List<CallStackModel>> allCallStacks = new HashMap<>(); private static final String TRACE = "org.ntlab.traceAnalysisPlatform.tracer.trace"; public List<CallStackModel> getDebuggingThreadCallStacks() { return debuggingThreadCallStacks; } public TreeNode[] getAllCallStacksTree() { TreeNode[] roots = new TreeNode[allCallStacks.size()]; int rootIndex = 0; for (String threadId : allCallStacks.keySet()) { TreeNode node = getCallStackModelsTree(threadId); roots[rootIndex++] = node; } return roots; } public TreeNode getCallStackModelsTree(String threadId) { List<CallStackModel> callStackModelsInThread = allCallStacks.get(threadId); if (callStackModelsInThread.isEmpty()) return null; TreeNode root = new TreeNode(threadId); TreeNode parentNode = root; TreeNode[] childrenNode = new TreeNode[callStackModelsInThread.size()]; parentNode.setChildren(childrenNode); for (int i = 0; i < callStackModelsInThread.size(); i++) { TreeNode childNode = new TreeNode(callStackModelsInThread.get(i)); childNode.setParent(parentNode); childrenNode[i] = childNode; } return root; } public TreeNode[] getDebugingThreadCallStacksTree() { TreeNode[] roots = new TreeNode[1]; roots[0] = getCallStackModelsTree(debuggingThreadId); return roots; } public void reset() { debuggingThreadId = ""; debuggingThreadCallStacks.clear(); allCallStacks.clear(); } public void updateByDebuggerStopMethodExecution(JDIInstanceMethodCaller me) { if (me == null) return; try { VirtualMachine vm = me.getVm(); ThreadReference thread = me.getThread(); int lineNo = thread.frame(0).location().lineNumber(); updateInAllThreads(vm, thread, me.getReceiver(), lineNo); } catch (InvalidTypeException | ClassNotLoadedException | InvocationException | IncompatibleThreadStateException e) { e.printStackTrace(); } } public void updateByAlias(JDIInstanceMethodCaller alias) { if (alias == null) return; VirtualMachine vm = alias.getVm(); ThreadReference thread = alias.getThread(); try { ObjectReference me = (ObjectReference)alias.callInstanceMethod("getMethodExecution"); int lineNo = ((IntegerValue)alias.callInstanceMethod("getLineNo")).value(); updateInAllThreads(vm, thread, me, lineNo); } catch (InvalidTypeException | ClassNotLoadedException | InvocationException | IncompatibleThreadStateException e) { e.printStackTrace(); } } private void updateInAllThreads(VirtualMachine vm, ThreadReference thread, ObjectReference me, int topMethodCallLineNo) throws InvalidTypeException, ClassNotLoadedException, InvocationException, IncompatibleThreadStateException { JDIInstanceMethodCaller mc = new JDIInstanceMethodCaller(vm, thread, null); mc.changeReceiver(me); ObjectReference statements = (ObjectReference)mc.callInstanceMethod("getStatements"); mc.changeReceiver(statements); int lastIndex = ((IntegerValue)mc.callInstanceMethod("size")).value() - 1; mc.changeReceiver(me); ObjectReference tp = (ObjectReference)mc.callInstanceMethod("getTracePoint", vm.mirrorOf(lastIndex)); mc.changeReceiver(tp); ObjectReference tpStatement = (ObjectReference)mc.callInstanceMethod("getStatement"); mc.changeReceiver(tpStatement); reset(); debuggingThreadId = ((StringReference)mc.callInstanceMethod("getThreadNo")).value(); debuggingThreadCallStacks = update(vm, thread, me, debuggingThreadId, topMethodCallLineNo); allCallStacks.put(debuggingThreadId, debuggingThreadCallStacks); Value visitor = getVisitor(vm, thread, tp); updateOtherThreadCallStacks(vm, thread, visitor); } private List<CallStackModel> update(VirtualMachine vm, ThreadReference thread, ObjectReference me, String threadId, int topMethodCallLineNo) throws InvalidTypeException, ClassNotLoadedException, InvocationException, IncompatibleThreadStateException { List<CallStackModel> list = new ArrayList<>(); JDIInstanceMethodCaller mc = new JDIInstanceMethodCaller(vm, thread, null); int callLineNo = topMethodCallLineNo; ObjectReference childMe = null; ObjectReference tmpMe = me; while (tmpMe != null) { CallStackModel callStackModel = new CallStackModel(vm, thread, tmpMe, threadId, callLineNo); list.add(callStackModel); childMe = tmpMe; tmpMe = (ObjectReference)callStackModel.callInstanceMethod("getParent"); if (tmpMe != null) { mc.changeReceiver(tmpMe); ObjectReference callStatement = (ObjectReference)mc.callInstanceMethod("getMethodInvocation", childMe); callLineNo = ((IntegerValue)mc.changeReceiver(callStatement).callInstanceMethod("getLineNo")).value(); } } return list; } private void updateOtherThreadCallStacks(VirtualMachine vm, ThreadReference thread, Value visitor) { try { JDIInstanceMethodCaller mc = new JDIInstanceMethodCaller(vm, thread, null); ObjectReference traceJSON = (ObjectReference)mc.callStaticMethod(TRACE, "TraceJSON", "getInstance"); mc.changeReceiver(traceJSON); ObjectReference allThreads = (ObjectReference)mc.callInstanceMethod("getAllThreads"); mc.changeReceiver(allThreads); ObjectReference keySet = (ObjectReference)mc.callInstanceMethod("keySet"); mc.changeReceiver(keySet); ObjectReference iterator = (ObjectReference)mc.callInstanceMethod("iterator"); mc.changeReceiver(iterator); boolean hasNext = ((BooleanValue)mc.callInstanceMethod("hasNext")).value(); while (hasNext) { StringReference threadId = (StringReference)mc.callInstanceMethod("next"); if (threadId.value().equals(debuggingThreadId)) { mc.changeReceiver(iterator); hasNext = ((BooleanValue)mc.callInstanceMethod("hasNext")).value(); continue; } ObjectReference start = getStart(vm, thread, allThreads, threadId); mc.changeReceiver(traceJSON); mc.callInstanceMethod("getLastStatementInThread", threadId, start, visitor); ObjectReference resultTp = (ObjectReference)mc.callStaticMethod("java.lang.reflect", "Array", "get", start, vm.mirrorOf(0)); updateOtherThreadCallStacks(vm, thread, threadId.value(), resultTp); mc.changeReceiver(iterator); hasNext = ((BooleanValue)mc.callInstanceMethod("hasNext")).value(); } } catch (InvalidTypeException | ClassNotLoadedException | InvocationException | IncompatibleThreadStateException e) { e.printStackTrace(); } } private void updateOtherThreadCallStacks(VirtualMachine vm, ThreadReference thread, String threadId, ObjectReference tp) throws InvalidTypeException, ClassNotLoadedException, InvocationException, IncompatibleThreadStateException { JDIInstanceMethodCaller mc = new JDIInstanceMethodCaller(vm, thread, null); mc.changeReceiver(tp); ObjectReference me = (ObjectReference)mc.callInstanceMethod("getMethodExecution"); mc.changeReceiver(me); ObjectReference statements = (ObjectReference)mc.callInstanceMethod("getStatements"); mc.changeReceiver(statements); int lastIndex = ((IntegerValue)mc.callInstanceMethod("size")).value() - 1; ObjectReference lastStatement = (ObjectReference)mc.callInstanceMethod("get", vm.mirrorOf(lastIndex)); mc.changeReceiver(lastStatement); int lineNo = ((IntegerValue)mc.callInstanceMethod("getLineNo")).value(); allCallStacks.put(threadId, update(vm, thread, me, threadId, lineNo)); } private ObjectReference getVisitor(VirtualMachine vm, ThreadReference thread, ObjectReference tp) throws InvalidTypeException, ClassNotLoadedException, InvocationException, IncompatibleThreadStateException { JDIInstanceMethodCaller mc = new JDIInstanceMethodCaller(vm, thread, null); // // visitor (コンストラクタの引数がない場合) // ObjectReference clazz = (ObjectReference)mc.callStaticMethod( // "java.lang", "Class", "forName", vm.mirrorOf("org.ntlab.reverseDebugger.CallStackVisitor")); // mc.changeReceiver(clazz); // ObjectReference visitor = (ObjectReference)mc.callInstanceMethod("newInstance"); // visitor (コンストラクタに引数がある場合) ObjectReference visitorClass = (ObjectReference)mc.callStaticMethod( "java.lang", "Class", "forName", vm.mirrorOf("org.ntlab.reverseDebugger.CallStackVisitor")); ObjectReference classClass = (ObjectReference)mc.callStaticMethod( "java.lang", "Class", "forName", vm.mirrorOf("java.lang.Class")); ObjectReference classClassArray = (ObjectReference)mc.callStaticMethod("java.lang.reflect", "Array", "newInstance", classClass, vm.mirrorOf(1)); // 引数が可変長のメソッドをJDIで呼び出す際は、引数1つでも配列にしないとエラーが出る mc.changeReceiver(tp); ObjectReference tpClass = (ObjectReference)mc.callInstanceMethod("getClass"); mc.callStaticMethod("java.lang.reflect", "Array", "set", classClassArray, vm.mirrorOf(0), tpClass); mc.changeReceiver(visitorClass); ObjectReference constructor = (ObjectReference)mc.callInstanceMethod("getConstructor", classClassArray); ObjectReference tpClassArray = (ObjectReference)mc.callStaticMethod("java.lang.reflect", "Array", "newInstance", tpClass, vm.mirrorOf(1)); // 引数が可変長のメソッドをJDIで呼び出す際は、引数1つでも配列にしないとエラーが出る mc.callStaticMethod("java.lang.reflect", "Array", "set", tpClassArray, vm.mirrorOf(0), tp); mc.changeReceiver(constructor); ObjectReference visitor = (ObjectReference)mc.callInstanceMethod("newInstance", tpClassArray); return visitor; } private ObjectReference getStart(VirtualMachine vm, ThreadReference thread, ObjectReference allThreads, StringReference threadId) throws InvalidTypeException, ClassNotLoadedException, InvocationException, IncompatibleThreadStateException { JDIInstanceMethodCaller mc = new JDIInstanceMethodCaller(vm, thread, null); // start mc.changeReceiver(allThreads); ObjectReference threadInstance = (ObjectReference)mc.callInstanceMethod("get", threadId); mc.changeReceiver(threadInstance); ObjectReference currentTp = (ObjectReference)mc.callInstanceMethod("getCurrentTracePoint"); mc.changeReceiver(currentTp); ObjectReference currentTpClass = (ObjectReference)mc.callInstanceMethod("getClass"); ObjectReference start = (ObjectReference)mc.callStaticMethod("java.lang.reflect", "Array", "newInstance", currentTpClass, vm.mirrorOf(1)); mc.callStaticMethod("java.lang.reflect", "Array", "set", start, vm.mirrorOf(0), currentTp); return start; } }