Bytebuddy constructor advice throws java.lang.VerifyError - byte-buddy

Basically, I want to intercept all classes that have a specific annotation (that was created by me) and i want to perform my custom logic in method and constructor entry.
I have tried using byte-buddy agent builder as you can see from my code sample.
public static void agentmain(final String agentArgs,
final Instrumentation inst) {
new AgentBuilder.Default()
.type(ElementMatchers.
<TypeDescription>isAnnotatedWith(Licensable.class))
.transform((builder, td, cl, m) -> builder
.visit(Advice.to(AdviceToClasses.class).on(isMethod()))
.visit(Advice.to(AdviceToConstructor.class).on(isConstructor())))
.installOn(inst);
}
AdviceToConstructor class
#Advice.OnMethodEnter
public static void enter(#Advice.Origin Constructor method) throws
Exception {
System.out.println("Intercepted Constr >> " + method);
}
Using above, i can get what i expected if use method advice and leave out constructor part. But when i use constructor part, it doesn't go to advice enter method and i get the below errors.
complete --- sample.TestAnnotation
complete --- java.lang.VerifyError
Exception in thread "main" complete --- java.lang.Throwable$WrappedPrintStream
complete --- java.lang.Throwable$PrintStreamOrWriter
complete --- java.util.IdentityHashMap$KeySet
java.lang.VerifyError: Inconsistent stackmap frames at branch target 96
Exception Details:
Location:
sample/TestAnnotation.()V #96: aload_0
Reason:
Type uninitializedThis (current frame, locals[0]) is not assignable to 'sample/TestAnnotation' (stack map, locals[0])
Current Frame:
bci: #93
flags: { flagThisUninit }
locals: { uninitializedThis, 'sample/annotation/Licensable' }
stack: { }
Stackmap Frame:
bci: #96
flags: { }
locals: { 'sample/TestAnnotation' }
stack: { }
Bytecode:
0x0000000: b200 02bb 0028 59b7 0029 122b b600 2f12
0x0000010: 0703 bd00 31b6 0035 b600 38b6 003c b600
0x0000020: 04b2 0002 123e b600 0412 0703 bd00 31b6
0x0000030: 0035 b600 4412 46b6 004a c000 464c 2bc6
0x0000040: 001e b200 502b b900 5301 00b6 0059 3d1c
0x0000050: 9a00 0dbb 005b 5912 5db7 005f bfa7 0003
0x0000060: 2ab7 0001 b1
Stackmap Table:
append_frame(#93,Object[#70])
full_frame(#96,{Object[#7]},{})
at sample.Sample.main(SampleApp.java:47)
complete --- java.util.IdentityHashMap$KeyIterator
complete --- java.util.IdentityHashMap$IdentityHashMapIterator
I checked the posts on StackOverflow and googled it but couldn't find a solution. Any advice regarding this error?

Related

How to mimic `tableswitch` using `MethodHandle`?

Context: I've been benchmarking the difference between using invokedynamic and manually generating bytecode (this is in the context of deciding whether a compiler targeting the JVM should emit more verbose "traditional" bytecode or just an invokedynamic call with a clever bootstrap method). In doing this, it has been pretty straightforward to map bytecode into MethodHandles combinators that are at least as fast, with the exception of tableswitch.
Question: Is there a trick to mimic tableswitch using MethodHandle? I tried mimicking it with a jump table: using a constant MethodHandle[], indexing into that with arrayElementGetter, then calling the found handle with MethodHandles.invoker. However, that ended up being around 50% slower than the original bytecode when I ran it through JMH.
Here's the code for producing the method handle:
private static MethodHandle makeProductElement(Class<?> receiverClass, List<MethodHandle> getters) {
MethodHandle[] boxedGetters = getters
.stream()
.map(getter -> getter.asType(getter.type().changeReturnType(java.lang.Object.class)))
.toArray(MethodHandle[]::new);
MethodHandle getGetter = MethodHandles // (I)H
.arrayElementGetter(MethodHandle[].class)
.bindTo(boxedGetters);
MethodHandle invokeGetter = MethodHandles.permuteArguments( // (RH)O
MethodHandles.invoker(MethodType.methodType(java.lang.Object.class, receiverClass)),
MethodType.methodType(java.lang.Object.class, receiverClass, MethodHandle.class),
1,
0
);
return MethodHandles.filterArguments(invokeGetter, 1, getGetter);
}
Here's the initial bytecode (which I'm trying to replace with one invokedynamic call)
public java.lang.Object productElement(int);
descriptor: (I)Ljava/lang/Object;
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=3, args_size=2
0: iload_1
1: istore_2
2: iload_2
3: tableswitch { // 0 to 2
0: 28
1: 38
2: 45
default: 55
}
28: aload_0
29: invokevirtual #62 // Method i:()I
32: invokestatic #81 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
35: goto 67
38: aload_0
39: invokevirtual #65 // Method s:()Ljava/lang/String;
42: goto 67
45: aload_0
46: invokevirtual #68 // Method l:()J
49: invokestatic #85 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
52: goto 67
55: new #87 // class java/lang/IndexOutOfBoundsException
58: dup
59: iload_1
60: invokestatic #93 // Method java/lang/Integer.toString:(I)Ljava/lang/String;
63: invokespecial #96 // Method java/lang/IndexOutOfBoundsException."<init>":(Ljava/lang/String;)V
66: athrow
67: areturn
The good thing about invokedynamic is that it allows to postpone the decision, how to implement the operation to the actual runtime. This is the trick behind LambdaMetafactory or StringConcatFactory which may return composed method handles, like in your example code, or dynamically generated code, at the particular implementation’s discretion.
There’s even a combined approach possible, generate classes which you compose to an operation, e.g. settling on the already existing LambdaMetafactory:
private static MethodHandle makeProductElement(
MethodHandles.Lookup lookup, Class<?> receiverClass, List<MethodHandle> getters)
throws Throwable {
Function[] boxedGetters = new Function[getters.size()];
MethodType factory = MethodType.methodType(Function.class);
for(int ix = 0; ix < boxedGetters.length; ix++) {
MethodHandle mh = getters.get(ix);
MethodType actual = mh.type().wrap(), generic = actual.erase();
boxedGetters[ix] = (Function)LambdaMetafactory.metafactory(lookup,
"apply", factory, generic, mh, actual).getTarget().invokeExact();
}
Object switcher = new Object() {
final Object get(Object receiver, int index) {
return boxedGetters[index].apply(receiver);
}
};
return lookup.bind(switcher, "get",
MethodType.methodType(Object.class, Object.class, int.class))
.asType(MethodType.methodType(Object.class, receiverClass, int.class));
}
This uses the LambdaMetafactory to generate a Function instance for each getter, similar to equivalent method references. Then, an actual class calling the right Function’s apply method is instantiated and a method handle to its get method returned.
This is a similar composition as your method handles, but with the reference implementation, no handles but fully materialized classes are used. I’d expect the composed handles and this approach to converge to the same performance for a very large number of invocations, but the materialized classes having a headstart for a medium number of invocations.
I added a first parameter MethodHandles.Lookup lookup which should be the lookup object received by the bootstrap method for the invokedynamic instruction. If used that way, the generated functions can access all methods the same way as the code containing the invokedynamic instruction, including private methods of that class.
Alternatively, you can generate a class containing a real switch instruction yourself. Using the ASM library, it may look like:
private static MethodHandle makeProductElement(
MethodHandles.Lookup lookup, Class<?> receiverClass, List<MethodHandle> getters)
throws ReflectiveOperationException {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(V1_8, ACC_INTERFACE|ACC_ABSTRACT,
lookup.lookupClass().getName().replace('.', '/')+"$Switch", null,
"java/lang/Object", null);
MethodType type = MethodType.methodType(Object.class, receiverClass, int.class);
MethodVisitor mv = cw.visitMethod(ACC_STATIC|ACC_PUBLIC, "get",
type.toMethodDescriptorString(), null, null);
mv.visitCode();
Label defaultCase = new Label();
Label[] cases = new Label[getters.size()];
for(int ix = 0; ix < cases.length; ix++) cases[ix] = new Label();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 1);
mv.visitTableSwitchInsn(0, cases.length - 1, defaultCase, cases);
String owner = receiverClass.getName().replace('.', '/');
for(int ix = 0; ix < cases.length; ix++) {
mv.visitLabel(cases[ix]);
MethodHandle mh = getters.get(ix);
mv.visitMethodInsn(INVOKEVIRTUAL, owner, lookup.revealDirect(mh).getName(),
mh.type().dropParameterTypes(0, 1).toMethodDescriptorString(), false);
if(mh.type().returnType().isPrimitive()) {
Class<?> boxed = mh.type().wrap().returnType();
MethodType box = MethodType.methodType(boxed, mh.type().returnType());
mv.visitMethodInsn(INVOKESTATIC, boxed.getName().replace('.', '/'),
"valueOf", box.toMethodDescriptorString(), false);
}
mv.visitInsn(ARETURN);
}
mv.visitLabel(defaultCase);
mv.visitTypeInsn(NEW, "java/lang/IndexOutOfBoundsException");
mv.visitInsn(DUP);
mv.visitVarInsn(ILOAD, 1);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String",
"valueOf", "(I)Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IndexOutOfBoundsException",
"<init>", "(Ljava/lang/String;)V", false);
mv.visitInsn(ATHROW);
mv.visitMaxs(-1, -1);
mv.visitEnd();
cw.visitEnd();
lookup = lookup.defineHiddenClass(
cw.toByteArray(), true, MethodHandles.Lookup.ClassOption.NESTMATE);
return lookup.findStatic(lookup.lookupClass(), "get", type);
}
This generates a new class with a static method containing the tableswitch instruction and the invocations (as well as the boxing conversions we now have to do ourselves). Also, it has the necessary code to create and throw an exception for out-of-bounds values. After generating the class, it returns a handle to that static method.
I don't know of your timeline. But it is likely there will be a MethodHandles.tableSwitch operation in Java 17. It is currently being integrated via https://github.com/openjdk/jdk/pull/3401/
Some more discussion about it here:
https://mail.openjdk.java.net/pipermail/core-libs-dev/2021-April/076105.html
The things is, tableswitch isn't always compiled to a jump table. For a small number of labels, like in your example, it's likely to act as a binary search. Thus using a tree of regular "if-then" MethodHandles will be the closest equivalent.

