/** * * Visit any byte-code instruction * * <p>This allows us to instrument methods an the instruction level. Instructions that are * instrumented this way regard exiting and synchronising byte-code instructions */ @Override public void visitInsn(int opcode) { instrumentationStatsRecord.addInstruction(); // if (opcode != ATHROW) { if (performanceModelParameters != null) { if (performanceModelParameters.removingMethodBody()) { if ((opcode >= IRETURN && opcode <= RETURN) || (!instrumentExceptionHandlingCode && opcode == ATHROW)) { if (profile) { onMethodExitInstrumentation(); } mv.visitInsn(opcode); } return; } } // These are all instructions which terminate a method if ((opcode >= IRETURN && opcode <= RETURN) || (!instrumentExceptionHandlingCode && opcode == ATHROW && tryCatchDepth == 0)) { if (profile) { onMethodExitInstrumentation(); } if (iparams.onlyLimitedJvmtiUsage() && isSynchronised) { if (isStatic) { mv.visitLdcInsn(Type.getType("L" + className + ";")); } else { mv.visitVarInsn(ALOAD, 0); } // mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I"); mv.visitMethodInsn( INVOKESTATIC, "java/lang/System", "identityHashCode", "(Ljava/lang/Object;)I"); mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "_beforeReleasingMonitor", "(I)V"); } if (shouldBeWrappedWithVexOptimizerLoop) { mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "_exportAndProcessMainIterationResults", "()V"); mv.visitJumpInsn(GOTO, startMainWrappedWithVexOptimizer); mv.visitLabel(endMainWrappedWithVexOptimizer); } mv.visitInsn(opcode); return; } // Monitor enter instrumentation - suspend thread if (opcode == MONITORENTER || opcode == MONITOREXIT) { instrumentationStatsRecord.addSyncPrimitive(); if (iparams.onlyLimitedJvmtiUsage()) { if (!iparams.shouldRemoveMonitors()) { mv.visitInsn(DUP); } if (opcode == MONITORENTER) { // mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I"); mv.visitMethodInsn( INVOKESTATIC, "java/lang/System", "identityHashCode", "(Ljava/lang/Object;)I"); onInteractionPointEncounter(); mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "_beforeAcquiringMonitor", "(I)V"); } else { onInteractionPointEncounter(); // mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "hashCode", "()I"); mv.visitMethodInsn( INVOKESTATIC, "java/lang/System", "identityHashCode", "(Ljava/lang/Object;)I"); mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "_beforeReleasingMonitor", "(I)V"); } if (iparams.shouldRemoveMonitors()) { return; } } } mv.visitInsn(opcode); }
/** * * Visitor of the method-calling byte-code instructions called by the method * * <p>This allows us to instrument the method-calling byte-code instructions of a profiled method. * These methods can be divided into the ones that: - denote thread start - denote thread yield - * denote thread wait - trigger synchronisation points - should register I/O points */ @Override public void visitMethodInsn(int opcode, String owner, String name, String desc) { if (DEBUG) { System.out.println(owner + " --> " + name + ": " + desc + " (" + opcode + ")"); } // called at the beginning of a method if (removingMethodBody()) { return; } instrumentationStatsRecord.addInstruction(); // Thread.yield - cannot be instrumented in java.lang.Thread, because it's a native method if (opcode == INVOKESTATIC && owner.equals("java/lang/Thread") && name.equals("yield") && desc.equals("()V")) { instrumentationStatsRecord.addSyncPrimitive(); mv.visitMethodInsn(INVOKESTATIC, "virtualtime/EventNotifier", "_yield", "()V"); return; } // Virtualizing waiting calls if (iparams.shouldTransformWaits()) { // In the limited JVMTI case we do not rely on JVMTI to trap Object.wait(), so we trap both // indefinite and timed-waiting events if (iparams.onlyLimitedJvmtiUsage()) { if (opcode == INVOKEVIRTUAL && owner.equals("java/lang/Object") && name.equals("wait")) { instrumentationStatsRecord.addSyncPrimitive(); if (iparams.shouldRemoveMonitors()) { mv.visitLdcInsn(ICONST_0); } else { mv.visitLdcInsn(ICONST_1); } if (desc.equals("()V")) { mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "waitInVirtualTimeWithoutJvmti", "(Ljava/lang/Object;Z)V"); } else if (desc.equals("(J)V")) { mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "waitInVirtualTimeWithoutJvmti", "(Ljava/lang/Object;JZ)V"); } else { mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "waitInVirtualTimeWithoutJvmti", "(Ljava/lang/Object;J*Z)V"); } return; } } else { // JVMTI traps Object.wait(), so we only trap timed-waiting events if (opcode == INVOKEVIRTUAL && owner.equals("java/lang/Object") && name.equals("wait") && desc.equals("(J)V")) { instrumentationStatsRecord.addSyncPrimitive(); mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "waitInVirtualTime", "(Ljava/lang/Object;J)V"); return; } if (opcode == INVOKEVIRTUAL && owner.equals("java/lang/Object") && name.equals("wait") && desc.equals("(JI)V")) { instrumentationStatsRecord.addSyncPrimitive(); // Object.wait(timeout, nanos) mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "waitInVirtualTime", "(Ljava/lang/Object;JI)V"); return; } } // Hack to include subclasses of Thread that override sleep - naive onLoad check does not work if (name.equals("sleep") && (desc.equals("(J)V") || desc.equals("(JI)V")) && (!owner.equals(className) || iparams .isSubClassOfJavaLangThread())) { // && owner.equals("java/lang/Thread")) { // || // iparams.isExtendindJavaLangThread(owner) instrumentationStatsRecord.addSyncPrimitive(); // System.out.println("changing sleep for " + owner + " " + name + " " + desc + " " + // className + " " + methodName); mv.visitMethodInsn(INVOKESTATIC, "virtualtime/EventNotifier", "sleepInVirtualTime", desc); return; } else if (name.equals("sleep")) { // System.out.println("*NOT* changing sleep for " + owner + " " + name + " " + desc + " // " + className + " " + methodName); } } if (iparams.shouldTransformWaits()) { if (!iparams.shouldRemoveMonitors()) { // Object.notifyAll(): if (opcode == INVOKEVIRTUAL && owner.equals("java/lang/Object") && name.equals("notifyAll") && desc.equals("()V")) { instrumentationStatsRecord.addSyncPrimitive(); mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "beforeNotifyAll", "(Ljava/lang/Object;)V"); return; } // Object.notify(): if (opcode == INVOKEVIRTUAL && owner.equals("java/lang/Object") && name.equals("notify") && desc.equals("()V")) { instrumentationStatsRecord.addSyncPrimitive(); mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "beforeNotify", "(Ljava/lang/Object;)V"); return; } } else { // WITHOUT MONITORS // Object.notifyAll(): if (opcode == INVOKEVIRTUAL && owner.equals("java/lang/Object") && name.equals("notifyAll") && desc.equals("()V")) { instrumentationStatsRecord.addSyncPrimitive(); mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "beforeNotifyAllWithoutMonitors", "(Ljava/lang/Object;)V"); return; } // Object.notify(): if (opcode == INVOKEVIRTUAL && owner.equals("java/lang/Object") && name.equals("notify") && desc.equals("()V")) { instrumentationStatsRecord.addSyncPrimitive(); mv.visitMethodInsn( INVOKESTATIC, "virtualtime/EventNotifier", "beforeNotifyWithoutMonitors", "(Ljava/lang/Object;)V"); return; } } } // Any I/O method: store invocation id before every I/O call of a profiled method if (owner.startsWith("java/io/") || (owner.startsWith("java/net/") && (name.equals("write") || name.equals("read")))) { instrumentationStatsRecord.addIoPoint(); mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;"); mv.visitIntInsn(SIPUSH, IoAdapter.getNextInvocationPointId()); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Thread", "setVtfIoInvocationPoint", "(I)V"); } // Adapting recursive call to self if (iparams.isRecursiveRetransformation() && iparams.usingExternalInstrumentation()) { if (owner.equals(className) && methodName.equals(iparams.getRetransformingMethodName()) && desc.equals(iparams.getRetransformingMethodDesc())) { if (DEBUG) { System.out.println( "Found recursive call " + owner + " --> " + name + ": " + desc + " (" + opcode + ")"); } name = "_vtfmethod_" + name; // if (!name.endsWith("init>")) { // name = "_vtfmethod_"+ name; // } else { // if (name.equals("<init>")) { // name = "_vtfmethod_constructor"; // } else { // name = "_vtfmethod_class_initialization"; // } // } } } if (iparams.shouldModifyTimeCalls()) { if ((opcode == INVOKESTATIC && owner.equals("java/lang/System") && name.equals("currentTimeMillis") && desc.equals("()J")) || (opcode == INVOKESTATIC && owner.equals("java/lang/System") && name.equals("nanoTime") && desc.equals("()J"))) { mv.visitMethodInsn(INVOKESTATIC, "virtualtime/EventNotifier", name, desc); return; } } mv.visitMethodInsn(opcode, owner, name, desc); }