Why does this simple Java bytecode cause a StackOverflow error?
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. He开发者_Python百科re'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?
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?
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
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.
精彩评论