TracingClassLoader

A ClassLoader that instruments classes on load-time with tracing output statements.

Download sources for version 2.0, upgraded to work with Jakarta BCEL 20050813 (older version used


This is a ClassLoader that can add trace output around methods of classes without modifying the classes on disk. It is one of a series of experiments I intend to perform in the field of AOP.

The ClassLoader is initialized with a ClassMethodFilter, which is queried to determine which classes and methods list should be instrumented. In any case, the ClassLoader will not try to redefine classes in packages starting with java.* (this is not possible), nor will it instrument constructors (because the way I add instrumentation will not work with constructors, which are validated with special rules).

The instrumentation is really basic for now. While entering a method it outputs:

``thread''``method name''(``arguments'')
or
``thread''``this''.``method name''(``arguments'')
depending on whether it's a static method or not, and while exiting it displays
``thread''``method name''==>``return value''
or
``thread''``method name''==>(void)
for void methods. In case an exception is thrown, it outputs
``thread''``method name''==>threw exception ``exception''

This is sample output from a small run:

[dsouflis@localhost TracingClassLoader]java -classpath bcel.jar:aopclsuite.jar:. name.dsouflis.Trace test.TestTarget test.Main Will trace test.TestTarget Tracing class test.TestTarget Thread[Thread-0,5,main]test.TestTarget@546e25.foo(3,test.TestTarget@546e25) Thread[Thread-0,5,main]test.TestTarget@546e25.bar() Thread[Thread-0,5,main]bar=>4 Thread[Thread-0,5,main]foo=>(void) Thread[Thread-0,5,main]test.TestTarget@546e25.bar() Thread[Thread-0,5,main]bar=>18

The utility name.dsouflis.aop.tracing.Trace is an application runner that uses a TracingClassLoader to run a class. It takes two arguments, the first being a comma-separated list of classes to trace, and the second being the class to run. It is used like this:

java name.dsouflis.aop.tracing.JavaWrapper ``class to instrument,...'' ``class to run'' ``arguments...''

The utility name.dsouflis.aop.tracing.AddTracing is a utility that uses internal methods of TracingClassLoader to instrument a class. The class image on disk is modified, and one can inspect it with javap or BCEL's listclass. If you run it more than once, or trace the resulting class, things will probably look very messy. It is used like this:

java name.dsouflis.aop.tracing.AddTracing ``class to instrument''

The supplied aopclsuite.jar and bcel.jar must be in the CLASSPATH.

How the instrumentation works

All methods that are instrumented have their names changed to "hidden$name", and new methods are created to take their place. The new methods call the hidden ones and take care of the output before and after the call. As an example, consider the following class:

/* * TestTarget.java * * Created on April 2, 2002, 9:33 PM */ package test; /** * * @author dsouflis * @version */ public class TracingTarget extends Object { int x; /** Creates new TestTarget */ public TracingTarget() { x=2; } public void foo(long y, TracingTarget t) { t.x=(int)(y+x)+bar(); } public int bar() { return x*2; } public static void main(String[] args) { TracingTarget t=new TracingTarget(); t.foo(3,t); t.bar(); t.foo(4,t); } } The instrumentation added is marked in red, and existing stuff that was modified is marked in blue: Compiled from TestTarget.java public class test.TestTarget extends java.lang.Object { int x; public test.TestTarget(); public void foo(long, test.TestTarget); public int bar(); public static void main(java.lang.String[]); } Method test.TestTarget() 0 nop 1 aload_0 2 invokespecial #11 <Method java.lang.Object()> 5 aload_0 6 iconst_2 7 putfield #13 <Field int x> 10 return Method void hidden$foo(long, test.TracingTarget) 0 nop 0 nop 1 aload_3 2 lload_1 3 aload_0 4 getfield #13 <Field int x> 7 i2l 8 ladd 9 l2i 10 aload_0 11 invokevirtual #23 <Method int bar()> 14 iadd 15 putfield #13 <Field int x> 18 lload_1 19 ldc2_w #24 <Long 4> 22 lcmp 23 ifne 36 26 new #27 <Class java.lang.Error> 29 dup 30 ldc #29 <String "aaa"> 32 invokespecial #32 <Method java.lang.Error(java.lang.String)> 35 athrow 36 return Method int hidden$bar() 0 nop 0 nop 1 aload_0 2 getfield #13 <Field int x> 5 iconst_2 6 imul 7 ireturn Method void hidden$main(java.lang.String[]) 0 nop 0 nop 1 new #2 <Class test.TestTarget> 4 dup 5 invokespecial #38 <Method test.TestTarget()> 8 astore_1 9 aload_1 10 ldc2_w #39 <Long 3> 13 aload_1 14 invokevirtual #42 <Method void foo(long, test.TestTarget)> 17 aload_1 18 invokevirtual #23 <Method int bar()> 21 pop 22 aload_1 23 ldc2_w #24 <Long 4> 26 aload_1 27 invokevirtual #42 <Method void foo(long, test.TestTarget)> 30 return Method void foo(long, test.TestTarget) 0 getstatic #53 <Field java.io.PrintStream out> 3 invokestatic #67 <Method java.lang.Thread currentThread()> 6 invokevirtual #73 <Method void print(java.lang.Object)> 9 getstatic #53 <Field java.io.PrintStream out> 12 aload_0 13 invokevirtual #73 <Method void print(java.lang.Object)> 16 getstatic #53 <Field java.io.PrintStream out> 19 ldc #75 <String "."> 21 invokevirtual #61 <Method void print(java.lang.String)> 24 getstatic #53 <Field java.io.PrintStream out> 27 ldc #77 <String "foo"> 29 invokevirtual #61 <Method void print(java.lang.String)> 32 getstatic #53 <Field java.io.PrintStream out> 35 ldc #79 <String "("> 37 invokevirtual #61 <Method void print(java.lang.String)> 40 getstatic #53 <Field java.io.PrintStream out> 43 lload_1 44 invokevirtual #82 <Method void print(long)> 47 getstatic #53 <Field java.io.PrintStream out> 50 ldc #84 <String ","> 52 invokevirtual #61 <Method void print(java.lang.String)> 55 getstatic #53 <Field java.io.PrintStream out> 58 aload_3 59 invokevirtual #73 <Method void print(java.lang.Object)> 62 getstatic #53 <Field java.io.PrintStream out> 65 ldc #86 <String ")"> 67 invokevirtual #58 <Method void println(java.lang.String)> 70 aload_0 71 lload_1 72 aload_3 73 invokespecial #70 <Method void hidden$foo(long, test.TestTarget)> 76 getstatic #53 <Field java.io.PrintStream out> 79 invokestatic #67 <Method java.lang.Thread currentThread()> 82 invokevirtual #73 <Method void print(java.lang.Object)> 85 getstatic #53 <Field java.io.PrintStream out> 88 ldc #77 <String "foo"> 90 invokevirtual #61 <Method void print(java.lang.String)> 93 getstatic #53 <Field java.io.PrintStream out> 96 ldc #88 <String "=>(void)"> 98 invokevirtual #58 <Method void println(java.lang.String)> 101 return 102 astore 4 104 getstatic #53 <Field java.io.PrintStream out> 107 invokestatic #67 <Method java.lang.Thread currentThread()> 110 invokevirtual #73 <Method void print(java.lang.Object)> 113 getstatic #53 <Field java.io.PrintStream out> 116 ldc #77 <String "foo"> 118 invokevirtual #61 <Method void print(java.lang.String)> 121 getstatic #53 <Field java.io.PrintStream out> 124 ldc #90 <String "=>threw exception "> 126 invokevirtual #61 <Method void print(java.lang.String)> 129 getstatic #53 <Field java.io.PrintStream out> 132 aload 4 134 invokevirtual #92 <Method void println(java.lang.Object)> 137 aload 4 139 athrow Exception table: from to target type 0 102 102 any Method int bar() 0 getstatic #53 <Field java.io.PrintStream out> 3 invokestatic #67 <Method java.lang.Thread currentThread()> 6 invokevirtual #73 <Method void print(java.lang.Object)> 9 getstatic #53 <Field java.io.PrintStream out> 12 aload_0 13 invokevirtual #73 <Method void print(java.lang.Object)> 16 getstatic #53 <Field java.io.PrintStream out> 19 ldc #75 <String "."> 21 invokevirtual #61 <Method void print(java.lang.String)> 24 getstatic #53 <Field java.io.PrintStream out> 27 ldc #99 <String "bar"> 29 invokevirtual #61 <Method void print(java.lang.String)> 32 getstatic #53 <Field java.io.PrintStream out> 35 ldc #79 <String "("> 37 invokevirtual #61 <Method void print(java.lang.String)> 40 getstatic #53 <Field java.io.PrintStream out> 43 ldc #86 <String ")"> 45 invokevirtual #58 <Method void println(java.lang.String)> 48 aload_0 49 invokespecial #97 <Method int hidden$bar()> 52 getstatic #53 <Field java.io.PrintStream out> 55 invokestatic #67 <Method java.lang.Thread currentThread()> 58 invokevirtual #73 <Method void print(java.lang.Object)> 61 getstatic #53 <Field java.io.PrintStream out> 64 ldc #99 <String "bar"> 66 invokevirtual #61 <Method void print(java.lang.String)> 69 istore_1 70 getstatic #53 <Field java.io.PrintStream out> 73 ldc #101 <String "=>"> 75 invokevirtual #61 <Method void print(java.lang.String)> 78 getstatic #53 <Field java.io.PrintStream out> 81 iload_1 82 invokevirtual #104 <Method void println(int)> 85 iload_1 86 ireturn 87 astore_1 88 getstatic #53 <Field java.io.PrintStream out> 91 invokestatic #67 <Method java.lang.Thread currentThread()> 94 invokevirtual #73 <Method void print(java.lang.Object)> 97 getstatic #53 <Field java.io.PrintStream out> 100 ldc #99 <String "bar"> 102 invokevirtual #61 <Method void print(java.lang.String)> 105 getstatic #53 <Field java.io.PrintStream out> 108 ldc #90 <String "=>threw exception "> 110 invokevirtual #61 <Method void print(java.lang.String)> 113 getstatic #53 <Field java.io.PrintStream out> 116 aload_1 117 invokevirtual #92 <Method void println(java.lang.Object)> 120 aload_1 121 athrow Exception table: from to target type 0 87 87 any Method void main(java.lang.String[]) 0 getstatic #53 <Field java.io.PrintStream out> 3 invokestatic #67 <Method java.lang.Thread currentThread()> 6 invokevirtual #73 <Method void print(java.lang.Object)> 9 getstatic #53 <Field java.io.PrintStream out> 12 ldc #111 <String "main"> 14 invokevirtual #61 <Method void print(java.lang.String)> 17 getstatic #53 <Field java.io.PrintStream out> 20 ldc #79 <String "("> 22 invokevirtual #61 <Method void print(java.lang.String)> 25 getstatic #53 <Field java.io.PrintStream out> 28 aload_0 29 invokevirtual #73 <Method void print(java.lang.Object)> 32 getstatic #53 <Field java.io.PrintStream out> 35 ldc #86 <String ")"> 37 invokevirtual #58 <Method void println(java.lang.String)> 40 aload_0 41 invokestatic #109 <Method void hidden$main(java.lang.String[])> 44 getstatic #53 <Field java.io.PrintStream out> 47 invokestatic #67 <Method java.lang.Thread currentThread()> 50 invokevirtual #73 <Method void print(java.lang.Object)> 53 getstatic #53 <Field java.io.PrintStream out> 56 ldc #111 <String "main"> 58 invokevirtual #61 <Method void print(java.lang.String)> 61 getstatic #53 <Field java.io.PrintStream out> 64 ldc #88 <String "=>(void)"> 66 invokevirtual #58 <Method void println(java.lang.String)> 69 return 70 astore_1 71 getstatic #53 <Field java.io.PrintStream out> 74 invokestatic #67 <Method java.lang.Thread currentThread()> 77 invokevirtual #73 <Method void print(java.lang.Object)> 80 getstatic #53 <Field java.io.PrintStream out> 83 ldc #111 <String "main"> 85 invokevirtual #61 <Method void print(java.lang.String)> 88 getstatic #53 <Field java.io.PrintStream out> 91 ldc #90 <String "=>threw exception "> 93 invokevirtual #61 <Method void print(java.lang.String)> 96 getstatic #53 <Field java.io.PrintStream out> 99 aload_1 100 invokevirtual #92 <Method void println(java.lang.Object)> 103 aload_1 104 athrow Exception table: from to target type 0 70 70 any