From 64601e6f3e77c2cb8bdaeb9d6da535001eaef32f Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 16 Nov 2010 09:31:49 -0700 Subject: [PATCH] name VM-internal classes for heapdump=true builds This makes heap dumps more useful since these classes are now refered to by name instead of number. This commit also adds a couple of utilities for parsing heap dumps: PrintDump and DumpStats. --- makefile | 3 +- src/machine.cpp | 11 +++ src/type-generator.cpp | 28 +++++++- test/extra/DumpStats.java | 147 ++++++++++++++++++++++++++++++++++++++ test/extra/PrintDump.java | 101 ++++++++++++++++++++++++++ 5 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 test/extra/DumpStats.java create mode 100644 test/extra/PrintDump.java diff --git a/makefile b/makefile index 7c14992ac4..69beda3adb 100644 --- a/makefile +++ b/makefile @@ -315,7 +315,8 @@ generated-code = \ $(native-build)/type-declarations.cpp \ $(native-build)/type-constructors.cpp \ $(native-build)/type-initializations.cpp \ - $(native-build)/type-java-initializations.cpp + $(native-build)/type-java-initializations.cpp \ + $(native-build)/type-name-initializations.cpp vm-depends = \ $(generated-code) \ diff --git a/src/machine.cpp b/src/machine.cpp index fedbf9c35f..d0b82f87fb 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -1888,6 +1888,13 @@ bootJavaClass(Thread* t, Machine::Type type, int superType, const char* name, hashMapInsert(t, t->m->bootstrapClassMap, n, class_, byteArrayHash); } +void +nameClass(Thread* t, Machine::Type type, const char* name) +{ + object n = makeByteArray(t, name); + set(t, arrayBody(t, t->m->types, type), ClassName, n); +} + void boot(Thread* t) { @@ -1990,6 +1997,10 @@ boot(Thread* t) PROTECT(t, bootMethod); #include "type-java-initializations.cpp" + +#ifdef AVIAN_HEAPDUMP +# include "type-name-initializations.cpp" +#endif } } diff --git a/src/type-generator.cpp b/src/type-generator.cpp index fc0fb72061..82f1748358 100644 --- a/src/type-generator.cpp +++ b/src/type-generator.cpp @@ -2184,6 +2184,27 @@ writeJavaInitializations(Output* out, Object* declarations) } } +void +writeNameInitialization(Output* out, Object* type) +{ + out->write("nameClass(t, Machine::"); + out->write(capitalize(typeName(type))); + out->write("Type, \"vm::"); + out->write(typeName(type)); + out->write("\");\n"); +} + +void +writeNameInitializations(Output* out, Object* declarations) +{ + for (Object* p = declarations; p; p = cdr(p)) { + Object* o = car(p); + if (o->type == Object::Type and typeJavaName(o) == 0) { + writeNameInitialization(out, o); + } + } +} + void usageAndExit(const char* command) { @@ -2206,7 +2227,8 @@ main(int ac, char** av) and not equal(av[2], "declarations") and not equal(av[2], "constructors") and not equal(av[2], "initializations") - and not equal(av[2], "java-initializations"))) + and not equal(av[2], "java-initializations") + and not equal(av[2], "name-initializations"))) { usageAndExit(av[0]); } @@ -2246,5 +2268,9 @@ main(int ac, char** av) writeJavaInitializations(&out, declarations); } + if (ac == 2 or equal(av[2], "name-initializations")) { + writeNameInitializations(&out, declarations); + } + return 0; } diff --git a/test/extra/DumpStats.java b/test/extra/DumpStats.java new file mode 100644 index 0000000000..c9e7774484 --- /dev/null +++ b/test/extra/DumpStats.java @@ -0,0 +1,147 @@ +package extra; + +import java.io.PrintStream; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.EOFException; +import java.util.Map; +import java.util.HashMap; +import java.util.Comparator; +import java.util.Arrays; + +/** + * This is a simple utility to generate and print statistics from a + * heap dump generated by Avian's heapdump.cpp. The output is a list + * of classes (identified by number in the case of anonymous, + * VM-internal classes), each followed by (1) the total memory + * footprint of all instances of the class, and (2) the number of + * instances. The output is ordered by instance memory footprint. + */ +public class DumpStats { + private static final int Root = 0; + private static final int Size = 1; + private static final int ClassName = 2; + private static final int Push = 3; + private static final int Pop = 4; + + private static int readInt(InputStream in) throws IOException { + int b1 = in.read(); + int b2 = in.read(); + int b3 = in.read(); + int b4 = in.read(); + if (b4 == -1) throw new EOFException(); + return (int) ((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4)); + } + + private static String readString(InputStream in) throws IOException { + int count = readInt(in); + byte[] b = new byte[count]; + int offset = 0; + int c; + while ((c = in.read(b, offset, b.length - offset)) != -1 + && offset < b.length) + { + offset += c; + } + if (offset != b.length) throw new EOFException(); + return new String(b); + } + + private static Record record(Map map, int key) { + Record r = map.get(key); + if (r == null) { + map.put(key, r = new Record(key)); + } + return r; + } + + private static Map read(InputStream in) + throws IOException + { + boolean done = false; + boolean popped = false; + int size = 0; + int last = 0; + Map map = new HashMap(); + + while (! done) { + int flag = in.read(); + switch (flag) { + case Root: { + last = readInt(in); + popped = false; + } break; + + case ClassName: { + record(map, last).name = readString(in); + } break; + + case Push: { + last = readInt(in); + if (! popped) { + Record r = record(map, last); + r.footprint += size; + ++ r.count; + } + popped = false; + } break; + + case Pop: { + popped = true; + } break; + + case Size: { + size = readInt(in); + } break; + + case -1: + done = true; + break; + + default: + throw new RuntimeException("bad flag: " + flag); + } + } + + return map; + } + + public static void main(String[] args) throws Exception { + Map map = read + (new BufferedInputStream(new FileInputStream(args[0]))); + + Record[] array = map.values().toArray(new Record[map.size()]); + Arrays.sort(array, new Comparator() { + public int compare(Record a, Record b) { + return b.footprint - a.footprint; + } + }); + + int footprint = 0; + int count = 0; + for (Record r: array) { + if (r.name == null) { + r.name = String.valueOf(r.key); + } + System.out.println(r.name + ": " + r.footprint + " " + r.count); + footprint += r.footprint; + count += r.count; + } + + System.out.println(); + System.out.println("total: " + footprint + " " + count); + } + + private static class Record { + public final int key; + public String name; + public int footprint; + public int count; + + public Record(int key) { + this.key = key; + } + } +} diff --git a/test/extra/PrintDump.java b/test/extra/PrintDump.java new file mode 100644 index 0000000000..8f43f419c9 --- /dev/null +++ b/test/extra/PrintDump.java @@ -0,0 +1,101 @@ +package extra; + +import java.io.PrintStream; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.EOFException; + +/** + * This is a simple utility to print the contents of a heap dump + * generated by Avian's heapdump.cpp in a human-readable format. + */ +public class PrintDump { + private static final int Root = 0; + private static final int Size = 1; + private static final int ClassName = 2; + private static final int Push = 3; + private static final int Pop = 4; + + private static void indent(PrintStream out, int level) { + for (; level > 0; --level) out.print(" "); + } + + private static int readInt(InputStream in) throws IOException { + int b1 = in.read(); + int b2 = in.read(); + int b3 = in.read(); + int b4 = in.read(); + if (b4 == -1) throw new EOFException(); + return (int) ((b1 << 24) | (b2 << 16) | (b3 << 8) | (b4)); + } + + private static String readString(InputStream in) throws IOException { + int count = readInt(in); + byte[] b = new byte[count]; + int offset = 0; + int c; + while ((c = in.read(b, offset, b.length - offset)) != -1 + && offset < b.length) + { + offset += c; + } + if (offset != b.length) throw new EOFException(); + return new String(b); + } + + private static void pipe(InputStream in, PrintStream out) + throws IOException + { + boolean done = false; + boolean popped = false; + int level = 0; + while (! done) { + int flag = in.read(); + switch (flag) { + case Root: { + out.print("\nroot " + readInt(in)); + popped = false; + } break; + + case ClassName: { + out.print(" class " + readString(in)); + } break; + + case Push: { + ++ level; + out.println(); + indent(out, level); + if (! popped) { + out.print("first "); + } + out.print("child " + readInt(in)); + popped = false; + } break; + + case Pop: { + -- level; + popped = true; + } break; + + case Size: { + out.print(" size " + readInt(in)); + } break; + + case -1: + out.println(); + out.flush(); + done = true; + break; + + default: + throw new RuntimeException("bad flag: " + flag); + } + } + } + + public static void main(String[] args) throws Exception { + pipe(new BufferedInputStream(new FileInputStream(args[0])), System.out); + } +}