From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libjava/gnu/gcj/tools/gc_analyze/BlockMap.java | 218 ++++++++++ libjava/gnu/gcj/tools/gc_analyze/BytePtr.java | 115 ++++++ libjava/gnu/gcj/tools/gc_analyze/ItemList.java | 72 ++++ .../gnu/gcj/tools/gc_analyze/MemoryAnalyze.java | 458 +++++++++++++++++++++ libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java | 359 ++++++++++++++++ libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java | 140 +++++++ libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java | 112 +++++ libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java | 198 +++++++++ libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java | 45 ++ 9 files changed, 1717 insertions(+) create mode 100644 libjava/gnu/gcj/tools/gc_analyze/BlockMap.java create mode 100644 libjava/gnu/gcj/tools/gc_analyze/BytePtr.java create mode 100644 libjava/gnu/gcj/tools/gc_analyze/ItemList.java create mode 100644 libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java create mode 100644 libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java create mode 100644 libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java create mode 100644 libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java create mode 100644 libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java create mode 100644 libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java (limited to 'libjava/gnu/gcj/tools/gc_analyze') diff --git a/libjava/gnu/gcj/tools/gc_analyze/BlockMap.java b/libjava/gnu/gcj/tools/gc_analyze/BlockMap.java new file mode 100644 index 000000000..6e7adae20 --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/BlockMap.java @@ -0,0 +1,218 @@ +/* BlockMap.java -- Container for information on GC maintained memory blocks. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Map; +import java.util.TreeMap; + +class BlockMap +{ + static final int HBLKSIZE = 4096; + + class SizeKind implements Comparable + { + int size; + int kind; + + public SizeKind(int size, int kind) + { + this.size = size; + this.kind = kind; + } + + public int compareTo(SizeKind b) + { + if (this.size != b.size) + return this.size - b.size; + return this.kind - b.kind; + } + } + + class PtrMarks + { + long ptr; + int marks; + + public PtrMarks(long ptr, int marks) + { + this.ptr = ptr; + this.marks = marks; + } + } + + private TreeMap> map = + new TreeMap>(); + + public BlockMap(BufferedReader reader) throws IOException + { + for (;;) + { + String s = reader.readLine(); + if (s == null) + break; + if (s.charAt(0) == '#') + continue; + if (s.indexOf("Begin block map") >= 0) + { + for (;;) + { + s = reader.readLine(); + if (s.charAt(0) == '#') + continue; + if (s.indexOf("End block map") >= 0) + return; + String[] items = s.split(","); + long ptr = 0; + int kind = 0, size = 0, marks = 0; + for (int i=0; i m = map.get(sk); + if (m == null) + { + m = new ArrayList(); + map.put(sk, m); + } + PtrMarks pm = new PtrMarks(ptr, marks); + m.add(pm); + } // inner loop + } // started inner loop + } // outer loop - finding begin + } // memoryMap + + public void dump() + { + System.out.println(); + System.out.println(); + System.out.println("*** Used Blocks ***\n"); + System.out.println(); + System.out.println(" Size Kind Blocks Used Free Wasted"); + System.out.println("------- ------------- ------- ---------- ---------- -------"); + + int total_blocks = 0, total_used = 0, total_free = 0, total_wasted = 0; + + for (Map.Entry> me : map.entrySet()) + { + SizeKind sk = me.getKey(); + + System.out.println(MemoryAnalyze.format(sk.size, 7) + " " + + MemoryAnalyze.kindToName(sk.kind)); + + int sub_blocks = 0, sub_used = 0, sub_free = 0, sub_wasted = 0; + int sub_count = 0; + + ArrayList v = me.getValue(); + + for (PtrMarks pm : v) + { + int bytes = sk.size; + int blocks = (sk.size + HBLKSIZE - 1) / HBLKSIZE; + int used; + int free; + int wasted; + + if (bytes < HBLKSIZE) + { + used = bytes * pm.marks; + free = bytes * (HBLKSIZE / bytes - pm.marks); + wasted = HBLKSIZE - HBLKSIZE / bytes * bytes; + } + else + { + if (pm.marks != 0) + { + used = bytes; + free = 0; + wasted = (bytes + HBLKSIZE - 1) + / HBLKSIZE * HBLKSIZE - used; + } + else + { + used = 0; + free = bytes; + wasted = 0; + } + } + + StringBuilder sb = new StringBuilder(); + sb.append(" "); + sb.append(MemoryAnalyze.format(blocks, 5)); + sb.append(" "); + sb.append(MemoryAnalyze.format(used, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(free, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(wasted, 9)); + System.out.println(sb); + + sub_blocks += blocks; + sub_used += used; + sub_free += free; + sub_wasted += wasted; + sub_count++; + + total_blocks += blocks; + total_used += used; + total_free += free; + total_wasted += wasted; + } // blocks with size/kind + if (sub_count > 1) + { + System.out.println( + " ------- ---------- ---------- -------"); + StringBuilder sb = new StringBuilder(); + sb.append(" "); + sb.append(MemoryAnalyze.format(sub_blocks, 5)); + sb.append(" "); + sb.append(MemoryAnalyze.format(sub_used, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(sub_free, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(sub_wasted, 9)); + System.out.println(sb); + } + } // size/kind + + System.out.println("------- ------------- ------- ---------- ---------- -------"); + StringBuilder sb = new StringBuilder(); + sb.append(" "); + sb.append(MemoryAnalyze.format(total_blocks, 5)); + sb.append(" "); + sb.append(MemoryAnalyze.format(total_used, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(total_free, 9)); + sb.append(" "); + sb.append(MemoryAnalyze.format(total_wasted, 9)); + System.out.println(sb); + System.out.println("Total bytes = " + + MemoryAnalyze.format(total_blocks * HBLKSIZE, 10)); + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/BytePtr.java b/libjava/gnu/gcj/tools/gc_analyze/BytePtr.java new file mode 100644 index 000000000..4afceeeec --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/BytePtr.java @@ -0,0 +1,115 @@ +/* BytePtr.java -- Container for bytes from a memory image. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.nio.ByteBuffer; + +public class BytePtr +{ + ByteBuffer content; + int wordSize; + + BytePtr(ByteBuffer b, int ws) + { + content = b; + wordSize = ws; + } + + public int getsize() + { + return content.limit(); + } + + public int getByte(int offset) + { + return content.get(offset); + } + + public int getInt(int n) + { + return content.getInt(n * 4); + } + + public int getShort(int n) + { + return content.getShort(n * 2); + } + + public long getWord(int n) + { + if (4 == wordSize) + return 0xffffffffL & content.getInt(n * 4); + else + return content.getLong(n * 8); + } + + public int intsPerWord() + { + return (4 == wordSize) ? 1 : 2; + } + + public BytePtr getRegion(int offset, int size) + { + int oldLimit = content.limit(); + content.position(offset); + content.limit(offset + size); + ByteBuffer n = content.slice(); + content.position(0); + content.limit(oldLimit); + + return new BytePtr(n, wordSize); + } + + public void setInt(int a, int n) + { + content.putInt(a * 4, n); + } + + public void dump() + { + // 38 5a f4 2a 50 bd 04 10 10 00 00 00 0e 00 00 00 8Z.*P........... + int i; + StringBuilder b = new StringBuilder(67); + for (i = 0; i < 66; i++) + b.append(' '); + b.append('\n'); + + i = 0; + do + { + for (int j = 0; j < 16; j++) + { + int k = i + j; + + if (k < content.limit()) + { + int v = 0xff & getByte(k); + // hex + int v1 = v/16; + b.setCharAt(j * 3 + 0, + (char)(v1 >= 10 ? 'a' - 10 + v1 : v1 + '0')); + v1 = v % 16; + b.setCharAt(j * 3 + 1, + (char)(v1 >= 10 ? 'a' - 10 + v1 : v1 + '0')); + // ascii + b.setCharAt(j + 50, (char)((v >= 32 && v <= 127) ? v: '.')); + } + else + { + b.setCharAt(j * 3 + 0, ' '); + b.setCharAt(j * 3 + 1, ' '); + b.setCharAt(j + 50, ' '); + } + } + i += 16; + System.out.print(b); + } while (i < content.limit()); + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/ItemList.java b/libjava/gnu/gcj/tools/gc_analyze/ItemList.java new file mode 100644 index 000000000..7912bebca --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/ItemList.java @@ -0,0 +1,72 @@ +/* ItemList.java -- Maps all objects keyed by their addresses. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +class ItemList +{ + public ItemList() + { + } + + private TreeMap> map; + + public void add(ObjectMap.ObjectItem item) + { + if (map == null) + map = new TreeMap>(); + Long x = new Long(item.klass); + HashMap list = map.get(x); + if (list == null) + { + list = new HashMap(); + map.put(x, list); + } + Integer count = list.get(item); + if (count == null) + list.put(item, new Integer(1)); + else + list.put(item, new Integer(count.intValue() + 1)); + } + + void dump(String title, SymbolLookup lookup) throws IOException + { + if (map == null) + return; + System.out.println(title); + for (Map.Entry> me : + map.entrySet()) + { + HashMap list = me.getValue(); + boolean first = true; + + for (Map.Entry me2 : list.entrySet()) + { + ObjectMap.ObjectItem item = me2.getKey(); + Integer count = me2.getValue(); + if (first) + { + String name = + MemoryAnalyze.getSymbolPretty(lookup, item, false); + System.out.println(" " + name + ":"); + first = false; + } + System.out.print(" 0x" + Long.toHexString(item.ptr)); + if (count.intValue() != 1) + System.out.print(" * " + count); + System.out.println(); + } + } + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java b/libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java new file mode 100644 index 000000000..d56a71da3 --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java @@ -0,0 +1,458 @@ +/* MemoryAnalyze.java -- Analyzes a libgcj heap dump. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gc_analyze; + +import gnu.classpath.tools.getopt.FileArgumentCallback; +import gnu.classpath.tools.getopt.Option; +import gnu.classpath.tools.getopt.OptionException; +import gnu.classpath.tools.getopt.Parser; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +class MemoryAnalyze +{ + public MemoryAnalyze() + { + } + + private static NumberFormat numberFormat; + private static boolean verbose; + static String format(long number, int digits) + { + if (numberFormat == null) + { + numberFormat = NumberFormat.getNumberInstance(); + numberFormat.setGroupingUsed(true); + } + String temp = numberFormat.format(number); + int spaces = digits - temp.length(); + if (spaces < 0) + spaces = 0; + return " ".substring(0,spaces) + temp; + } + + static void sorted_report(String description, + int total_space, + ArrayList list, + Comparator comparator) + { + System.out.println("*** " + description + " ***"); + System.out.println(); + System.out.println(" Total Size Count Size Description"); + System.out.println("-------------- ----- -------- -----------------------------------"); + Collections.sort(list, comparator); + for (Iterator it = list.iterator(); it.hasNext(); ) + { + String v = (String)it.next(); + System.out.println(stripend(v)); + } + System.out.println("-------------- ----- -------- -----------------------------------"); + System.out.println(format(total_space, 14)); + System.out.println(); + System.out.println(); + } + + private static String stripend(String s) + { + int n = s.lastIndexOf(" /"); + if (n > 0) + return s.substring(0,n); + return s; + } + + static class SubstringComparator implements Comparator + { + private int begin, end; + private boolean reverse; + + SubstringComparator(int begin, int end, boolean reverse) + { + this.begin = begin; + this.end = end; + this.reverse = reverse; + } + + public int compare(String s1, String s2) + { + if (end == 0) + s1 = s1.substring(begin); + else + s1 = s1.substring(begin, end); + + if (end == 0) + s2 = s2.substring(begin); + else + s2 = s2.substring(begin, end); + int i = s1.compareTo(s2); + if (reverse) + return -i; + return i; + } + } + + static class OptionParser extends Parser + { + int filesFound; + + OptionParser() + { + super("gc-analyze", + "gc-analyze (" + System.getProperty("java.vm.version") + ")"); + + add(new Option('d', + "Directory containing runtime objects", + "directory") + { + public void parsed(String argument) throws OptionException + { + ToolPrefix.pathPrefix = argument; + } + }); + + add(new Option('p', + "Binary tool prefix, prepended to nm and readelf to " + + "obtain target specific versions of these commands", + "prefix") + { + public void parsed(String argument) throws OptionException + { + ToolPrefix.toolPrefix = argument; + } + }); + + add(new Option("verbose", 'v', + "Verbose output; requires filename.bytes") + { + public void parsed(String argument) throws OptionException + { + verbose = true; + } + }); + + setHeader("usage: gc-analyze [-v] [-p tool-prefix] [-d ] " + + "filename"); + } + + protected void validate() throws OptionException + { + if (filesFound != 1) + throw new OptionException("Must specify exactly one filename"); + } + + public String[] parse(String[] inArgs) + { + final ArrayList fileResult = new ArrayList(); + parse(inArgs, new FileArgumentCallback() + { + public void notifyFile(String fileArgument) + { + filesFound++; + fileResult.add(fileArgument); + } + }); + return fileResult.toArray(new String[1]); + } + } + + public static void main(String[] args) + { + class Info + { + int size; + int count; + } + int total_space = 0; + + Parser optionParser = new OptionParser(); + + String rest[] = optionParser.parse(args); + + String filename = rest[0]; + + try + { + BufferedReader reader = + new BufferedReader(new InputStreamReader(new FileInputStream(filename))); + SymbolLookup lookup = new SymbolLookup(reader, filename + ".bytes"); + ObjectMap objectMap = new ObjectMap(reader); + BlockMap blockMap = new BlockMap(reader); + reader.close(); + + // add info to item(s) + // add item.klass + for (Map.Entry me : objectMap) + { + ObjectMap.ObjectItem item = me.getValue(); + + // try to get a klass (happens with intern'ed strings...) + if (item.klass==0) + { + BytePtr p = lookup.getBytePtr(item.ptr, item.size); + if (p!=null) + { + long vtable = p.getWord(0); + String sym = + lookup.getSymbolViaVtable(vtable - 2 * lookup.memoryMap.wordSize); + if (sym != null) + { + item.typeName = SymbolTable.demangleVTName(sym); + } + else if (vtable != 0) + { + // get klass from vtable + p = lookup.getBytePtr(vtable, + lookup.memoryMap.wordSize); + if (p != null) + { + long klass = p.getWord(0); + item.klass = klass; + } + } + } + } + + // figure out strings + String class_name; + if (null == item.typeName) + { + class_name = + MemoryAnalyze.getSymbolPretty(lookup, item, false); + item.typeName = class_name; + } + else + { + class_name = item.typeName; + } + System.out.print("class_name=[" + class_name + "]"); + + if (class_name.compareTo("_ZTVN4java4lang6StringE")==0 + || class_name.compareTo("java.lang.String")==0) + { + BytePtr p = lookup.getBytePtr(item.ptr, item.size); + long data = p.getWord(1); + int boffset = p.getInt(2 * p.intsPerWord()); + int count = p.getInt(1 + 2 * p.intsPerWord()); + int hash = p.getInt(2 + 2 * p.intsPerWord()); + BytePtr chars = lookup.getBytePtr(data+boffset, count * 2); + StringBuffer sb = new StringBuffer(count); + for (int qq = 0; qq map = new HashMap(); + for (Map.Entry me : objectMap) + { + ObjectMap.ObjectItem item = me.getValue(); + String name = getSymbolPretty(lookup, item, true); + Info info = map.get(name); + if (info == null) + { + info = new Info(); + info.count = 0; + info.size = item.size; + map.put(name, info); + } + info.count++; + total_space += item.size; + } + + ArrayList list = new ArrayList(); + for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry me = (Map.Entry)it.next(); + String name = (String)me.getKey(); + Info info = (Info)me.getValue(); + + StringBuffer sb = new StringBuffer(); + sb.append(format(info.count * info.size * 100 / total_space, + 3)); + sb.append("%"); + sb.append(format(info.count * info.size, 10)); + sb.append(" = "); + sb.append(format(info.count, 7)); + sb.append(" * "); + sb.append(format(info.size, 9)); + sb.append(" - "); + sb.append(name); + list.add(sb.toString()); + } + + sorted_report("Memory Usage Sorted by Total Size", + total_space, list, new SubstringComparator(5,14,true)); + sorted_report("Memory Usage Sorted by Description", + total_space, list, new SubstringComparator(39,0,false)); + sorted_report("Memory Usage Sorted by Count", + total_space, list, new SubstringComparator(17,25,true)); + sorted_report("Memory Usage Sorted by Size", + total_space, list, new SubstringComparator(28,37,true)); + + blockMap.dump(); + + // dump raw memory + if (verbose) + { + // analyze references + for (Map.Entry me : objectMap) + { + long ptr = me.getKey(); + ObjectMap.ObjectItem item = me.getValue(); + BytePtr p = lookup.getBytePtr(ptr, item.size); + if (p == null) + System.out.println("can't find ptr 0x" + + Long.toHexString(ptr)); + else if (item.kind != 0) // not GC_PTRFREE + for (int i = 1; + i < item.size / lookup.memoryMap.wordSize; i++) + { + long maybe_ptr = p.getWord(i); + ObjectMap.ObjectItem item2 = objectMap.get(maybe_ptr); + if (item2 != null) + { + item2.pointed_by.add(item); + item.points_to.add(item2); + } + } + } + System.out.println(); + System.out.println("*** All Objects ***"); + System.out.println(); + + for (Map.Entry me : objectMap) + { + long ptr = me.getKey(); + ObjectMap.ObjectItem item = me.getValue(); + String name = getSymbolPretty(lookup, item, false); + System.out.print("0x" + Long.toHexString(ptr) + " - " + name + + " (" + item.size + ")"); + if (item.string != null) + System.out.println(" \"" + item.string + "\""); + else + System.out.println(); + + BytePtr p = lookup.getBytePtr(ptr, item.size); + + if (p == null) + System.out.println( + "can't find memory; recently allocated from free list?"); + else + p.dump(); + + item.points_to.dump(" points to:", lookup); + item.pointed_by.dump(" pointed to by:", lookup); + System.out.println(); + } + } + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + public static String kindToName(int kind) + { + String name; + switch (kind) + { + case 0: + name = "GC_PTRFREE"; + break; + case 1: + name = "GC_NORMAL"; + break; + case 2: + name = "GC_UNCOLLECTABLE"; + break; + case 3: + name = "GC_AUUNCOLLCTABLE"; + break; + case 4: + name = "(Java)"; + break; + case 5: + name = "(Java Debug)"; + break; + case 6: + name = "(Java Array)"; + break; + default: + name = "(Kind " + kind + ")"; + break; + } + return name; + } + + public static String getSymbolPretty(SymbolLookup lookup, + ObjectMap.ObjectItem item, + boolean bsize) + throws IOException + { + + String name = item.typeName; + + if (name == null) + name = lookup.getSymbol(item.klass); + + if (name == null) + { + String v = lookup.decodeUTF8(item.ptr, item.size); + if (null != v) + { + name = "UTF8Const"; + item.string = v; + } + } + + if (name == null) + { + name = kindToName(item.kind); + } + if (item.kind==6) + name += "[" + format(item.data, 0) + "]"; + if (bsize) + name = name + " / " + format(item.size, 7); + return name; + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java b/libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java new file mode 100644 index 000000000..1bc06d584 --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java @@ -0,0 +1,359 @@ +/* MemoryMap.java -- Maps address ranges to their data. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.BufferedReader; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.util.Comparator; +import java.util.HashMap; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Reads /proc/self/maps output from dump file. + * Creates map of to Range. + * + * Returns filename given address. + * Returns offset given address. + * Returns BytePtr given address. + * + */ +class MemoryMap +{ + static class RangeComparator implements Comparator + { + public int compare(Range r1, Range r2) + { + if (r2.end == 0 && r1.end != 0) + return -compare(r2, r1); + + if (r1.begin < r2.begin) + return -1; + else if (r1.begin >= r2.end) + return 1; + else + return 0; + } + } + + static class Range + { + long begin; + long end; + + long offset; + String filename; + Range() + { + } + + Range(long b, long e, String s, long o) + { + begin = b; + end = e; + filename = s; + offset = o; + } + } + + /** + * Parse the string as an unsigned hexadecimal number. This is + * similar to Long.parseInt(s,16), but without the restriction that + * values that have the sign bit set not being allowed. + * + * @param s the number as a String. + * @return the number. + */ + static long parseHexLong(String s) + { + if (s.length() > 16) + throw new NumberFormatException(); + long r = 0; + for (int i = 0; i < s.length(); i++) + { + int digit = 0; + char c = s.charAt(i); + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + digit = c - '0'; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + digit = 10 + c - 'a'; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + digit = 10 + c - 'A'; + break; + default: + throw new NumberFormatException(); + } + r = (r << 4) + digit; + } + return r; + } + + // String filename -> Range + TreeSet map = new TreeSet(new RangeComparator()); + HashMap symbolTables = + new HashMap(); + ByteOrder byteOrder; + int wordSize; + + public MemoryMap(BufferedReader reader, + String rawFileName) throws IOException + { + FileChannel raw = (new RandomAccessFile(rawFileName, "r")).getChannel(); + ByteBuffer buf = ByteBuffer.allocate(8); + raw.read(buf); + if (buf.hasRemaining()) + { + raw.close(); + throw new EOFException(); + } + buf.flip(); + wordSize = buf.get(); + + if (wordSize == 8 || wordSize == 4) + byteOrder = ByteOrder.LITTLE_ENDIAN; + else + { + byteOrder = ByteOrder.BIG_ENDIAN; + buf.rewind(); + wordSize = buf.getInt(); + if (0 == wordSize) + wordSize = buf.getInt(); + } + switch (wordSize) + { + case 4: + case 8: + break; + default: + throw new IOException("Bad .bytes file header"); + } + buf = ByteBuffer.allocate(3 * wordSize); + buf.order(byteOrder); + raw.position(0L); + + for(;;) + { + // Read the block header. + buf.clear(); + if (-1 == raw.read(buf)) + { + //EOF + raw.close(); + break; + } + if (buf.hasRemaining()) + { + raw.close(); + throw new EOFException(); + } + buf.flip(); + long dummy + = (wordSize == 4) ? (buf.getInt() & 0xffffffffL) : buf.getLong(); + if (dummy != wordSize) + throw new IOException("Bad .bytes file header"); + long start + = wordSize == 4 ? (buf.getInt() & 0xffffffffL) : buf.getLong(); + long length + = wordSize == 4 ? (buf.getInt() & 0xffffffffL) : buf.getLong(); + if (length < 0L) + throw new IOException("Bad .bytes file header"); + + long currentPos = raw.position(); + raw.position(currentPos + length); + + Range range = new Range(start, start + length, + rawFileName, currentPos); + map.add(range); + } + + for (;;) + { + String s = reader.readLine(); + if (s == null) + break; + if (s.indexOf("Begin address map") >= 0) + { + for (;;) + { + s = reader.readLine(); + if (s.indexOf("End address map") >= 0) + { + dump(); + return; + } + int endOfAddress = s.indexOf('-'); + long address = parseHexLong(s.substring(0, endOfAddress)); + int endOfAddress2 = s.indexOf(' ', endOfAddress + 1); + long address2 = parseHexLong(s.substring(endOfAddress + 1, + endOfAddress2)); + int endOfOffset = s.indexOf(' ', endOfAddress2 + 6); + long offset; + try + { + offset = parseHexLong(s.substring(endOfAddress2 + 6, + endOfOffset)); + } + catch (Exception e) + { + offset = 0; + } + int end = s.indexOf('/'); + + if (end > 0) + { + String file = s.substring(end); + if (file.startsWith("/dev/")) + continue; + + Range r = new Range(address, address2, file, offset); + if (offset == 0) + { + // Read the file's symbol table + try + { + File f = ToolPrefix.fileForName(file); + if (f != null) + { + SymbolTable st = new SymbolTable(f.getPath()); + if (st.loadAddr != address) + st.relocation = address - st.loadAddr; + symbolTables.put(file, st); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + map.add(r); + } + } // inner loop + } // started inner loop + } // outer loop - finding begin + } // memoryMap + + + public void dump() + { + System.out.println("MemoryMap:"); + for (Range r : map) + { + System.out.println(Long.toHexString(r.begin) + "-" + + Long.toHexString(r.end) + " -> " + + r.filename + " offset " + + Long.toHexString(r.offset)); + } + } + + Range getRange(long addr) + { + Range r = new Range(); + r.begin = addr; + SortedSet t = map.tailSet(r); + if (t.isEmpty()) + return null; + Range c = t.first(); + if (c.begin <= addr && addr < c.end) + return c; + return null; + } + + String getFile(long addr) + { + Range r = getRange(addr); + if (null != r) + return r.filename; + return null; + } + + long getOffset(long addr) + { + Range r = getRange(addr); + if (null != r) + return r.offset; + return 0L; + } + + /** + * @return BytePtr which includes given address. + */ + BytePtr getBytePtr(long addr, int length) throws IOException + { + Range r = getRange(addr); + + if (null == r) + return null; + + File f = ToolPrefix.fileForName(r.filename); + if (null == f) + return null; + + if (addr + length > r.end) + length = (int)(r.end - addr); + + ByteBuffer b = ByteBuffer.allocate(length); + b.order(byteOrder); + + FileChannel fc = (new RandomAccessFile(f, "r")).getChannel(); + fc.position(r.offset + addr - r.begin); + int nr = fc.read(b); + fc.close(); + if (nr != length) + return null; + b.flip(); + return new BytePtr(b, wordSize); + } + + public String getSymbol(long addr) + { + Range r = getRange(addr); + + if (r == null) + return null; + + SymbolTable st = symbolTables.get(r.filename); + if (st == null) + return null; + + // Apply relocation + addr -= st.relocation; + + return st.getSymbol(addr); + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java b/libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java new file mode 100644 index 000000000..b55034be1 --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java @@ -0,0 +1,140 @@ +/* ObjectMap.java -- Contains a map of all objects keyed by their addresses. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + +class ObjectMap implements Iterable> +{ + + class ObjectItem + { + int used; + int size; + int kind; + long klass; + long data; + long ptr; + String typeName; + String string; // only for string objects + boolean stringData; // character array pointed to by a string + ObjectItem reference; // object at reference points to this + + ItemList points_to = new ItemList(); + ItemList pointed_by = new ItemList(); + } + + private TreeMap map = new TreeMap(); + + public Iterator> iterator() + { + return map.entrySet().iterator(); + } + + public ObjectItem get(long ptr) + { + ObjectItem item = map.get(ptr); + return item; + } + + public ObjectMap(BufferedReader reader) throws IOException + { + outer_loop: + for (;;) + { + String s = reader.readLine(); + if (s == null) + break; + if (s.indexOf("Begin object map") >= 0) + { + for (;;) + { + s = reader.readLine(); + if (s.indexOf("End object map") >= 0) + break outer_loop; + String[] items = s.split(","); + ObjectItem item = new ObjectItem(); + long ptr = 0; + for (int i=0; i 1) + item.klass = + MemoryMap.parseHexLong(last.substring(2)); + else + item.klass = Integer.parseInt(last,16); + break; + case 5: + try + { + item.data = + Integer.parseInt(last.substring(2), 16); + } + catch (Exception e) + { + item.data = 0; + } + break; + } + } + item.ptr = ptr; + map.put(ptr, item); + } // inner loop + } // started inner loop + } // outer loop - finding begin + for (Map.Entry me : this) + { + ObjectItem item = me.getValue(); + if (item.data != 0) + { + // see if data is a pointer to a block + ObjectItem referenced = map.get(item.data); + if (referenced != null) + { + referenced.reference = item; + } + } + } + } // memoryMap + + public void dump() + { + for (Map.Entry me : this) + { + long ptr = me.getKey(); + ObjectItem item = me.getValue(); + System.out.println("ptr = " + Long.toHexString(ptr) + + ", size = " + item.size + + ", klass = " + Long.toHexString(item.klass) + + ", kind = " + item.kind + + ", data = " + item.data); + } + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java b/libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java new file mode 100644 index 000000000..b3963d8cf --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java @@ -0,0 +1,112 @@ +/* SymbolLookup.java -- Finds class names by analyzing memory. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.BufferedReader; +import java.io.IOException; + +class SymbolLookup +{ + MemoryMap memoryMap; + + public SymbolLookup(BufferedReader reader, + String rawFileName) + throws IOException + { + memoryMap = new MemoryMap(reader, rawFileName); + } + + public String decodeUTF8(long address) throws IOException + { + return decodeUTF8(address, -1); + } + + public String decodeUTF8(long address, int limit) throws IOException + { + if (address == 0) + return null; + + BytePtr utf8 = memoryMap.getBytePtr(address, 64); + + if (utf8 == null) + return null; + + int len = utf8.getShort(1); + int hash16 = utf8.getShort(0) & 0xffff; + + if (len <= 0 || (limit > 0 && len > (limit - 4))) + return null; + + if (len > utf8.getsize() + 4) + utf8 = memoryMap.getBytePtr(address, len + 4); + + if (utf8 == null) + return null; + + StringBuilder sb = new StringBuilder(len); + int pos = 4; + len += 4; + + while (pos < len) + { + int f = utf8.getByte(pos++); + if ((f & 0x80) == 0) + { + sb.append((char)f); + } + else if ((f & 0xe0) == 0xc0) + { + int s = utf8.getByte(pos++); + char c = (char)(((f & 0x1f) << 6) | (s & 0x80)); + sb.append(c); + } + else if ((f & 0xe0) == 0xe0) + { + int s = utf8.getByte(pos++); + int t = utf8.getByte(pos++); + char c = (char)(((f & 0x0f) << 12) + | ((s & 0x80) << 6) | (t & 0x80)); + sb.append(c); + } + else + break; // Bad utf8 + } + String rv = sb.toString(); + if (hash16 == (rv.hashCode() & 0xffff)) + return rv; + else + return null; + } + + public String getSymbolViaVtable(long address) throws IOException + { + return memoryMap.getSymbol(address); + } + + public String getSymbol(long address) throws IOException + { + String symbol = memoryMap.getSymbol(address); + if (null != symbol) + return symbol; + + BytePtr klass = memoryMap.getBytePtr(address, 3 * memoryMap.wordSize); + if (klass == null) + return null; + + long nameUTF8p = klass.getWord(2); + + return decodeUTF8(nameUTF8p); + } + + BytePtr getBytePtr(long addr, int length) throws IOException + { + return memoryMap.getBytePtr(addr, length); + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java b/libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java new file mode 100644 index 000000000..eb5df7641 --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java @@ -0,0 +1,198 @@ +/* SymbolTable.java -- Maintains a mapping of addresses to names. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + + This software is copyrighted work licensed under the terms of the + Libgcj License. Please consult the file "LIBGCJ_LICENSE" for + details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class SymbolTable +{ + // Long address->String name + private HashMap map = new HashMap(); + + // Reverse + // String name -> Long address + // used for RelocateImage + private HashMap reverse = new HashMap(); + + long loadAddr; + long relocation; + + static Matcher interestingSymbol = + Pattern.compile("^([0-9a-fA-F]+)\\s+\\S+\\s+(_Z\\S+)").matcher(""); + static Matcher readelfLoadMatcher = + Pattern.compile("^\\s+LOAD\\s+(\\S+)\\s+(\\S+)\\s.*").matcher(""); + + public SymbolTable(String filename) throws IOException + { + Process p = Runtime.getRuntime().exec(ToolPrefix.toolPrefix + + "nm " + filename); + InputStream es = p.getErrorStream(); + InputStream is = p.getInputStream(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + int count = 0; + + String line; + while ((line = reader.readLine()) != null) + { + interestingSymbol.reset(line); + if (interestingSymbol.matches()) + { + try + { + String name = interestingSymbol.group(2); + String addr = interestingSymbol.group(1); + if (name.startsWith("_ZTVN") || name.endsWith("6class$E")) + { + long address = MemoryMap.parseHexLong(addr); + Long l = new Long(address); + map.put(l, name); + count++; + reverse.put(name, l); + } + } + catch (NumberFormatException e) + { + // ignore it + } + } + } + es.close(); + is.close(); + p.destroy(); + + if (count > 0) + { + // Assume nm read some symbols from it and that + // readelf can tell us something about how it is loaded. + p = Runtime.getRuntime().exec(ToolPrefix.toolPrefix + + "readelf -l " + filename); + es = p.getErrorStream(); + is = p.getInputStream(); + + reader = new BufferedReader(new InputStreamReader(is)); + while ((line = reader.readLine()) != null) + { + readelfLoadMatcher.reset(line); + if (readelfLoadMatcher.matches()) + { + loadAddr + = Long.decode(readelfLoadMatcher.group(2)).longValue(); + break; + } + } + es.close(); + is.close(); + p.destroy(); + } + + System.out.println(ToolPrefix.toolPrefix + "nm " + filename + + " -> " + count + " symbols"); + } + + public static void main(String args[]) + { + try + { + SymbolTable st = new SymbolTable(args[0]); + st.dump(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + public static String demangleVTName(String n) + { + if (n.startsWith("_ZTVN") && n.endsWith("E")) + return demangle(n.substring(5, n.length() - 1)); + else + return null; + } + + public void dump() + { + for (Map.Entry me : map.entrySet()) + { + long address = me.getKey(); + String symbol = me.getValue(); + System.out.println(Long.toHexString(address) + " -> " + symbol); + if (symbol.startsWith("_ZN") && symbol.endsWith("6class$E")) + { + System.out.println(" Class: " + + demangle(symbol.substring(3, symbol.length() + - 8))); + } + else if (symbol.startsWith("_ZTVN") && symbol.endsWith("E")) + { + System.out.println(" VT: " + + demangle(symbol.substring(5, symbol.length() + - 1))); + } + } + } + + private static String demangle(String symbol) + { + StringBuilder sb = new StringBuilder(); + for (int i=0; i '9') + break; + l = 10 * l + (d - '0'); + i++; + } + if (l == 0) + break; + // copy + if (sb.length() > 0) + sb.append('.'); + while (l > 0 && i < symbol.length()) + { + sb.append(symbol.charAt(i)); + l--; + i++; + } + } + return sb.toString(); + } + + public String getSymbol(long address) + { + String symbol = map.get(address); + if (symbol == null) + return null; + + if (symbol.startsWith("_ZN") && symbol.endsWith("6class$E")) + symbol = demangle(symbol.substring(3, symbol.length() - 8)); + return symbol; + } + + // will return -1 if not found + public long getAddress(String symbol) + { + Long address = reverse.get(symbol); + if (address == null) + return -1; + return address.longValue(); + } +} diff --git a/libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java b/libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java new file mode 100644 index 000000000..e8d73ae92 --- /dev/null +++ b/libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java @@ -0,0 +1,45 @@ +/* ToolPrefix.java -- Container of the toolPrefix String. + Copyright (C) 2007 Free Software Foundation + + This file is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +package gnu.gcj.tools.gc_analyze; + +import java.io.File; + +class ToolPrefix +{ + /** + * Private constructor. No creation allowed. This class has + * Static methods only. + */ + private ToolPrefix() + { + } + + static String toolPrefix = ""; + + static String pathPrefix = ""; + + static File fileForName(String filename) + { + File f = new File(pathPrefix + filename); + if (!f.canRead()) + { + // Try it without the prefix. + f = new File(filename); + if (!f.canRead()) + { + // Try to find it in the current directory. + f = new File(f.getName()); + if (!f.canRead()) + return null; + } + } + return f; + } +} -- cgit v1.2.3