package nginx.clojure.wave; import java.lang.reflect.Method; import java.util.ArrayList; import nginx.clojure.Stack; import nginx.clojure.asm.Type; import nginx.clojure.wave.MethodDatabase.ClassEntry; import nginx.clojure.wave.SuspendMethodTracer.MethodInfo; public class SuspendMethodVerifier { public static class VerifyVarInfo implements Cloneable { public int idx = -1; public int dataIdx = -1; public String name; public Object value; public VerifyVarInfo clone() { try { return (VerifyVarInfo) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } } public static class VerifyMethodInfo extends MethodInfo { public int idx = -1; public VerifyVarInfo[] vars; public String classAndMethod; public VerifyMethodInfo(String owner, String method) { super(owner, method); classAndMethod = owner + "." + method; } } public static class VerifyInfo { public long vid; public boolean quite = false; public boolean exception = false; public ArrayList<VerifyMethodInfo> tracerStacks = new ArrayList<SuspendMethodVerifier.VerifyMethodInfo>(); public VerifyMethodInfo[] methodIdxInfos = new VerifyMethodInfo[8]; // private long id; } protected static MethodDatabase db; public SuspendMethodVerifier() { } public static void onYield() { VerifyInfo vi = Stack.getVerifyInfo(); if (vi == null) { return; } vi.quite = true; try { ArrayList<VerifyMethodInfo> stack = vi.tracerStacks; MethodInfo cmi = stack.get(stack.size() -1); if (db.meetTraceTargetClassMethod(cmi.owner, cmi.method)) { db.info("#%d onYield %s.%s", vi.vid , cmi.owner, cmi.method); } for (int i = stack.size() - 1; i > -1; i--) { MethodInfo mi = stack.get(i); if (mi.suspendType != -1 || ("nginx/clojure/Coroutine".equals(mi.owner) && "resume()V".equals(mi.method))) { break; } ClassEntry ce = db.getClasses().get(mi.owner); if (ce == null) { vi.exception = true; db.error(new RuntimeException(String.format("#%d onYield: can not found ClassEntry for %s", vi.vid, mi.owner))); return; } if (!ce.isAlreadyInstrumented()) { vi.exception = true; db.error(new RuntimeException(String.format("#%d onYield: %s is not AlreadyInstrumented!", vi.vid, mi.owner))); return; } // boolean meetTraced = db.meetTraceTargetClassMethod(mi.owner, mi.method); Integer knownType = db.checkMethodSuspendType(mi.owner, mi.method, false, true); if (knownType == null || knownType < MethodDatabase.SUSPEND_NORMAL) { mi.suspendType = knownType == null ? MethodDatabase.SUSPEND_NONE : knownType; // db.warn("meet type %s != SUSPEND_NORMAL from %s.%s", MethodDatabase.SUSPEND_TYPE_STRS[knownType], mi.owner, mi.method); vi.exception = true; db.error( new RuntimeException(String.format("#%d onYield: meet type %s != SUSPEND_NORMAL from %s.%s", vi.vid, MethodDatabase.SUSPEND_TYPE_STRS[knownType], mi.owner, mi.method) )); }else if (knownType > MethodDatabase.SUSPEND_NORMAL) { mi.suspendType = knownType; // if (meetTraced) { // db.info("meet traced method %s.%s, known suspend type=%s", mi.owner, mi.method, MethodDatabase.SUSPEND_TYPE_STRS[knownType]); // } //we need not record those records which has been defined by predefined configuration files db.warn("#%d onYield: meet predefined type %s != SUSPEND_NORMAL from %s.%s", vi.vid, MethodDatabase.SUSPEND_TYPE_STRS[knownType], mi.owner, mi.method); continue; }else { mi.suspendType = MethodDatabase.SUSPEND_NORMAL; // if (meetTraced) { // db.info("meet traced method %s.%s, set unknown suspend type to =%s", mi.owner, mi.method, MethodDatabase.SUSPEND_TYPE_STRS[knownType]); // } } } }finally { vi.quite = false; } } public static void enter(String owner, String method) { VerifyInfo vi = Stack.getVerifyInfo(); if (vi == null) { return; } if (vi.quite || vi.exception) { return; } vi.quite = true; ArrayList<VerifyMethodInfo> stack = vi.tracerStacks; try { if (db.meetTraceTargetClassMethod(owner, method)) { db.info("#%d enter %s.%s", vi.vid , owner, method); } stack.add(new VerifyMethodInfo(owner, method)); }finally{ vi.quite = false; } } public static void downProxyInvoke(Method m) { VerifyInfo vi = Stack.getVerifyInfo(); if (vi == null) { return; } if (vi.quite || vi.exception) { return; } enter(Type.getInternalName(m.getDeclaringClass()), m.getName()+Type.getMethodDescriptor(m)); } public static void upProxyInvoke(Method m) { VerifyInfo vi = Stack.getVerifyInfo(); if (vi == null) { return; } if (vi.quite || vi.exception) { return; } leave(Type.getInternalName(m.getDeclaringClass()), m.getName()+Type.getMethodDescriptor(m)); } public static void leave(String owner, String method) { VerifyInfo vi = Stack.getVerifyInfo(); if (vi == null) { return; } if (vi.quite || vi.exception) { return; } vi.quite = true; ArrayList<VerifyMethodInfo> stack = vi.tracerStacks; try{ if (db.meetTraceTargetClassMethod(owner, method)) { db.info("#%d leave %s.%s", vi.vid , owner, method); } MethodInfo mi = stack.get(stack.size() - 1); if (!mi.owner.equals(owner) || !mi.method.equals(method)) { vi.exception = true; db.error(new RuntimeException(String.format("#%d Thread #%d, leave != enter %s.%s != %s.%s", vi.vid, Thread .currentThread().getId(), owner, method, mi.owner, mi.method))); return; }else { stack.remove(stack.size() - 1); } }finally{ vi.quite = false; } } }