summaryrefslogtreecommitdiff
path: root/libjava/gnu/gcj/tools
diff options
context:
space:
mode:
authorupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
committerupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
commit554fd8c5195424bdbcabf5de30fdc183aba391bd (patch)
tree976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/gnu/gcj/tools
downloadcbb-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.java218
-rw-r--r--libjava/gnu/gcj/tools/gc_analyze/BytePtr.java115
-rw-r--r--libjava/gnu/gcj/tools/gc_analyze/ItemList.java72
-rw-r--r--libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java458
-rw-r--r--libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java359
-rw-r--r--libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java140
-rw-r--r--libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java112
-rw-r--r--libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java198
-rw-r--r--libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java45
-rw-r--r--libjava/gnu/gcj/tools/gcj_dbtool/Fileset.h42
-rw-r--r--libjava/gnu/gcj/tools/gcj_dbtool/Main.h55
-rw-r--r--libjava/gnu/gcj/tools/gcj_dbtool/Main.java541
-rw-r--r--libjava/gnu/gcj/tools/gcj_dbtool/Tokenizer.h40
-rw-r--r--libjava/gnu/gcj/tools/gcj_dbtool/natMain.cc24
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);
+}