SC_METHOD Exception

I'm trying to use a SC_METHOD in my simulation. Here is the code:
gcrypt::gcrypt(sc_module_name name):
gcrypt_base(name)
{
SC_METHOD(on_clock_update);
sensitive << clock;
dont_initialize();
};
void gcrypt::on_clock_update()
{
if (clock.read() == 0)
{
SC_REPORT_WARNING(name(), "Invalid clock port value of 0");
_ns_per_cycle = 0;
return;
}
_ns_per_cycle = 1e9 / clock.read();
}
The gcrypt_base constructor is:
gcrypt_base::gcrypt_base(sc_module_name name) :
sc_module(name),
...
{
...
}
I get this exception thrown by SC_METHOD:
Exception thrown at 0x6FB78281 (vcruntime140d.dll) in
SystemCModuleTest.exe: 0xC0000005: Access violation reading location
0x115348EF.
I saw the __vfptr value was "Unable to read memory".
How to solve this problem?
I believe you haven't specified the /vmg option while compiling your code. The /vmg option is required because of the way SystemC implements method processes.

Antlr 4: Is getting this form of output possible?

Within the context of scanning, what do i need to override, extend, listen to, visit to be able to print out this form of informative output when my text is being scanned?
-- Example output only ---------
DEBUG ... current mode: DEFAULT_MODE
DEBUG ... matching text '#' on rule SHARP ; pushing and switching to DIRECTIVE_MODE
DEBUG ... matching text 'IF" on rule IF ; pushing and switching to IF_MODE
DEBUG ... matching text ' ' on rule WS; skipping
DEBUG ... no match for text %
DEBUG ... no match for text &
DEBUG ... mathcing text '\r\n' on rule EOL; popping mode; current mode: DIRECTIVE_MODE
...
thanks
The solution was a lot simpler than I thought.
You just need to subclass the generated Lexer and override methods such as popMode(), pushMode() to get the printout you want. If you do this you should also override emit() methods as well to get properly sequential and contextual information.
Here's an example in C#:
class ExtendedLexer : MyGeneratedLexer
{
public ExtendedLexer(ICharStream input)
: base(input) { }
public override int PopMode()
{
Console.WriteLine($"Mode is being popped: Line: {Line} Column:{Column} ModeName: {ModeNames[ModeStack.Peek()]}");
return base.PopMode();
}
public override void PushMode(int m)
{
Console.WriteLine($"Mode is being pushed: Line: {Line} Column:{Column} ModeName: {ModeNames[m]}");
base.PushMode(m);
}
public override void Emit(IToken t)
{
Console.WriteLine($"[#{t.TokenIndex},{t.StartIndex}:{t.StopIndex}, <{Vocabulary.GetSymbolicName(t.Type)}> = '{t.Text}']");
base.Emit(t);
}
}
And the output would be something like:
Mode is being pushed: Line: 4 Column:3 ModeName: IF_MODE
[#-1,163:165, <IF> = '#IF']
Mode is being pushed: Line: 4 Column:4 ModeName: CONDITION_MODE
[#-1,166:166, <LPAREN> = '(']
[#-1,167:189, <EXP> = '#setStartDateAndEndDate']
Mode is being popped: Line: 4 Column:28 ModeName: IF_MODE
[#-1,190:190, <RPAREN> = ')']

Spark CodeGenerator failed to compile, got NPE, infrequently

I'm doing simple spark aggregation operation, reading data from avro file as dataframe and then mapping them to case-classes using rdd.map method then doing some aggregation operation, like count etc.
Most of the time it works just fine. But sometimes it generating weird CodeGen exception;
[ERROR] 2017-03-24 08:43:20,595 org.apache.spark.sql.catalyst.expressions.codegen.CodeGenerator logError - failed to compile: java.lang.NullPointerException
/* 001 */ public java.lang.Object generate(Object[] references) {
/* 002 */ return new SpecificUnsafeProjection(references);
/* 003 */ }
/* 004 */
/* 005 */ class SpecificUnsafeProjection extends org.apache.spark.sql.catalyst.expressions.UnsafeProjection {
I am using this code;
val deliveries = sqlContext.read.format("com.databricks.spark.avro").load(deliveryDir)
.selectExpr("FROM_UNIXTIME(timestamp/1000, 'yyyyMMdd') as day",
"FROM_UNIXTIME(timestamp/1000, 'yyyyMMdd_HH') as hour",
"deliveryId"
)
.filter("valid = true").rdd
.map(row => {
val deliveryId = row.getAs[Long]("deliveryId")
val uid = row.getAs[Long]("uid")
val deviceModelId: Integer = if(row.getAs[Integer]("deviceModelId") == null) {
0
} else {
row.getAs[Integer]("deviceModelId")
}
val delivery = new DeliveryEvent(deliveryId, row.getAs[Integer]("adId"), row.getAs[Integer]("adSpaceId"), uid, deviceModelId)
eventCache.getDeliverCache().put(new Element(deliveryId, delivery))
new InteractedAdInfo(row.getAs[String]("day"), delivery.deliveryId, delivery.adId, delivery.adSpaceId, uid, deviceModelId, deliveryEvent=1)
})
deliveries.count()
I can't regenerate the problem. But i get it irregularly in production. I'm using from java-app and taking spark-core_2.11:2.1.0 and spark-avro_2.11:3.1.0 maven co-ordinates.
Where might be the problem, i'm setting java -Xms8G -Xmx12G -XX:PermSize=1G -XX:MaxPermSize=1G while running the app.
I'm seeing a similar error with the very simple action spark.read.format("com.databricks.spark.avro").load(fn).cache.count, which is intermittent when applied to large AVRO files (4GB-10GB range in my tests). However, I can eliminate the error removing the setting --conf spark.executor.cores=4 and letting it default to 1.
WARN TaskSetManager: Lost task 58.0 in stage 2.0 (TID 82, foo.com executor 10): java.lang.RuntimeException:
Error while encoding:
java.util.concurrent.ExecutionException:
java.lang.Exception: failed to compile: java.lang.NullPointerException
/* 001 */ public java.lang.Object generate(Object[] references) {
/* 002 */ return new SpecificUnsafeProjection(references);
/* 003 */ }

Using Javassist, how do I add code to an empty loop?

Using Javassist 3.20.0.GA, I am trying to inject code in to an empty loop.
Everything I try, I keep running in to a java.lang.VerifyError error at the point I attempt to create a new instance of the modified class.
I've attempted to isolate the issue to a small program that fails to run successfully.
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.Bytecode;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.InstructionPrinter;
import javassist.bytecode.MethodInfo;
import javassist.compiler.Javac;
public class EmptyLoopTest {
private void testEmptyLoopModification() throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(EmptyLoopTest.class.getName() + "$EmptyLoopClass");
CtMethod m = cc.getDeclaredMethod("emptyLoopMethod");
printMethod("Before modifications", m);
MethodInfo methodInfo = m.getMethodInfo();
CodeAttribute ca = methodInfo.getCodeAttribute();
CodeIterator ci = ca.iterator();
Javac jv = new Javac(cc);
jv.compileStmnt("System.out.println(\"injected into loop\");");
Bytecode b = jv.getBytecode();
adjustCodeAttributeIfNeeded(b, ca);
ci.insertAt(0, b.get());
printMethod("After modifications", m);
Class c = cc.toClass();
logInfo("Attempting to create instance of modified class");
c.newInstance();
}
private void adjustCodeAttributeIfNeeded(Bytecode b, CodeAttribute ca){
int locals = b.getMaxLocals();
int stack = b.getMaxStack();
if(stack > ca.getMaxStack()) {
ca.setMaxStack(stack);
}
if(locals > ca.getMaxLocals()) {
ca.setMaxLocals(locals);
}
}
private void printMethod(String title, CtMethod m){
logInfo(title);
InstructionPrinter instructionPrinter = new InstructionPrinter(System.out);
instructionPrinter.print(m);
}
private void logInfo(String message){
System.out.println("");
System.out.println("------" + message);
}
public class EmptyLoopClass {
public void emptyLoopMethod() {
for(;;){
}
}
}
public static void main(String[] args) throws Exception {
// have errors written to sysout so all output from this program is in order
System.setErr(System.out);
new EmptyLoopTest().testEmptyLoopModification();
}
}
When I run this program, the following is written to the console..
------Before modifications
0: goto 0
------After modifications
0: getstatic #32 = Field java.lang.System.out(Ljava/io/PrintStream;)
3: ldc #34 = "injected into loop"
5: invokevirtual #40 = Method java.io.PrintStream.println((Ljava/lang/String;)V)
8: goto 8
------Attempting to create instance of modified class
Exception in thread "main" java.lang.VerifyError: Expecting a stackmap frame at branch target 8
Exception Details:
Location:
EmptyLoopTest$EmptyLoopClass.emptyLoopMethod()V #8: goto
Reason:
Expected stackmap frame at this location.
Bytecode:
0x0000000: b200 2012 22b6 0028 a700 00
Stackmap Table:
same_frame(#0)
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.newInstance(Class.java:412)
at EmptyLoopTest.testEmptyLoopModification(EmptyLoopTest.java:35)
at EmptyLoopTest.main(EmptyLoopTest.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Process finished with exit code 1
If everything was working as expected, I'd expect the goto instruction after modifications to goto 0 instead of what it's currently showing, goto 8. It's as if the StackMapTable wasn't adjusted appropriately when I invoked javassist.bytecode.CodeIterator#insertAt.
Can anyone spot what I'm doing wrong here?
Thanks,
Eric
you create class like this c.newInstance(); so it supposed to have 0-parameters constructor - to be static inner class
public static class EmptyLoopClass {
public void emptyLoopMethod() {
for(;;){
}
}
you should call methodInfo.rebuildStackMap(cp); after code modifications. JVM uses StackMap to validate class file.
Anyway you are ending up with bytecode
3: ldc #34 = "injected into loop"
5: invokevirtual #40 = Method java.io.PrintStream.println((Ljava/lang/String;)V)
8: goto 8
This code is not inside the loop, it is before loop. Actual loop is single instruction 8: goto 8 and you have to generate some additional loop code if you want to inject instructions inside it.