diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/gnu/gcj/tools | |
download | cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2 cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
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.
Diffstat (limited to 'libjava/gnu/gcj/tools')
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/BlockMap.java | 218 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/BytePtr.java | 115 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/ItemList.java | 72 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java | 458 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java | 359 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java | 140 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java | 112 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java | 198 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java | 45 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gcj_dbtool/Fileset.h | 42 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gcj_dbtool/Main.h | 55 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gcj_dbtool/Main.java | 541 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gcj_dbtool/Tokenizer.h | 40 | ||||
-rw-r--r-- | libjava/gnu/gcj/tools/gcj_dbtool/natMain.cc | 24 |
14 files changed, 2419 insertions, 0 deletions
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<SizeKind> + { + 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<SizeKind, ArrayList<PtrMarks>> map = + new TreeMap<SizeKind, ArrayList<PtrMarks>>(); + + 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<items.length; i++) + { + String[] x = items[i].split(" "); + String last = x[x.length - 1]; + switch (i) + { + case 0: + ptr = MemoryMap.parseHexLong(last.substring(2)); + break; + case 1: + kind = Integer.parseInt(last); + break; + case 2: + size = Integer.parseInt(last); + break; + case 3: + marks = Integer.parseInt(last); + break; + } + } + SizeKind sk = new SizeKind(size, kind); + ArrayList<PtrMarks> m = map.get(sk); + if (m == null) + { + m = new ArrayList<PtrMarks>(); + 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<SizeKind, ArrayList<PtrMarks>> 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<PtrMarks> 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<Long, HashMap<ObjectMap.ObjectItem, Integer>> map; + + public void add(ObjectMap.ObjectItem item) + { + if (map == null) + map = new TreeMap<Long, HashMap<ObjectMap.ObjectItem, Integer>>(); + Long x = new Long(item.klass); + HashMap<ObjectMap.ObjectItem, Integer> list = map.get(x); + if (list == null) + { + list = new HashMap<ObjectMap.ObjectItem, Integer>(); + 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<Long, HashMap<ObjectMap.ObjectItem, Integer>> me : + map.entrySet()) + { + HashMap<ObjectMap.ObjectItem, Integer> list = me.getValue(); + boolean first = true; + + for (Map.Entry<ObjectMap.ObjectItem, Integer> 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<String> list, + Comparator<String> 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<String> + { + 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 <directory>] " + + "filename"); + } + + protected void validate() throws OptionException + { + if (filesFound != 1) + throw new OptionException("Must specify exactly one filename"); + } + + public String[] parse(String[] inArgs) + { + final ArrayList<String> fileResult = new ArrayList<String>(); + 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<Long, ObjectMap.ObjectItem> 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<count; qq++) + sb.append((char)chars.getShort(qq)); + int newhash = sb.toString().hashCode(); + if (newhash!=hash) + { + p.setInt(4, newhash); + } + + item.string = sb.toString(); + System.out.println(" value = \"" + item.string + "\""); + if (data != item.ptr) + { + ObjectMap.ObjectItem next = objectMap.get(data); + if (next != null) + next.stringData = true; + else + System.out.println("String [" + item.string + "] at " + + Long.toHexString(item.ptr) + + " can't find array at " + + Long.toHexString(data)); + } + } + else if (null != item.string) + System.out.println(" value = \"" + item.string + "\""); + else + System.out.println(); + } + + + HashMap<String, Info> map = new HashMap<String, Info>(); + for (Map.Entry<Long, ObjectMap.ObjectItem> 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<String> list = new ArrayList<String>(); + 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<Long, ObjectMap.ObjectItem> 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<Long, ObjectMap.ObjectItem> 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 <filename> to Range. + * + * Returns filename given address. + * Returns offset given address. + * Returns BytePtr given address. + * + */ +class MemoryMap +{ + static class RangeComparator implements Comparator<Range> + { + 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<Range> map = new TreeSet<Range>(new RangeComparator()); + HashMap<String, SymbolTable> symbolTables = + new HashMap<String, SymbolTable>(); + 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<Range> 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<Map.Entry<Long, ObjectMap.ObjectItem>> +{ + + 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<Long, ObjectItem> map = new TreeMap<Long, ObjectItem>(); + + public Iterator<Map.Entry<Long, ObjectItem>> 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<items.length; i++) + { + String[] x = items[i].split(" "); + String last = x[x.length-1]; + switch (i) + { + case 0: + item.used = Integer.parseInt(last); + break; + case 1: + ptr = MemoryMap.parseHexLong(last.substring(2)); + break; + case 2: + item.size = Integer.parseInt(last); + break; + case 3: + item.kind = Integer.parseInt(last); + break; + case 4: + if (last.length() > 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<Long, ObjectItem> 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<Long, ObjectItem> 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<Long, String> map = new HashMap<Long, String>(); + + // Reverse + // String name -> Long address + // used for RelocateImage + private HashMap<String, Long> reverse = new HashMap<String, Long>(); + + 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<Long, String> 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<symbol.length(); ) + { + int l = 0; + while (i < symbol.length()) + { + int d = symbol.charAt(i); + if (d < '0' || d > '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; + } +} diff --git a/libjava/gnu/gcj/tools/gcj_dbtool/Fileset.h b/libjava/gnu/gcj/tools/gcj_dbtool/Fileset.h new file mode 100644 index 000000000..fb9b2d69e --- /dev/null +++ b/libjava/gnu/gcj/tools/gcj_dbtool/Fileset.h @@ -0,0 +1,42 @@ + +// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*- + +#ifndef __gnu_gcj_tools_gcj_dbtool_Fileset__ +#define __gnu_gcj_tools_gcj_dbtool_Fileset__ + +#pragma interface + +#include <java/lang/Object.h> +#include <gcj/array.h> + +extern "Java" +{ + namespace gnu + { + namespace gcj + { + namespace tools + { + namespace gcj_dbtool + { + class Fileset; + } + } + } + } +} + +class gnu::gcj::tools::gcj_dbtool::Fileset : public ::java::lang::Object +{ + +public: // actually package-private + Fileset(JArray< ::java::lang::String * > *, jint, jint); + Fileset(::java::io::InputStream *, jchar); + virtual ::java::util::Iterator * iterator(); + virtual jint size(); + ::java::util::LinkedHashSet * __attribute__((aligned(__alignof__( ::java::lang::Object)))) files; +public: + static ::java::lang::Class class$; +}; + +#endif // __gnu_gcj_tools_gcj_dbtool_Fileset__ diff --git a/libjava/gnu/gcj/tools/gcj_dbtool/Main.h b/libjava/gnu/gcj/tools/gcj_dbtool/Main.h new file mode 100644 index 000000000..4052bb34c --- /dev/null +++ b/libjava/gnu/gcj/tools/gcj_dbtool/Main.h @@ -0,0 +1,55 @@ + +// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*- + +#ifndef __gnu_gcj_tools_gcj_dbtool_Main__ +#define __gnu_gcj_tools_gcj_dbtool_Main__ + +#pragma interface + +#include <java/lang/Object.h> +#include <gcj/array.h> + +extern "Java" +{ + namespace gnu + { + namespace gcj + { + namespace runtime + { + class PersistentByteMap; + } + namespace tools + { + namespace gcj_dbtool + { + class Fileset; + class Main; + } + } + } + } +} + +class gnu::gcj::tools::gcj_dbtool::Main : public ::java::lang::Object +{ + +public: + Main(); + static void main(JArray< ::java::lang::String * > *); +private: + static ::java::lang::String * getDbPathTail(); + static void insist(jboolean); + static void usage(::java::io::PrintStream *); + static ::gnu::gcj::runtime::PersistentByteMap * addJar(::java::io::File *, ::gnu::gcj::runtime::PersistentByteMap *, ::java::io::File *); +public: // actually package-private + static ::gnu::gcj::runtime::PersistentByteMap * resizeMap(::gnu::gcj::runtime::PersistentByteMap *, jint, jboolean); + static ::java::lang::String * bytesToString(JArray< jbyte > *); +private: + static ::gnu::gcj::tools::gcj_dbtool::Fileset * getFiles(JArray< ::java::lang::String * > *, jint, jboolean, jchar); + static jboolean verbose; +public: + static ::java::lang::Class class$; +}; + +#endif // __gnu_gcj_tools_gcj_dbtool_Main__ diff --git a/libjava/gnu/gcj/tools/gcj_dbtool/Main.java b/libjava/gnu/gcj/tools/gcj_dbtool/Main.java new file mode 100644 index 000000000..e5e3e44ae --- /dev/null +++ b/libjava/gnu/gcj/tools/gcj_dbtool/Main.java @@ -0,0 +1,541 @@ +/* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 + 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.gcj_dbtool; + + +import gnu.gcj.runtime.PersistentByteMap; +import java.io.*; +import java.nio.channels.*; +import java.util.*; +import java.util.jar.*; +import java.security.MessageDigest; + +public class Main +{ + static private boolean verbose = false; + + public static void main (String[] s) + { + boolean fileListFromStdin = false; + char filenameSeparator = ' '; + + insist (s.length >= 1); + + if (s[0].equals("-") || + s[0].equals("-0")) + { + if (s[0].equals("-0")) + filenameSeparator = (char)0; + fileListFromStdin = true; + String[] newArgs = new String[s.length - 1]; + System.arraycopy(s, 1, newArgs, 0, s.length - 1); + s = newArgs; + } + + if (s[0].equals("-v") || s[0].equals("--version")) + { + insist (s.length == 1); + System.out.println("gcj-dbtool (" + + System.getProperty("java.vm.name") + + ") " + + System.getProperty("java.vm.version")); + System.out.println(); + System.out.println("Copyright 2011 Free Software Foundation, Inc."); + System.out.println("This is free software; see the source for copying conditions. There is NO"); + System.out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."); + return; + } + if (s[0].equals("--help")) + { + usage(System.out); + return; + } + + if (s[0].equals("-n")) + { + // Create a new database. + insist (s.length >= 2 && s.length <= 3); + + int capacity = 32749; + + if (s.length == 3) + { + capacity = Integer.parseInt(s[2]); + + if (capacity <= 2) + { + usage(System.err); + System.exit(1); + } + } + + try + { + PersistentByteMap b + = PersistentByteMap.emptyPersistentByteMap(new File(s[1]), + capacity, capacity*32); + } + catch (Exception e) + { + System.err.println ("error: could not create " + + s[1] + ": " + e.toString()); + System.exit(2); + } + return; + } + + if (s[0].equals("-a") || s[0].equals("-f")) + { + // Add a jar file to a database, creating it if necessary. + // Copies the database, adds the jar file to the copy, and + // then renames the new database over the old. + try + { + insist (s.length == 4); + File database = new File(s[1]); + database = database.getAbsoluteFile(); + File jar = new File(s[2]); + PersistentByteMap map; + if (database.isFile()) + map = new PersistentByteMap(database, + PersistentByteMap.AccessMode.READ_ONLY); + else + map = PersistentByteMap.emptyPersistentByteMap(database, + 100, 100*32); + File soFile = new File(s[3]); + if (! s[0].equals("-f") && ! soFile.isFile()) + throw new IllegalArgumentException(s[3] + " is not a file"); + map = addJar(jar, map, soFile); + } + catch (Exception e) + { + System.err.println ("error: could not update " + s[1] + + ": " + e.toString()); + System.exit(2); + } + return; + } + + if (s[0].equals("-t")) + { + // Test + try + { + insist (s.length == 2); + PersistentByteMap b + = new PersistentByteMap(new File(s[1]), + PersistentByteMap.AccessMode.READ_ONLY); + Iterator iterator = b.iterator(PersistentByteMap.ENTRIES); + + while (iterator.hasNext()) + { + PersistentByteMap.MapEntry entry + = (PersistentByteMap.MapEntry)iterator.next(); + byte[] key = (byte[])entry.getKey(); + byte[] value = (byte[])b.get(key); + if (! Arrays.equals (value, (byte[])entry.getValue())) + { + String err + = ("Key " + bytesToString(key) + " at bucket " + + entry.getBucket()); + + throw new RuntimeException(err); + } + } + } + catch (Exception e) + { + e.printStackTrace(); + System.exit(3); + } + return; + } + + if (s[0].equals("-m")) + { + // Merge databases. + insist (s.length >= 3 + || fileListFromStdin && s.length == 2); + try + { + File database = new File(s[1]); + database = database.getAbsoluteFile(); + File temp = File.createTempFile(database.getName(), "", + database.getParentFile()); + + int newSize = 0; + int newStringTableSize = 0; + Fileset files = getFiles(s, 2, fileListFromStdin, + filenameSeparator); + PersistentByteMap[] sourceMaps + = new PersistentByteMap[files.size()]; + + // Scan all the input files, calculating worst case string + // table and hash table use. + { + Iterator it = files.iterator(); + int i = 0; + while (it.hasNext()) + { + PersistentByteMap b + = new PersistentByteMap((File)it.next(), + PersistentByteMap.AccessMode.READ_ONLY); + newSize += b.size(); + newStringTableSize += b.stringTableSize(); + sourceMaps[i++] = b; + } + } + + newSize *= 1.5; // Scaling the new size by 1.5 results in + // fewer collisions. + PersistentByteMap map + = PersistentByteMap.emptyPersistentByteMap + (temp, newSize, newStringTableSize); + + for (int i = 0; i < sourceMaps.length; i++) + { + if (verbose) + System.err.println("adding " + sourceMaps[i].size() + + " elements from " + + sourceMaps[i].getFile()); + map.putAll(sourceMaps[i]); + } + map.close(); + temp.renameTo(database); + } + catch (Exception e) + { + e.printStackTrace(); + System.exit(3); + } + return; + } + + if (s[0].equals("-l")) + { + // List a database. + insist (s.length == 2); + try + { + PersistentByteMap b + = new PersistentByteMap(new File(s[1]), + PersistentByteMap.AccessMode.READ_ONLY); + + System.out.println ("Capacity: " + b.capacity()); + System.out.println ("Size: " + b.size()); + System.out.println (); + + System.out.println ("Elements: "); + Iterator iterator = b.iterator(PersistentByteMap.ENTRIES); + + while (iterator.hasNext()) + { + PersistentByteMap.MapEntry entry + = (PersistentByteMap.MapEntry)iterator.next(); + byte[] digest = (byte[])entry.getKey(); + System.out.print ("[" + entry.getBucket() + "] " + + bytesToString(digest) + + " -> "); + System.out.println (new String((byte[])entry.getValue())); + } + } + catch (Exception e) + { + System.err.println ("error: could not list " + + s[1] + ": " + e.toString()); + System.exit(2); + } + return; + } + + if (s[0].equals("-d")) + { + // For testing only: fill the byte map with random data. + insist (s.length == 2); + try + { + MessageDigest md = MessageDigest.getInstance("MD5"); + PersistentByteMap b + = new PersistentByteMap(new File(s[1]), + PersistentByteMap.AccessMode.READ_WRITE); + int N = b.capacity(); + byte[] bytes = new byte[1]; + byte digest[] = md.digest(bytes); + for (int i = 0; i < N; i++) + { + digest = md.digest(digest); + b.put(digest, digest); + } + } + catch (Exception e) + { + e.printStackTrace(); + System.exit(3); + } + return; + } + + if (s[0].equals("-p")) + { + insist (s.length == 1 || s.length == 2); + String result; + + if (s.length == 1) + result = System.getProperty("gnu.gcj.precompiled.db.path", ""); + else + result = (s[1] + + (s[1].endsWith(File.separator) ? "" : File.separator) + + getDbPathTail ()); + + System.out.println (result); + return; + } + + usage(System.err); + System.exit(1); + } + + private static native String getDbPathTail (); + + private static void insist(boolean ok) + { + if (! ok) + { + usage(System.err); + System.exit(1); + } + } + + private static void usage(PrintStream out) + { + out.println + ("gcj-dbtool: Manipulate gcj map database files\n" + + "\n" + + " Usage: \n" + + " gcj-dbtool -n file.gcjdb [size] - Create a new gcj map database\n" + + " gcj-dbtool -a file.gcjdb file.jar file.so\n" + + " - Add the contents of file.jar to a gcj map database\n" + + " gcj-dbtool -f file.gcjdb file.jar file.so\n" + + " - Add the contents of file.jar to a gcj map database\n" + + " gcj-dbtool -t file.gcjdb - Test a gcj map database\n" + + " gcj-dbtool -l file.gcjdb - List a gcj map database\n" + + " gcj-dbtool [-][-0] -m dest.gcjdb [source.gcjdb]...\n" + + " - Merge gcj map databases into dest\n" + + " Replaces dest\n" + + " To add to dest, include dest in the list of sources\n" + + " If the first arg is -, read the list from stdin\n" + + " If the first arg is -0, filenames separated by nul\n" + + " gcj-dbtool -p [LIBDIR] - Print default database name" + ); + } + + // Add a jar to a map. This copies the map first and returns a + // different map that contains the data. The original map is + // closed. + + private static PersistentByteMap + addJar(File f, PersistentByteMap b, File soFile) + throws Exception + { + MessageDigest md = MessageDigest.getInstance("MD5"); + + JarFile jar = new JarFile (f); + + int count = 0; + { + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) + { + JarEntry classfile = (JarEntry)entries.nextElement(); + if (classfile.getName().endsWith(".class")) + count++; + } + } + + if (verbose) + System.err.println("adding " + count + " elements from " + + f + " to " + b.getFile()); + + // Maybe resize the destination map. We're allowing plenty of + // extra space by using a loadFactor of 2. + b = resizeMap(b, (b.size() + count) * 2, true); + + Enumeration entries = jar.entries(); + + byte[] soFileName = soFile.getCanonicalPath().getBytes("UTF-8"); + while (entries.hasMoreElements()) + { + JarEntry classfile = (JarEntry)entries.nextElement(); + if (classfile.getName().endsWith(".class")) + { + InputStream str = jar.getInputStream(classfile); + int length = (int) classfile.getSize(); + if (length == -1) + throw new EOFException(); + + byte[] data = new byte[length]; + int pos = 0; + while (length - pos > 0) + { + int len = str.read(data, pos, length - pos); + if (len == -1) + throw new EOFException("Not enough data reading from: " + + classfile.getName()); + pos += len; + } + b.put(md.digest(data), soFileName); + } + } + return b; + } + + // Resize a map by creating a new one with the same data and + // renaming it. If close is true, close the original map. + + static PersistentByteMap resizeMap(PersistentByteMap m, int newCapacity, boolean close) + throws IOException, IllegalAccessException + { + newCapacity = Math.max(m.capacity(), newCapacity); + File name = m.getFile(); + File copy = File.createTempFile(name.getName(), "", name.getParentFile()); + try + { + PersistentByteMap dest + = PersistentByteMap.emptyPersistentByteMap + (copy, newCapacity, newCapacity*32); + dest.putAll(m); + dest.force(); + if (close) + m.close(); + copy.renameTo(name); + return dest; + } + catch (Exception e) + { + copy.delete(); + } + return null; + } + + + static String bytesToString(byte[] b) + { + StringBuffer hexBytes = new StringBuffer(); + int length = b.length; + for (int i = 0; i < length; ++i) + { + int v = b[i] & 0xff; + if (v < 16) + hexBytes.append('0'); + hexBytes.append(Integer.toHexString(v)); + } + return hexBytes.toString(); + } + + + // Return a Fileset, either from a String array or from System.in, + // depending on fileListFromStdin. + private static final Fileset getFiles(String[] s, int startPos, + boolean fileListFromStdin, + char separator) + { + if (fileListFromStdin) + return new Fileset(System.in, separator); + else + return new Fileset(s, startPos, s.length); + } +} + +// Parse a stream into tokens. The separator can be any char, and +// space is equivalent to any whitepace character. +class Tokenizer +{ + final Reader r; + final char separator; + + Tokenizer(Reader r, char separator) + { + this.r = r; + this.separator = separator; + } + + boolean isSeparator(int c) + { + if (Character.isWhitespace(separator)) + return Character.isWhitespace((char)c); + else + return c == separator; + } + + // Parse a token from the input stream. Return the empty string + // when the stream is exhausted. + String nextToken () + { + StringBuffer buf = new StringBuffer(); + int c; + try + { + while ((c = r.read()) != -1) + { + if (! isSeparator(c)) + { + buf.append((char)c); + break; + } + } + while ((c = r.read()) != -1) + { + if (isSeparator(c)) + break; + else + buf.append((char)c); + } + } + catch (java.io.IOException e) + { + } + return buf.toString(); + } +} + +// A Fileset is a container for a set of files; it can be created +// either from a string array or from an input stream, given a +// separator character. +class Fileset +{ + LinkedHashSet files = new LinkedHashSet(); + + Fileset (String[] s, int start, int end) + { + for (int i = start; i < end; i++) + { + files.add(new File(s[i])); + } + } + + Fileset (InputStream is, char separator) + { + Reader r = new BufferedReader(new InputStreamReader(is)); + Tokenizer st = new Tokenizer(r, separator); + String name; + while (! "".equals(name = st.nextToken())) + files.add(new File(name)); + } + + Iterator iterator() + { + return files.iterator(); + } + + int size() + { + return files.size(); + } +} diff --git a/libjava/gnu/gcj/tools/gcj_dbtool/Tokenizer.h b/libjava/gnu/gcj/tools/gcj_dbtool/Tokenizer.h new file mode 100644 index 000000000..7c5c411bb --- /dev/null +++ b/libjava/gnu/gcj/tools/gcj_dbtool/Tokenizer.h @@ -0,0 +1,40 @@ + +// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*- + +#ifndef __gnu_gcj_tools_gcj_dbtool_Tokenizer__ +#define __gnu_gcj_tools_gcj_dbtool_Tokenizer__ + +#pragma interface + +#include <java/lang/Object.h> +extern "Java" +{ + namespace gnu + { + namespace gcj + { + namespace tools + { + namespace gcj_dbtool + { + class Tokenizer; + } + } + } + } +} + +class gnu::gcj::tools::gcj_dbtool::Tokenizer : public ::java::lang::Object +{ + +public: // actually package-private + Tokenizer(::java::io::Reader *, jchar); + virtual jboolean isSeparator(jint); + virtual ::java::lang::String * nextToken(); + ::java::io::Reader * __attribute__((aligned(__alignof__( ::java::lang::Object)))) r; + jchar separator; +public: + static ::java::lang::Class class$; +}; + +#endif // __gnu_gcj_tools_gcj_dbtool_Tokenizer__ diff --git a/libjava/gnu/gcj/tools/gcj_dbtool/natMain.cc b/libjava/gnu/gcj/tools/gcj_dbtool/natMain.cc new file mode 100644 index 000000000..52ba0d7d1 --- /dev/null +++ b/libjava/gnu/gcj/tools/gcj_dbtool/natMain.cc @@ -0,0 +1,24 @@ +// natMain -- gcj-dbtool native code. + +/* Copyright (C) 2005 Free Software Foundation + + This file is part of gcj-dbtool. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the file "LIBGCJ_LICENSE" for +details. */ + +/* Author: Anthony Green <green@redhat.com>. */ + +#include <config.h> + +#include <gcj/cni.h> +#include <jvm.h> + +#include <gnu/gcj/tools/gcj_dbtool/Main.h> + +java::lang::String * +gnu::gcj::tools::gcj_dbtool::Main::getDbPathTail () +{ + return JvNewStringLatin1 (LIBGCJ_DEFAULT_DATABASE_PATH_TAIL); +} |