package com.foursquare.heapaudit;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class HeapMethod extends HeapUtil implements MethodVisitor {
public HeapMethod(MethodVisitor mv,
String source,
String methodId,
boolean suppressAuditing,
boolean debugAuditing,
boolean traceAuditing,
boolean injectRecorder) {
this.mv = new MethodAdapter(mv);
this.source = source;
this.id = methodId;
this.suppressAuditing = suppressAuditing;
this.debugAuditing = debugAuditing;
this.traceAuditing = traceAuditing;
this.injectRecorder = injectRecorder && HeapUtil.inject(id);
log(debugAuditing,
"\t{ # METHOD " + id);
}
public final MethodAdapter mv;
private final String source;
private final String id;
private boolean suppressAuditing;
private final boolean debugAuditing;
private final boolean traceAuditing;
private final boolean injectRecorder;
public HeapVariables lvs = null;
public AnnotationVisitor visitAnnotationDefault() {
log(debugAuditing,
"\t\tvisitAnnotationDefault()");
return mv.visitAnnotationDefault();
}
public AnnotationVisitor visitAnnotation(String desc,
boolean visible) {
log(debugAuditing,
"\t\tvisitAnnotation(" + desc + ", " + visible + ")");
if (desc.equals("Lcom/foursquare/heapaudit/HeapRecorder$Suppress;")) {
suppressAuditing = true;
}
return mv.visitAnnotation(desc,
visible);
}
public AnnotationVisitor visitParameterAnnotation(int parameter,
String desc,
boolean visible) {
log(debugAuditing,
"\t\tvisitParameterAnnotation()");
return mv.visitParameterAnnotation(parameter,
desc,
visible);
}
public void visitAttribute(Attribute attr) {
log(debugAuditing,
"\t\tvisitAttribute(" + attr.type + ")");
mv.visitAttribute(attr);
}
public void visitCode() {
mv.visitCode();
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitCode() " + source + ":" + id);
visitEnter();
}
public void visitFrame(int type,
int nLocal,
Object[] local,
int nStack,
Object[] stack) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitFrame()");
mv.visitFrame(type,
nLocal,
local,
nStack,
stack);
}
public void visitInsn(int opcode) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitInsn(" + opcode + ")");
switch (opcode) {
case Opcodes.ARETURN:
case Opcodes.DRETURN:
case Opcodes.FRETURN:
case Opcodes.IRETURN:
case Opcodes.LRETURN:
case Opcodes.RETURN:
visitReturn();
break;
}
mv.visitInsn(opcode);
}
public void visitLdcInsn(Object cst) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitLdcInsn(" + cst + ")");
mv.visitLdcInsn(cst);
}
public void visitIincInsn(int var,
int increment) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitIincInsn()");
mv.visitIincInsn(var,
increment);
}
public void visitVarInsn(int opcode,
int var) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitVarInsn(" + opcode + ", " + var + ")");
switch (opcode) {
case Opcodes.RET:
visitReturn();
break;
}
mv.visitVarInsn(opcode,
var);
}
public void visitFieldInsn(int opcode,
String owner,
String name,
String desc) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitFieldInsn(" + opcode + ", " + owner + ", " + name + ", " + desc + ")");
mv.visitFieldInsn(opcode,
owner,
name,
desc);
}
public void visitIntInsn(int opcode,
int operand) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitIntInsn(" + opcode + ", " + operand + ")");
switch (opcode) {
case Opcodes.NEWARRAY:
HeapNEWARRAY.before(debugAuditing,
traceAuditing,
mv,
operand);
break;
default:
}
mv.visitIntInsn(opcode,
operand);
switch (opcode) {
case Opcodes.NEWARRAY:
HeapNEWARRAY.after(debugAuditing,
traceAuditing,
mv,
operand);
break;
default:
}
}
private int allocating = 0;
public void visitTypeInsn(int opcode,
String type) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitTypeInsn(" + opcode + ", " + type + ")");
switch (opcode) {
case Opcodes.NEW:
++allocating;
break;
case Opcodes.ANEWARRAY:
HeapANEWARRAY.before(debugAuditing,
traceAuditing,
mv,
type);
break;
default:
}
mv.visitTypeInsn(opcode,
type);
switch (opcode) {
case Opcodes.ANEWARRAY:
HeapANEWARRAY.after(debugAuditing,
traceAuditing,
mv,
type);
break;
default:
}
}
public void visitMethodInsn(int opcode,
String owner,
String name,
String signature) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitMethodInsn(" + opcode + ", " + owner + ", " + name + ", " + signature + ")");
switch (opcode) {
case Opcodes.INVOKESPECIAL:
if (name.equals("<init>")) {
if (allocating > 0) {
HeapNEW.before(debugAuditing,
traceAuditing,
mv,
lvs,
signature);
}
}
else if (owner.equals("java/lang/Thread") &&
name.equals("init")) {
if (HeapSettings.threaded) {
HeapThreaded.before(debugAuditing,
traceAuditing,
mv,
lvs,
signature);
}
}
break;
case Opcodes.INVOKESTATIC:
if (owner.equals("java/lang/reflect/Array") &&
name.equals("newInstance")) {
HeapNEWINSTANCE.beforeX(debugAuditing,
traceAuditing,
mv);
}
break;
case Opcodes.INVOKEVIRTUAL:
if (name.equals("newInstance")) {
if (owner.equals("java/lang/Class") &&
signature.equals("()Ljava/lang/Object;")) {
HeapNEWINSTANCE.before(debugAuditing,
traceAuditing,
mv);
}
}
break;
default:
}
mv.visitMethodInsn(opcode,
owner,
name,
signature);
switch (opcode) {
case Opcodes.INVOKESPECIAL:
if (name.equals("<init>")) {
if (allocating > 0) {
--allocating;
HeapNEW.after(debugAuditing,
traceAuditing,
mv,
owner);
}
}
else if (owner.equals("java/lang/Object") &&
name.equals("clone")) {
HeapCLONEOBJECT.after(debugAuditing,
traceAuditing,
mv);
}
else if (owner.equals("java/lang/Thread") &&
name.equals("init")) {
if (HeapSettings.threaded) {
HeapThreaded.after(debugAuditing,
traceAuditing,
mv);
}
}
break;
case Opcodes.INVOKESTATIC:
if (owner.equals("java/lang/reflect/Array") &&
name.equals("newInstance")) {
if (signature.equals("(Ljava/lang/Class;I)Ljava/lang/Object;")) {
HeapNEWINSTANCE.after(debugAuditing,
traceAuditing,
mv);
}
else if (signature.equals("(Ljava/lang/Class;[I)Ljava/lang/Object;")) {
HeapNEWINSTANCE.afterY(debugAuditing,
traceAuditing,
mv);
}
}
break;
case Opcodes.INVOKEVIRTUAL:
if (name.equals("newInstance")) {
if (owner.equals("java/lang/Class") &&
signature.equals("()Ljava/lang/Object;")) {
HeapNEWINSTANCE.after(debugAuditing,
traceAuditing,
mv);
}
else if (owner.equals("java/lang/reflect/Constructor") &&
signature.equals("([Ljava/lang/Object;)Ljava/lang/Object;")) {
HeapCLONEOBJECT.after(debugAuditing,
traceAuditing,
mv);
}
}
else if (owner.startsWith("[") &&
name.equals("clone")) {
HeapCLONEARRAY.after(debugAuditing,
traceAuditing,
mv,
owner);
}
break;
default:
}
}
public void visitMultiANewArrayInsn(String desc,
int dims) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitMultiANewArrayInsn(" + desc + ", " + dims + ")");
mv.visitMultiANewArrayInsn(desc,
dims);
HeapMULTIARRAY.after(debugAuditing,
traceAuditing,
mv,
desc);
}
public void visitJumpInsn(int opcode,
Label label) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitJumpInsn(" + opcode + ", " + label + ")");
mv.visitJumpInsn(opcode,
label);
}
public void visitLookupSwitchInsn(Label dlft,
int[] keys,
Label[] labels) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitLookupSwitchInsn()");
mv.visitLookupSwitchInsn(dlft,
keys,
labels);
}
public void visitTableSwitchInsn(int min,
int max,
Label dlft,
Label[] labels) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitTableSwitchInsn()");
mv.visitTableSwitchInsn(min,
max,
dlft,
labels);
}
public void visitLabel(Label label) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitLabel() " + source + ":" + label);
mv.visitLabel(label);
}
public void visitTryCatchBlock(Label start,
Label end,
Label handler,
String type) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitTryCatchBlock()");
mv.visitTryCatchBlock(start,
end,
handler,
type);
}
public void visitLocalVariable(String name,
String desc,
String signature,
Label start,
Label end,
int index) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitLocalVariable(" + name + ")");
mv.visitLocalVariable(name,
desc,
signature,
start,
end,
index);
}
public void visitLineNumber(int line,
Label start) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitLineNumber() " + source + ":" + start + "#" + line);
mv.visitLineNumber(line,
start);
}
public void visitMaxs(int maxStack,
int maxLocals) {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitMaxs(" + maxStack + ", " + maxLocals + ")");
lvs.declare();
mv.visitMaxs(maxStack,
maxLocals);
}
public void visitEnd() {
log(debugAuditing,
traceAuditing,
mv,
"\t\tvisitEnd()\n\t}");
mv.visitEnd();
}
private void visitEnter() {
if (suppressAuditing) {
// STACK: [...]
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/foursquare/heapaudit/HeapRecorder",
"suppress",
"()Ljava/lang/Object;");
// STACK: [...|context]
mv.visitInsn(Opcodes.POP);
// STACK: [...]
}
else if (injectRecorder) {
// STACK: [...]
mv.visitLdcInsn(id);
// STACK: [...|id]
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/foursquare/heapaudit/HeapUtil",
"register",
"(Ljava/lang/String;)V");
// STACK: [...]
}
}
private void visitReturn() {
if (suppressAuditing) {
// STACK: [...]
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/foursquare/heapaudit/HeapRecorder",
"unwind",
"()Ljava/lang/Object;");
// STACK: [...|context]
mv.visitInsn(Opcodes.POP);
// STACK: [...]
}
else if (injectRecorder) {
// STACK: [...]
mv.visitLdcInsn(id);
// STACK: [...|id]
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/foursquare/heapaudit/HeapUtil",
"unregister",
"(Ljava/lang/String;)V");
// STACK: [...]
}
}
}