/** * Code to be instrumented when a method is exited. */ private void onMethodExitInstrumentation() { if (!iparams.usingExternalInstrumentation() || methodName.endsWith("init>")) { if (iparams.methodShouldBeAdapted(methodId)) { if (isMethodDescribedByPerformanceModel()) { if (!shouldMethodBodyBeRemoved()) { // Only the case that real code is executed under performance model time will call this BasicBlockAdapter.insertMethodInstrument(mv, methodId, "_exitPerfModel"); } else { BasicBlockAdapter.insertMethodInstrument(mv, methodId, "_exitPerfModel"); } } else { BasicBlockAdapter.insertMethodInstrument( mv, methodId, BasicBlockAdapter.vtfMethodExitCallback); } } } }
/** * Code to be instrumented when a method is entered. */ private void onMethodEntryInstrumentation() { if (!iparams.usingExternalInstrumentation() || methodName.endsWith("init>")) { if (iparams.methodShouldBeAdapted(methodId)) { if (isMethodDescribedByPerformanceModel()) { if (!shouldMethodBodyBeRemoved()) { // The method body will be executed as normal, but the performance impact will only // depend on the provided model // This is used to simulate the behaviour, but still get the correct behaviour BasicBlockAdapter.insertMethodInstrument(mv, methodId, "_enterPerfModel"); } else { // No method behaviour, just performance cost added by the model BasicBlockAdapter.insertMethodInstrument(mv, methodId, "_enterPerfModel"); } } else { BasicBlockAdapter.insertMethodInstrument( mv, methodId, BasicBlockAdapter.vtfMethodEntryCallback); } } } }
/** * Code to be instrumented when an interaction point is encountered. */ private void onInteractionPointEncounter() { if (iparams.trappingInteractionPoints()) { mv.visitMethodInsn(INVOKESTATIC, "virtualtime/EventNotifier", "_interactionPoint", "()V"); } }
/** * * 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); }
/** * * Constructor setting the parameters of the method * * @param arg0 the method visitor object to write the new code * @param _methodId the unique JINE id of the method * @param isProfiled should we add instrumentation code to profile the method * @param access access rules of the method * @param cName class name * @param mName method name * @param desc arguments description * @param signature signature * @param exceptions array of strings with the possibly thrown exception classes * @param _iparams instrumentation parameters as selected by the user options */ public BasicBlockMethodAdapter( MethodVisitor arg0, int _methodId, boolean isProfiled, int access, String cName, String mName, String desc, String signature, String[] exceptions, InstrumentationParameters _iparams, MethodAdaptationInfo _methodAdaptationInfo) { super(arg0); instrumentationStatsRecord = InstrumentationRecordFactory.getRecord(); allStartingTryCatchingBlockLabels = null; allEndingTryCatchingBlockLabels = null; tryCatchDepth = 0; iparams = _iparams; methodAdaptationInfo = _methodAdaptationInfo; methodId = _methodId; className = cName; methodName = mName; methodDesc = desc; isSynchronised = (access & ACC_SYNCHRONIZED) != 0; isStatic = (access & ACC_STATIC) != 0; profile = isProfiled && !(isSynchronised && (iparams.trappingInteractionPoints() || (iparams .usingExternalInstrumentation()))); // sync methods are profiled externally // when IPs are trapped // Some methods if (iparams.exceptionHandlingEverywhere()) { instrumentExceptionHandlingCode = true; } else { instrumentExceptionHandlingCode = false; } // Register the methodId to the VTF if (profile) { instrumentationStatsRecord.setProfiled(); if (!iparams.isRetransformingInstrumentation()) { EventNotifier.registerMethod( cName + " " + mName + " " + desc, methodId, _iparams.getMethod0()); } else { instrumentationStatsRecord.setRetransformed(); } shouldBeWrappedWithVexOptimizerLoop = iparams.shouldBeWrappedWithVexOptimizerLoop(mName); // wrap the first appearance of main if (shouldBeWrappedWithVexOptimizerLoop) { System.out.println("will wrap vex optimizer " + cName + " " + mName + " " + desc); } if (iparams.methodShouldBeAdapted(methodId)) { if (exceptions != null && exceptions.length > 0) { instrumentExceptionHandlingCode = true; } } } if (DEBUG) { System.out.println("Instrumenting " + cName + " -> " + mName + "(" + methodId + ")" + desc); } performanceModelParameters = null; }