I am trying to generate a random number with the instrumented code. For that I have added following expression to generate a random number. But it is throwing a verify Error as given below.
method.addLocalVariable("instMethod_correlationId", CtClass.longType); beforeBuilder.append("instMethod_correlationId = Long.valueOf(String.valueOf(System.nanoTime()) + String.valueOf(Math.round(Math.random())));");
I have added a long variable and assign the above generated value. But I am getting following exception during the run time. But if I run the same number generation in a normal project it doesn’t throw any exceptions.
java.lang.VerifyError: Bad type on operand stack Exception Details: Location: org/h2/jdbc/JdbcConnection.prepareStatement(Ljava/lang/String;)Ljava/sql/PreparedStatement; @38: i2l Reason: Type 'java/lang/Long' (current frame, stack[0]) is not assignable to integer Current Frame: bci: @38 flags: { } locals: { 'org/h2/jdbc/JdbcConnection', 'java/lang/String', top, long, long_2nd } stack: { 'java/lang/Long' } Bytecode: 0000000: b803 6c42 bb03 6e59 b703 6fb8 036c b803 0000010: 73b6 0376 b803 7cb8 0380 b803 73b6 0376 0000020: b603 81b8 0386 8537 05b8 038b b803 8e16 0000030: 0513 0390 b603 94bb 0396 59b7 0397 3a07 0000040: 1907 1303 992b b903 9d03 0057 b803 8bb8 0000050: 038e 1605 1303 9f19 07b6 03a2 2a06 b600 0000060: 113d 2ab6 002a 9900 272a 1237 061c bb00 0000070: 1659 b700 1712 38b6 0019 2a2b b600 1db6 0000080: 0019 1234 b600 19b6 0020 b600 2d2a b600 0000090: 2e2a 2bb7 0039 4cbb 003a 592a 2b1c 1103 00000a0: ebb2 0030 03b7 003b a700 0a4d 2a2c b600 00000b0: 28bf 3a09 bb03 9659 b703 973a 0a19 0a13 00000c0: 03a4 1303 a6b9 039d 0300 57b8 038b b803 00000d0: 8e16 05bb 036e 59b7 036f 1303 a8b6 0376 00000e0: b803 6c21 65b8 0373 b603 76b6 0381 190a 00000f0: b603 a219 09b0 Exception Handler Table: bci [55, 168] => handler: 171 Stackmap Table: full_frame(@141,{Object[#320],Object[#331],Integer,Long,Long,Object[#918]},{}) full_frame(@171,{Object[#320],Object[#331],Top,Long,Long},{Object[#322]}) full_frame(@178,{Object[#320],Object[#331],Integer,Long,Long,Object[#918]},{Object[#58]}) at org.h2.Driver.connect(Driver.java:73) at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:278) at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:182) at org.apache.tomcat.jdbc.pool.ConnectionPool.createConnection(ConnectionPool.java:701) at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:635) at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:188) at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:128) at org.wso2.carbon.user.core.claim.dao.ClaimDAO.getDialectCount(ClaimDAO.java:158) at org.wso2.carbon.user.core.common.DefaultRealm.populateProfileAndClaimMaps(DefaultRealm.java:429) at org.wso2.carbon.user.core.common.DefaultRealm.init(DefaultRealm.java:105) at org.wso2.carbon.user.core.common.DefaultRealmService.initializeRealm(DefaultRealmService.java:262) at org.user.core.common.DefaultRealmService.<init>(DefaultRealmService.java:99) at org.user.core.common.DefaultRealmService.<init>(DefaultRealmService.java:112) at org.user.core.internal.Activator.startDeploy(Activator.java:68) at org.user.core.internal.BundleCheckActivator.start(BundleCheckActivator.java:61) at org.eclipse.osgi.framework.internal.core.BundleContextImpl$1.run(BundleContextImpl.java:711) at java.security.AccessController.doPrivileged(Native Method) at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:702) at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:683) at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:381) at org.eclipse.osgi.framework.internal.core.AbstractBundle.resume(AbstractBundle.java:390) at org.eclipse.osgi.framework.internal.core.Framework.resumeBundle(Framework.java:1176) at org.eclipse.osgi.framework.internal.core.StartLevelManager.resumeBundles(StartLevelManager.java:559) at org.eclipse.osgi.framework.internal.core.StartLevelManager.resumeBundles(StartLevelManager.java:544) at org.eclipse.osgi.framework.internal.core.StartLevelManager.incFWSL(StartLevelManager.java:457) at org.eclipse.osgi.framework.internal.core.StartLevelManager.doSetStartLevel(StartLevelManager.java:243) at org.eclipse.osgi.framework.internal.core.StartLevelManager.dispatchEvent(StartLevelManager.java:438) at org.eclipse.osgi.framework.internal.core.StartLevelManager.dispatchEvent(StartLevelManager.java:1) at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230) at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:340)
What should I do to overcome this? What am I doing wrong here?
Advertisement
Answer
It seems you hit a bug in Javassist’s byte code generator. Here is the disassembled code as reported from the VerifyError
. Note that due to the absence of the constant pool in the exception message, the actual target methods are guessed from your source code (but it looks plausible):
0 invokestatic [876] 3 lstore_3 4 new [878] guess: new StringBuilder 7 dup 8 invokespecial [879] guess: StringBuilder.<init> 11 invokestatic [876] guess: System.nanoTime() 14 invokestatic [883] guess: String.valueOf(long) 17 invokevirtual [886] guess: StringBuilder.append(String) 20 invokestatic [892] guess: Math.random() 23 invokestatic [896] guess: Math.round(double) 26 invokestatic [883] guess: String.valueOf(long) 29 invokevirtual [886] guess: StringBuilder.append(String) 32 invokevirtual [897] guess: StringBuilder.toString() 35 invokestatic [902] guess: Long.valueOf(String) 38 i2l 39 lstore <5> … rest omitted
Note that the method invocations match your source code fragment (being static
or virtual as expected and being the same reference where supposed to be the same), whereas the instruction at location 38
, the one rejected by the verifier, is a spurious i2l
instruction (a conversion from int
to long
). At this place, an unboxing conversion from Long
to long
should happen. Since the following lstore
instruction will store a long
value into a local variable, it seems that Javassist got your local variable declaration right.
Note that the first two instructions indicate that there is another instrumentation happening; since it’s invoking the same method as the instruction at location 11
, which is supposed to be System.nanoTime
, it seems the other instrumentation is intended to measure the method’s overall execution time. But that shouldn’t affect your instrumentation code.
You may contact the authors of Javassist to find out whether Long
unboxing should work. At the same time, you may workaround the problem by using Long.parseLong
instead of Long.valueOf
to avoid the boxing/unboxing in the first place. You may also simplify the operation from the generated code’s perspective by using String.concat
instead of the +
operator as this eliminates the need to deal with StringBuilder
internally:
Replace
method.addLocalVariable("instMethod_correlationId", CtClass.longType); beforeBuilder.append("instMethod_correlationId = Long.valueOf(String.valueOf(System.nanoTime()) + String.valueOf(Math.round(Math.random())));");
with
method.addLocalVariable("instMethod_correlationId", CtClass.longType); beforeBuilder.append("instMethod_correlationId = Long.parseLong(String.valueOf(System.nanoTime()).concat(String.valueOf(Math.round(Math.random()))));");
This should lower the requirements to Javassist’s compiling capabilities and workaround the bug triggered by your original code fragment.
Update: for those, interested in decoding the hex dump of a VerifyError
to a readable instruction sequence: Online Decoder using tio.run