Skip to content

Commit

Permalink
Version 0.6.0: Switched from maven to gradle, added jsmud-analysis-all
Browse files Browse the repository at this point in the history
which includes a shadowed ASM.
  • Loading branch information
srogmann committed Feb 27, 2022
1 parent 4e8683f commit a4e96e3
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 5 deletions.
68 changes: 64 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ The module-system in newer JVMs doesn't allow reflection in internal JVM-classes
### Classes defined at runtime
When ClassLoader#defineClass is called by an application jsmud-analysis stores the corresponding bytecode in an internal map to support analyzing the execution of classes defined at runtime.

The analysis of classes defined at runtime can be a bit tricky because often reflection and static initializers are involved, too.
The analysis of classes defined at runtime can be a bit tricky because often reflection and static initializers are involved, too. For example the usage of toString() when displaying objects in local variables or the stack can lead to side-effects. I switched to IdentityHashMap at one place to avoid calling hashCode() which muddled a class generated by CGLIB.

#### Example CGLIB
This paragraphs shows an example of analyzing CGLIB.
Expand Down Expand Up @@ -254,16 +254,75 @@ Some instructions at the execution of GetLength:

Depending of the application examined it may be necessary to filter some classes to prevent them from being analyzed (e.g. logging-calls or classes which can't be analyzed).

To debug the bytecode defined at runtime you can use the class SourceFilesLocalDirectory:

public class JsmudCglibDebug {
public static void main(String[] args) {
final Supplier<Void> supplier = new Supplier<Void>() {
@Override
public Void get() {
CglibTest.main(null);
return null;
}
};
final String packageApplication = Utils.getPackage(CglibTest.class.getName());

final Class<?> classSupplier = supplier.getClass();
final ClassLoader clSupplier = classSupplier.getClassLoader();

final ClassExecutionFilter filter = JvmHelper.createNonJavaExecutionFilter();
// Analyze static initializers of CGLIB (net.sf.cglib.*) and the application.
Predicate<String> patchFilter = (c -> c.startsWith("net.sf.")
|| (c.startsWith(packageApplication) && !c.startsWith("org.rogmann.jsmud.tests.cglib.JsmudCglibDebug"))
|| c.startsWith("$"));
boolean patchClinit = true;
boolean patchInit = false;
boolean redefClasses = true;
final JsmudConfiguration config = new JsmudConfiguration();
final JsmudClassLoader jcl = new JsmudClassLoader(clSupplier, config, patchFilter, patchClinit, patchInit, redefClasses);
final Predicate<Class<?>> sfFilter = (c -> (c.getName().startsWith(packageApplication) && c.getName().contains("$")
&& !c.getName().startsWith("org.rogmann.jsmud.tests.cglib.JsmudCglibDebug"))
|| c.getName().startsWith("net.sf.cglib")
);
final File dirDest = new File("/tmp/dir-bytecode");
final SourceFileRequester sfr = new SourceFilesLocalDirectory(sfFilter, dirDest, "java", StandardCharsets.UTF_8, "\n");
final DebuggerJvmVisitor visitor = JvmHelper.createDebuggerVisitor(filter, jcl, sfr);
JvmHelper.connectSupplierToDebugger(visitor, "127.0.0.1", 16000, supplier, Void.class);
}
}

In the debugging JVM you need to access or include the source-folder (/tmp/dir-bytecode) which is generated at runtime (except mkdir /tmp/dir-bytecode).

Example of JVM-options:

--add-opens java.base/java.lang.reflect=ALL-UNNAMED
-Djsmud.FolderDumpJsmudPatchedBytecode=/tmp/dir-patched
-Djsmud.FolderDumpDefinedClasses=/tmp/dir-defined

## Build and Test
Jsmud-analysis is compiled with Java 8 and ASM, see pom.xml. The execution of Java 11 or Java 17 classes is possible. There are a lot of tests in test-class JvmTests. JUnit-tests can be started with test-class JvmTestsJUnit which executes test-class JvmTests. The junit-tests are generated with test-class GenerateJUnitTests.
Jsmud-analysis is compiled with Java 8 and ASM, see pom.xml. The execution of Java 11 or Java 17 classes is possible. There are a lot of tests in test-class JvmTests. JUnit-tests can be started with test-class JvmTestsJUnit which executes test-class JvmTests but require JRE 8. The junit-tests are generated with test-class GenerateJUnitTests.

To build a jar with a shadowed asm you can use:

gradle -PapplyJsmudShadow=true clean build

This will shadow ASM into the package org.rogmann.jsmud.shadow.asm and include the shadowed sources. I discarded my first try using johnrengelman/shadow because of missing dependency sources.

## Download
GAV in Maven Central Repository:
GAV in Maven Central Repository with ASM included in an internal package to avoid conflicts with an ASM already included:

<dependency>
<groupId>org.rogmann.jsmud</groupId>
<artifactId>jsmud-analysis-all</artifactId>
<version>0.6.0</version>
</dependency>

GAV in Maven Central Repository without included ASM (dependencies are in pom.xml):

<dependency>
<groupId>org.rogmann.jsmud</groupId>
<artifactId>jsmud-analysis</artifactId>
<version>0.5.1</version>
<version>0.6.0</version>
</dependency>

A compiled and signed Jar of jsmud-analysis is also available at <a href="http://www.rogmann.org/releases/">http://www.rogmann.org/releases/</a>.
Expand All @@ -276,6 +335,7 @@ I wrote this project in my free time and I like my free time so support is given
* [ASM, the all purpose Java bytecode manipulation and analysis framework](https://asm.ow2.io/)

## Changelog
* V 0.6.0, 2022-02-27: Switched from maven to gradle, added jsmud-analysis-all which includes a shadowed ASM.
* V 0.5.2, 2022-02-12: Register bytecode of patched classes, bugfix INVOKEDYNAMIC (array-args), added stacktrace-elements, execute static initializer at (NEW, GETSTATIC, PUTSTATIC or INVOKESTATIC), simulation of Class#newInstance, bugfix STEP-events, bugfix invalid return-type, debugging of static initializer of dynamic generated class.
* V 0.5.1, 2022-01-16: Use sun.reflect.ReflectionFactory to load a class without constructor-execution, support FutureTask, bugfixes (GETFIELD, LDC, INVOKEDYNAMIC, ...), analyze classes defined at runtime.
* V 0.5.0, 2021-12-25: Added a (pseudo-code)-decompiler to support source-line-aligned debugging of classes without source-code (the decompiler isn't full-fledged, it still displays to GOTO-instructions). Handling of exceptions. Support of super-static.
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/rogmann/jsmud/vm/ClassRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public class ClassRegistry implements VM, ObjectMonitor {
private static final Logger LOG = LoggerFactory.getLogger(ClassRegistry.class);

/** version */
public static String VERSION = "jsmud 0.5.2 (2022-02-12)";
public static String VERSION = "jsmud 0.6.0 (2022-02-27)";

/** maximal wait-time in a monitor (this would be infinity in a read JVM) */
private static final AtomicInteger MONITOR_MAX_MILLIS = new AtomicInteger(60000);
Expand Down

0 comments on commit a4e96e3

Please sign in to comment.