1
votes

I need to instrument native methods to make a simple static call before executing normally. Because the methods are native, I have to use the "setNativePrefix" feature and wrap the native methods with an intermediate call with the original method signature.

After what I thought was a simple bytecode change to accomplish this, I'm getting a StackOverflowError right before the wrapper method gets executed even though the stack is basically empty. Here's my test class:

public class SimpleTest {
    public static void main(String[] args) throws IOException {
        Perf.getPerf().highResCounter();
    }
}

Normally, that program would produce nothing on the console. However, my instrumented bytecode executes a println() before executing the native method $wrapper$highResCounter(). This can be seen in the relevant Perf class bytecode after instrumentation:

public long highResCounter() {
    getstatic PrintStream System.out
    ldc String Constant "this is an instrumented println"
    invokevirtual void PrintStream.println(String)
    aload 0
    invokevirtual long Perf.$wrapped$highResCounter()
    lreturn
}

public native long $wrapped$highResCounter();

I'm sort of new to Java bytecode, so I probably made a mistake here. Here's the output of the program, which shows that the println() gets executed, but somewhere after the first invokevirtual call a StackOverflowError is thrown:

this is an instrumented println call
Exception in thread "main" java.lang.StackOverflowError
   at com.foo.SimpleTest.main(SimpleTest.java:17)

What could be causing this StackOverflowError? And how do I fix it?

3
Do you have the complete SimpleTest.java for us? Have you stepped through with a debugger? Maybe you aren't running what you think you are running. (Reminds me of a bug years ago I searched for hours until I realized that my classpath was pointing to old class files :)Jochen Bedersdorfer
That is the entirety of the class, minus import statement and package identifier. No static initializers or anything.nahsra
It also happens on any native method: like StrictMath.sin(). I don't think it has anything to do with Perf.nahsra

3 Answers

1
votes

Your highResCounter method is calling itself:

public long highResCounter() {
    [...]
    invokevirtual long Perf.$wrapped$highResCounter()

Do you have any more code you can show us to find out why?

1
votes

There are no bugs in the code posted. The problem was being caused by invalid values for the MAX_STACK and MAX_LOCALS method attributes, which are usually calculated by the compiler.

The bytecode library I'm using, ASM, gives you the opportunity to calculate those values automatically, and I wasn't taking advantage of it. By changing the ASM ClassWriter constructor to the following I get error-free code:

int flags = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS;
ClassWriter writer = new ClassWriter(flags);

[1] http://asm.ow2.org/asm33/javadoc/user/org/objectweb/asm/ClassWriter.html#COMPUTE_FRAMES

0
votes

show the native function, it can also be a problem on its right own. iirc StackOverFlowError is trapped, so even JNI C code can cause it.