diff --git a/.gitignore b/.gitignore index 44e1417..348dae0 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ hs_err_pid* /.classpath /build/* /bin/ +/.gradle/ *.gradle/ /gradle.properties /site/ diff --git a/.gradle/2.10/taskArtifacts/cache.properties b/.gradle/2.10/taskArtifacts/cache.properties deleted file mode 100644 index 079d953..0000000 --- a/.gradle/2.10/taskArtifacts/cache.properties +++ /dev/null @@ -1 +0,0 @@ -#Mon Jun 06 21:35:20 EDT 2016 diff --git a/.gradle/2.10/taskArtifacts/cache.properties.lock b/.gradle/2.10/taskArtifacts/cache.properties.lock deleted file mode 100644 index 5138db6..0000000 Binary files a/.gradle/2.10/taskArtifacts/cache.properties.lock and /dev/null differ diff --git a/.gradle/2.10/taskArtifacts/fileHashes.bin b/.gradle/2.10/taskArtifacts/fileHashes.bin deleted file mode 100644 index 7672956..0000000 Binary files a/.gradle/2.10/taskArtifacts/fileHashes.bin and /dev/null differ diff --git a/.gradle/2.10/taskArtifacts/fileSnapshots.bin b/.gradle/2.10/taskArtifacts/fileSnapshots.bin deleted file mode 100644 index 88e3f7e..0000000 Binary files a/.gradle/2.10/taskArtifacts/fileSnapshots.bin and /dev/null differ diff --git a/.gradle/2.10/taskArtifacts/outputFileStates.bin b/.gradle/2.10/taskArtifacts/outputFileStates.bin deleted file mode 100644 index a9b7cce..0000000 Binary files a/.gradle/2.10/taskArtifacts/outputFileStates.bin and /dev/null differ diff --git a/.gradle/2.10/taskArtifacts/taskArtifacts.bin b/.gradle/2.10/taskArtifacts/taskArtifacts.bin deleted file mode 100644 index 7e29a03..0000000 Binary files a/.gradle/2.10/taskArtifacts/taskArtifacts.bin and /dev/null differ diff --git a/README.md b/README.md index a8d538f..80916fe 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,17 @@ Thank you for downloading the Omniscient Debugger (the ODB), the Java debugger t "backwards in time" so you can examine your program's execution. Also please visit the project [website](http://omniscientdebugger.github.io/) +# Provenance + +The Omniscient Debugger was originally written by Bil Lewis. This repository +continues that work under the LewisODB organization. + +This codebase was restarted from the community-maintained +[OmniscientDebugger/LewisOmniscientDebugger](https://github.com/OmniscientDebugger/LewisOmniscientDebugger) +repository, which preserved and modernized the project after the original +distribution. We gratefully acknowledge that work and the contributors whose +history is preserved in Git. + # System Requirements ODB 1.4 works on code complied for JDK 1.3 and 1.4. ODB 1.5 works on code compiled for JDK 1.5. I have tested it on 1.6 and it works. Code compiled for Java 7 and up use invokedetatch, which seems to cause problems at the moment. See [Issue](https://github.com/OmniscientDebugger/LewisOmniscientDebugger/issues/1): The Jar's compiled for 1.6 and older do work on the 1.8 JVM. @@ -70,7 +81,7 @@ mkdocs build --strict # Development Here is how to build and run the debug the debugger step ``` -git clone https://github.com/OmniscientDebugger/LewisOmniscientDebugger.git +git clone https://github.com/LewisODB/OmniscientDebugger.git gradle jar diff --git a/docs/user-manual/index.md b/docs/user-manual/index.md index 38c7dcf..1285646 100644 --- a/docs/user-manual/index.md +++ b/docs/user-manual/index.md @@ -289,8 +289,8 @@ Here is a sample. All of the lines may have duplicate entires (e.g., the DontIns StartPattern: StopPattern: SourceDirectory: - OnlyInstrument: "org.apache" - OnlyInstrument: "fr.emn.info.eaop.cps" + OnlyInstrument: "org.apache." + OnlyInstrument: "fr.emn.info.eaop.cps." DontInstrument: "recoder.java.declaration.TypeDeclaration" DontInstrumentMethod: "* toString" DontInstrumentMethod: "* valueOf" @@ -303,7 +303,7 @@ Here is a sample. All of the lines may have duplicate entires (e.g., the DontIns You may request some objects to include a field value in their print string by setting the user selected field as above. The field choosen must be a primitive and must not change. -The ODB will write out a "`DidntInstrument/OnlyInstrument`" line for every single class loaded. This makes it easier to change the set of classes being instrumented. By default, only the classes in the package of the "main" method are instrumented. +The ODB will write out a "`DidntInstrument/OnlyInstrument`" line for every single class loaded. This makes it easier to change the set of classes being instrumented. By default, only the classes in the package of the "main" method are instrumented. A trailing dot means a package prefix, such as `"java_programs."`; package-only lists let the runtime loader delegate non-matching classes to the parent loader. ## Finding the Source Code diff --git a/src/main/java/com/lambda/Debugger/Debugify.java b/src/main/java/com/lambda/Debugger/Debugify.java index 49764ce..4155d71 100644 --- a/src/main/java/com/lambda/Debugger/Debugify.java +++ b/src/main/java/com/lambda/Debugger/Debugify.java @@ -166,7 +166,7 @@ public final class Debugify implements Constants { NO_ATHROW = false, NO_CATCH = false, NO_INVOKESTATIC = false, NO_NEW = false, NO_AASTORE = false, NO_PREVIOUS = false, NO_LOCKS = false, NO_WAITS = false, DONT_REPLACE_VECTOR = false; - static VectorD dontRecord, dontInstrument, instrumentOnlyPackages; + static VectorD dontRecord, dontInstrument; static CodeExceptionGen[] ceg; static String classPackageName, className, classNoNumbers; @@ -291,7 +291,6 @@ public static void initialize() { // Read the file for methods not to record dontInstrument = Defaults.dontInstrument; // Read the file for methods not to instrument - instrumentOnlyPackages = Defaults.instrumentOnlyPackages; } public static JavaClass debugifyClass(JavaClass javaClass1, @@ -569,24 +568,7 @@ public static JavaClass publicifyClass(JavaClass javaClass1, static boolean dontProcessPackage(String cName) { if (calledFromDebugify) return false; // If this is a direct request, do it! - int len = instrumentOnlyPackages.size(); - if (len == 0) - return false; - - for (int i = 0; i < len; i++) { - String iOnly = (String) instrumentOnlyPackages.elementAt(i); - // Debugger.println("only methods: " + cName + "."+ "? startswith " - // + iOnly); - if (iOnly.equals("")) { - int dot = cName.indexOf("."); - if (dot == -1) - return false; - } else if (cName.startsWith(iOnly)) - return false; - } - if (!Defaults.didntInstrument.contains(cName)) - Defaults.didntInstrument.add(cName); - return true; + return Defaults.instrumentOnlyExcludes(cName, false); } static boolean dontProcessMethod(VectorD cmPairs, String mName, diff --git a/src/main/java/com/lambda/Debugger/DebugifyingClassLoader.java b/src/main/java/com/lambda/Debugger/DebugifyingClassLoader.java index 4562478..16fbd37 100644 --- a/src/main/java/com/lambda/Debugger/DebugifyingClassLoader.java +++ b/src/main/java/com/lambda/Debugger/DebugifyingClassLoader.java @@ -41,6 +41,9 @@ public class DebugifyingClassLoader extends java.lang.ClassLoader { dontInstrument.add("java."); dontInstrument.add("sun."); dontInstrument.add("apple."); + dontInstrument.add("com.apple."); + dontInstrument.add("com.sun."); + dontInstrument.add("jdk."); dontInstrument.add("javax."); dontInstrument.add("JAVAX."); dontInstrument.add("org.apache.bcel"); @@ -97,8 +100,7 @@ protected Class loadClass(String className, boolean resolve) if (clazz != null) return clazz; - if ((!className.startsWith("javax.xml.")) - && (dontInstrument(className) || (!Debugger.INSTRUMENT))) { + if (shouldDelegateToParent(className)) { clazz = getParent().loadClass(className); if (Debugger.TRACE_LOADER) println("loaded via parent: " + getParent() + " " + className); @@ -132,6 +134,14 @@ protected Class loadClass(String className, boolean resolve) return clazz; } + private boolean shouldDelegateToParent(String className) { + if (className.startsWith("javax.xml.")) + return false; + + return dontInstrument(className) || (!Debugger.INSTRUMENT) + || Defaults.instrumentOnlyExcludes(className, true); + } + public static byte[] debugify(String className, byte[] bytes) { if (!Debugger.INSTRUMENT) return bytes; diff --git a/src/main/java/com/lambda/Debugger/Defaults.java b/src/main/java/com/lambda/Debugger/Defaults.java index 2f95c45..8f85538 100644 --- a/src/main/java/com/lambda/Debugger/Defaults.java +++ b/src/main/java/com/lambda/Debugger/Defaults.java @@ -179,6 +179,35 @@ static private void setStopOutput(String value){ // "Stop recording when the pr static private void addInstrumentOnly(String value){ // "com.foo" instrumentOnlyPackages.add(getString(value)); } + static boolean instrumentOnlyExcludes(String cName, boolean packagePrefixesOnly) { + if (packagePrefixesOnly && !instrumentOnlyEntriesArePackagePrefixes()) + return false; + + int len = instrumentOnlyPackages.size(); + if (len == 0) + return false; + + for (int i = 0; i < len; i++) { + String iOnly = (String) instrumentOnlyPackages.elementAt(i); + if (iOnly.equals("")) { + if (cName.indexOf(".") == -1) + return false; + } else if (cName.startsWith(iOnly)) + return false; + } + if (!didntInstrument.contains(cName)) + didntInstrument.add(cName); + return true; + } + static private boolean instrumentOnlyEntriesArePackagePrefixes() { + int len = instrumentOnlyPackages.size(); + for (int i = 0; i < len; i++) { + String iOnly = (String) instrumentOnlyPackages.elementAt(i); + if (!(iOnly.equals("") || iOnly.endsWith("."))) + return false; + } + return true; + } static private void addDidntInstrument(String value){ // "com.foo.UnparentedToo" didntInstrument.add(getString(value)); } @@ -301,6 +330,7 @@ static public void writeDefaults(){ w.write("# SourceDirectory: If sources can't be found normally, look here.\n"); w.write("# OnlyInstrument: Only classes which match this prefix will be instrumented.\n"); w.write("# OnlyInstrument: \"\" means default package only. No entry means everything.\n"); + w.write("# OnlyInstrument: Trailing-dot package prefixes let the runtime loader delegate non-matches.\n"); w.write("# UserSelectedField: This instance variable (a final String) will be appended to the display string\n"); w.write("# UserSelectedField: \"com.lambda.Thing name\" -> \n"); w.write("# SpecialFormatter: com.lambda.Debugger.SpecialTimeStampFormatter\n"); diff --git a/src/test/java/com/lambda/Debugger/DebugifyingClassLoaderTest.java b/src/test/java/com/lambda/Debugger/DebugifyingClassLoaderTest.java new file mode 100644 index 0000000..123321a --- /dev/null +++ b/src/test/java/com/lambda/Debugger/DebugifyingClassLoaderTest.java @@ -0,0 +1,77 @@ +package com.lambda.Debugger; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class DebugifyingClassLoaderTest { + private VectorD originalInstrumentOnlyPackages; + private VectorD originalDidntInstrument; + + @Before + public void saveDefaults() { + originalInstrumentOnlyPackages = Defaults.instrumentOnlyPackages; + originalDidntInstrument = Defaults.didntInstrument; + Defaults.instrumentOnlyPackages = new VectorD(); + Defaults.didntInstrument = new VectorD(); + } + + @After + public void restoreDefaults() { + Defaults.instrumentOnlyPackages = originalInstrumentOnlyPackages; + Defaults.didntInstrument = originalDidntInstrument; + } + + @Test + public void emptyOnlyInstrumentKeepsLegacyClassloaderBehavior() { + assertFalse(Defaults.instrumentOnlyExcludes("com.apple.laf.AquaLookAndFeel", true)); + } + + @Test + public void packageOnlyInstrumentDelegatesClassesOutsidePackage() { + Defaults.instrumentOnlyPackages.add("java_programs."); + + assertFalse(Defaults.instrumentOnlyExcludes("java_programs.GCDRunner", true)); + assertFalse(Defaults.instrumentOnlyExcludes("java_programs.extra.NESTED_PARENS", true)); + assertTrue(Defaults.instrumentOnlyExcludes("com.apple.laf.AquaLookAndFeel", true)); + assertTrue(Defaults.didntInstrument.contains("com.apple.laf.AquaLookAndFeel")); + } + + @Test + public void defaultPackageOnlyInstrumentDelegatesNamedPackages() { + Defaults.instrumentOnlyPackages.add(""); + + assertFalse(Defaults.instrumentOnlyExcludes("Main", true)); + assertTrue(Defaults.instrumentOnlyExcludes("helpers.Main", true)); + } + + @Test + public void classPrefixOnlyInstrumentKeepsPublicifyBehavior() { + Defaults.instrumentOnlyPackages.add("java_programs.GCD"); + + assertFalse(Defaults.instrumentOnlyExcludes("java_programs.Helper", true)); + assertFalse(Defaults.instrumentOnlyExcludes("other.Helper", true)); + assertTrue(Defaults.instrumentOnlyExcludes("other.Helper", false)); + } + + @Test + public void mixedOnlyInstrumentKeepsPublicifyBehavior() { + Defaults.instrumentOnlyPackages.add("java_programs."); + Defaults.instrumentOnlyPackages.add("other.Specific"); + + assertFalse(Defaults.instrumentOnlyExcludes("helpers.Main", true)); + } + + @Test + public void platformVendorClassesLoadThroughParent() throws Exception { + DebugifyingClassLoader loader = new DebugifyingClassLoader(); + + Class clazz = loader.loadClass("com.sun.java.swing.plaf.motif.MotifLookAndFeel"); + + assertNotSame(loader, clazz.getClassLoader()); + } +}