diff --git a/org.intrace/build.xml b/org.intrace/build.xml index a7b351b..53698e6 100644 --- a/org.intrace/build.xml +++ b/org.intrace/build.xml @@ -423,6 +423,14 @@ + + + + + + + + diff --git a/org.intrace/src/org/intrace/agent/AgentSettings.java b/org.intrace/src/org/intrace/agent/AgentSettings.java index 645983b..1b57059 100644 --- a/org.intrace/src/org/intrace/agent/AgentSettings.java +++ b/org.intrace/src/org/intrace/agent/AgentSettings.java @@ -40,6 +40,7 @@ private InstrCriteria classesToExclude = null; private boolean instruEnabled = true; private boolean saveTracedClassfiles = false; private boolean verboseMode = false; + private boolean _instrumentImplementors = false; @@ -54,6 +55,7 @@ private InstrCriteria classesToExclude = null; instruEnabled = oldInstance.isInstrumentationEnabled(); saveTracedClassfiles = oldInstance.saveTracedClassfiles(); verboseMode = oldInstance.isVerboseMode(); + _instrumentImplementors = oldInstance.instrumentImplementors(); } public AgentSettings(String args) { @@ -138,8 +140,17 @@ public void parseArgs(String args) this.classesToExclude = new InstrCriteria(classExcludeRegexStr); this.classesToExclude.verboseLogger = this; } + else if (arg.toLowerCase(Locale.ROOT).equals( + AgentConfigConstants.INSTRUMENT_IMPLEMENTORS + + "true")) + { + this._instrumentImplementors = true; + } } + public boolean instrumentImplementors() { + return _instrumentImplementors; + } public boolean isWaitStart() { return waitStart; diff --git a/org.intrace/src/org/intrace/agent/ClassTransformer.java b/org.intrace/src/org/intrace/agent/ClassTransformer.java index 98eb80d..5882249 100644 --- a/org.intrace/src/org/intrace/agent/ClassTransformer.java +++ b/org.intrace/src/org/intrace/agent/ClassTransformer.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import org.intrace.agent.server.AgentClientConnection; import org.intrace.agent.server.AgentServer; @@ -51,11 +52,21 @@ public class ClassTransformer implements ClassFileTransformer * Set of modified class names */ private final Set modifiedClasses = new ConcurrentSkipListSet(); + /** + * Javadoc and stacktraces show that ConcurrentSkipListSet.size() takes up large amount of time. + * This variable will be used to store count. + */ + private final AtomicInteger modifiedClassesCount = new AtomicInteger(); /** * Map of all seen class names */ private final Set allClasses = new ConcurrentSkipListSet(); + /** + * Javadoc and stacktraces show that ConcurrentSkipListSet.size() takes up large amount of time. + * This variable will be used to store count. + */ + private final AtomicInteger allClassesCount = new AtomicInteger(); /** * Instrumentation interface. @@ -90,6 +101,8 @@ public class ClassTransformer implements ClassFileTransformer { TraceHandler.INSTANCE.writeTraceOutput("DEBUG: " + settings.toString()); } + this.allClassesCount.set(0); + this.modifiedClassesCount.set(0); } /** @@ -110,6 +123,7 @@ public class ClassTransformer implements ClassFileTransformer ClassAnalysis analysis = new ClassAnalysis(); cr.accept(analysis, 0); + //System.out.println("getInstrumentedClassBytes [" + xiClassName + "]"); InstrumentedClassWriter writer = new InstrumentedClassWriter(xiClassName, cr, analysis, shouldInstrument, @@ -152,11 +166,13 @@ public class ClassTransformer implements ClassFileTransformer ProtectionDomain protectionDomain, byte[] originalClassfile) { +// System.out.println("itbcfi [" + className + "] klass [" + klass.getName() + "] protectionDomain [" + protectionDomain + "]"); ComparableClassName compklass = new ComparableClassName(className, klassloader); // Record all class names which get this far allClasses.add(compklass); + allClassesCount.incrementAndGet(); // Don't modify anything if tracing is disabled if (!settings.isInstrumentationEnabled()) @@ -201,13 +217,15 @@ public class ClassTransformer implements ClassFileTransformer { // Actually we should modify if any of the interfaces match the regex boolean matchedInterface = false; - for(String klassInterface : getInterfaces(className, originalClassfile)) - { - if ((settings.getClassRegex() != null) - && matches(settings.getClassRegex(), klassInterface)) - { - matchedInterface |= true; - } + if (originalClassfile !=null && settings.instrumentImplementors() ) { + for(String klassInterface : getInterfaces(className, originalClassfile)) + { + if ((settings.getClassRegex() != null) + && matches(settings.getClassRegex(), klassInterface)) + { + matchedInterface |= true; + } + } } if(!matchedInterface) @@ -300,8 +318,8 @@ public class ClassTransformer implements ClassFileTransformer { String className = internalClassName.replace('/', '.'); ComparableClassName compclass = new ComparableClassName(className, loader); - int modifiedSize = modifiedClasses.size(); - int allClassesSize = allClasses.size(); + int modifiedSize = modifiedClassesCount.get(); + int allClassesSize = allClassesCount.get(); boolean shouldInstrument = isToBeConsideredForInstrumentation(classBeingRedefined, loader, className, protectionDomain, @@ -342,6 +360,7 @@ public class ClassTransformer implements ClassFileTransformer } modifiedClasses.add(compclass); + modifiedClassesCount.getAndIncrement(); sendStatusUpdate(modifiedSize, allClassesSize); @@ -350,6 +369,7 @@ public class ClassTransformer implements ClassFileTransformer else { modifiedClasses.remove(compclass); + modifiedClassesCount.decrementAndGet(); sendStatusUpdate(modifiedSize, allClassesSize); return null; @@ -409,13 +429,13 @@ public class ClassTransformer implements ClassFileTransformer try { StatusUpdate update = statusHolder.getStatus(); - int newModifiedSize = modifiedClasses.size(); - int newAllClassesSize = allClasses.size(); + int newModifiedSize = modifiedClassesCount.get(); + int newAllClassesSize = allClassesCount.get(); if (!bulkUpdateActive.get() && ((newModifiedSize != update.modifiedSize) || (newAllClassesSize != update.allClassesSize))) { - broadcastStatus(modifiedClasses.size(), allClasses.size()); + broadcastStatus(modifiedClassesCount.get(), allClassesCount.get()); } } catch (InterruptedException e) @@ -524,9 +544,9 @@ public class ClassTransformer implements ClassFileTransformer { Map settingsMap = settings.getSettingsMap(); settingsMap.put(AgentConfigConstants.STCLS, - Integer.toString(allClasses.size())); + Integer.toString(allClassesCount.get())); settingsMap.put(AgentConfigConstants.STINST, - Integer.toString(modifiedClasses.size())); + Integer.toString(modifiedClassesCount.get())); return settingsMap; } @@ -551,12 +571,12 @@ public class ClassTransformer implements ClassFileTransformer else if (oldSettings.isInstrumentationEnabled() != settings .isInstrumentationEnabled()) { -// System.out.println("## Settings Changed"); + //System.out.println("## Settings Changed"); setInstrumentationEnabled(settings.isInstrumentationEnabled()); } else if (!Arrays.equals(oldSettings.getClassRegex(), settings.getClassRegex())) { -// System.out.println("## Settings Changed"); + //System.out.println("## Settings Changed"); Set klasses = new HashSet(getModifiedClasses()); modifiedClasses.clear(); klasses.addAll(getLoadedClassesForModification()); @@ -564,7 +584,7 @@ public class ClassTransformer implements ClassFileTransformer } else if (!Arrays.equals(oldSettings.getExcludeClassRegex(), settings.getExcludeClassRegex())) { -// System.out.println("## Settings Changed"); + //System.out.println("## Settings Changed"); Set klasses = new HashSet(getModifiedClasses()); modifiedClasses.clear(); klasses.addAll(getLoadedClassesForModification()); @@ -573,7 +593,7 @@ public class ClassTransformer implements ClassFileTransformer else if (oldSettings.saveTracedClassfiles() != settings .saveTracedClassfiles()) { -// System.out.println("## Settings Changed"); + //System.out.println("## Settings Changed"); Set klasses = getModifiedClasses(); modifiedClasses.clear(); klasses.addAll(getLoadedClassesForModification()); @@ -656,7 +676,12 @@ public class ClassTransformer implements ClassFileTransformer + loadedClass.getCanonicalName()); } } - else + else if (settings.instrumentImplementors() || isToBeConsideredForInstrumentation( + loadedClass, + loadedClass.getClassLoader(), + loadedClass.getName(), + loadedClass.getProtectionDomain(), + null )) { ComparableClass loadedKlass = new ComparableClass(loadedClass); unmodifiedKlasses.add(loadedKlass); @@ -665,6 +690,18 @@ public class ClassTransformer implements ClassFileTransformer return unmodifiedKlasses; } +/* +The catch block in this method should have caught the following, but it did not. +Need to figure out why. +
+     [java] java.lang.VerifyError
+     [java]     at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
+     [java]     at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)
+     [java]     at org.intrace.agent.ClassTransformer.instrumentKlasses(ClassTransformer.java:719)
+     [java]     at org.intrace.agent.ClassTransformer.getResponse(ClassTransformer.java:584)
+     [java]     at org.intrace.agent.server.AgentClientConnection.runMethod(AgentClientConnection.java:118)
+
+*/ public void instrumentKlasses(Set klasses) { if (!inst.isRetransformClassesSupported()) @@ -692,10 +729,10 @@ public class ClassTransformer implements ClassFileTransformer inst.retransformClasses(klass.klass); countNumClasses++; - if ((countNumClasses % 100) == 0) - { +// if ((countNumClasses % 100) == 0) +// { broadcastProgress(countNumClasses, totalNumClasses); - } +// } } catch (Throwable e) { @@ -711,7 +748,7 @@ public class ClassTransformer implements ClassFileTransformer finally { bulkUpdateActive.set(false); - broadcastStatus(modifiedClasses.size(), allClasses.size()); + broadcastStatus(modifiedClasses.size(), allClassesCount.get()); } } diff --git a/org.intrace/src/org/intrace/agent/InstrumentedClassWriter.java b/org.intrace/src/org/intrace/agent/InstrumentedClassWriter.java index c707020..e1cff72 100644 --- a/org.intrace/src/org/intrace/agent/InstrumentedClassWriter.java +++ b/org.intrace/src/org/intrace/agent/InstrumentedClassWriter.java @@ -79,6 +79,7 @@ public class InstrumentedClassWriter extends ClassWriter sb.append(InstrCriteria.CLASS_METHOD_DELIMITER); sb.append(name); sb.append(desc); + //System.out.println("@#%:" + sb.toString() ); TraceHandler.INSTANCE.writeTraceOutput("DEBUG: method signature: " + sb.toString()); } MethodVisitor mv = super.visitMethod(access, name, desc, signature, diff --git a/org.intrace/src/org/intrace/output/trace/TraceHandler.java b/org.intrace/src/org/intrace/output/trace/TraceHandler.java index d6f2393..88ed9ae 100644 --- a/org.intrace/src/org/intrace/output/trace/TraceHandler.java +++ b/org.intrace/src/org/intrace/output/trace/TraceHandler.java @@ -472,7 +472,7 @@ private static final Object STACK_ELE_DELIM = ","; + "]:" + xiOutput; // if (AgentHelper.getOutputSettings().isStdoutOutputEnabled()) // { -//// System.out.println(traceString); +// System.out.println(traceString); // } if (AgentHelper.getOutputSettings().isFileOutputEnabled()) diff --git a/org.intrace/src/org/intrace/shared/AgentConfigConstants.java b/org.intrace/src/org/intrace/shared/AgentConfigConstants.java index d4826bc..a788176 100644 --- a/org.intrace/src/org/intrace/shared/AgentConfigConstants.java +++ b/org.intrace/src/org/intrace/shared/AgentConfigConstants.java @@ -24,6 +24,7 @@ public class AgentConfigConstants public static final String OPT_SERVER_PORT = "[serverport-"; public static final String CALLBACK_PORT = "[callbackport-"; public static final String EXIT_STACK_TRACE = "[exit-stack-trace-"; + public static final String INSTRUMENT_IMPLEMENTORS = "[instrument-implementors"; public static final String START_WAIT = "[startwait"; public static final String START_ACTIVATE = "[startactivate"; @@ -39,5 +40,6 @@ public class AgentConfigConstants COMMANDS.add(OPT_SERVER_PORT + ""); COMMANDS.add(CALLBACK_PORT + ""); COMMANDS.add(EXIT_STACK_TRACE + ""); + COMMANDS.add(INSTRUMENT_IMPLEMENTORS + ""); } } diff --git a/org.intrace/testsrc/example/InstrumentMe.java b/org.intrace/testsrc/example/InstrumentMe.java new file mode 100644 index 0000000..3b33597 --- /dev/null +++ b/org.intrace/testsrc/example/InstrumentMe.java @@ -0,0 +1,20 @@ +package example; + +import java.util.Arrays; + +public class InstrumentMe implements Runnable +{ + private byte myField = 1; + + @Override + public void run() + { + byteArg((byte) 0); + } + + private void byteArg(byte arg) + { + this.myField = arg; + } + +} diff --git a/org.intrace/testsrc/example/InstrumentMeLaunch.java b/org.intrace/testsrc/example/InstrumentMeLaunch.java new file mode 100644 index 0000000..a911c49 --- /dev/null +++ b/org.intrace/testsrc/example/InstrumentMeLaunch.java @@ -0,0 +1,27 @@ +package example; + +public class InstrumentMeLaunch +{ + public static void main(String args[]) throws Exception { + System.out.println("Currently running sample code in background thread."); + System.out.println("Make sure this program has -javaagent:./path/to/intrace-agent.jar on its command line."); + System.out.println("Start InTrace GUI, download-able at https://mchr3k.github.io/org.intrace/"); + System.out.println("'Connect' & then configure GUI to trace any of these patterns:\n\n"); + System.out.println("example.InstrumentMe -- to trace all methods in class example.InstrumentMe"); + System.out.println("example.InstrumentMe -- to trace all methods in class example.InstrumentMeLaunch"); + System.out.println("example.InstrumentMe#()V -- to trace this single method."); + System.out.println("example.InstrumentMe#run()V -- to trace this single method."); + System.out.println("example.InstrumentMe#byteArg(B)V -- to trace this single method."); + System.out.println("example.InstrumentMeLaunch#()V -- to trace this single method."); + System.out.println("example.InstrumentMeLaunch#main([Ljava/lang/String;)V -- to trace this single method.\n\n"); + + System.out.println("Press Ctrl+C to quit this program."); + Runnable r = new InstrumentMe(); + while(true) { + System.out.println("About to start thread."); + new Thread(r).start(); + Thread.sleep(2000); + + } + } +} diff --git a/org.intrace/testsrc/org/intracetest/agent/AgentSettingsTest.java b/org.intrace/testsrc/org/intracetest/agent/AgentSettingsTest.java index 420e77e..bc8e8aa 100644 --- a/org.intrace/testsrc/org/intracetest/agent/AgentSettingsTest.java +++ b/org.intrace/testsrc/org/intracetest/agent/AgentSettingsTest.java @@ -64,6 +64,25 @@ public class AgentSettingsTest extends TestCase assertEquals(settingsMap.get(AgentConfigConstants.VERBOSE_MODE), "true"); } + public void testInstrumentImplementorsSetting() { + AgentSettings as1 = new AgentSettings( + AgentConfigConstants.INSTRUMENT_IMPLEMENTORS + + "true" + ); + assertTrue( "unable to recognize parameter that instruments implementors of interfaces", as1.instrumentImplementors() ); + + AgentSettings as2 = new AgentSettings( + AgentConfigConstants.INSTRUMENT_IMPLEMENTORS + + "false" + ); + assertFalse( "unable to recognize parameter that instruments implementors of interfaces",as2.instrumentImplementors() ); + + AgentSettings as3 = new AgentSettings(""); + assertFalse( "unable to recognize parameter that instruments implementors of interfaces", as3.instrumentImplementors() ); + + + } + public void testInstrCriteria() { InstrCriteria ic = new InstrCriteria("foo|bar");