Skip to content

Instantly share code, notes, and snippets.

@serkan-ozal
Last active August 29, 2015 14:08
Show Gist options
  • Save serkan-ozal/4b88aa21254c586bae50 to your computer and use it in GitHub Desktop.
Save serkan-ozal/4b88aa21254c586bae50 to your computer and use it in GitHub Desktop.
A piece of code from Jillegal 3.0 to instrument all non-primitive field assignments in OffHeap object
protected <T> void instrumentNonPrimitiveFieldAssignments(final Class<T> elementType) {
try {
Jillegal.init();
final InstrumentService instrumenterService = InstrumentServiceFactory.getInstrumentService();
final String ownerClassDesc = elementType.getName().replace(".", "/");
final Set<String> ownerClassDescSet = new HashSet<String>();
ownerClassDescSet.add(ownerClassDesc);
for ( Class<T> parentClass = (Class<T>) elementType.getSuperclass();
parentClass != null && !parentClass.equals(Object.class);
parentClass = (Class<T>) parentClass.getSuperclass()) {
ownerClassDescSet.add(parentClass.getName().replace(".", "/"));
}
ClassReader cr = new ClassReader(elementType.getName());
ClassWriter cw = new ClassWriter(ClassReader.SKIP_DEBUG) {
@Override
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
return
new MethodAdapter(mv) {
public void visitFieldInsn(final int opcode, final String owner,
final String name, final String desc) {
if ( opcode == Opcodes.PUTFIELD &&
ownerClassDescSet.contains(owner) &&
(desc.startsWith("L") && desc.endsWith(";"))) {
int fieldOffset =
(int) JvmUtil.getUnsafe().
objectFieldOffset(ReflectionUtil.getField(elementType, name));
// Current operand stack snapshot: ..., <this>, <value_to_set>
super.visitInsn(Opcode.SWAP);
// Current operand stack snapshot:
// ..., <value_to_set>, <this>
super.visitInsn(Opcodes.POP);
// Current operand stack snapshot:
// ..., <value_to_set>
super.visitMethodInsn( // Call "getProperties" method of "java.lang.System" class
Opcodes.INVOKESTATIC,
"java/lang/System",
"getProperties",
"()Ljava/util/Properties;");
super.visitLdcInsn(
DirectMemoryServiceFactory.
DIRECT_MEMORY_SERVICE_$_SET_OBJECT_FIELD_ACCESSOR);
super.visitMethodInsn( // Call "get" method of "java/util/Properties" instance
Opcodes.INVOKEVIRTUAL,
"java/util/Properties",
"get",
"(Ljava/lang/Object;)Ljava/lang/Object;");
// Current operand stack snapshot:
// ..., <value_to_set>, <DirectMemoryService_setObjectField>
super.visitTypeInsn(
Opcode.CHECKCAST,
PropertyChangeSupport.class.getName().replace(".", "/"));
// Current operand stack snapshot:
// ..., <value_to_set>, <DirectMemoryService_setObjectField>
super.visitInsn(Opcode.SWAP);
// Current operand stack snapshot:
// ..., <DirectMemoryService_setObjectField>, <value_to_set>
super.visitInsn(Opcode.ACONST_NULL);
// Current operand stack snapshot:
// ..., <DirectMemoryService_setObjectField>, <value_to_set>, <null>
super.visitInsn(Opcode.SWAP);
// Current operand stack snapshot:
// ..., <DirectMemoryService_setObjectField>, <null>, <value_to_set>
super.visitLdcInsn(fieldOffset); // offset of field
// Current operand stack snapshot:
// ..., <DirectMemoryService_setObjectField>, <null>, <value_to_set>, <field_offset>
super.visitInsn(Opcode.SWAP);
// Current operand stack snapshot:
// ..., <DirectMemoryService_setObjectField>, <null>, <field_offset>, <value_to_set>
super.visitVarInsn(Opcodes.ALOAD, 0); // this
// Current operand stack snapshot:
// ..., <DirectMemoryService_setObjectField>, <null>, <field_offset>, <value_to_set>, <this>
super.visitInsn(Opcode.SWAP);
// Current operand stack snapshot:
// ..., <DirectMemoryService_setObjectField>, <null>, <field_offset>, <this>, <value_to_set>,
// Now <field_offset>, <this>, <value_to_set> are the parameters in this order
// to be passed for "setObjectField" method "DirectMemoryService" instance
super.visitMethodInsn( // Call "fireIndexedPropertyChange" method of "java/beans/PropertyChangeSupport" instance
Opcodes.INVOKEVIRTUAL,
PropertyChangeSupport.class.getName().replace(".", "/"),
"fireIndexedPropertyChange",
"(Ljava/lang/String;ILjava/lang/Object;Ljava/lang/Object;)V");
logger.debug("Instrumenting assignment to field \"" + name +
"\" in class " + elementType.getName());
}
else {
super.visitFieldInsn(opcode, owner, name, desc);
}
}
};
}
};
cr.accept(cw, ClassReader.SKIP_DEBUG);
byte[] instrumentedBytecodes = cw.toByteArray();
instrumenterService.redefineClass(elementType, instrumentedBytecodes);
logger.info("Instrumented class " + elementType.getName() + " for non-primitive field assignment");
}
catch (Throwable t) {
t.printStackTrace();
logger.error("Error occured while instrumenting non-primitive field assignments for class " +
elementType.getName(), t);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment