package com.foursquare.heapaudit;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public abstract class HeapUtil {
static void log(String text) {
HeapSettings.output.println(text);
}
static void log(boolean debug,
String text) {
if (debug) {
log(text);
}
}
static void log(boolean debug,
boolean trace,
MethodAdapter mv,
String text) {
log(debug,
text);
if (trace) {
// STACK [...]
mv.visitLdcInsn(text);
// STACK [...|text]
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/foursquare/heapaudit/HeapUtil",
"log",
"(Ljava/lang/String;)V");
// STACK [...]
}
}
protected static void visitCheck(MethodVisitor mv,
Label cleanup) {
// STACK: [...]
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"com/foursquare/heapaudit/HeapRecorder",
"hasRecorders",
"()Z");
// STACK: [...|status]
mv.visitJumpInsn(Opcodes.IFEQ,
cleanup);
// STACK: [...]
}
protected static void visitCleanup(MethodVisitor mv,
Label cleanup,
Label finish) {
// STACK: [...]
mv.visitJumpInsn(Opcodes.GOTO,
finish);
// STACK: [...]
mv.visitLabel(cleanup);
// STACK: [...]
}
protected static void visitFinish(MethodVisitor mv,
Label finish) {
// STACK: [...]
mv.visitLabel(finish);
// STACK: [...]
}
// The following holds a cache of type to size mappings.
private static ConcurrentHashMap<String, Long> sizes = new ConcurrentHashMap<String, Long>();
private static long sizeOf(Object obj,
String type) {
Long size = sizes.get(type);
if (size == null) {
size = HeapRecorder.instrumentation.getObjectSize(obj);
sizes.put(type,
size);
}
return size;
}
public static void record(Object obj,
int count,
String type,
long size) {
if (size < 0) {
if (HeapRecorder.suppress() != null) {
size = sizeOf(obj,
"" + count + type);
}
HeapRecorder.unwind();
}
record(count,
type,
size);
}
public static void record(Object obj,
String type) {
if (type.charAt(0) != '[') {
record(obj,
-1,
type,
-1);
}
else if (HeapRecorder.suppress() != null) {
long overhead = 0;
Object[] o = (Object[])obj;
int length = o.length;
int count = length;
for (int i = 1; i < type.length(); ++i) {
// note that "o" might be null if this was a multidimensional
// array with empty dims
if (type.charAt(i) == '[' && o != null) {
// The following assumes the size of array of array,
// including the overhead of the array bookkeeping itself
// is only affected by the number of elements, not the
// actual element type.
overhead += sizeOf(o,
"" + length + "[[L");
// o[0] might be null if this was a multidimensional
// array with empty dims - if so, set length to 0.
// In addition, o.length might == 0 if the dimension was
// explicitly set to zero.
switch (type.charAt(i + 1)) {
case 'Z':
length = (o.length > 0 && o[0] != null ? ((boolean[])o[0]).length : 0 );
break;
case 'B':
length = (o.length > 0 && o[0] != null ? ((byte[])o[0]).length : 0 );
break;
case 'C':
length = (o.length > 0 && o[0] != null ? ((char[])o[0]).length : 0 );
break;
case 'S':
length = (o.length > 0 && o[0] != null ? ((short[])o[0]).length : 0 );
break;
case 'I':
length = (o.length > 0 && o[0] != null ? ((int[])o[0]).length : 0 );
break;
case 'J':
length = (o.length > 0 && o[0] != null ? ((long[])o[0]).length : 0 );
break;
case 'F':
length = (o.length > 0 && o[0] != null ? ((float[])o[0]).length : 0 );
break;
case 'D':
length = (o.length > 0 && o[0] != null ? ((double[])o[0]).length : 0 );
break;
case 'L':
length = (o.length > 0 && o[0] != null ? ((Object[])o[0]).length : 0 );
break;
default:
// o.length can be zero if a dimension was explicitly set to zero
o = (o.length > 0 ? (Object[])(o[0]) : null );
// make sure this is not null due to a
// multidimensional array with empty or zero length dims
if (o != null) {
length = o.length;
count *= length;
}
}
}
else {
final String name;
final long size;
// check for o.length being zero - this can happen if an array was created as [0]
if (o != null && o.length > 0 && o[0] != null) {
name = type.substring(i);
size = overhead + count * sizeOf(o[0],
"" + length + type.substring(i - 1));
}
else {
// patch things up so we record the right length, name
// and size when this was a multidimensional array with
// empty or zero length dims
length = 1;
name = type.substring(i-1);
size = overhead;
}
HeapRecorder.unwind();
record(obj,
count * length,
name,
size);
break;
}
}
}
else {
HeapRecorder.unwind();
}
}
public static void record(Object obj,
int count,
String type) {
if (HeapRecorder.suppress() != null) {
long size = sizeOf(obj,
"" + count + "[" + type);
HeapRecorder.unwind();
record(obj,
count,
type,
size);
}
else {
HeapRecorder.unwind();
}
}
public static void record(Object obj,
int[] dimensions,
String type) {
if (HeapRecorder.suppress() != null) {
long overhead = 0;
Object o[] = (Object[])obj;
int count = 1;
for (int i = 0; i < dimensions.length - 1 && count > 0; ++i) {
int length = dimensions[i];
if (length >= 0) {
// The following assumes the size of array of array, including
// the overhead of the array bookkeeping itself is only affected
// by the number of elements, not the actual element type.
overhead += sizeOf(o,
"" + length + "[[L");
o = (Object[])(o[0]);
}
count *= length;
}
if (count > 0) {
int length = dimensions[dimensions.length - 1];
long size = overhead + count * sizeOf(o,
"" + length + "[" + type);
HeapRecorder.unwind();
record(obj,
count * length,
type,
size);
}
else {
HeapRecorder.unwind();
}
}
else {
HeapRecorder.unwind();
}
}
public static void record(int count,
String type,
long size) {
Object context = HeapRecorder.suppress();
if (context != null) {
for (HeapRecorder recorder: HeapRecorder.getRecorders(context)) {
try {
recorder.record(type,
count,
size);
} catch (Exception e) {
System.err.println(e);
}
}
}
HeapRecorder.unwind();
}
private final static HashMap<String, HeapSummary> recorders = new HashMap<String, HeapSummary>();
public static boolean inject(String id) {
HeapRecorder.suppress();
try {
HeapSummary recorder = HeapSettings.recorderClass.newInstance();
recorder.setId(id);
recorders.put(id,
recorder);
}
catch (IllegalAccessException e) {
return false;
}
catch (InstantiationException e) {
return false;
}
HeapRecorder.unwind();
return true;
}
static void dump() {
for (HeapSummary recorder: recorders.values()) {
HeapSettings.output.println(recorder.summarize());
}
recorders.clear();
}
public static void register(String id) {
HeapRecorder.suppress();
HeapSummary recorder = recorders.get(id);
if (recorder != null) {
HeapRecorder.register(recorder,
HeapRecorder.Threading.Local);
}
HeapRecorder.unwind();
}
public static void unregister(String id) {
HeapRecorder.suppress();
HeapSummary recorder = recorders.get(id);
if (recorder != null) {
HeapRecorder.unregister(recorder,
HeapRecorder.Threading.Local);
}
HeapRecorder.unwind();
}
public static void extend(long id) {
HeapRecorder.extend(id);
}
}