public inbox for java-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [Patch] Java: Add heap dump and analyze support.
@ 2007-01-17 23:47 David Daney
  2007-01-23 22:19 ` Tom Tromey
  0 siblings, 1 reply; 17+ messages in thread
From: David Daney @ 2007-01-17 23:47 UTC (permalink / raw)
  To: Java Patch List, gcc-patches; +Cc: Johannes P. Schmidt

[-- Attachment #1: Type: text/plain, Size: 4282 bytes --]

This patch adds the ability to dump the contents of the runtime heap to 
a file from a java program.  Also included is a program (gc-analyze) 
that analyzes the dump file and prints out a bunch of things about the 
state of the heap.

Documentation about how to use this feature is in the included 
gc-analyze documentation.

In addition to the usual machine generated files, all the .h and class 
files are machine generated so are also omitted from the attached patch.

Tested on x86_64-linux and i686-linux.

I will re-test on those platforms as well as a mipsel-linux cross 
compiler before committing.

Comments?

OK to commit?

gcc/java:
2007-01-17  David Daney  <ddaney@avtrex.com>

    * Make-lang.in (JAVA_MANFILES): Added doc/gc-analyze.1.
    (java.maintainer-clean):Added gc-analyze.1.
    (.INTERMEDIATE): Added gc-analyze.pod.
    (gc-analyze.pod): New rule.
    (java.install-man): Install gc-analyze.1
    * gcj.texi: Added new section for the gc-analyze program.

libjava:
2007-01-17  Johannes Schmidt  <jschmidt@avtrex.com>
    David Daney  <ddaney@avtrex.com>

    * configure.ac: Added check for /proc/self/maps.
    * Makefile.am (bin_PROGRAMS): Added gc-analyze.
    (gc_analyze_SOURCES): New.
    (gc_analyze_LDFLAGS): New.
    (gc_analyze_LINK): New.
    (gc_analyze_LDADD): New.
    (gc_analyze_DEPENDENCIES): New.
    (nat_source_files): Added gnu/gcj/util/natGCInfo.cc.
    * Makefile.in: Regenerated.
    * configure.ac: Regenerated.
    * include/config.h.in: Regenerated.
    * sources.am: Regenerated.
    * scripts/makemake.tcl: Don't include gc-analyze classes in libgcj.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$SubstringComparator.h: New.
    * gnu/gcj/tools/gc_analyze/SymbolLookup.java: New.
    * gnu/gcj/tools/gc_analyze/BytePtr.h: New.
    * gnu/gcj/tools/gc_analyze/ItemList.h: New.
    * gnu/gcj/tools/gc_analyze/ToolPrefix.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.h: New.
    * gnu/gcj/tools/gc_analyze/ObjectMap.java: New.
    * gnu/gcj/tools/gc_analyze/SymbolLookup.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.h: New.
    * gnu/gcj/tools/gc_analyze/ObjectMap.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap.h: New.
    * gnu/gcj/tools/gc_analyze/SymbolTable.java: New.
    * gnu/gcj/tools/gc_analyze/SymbolTable.h: New.
    * gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparator.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap.java: New.
    * gnu/gcj/tools/gc_analyze/BytePtr.java: New.
    * gnu/gcj/tools/gc_analyze/ItemList.java: New.
    * gnu/gcj/tools/gc_analyze/ToolPrefix.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap$Range.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap.h: New.
    * gnu/gcj/util/GCInfo.java: New.
    * gnu/gcj/util/GCInfo.h: New.
    * gnu/gcj/util/natGCInfo.cc: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/SymbolTable.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.class: 
New.
    * 
classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparator.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap$Range.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BytePtr.class: New.
    * 
classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$SubstringComparator.class: 
New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ItemList.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ToolPrefix.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/SymbolLookup.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.class: 
New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ObjectMap.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap.class: New.
    * classpath/lib/gnu/gcj/util/GCInfo.class: New.


[-- Attachment #2: gc-analyze.diff.txt --]
[-- Type: text/plain, Size: 74504 bytes --]

Index: gcc/java/Make-lang.in
===================================================================
--- gcc/java/Make-lang.in	(revision 120765)
+++ gcc/java/Make-lang.in	(working copy)
@@ -139,7 +139,7 @@ java.pdf: doc/gcj.pdf
 java.html: $(build_htmldir)/java/index.html
 JAVA_MANFILES = doc/gcj.1 doc/jcf-dump.1 doc/gij.1 \
                 doc/jv-convert.1 doc/grmic.1 doc/grmiregistry.1 \
-		doc/gcj-dbtool.1
+		doc/gcj-dbtool.1 doc/gc-analyze.1
 
 java.man: $(JAVA_MANFILES)
 
@@ -208,6 +208,7 @@ java.maintainer-clean:
 	-rm -f $(docobjdir)/grmic.1
 	-rm -f $(docobjdir)/grmiregistry.1
 	-rm -f $(docobjdir)/gcj-dbtool.1
+	-rm -f $(docobjdir)/gc-analyze.1
 #\f
 # Stage hooks:
 # The main makefile has already created stage?/java.
@@ -318,7 +319,7 @@ $(build_htmldir)/java/index.html: $(TEXI
 	$(TEXI2HTML) -I $(gcc_docdir)/include -I $(srcdir)/java -o $(@D) $<
 
 .INTERMEDIATE: gcj.pod jcf-dump.pod gij.pod \
-  jv-convert.pod grmic.pod grmiregistry.pod gcj-dbtool.pod
+  jv-convert.pod grmic.pod grmiregistry.pod gcj-dbtool.pod gc-analyze.pod
 
 gcj.pod: java/gcj.texi
 	-$(TEXI2POD) -D gcj < $< > $@
@@ -334,15 +335,17 @@ grmiregistry.pod: java/gcj.texi
 	-$(TEXI2POD) -D grmiregistry < $< > $@
 gcj-dbtool.pod: java/gcj.texi
 	-$(TEXI2POD) -D gcj-dbtool < $< > $@
+gc-analyze.pod: java/gcj.texi
+	-$(TEXI2POD) -D gc-analyze < $< > $@
 
 # Install the man pages.
 java.install-man: installdirs \
                   $(DESTDIR)$(man1dir)/$(JAVA_INSTALL_NAME)$(man1ext) \
 		  $(JAVA_TARGET_INDEPENDENT_BIN_TOOLS:%=doc/%.1) \
 		  doc/gij.1 doc/jv-convert.1 doc/grmic.1 doc/grmiregistry.1 \
-		  doc/gcj-dbtool.1
+		  doc/gcj-dbtool.1 doc/gc-analyze.1
 	for tool in $(JAVA_TARGET_INDEPENDENT_BIN_TOOLS) \
-		gij jv-convert grmic grmiregistry gcj-dbtool ; do \
+		gij jv-convert grmic grmiregistry gcj-dbtool gc-analyze ; do \
 	  tool_transformed_name=`echo $$tool|sed '$(program_transform_name)'`; \
 	  man_name=$(DESTDIR)$(man1dir)/$${tool_transformed_name}$(man1ext); \
 	  rm -f $$man_name ; \
Index: gcc/java/gcj.texi
===================================================================
--- gcc/java/gcj.texi	(revision 120765)
+++ gcc/java/gcj.texi	(working copy)
@@ -17,7 +17,7 @@
 @c the word ``Java'.
 
 @c When this manual is copyrighted.
-@set copyrights-gcj 2001, 2002, 2003, 2004, 2005
+@set copyrights-gcj 2001, 2002, 2003, 2004, 2005, 2006, 2007
 
 @c Versions
 @set which-gcj GCC-@value{version-GCC}
@@ -79,6 +79,8 @@ man page gfdl(7).
                             Generate stubs for Remote Method Invocation.
 * grmiregistry: (gcj)Invoking grmiregistry.
                             The remote object registry.
+* gc-analyze: (gcj)Invoking gc-analyze.
+                            Analyze Garbage Collector (GC) memory dumps.
 @end direntry
 @end format
 
@@ -125,6 +127,7 @@ files and object files, and it can read 
 * Invoking jv-convert:: Converting from one encoding to another
 * Invoking grmic::      Generate stubs for Remote Method Invocation.
 * Invoking grmiregistry:: The remote object registry.
+* Invoking gc-analyze:: Analyze Garbage Collector (GC) memory dumps.
 * About CNI::           Description of the Compiled Native Interface
 * System properties::   Modifying runtime behavior of the libgcj library
 * Resources::		Where to look for more information
@@ -1412,6 +1415,80 @@ Print version information, then exit.
 @c man end
 
 
+@node Invoking gc-analyze
+@chapter Invoking gc-analyze
+
+@c man title gc-analyze Analyze Garbage Collector (GC) memory dumps
+
+@c man begin SYNOPSIS gc-analyze
+@command{gc-analyze} [@option{OPTION}] @dots{} [@var{file}]
+@ignore
+  [@option{-v}]
+  [@option{-p} @var{tool-prefix}]
+  [@option{-d} @var{directory}]
+@end ignore
+@c man end
+
+@c man begin DESCRIPTION gc-analyze
+
+@command{gc-analyze} prints an analysis of a GC memory dump to
+standard out.
+
+The memory dumps may be created by calling
+@code{gnu.gcj.util.GCInfo.enumerate(String namePrefix)} from java
+code.  A memory dump will be created on an out of memory condition if
+@code{gnu.gcj.util.GCInfo.setOOMDump(String namePrefix)} is called
+before the out of memory occurs.
+
+Running this program will create two files: @file{TestDump001} and
+@file{TestDump001.bytes}.
+
+@example
+import gnu.gcj.util.*;
+import java.util.*;
+
+public class GCDumpTest
+@{
+    static public void main(String args[])
+    @{
+        ArrayList<String> l = new ArrayList<String>(1000);
+        
+        for (int i = 1; i < 1500; i++) @{
+            l.add("This is string #" + i);
+        @}
+        GCInfo.enumerate("TestDump");
+    @}
+@}
+@end example
+
+The memory dump may then be displayed by running:
+
+@example
+gc-analyze -v TestDump001
+@end example
+
+@c FIXME: Add real information here.
+@c This really isn't much more than the --help output.
+
+@c man end
+
+@c man begin OPTIONS gc-analyze
+
+@table @gcctabopt
+@item -v
+Verbose output.
+
+@item -p @var{tool-prefix}
+Prefix added to the names of the @command{nm} and @command{readelf} commands.
+
+@item -d @var{directory}
+Directory that contains the executable and shared libraries used when
+the dump was generated.
+@end table
+
+@c man end
+
+
 @node About CNI
 @chapter About CNI
 
Index: libjava/scripts/makemake.tcl
===================================================================
--- libjava/scripts/makemake.tcl	(revision 120765)
+++ libjava/scripts/makemake.tcl	(working copy)
@@ -292,7 +292,8 @@ proc emit_package_rule {package} {
   puts ""
 
   if {$pkgname != "gnu/gcj/xlib" && $pkgname != "gnu/awt/xlib"
-      && $pkgname != "gnu/gcj/tools/gcj_dbtool"} {
+      && $pkgname != "gnu/gcj/tools/gcj_dbtool"
+      && $pkgname != "gnu/gcj/tools/gc_analyze"} {
     lappend package_files $lname
   }
 }
Index: libjava/configure.ac
===================================================================
--- libjava/configure.ac	(revision 120765)
+++ libjava/configure.ac	(working copy)
@@ -1019,10 +1019,14 @@ else
    if test x"$build" = x"$host"; then
      AC_CHECK_FILES(/proc/self/exe, [
        AC_DEFINE(HAVE_PROC_SELF_EXE, 1, [Define if you have /proc/self/exe])])
+     AC_CHECK_FILES(/proc/self/maps, [
+       AC_DEFINE(HAVE_PROC_SELF_MAPS, 1,
+         [Define if you have /proc/self/maps])])
    else
      case $host in
      *-linux*)
        AC_DEFINE(HAVE_PROC_SELF_EXE, 1, [Define if you have /proc/self/exe])
+       AC_DEFINE(HAVE_PROC_SELF_MAPS, 1, [Define if you have /proc/self/maps])
        ;;
      esac
    fi
Index: libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java	(revision 0)
@@ -0,0 +1,117 @@
+/* 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);
+  }
+
+  long getAddress(String symbol) throws IOException
+  {
+    return memoryMap.getAddress(this, symbol);
+  }
+
+  BytePtr getBytePtr(long addr, int length) throws IOException
+  {
+    return memoryMap.getBytePtr(addr, length);
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java	(revision 0)
@@ -0,0 +1,138 @@
+/* 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);
+      }
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java	(revision 0)
@@ -0,0 +1,375 @@
+/* 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);
+  }
+  
+  // search all symbol tables
+  // return -1 if none found
+  long getAddress(SymbolLookup lookup, String symbol) throws IOException
+  {
+    //    for (Iterator it=map.entrySet().iterator(); it.hasNext(); )
+    //      {
+    //        Map.Entry me = (Map.Entry)it.next();
+    //        String file = (String)me.getKey();
+    //        SymbolTable st = lookup.getSymbolTable(file);
+    //        int a = st.getAddress(symbol);
+    //        if (a != -1)
+    //          return a;
+    //      }
+    return -1;
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java	(revision 0)
@@ -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();
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/BlockMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/BlockMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/BlockMap.java	(revision 0)
@@ -0,0 +1,217 @@
+/* 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.charAt(0) == '#')
+          continue;
+        if (s == null)
+          break;
+        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));
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/BytePtr.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/BytePtr.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/BytePtr.java	(revision 0)
@@ -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());
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ItemList.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ItemList.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ItemList.java	(revision 0)
@@ -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();
+          }
+      }
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java	(revision 0)
@@ -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;
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java	(revision 0)
@@ -0,0 +1,445 @@
+/* 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 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;
+  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 void usage()
+  {
+    System.out.println(
+      "usage: gc-analyze [-v] [-p tool-prefix] [-d <directory>] filename");
+    System.out.println(
+      "  -v verbose; requires filename.bytes");
+    System.out.println(
+      "  -p tool-prefix; A string prepended to nm and readelf to obtain\n"
+      + "                  target specific versions of these commands");
+    System.out.println();
+    System.exit(1);
+  }
+
+  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;
+    }
+  }
+
+  public static void main(String[] args)
+  {
+    class Info
+    {
+      int size;
+      int count;
+    }
+    int total_space = 0;
+
+    boolean verbose = false;
+    try
+      {
+        // parse command-line
+        String filename = null;
+        for (int i=0; i<args.length; i++) {
+          if (args[i].equals("-h"))
+            usage();
+          if (args[i].equals("-d"))
+            {
+              ToolPrefix.pathPrefix = args[i+1];
+              i++;
+              continue;
+            }
+          if (args[i].equals("-p"))
+            {
+              ToolPrefix.toolPrefix = args[i+1];
+              i++;
+              continue;
+            }
+          if (args[i].equals("-v"))
+            {
+              verbose = true;
+              continue;
+            }
+          if (filename != null)
+            usage();
+          filename = args[i];
+        }
+        if (filename == null)
+          usage();
+
+        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();
+        int char_array_vtable = 0;
+
+        // 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("[C") == 0)
+              {
+                BytePtr p = lookup.getBytePtr(item.ptr, item.size);
+                if (p!=null) {
+                  int vtable = p.getInt(0);
+                  if (vtable!=0) {
+                    if (char_array_vtable==0)
+                      char_array_vtable = vtable;
+                    assert(vtable == char_array_vtable);
+                  }
+                }
+              }
+
+            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);
+              if (name.compareTo("gnu.gcj.runtime.MethodRef") == 0)
+                {
+                  BytePtr p2 = lookup.getBytePtr(item.ptr, item.size);
+                  int klass = p2.getInt(2);
+                  String name2 = lookup.getSymbol(klass);
+                  System.out.println("  referenced klass: " + name2);
+                }
+              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;
+  }
+}
Index: libjava/gnu/gcj/util/GCInfo.java
===================================================================
--- libjava/gnu/gcj/util/GCInfo.java	(revision 0)
+++ libjava/gnu/gcj/util/GCInfo.java	(revision 0)
@@ -0,0 +1,51 @@
+/* GCInfo.java -- Support for creating heap dumps.
+   Copyright (C) 2006  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.util;
+
+public class GCInfo
+{
+  private GCInfo()
+  {
+  }
+
+  public static synchronized void dump(String name)
+  {
+    dump0(name);
+  }
+  
+  public static native void dump0(String name);
+
+
+  /**
+   * Create a heap dump.
+   *
+   * @param namePrefix The filename prefix for the dump files.
+   */
+  public static synchronized void enumerate(String namePrefix)
+  {
+    enumerate0(namePrefix);
+  }
+  
+  public static native void enumerate0(String namePrefix);
+
+  /**
+   * Cause a heap dump if out-of-memory condition occurs.
+   *
+   * @param namePrefix The filename prefix for the dump files.  If
+   * null no dumps are created.
+   */
+  public static synchronized void setOOMDump(String namePrefix)
+  {
+    setOOMDump0(namePrefix);
+  }
+  
+  public static native void setOOMDump0(String namePrefix);
+  
+}
Index: libjava/gnu/gcj/util/natGCInfo.cc
===================================================================
--- libjava/gnu/gcj/util/natGCInfo.cc	(revision 0)
+++ libjava/gnu/gcj/util/natGCInfo.cc	(revision 0)
@@ -0,0 +1,461 @@
+/* natGCInfo.cc -- Native portion of support for creating heap dumps.
+   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.  */
+
+
+#include <config.h>
+
+#include <gcj/cni.h>
+
+#include <gnu/gcj/util/GCInfo.h>
+
+#ifdef HAVE_PROC_SELF_MAPS
+//
+// If /proc/self/maps does not exist we assume we are doomed and do nothing.
+//
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+//
+// Boehm GC includes.
+//
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+
+#ifdef FALSE
+#undef FALSE
+#endif
+
+extern "C" {
+#include "private/dbg_mlc.h"
+  int GC_n_set_marks(hdr* hhdr);
+  ptr_t GC_clear_stack(ptr_t p);
+  extern int GC_gcj_kind;
+  extern int GC_gcj_debug_kind;
+}
+
+#endif
+
+#ifdef HAVE_PROC_SELF_MAPS
+
+static int gc_ok = 1;
+
+typedef struct gc_debug_info
+{
+  int used;
+  int free;
+  int wasted;
+  int blocks;
+  FILE* fp;
+};
+
+static void
+GC_print_debug_callback(hblk *h, word user_data)
+{
+  hdr *hhdr = HDR(h);
+  size_t bytes = WORDS_TO_BYTES(hhdr -> hb_sz);
+
+  gc_debug_info *pinfo = (gc_debug_info *)user_data;
+
+  fprintf(pinfo->fp, "ptr = %#lx, kind = %d, size = %d, marks = %d\n",
+          (unsigned long)h, hhdr->hb_obj_kind, bytes, GC_n_set_marks(hhdr));
+}
+
+/*
+  this next section of definitions shouldn't really be here.
+  copied from boehmgc/allchblk.c
+*/
+
+# define UNIQUE_THRESHOLD 32
+# define HUGE_THRESHOLD 256
+# define FL_COMPRESSION 8
+# define N_HBLK_FLS (HUGE_THRESHOLD - UNIQUE_THRESHOLD)/FL_COMPRESSION \
+                         + UNIQUE_THRESHOLD
+#ifndef USE_MUNMAP
+extern "C" {
+  extern word GC_free_bytes[N_HBLK_FLS+1];
+}
+#endif
+
+# ifdef USE_MUNMAP
+#   define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0)
+# else  /* !USE_MMAP */
+#   define IS_MAPPED(hhdr) 1
+# endif /* USE_MUNMAP */
+
+static void
+GC_print_hblkfreelist_file(FILE *fp)
+{
+  struct hblk * h;
+  word total_free = 0;
+  hdr * hhdr;
+  word sz;
+  int i;
+    
+  fprintf(fp, "---------- Begin free map ----------\n");
+  for (i = 0; i <= N_HBLK_FLS; ++i)
+    {
+      h = GC_hblkfreelist[i];
+#ifdef USE_MUNMAP
+      if (0 != h)
+        fprintf (fp, "Free list %ld:\n", (unsigned long)i);
+#else
+      if (0 != h)
+        fprintf (fp, "Free list %ld (Total size %ld):\n",
+                 (unsigned long)i,
+                 (unsigned long)GC_free_bytes[i]);
+#endif
+      while (h != 0)
+        {
+          hhdr = HDR(h);
+          sz = hhdr -> hb_sz;
+          fprintf (fp, "\t0x%lx size %lu ", (unsigned long)h,
+                   (unsigned long)sz);
+          total_free += sz;
+
+          if (GC_is_black_listed (h, HBLKSIZE) != 0)
+            fprintf (fp, "start black listed\n");
+          else if (GC_is_black_listed(h, hhdr -> hb_sz) != 0)
+            fprintf (fp, "partially black listed\n");
+          else
+            fprintf (fp, "not black listed\n");
+
+          h = hhdr -> hb_next;
+        }
+    }
+#ifndef USE_MUNMAP
+  if (total_free != GC_large_free_bytes)
+    {
+      fprintf (fp, "GC_large_free_bytes = %lu (INCONSISTENT!!)\n",
+               (unsigned long) GC_large_free_bytes);
+    }
+#endif
+  fprintf (fp, "Total of %lu bytes on free list\n", (unsigned long)total_free);
+  fprintf (fp, "---------- End free map ----------\n");
+}
+
+static int GC_dump_count = 1;
+
+static void
+GC_print_debug_info_file(FILE* fp)
+{
+  gc_debug_info info;
+
+  memset(&info, 0, sizeof info);
+  info.fp = fp;
+
+  if (gc_ok)
+    GC_gcollect();
+  fprintf(info.fp, "---------- Begin block map ----------\n");
+  GC_apply_to_all_blocks(GC_print_debug_callback, (word)(void*)(&info));
+  //fprintf(fp, "#Total used %d free %d wasted %d\n", info.used, info.free, info.wasted);
+  //fprintf(fp, "#Total blocks %d; %dK bytes\n", info.blocks, info.blocks*4);
+  fprintf(info.fp, "---------- End block map ----------\n");
+
+  //fprintf(fp, "\n***Free blocks:\n");
+  //GC_print_hblkfreelist();
+}
+
+namespace gnu
+{
+  namespace gcj
+  {
+    namespace util
+    {
+      class GC_enumerator
+      {
+      public:
+        GC_enumerator(const char *name);
+        void enumerate();
+      private:
+        FILE* fp;
+        int bytes_fd;
+
+        void print_address_map();
+        void enumerate_callback(struct hblk *h);
+        static void enumerate_callback_adaptor(struct hblk *h, word dummy);
+      };
+    }
+  }
+}
+
+::gnu::gcj::util::GC_enumerator::GC_enumerator(const char *name)
+{
+  bytes_fd = -1;
+  fp = fopen (name, "w");
+  if (!fp)
+    {
+      printf ("GC_enumerator failed to open [%s]\n", name);
+      return;
+    }
+  printf ("GC_enumerator saving summary to [%s]\n", name);
+
+  // open heap file
+  char bytes_name[strlen(name) + 10];
+  sprintf (bytes_name, "%s.bytes", name);
+  bytes_fd = open (bytes_name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+  if (bytes_fd <= 0)
+    {
+      printf ("GC_enumerator failed to open [%s]\n", bytes_name);
+      return;
+    }
+  printf ("GC_enumerator saving heap contents to [%s]\n", bytes_name);
+}
+
+/*
+  sample format of /proc/self/maps
+
+  0063b000-00686000 rw-p 001fb000 03:01 81993      /avtrex/bin/dumppropapp
+  00686000-0072e000 rwxp 00000000 00:00 0 
+
+  These are parsed below as:
+  start   -end      xxxx xxxxxxxx  a:b xxxxxxxxxxxxxxx
+
+*/
+
+
+void
+::gnu::gcj::util::GC_enumerator::print_address_map()
+{
+  FILE* fm;
+  char buffer[128];
+
+  fprintf(fp, "---------- Begin address map ----------\n");
+
+  fm = fopen("/proc/self/maps", "r");
+  if (fm == NULL)
+    {
+      if (0 == strerror_r (errno, buffer, sizeof buffer))
+        fputs (buffer, fp);
+    }
+  else
+    {
+      while (fgets (buffer, sizeof buffer, fm) != NULL)
+        {
+          fputs (buffer, fp);
+          char *dash = strchr(buffer, '-');
+          char *colon = strchr(buffer, ':');
+          if (dash && colon && (strlen(buffer) > (colon - buffer) + 2U))
+            {
+              char *endp;
+              unsigned long start = strtoul(buffer, NULL, 16);
+              unsigned long end   = strtoul(dash + 1, &endp, 16);
+              unsigned long a     = strtoul(colon - 2, NULL, 16);
+              unsigned long b     = strtoul(colon + 1, NULL, 16);
+              // If it is an anonymous mapping 00:00 and both readable
+              // and writeable then dump the contents of the mapping
+              // to the bytes file.  Each block has a header of three
+              // unsigned longs:
+              // 0 - The number sizeof(unsigned long) to detect endianness and
+              //     structure layout.
+              // 1 - The offset in VM.
+              // 2 - The Length in bytes.
+              // Followed by the bytes.
+              if (!a && !b && endp < colon && 'r' == endp[1] && 'w' == endp[2])
+                {
+                  unsigned long t = sizeof(unsigned long);
+                  write(bytes_fd, (void*)&t, sizeof(t));
+                  write(bytes_fd, (void*)&start, sizeof(start));
+                  t = end - start;
+                  write(bytes_fd, (void*)&t, sizeof(t));
+                  write(bytes_fd, (void*)start, (end - start));
+                }
+            }
+        } 
+      fclose(fm);
+    }
+  fprintf(fp, "---------- End address map ----------\n");
+  fflush(fp);
+}
+
+void
+::gnu::gcj::util::GC_enumerator::enumerate()
+{
+  print_address_map();
+  fprintf(fp, "---------- Begin object map ----------\n");
+  if (gc_ok)
+    GC_gcollect();
+  GC_apply_to_all_blocks(enumerate_callback_adaptor, 
+                         (word)(void*)(this));
+  fprintf(fp, "---------- End object map ----------\n");
+  fflush(fp); 
+
+  GC_print_debug_info_file(fp);
+  fflush(fp); 
+  GC_print_hblkfreelist_file(fp);
+  fflush(fp); 
+
+  close(bytes_fd);
+  fclose(fp);
+
+  GC_clear_stack(0);
+}
+
+void
+::gnu::gcj::util::GC_enumerator::enumerate_callback_adaptor(struct hblk *h,
+                                                            word dummy)
+{
+  GC_enumerator* pinfo = (GC_enumerator*)dummy;
+  pinfo->enumerate_callback(h);
+}
+
+void
+::gnu::gcj::util::GC_enumerator::enumerate_callback(struct hblk *h)
+{
+  hdr * hhdr = HDR(h);
+  size_t bytes = WORDS_TO_BYTES(hhdr->hb_sz);
+  int i;
+
+  for (i = 0; i == 0 || (i + bytes <= HBLKSIZE); i += bytes)
+    {
+      int inUse = mark_bit_from_hdr(hhdr,BYTES_TO_WORDS(i));  // in use
+      char *ptr = (char*)h+i;                                 // address
+      int size = bytes;                                       // size
+      int kind = hhdr->hb_obj_kind;                           // kind
+      void *klass = 0;
+      void *data = 0;
+      if (kind == GC_gcj_kind
+          || kind == GC_gcj_debug_kind
+          || kind == GC_gcj_debug_kind+1)
+        {
+          void* v = *(void **)ptr;
+          if (v)
+            {
+              klass = *(void **)v;
+              data = *(void **)(ptr + sizeof(void*));
+            }
+        }
+      if (inUse)
+        fprintf (fp, "used = %d, ptr = %#lx, size = %d, kind = %d, "
+                 "klass = %#lx, data = %#lx\n", 
+                 inUse, (unsigned long)ptr, size, kind,
+                 (unsigned long)klass, (unsigned long)data);
+    }
+}
+
+/*
+ * Fill in a char[] with low bytes of the string characters.  These
+ * methods may be called while an OutOfMemoryError is being thrown, so
+ * we cannot call nice java methods to get the encoding of the string.
+ */
+static void
+J2A(::java::lang::String* str, char *dst)
+{
+  jchar * pchars = JvGetStringChars(str);
+  jint len = str->length();
+  int i;
+  for (i=0; i<len; i++)
+    dst[i] = (char)pchars[i];
+  dst[i] = 0;
+}
+
+void
+::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
+{
+  char n[name->length() + 1];
+  J2A(name, n);
+  
+  char temp[name->length() + 20];
+  sprintf(temp, "%s%03d", n, GC_dump_count++);
+  FILE* fp = fopen(temp, "w");
+
+  GC_print_debug_info_file(fp);
+
+  fclose(fp);
+}
+
+void
+::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
+{
+  char n[name->length() + 1];
+  J2A(name, n);
+  char temp[name->length() + 20];
+  sprintf(temp, "%s%03d", n, GC_dump_count++);
+
+  GC_enumerator x(temp);
+  x.enumerate();
+}
+
+static char *oomDumpName = NULL;
+
+static void *
+nomem_handler(size_t size)
+{
+  if (oomDumpName)
+    {
+      char temp[strlen(oomDumpName) + 20];
+      sprintf(temp, "%s%03d", temp, GC_dump_count++);
+      printf("nomem_handler(%d) called\n", size);
+      gc_ok--;
+      ::gnu::gcj::util::GC_enumerator x(temp);
+      x.enumerate();
+      gc_ok++;
+    }
+  return (void*)0;
+}
+
+void
+::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
+{
+  char *oldName = oomDumpName;
+  oomDumpName = NULL;
+  free (oldName);
+  
+  if (NULL == name)
+    return;
+  
+  char *n = (char *)malloc(name->length() + 1);
+
+  J2A(name, n);
+  oomDumpName = n;
+  GC_oom_fn = nomem_handler;
+}
+
+#else  // HAVE_PROC_SELF_MAPS
+
+void
+::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+void
+::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+void
+::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+#endif // HAVE_PROC_SELF_MAPS
+
Index: libjava/Makefile.am
===================================================================
--- libjava/Makefile.am	(revision 120765)
+++ libjava/Makefile.am	(working copy)
@@ -73,7 +73,7 @@ db_pathtail = gcj-$(gcc_version)/$(db_na
 if NATIVE
 bin_PROGRAMS = jv-convert gij grmic grmiregistry gcj-dbtool \
 	gappletviewer gjarsigner gkeytool gjar gjavah gnative2ascii \
-	gorbd grmid gserialver gtnameserv
+	gorbd grmid gserialver gtnameserv gc-analyze
 
 ## It is convenient to actually build and install the default database
 ## when gcj-dbtool is available.
@@ -639,6 +639,20 @@ gcj_dbtool_LDADD = gnu/gcj/tools/gcj_dbt
 ## linking this program.
 gcj_dbtool_DEPENDENCIES = gnu/gcj/tools/gcj_dbtool.lo libgcj.la libgcj.spec
 
+gc_analyze_SOURCES =
+
+## We need -nodefaultlibs because we want to avoid gcj's `-lgcj'.  We
+## need this because we are explicitly using libtool to link using the
+## `.la' file.
+gc_analyze_LDFLAGS = --main=gnu.gcj.tools.gc_analyze.MemoryAnalyze \
+        -rpath $(toolexeclibdir) -shared-libgcc $(THREADLDFLAGS)
+gc_analyze_LINK = $(GCJLINK)
+## See jv_convert_LDADD.
+gc_analyze_LDADD = gnu/gcj/tools/gc_analyze.lo -L$(here)/.libs libgcj.la
+## Depend on the spec file to make sure it is up to date before
+## linking this program.
+gc_analyze_DEPENDENCIES = gnu/gcj/tools/gc_analyze.lo libgcj.la libgcj.spec
+
 gij_SOURCES = 
 ## We need -nodefaultlibs because we want to avoid gcj's `-lgcj'.  We
 ## need this because we are explicitly using libtool to link using the
@@ -820,6 +834,7 @@ gnu/gcj/runtime/natSharedLibLoader.cc \
 gnu/gcj/runtime/natSystemClassLoader.cc \
 gnu/gcj/runtime/natStringBuffer.cc \
 gnu/gcj/util/natDebug.cc \
+gnu/gcj/util/natGCInfo.cc \
 gnu/java/lang/natMainThread.cc \
 gnu/java/lang/management/natVMClassLoadingMXBeanImpl.cc \
 gnu/java/lang/management/natVMCompilationMXBeanImpl.cc \

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-01-17 23:47 [Patch] Java: Add heap dump and analyze support David Daney
@ 2007-01-23 22:19 ` Tom Tromey
  2007-01-23 23:15   ` David Daney
  2007-01-29 23:58   ` David Daney
  0 siblings, 2 replies; 17+ messages in thread
From: Tom Tromey @ 2007-01-23 22:19 UTC (permalink / raw)
  To: David Daney; +Cc: Java Patch List, gcc-patches, Johannes P. Schmidt

>>>>> "David" == David Daney <ddaney@avtrex.com> writes:

Sorry for the delay on this.

David> This patch adds the ability to dump the contents of the runtime heap
David> to a file from a java program.

Nice.

I read through this.  I don't see many issues -- a few formatting
things (on irc you said these are fixed).

I'm a little concerned about the ad hoc approach to demangling, but
not enough to want to change it.

I'm also worried about the GC private bits copied into the native
code.  My main concern is that if the GC changes, we won't notice.

My final high-level worry is about security.  Right now it is possible
for any program to call the code that dumps the memory map.  I suppose
we can just sweep this under the blanket we're putting 'gnu.classpath'
stuff under.

Please comment on these.


I have a few more minor comments.

David> +@item -p @var{tool-prefix}
David> +Prefix added to the names of the @command{nm} and @command{readelf} commands.

I'd prefer to compile in the target, but I suppose this tool isn't
built in non-native builds, so that wouldn't help.  Darn.

David> +  // search all symbol tables
David> +  // return -1 if none found
David> +  long getAddress(SymbolLookup lookup, String symbol) throws IOException
David> +  {
David> +    //    for (Iterator it=map.entrySet().iterator(); it.hasNext(); )
David> +    //      {

Why is this commented out?

David> +        if (s.charAt(0) == '#')
David> +          continue;
David> +        if (s == null)
David> +          break;

These tests are in the wrong order.

David> +  static void usage()
David> +  {

Someday I suppose we should convert the tools in gcj to use the
classpath getopt code.

David> +              if (name.compareTo("gnu.gcj.runtime.MethodRef") == 0)

What is this?

David> +  public static String KindToName(int kind)

Non-java name... method names should start with a lower case letter.

David> +namespace gnu
David> +{
David> +  namespace gcj
David> +  {
David> +    namespace util
David> +    {
David> +      class GC_enumerator

This appears to be local to a single .cc file.  Do we need to put it
into a namespace like this?  I've always thought of these namespaces
as reserved for 'extern "Java"' code.

Tom

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-01-23 22:19 ` Tom Tromey
@ 2007-01-23 23:15   ` David Daney
  2007-01-24  1:08     ` Joel Dice
  2007-01-29 23:58   ` David Daney
  1 sibling, 1 reply; 17+ messages in thread
From: David Daney @ 2007-01-23 23:15 UTC (permalink / raw)
  To: tromey; +Cc: Java Patch List, gcc-patches, Johannes P. Schmidt

Tom Tromey wrote:
>>>>>> "David" == David Daney <ddaney@avtrex.com> writes:
> 
> Sorry for the delay on this.
> 
> David> This patch adds the ability to dump the contents of the runtime heap
> David> to a file from a java program.
> 
> Nice.
> 
> I read through this.  I don't see many issues -- a few formatting
> things (on irc you said these are fixed).
> 
> I'm a little concerned about the ad hoc approach to demangling, but
> not enough to want to change it.

There are several ad hoc things done in the analyzer.  My feeling was 
that if issues arise, we can fix them on a case by case basis.

> 
> I'm also worried about the GC private bits copied into the native
> code.  My main concern is that if the GC changes, we won't notice.

I look at this quite a bit.  The GC has code that prints the needed 
information to stdout.  Unfortunately we need it in a file.  I was 
thinking about modifying the GC so that you could supply a file 
descriptor, but that code has #ifdefs for every kind of OS known to man, 
and I was afraid I would break some obscure untestable configuration.

> 
> My final high-level worry is about security.  Right now it is possible
> for any program to call the code that dumps the memory map.  I suppose
> we can just sweep this under the blanket we're putting 'gnu.classpath'
> stuff under.
> 

I forget exactly why gnu.classpath.SystemProperties is not visible to 
user code, but its documentation states that it is not.  Who am I to argue?

I will add an explicit check to see if the caller has permission to call 
  things in GCInfo.


> Please comment on these.
> 
> 
> I have a few more minor comments.
> 
> David> +@item -p @var{tool-prefix}
> David> +Prefix added to the names of the @command{nm} and @command{readelf} commands.
> 
> I'd prefer to compile in the target, but I suppose this tool isn't
> built in non-native builds, so that wouldn't help.  Darn.


The gc-analyze program is target independent.  The same build of it can 
analyze dumps from x86_64-linux and mipsel-linux.  It should work with 
big-endian dumps as well, but I have not tested it.

> 
> David> +  // search all symbol tables
> David> +  // return -1 if none found
> David> +  long getAddress(SymbolLookup lookup, String symbol) throws IOException
> David> +  {
> David> +    //    for (Iterator it=map.entrySet().iterator(); it.hasNext(); )
> David> +    //      {
> 
> Why is this commented out?

I think it is related to the un-exec capability that I stripped out of 
this version of the analyzer.  I was going to submit that later.  I will 
clean it up.

> 
> David> +        if (s.charAt(0) == '#')
> David> +          continue;
> David> +        if (s == null)
> David> +          break;
> 
> These tests are in the wrong order.

Yes they are.  Case in point as to why code review is a good idea.


> 
> David> +  static void usage()
> David> +  {
> 
> Someday I suppose we should convert the tools in gcj to use the
> classpath getopt code.
> 
> David> +              if (name.compareTo("gnu.gcj.runtime.MethodRef") == 0)
> 
> What is this?


Ha! Good catch.  Leftover from the 3.4.3 based version.  Interesting 
classes with gnu.gcj.RawData don't have their references traversed. 
This was a special case we added to find some long forgotten problem.

The class no longer exists in 4.3 and the code was not 32/64 bit safe 
anyhow.  I will remove it.

> 
> David> +  public static String KindToName(int kind)
> 
> Non-java name... method names should start with a lower case letter.


OK.  I missed that one.

> 
> David> +namespace gnu
> David> +{
> David> +  namespace gcj
> David> +  {
> David> +    namespace util
> David> +    {
> David> +      class GC_enumerator
> 
> This appears to be local to a single .cc file.  Do we need to put it
> into a namespace like this?  I've always thought of these namespaces
> as reserved for 'extern "Java"' code.
> 

I didn't want to pollute the global namespace of libgcj.  We know what 
is in gnu.gcj.util so there will be no collisions there.

I will generate a new patch and retest.

David Daney.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-01-23 23:15   ` David Daney
@ 2007-01-24  1:08     ` Joel Dice
  2007-01-24  1:52       ` Tom Tromey
  0 siblings, 1 reply; 17+ messages in thread
From: Joel Dice @ 2007-01-24  1:08 UTC (permalink / raw)
  To: David Daney; +Cc: tromey, Java Patch List, gcc-patches, Johannes P. Schmidt

On Tue, 23 Jan 2007, David Daney wrote:
> Tom Tromey wrote:

<snip>

>> David> +namespace gnu
>> David> +{
>> David> +  namespace gcj
>> David> +  {
>> David> +    namespace util
>> David> +    {
>> David> +      class GC_enumerator
>> 
>> This appears to be local to a single .cc file.  Do we need to put it
>> into a namespace like this?  I've always thought of these namespaces
>> as reserved for 'extern "Java"' code.
>> 
>
> I didn't want to pollute the global namespace of libgcj.  We know what is in 
> gnu.gcj.util so there will be no collisions there.

Perhaps an anonymous namespace would be appropriate.

This is actually something I've been curious about with respect to libgcj. 
My understanding is that internal linkage is deprecated in C++, with 
anonymous namespaces as the prefered mechanism.  So I'm a bit suprised 
when I see new code that uses the former.  Is this just a matter of being 
consistent with old code, or is there a technical reason?  Does g++ 
produce better code when using internal linkage?

This patch is really cool, by the way.

  - Joel

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-01-24  1:08     ` Joel Dice
@ 2007-01-24  1:52       ` Tom Tromey
  2007-01-24  1:58         ` Chris Lattner
  0 siblings, 1 reply; 17+ messages in thread
From: Tom Tromey @ 2007-01-24  1:52 UTC (permalink / raw)
  To: Joel Dice; +Cc: David Daney, Java Patch List, gcc-patches, Johannes P. Schmidt

>>>>> "Joel" == Joel Dice <dicej@mailsnare.net> writes:

Joel> This is actually something I've been curious about with respect
Joel> to libgcj. My understanding is that internal linkage is
Joel> deprecated in C++, with anonymous namespaces as the prefered
Joel> mechanism.  So I'm a bit suprised when I see new code that uses
Joel> the former.  Is this just a matter of being consistent with old
Joel> code, or is there a technical reason?

libgcj is just old school.  I suppose if we were doing it over I would
push to put all the compiler API stuff into a namespace (instead of
having it all 'extern "C"') as well.

Joel> Does g++ produce better code when using internal linkage?

Not AFAIK.

Tom

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-01-24  1:52       ` Tom Tromey
@ 2007-01-24  1:58         ` Chris Lattner
  2007-01-24  2:32           ` David Daney
  0 siblings, 1 reply; 17+ messages in thread
From: Chris Lattner @ 2007-01-24  1:58 UTC (permalink / raw)
  To: tromey
  Cc: Joel Dice, David Daney, Java Patch List, gcc-patches,
	Johannes P. Schmidt


On Jan 23, 2007, at 3:42 PM, Tom Tromey wrote:

>>>>>> "Joel" == Joel Dice <dicej@mailsnare.net> writes:
>
> Joel> This is actually something I've been curious about with respect
> Joel> to libgcj. My understanding is that internal linkage is
> Joel> deprecated in C++, with anonymous namespaces as the prefered
> Joel> mechanism.  So I'm a bit suprised when I see new code that uses
> Joel> the former.  Is this just a matter of being consistent with old
> Joel> code, or is there a technical reason?
>
> libgcj is just old school.  I suppose if we were doing it over I would
> push to put all the compiler API stuff into a namespace (instead of
> having it all 'extern "C"') as well.
>
> Joel> Does g++ produce better code when using internal linkage?
>
> Not AFAIK.

G++ prior to 4.2 actually didn't mark symbols in anonymous namespaces  
as "internal", but it certainly does when symbols are marked  
'static'.  This could cause significant performance and size issues,  
depending on your code base.  In LLVM, we've resorted to marking  
classes in anonymous namespaces as attribute(hidden) to work around  
this (because you can't mark a class 'static').

See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=21581 and the related  
web of bugs.

-Chris

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-01-24  1:58         ` Chris Lattner
@ 2007-01-24  2:32           ` David Daney
  0 siblings, 0 replies; 17+ messages in thread
From: David Daney @ 2007-01-24  2:32 UTC (permalink / raw)
  To: Chris Lattner
  Cc: tromey, Joel Dice, Java Patch List, gcc-patches, Johannes P. Schmidt

Chris Lattner wrote:
>
> On Jan 23, 2007, at 3:42 PM, Tom Tromey wrote:
>
>>>>>>> "Joel" == Joel Dice <dicej@mailsnare.net> writes:
>>
>> Joel> This is actually something I've been curious about with respect
>> Joel> to libgcj. My understanding is that internal linkage is
>> Joel> deprecated in C++, with anonymous namespaces as the prefered
>> Joel> mechanism.  So I'm a bit suprised when I see new code that uses
>> Joel> the former.  Is this just a matter of being consistent with old
>> Joel> code, or is there a technical reason?
>>
>> libgcj is just old school.
So am I.  Why I remember when....

But seriously, I will change it to use the anonymous namespace as suggested.

>
> G++ prior to 4.2 actually didn't mark symbols in anonymous namespaces 
> as "internal", but it certainly does when symbols are marked 
> 'static'.  This could cause significant performance and size issues, 
> depending on your code base.  In LLVM, we've resorted to marking 
> classes in anonymous namespaces as attribute(hidden) to work around 
> this (because you can't mark a class 'static').
>
Thanks for this pointer.  It will undoubtedly be of use.

Thanks for the pointers,
David Daney

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-01-23 22:19 ` Tom Tromey
  2007-01-23 23:15   ` David Daney
@ 2007-01-29 23:58   ` David Daney
       [not found]     ` <BDA38860DCFD334EAEA905E44EE8E7EF6DD409@G3W0067.americas.hpqcorp.net>
  2007-02-07 22:37     ` Tom Tromey
  1 sibling, 2 replies; 17+ messages in thread
From: David Daney @ 2007-01-29 23:58 UTC (permalink / raw)
  To: tromey; +Cc: Java Patch List, gcc-patches, Johannes P. Schmidt

[-- Attachment #1: Type: text/plain, Size: 7741 bytes --]

New version of the patch:

Tom Tromey wrote:
>>>>>> "David" == David Daney <ddaney@avtrex.com> writes:
>>>>>>             
>
> Nice.
>
> I read through this.  I don't see many issues -- a few formatting
> things (on irc you said these are fixed).
>
> I'm a little concerned about the ad hoc approach to demangling, but
> not enough to want to change it.
>
> I'm also worried about the GC private bits copied into the native
> code.  My main concern is that if the GC changes, we won't notice.
>   
As I said before: I look at this quite a bit.  The GC has code that 
prints the needed information to stdout.  Unfortunately we need it in a 
file.  I was thinking about modifying the GC so that you could supply a 
file descriptor, but that code has #ifdefs for every kind of OS known to 
man, and I was afraid I would break some obscure untestable configuration.
> My final high-level worry is about security.  Right now it is possible
> for any program to call the code that dumps the memory map.  I suppose
> we can just sweep this under the blanket we're putting 'gnu.classpath'
> stuff under.
>
>   

Fixed.
> I have a few more minor comments.
>
> David> +@item -p @var{tool-prefix}
> David> +Prefix added to the names of the @command{nm} and @command{readelf} commands.
>
> I'd prefer to compile in the target, but I suppose this tool isn't
> built in non-native builds, so that wouldn't help.  Darn.
>
> David> +  // search all symbol tables
> David> +  // return -1 if none found
> David> +  long getAddress(SymbolLookup lookup, String symbol) throws IOException
> David> +  {
> David> +    //    for (Iterator it=map.entrySet().iterator(); it.hasNext(); )
> David> +    //      {
>
> Why is this commented out?
>   
Unused code.  It has been removed.
> David> +        if (s.charAt(0) == '#')
> David> +          continue;
> David> +        if (s == null)
> David> +          break;
>
> These tests are in the wrong order.
>   
Fixed.
> David> +  static void usage()
> David> +  {
>
> Someday I suppose we should convert the tools in gcj to use the
> classpath getopt code.
>   
Done.
> David> +              if (name.compareTo("gnu.gcj.runtime.MethodRef") == 0)
>
> What is this?
>   
Bogus code left over from 3.4.3 version.  It has been removed.
> David> +  public static String KindToName(int kind)
>
> Non-java name... method names should start with a lower case letter.
>   
Fixed.
> David> +namespace gnu
> David> +{
> David> +  namespace gcj
> David> +  {
> David> +    namespace util
> David> +    {
> David> +      class GC_enumerator
>
> This appears to be local to a single .cc file.  Do we need to put it
> into a namespace like this?  I've always thought of these namespaces
> as reserved for 'extern "Java"' code.
>   
My misunderstanding of namespaces.  Changed to anonymous namespace as 
suggested by Joel Dice and Chris Lattner.

Tested on x86_64-pc-linux-gnu (FC6) with no regressions.  Currently 
testing on i686-pc-linux-gnu.

OK to commit if it passes?
2007-01-29  David Daney  <ddaney@avtrex.com>
gcc/java:
    * Make-lang.in (JAVA_MANFILES): Added doc/gc-analyze.1.
    (java.maintainer-clean):Added gc-analyze.1.
    (.INTERMEDIATE): Added gc-analyze.pod.
    (gc-analyze.pod): New rule.
    (java.install-man): Install gc-analyze.1
    * gcj.texi: Added new section for the gc-analyze program.

libjava:
2007-01-29  Johannes Schmidt  <jschmidt@avtrex.com>
    David Daney  <ddaney@avtrex.com>

    * configure.ac: Added check for /proc/self/maps.
    * Makefile.am (bin_PROGRAMS): Added gc-analyze.
    (AM_GCJFLAGS): Set classpath to $(srcdir)/classpath/tools/classes.
    (gcjh.stamp): Same.
    (gc_analyze_SOURCES): New.
    (gc_analyze_LDFLAGS): New.
    (gc_analyze_LINK): New.
    (gc_analyze_LDADD): New.
    (gc_analyze_DEPENDENCIES): New.
    (nat_source_files): Added gnu/gcj/util/natGCInfo.cc.
    * Makefile.in: Regenerated.
    * configure: Regenerated.
    * include/config.h.in: Regenerated.
    * sources.am: Regenerated.
    * scripts/makemake.tcl: Don't include gc-analyze classes in libgcj.
    * gnu/classpath/tools/getopt/Parser.h: New.
    * gnu/classpath/tools/getopt/FileArgumentCallback.h: New.
    * gnu/classpath/tools/getopt/Parser$1.h: New.
    * gnu/classpath/tools/getopt/Parser$2.h: New.
    * gnu/classpath/tools/getopt/Parser$3.h: New.
    * gnu/classpath/tools/getopt/OptionGroup.h: New.
    * gnu/classpath/tools/getopt/OptionException.h: New.
    * gnu/classpath/tools/getopt/Messages.h: New.
    * gnu/classpath/tools/getopt/Option.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$SubstringComparator.h: New.
    * gnu/gcj/tools/gc_analyze/SymbolLookup.java: New.
    * gnu/gcj/tools/gc_analyze/BytePtr.h: New.
    * gnu/gcj/tools/gc_analyze/ItemList.h: New.
    * gnu/gcj/tools/gc_analyze/ToolPrefix.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$1.h: New
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$2.h: New
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$3.h: New
    * gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.h: New.
    * gnu/gcj/tools/gc_analyze/ObjectMap.java: New.
    * gnu/gcj/tools/gc_analyze/SymbolLookup.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.h: New.
    * gnu/gcj/tools/gc_analyze/ObjectMap.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap.h: New.
    * gnu/gcj/tools/gc_analyze/SymbolTable.java: New.
    * gnu/gcj/tools/gc_analyze/SymbolTable.h: New.
    * gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparator.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap.java: New.
    * gnu/gcj/tools/gc_analyze/BytePtr.java: New.
    * gnu/gcj/tools/gc_analyze/ItemList.java: New.
    * gnu/gcj/tools/gc_analyze/ToolPrefix.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap$Range.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap.h: New.
    * gnu/gcj/util/GCInfo.java: New.
    * gnu/gcj/util/GCInfo.h: New.
    * gnu/gcj/util/natGCInfo.cc: New.
    * gnu/gcj/util/UtilPermission.java: New.
    * gnu/gcj/util/UtilPermission.h: New.
    * classpath/lib/Makefile.am (gcj_tools_classpath): New variable.
    (compile_classpath): Add $(gcj_tools_classpath) to classpath.
    * classpath/lib/Makefile.in: Regenerated.
    * classpath/lib/gnu/gcj/tools/gc_analyze/SymbolTable.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.class: 
New.
    * 
classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparator.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap$Range.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BytePtr.class: New.
    * 
classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$SubstringComparator.class: 
New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ItemList.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ToolPrefix.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/SymbolLookup.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.class: 
New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ObjectMap.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap.class: New.
    * classpath/lib/gnu/gcj/util/GCInfo.class: New.
    * classpath/lib/gnu/gcj/util/UtilPermission.class: New.

As before machine generated files are omitted from the diff.


[-- Attachment #2: gc-analyze.diff.txt --]
[-- Type: text/plain, Size: 77501 bytes --]

Index: gcc/java/Make-lang.in
===================================================================
--- gcc/java/Make-lang.in	(revision 121258)
+++ gcc/java/Make-lang.in	(working copy)
@@ -139,7 +139,7 @@ java.pdf: doc/gcj.pdf
 java.html: $(build_htmldir)/java/index.html
 JAVA_MANFILES = doc/gcj.1 doc/jcf-dump.1 doc/gij.1 \
                 doc/jv-convert.1 doc/grmic.1 doc/grmiregistry.1 \
-		doc/gcj-dbtool.1
+		doc/gcj-dbtool.1 doc/gc-analyze.1
 
 java.man: $(JAVA_MANFILES)
 
@@ -208,6 +208,7 @@ java.maintainer-clean:
 	-rm -f $(docobjdir)/grmic.1
 	-rm -f $(docobjdir)/grmiregistry.1
 	-rm -f $(docobjdir)/gcj-dbtool.1
+	-rm -f $(docobjdir)/gc-analyze.1
 #\f
 # Stage hooks:
 # The main makefile has already created stage?/java.
@@ -318,7 +319,7 @@ $(build_htmldir)/java/index.html: $(TEXI
 	$(TEXI2HTML) -I $(gcc_docdir)/include -I $(srcdir)/java -o $(@D) $<
 
 .INTERMEDIATE: gcj.pod jcf-dump.pod gij.pod \
-  jv-convert.pod grmic.pod grmiregistry.pod gcj-dbtool.pod
+  jv-convert.pod grmic.pod grmiregistry.pod gcj-dbtool.pod gc-analyze.pod
 
 gcj.pod: java/gcj.texi
 	-$(TEXI2POD) -D gcj < $< > $@
@@ -334,15 +335,17 @@ grmiregistry.pod: java/gcj.texi
 	-$(TEXI2POD) -D grmiregistry < $< > $@
 gcj-dbtool.pod: java/gcj.texi
 	-$(TEXI2POD) -D gcj-dbtool < $< > $@
+gc-analyze.pod: java/gcj.texi
+	-$(TEXI2POD) -D gc-analyze < $< > $@
 
 # Install the man pages.
 java.install-man: installdirs \
                   $(DESTDIR)$(man1dir)/$(JAVA_INSTALL_NAME)$(man1ext) \
 		  $(JAVA_TARGET_INDEPENDENT_BIN_TOOLS:%=doc/%.1) \
 		  doc/gij.1 doc/jv-convert.1 doc/grmic.1 doc/grmiregistry.1 \
-		  doc/gcj-dbtool.1
+		  doc/gcj-dbtool.1 doc/gc-analyze.1
 	for tool in $(JAVA_TARGET_INDEPENDENT_BIN_TOOLS) \
-		gij jv-convert grmic grmiregistry gcj-dbtool ; do \
+		gij jv-convert grmic grmiregistry gcj-dbtool gc-analyze ; do \
 	  tool_transformed_name=`echo $$tool|sed '$(program_transform_name)'`; \
 	  man_name=$(DESTDIR)$(man1dir)/$${tool_transformed_name}$(man1ext); \
 	  rm -f $$man_name ; \
Index: gcc/java/gcj.texi
===================================================================
--- gcc/java/gcj.texi	(revision 121258)
+++ gcc/java/gcj.texi	(working copy)
@@ -17,7 +17,7 @@
 @c the word ``Java'.
 
 @c When this manual is copyrighted.
-@set copyrights-gcj 2001, 2002, 2003, 2004, 2005
+@set copyrights-gcj 2001, 2002, 2003, 2004, 2005, 2006, 2007
 
 @c Versions
 @set which-gcj GCC-@value{version-GCC}
@@ -79,6 +79,8 @@ man page gfdl(7).
                             Generate stubs for Remote Method Invocation.
 * grmiregistry: (gcj)Invoking grmiregistry.
                             The remote object registry.
+* gc-analyze: (gcj)Invoking gc-analyze.
+                            Analyze Garbage Collector (GC) memory dumps.
 @end direntry
 @end format
 
@@ -125,6 +127,7 @@ files and object files, and it can read 
 * Invoking jv-convert:: Converting from one encoding to another
 * Invoking grmic::      Generate stubs for Remote Method Invocation.
 * Invoking grmiregistry:: The remote object registry.
+* Invoking gc-analyze:: Analyze Garbage Collector (GC) memory dumps.
 * About CNI::           Description of the Compiled Native Interface
 * System properties::   Modifying runtime behavior of the libgcj library
 * Resources::		Where to look for more information
@@ -1412,6 +1415,89 @@ Print version information, then exit.
 @c man end
 
 
+@node Invoking gc-analyze
+@chapter Invoking gc-analyze
+
+@c man title gc-analyze Analyze Garbage Collector (GC) memory dumps
+
+@c man begin SYNOPSIS gc-analyze
+@command{gc-analyze} [@option{OPTION}] @dots{} [@var{file}]
+@ignore
+  [@option{-v}]
+  [@option{--verbose}]
+  [@option{-p} @var{tool-prefix}]
+  [@option{-d} @var{directory}]
+  [@option{--version}]
+  [@option{--help}]
+@end ignore
+@c man end
+
+@c man begin DESCRIPTION gc-analyze
+
+@command{gc-analyze} prints an analysis of a GC memory dump to
+standard out.
+
+The memory dumps may be created by calling
+@code{gnu.gcj.util.GCInfo.enumerate(String namePrefix)} from java
+code.  A memory dump will be created on an out of memory condition if
+@code{gnu.gcj.util.GCInfo.setOOMDump(String namePrefix)} is called
+before the out of memory occurs.
+
+Running this program will create two files: @file{TestDump001} and
+@file{TestDump001.bytes}.
+
+@example
+import gnu.gcj.util.*;
+import java.util.*;
+
+public class GCDumpTest
+@{
+    static public void main(String args[])
+    @{
+        ArrayList<String> l = new ArrayList<String>(1000);
+        
+        for (int i = 1; i < 1500; i++) @{
+            l.add("This is string #" + i);
+        @}
+        GCInfo.enumerate("TestDump");
+    @}
+@}
+@end example
+
+The memory dump may then be displayed by running:
+
+@example
+gc-analyze -v TestDump001
+@end example
+
+@c FIXME: Add real information here.
+@c This really isn't much more than the --help output.
+
+@c man end
+
+@c man begin OPTIONS gc-analyze
+
+@table @gcctabopt
+@item --verbose
+@itemx -v
+Verbose output.
+
+@item -p @var{tool-prefix}
+Prefix added to the names of the @command{nm} and @command{readelf} commands.
+
+@item -d @var{directory}
+Directory that contains the executable and shared libraries used when
+the dump was generated.
+
+@item --help
+Print a help message, then exit.
+
+@item --version
+Print version information, then exit.
+@end table
+
+@c man end
+
 @node About CNI
 @chapter About CNI
 
Index: libjava/scripts/makemake.tcl
===================================================================
--- libjava/scripts/makemake.tcl	(revision 121258)
+++ libjava/scripts/makemake.tcl	(working copy)
@@ -292,7 +292,8 @@ proc emit_package_rule {package} {
   puts ""
 
   if {$pkgname != "gnu/gcj/xlib" && $pkgname != "gnu/awt/xlib"
-      && $pkgname != "gnu/gcj/tools/gcj_dbtool"} {
+      && $pkgname != "gnu/gcj/tools/gcj_dbtool"
+      && $pkgname != "gnu/gcj/tools/gc_analyze"} {
     lappend package_files $lname
   }
 }
Index: libjava/configure.ac
===================================================================
--- libjava/configure.ac	(revision 121258)
+++ libjava/configure.ac	(working copy)
@@ -1000,10 +1000,14 @@ else
    if test x"$cross_compiling" = x"no"; then
      AC_CHECK_FILES(/proc/self/exe, [
        AC_DEFINE(HAVE_PROC_SELF_EXE, 1, [Define if you have /proc/self/exe])])
+     AC_CHECK_FILES(/proc/self/maps, [
+       AC_DEFINE(HAVE_PROC_SELF_MAPS, 1,
+         [Define if you have /proc/self/maps])])
    else
      case $host in
      *-linux*)
        AC_DEFINE(HAVE_PROC_SELF_EXE, 1, [Define if you have /proc/self/exe])
+       AC_DEFINE(HAVE_PROC_SELF_MAPS, 1, [Define if you have /proc/self/maps])
        ;;
      esac
    fi
Index: libjava/classpath/lib/Makefile.am
===================================================================
--- libjava/classpath/lib/Makefile.am	(revision 121258)
+++ libjava/classpath/lib/Makefile.am	(working copy)
@@ -9,10 +9,11 @@ propertyfiles :=  $(shell cd $(top_srcdi
 cssfiles := $(shell cd $(top_srcdir) && $(FIND) gnu java javax org -name \*\.css -print)
 ## GCJ LOCAL: prune .svn directories
 metafiles :=  $(shell cd $(top_srcdir)/resource && $(FIND) META-INF -name CVS -prune -o -name \*\.in -prune -o -type f -print | fgrep -v .svn)
+gcj_tools_classpath = $(top_srcdir)/tools/classes
 ## END GCJ LOCAL
 iconfiles :=  $(shell cd $(top_srcdir) && $(FIND) gnu/javax/swing/plaf/gtk/icons -name *.png -type f -print)
 
-compile_classpath = $(vm_classes):$(top_srcdir):$(top_srcdir)/external/w3c_dom:$(top_srcdir)/external/sax:$(top_srcdir)/external/relaxngDatatype:$(top_srcdir)/external/jsr166:.:$(USER_CLASSLIB):$(PATH_TO_ESCHER)
+compile_classpath = $(vm_classes):$(top_srcdir):$(top_srcdir)/external/w3c_dom:$(top_srcdir)/external/sax:$(top_srcdir)/external/relaxngDatatype:$(top_srcdir)/external/jsr166:.:$(USER_CLASSLIB):$(PATH_TO_ESCHER):$(gcj_tools_classpath)
 
 # handling source to bytecode compiler programs like gcj, jikes  and kjc
 if FOUND_GCJ
Index: libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java	(revision 0)
@@ -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);
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java	(revision 0)
@@ -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);
+      }
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java	(revision 0)
@@ -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);
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java	(revision 0)
@@ -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();
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/BlockMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/BlockMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/BlockMap.java	(revision 0)
@@ -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));
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/BytePtr.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/BytePtr.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/BytePtr.java	(revision 0)
@@ -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());
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ItemList.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ItemList.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ItemList.java	(revision 0)
@@ -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();
+          }
+      }
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java	(revision 0)
@@ -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;
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java	(revision 0)
@@ -0,0 +1,435 @@
+/* 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.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;
+    }
+  }
+
+  public static void main(String[] args)
+  {
+    class Info
+    {
+      int size;
+      int count;
+    }
+    int total_space = 0;
+
+    Parser optionParser = new Parser("gc-analyze",
+                                     "gc-analyze ("
+                                     + System.getProperty("java.vm.version")
+                                     + ")");
+    
+    optionParser.add(new Option('d',
+                                "Directory containing runtime objects",
+                                "directory")
+      {
+        public void parsed(String argument) throws OptionException
+        {
+          ToolPrefix.pathPrefix = argument;			
+        }
+      });
+    
+    optionParser.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;			
+        }
+      });
+    
+    optionParser.add(new Option("verbose", 'v',
+                                "Verbose output; requires filename.bytes")
+      {
+        public void parsed(String argument) throws OptionException
+        {
+          verbose = true;			
+        }
+      });
+    
+    optionParser.setHeader("usage: gc-analyze [-v] [-p tool-prefix] "
+                           + "[-d <directory>] filename");
+    
+    String rest[] = optionParser.parse(args);
+    
+    if (rest.length != 1)
+    {
+    	optionParser.printHelp();
+    	System.exit(1);
+    }
+    
+    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;
+  }
+}
Index: libjava/gnu/gcj/util/GCInfo.java
===================================================================
--- libjava/gnu/gcj/util/GCInfo.java	(revision 0)
+++ libjava/gnu/gcj/util/GCInfo.java	(revision 0)
@@ -0,0 +1,84 @@
+/* GCInfo.java -- Support for creating heap dumps.
+   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.util;
+
+public class GCInfo
+{
+  private GCInfo()
+  {
+  }
+
+  /**
+   * @throws SecurityException if there is a SecurityManager installed
+   * and BasicPermission("gnu.gcj.util.GCInfo.dumpHeap") is not
+   * granted.
+   */
+  private static void checkPermission()
+  {
+    SecurityManager sm = System.getSecurityManager();
+    if (sm != null)
+      sm.checkPermission(new UtilPermission("dumpHeap"));
+  }
+  
+
+  /**
+   * Dump a description of the heap state.
+   *
+   * @param namePrefix The filename prefix for the dump files.
+   *
+   * @throws SecurityException if there is a SecurityManager installed
+   * and BasicPermission("gnu.gcj.util.GCInfo.dumpHeap") is not
+   * granted.
+   */
+  public static synchronized void dump(String name)
+  {
+    checkPermission();
+    dump0(name);
+  }
+  
+  private static native void dump0(String name);
+
+
+  /**
+   * Create a heap dump.
+   *
+   * @param namePrefix The filename prefix for the dump files.
+   *
+   * @throws SecurityException if there is a SecurityManager installed
+   * and BasicPermission("gnu.gcj.util.GCInfo.dumpHeap") is not
+   * granted.
+   */
+  public static synchronized void enumerate(String namePrefix)
+  {
+    checkPermission();
+    enumerate0(namePrefix);
+  }
+  
+  private static native void enumerate0(String namePrefix);
+
+  /**
+   * Cause a heap dump if out-of-memory condition occurs.
+   *
+   * @param namePrefix The filename prefix for the dump files.  If
+   * null no dumps are created.
+   *
+   * @throws SecurityException if there is a SecurityManager installed
+   * and BasicPermission("gnu.gcj.util.GCInfo.dumpHeap") is not
+   * granted.
+   */
+  public static synchronized void setOOMDump(String namePrefix)
+  {
+    checkPermission();
+    setOOMDump0(namePrefix);
+  }
+  
+  private static native void setOOMDump0(String namePrefix);
+  
+}
Index: libjava/gnu/gcj/util/UtilPermission.java
===================================================================
--- libjava/gnu/gcj/util/UtilPermission.java	(revision 0)
+++ libjava/gnu/gcj/util/UtilPermission.java	(revision 0)
@@ -0,0 +1,20 @@
+/* GCInfo.java -- Support for creating heap dumps.
+   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.util;
+
+import java.security.BasicPermission;
+
+public class UtilPermission extends BasicPermission
+{
+  public UtilPermission(String name)
+  {
+    super(name);
+  }
+}
Index: libjava/gnu/gcj/util/natGCInfo.cc
===================================================================
--- libjava/gnu/gcj/util/natGCInfo.cc	(revision 0)
+++ libjava/gnu/gcj/util/natGCInfo.cc	(revision 0)
@@ -0,0 +1,454 @@
+/* natGCInfo.cc -- Native portion of support for creating heap dumps.
+   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.  */
+
+
+#include <config.h>
+
+#include <gcj/cni.h>
+
+#include <gnu/gcj/util/GCInfo.h>
+
+#ifdef HAVE_PROC_SELF_MAPS
+//
+// If /proc/self/maps does not exist we assume we are doomed and do nothing.
+//
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+//
+// Boehm GC includes.
+//
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+
+#ifdef FALSE
+#undef FALSE
+#endif
+
+extern "C" {
+#include "private/dbg_mlc.h"
+  int GC_n_set_marks(hdr* hhdr);
+  ptr_t GC_clear_stack(ptr_t p);
+  extern int GC_gcj_kind;
+  extern int GC_gcj_debug_kind;
+}
+
+#endif
+
+#ifdef HAVE_PROC_SELF_MAPS
+
+static int gc_ok = 1;
+
+typedef struct gc_debug_info
+{
+  int used;
+  int free;
+  int wasted;
+  int blocks;
+  FILE* fp;
+};
+
+static void
+GC_print_debug_callback(hblk *h, word user_data)
+{
+  hdr *hhdr = HDR(h);
+  size_t bytes = WORDS_TO_BYTES(hhdr -> hb_sz);
+
+  gc_debug_info *pinfo = (gc_debug_info *)user_data;
+
+  fprintf(pinfo->fp, "ptr = %#lx, kind = %d, size = %zd, marks = %d\n",
+          (unsigned long)h, hhdr->hb_obj_kind, bytes, GC_n_set_marks(hhdr));
+}
+
+/*
+  this next section of definitions shouldn't really be here.
+  copied from boehmgc/allchblk.c
+*/
+
+# define UNIQUE_THRESHOLD 32
+# define HUGE_THRESHOLD 256
+# define FL_COMPRESSION 8
+# define N_HBLK_FLS (HUGE_THRESHOLD - UNIQUE_THRESHOLD)/FL_COMPRESSION \
+                         + UNIQUE_THRESHOLD
+#ifndef USE_MUNMAP
+extern "C" {
+  extern word GC_free_bytes[N_HBLK_FLS+1];
+}
+#endif
+
+# ifdef USE_MUNMAP
+#   define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0)
+# else  /* !USE_MMAP */
+#   define IS_MAPPED(hhdr) 1
+# endif /* USE_MUNMAP */
+
+static void
+GC_print_hblkfreelist_file(FILE *fp)
+{
+  struct hblk * h;
+  word total_free = 0;
+  hdr * hhdr;
+  word sz;
+  int i;
+    
+  fprintf(fp, "---------- Begin free map ----------\n");
+  for (i = 0; i <= N_HBLK_FLS; ++i)
+    {
+      h = GC_hblkfreelist[i];
+#ifdef USE_MUNMAP
+      if (0 != h)
+        fprintf (fp, "Free list %ld:\n", (unsigned long)i);
+#else
+      if (0 != h)
+        fprintf (fp, "Free list %ld (Total size %ld):\n",
+                 (unsigned long)i,
+                 (unsigned long)GC_free_bytes[i]);
+#endif
+      while (h != 0)
+        {
+          hhdr = HDR(h);
+          sz = hhdr -> hb_sz;
+          fprintf (fp, "\t0x%lx size %lu ", (unsigned long)h,
+                   (unsigned long)sz);
+          total_free += sz;
+
+          if (GC_is_black_listed (h, HBLKSIZE) != 0)
+            fprintf (fp, "start black listed\n");
+          else if (GC_is_black_listed(h, hhdr -> hb_sz) != 0)
+            fprintf (fp, "partially black listed\n");
+          else
+            fprintf (fp, "not black listed\n");
+
+          h = hhdr -> hb_next;
+        }
+    }
+#ifndef USE_MUNMAP
+  if (total_free != GC_large_free_bytes)
+    {
+      fprintf (fp, "GC_large_free_bytes = %lu (INCONSISTENT!!)\n",
+               (unsigned long) GC_large_free_bytes);
+    }
+#endif
+  fprintf (fp, "Total of %lu bytes on free list\n", (unsigned long)total_free);
+  fprintf (fp, "---------- End free map ----------\n");
+}
+
+static int GC_dump_count = 1;
+
+static void
+GC_print_debug_info_file(FILE* fp)
+{
+  gc_debug_info info;
+
+  memset(&info, 0, sizeof info);
+  info.fp = fp;
+
+  if (gc_ok)
+    GC_gcollect();
+  fprintf(info.fp, "---------- Begin block map ----------\n");
+  GC_apply_to_all_blocks(GC_print_debug_callback, (word)(void*)(&info));
+  //fprintf(fp, "#Total used %d free %d wasted %d\n", info.used, info.free, info.wasted);
+  //fprintf(fp, "#Total blocks %d; %dK bytes\n", info.blocks, info.blocks*4);
+  fprintf(info.fp, "---------- End block map ----------\n");
+
+  //fprintf(fp, "\n***Free blocks:\n");
+  //GC_print_hblkfreelist();
+}
+
+namespace
+{
+  class  __attribute__ ((visibility ("hidden"))) GC_enumerator
+  {
+  public:
+    GC_enumerator(const char *name);
+    void enumerate();
+  private:
+    FILE* fp;
+    int bytes_fd;
+
+    void print_address_map();
+    void enumerate_callback(struct hblk *h);
+    static void enumerate_callback_adaptor(struct hblk *h, word dummy);
+  };
+}
+
+GC_enumerator::GC_enumerator(const char *name)
+{
+  bytes_fd = -1;
+  fp = fopen (name, "w");
+  if (!fp)
+    {
+      printf ("GC_enumerator failed to open [%s]\n", name);
+      return;
+    }
+  printf ("GC_enumerator saving summary to [%s]\n", name);
+
+  // open heap file
+  char bytes_name[strlen(name) + 10];
+  sprintf (bytes_name, "%s.bytes", name);
+  bytes_fd = open (bytes_name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+  if (bytes_fd <= 0)
+    {
+      printf ("GC_enumerator failed to open [%s]\n", bytes_name);
+      return;
+    }
+  printf ("GC_enumerator saving heap contents to [%s]\n", bytes_name);
+}
+
+/*
+  sample format of /proc/self/maps
+
+  0063b000-00686000 rw-p 001fb000 03:01 81993      /avtrex/bin/dumppropapp
+  00686000-0072e000 rwxp 00000000 00:00 0 
+
+  These are parsed below as:
+  start   -end      xxxx xxxxxxxx  a:b xxxxxxxxxxxxxxx
+
+*/
+
+
+void
+GC_enumerator::print_address_map()
+{
+  FILE* fm;
+  char buffer[128];
+
+  fprintf(fp, "---------- Begin address map ----------\n");
+
+  fm = fopen("/proc/self/maps", "r");
+  if (fm == NULL)
+    {
+      if (0 == strerror_r (errno, buffer, sizeof buffer))
+        fputs (buffer, fp);
+    }
+  else
+    {
+      while (fgets (buffer, sizeof buffer, fm) != NULL)
+        {
+          fputs (buffer, fp);
+          char *dash = strchr(buffer, '-');
+          char *colon = strchr(buffer, ':');
+          if (dash && colon && ((ptrdiff_t)strlen(buffer) > (colon - buffer) + 2))
+            {
+              char *endp;
+              unsigned long start = strtoul(buffer, NULL, 16);
+              unsigned long end   = strtoul(dash + 1, &endp, 16);
+              unsigned long a     = strtoul(colon - 2, NULL, 16);
+              unsigned long b     = strtoul(colon + 1, NULL, 16);
+              // If it is an anonymous mapping 00:00 and both readable
+              // and writeable then dump the contents of the mapping
+              // to the bytes file.  Each block has a header of three
+              // unsigned longs:
+              // 0 - The number sizeof(unsigned long) to detect endianness and
+              //     structure layout.
+              // 1 - The offset in VM.
+              // 2 - The Length in bytes.
+              // Followed by the bytes.
+              if (!a && !b && endp < colon && 'r' == endp[1] && 'w' == endp[2])
+                {
+                  unsigned long t = sizeof(unsigned long);
+                  write(bytes_fd, (void*)&t, sizeof(t));
+                  write(bytes_fd, (void*)&start, sizeof(start));
+                  t = end - start;
+                  write(bytes_fd, (void*)&t, sizeof(t));
+                  write(bytes_fd, (void*)start, (end - start));
+                }
+            }
+        } 
+      fclose(fm);
+    }
+  fprintf(fp, "---------- End address map ----------\n");
+  fflush(fp);
+}
+
+void
+GC_enumerator::enumerate()
+{
+  print_address_map();
+  fprintf(fp, "---------- Begin object map ----------\n");
+  if (gc_ok)
+    GC_gcollect();
+  GC_apply_to_all_blocks(enumerate_callback_adaptor, 
+                         (word)(void*)(this));
+  fprintf(fp, "---------- End object map ----------\n");
+  fflush(fp); 
+
+  GC_print_debug_info_file(fp);
+  fflush(fp); 
+  GC_print_hblkfreelist_file(fp);
+  fflush(fp); 
+
+  close(bytes_fd);
+  fclose(fp);
+
+  GC_clear_stack(0);
+}
+
+void
+GC_enumerator::enumerate_callback_adaptor(struct hblk *h,
+                                          word dummy)
+{
+  GC_enumerator* pinfo = (GC_enumerator*)dummy;
+  pinfo->enumerate_callback(h);
+}
+
+void
+GC_enumerator::enumerate_callback(struct hblk *h)
+{
+  hdr * hhdr = HDR(h);
+  size_t bytes = WORDS_TO_BYTES(hhdr->hb_sz);
+  int i;
+
+  for (i = 0; i == 0 || (i + bytes <= HBLKSIZE); i += bytes)
+    {
+      int inUse = mark_bit_from_hdr(hhdr,BYTES_TO_WORDS(i));  // in use
+      char *ptr = (char*)h+i;                                 // address
+      int kind = hhdr->hb_obj_kind;                           // kind
+      void *klass = 0;
+      void *data = 0;
+      if (kind == GC_gcj_kind
+          || kind == GC_gcj_debug_kind
+          || kind == GC_gcj_debug_kind+1)
+        {
+          void* v = *(void **)ptr;
+          if (v)
+            {
+              klass = *(void **)v;
+              data = *(void **)(ptr + sizeof(void*));
+            }
+        }
+      if (inUse)
+        fprintf (fp, "used = %d, ptr = %#lx, size = %zd, kind = %d, "
+                 "klass = %#lx, data = %#lx\n", 
+                 inUse, (unsigned long)ptr, bytes, kind,
+                 (unsigned long)klass, (unsigned long)data);
+    }
+}
+
+/*
+ * Fill in a char[] with low bytes of the string characters.  These
+ * methods may be called while an OutOfMemoryError is being thrown, so
+ * we cannot call nice java methods to get the encoding of the string.
+ */
+static void
+J2A(::java::lang::String* str, char *dst)
+{
+  jchar * pchars = JvGetStringChars(str);
+  jint len = str->length();
+  int i;
+  for (i=0; i<len; i++)
+    dst[i] = (char)pchars[i];
+  dst[i] = 0;
+}
+
+void
+::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
+{
+  char n[name->length() + 1];
+  J2A(name, n);
+  
+  char temp[name->length() + 20];
+  sprintf(temp, "%s%03d", n, GC_dump_count++);
+  FILE* fp = fopen(temp, "w");
+
+  GC_print_debug_info_file(fp);
+
+  fclose(fp);
+}
+
+void
+::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
+{
+  char n[name->length() + 1];
+  J2A(name, n);
+  char temp[name->length() + 20];
+  sprintf(temp, "%s%03d", n, GC_dump_count++);
+
+  GC_enumerator x(temp);
+  x.enumerate();
+}
+
+static char *oomDumpName = NULL;
+
+static void *
+nomem_handler(size_t size)
+{
+  if (oomDumpName)
+    {
+      char temp[strlen(oomDumpName) + 20];
+      sprintf(temp, "%s%03d", temp, GC_dump_count++);
+      printf("nomem_handler(%zd) called\n", size);
+      gc_ok--;
+      GC_enumerator x(temp);
+      x.enumerate();
+      gc_ok++;
+    }
+  return (void*)0;
+}
+
+void
+::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
+{
+  char *oldName = oomDumpName;
+  oomDumpName = NULL;
+  free (oldName);
+  
+  if (NULL == name)
+    return;
+  
+  char *n = (char *)malloc(name->length() + 1);
+
+  J2A(name, n);
+  oomDumpName = n;
+  GC_oom_fn = nomem_handler;
+}
+
+#else  // HAVE_PROC_SELF_MAPS
+
+void
+::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+void
+::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+void
+::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+#endif // HAVE_PROC_SELF_MAPS
+
Index: libjava/Makefile.am
===================================================================
--- libjava/Makefile.am	(revision 121258)
+++ libjava/Makefile.am	(working copy)
@@ -73,7 +73,7 @@ db_pathtail = gcj-$(gcc_version)/$(db_na
 if NATIVE
 bin_PROGRAMS = jv-convert gij grmic grmiregistry gcj-dbtool \
 	gappletviewer gjarsigner gkeytool gjar gjavah gnative2ascii \
-	gorbd grmid gserialver gtnameserv
+	gorbd grmid gserialver gtnameserv gc-analyze
 
 ## It is convenient to actually build and install the default database
 ## when gcj-dbtool is available.
@@ -144,8 +144,8 @@ AM_CXXFLAGS = \
 
 AM_GCJFLAGS = \
 	@LIBGCJ_JAVAFLAGS@ \
-	-fclasspath= -fbootclasspath=$(BOOTCLASSPATH) \
-	--encoding=UTF-8 \
+	-fclasspath=$(srcdir)/classpath/tools/classes \
+	 -fbootclasspath=$(BOOTCLASSPATH) --encoding=UTF-8 \
 	-Wno-deprecated -fbootstrap-classes
 
 AM_CFLAGS = @LIBGCJ_CFLAGS@
@@ -415,8 +415,9 @@ $(generic_header_files): gcjh.stamp
 
 gcjh.stamp: classpath/lib/compile-classes
 if JAVA_MAINTAINER_MODE
-	$(MYGCJH) --cni --all $(srcdir)/classpath/lib \
-	    --cmdfile=$(srcdir)/headers.txt -d $(srcdir) --force
+	$(MYGCJH) --classpath $(srcdir)/classpath/tools/classes --cni \
+	--all $(srcdir)/classpath/lib --cmdfile=$(srcdir)/headers.txt -d \
+	$(srcdir) --force
 endif
 	echo > gcjh.stamp
 
@@ -643,6 +644,22 @@ gcj_dbtool_LDADD = gnu/gcj/tools/gcj_dbt
 ## linking this program.
 gcj_dbtool_DEPENDENCIES = gnu/gcj/tools/gcj_dbtool.lo libgcj.la libgcj.spec
 
+gc_analyze_SOURCES =
+
+## We need -nodefaultlibs because we want to avoid gcj's `-lgcj'.  We
+## need this because we are explicitly using libtool to link using the
+## `.la' file.
+gc_analyze_LDFLAGS = --main=gnu.gcj.tools.gc_analyze.MemoryAnalyze \
+        -rpath $(toolexeclibdir) -shared-libgcc $(THREADLDFLAGS)
+gc_analyze_LINK = $(GCJLINK)
+## See jv_convert_LDADD.
+gc_analyze_LDADD = gnu/gcj/tools/gc_analyze.lo -L$(here)/.libs \
+	libgcj-tools.la libgcj.la
+## Depend on the spec file to make sure it is up to date before
+## linking this program.
+gc_analyze_DEPENDENCIES = gnu/gcj/tools/gc_analyze.lo libgcj-tools.la \
+	libgcj.la libgcj.spec
+
 gij_SOURCES = 
 ## We need -nodefaultlibs because we want to avoid gcj's `-lgcj'.  We
 ## need this because we are explicitly using libtool to link using the
@@ -827,6 +844,7 @@ gnu/gcj/runtime/natSharedLibLoader.cc \
 gnu/gcj/runtime/natSystemClassLoader.cc \
 gnu/gcj/runtime/natStringBuffer.cc \
 gnu/gcj/util/natDebug.cc \
+gnu/gcj/util/natGCInfo.cc \
 gnu/java/lang/natMainThread.cc \
 gnu/java/lang/management/natVMClassLoadingMXBeanImpl.cc \
 gnu/java/lang/management/natVMCompilationMXBeanImpl.cc \

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
       [not found]     ` <BDA38860DCFD334EAEA905E44EE8E7EF6DD409@G3W0067.americas.hpqcorp.net>
@ 2007-01-30 21:57       ` David Daney
  2007-01-30 22:21         ` Boehm, Hans
  0 siblings, 1 reply; 17+ messages in thread
From: David Daney @ 2007-01-30 21:57 UTC (permalink / raw)
  To: Boehm, Hans; +Cc: tromey, Johannes P. Schmidt, Java Patch List

Boehm, Hans wrote:
> I think the only partial excuse for the ugly way this is handled in the
> GC is that it tries to avoid stdio, in order to avoid accidentally
> calling malloc.  This probably doesn't matter for gcj, but it's
> important if malloc is actually an alias for GC_malloc, which it
> sometimes is.
> 
> Nonetheless, this should probably be parameterized with respect to file
> descriptors (probably not FILE *) on Posix systems, and the approximate
> equivalent on other systems.
> 
> GC7 handles this slightly better, but not a lot.
> 

I would propose that we commit the patch much as it is.

If the GC could add an entry point to allow the information to be 
printed to an arbitrary FILE* or file descriptor, then we would adjust 
the heap dumper when that is imported into GCC.

Does that sound reasonable?

David Daney

> Hans
> 
>> -----Original Message-----
>> From: java-patches-owner@gcc.gnu.org 
>> [mailto:java-patches-owner@gcc.gnu.org] On Behalf Of David Daney
>> Sent: Monday, January 29, 2007 3:58 PM
>> To: tromey@redhat.com
>> Cc: Java Patch List; gcc-patches@gcc.gnu.org; Johannes P. Schmidt
>> Subject: Re: [Patch] Java: Add heap dump and analyze support.
>>
>> New version of the patch:
>>
>> Tom Tromey wrote:
>>>>>>>> "David" == David Daney <ddaney@avtrex.com> writes:
>>>>>>>>             
>>> Nice.
>>>
>>> I read through this.  I don't see many issues -- a few formatting 
>>> things (on irc you said these are fixed).
>>>
>>> I'm a little concerned about the ad hoc approach to demangling, but 
>>> not enough to want to change it.
>>>
>>> I'm also worried about the GC private bits copied into the native 
>>> code.  My main concern is that if the GC changes, we won't notice.
>>>   
>> As I said before: I look at this quite a bit.  The GC has 
>> code that prints the needed information to stdout.  
>> Unfortunately we need it in a file.  I was thinking about 
>> modifying the GC so that you could supply a file descriptor, 
>> but that code has #ifdefs for every kind of OS known to man, 
>> and I was afraid I would break some obscure untestable configuration.
>>> My final high-level worry is about security.  Right now it 
>> is possible 
>>> for any program to call the code that dumps the memory map. 
>>  I suppose 
>>> we can just sweep this under the blanket we're putting 
>> 'gnu.classpath'
>>> stuff under.
>>>
>>>   
>> Fixed.
>>> I have a few more minor comments.
>>>
>>> David> +@item -p @var{tool-prefix}
>>> David> +Prefix added to the names of the @command{nm} and 
>> @command{readelf} commands.
>>> I'd prefer to compile in the target, but I suppose this tool isn't 
>>> built in non-native builds, so that wouldn't help.  Darn.
>>>
>>> David> +  // search all symbol tables
>>> David> +  // return -1 if none found
>>> David> +  long getAddress(SymbolLookup lookup, String 
>> symbol) throws 
>>> David> + IOException  {
>>> David> +    //    for (Iterator 
>> it=map.entrySet().iterator(); it.hasNext(); )
>>> David> +    //      {
>>>
>>> Why is this commented out?
>>>   
>> Unused code.  It has been removed.
>>> David> +        if (s.charAt(0) == '#')
>>> David> +          continue;
>>> David> +        if (s == null)
>>> David> +          break;
>>>
>>> These tests are in the wrong order.
>>>   
>> Fixed.
>>> David> +  static void usage()
>>> David> +  {
>>>
>>> Someday I suppose we should convert the tools in gcj to use the 
>>> classpath getopt code.
>>>   
>> Done.
>>> David> +              if 
>> (name.compareTo("gnu.gcj.runtime.MethodRef") 
>>> David> + == 0)
>>>
>>> What is this?
>>>   
>> Bogus code left over from 3.4.3 version.  It has been removed.
>>> David> +  public static String KindToName(int kind)
>>>
>>> Non-java name... method names should start with a lower case letter.
>>>   
>> Fixed.
>>> David> +namespace gnu
>>> David> +{
>>> David> +  namespace gcj
>>> David> +  {
>>> David> +    namespace util
>>> David> +    {
>>> David> +      class GC_enumerator
>>>
>>> This appears to be local to a single .cc file.  Do we need 
>> to put it 
>>> into a namespace like this?  I've always thought of these 
>> namespaces 
>>> as reserved for 'extern "Java"' code.
>>>   
>> My misunderstanding of namespaces.  Changed to anonymous 
>> namespace as suggested by Joel Dice and Chris Lattner.
>>
>> Tested on x86_64-pc-linux-gnu (FC6) with no regressions.  
>> Currently testing on i686-pc-linux-gnu.
>>
>> OK to commit if it passes?
>> 2007-01-29  David Daney  <ddaney@avtrex.com>
>> gcc/java:
>>     * Make-lang.in (JAVA_MANFILES): Added doc/gc-analyze.1.
>>     (java.maintainer-clean):Added gc-analyze.1.
>>     (.INTERMEDIATE): Added gc-analyze.pod.
>>     (gc-analyze.pod): New rule.
>>     (java.install-man): Install gc-analyze.1
>>     * gcj.texi: Added new section for the gc-analyze program.
>>
>> libjava:
>> 2007-01-29  Johannes Schmidt  <jschmidt@avtrex.com>
>>     David Daney  <ddaney@avtrex.com>
>>
>>     * configure.ac: Added check for /proc/self/maps.
>>     * Makefile.am (bin_PROGRAMS): Added gc-analyze.
>>     (AM_GCJFLAGS): Set classpath to $(srcdir)/classpath/tools/classes.
>>     (gcjh.stamp): Same.
>>     (gc_analyze_SOURCES): New.
>>     (gc_analyze_LDFLAGS): New.
>>     (gc_analyze_LINK): New.
>>     (gc_analyze_LDADD): New.
>>     (gc_analyze_DEPENDENCIES): New.
>>     (nat_source_files): Added gnu/gcj/util/natGCInfo.cc.
>>     * Makefile.in: Regenerated.
>>     * configure: Regenerated.
>>     * include/config.h.in: Regenerated.
>>     * sources.am: Regenerated.
>>     * scripts/makemake.tcl: Don't include gc-analyze classes 
>> in libgcj.
>>     * gnu/classpath/tools/getopt/Parser.h: New.
>>     * gnu/classpath/tools/getopt/FileArgumentCallback.h: New.
>>     * gnu/classpath/tools/getopt/Parser$1.h: New.
>>     * gnu/classpath/tools/getopt/Parser$2.h: New.
>>     * gnu/classpath/tools/getopt/Parser$3.h: New.
>>     * gnu/classpath/tools/getopt/OptionGroup.h: New.
>>     * gnu/classpath/tools/getopt/OptionException.h: New.
>>     * gnu/classpath/tools/getopt/Messages.h: New.
>>     * gnu/classpath/tools/getopt/Option.h: New.
>>     * 
>> gnu/gcj/tools/gc_analyze/MemoryAnalyze$SubstringComparator.h: New.
>>     * gnu/gcj/tools/gc_analyze/SymbolLookup.java: New.
>>     * gnu/gcj/tools/gc_analyze/BytePtr.h: New.
>>     * gnu/gcj/tools/gc_analyze/ItemList.h: New.
>>     * gnu/gcj/tools/gc_analyze/ToolPrefix.h: New.
>>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze.h: New.
>>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze$1.h: New
>>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze$2.h: New
>>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze$3.h: New
>>     * gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.h: New.
>>     * gnu/gcj/tools/gc_analyze/ObjectMap.java: New.
>>     * gnu/gcj/tools/gc_analyze/SymbolLookup.h: New.
>>     * gnu/gcj/tools/gc_analyze/MemoryMap.java: New.
>>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.h: New.
>>     * gnu/gcj/tools/gc_analyze/ObjectMap.h: New.
>>     * gnu/gcj/tools/gc_analyze/MemoryMap.h: New.
>>     * gnu/gcj/tools/gc_analyze/SymbolTable.java: New.
>>     * gnu/gcj/tools/gc_analyze/SymbolTable.h: New.
>>     * gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.h: New.
>>     * gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparator.h: New.
>>     * gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.h: New.
>>     * gnu/gcj/tools/gc_analyze/BlockMap.java: New.
>>     * gnu/gcj/tools/gc_analyze/BytePtr.java: New.
>>     * gnu/gcj/tools/gc_analyze/ItemList.java: New.
>>     * gnu/gcj/tools/gc_analyze/ToolPrefix.java: New.
>>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze.java: New.
>>     * gnu/gcj/tools/gc_analyze/MemoryMap$Range.h: New.
>>     * gnu/gcj/tools/gc_analyze/BlockMap.h: New.
>>     * gnu/gcj/util/GCInfo.java: New.
>>     * gnu/gcj/util/GCInfo.h: New.
>>     * gnu/gcj/util/natGCInfo.cc: New.
>>     * gnu/gcj/util/UtilPermission.java: New.
>>     * gnu/gcj/util/UtilPermission.h: New.
>>     * classpath/lib/Makefile.am (gcj_tools_classpath): New variable.
>>     (compile_classpath): Add $(gcj_tools_classpath) to classpath.
>>     * classpath/lib/Makefile.in: Regenerated.
>>     * classpath/lib/gnu/gcj/tools/gc_analyze/SymbolTable.class: New.
>>     * 
>> classpath/lib/gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.class: 
>> New.
>>     *
>> classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparat
>> or.class: New.
>>     * 
>> classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.class: New.
>>     * 
>> classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap$Range.class: New.
>>     * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap.class: New.
>>     * classpath/lib/gnu/gcj/tools/gc_analyze/BytePtr.class: New.
>>     *
>> classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$Substring
>> Comparator.class: 
>> New.
>>     * classpath/lib/gnu/gcj/tools/gc_analyze/ItemList.class: New.
>>     * classpath/lib/gnu/gcj/tools/gc_analyze/ToolPrefix.class: New.
>>     * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze.class: New.
>>     * 
>> classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.class: New.
>>     * classpath/lib/gnu/gcj/tools/gc_analyze/SymbolLookup.class: New.
>>     * 
>> classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.class: 
>> New.
>>     * classpath/lib/gnu/gcj/tools/gc_analyze/ObjectMap.class: New.
>>     * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap.class: New.
>>     * classpath/lib/gnu/gcj/util/GCInfo.class: New.
>>     * classpath/lib/gnu/gcj/util/UtilPermission.class: New.
>>
>> As before machine generated files are omitted from the diff.
>>
>>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* RE: [Patch] Java: Add heap dump and analyze support.
  2007-01-30 21:57       ` David Daney
@ 2007-01-30 22:21         ` Boehm, Hans
  0 siblings, 0 replies; 17+ messages in thread
From: Boehm, Hans @ 2007-01-30 22:21 UTC (permalink / raw)
  To: David Daney; +Cc: tromey, Johannes P. Schmidt, Java Patch List

Sounds fine to me.  I just didn't want to discourage patches to the GC
in this area.

Hans

> -----Original Message-----
> From: David Daney [mailto:ddaney@avtrex.com] 
> Sent: Tuesday, January 30, 2007 1:57 PM
> To: Boehm, Hans
> Cc: tromey@redhat.com; Johannes P. Schmidt; Java Patch List
> Subject: Re: [Patch] Java: Add heap dump and analyze support.
> 
> Boehm, Hans wrote:
> > I think the only partial excuse for the ugly way this is handled in 
> > the GC is that it tries to avoid stdio, in order to avoid 
> accidentally 
> > calling malloc.  This probably doesn't matter for gcj, but it's 
> > important if malloc is actually an alias for GC_malloc, which it 
> > sometimes is.
> > 
> > Nonetheless, this should probably be parameterized with respect to 
> > file descriptors (probably not FILE *) on Posix systems, and the 
> > approximate equivalent on other systems.
> > 
> > GC7 handles this slightly better, but not a lot.
> > 
> 
> I would propose that we commit the patch much as it is.
> 
> If the GC could add an entry point to allow the information 
> to be printed to an arbitrary FILE* or file descriptor, then 
> we would adjust the heap dumper when that is imported into GCC.
> 
> Does that sound reasonable?
> 
> David Daney
> 
> > Hans
> > 
> >> -----Original Message-----
> >> From: java-patches-owner@gcc.gnu.org 
> >> [mailto:java-patches-owner@gcc.gnu.org] On Behalf Of David Daney
> >> Sent: Monday, January 29, 2007 3:58 PM
> >> To: tromey@redhat.com
> >> Cc: Java Patch List; gcc-patches@gcc.gnu.org; Johannes P. Schmidt
> >> Subject: Re: [Patch] Java: Add heap dump and analyze support.
> >>
> >> New version of the patch:
> >>
> >> Tom Tromey wrote:
> >>>>>>>> "David" == David Daney <ddaney@avtrex.com> writes:
> >>>>>>>>             
> >>> Nice.
> >>>
> >>> I read through this.  I don't see many issues -- a few formatting 
> >>> things (on irc you said these are fixed).
> >>>
> >>> I'm a little concerned about the ad hoc approach to 
> demangling, but 
> >>> not enough to want to change it.
> >>>
> >>> I'm also worried about the GC private bits copied into the native 
> >>> code.  My main concern is that if the GC changes, we won't notice.
> >>>   
> >> As I said before: I look at this quite a bit.  The GC has 
> code that 
> >> prints the needed information to stdout.
> >> Unfortunately we need it in a file.  I was thinking about 
> modifying 
> >> the GC so that you could supply a file descriptor, but 
> that code has 
> >> #ifdefs for every kind of OS known to man, and I was 
> afraid I would 
> >> break some obscure untestable configuration.
> >>> My final high-level worry is about security.  Right now it
> >> is possible
> >>> for any program to call the code that dumps the memory map. 
> >>  I suppose
> >>> we can just sweep this under the blanket we're putting
> >> 'gnu.classpath'
> >>> stuff under.
> >>>
> >>>   
> >> Fixed.
> >>> I have a few more minor comments.
> >>>
> >>> David> +@item -p @var{tool-prefix}
> >>> David> +Prefix added to the names of the @command{nm} and
> >> @command{readelf} commands.
> >>> I'd prefer to compile in the target, but I suppose this 
> tool isn't 
> >>> built in non-native builds, so that wouldn't help.  Darn.
> >>>
> >>> David> +  // search all symbol tables  // return -1 if 
> none found  
> >>> David> + long getAddress(SymbolLookup lookup, String
> >> symbol) throws
> >>> David> + IOException  {
> >>> David> +    //    for (Iterator 
> >> it=map.entrySet().iterator(); it.hasNext(); )
> >>> David> +    //      {
> >>>
> >>> Why is this commented out?
> >>>   
> >> Unused code.  It has been removed.
> >>> David> +        if (s.charAt(0) == '#')
> >>> David> +          continue;
> >>> David> +        if (s == null)
> >>> David> +          break;
> >>>
> >>> These tests are in the wrong order.
> >>>   
> >> Fixed.
> >>> David> +  static void usage()
> >>> David> +  {
> >>>
> >>> Someday I suppose we should convert the tools in gcj to use the 
> >>> classpath getopt code.
> >>>   
> >> Done.
> >>> David> +              if
> >> (name.compareTo("gnu.gcj.runtime.MethodRef")
> >>> David> + == 0)
> >>>
> >>> What is this?
> >>>   
> >> Bogus code left over from 3.4.3 version.  It has been removed.
> >>> David> +  public static String KindToName(int kind)
> >>>
> >>> Non-java name... method names should start with a lower 
> case letter.
> >>>   
> >> Fixed.
> >>> David> +namespace gnu
> >>> David> +{
> >>> David> +  namespace gcj
> >>> David> +  {
> >>> David> +    namespace util
> >>> David> +    {
> >>> David> +      class GC_enumerator
> >>>
> >>> This appears to be local to a single .cc file.  Do we need
> >> to put it
> >>> into a namespace like this?  I've always thought of these
> >> namespaces
> >>> as reserved for 'extern "Java"' code.
> >>>   
> >> My misunderstanding of namespaces.  Changed to anonymous 
> namespace as 
> >> suggested by Joel Dice and Chris Lattner.
> >>
> >> Tested on x86_64-pc-linux-gnu (FC6) with no regressions.  
> >> Currently testing on i686-pc-linux-gnu.
> >>
> >> OK to commit if it passes?
> >> 2007-01-29  David Daney  <ddaney@avtrex.com>
> >> gcc/java:
> >>     * Make-lang.in (JAVA_MANFILES): Added doc/gc-analyze.1.
> >>     (java.maintainer-clean):Added gc-analyze.1.
> >>     (.INTERMEDIATE): Added gc-analyze.pod.
> >>     (gc-analyze.pod): New rule.
> >>     (java.install-man): Install gc-analyze.1
> >>     * gcj.texi: Added new section for the gc-analyze program.
> >>
> >> libjava:
> >> 2007-01-29  Johannes Schmidt  <jschmidt@avtrex.com>
> >>     David Daney  <ddaney@avtrex.com>
> >>
> >>     * configure.ac: Added check for /proc/self/maps.
> >>     * Makefile.am (bin_PROGRAMS): Added gc-analyze.
> >>     (AM_GCJFLAGS): Set classpath to 
> $(srcdir)/classpath/tools/classes.
> >>     (gcjh.stamp): Same.
> >>     (gc_analyze_SOURCES): New.
> >>     (gc_analyze_LDFLAGS): New.
> >>     (gc_analyze_LINK): New.
> >>     (gc_analyze_LDADD): New.
> >>     (gc_analyze_DEPENDENCIES): New.
> >>     (nat_source_files): Added gnu/gcj/util/natGCInfo.cc.
> >>     * Makefile.in: Regenerated.
> >>     * configure: Regenerated.
> >>     * include/config.h.in: Regenerated.
> >>     * sources.am: Regenerated.
> >>     * scripts/makemake.tcl: Don't include gc-analyze classes in 
> >> libgcj.
> >>     * gnu/classpath/tools/getopt/Parser.h: New.
> >>     * gnu/classpath/tools/getopt/FileArgumentCallback.h: New.
> >>     * gnu/classpath/tools/getopt/Parser$1.h: New.
> >>     * gnu/classpath/tools/getopt/Parser$2.h: New.
> >>     * gnu/classpath/tools/getopt/Parser$3.h: New.
> >>     * gnu/classpath/tools/getopt/OptionGroup.h: New.
> >>     * gnu/classpath/tools/getopt/OptionException.h: New.
> >>     * gnu/classpath/tools/getopt/Messages.h: New.
> >>     * gnu/classpath/tools/getopt/Option.h: New.
> >>     *
> >> gnu/gcj/tools/gc_analyze/MemoryAnalyze$SubstringComparator.h: New.
> >>     * gnu/gcj/tools/gc_analyze/SymbolLookup.java: New.
> >>     * gnu/gcj/tools/gc_analyze/BytePtr.h: New.
> >>     * gnu/gcj/tools/gc_analyze/ItemList.h: New.
> >>     * gnu/gcj/tools/gc_analyze/ToolPrefix.h: New.
> >>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze.h: New.
> >>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze$1.h: New
> >>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze$2.h: New
> >>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze$3.h: New
> >>     * gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.h: New.
> >>     * gnu/gcj/tools/gc_analyze/ObjectMap.java: New.
> >>     * gnu/gcj/tools/gc_analyze/SymbolLookup.h: New.
> >>     * gnu/gcj/tools/gc_analyze/MemoryMap.java: New.
> >>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.h: New.
> >>     * gnu/gcj/tools/gc_analyze/ObjectMap.h: New.
> >>     * gnu/gcj/tools/gc_analyze/MemoryMap.h: New.
> >>     * gnu/gcj/tools/gc_analyze/SymbolTable.java: New.
> >>     * gnu/gcj/tools/gc_analyze/SymbolTable.h: New.
> >>     * gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.h: New.
> >>     * gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparator.h: New.
> >>     * gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.h: New.
> >>     * gnu/gcj/tools/gc_analyze/BlockMap.java: New.
> >>     * gnu/gcj/tools/gc_analyze/BytePtr.java: New.
> >>     * gnu/gcj/tools/gc_analyze/ItemList.java: New.
> >>     * gnu/gcj/tools/gc_analyze/ToolPrefix.java: New.
> >>     * gnu/gcj/tools/gc_analyze/MemoryAnalyze.java: New.
> >>     * gnu/gcj/tools/gc_analyze/MemoryMap$Range.h: New.
> >>     * gnu/gcj/tools/gc_analyze/BlockMap.h: New.
> >>     * gnu/gcj/util/GCInfo.java: New.
> >>     * gnu/gcj/util/GCInfo.h: New.
> >>     * gnu/gcj/util/natGCInfo.cc: New.
> >>     * gnu/gcj/util/UtilPermission.java: New.
> >>     * gnu/gcj/util/UtilPermission.h: New.
> >>     * classpath/lib/Makefile.am (gcj_tools_classpath): New 
> variable.
> >>     (compile_classpath): Add $(gcj_tools_classpath) to classpath.
> >>     * classpath/lib/Makefile.in: Regenerated.
> >>     * 
> classpath/lib/gnu/gcj/tools/gc_analyze/SymbolTable.class: New.
> >>     *
> >> classpath/lib/gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.class: 
> >> New.
> >>     *
> >> classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparat
> >> or.class: New.
> >>     *
> >> 
> classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.class: New.
> >>     *
> >> classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap$Range.class: New.
> >>     * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap.class: New.
> >>     * classpath/lib/gnu/gcj/tools/gc_analyze/BytePtr.class: New.
> >>     *
> >> classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$Substring
> >> Comparator.class: 
> >> New.
> >>     * classpath/lib/gnu/gcj/tools/gc_analyze/ItemList.class: New.
> >>     * classpath/lib/gnu/gcj/tools/gc_analyze/ToolPrefix.class: New.
> >>     * 
> classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze.class: New.
> >>     *
> >> 
> classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.class: New.
> >>     * 
> classpath/lib/gnu/gcj/tools/gc_analyze/SymbolLookup.class: New.
> >>     *
> >> classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.class: 
> >> New.
> >>     * classpath/lib/gnu/gcj/tools/gc_analyze/ObjectMap.class: New.
> >>     * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap.class: New.
> >>     * classpath/lib/gnu/gcj/util/GCInfo.class: New.
> >>     * classpath/lib/gnu/gcj/util/UtilPermission.class: New.
> >>
> >> As before machine generated files are omitted from the diff.
> >>
> >>
> 
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-01-29 23:58   ` David Daney
       [not found]     ` <BDA38860DCFD334EAEA905E44EE8E7EF6DD409@G3W0067.americas.hpqcorp.net>
@ 2007-02-07 22:37     ` Tom Tromey
  2007-02-13  5:36       ` David Daney
  1 sibling, 1 reply; 17+ messages in thread
From: Tom Tromey @ 2007-02-07 22:37 UTC (permalink / raw)
  To: David Daney; +Cc: Java Patch List, gcc-patches, Johannes P. Schmidt

>>>>> "David" == David Daney <ddaney@avtrex.com> writes:

David> New version of the patch:

Thanks.  I'm afraid I have one blocking concern.

David> Index: libjava/classpath/lib/Makefile.am
[...]
David> +gcj_tools_classpath = $(top_srcdir)/tools/classes
David> +compile_classpath = $(vm_classes):$(top_srcdir):$(top_srcdir)/external/w3c_dom:$(top_srcdir)/external/sax:$(top_srcdir)/external/relaxngDatatype:$(top_srcdir)/external/jsr166:.:$(USER_CLASSLIB):$(PATH_TO_ESCHER):$(gcj_tools_classpath)
 
We purposely avoid putting the tools classes into the core class path.
This avoids any chance of a dependency on tools stuff by the core;
this would be bad due to the use of ASM in the tools.

I wonder if we could fix this by putting the new tool code into
standard.omit, and then compile it separately using a special rule of
some kind, with a classpath pointing to the tools zip.

David> +    if (rest.length != 1)
David> +    {
David> +    	optionParser.printHelp();
David> +    	System.exit(1);
David> +    }

It is somewhat better to put checking code like this into a validate()
method which throws OptionException.  This yields more "GNU like"
output.

Tom

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-02-07 22:37     ` Tom Tromey
@ 2007-02-13  5:36       ` David Daney
  2007-02-13 18:28         ` Tom Tromey
  0 siblings, 1 reply; 17+ messages in thread
From: David Daney @ 2007-02-13  5:36 UTC (permalink / raw)
  To: tromey; +Cc: Java Patch List, gcc-patches, Johannes P. Schmidt

[-- Attachment #1: Type: text/plain, Size: 7556 bytes --]

New version of the patch attached:

Tom Tromey wrote:
>>>>>> "David" == David Daney <ddaney@avtrex.com> writes:
>>>>>>             
>
> David> New version of the patch:
>
> Thanks.  I'm afraid I have one blocking concern.
>
> David> Index: libjava/classpath/lib/Makefile.am
> [...]
> David> +gcj_tools_classpath = $(top_srcdir)/tools/classes
> David> +compile_classpath = $(vm_classes):$(top_srcdir):$(top_srcdir)/external/w3c_dom:$(top_srcdir)/external/sax:$(top_srcdir)/external/relaxngDatatype:$(top_srcdir)/external/jsr166:.:$(USER_CLASSLIB):$(PATH_TO_ESCHER):$(gcj_tools_classpath)
>  
> We purposely avoid putting the tools classes into the core class path.
> This avoids any chance of a dependency on tools stuff by the core;
> this would be bad due to the use of ASM in the tools.
>
> I wonder if we could fix this by putting the new tool code into
> standard.omit, and then compile it separately using a special rule of
> some kind, with a classpath pointing to the tools zip.
>   
Ok, I added some rules to classpath/lib/Makefile.am, that parallel the 
special libgcj rules, to build gcj tools with a different classpath.  To 
build more packages this way (i.e. gcj_dbtool), it should be as simple 
as adding the package name to the list in libjava's configure.ac. 

There will no longer be any excuses to prevent us from converting 
gcj_dbtool to use the getopt things.

> David> +    if (rest.length != 1)
> David> +    {
> David> +    	optionParser.printHelp();
> David> +    	System.exit(1);
> David> +    }
>
> It is somewhat better to put checking code like this into a validate()
> method which throws OptionException.  This yields more "GNU like"
> output.
>
>   
Done (I hope).
> Tom
>   
Java-maintainer-mode seems to work, currently testing non-maintainer 
mode build on i686-pc-linux-gnu and x86_64-pc-linux-gnu.

OK to commit if no regressions?
gcc/java:
2007-02-12  David Daney  <ddaney@avtrex.com>

    * Make-lang.in (JAVA_MANFILES): Add doc/gc-analyze.1.
    (java.maintainer-clean):Add gc-analyze.1.
    (.INTERMEDIATE): Add gc-analyze.pod.
    (gc-analyze.pod): New rule.
    (java.install-man): Install gc-analyze.1
    * gcj.texi: Add new section for the gc-analyze program.

libjava:
2007-02-12  Johannes Schmidt  <jschmidt@avtrex.com>
    David Daney  <ddaney@avtrex.com>

    * configure.ac: Create gcj-tools-packages file.  Add
    gnu/gcj/tools/gc_analyze to standard.omit and gcj-tools-packages.
    Check for /proc/self/maps.
    * Makefile.am (bin_PROGRAMS): Added gc-analyze.
    (AM_GCJFLAGS): Set classpath to $(srcdir)/classpath/tools/classes.
    (gcjh.stamp): Same.
    (gc_analyze_SOURCES): New.
    (gc_analyze_LDFLAGS): New.
    (gc_analyze_LINK): New.
    (gc_analyze_LDADD): New.
    (gc_analyze_DEPENDENCIES): New.
    (nat_source_files): Added gnu/gcj/util/natGCInfo.cc.
    * Makefile.in: Regenerated.
    * configure: Regenerated.
    * include/config.h.in: Regenerated.
    * sources.am: Regenerated.
    * scripts/makemake.tcl: Don't include gc-analyze classes in libgcj.
    * gnu/classpath/tools/getopt/Parser.h: New.
    * gnu/classpath/tools/getopt/FileArgumentCallback.h: New.
    * gnu/classpath/tools/getopt/Parser$1.h: New.
    * gnu/classpath/tools/getopt/Parser$2.h: New.
    * gnu/classpath/tools/getopt/Parser$3.h: New.
    * gnu/classpath/tools/getopt/OptionGroup.h: New.
    * gnu/classpath/tools/getopt/OptionException.h: New.
    * gnu/classpath/tools/getopt/Messages.h: New.
    * gnu/classpath/tools/getopt/Option.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$SubstringComparator.h: New.
    * gnu/gcj/tools/gc_analyze/SymbolLookup.java: New.
    * gnu/gcj/tools/gc_analyze/BytePtr.h: New.
    * gnu/gcj/tools/gc_analyze/ItemList.h: New.
    * gnu/gcj/tools/gc_analyze/ToolPrefix.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$1.h: New
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$2.h: New
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$3.h: New
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$4.h: New
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$OptionParser.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.h: New.
    * gnu/gcj/tools/gc_analyze/ObjectMap.java: New.
    * gnu/gcj/tools/gc_analyze/SymbolLookup.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.h: New.
    * gnu/gcj/tools/gc_analyze/ObjectMap.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap.h: New.
    * gnu/gcj/tools/gc_analyze/SymbolTable.java: New.
    * gnu/gcj/tools/gc_analyze/SymbolTable.h: New.
    * gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.h: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparator.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap.java: New.
    * gnu/gcj/tools/gc_analyze/BytePtr.java: New.
    * gnu/gcj/tools/gc_analyze/ItemList.java: New.
    * gnu/gcj/tools/gc_analyze/ToolPrefix.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap$Range.h: New.
    * gnu/gcj/tools/gc_analyze/BlockMap.h: New.
    * gnu/gcj/util/GCInfo.java: New.
    * gnu/gcj/util/GCInfo.h: New.
    * gnu/gcj/util/natGCInfo.cc: New.
    * gnu/gcj/util/UtilPermission.java: New.
    * gnu/gcj/util/UtilPermission.h: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/SymbolTable.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.class: 
New.
    * 
classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparator.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap$Range.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/BytePtr.class: New.
    * 
classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$SubstringComparator.class: 
New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ItemList.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ToolPrefix.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.class: 
New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$1.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$2.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$3.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$4.class: New.
    * 
classpath/lib/gnu/gcj/tools/gc_analyze/MemoryAnalyze$OptionParser.class
    * classpath/lib/gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/SymbolLookup.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/ObjectMap.class: New.
    * classpath/lib/gnu/gcj/tools/gc_analyze/MemoryMap.class: New.
    * classpath/lib/gnu/gcj/util/GCInfo.class: New.
    * classpath/lib/gnu/gcj/util/UtilPermission.class: New.

libjava/classpath:
2007-02-12  David Daney  <ddaney@avtrex.com>

    * configure.ac (AC_CONFIG_FILES): Add lib/gen-gcj-tools-classlist.sh.
    * lib/Makefile.am (GCJ_TOOLS_COMPILE): New variable.
    (noinst_DATA): Add compile-gcj-tools-classes.
    (install-data-local): Add dependency on compile-gcj-tools-classes.
    (gcj-tools-classes): New target.
    (compile-gcj-tools-classes): New target.
    (CLEANFILES): Add compile-gcj-tools-classes and gcj-tools-classes.
    * lib/gen-gcj-tools-classlist.sh.in: New.
    * lib/Makefile.in: Regenerated.
    * configure: Regenerated.


[-- Attachment #2: gc-analyze.diff.txt --]
[-- Type: text/plain, Size: 80892 bytes --]

Index: gcc/java/Make-lang.in
===================================================================
--- gcc/java/Make-lang.in	(revision 121258)
+++ gcc/java/Make-lang.in	(working copy)
@@ -139,7 +139,7 @@ java.pdf: doc/gcj.pdf
 java.html: $(build_htmldir)/java/index.html
 JAVA_MANFILES = doc/gcj.1 doc/jcf-dump.1 doc/gij.1 \
                 doc/jv-convert.1 doc/grmic.1 doc/grmiregistry.1 \
-		doc/gcj-dbtool.1
+		doc/gcj-dbtool.1 doc/gc-analyze.1
 
 java.man: $(JAVA_MANFILES)
 
@@ -208,6 +208,7 @@ java.maintainer-clean:
 	-rm -f $(docobjdir)/grmic.1
 	-rm -f $(docobjdir)/grmiregistry.1
 	-rm -f $(docobjdir)/gcj-dbtool.1
+	-rm -f $(docobjdir)/gc-analyze.1
 #\f
 # Stage hooks:
 # The main makefile has already created stage?/java.
@@ -318,7 +319,7 @@ $(build_htmldir)/java/index.html: $(TEXI
 	$(TEXI2HTML) -I $(gcc_docdir)/include -I $(srcdir)/java -o $(@D) $<
 
 .INTERMEDIATE: gcj.pod jcf-dump.pod gij.pod \
-  jv-convert.pod grmic.pod grmiregistry.pod gcj-dbtool.pod
+  jv-convert.pod grmic.pod grmiregistry.pod gcj-dbtool.pod gc-analyze.pod
 
 gcj.pod: java/gcj.texi
 	-$(TEXI2POD) -D gcj < $< > $@
@@ -334,15 +335,17 @@ grmiregistry.pod: java/gcj.texi
 	-$(TEXI2POD) -D grmiregistry < $< > $@
 gcj-dbtool.pod: java/gcj.texi
 	-$(TEXI2POD) -D gcj-dbtool < $< > $@
+gc-analyze.pod: java/gcj.texi
+	-$(TEXI2POD) -D gc-analyze < $< > $@
 
 # Install the man pages.
 java.install-man: installdirs \
                   $(DESTDIR)$(man1dir)/$(JAVA_INSTALL_NAME)$(man1ext) \
 		  $(JAVA_TARGET_INDEPENDENT_BIN_TOOLS:%=doc/%.1) \
 		  doc/gij.1 doc/jv-convert.1 doc/grmic.1 doc/grmiregistry.1 \
-		  doc/gcj-dbtool.1
+		  doc/gcj-dbtool.1 doc/gc-analyze.1
 	for tool in $(JAVA_TARGET_INDEPENDENT_BIN_TOOLS) \
-		gij jv-convert grmic grmiregistry gcj-dbtool ; do \
+		gij jv-convert grmic grmiregistry gcj-dbtool gc-analyze ; do \
 	  tool_transformed_name=`echo $$tool|sed '$(program_transform_name)'`; \
 	  man_name=$(DESTDIR)$(man1dir)/$${tool_transformed_name}$(man1ext); \
 	  rm -f $$man_name ; \
Index: gcc/java/gcj.texi
===================================================================
--- gcc/java/gcj.texi	(revision 121258)
+++ gcc/java/gcj.texi	(working copy)
@@ -17,7 +17,7 @@
 @c the word ``Java'.
 
 @c When this manual is copyrighted.
-@set copyrights-gcj 2001, 2002, 2003, 2004, 2005
+@set copyrights-gcj 2001, 2002, 2003, 2004, 2005, 2006, 2007
 
 @c Versions
 @set which-gcj GCC-@value{version-GCC}
@@ -79,6 +79,8 @@ man page gfdl(7).
                             Generate stubs for Remote Method Invocation.
 * grmiregistry: (gcj)Invoking grmiregistry.
                             The remote object registry.
+* gc-analyze: (gcj)Invoking gc-analyze.
+                            Analyze Garbage Collector (GC) memory dumps.
 @end direntry
 @end format
 
@@ -125,6 +127,7 @@ files and object files, and it can read 
 * Invoking jv-convert:: Converting from one encoding to another
 * Invoking grmic::      Generate stubs for Remote Method Invocation.
 * Invoking grmiregistry:: The remote object registry.
+* Invoking gc-analyze:: Analyze Garbage Collector (GC) memory dumps.
 * About CNI::           Description of the Compiled Native Interface
 * System properties::   Modifying runtime behavior of the libgcj library
 * Resources::		Where to look for more information
@@ -1412,6 +1415,89 @@ Print version information, then exit.
 @c man end
 
 
+@node Invoking gc-analyze
+@chapter Invoking gc-analyze
+
+@c man title gc-analyze Analyze Garbage Collector (GC) memory dumps
+
+@c man begin SYNOPSIS gc-analyze
+@command{gc-analyze} [@option{OPTION}] @dots{} [@var{file}]
+@ignore
+  [@option{-v}]
+  [@option{--verbose}]
+  [@option{-p} @var{tool-prefix}]
+  [@option{-d} @var{directory}]
+  [@option{--version}]
+  [@option{--help}]
+@end ignore
+@c man end
+
+@c man begin DESCRIPTION gc-analyze
+
+@command{gc-analyze} prints an analysis of a GC memory dump to
+standard out.
+
+The memory dumps may be created by calling
+@code{gnu.gcj.util.GCInfo.enumerate(String namePrefix)} from java
+code.  A memory dump will be created on an out of memory condition if
+@code{gnu.gcj.util.GCInfo.setOOMDump(String namePrefix)} is called
+before the out of memory occurs.
+
+Running this program will create two files: @file{TestDump001} and
+@file{TestDump001.bytes}.
+
+@example
+import gnu.gcj.util.*;
+import java.util.*;
+
+public class GCDumpTest
+@{
+    static public void main(String args[])
+    @{
+        ArrayList<String> l = new ArrayList<String>(1000);
+        
+        for (int i = 1; i < 1500; i++) @{
+            l.add("This is string #" + i);
+        @}
+        GCInfo.enumerate("TestDump");
+    @}
+@}
+@end example
+
+The memory dump may then be displayed by running:
+
+@example
+gc-analyze -v TestDump001
+@end example
+
+@c FIXME: Add real information here.
+@c This really isn't much more than the --help output.
+
+@c man end
+
+@c man begin OPTIONS gc-analyze
+
+@table @gcctabopt
+@item --verbose
+@itemx -v
+Verbose output.
+
+@item -p @var{tool-prefix}
+Prefix added to the names of the @command{nm} and @command{readelf} commands.
+
+@item -d @var{directory}
+Directory that contains the executable and shared libraries used when
+the dump was generated.
+
+@item --help
+Print a help message, then exit.
+
+@item --version
+Print version information, then exit.
+@end table
+
+@c man end
+
 @node About CNI
 @chapter About CNI
 
Index: libjava/scripts/makemake.tcl
===================================================================
--- libjava/scripts/makemake.tcl	(revision 121258)
+++ libjava/scripts/makemake.tcl	(working copy)
@@ -292,7 +292,8 @@ proc emit_package_rule {package} {
   puts ""
 
   if {$pkgname != "gnu/gcj/xlib" && $pkgname != "gnu/awt/xlib"
-      && $pkgname != "gnu/gcj/tools/gcj_dbtool"} {
+      && $pkgname != "gnu/gcj/tools/gcj_dbtool"
+      && $pkgname != "gnu/gcj/tools/gc_analyze"} {
     lappend package_files $lname
   }
 }
Index: libjava/configure.ac
===================================================================
--- libjava/configure.ac	(revision 121258)
+++ libjava/configure.ac	(working copy)
@@ -283,6 +283,12 @@ if test "$use_x_awt" != yes; then
    echo gnu/java/awt/peer/x >> standard.omit
 fi
 
+# Tools that need to be compiled against classpath's tools classes
+for package in gnu/gcj/tools/gc_analyze ; do
+    echo $package >> standard.omit
+    echo $package >> gcj-tools-packages
+done
+
 if test -z "${with_multisubdir}"; then
    builddotdot=.
 else
@@ -1000,10 +1006,14 @@ else
    if test x"$cross_compiling" = x"no"; then
      AC_CHECK_FILES(/proc/self/exe, [
        AC_DEFINE(HAVE_PROC_SELF_EXE, 1, [Define if you have /proc/self/exe])])
+     AC_CHECK_FILES(/proc/self/maps, [
+       AC_DEFINE(HAVE_PROC_SELF_MAPS, 1,
+         [Define if you have /proc/self/maps])])
    else
      case $host in
      *-linux*)
        AC_DEFINE(HAVE_PROC_SELF_EXE, 1, [Define if you have /proc/self/exe])
+       AC_DEFINE(HAVE_PROC_SELF_MAPS, 1, [Define if you have /proc/self/maps])
        ;;
      esac
    fi
Index: libjava/classpath/configure.ac
===================================================================
--- libjava/classpath/configure.ac	(revision 121258)
+++ libjava/classpath/configure.ac	(working copy)
@@ -894,6 +894,7 @@ scripts/Makefile
 scripts/classpath.spec
 lib/Makefile
 lib/gen-classlist.sh
+lib/gen-gcj-tools-classlist.sh
 lib/copy-vmresources.sh
 tools/Makefile
 examples/Makefile
@@ -929,6 +930,8 @@ AC_CONFIG_COMMANDS([gjavah], [chmod 755 
 fi
 
 AC_CONFIG_COMMANDS([gen-classlist],[chmod 755 lib/gen-classlist.sh])
+AC_CONFIG_COMMANDS([gen-gcj-tools-classlist],
+                   [chmod 755 lib/gen-gcj-tools-classlist.sh])
 AC_CONFIG_COMMANDS([copy-vmresources],[chmod 755 lib/copy-vmresources.sh])
 AC_OUTPUT
 
Index: libjava/classpath/lib/gen-gcj-tools-classlist.sh.in
===================================================================
--- libjava/classpath/lib/gen-gcj-tools-classlist.sh.in	(revision 0)
+++ libjava/classpath/lib/gen-gcj-tools-classlist.sh.in	(revision 0)
@@ -0,0 +1,26 @@
+#! @SHELL@
+# @configure_input@
+
+# Make sure sorting is done the same on all configurations
+# Note the use of sort -r below. This is done explicitly to work around
+# a gcj bug (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=21418)
+LC_ALL=C; export LC_ALL
+LANG=C; export LANG
+
+
+vm_dirlist=`echo "@vm_classes@" | sed -e 's/:/ /g'`
+vm_tools_packages=`cat ${top_builddir}/../gcj-tools-packages`
+for dir in $vm_dirlist; do
+    echo "Adding gcj tools source files from srcdir $dir."
+
+    (cd $dir
+     for subdir in $vm_tools_packages ; do
+        @FIND@ $subdir -follow -name '*.java' -print |
+	sort -r |
+	while read file; do
+	    echo $dir/$file
+	done
+     done) >> ${top_builddir}/lib/gcj-tools-classes 2>/dev/null
+done
+
+exit 0
Index: libjava/classpath/lib/Makefile.am
===================================================================
--- libjava/classpath/lib/Makefile.am	(revision 121258)
+++ libjava/classpath/lib/Makefile.am	(working copy)
@@ -20,6 +20,8 @@ if FOUND_GCJ
 ## There's no point in warning, and we always want debug info.
 ## GCJ LOCAL: use srcdir
 JAVAC = $(GCJ) -C -g -w --encoding=UTF-8 -bootclasspath '' --classpath $(compile_classpath) -d $(srcdir) @classes
+GCJ_TOOLS_COMPILE = $(GCJ) -C -g -w --encoding=UTF-8 -bootclasspath $(srcdir) \
+  --classpath $(top_srcdir)/tools/classes -d $(srcdir) @gcj-tools-classes
 else
 if FOUND_JIKES
 JAVAC = $(JIKES) $(JIKESWARNINGS) +F $(JIKESENCODING) -bootclasspath '' -extdirs '' -sourcepath '' --classpath $(compile_classpath) -d . @classes
@@ -63,12 +65,12 @@ if INSTALL_GLIBJ_ZIP
 endif # INSTALL_GLIBJ_ZIP
 
 if BUILD_CLASS_FILES
-noinst_DATA = genclasses compile-classes resources
+noinst_DATA = genclasses compile-classes compile-gcj-tools-classes resources
 endif # BUILD_CLASS_FILES
 
 if INSTALL_CLASS_FILES
 
-install-data-local: genclasses compile-classes
+install-data-local: genclasses compile-classes compile-gcj-tools-classes
 	-$(top_srcdir)/mkinstalldirs $(DESTDIR)$(pkgdatadir)
 	cp -R gnu $(DESTDIR)$(pkgdatadir)
 	cp -R java $(DESTDIR)$(pkgdatadir)
@@ -170,11 +172,21 @@ if JAVA_MAINTAINER_MODE
 	$(JAVAC)
 endif
 	touch compile-classes
+
+
+gcj-tools-classes: gen-gcj-tools-classlist.sh
+	top_builddir=$(top_builddir) top_srcdir=$(top_srcdir) $(SHELL) gen-gcj-tools-classlist.sh
+
+compile-gcj-tools-classes: gcj-tools-classes $(JAVA_SRCS) Makefile
+if JAVA_MAINTAINER_MODE
+	if test -s gcj-tools-classes ; then  $(GCJ_TOOLS_COMPILE) ; fi
+endif
+	touch compile-gcj-tools-classes
 # endif
 
 EXTRA_DIST = standard.omit.in mkcollections.pl.in Makefile.gcj split-for-gcj.sh
-CLEANFILES = compile-classes resources classes \
-	glibj.zip classes.1 classes.2 Makefile.deps \
+CLEANFILES = compile-classes compile-gcj-tools-classes resources \
+	classes gcj-tools-classes glibj.zip classes.1 classes.2 Makefile.deps \
 	$(top_builddir)/gnu/java/locale/LocaleData.java \
 	$(JAVA_DEPEND)
 DISTCLEANFILES = standard.omit
Index: libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java	(revision 0)
@@ -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);
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java	(revision 0)
@@ -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);
+      }
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java	(revision 0)
@@ -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);
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java	(revision 0)
@@ -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();
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/BlockMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/BlockMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/BlockMap.java	(revision 0)
@@ -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));
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/BytePtr.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/BytePtr.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/BytePtr.java	(revision 0)
@@ -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());
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ItemList.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ItemList.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ItemList.java	(revision 0)
@@ -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();
+          }
+      }
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java	(revision 0)
@@ -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;
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java	(revision 0)
@@ -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;
+  }
+}
Index: libjava/gnu/gcj/util/GCInfo.java
===================================================================
--- libjava/gnu/gcj/util/GCInfo.java	(revision 0)
+++ libjava/gnu/gcj/util/GCInfo.java	(revision 0)
@@ -0,0 +1,79 @@
+/* GCInfo.java -- Support for creating heap dumps.
+   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.util;
+
+public class GCInfo
+{
+  private GCInfo()
+  {
+  }
+
+  /**
+   * @throws SecurityException if there is a SecurityManager installed
+   * and UtilPermission("dumpHeap") is not granted.
+   */
+  private static void checkPermission()
+  {
+    SecurityManager sm = System.getSecurityManager();
+    if (sm != null)
+      sm.checkPermission(new UtilPermission("dumpHeap"));
+  }
+  
+
+  /**
+   * Dump a description of the heap state.
+   *
+   * @param namePrefix The filename prefix for the dump files.
+   *
+   * @throws SecurityException if there is a SecurityManager installed
+   * and UtilPermission("dumpHeap") is not granted.
+   */
+  public static synchronized void dump(String name)
+  {
+    checkPermission();
+    dump0(name);
+  }
+  
+  private static native void dump0(String name);
+
+
+  /**
+   * Create a heap dump.
+   *
+   * @param namePrefix The filename prefix for the dump files.
+   *
+   * @throws SecurityException if there is a SecurityManager installed
+   * and UtilPermission("dumpHeap") is not granted.
+   */
+  public static synchronized void enumerate(String namePrefix)
+  {
+    checkPermission();
+    enumerate0(namePrefix);
+  }
+  
+  private static native void enumerate0(String namePrefix);
+
+  /**
+   * Cause a heap dump if out-of-memory condition occurs.
+   *
+   * @param namePrefix The filename prefix for the dump files.  If
+   * null no dumps are created.
+   *
+   * @throws SecurityException if there is a SecurityManager installed
+   * and UtilPermission("dumpHeap") is not granted.
+   */
+  public static synchronized void setOOMDump(String namePrefix)
+  {
+    checkPermission();
+    setOOMDump0(namePrefix);
+  }
+  
+  private static native void setOOMDump0(String namePrefix);
+}
Index: libjava/gnu/gcj/util/UtilPermission.java
===================================================================
--- libjava/gnu/gcj/util/UtilPermission.java	(revision 0)
+++ libjava/gnu/gcj/util/UtilPermission.java	(revision 0)
@@ -0,0 +1,20 @@
+/* GCInfo.java -- Support for creating heap dumps.
+   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.util;
+
+import java.security.BasicPermission;
+
+public class UtilPermission extends BasicPermission
+{
+  public UtilPermission(String name)
+  {
+    super(name);
+  }
+}
Index: libjava/gnu/gcj/util/natGCInfo.cc
===================================================================
--- libjava/gnu/gcj/util/natGCInfo.cc	(revision 0)
+++ libjava/gnu/gcj/util/natGCInfo.cc	(revision 0)
@@ -0,0 +1,454 @@
+/* natGCInfo.cc -- Native portion of support for creating heap dumps.
+   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.  */
+
+
+#include <config.h>
+
+#include <gcj/cni.h>
+
+#include <gnu/gcj/util/GCInfo.h>
+
+#ifdef HAVE_PROC_SELF_MAPS
+//
+// If /proc/self/maps does not exist we assume we are doomed and do nothing.
+//
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+//
+// Boehm GC includes.
+//
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+
+#ifdef FALSE
+#undef FALSE
+#endif
+
+extern "C" {
+#include "private/dbg_mlc.h"
+  int GC_n_set_marks(hdr* hhdr);
+  ptr_t GC_clear_stack(ptr_t p);
+  extern int GC_gcj_kind;
+  extern int GC_gcj_debug_kind;
+}
+
+#endif
+
+#ifdef HAVE_PROC_SELF_MAPS
+
+static int gc_ok = 1;
+
+typedef struct gc_debug_info
+{
+  int used;
+  int free;
+  int wasted;
+  int blocks;
+  FILE* fp;
+};
+
+static void
+GC_print_debug_callback(hblk *h, word user_data)
+{
+  hdr *hhdr = HDR(h);
+  size_t bytes = WORDS_TO_BYTES(hhdr -> hb_sz);
+
+  gc_debug_info *pinfo = (gc_debug_info *)user_data;
+
+  fprintf(pinfo->fp, "ptr = %#lx, kind = %d, size = %zd, marks = %d\n",
+          (unsigned long)h, hhdr->hb_obj_kind, bytes, GC_n_set_marks(hhdr));
+}
+
+/*
+  this next section of definitions shouldn't really be here.
+  copied from boehmgc/allchblk.c
+*/
+
+# define UNIQUE_THRESHOLD 32
+# define HUGE_THRESHOLD 256
+# define FL_COMPRESSION 8
+# define N_HBLK_FLS (HUGE_THRESHOLD - UNIQUE_THRESHOLD)/FL_COMPRESSION \
+                         + UNIQUE_THRESHOLD
+#ifndef USE_MUNMAP
+extern "C" {
+  extern word GC_free_bytes[N_HBLK_FLS+1];
+}
+#endif
+
+# ifdef USE_MUNMAP
+#   define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0)
+# else  /* !USE_MMAP */
+#   define IS_MAPPED(hhdr) 1
+# endif /* USE_MUNMAP */
+
+static void
+GC_print_hblkfreelist_file(FILE *fp)
+{
+  struct hblk * h;
+  word total_free = 0;
+  hdr * hhdr;
+  word sz;
+  int i;
+    
+  fprintf(fp, "---------- Begin free map ----------\n");
+  for (i = 0; i <= N_HBLK_FLS; ++i)
+    {
+      h = GC_hblkfreelist[i];
+#ifdef USE_MUNMAP
+      if (0 != h)
+        fprintf (fp, "Free list %ld:\n", (unsigned long)i);
+#else
+      if (0 != h)
+        fprintf (fp, "Free list %ld (Total size %ld):\n",
+                 (unsigned long)i,
+                 (unsigned long)GC_free_bytes[i]);
+#endif
+      while (h != 0)
+        {
+          hhdr = HDR(h);
+          sz = hhdr -> hb_sz;
+          fprintf (fp, "\t0x%lx size %lu ", (unsigned long)h,
+                   (unsigned long)sz);
+          total_free += sz;
+
+          if (GC_is_black_listed (h, HBLKSIZE) != 0)
+            fprintf (fp, "start black listed\n");
+          else if (GC_is_black_listed(h, hhdr -> hb_sz) != 0)
+            fprintf (fp, "partially black listed\n");
+          else
+            fprintf (fp, "not black listed\n");
+
+          h = hhdr -> hb_next;
+        }
+    }
+#ifndef USE_MUNMAP
+  if (total_free != GC_large_free_bytes)
+    {
+      fprintf (fp, "GC_large_free_bytes = %lu (INCONSISTENT!!)\n",
+               (unsigned long) GC_large_free_bytes);
+    }
+#endif
+  fprintf (fp, "Total of %lu bytes on free list\n", (unsigned long)total_free);
+  fprintf (fp, "---------- End free map ----------\n");
+}
+
+static int GC_dump_count = 1;
+
+static void
+GC_print_debug_info_file(FILE* fp)
+{
+  gc_debug_info info;
+
+  memset(&info, 0, sizeof info);
+  info.fp = fp;
+
+  if (gc_ok)
+    GC_gcollect();
+  fprintf(info.fp, "---------- Begin block map ----------\n");
+  GC_apply_to_all_blocks(GC_print_debug_callback, (word)(void*)(&info));
+  //fprintf(fp, "#Total used %d free %d wasted %d\n", info.used, info.free, info.wasted);
+  //fprintf(fp, "#Total blocks %d; %dK bytes\n", info.blocks, info.blocks*4);
+  fprintf(info.fp, "---------- End block map ----------\n");
+
+  //fprintf(fp, "\n***Free blocks:\n");
+  //GC_print_hblkfreelist();
+}
+
+namespace
+{
+  class  __attribute__ ((visibility ("hidden"))) GC_enumerator
+  {
+  public:
+    GC_enumerator(const char *name);
+    void enumerate();
+  private:
+    FILE* fp;
+    int bytes_fd;
+
+    void print_address_map();
+    void enumerate_callback(struct hblk *h);
+    static void enumerate_callback_adaptor(struct hblk *h, word dummy);
+  };
+}
+
+GC_enumerator::GC_enumerator(const char *name)
+{
+  bytes_fd = -1;
+  fp = fopen (name, "w");
+  if (!fp)
+    {
+      printf ("GC_enumerator failed to open [%s]\n", name);
+      return;
+    }
+  printf ("GC_enumerator saving summary to [%s]\n", name);
+
+  // open heap file
+  char bytes_name[strlen(name) + 10];
+  sprintf (bytes_name, "%s.bytes", name);
+  bytes_fd = open (bytes_name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+  if (bytes_fd <= 0)
+    {
+      printf ("GC_enumerator failed to open [%s]\n", bytes_name);
+      return;
+    }
+  printf ("GC_enumerator saving heap contents to [%s]\n", bytes_name);
+}
+
+/*
+  sample format of /proc/self/maps
+
+  0063b000-00686000 rw-p 001fb000 03:01 81993      /avtrex/bin/dumppropapp
+  00686000-0072e000 rwxp 00000000 00:00 0 
+
+  These are parsed below as:
+  start   -end      xxxx xxxxxxxx  a:b xxxxxxxxxxxxxxx
+
+*/
+
+
+void
+GC_enumerator::print_address_map()
+{
+  FILE* fm;
+  char buffer[128];
+
+  fprintf(fp, "---------- Begin address map ----------\n");
+
+  fm = fopen("/proc/self/maps", "r");
+  if (fm == NULL)
+    {
+      if (0 == strerror_r (errno, buffer, sizeof buffer))
+        fputs (buffer, fp);
+    }
+  else
+    {
+      while (fgets (buffer, sizeof buffer, fm) != NULL)
+        {
+          fputs (buffer, fp);
+          char *dash = strchr(buffer, '-');
+          char *colon = strchr(buffer, ':');
+          if (dash && colon && ((ptrdiff_t)strlen(buffer) > (colon - buffer) + 2))
+            {
+              char *endp;
+              unsigned long start = strtoul(buffer, NULL, 16);
+              unsigned long end   = strtoul(dash + 1, &endp, 16);
+              unsigned long a     = strtoul(colon - 2, NULL, 16);
+              unsigned long b     = strtoul(colon + 1, NULL, 16);
+              // If it is an anonymous mapping 00:00 and both readable
+              // and writeable then dump the contents of the mapping
+              // to the bytes file.  Each block has a header of three
+              // unsigned longs:
+              // 0 - The number sizeof(unsigned long) to detect endianness and
+              //     structure layout.
+              // 1 - The offset in VM.
+              // 2 - The Length in bytes.
+              // Followed by the bytes.
+              if (!a && !b && endp < colon && 'r' == endp[1] && 'w' == endp[2])
+                {
+                  unsigned long t = sizeof(unsigned long);
+                  write(bytes_fd, (void*)&t, sizeof(t));
+                  write(bytes_fd, (void*)&start, sizeof(start));
+                  t = end - start;
+                  write(bytes_fd, (void*)&t, sizeof(t));
+                  write(bytes_fd, (void*)start, (end - start));
+                }
+            }
+        } 
+      fclose(fm);
+    }
+  fprintf(fp, "---------- End address map ----------\n");
+  fflush(fp);
+}
+
+void
+GC_enumerator::enumerate()
+{
+  print_address_map();
+  fprintf(fp, "---------- Begin object map ----------\n");
+  if (gc_ok)
+    GC_gcollect();
+  GC_apply_to_all_blocks(enumerate_callback_adaptor, 
+                         (word)(void*)(this));
+  fprintf(fp, "---------- End object map ----------\n");
+  fflush(fp); 
+
+  GC_print_debug_info_file(fp);
+  fflush(fp); 
+  GC_print_hblkfreelist_file(fp);
+  fflush(fp); 
+
+  close(bytes_fd);
+  fclose(fp);
+
+  GC_clear_stack(0);
+}
+
+void
+GC_enumerator::enumerate_callback_adaptor(struct hblk *h,
+                                          word dummy)
+{
+  GC_enumerator* pinfo = (GC_enumerator*)dummy;
+  pinfo->enumerate_callback(h);
+}
+
+void
+GC_enumerator::enumerate_callback(struct hblk *h)
+{
+  hdr * hhdr = HDR(h);
+  size_t bytes = WORDS_TO_BYTES(hhdr->hb_sz);
+  int i;
+
+  for (i = 0; i == 0 || (i + bytes <= HBLKSIZE); i += bytes)
+    {
+      int inUse = mark_bit_from_hdr(hhdr,BYTES_TO_WORDS(i));  // in use
+      char *ptr = (char*)h+i;                                 // address
+      int kind = hhdr->hb_obj_kind;                           // kind
+      void *klass = 0;
+      void *data = 0;
+      if (kind == GC_gcj_kind
+          || kind == GC_gcj_debug_kind
+          || kind == GC_gcj_debug_kind+1)
+        {
+          void* v = *(void **)ptr;
+          if (v)
+            {
+              klass = *(void **)v;
+              data = *(void **)(ptr + sizeof(void*));
+            }
+        }
+      if (inUse)
+        fprintf (fp, "used = %d, ptr = %#lx, size = %zd, kind = %d, "
+                 "klass = %#lx, data = %#lx\n", 
+                 inUse, (unsigned long)ptr, bytes, kind,
+                 (unsigned long)klass, (unsigned long)data);
+    }
+}
+
+/*
+ * Fill in a char[] with low bytes of the string characters.  These
+ * methods may be called while an OutOfMemoryError is being thrown, so
+ * we cannot call nice java methods to get the encoding of the string.
+ */
+static void
+J2A(::java::lang::String* str, char *dst)
+{
+  jchar * pchars = JvGetStringChars(str);
+  jint len = str->length();
+  int i;
+  for (i=0; i<len; i++)
+    dst[i] = (char)pchars[i];
+  dst[i] = 0;
+}
+
+void
+::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
+{
+  char n[name->length() + 1];
+  J2A(name, n);
+  
+  char temp[name->length() + 20];
+  sprintf(temp, "%s%03d", n, GC_dump_count++);
+  FILE* fp = fopen(temp, "w");
+
+  GC_print_debug_info_file(fp);
+
+  fclose(fp);
+}
+
+void
+::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
+{
+  char n[name->length() + 1];
+  J2A(name, n);
+  char temp[name->length() + 20];
+  sprintf(temp, "%s%03d", n, GC_dump_count++);
+
+  GC_enumerator x(temp);
+  x.enumerate();
+}
+
+static char *oomDumpName = NULL;
+
+static void *
+nomem_handler(size_t size)
+{
+  if (oomDumpName)
+    {
+      char temp[strlen(oomDumpName) + 20];
+      sprintf(temp, "%s%03d", temp, GC_dump_count++);
+      printf("nomem_handler(%zd) called\n", size);
+      gc_ok--;
+      GC_enumerator x(temp);
+      x.enumerate();
+      gc_ok++;
+    }
+  return (void*)0;
+}
+
+void
+::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
+{
+  char *oldName = oomDumpName;
+  oomDumpName = NULL;
+  free (oldName);
+  
+  if (NULL == name)
+    return;
+  
+  char *n = (char *)malloc(name->length() + 1);
+
+  J2A(name, n);
+  oomDumpName = n;
+  GC_oom_fn = nomem_handler;
+}
+
+#else  // HAVE_PROC_SELF_MAPS
+
+void
+::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+void
+::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+void
+::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+#endif // HAVE_PROC_SELF_MAPS
+
Index: libjava/Makefile.am
===================================================================
--- libjava/Makefile.am	(revision 121258)
+++ libjava/Makefile.am	(working copy)
@@ -73,7 +73,7 @@ db_pathtail = gcj-$(gcc_version)/$(db_na
 if NATIVE
 bin_PROGRAMS = jv-convert gij grmic grmiregistry gcj-dbtool \
 	gappletviewer gjarsigner gkeytool gjar gjavah gnative2ascii \
-	gorbd grmid gserialver gtnameserv
+	gorbd grmid gserialver gtnameserv gc-analyze
 
 ## It is convenient to actually build and install the default database
 ## when gcj-dbtool is available.
@@ -144,8 +144,8 @@ AM_CXXFLAGS = \
 
 AM_GCJFLAGS = \
 	@LIBGCJ_JAVAFLAGS@ \
-	-fclasspath= -fbootclasspath=$(BOOTCLASSPATH) \
-	--encoding=UTF-8 \
+	-fclasspath=$(srcdir)/classpath/tools/classes \
+	 -fbootclasspath=$(BOOTCLASSPATH) --encoding=UTF-8 \
 	-Wno-deprecated -fbootstrap-classes
 
 AM_CFLAGS = @LIBGCJ_CFLAGS@
@@ -415,8 +415,9 @@ $(generic_header_files): gcjh.stamp
 
 gcjh.stamp: classpath/lib/compile-classes
 if JAVA_MAINTAINER_MODE
-	$(MYGCJH) --cni --all $(srcdir)/classpath/lib \
-	    --cmdfile=$(srcdir)/headers.txt -d $(srcdir) --force
+	$(MYGCJH) --classpath $(srcdir)/classpath/tools/classes --cni \
+	--all $(srcdir)/classpath/lib --cmdfile=$(srcdir)/headers.txt -d \
+	$(srcdir) --force
 endif
 	echo > gcjh.stamp
 
@@ -643,6 +644,22 @@ gcj_dbtool_LDADD = gnu/gcj/tools/gcj_dbt
 ## linking this program.
 gcj_dbtool_DEPENDENCIES = gnu/gcj/tools/gcj_dbtool.lo libgcj.la libgcj.spec
 
+gc_analyze_SOURCES =
+
+## We need -nodefaultlibs because we want to avoid gcj's `-lgcj'.  We
+## need this because we are explicitly using libtool to link using the
+## `.la' file.
+gc_analyze_LDFLAGS = --main=gnu.gcj.tools.gc_analyze.MemoryAnalyze \
+        -rpath $(toolexeclibdir) -shared-libgcc $(THREADLDFLAGS)
+gc_analyze_LINK = $(GCJLINK)
+## See jv_convert_LDADD.
+gc_analyze_LDADD = gnu/gcj/tools/gc_analyze.lo -L$(here)/.libs \
+	libgcj-tools.la libgcj.la
+## Depend on the spec file to make sure it is up to date before
+## linking this program.
+gc_analyze_DEPENDENCIES = gnu/gcj/tools/gc_analyze.lo libgcj-tools.la \
+	libgcj.la libgcj.spec
+
 gij_SOURCES = 
 ## We need -nodefaultlibs because we want to avoid gcj's `-lgcj'.  We
 ## need this because we are explicitly using libtool to link using the
@@ -827,6 +844,7 @@ gnu/gcj/runtime/natSharedLibLoader.cc \
 gnu/gcj/runtime/natSystemClassLoader.cc \
 gnu/gcj/runtime/natStringBuffer.cc \
 gnu/gcj/util/natDebug.cc \
+gnu/gcj/util/natGCInfo.cc \
 gnu/java/lang/natMainThread.cc \
 gnu/java/lang/management/natVMClassLoadingMXBeanImpl.cc \
 gnu/java/lang/management/natVMCompilationMXBeanImpl.cc \

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-02-13  5:36       ` David Daney
@ 2007-02-13 18:28         ` Tom Tromey
  2007-02-13 19:38           ` David Daney
  2007-02-14 23:22           ` David Daney
  0 siblings, 2 replies; 17+ messages in thread
From: Tom Tromey @ 2007-02-13 18:28 UTC (permalink / raw)
  To: David Daney; +Cc: Java Patch List, gcc-patches, Johannes P. Schmidt

>>>>> "David" == David Daney <ddaney@avtrex.com> writes:

>> I wonder if we could fix this by putting the new tool code into
>> standard.omit, and then compile it separately using a special rule of
>> some kind, with a classpath pointing to the tools zip.

David> Ok, I added some rules to classpath/lib/Makefile.am, that parallel the
David> special libgcj rules, to build gcj tools with a different classpath.
David> To build more packages this way (i.e. gcj_dbtool), it should be as
David> simple as adding the package name to the list in libjava's
David> configure.ac. There will no longer be any excuses to prevent us from
David> converting gcj_dbtool to use the getopt things.

David> OK to commit if no regressions?

I was hoping for 2 things, which I didn't communicate well.

One is, no changes to libjava/classpath/.  But I realize now that this
is too hard.

The other is, not adding the tools classes to AM_GCJFLAGS.

Instead of adding a new script in classpath/lib, would it be posssible
to build the new tool code in classpath/tools/Makefile?  Then the new
tool could be built in libjava/Makefile.am following the recipe for
all the other classpath tools.

Tom

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-02-13 18:28         ` Tom Tromey
@ 2007-02-13 19:38           ` David Daney
  2007-02-13 20:27             ` Tom Tromey
  2007-02-14 23:22           ` David Daney
  1 sibling, 1 reply; 17+ messages in thread
From: David Daney @ 2007-02-13 19:38 UTC (permalink / raw)
  To: tromey; +Cc: Java Patch List, gcc-patches

Tom Tromey wrote:
>>>>>> "David" == David Daney <ddaney@avtrex.com> writes:
> 
>>> I wonder if we could fix this by putting the new tool code into
>>> standard.omit, and then compile it separately using a special rule of
>>> some kind, with a classpath pointing to the tools zip.
> 
> David> Ok, I added some rules to classpath/lib/Makefile.am, that parallel the
> David> special libgcj rules, to build gcj tools with a different classpath.
> David> To build more packages this way (i.e. gcj_dbtool), it should be as
> David> simple as adding the package name to the list in libjava's
> David> configure.ac. There will no longer be any excuses to prevent us from
> David> converting gcj_dbtool to use the getopt things.
> 
> David> OK to commit if no regressions?
> 
> I was hoping for 2 things, which I didn't communicate well.
> 
> One is, no changes to libjava/classpath/.  But I realize now that this
> is too hard.
> 
> The other is, not adding the tools classes to AM_GCJFLAGS.

This shouldn't matter because the tools classes are not included in the 
java -> class compilation classpath.  If there are problems, they will 
show up in this step.

> 
> Instead of adding a new script in classpath/lib, would it be posssible
> to build the new tool code in classpath/tools/Makefile?  Then the new
> tool could be built in libjava/Makefile.am following the recipe for
> all the other classpath tools.
> 

(Almost) anything is possible.  If I did it that way, there would be a 
great asymmetry in that gcj_dbtool is not built that way.

David Daney

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-02-13 19:38           ` David Daney
@ 2007-02-13 20:27             ` Tom Tromey
  0 siblings, 0 replies; 17+ messages in thread
From: Tom Tromey @ 2007-02-13 20:27 UTC (permalink / raw)
  To: David Daney; +Cc: Java Patch List, gcc-patches

>>>>> "David" == David Daney <ddaney@avtrex.com> writes:

David> (Almost) anything is possible.  If I did it that way, there would be a
David> great asymmetry in that gcj_dbtool is not built that way.

If you do it I will be happy to fix gcj_dbtool.

Tom

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-02-13 18:28         ` Tom Tromey
  2007-02-13 19:38           ` David Daney
@ 2007-02-14 23:22           ` David Daney
  2007-02-15  0:12             ` Tom Tromey
  1 sibling, 1 reply; 17+ messages in thread
From: David Daney @ 2007-02-14 23:22 UTC (permalink / raw)
  To: tromey; +Cc: Java Patch List, gcc-patches, Johannes P. Schmidt

[-- Attachment #1: Type: text/plain, Size: 5064 bytes --]

New version of the patch.

Tom Tromey wrote:
>>>>>> "David" == David Daney <ddaney@avtrex.com> writes:
>>>>>>             
>
>   
>>> I wonder if we could fix this by putting the new tool code into
>>> standard.omit, and then compile it separately using a special rule of
>>> some kind, with a classpath pointing to the tools zip.
>>>       
>
> David> Ok, I added some rules to classpath/lib/Makefile.am, that parallel the
> David> special libgcj rules, to build gcj tools with a different classpath.
> David> To build more packages this way (i.e. gcj_dbtool), it should be as
> David> simple as adding the package name to the list in libjava's
> David> configure.ac. There will no longer be any excuses to prevent us from
> David> converting gcj_dbtool to use the getopt things.
>
> David> OK to commit if no regressions?
>
> I was hoping for 2 things, which I didn't communicate well.
>
> One is, no changes to libjava/classpath/.  But I realize now that this
> is too hard.
>
> The other is, not adding the tools classes to AM_GCJFLAGS.
>
> Instead of adding a new script in classpath/lib, would it be posssible
> to build the new tool code in classpath/tools/Makefile?  Then the new
> tool could be built in libjava/Makefile.am following the recipe for
> all the other classpath tools.
>   
Done (I hope!).

That should be the last problem with the patch.

Initial build looks good.  I will finish testing tonight.

OK to commit if no regressions?

gcc/java:
2007-02-14  David Daney  <ddaney@avtrex.com>

    * Make-lang.in (JAVA_MANFILES): Add doc/gc-analyze.1.
    (java.maintainer-clean):Add gc-analyze.1.
    (.INTERMEDIATE): Add gc-analyze.pod.
    (gc-analyze.pod): New rule.
    (java.install-man): Install gc-analyze.1
    * gcj.texi: Add new section for the gc-analyze program.

libjava:
2007-02-14  Johannes Schmidt  <jschmidt@avtrex.com>
    David Daney  <ddaney@avtrex.com>

    * configure.ac: Create vm-tools-packages file.  Add
    gnu/gcj/tools/gc_analyze to standard.omit and vm-tools-packages.
    Check for /proc/self/maps.
    * Makefile.am (bin_PROGRAMS): Added gc-analyze.
    (gc_analyze_SOURCES): New.
    (gc_analyze_LDFLAGS): New.
    (gc_analyze_LINK): New.
    (gc_analyze_LDADD): New.
    (gc_analyze_DEPENDENCIES): New.
    (nat_source_files): Add gnu/gcj/util/natGCInfo.cc.
    * Makefile.in: Regenerated.
    * configure: Regenerated.
    * include/config.h.in: Regenerated.
    * sources.am: Regenerated.
    * scripts/makemake.tcl: Don't include gc-analyze classes in libgcj.
    * gnu/gcj/tools/gc_analyze/SymbolLookup.java: New.
    * gnu/gcj/tools/gc_analyze/ObjectMap.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryMap.java: New.
    * gnu/gcj/tools/gc_analyze/SymbolTable.java: New.
    * gnu/gcj/tools/gc_analyze/BlockMap.java: New.
    * gnu/gcj/tools/gc_analyze/BytePtr.java: New.
    * gnu/gcj/tools/gc_analyze/ItemList.java: New.
    * gnu/gcj/tools/gc_analyze/ToolPrefix.java: New.
    * gnu/gcj/tools/gc_analyze/MemoryAnalyze.java: New.
    * gnu/gcj/util/GCInfo.java: New.
    * gnu/gcj/util/GCInfo.h: New.
    * gnu/gcj/util/natGCInfo.cc: New.
    * gnu/gcj/util/UtilPermission.java: New.
    * gnu/gcj/util/UtilPermission.h: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/SymbolTable.class: New.
    * 
classpath/tools/gnu/gcj/tools/gc_analyze/ObjectMap$ObjectItem.class: New.
    * 
classpath/tools/gnu/gcj/tools/gc_analyze/MemoryMap$RangeComparator.class: 
New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/BlockMap$PtrMarks.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/MemoryMap$Range.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/BlockMap.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/BytePtr.class: New.
    * 
classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$SubstringComparator.class: 
New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/ItemList.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/ToolPrefix.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze.class: New.
    * 
classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$1$Info.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$1.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$2.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$3.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$4.class: New.
    * 
classpath/tools/gnu/gcj/tools/gc_analyze/MemoryAnalyze$OptionParser.class: 
New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/BlockMap$SizeKind.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/SymbolLookup.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/ObjectMap.class: New.
    * classpath/tools/gnu/gcj/tools/gc_analyze/MemoryMap.class: New.
    * classpath/lib/gnu/gcj/util/GCInfo.class: New.
    * classpath/lib/gnu/gcj/util/UtilPermission.class: New.

libjava/classpath:
2007-02-14  David Daney  <ddaney@avtrex.com>

    * tools/Makefile.am (TOOLS_ZIP): Add classes from vm-tools-packages.
    * tools/Makefile.in: Regenerated.



[-- Attachment #2: gc-analyze.diff.txt --]
[-- Type: text/plain, Size: 77351 bytes --]

Index: gcc/java/Make-lang.in
===================================================================
--- gcc/java/Make-lang.in	(revision 121878)
+++ gcc/java/Make-lang.in	(working copy)
@@ -139,7 +139,7 @@ java.pdf: doc/gcj.pdf
 java.html: $(build_htmldir)/java/index.html
 JAVA_MANFILES = doc/gcj.1 doc/jcf-dump.1 doc/gij.1 \
                 doc/jv-convert.1 doc/grmic.1 doc/grmiregistry.1 \
-		doc/gcj-dbtool.1
+		doc/gcj-dbtool.1 doc/gc-analyze.1
 
 java.man: $(JAVA_MANFILES)
 
@@ -208,6 +208,7 @@ java.maintainer-clean:
 	-rm -f $(docobjdir)/grmic.1
 	-rm -f $(docobjdir)/grmiregistry.1
 	-rm -f $(docobjdir)/gcj-dbtool.1
+	-rm -f $(docobjdir)/gc-analyze.1
 #\f
 # Stage hooks:
 # The main makefile has already created stage?/java.
@@ -318,7 +319,7 @@ $(build_htmldir)/java/index.html: $(TEXI
 	$(TEXI2HTML) -I $(gcc_docdir)/include -I $(srcdir)/java -o $(@D) $<
 
 .INTERMEDIATE: gcj.pod jcf-dump.pod gij.pod \
-  jv-convert.pod grmic.pod grmiregistry.pod gcj-dbtool.pod
+  jv-convert.pod grmic.pod grmiregistry.pod gcj-dbtool.pod gc-analyze.pod
 
 gcj.pod: java/gcj.texi
 	-$(TEXI2POD) -D gcj < $< > $@
@@ -334,15 +335,17 @@ grmiregistry.pod: java/gcj.texi
 	-$(TEXI2POD) -D grmiregistry < $< > $@
 gcj-dbtool.pod: java/gcj.texi
 	-$(TEXI2POD) -D gcj-dbtool < $< > $@
+gc-analyze.pod: java/gcj.texi
+	-$(TEXI2POD) -D gc-analyze < $< > $@
 
 # Install the man pages.
 java.install-man: installdirs \
                   $(DESTDIR)$(man1dir)/$(JAVA_INSTALL_NAME)$(man1ext) \
 		  $(JAVA_TARGET_INDEPENDENT_BIN_TOOLS:%=doc/%.1) \
 		  doc/gij.1 doc/jv-convert.1 doc/grmic.1 doc/grmiregistry.1 \
-		  doc/gcj-dbtool.1
+		  doc/gcj-dbtool.1 doc/gc-analyze.1
 	for tool in $(JAVA_TARGET_INDEPENDENT_BIN_TOOLS) \
-		gij jv-convert grmic grmiregistry gcj-dbtool ; do \
+		gij jv-convert grmic grmiregistry gcj-dbtool gc-analyze ; do \
 	  tool_transformed_name=`echo $$tool|sed '$(program_transform_name)'`; \
 	  man_name=$(DESTDIR)$(man1dir)/$${tool_transformed_name}$(man1ext); \
 	  rm -f $$man_name ; \
Index: gcc/java/gcj.texi
===================================================================
--- gcc/java/gcj.texi	(revision 121878)
+++ gcc/java/gcj.texi	(working copy)
@@ -17,7 +17,7 @@
 @c the word ``Java'.
 
 @c When this manual is copyrighted.
-@set copyrights-gcj 2001, 2002, 2003, 2004, 2005
+@set copyrights-gcj 2001, 2002, 2003, 2004, 2005, 2006, 2007
 
 @c Versions
 @set which-gcj GCC-@value{version-GCC}
@@ -79,6 +79,8 @@ man page gfdl(7).
                             Generate stubs for Remote Method Invocation.
 * grmiregistry: (gcj)Invoking grmiregistry.
                             The remote object registry.
+* gc-analyze: (gcj)Invoking gc-analyze.
+                            Analyze Garbage Collector (GC) memory dumps.
 @end direntry
 @end format
 
@@ -125,6 +127,7 @@ files and object files, and it can read 
 * Invoking jv-convert:: Converting from one encoding to another
 * Invoking grmic::      Generate stubs for Remote Method Invocation.
 * Invoking grmiregistry:: The remote object registry.
+* Invoking gc-analyze:: Analyze Garbage Collector (GC) memory dumps.
 * About CNI::           Description of the Compiled Native Interface
 * System properties::   Modifying runtime behavior of the libgcj library
 * Resources::		Where to look for more information
@@ -1412,6 +1415,89 @@ Print version information, then exit.
 @c man end
 
 
+@node Invoking gc-analyze
+@chapter Invoking gc-analyze
+
+@c man title gc-analyze Analyze Garbage Collector (GC) memory dumps
+
+@c man begin SYNOPSIS gc-analyze
+@command{gc-analyze} [@option{OPTION}] @dots{} [@var{file}]
+@ignore
+  [@option{-v}]
+  [@option{--verbose}]
+  [@option{-p} @var{tool-prefix}]
+  [@option{-d} @var{directory}]
+  [@option{--version}]
+  [@option{--help}]
+@end ignore
+@c man end
+
+@c man begin DESCRIPTION gc-analyze
+
+@command{gc-analyze} prints an analysis of a GC memory dump to
+standard out.
+
+The memory dumps may be created by calling
+@code{gnu.gcj.util.GCInfo.enumerate(String namePrefix)} from java
+code.  A memory dump will be created on an out of memory condition if
+@code{gnu.gcj.util.GCInfo.setOOMDump(String namePrefix)} is called
+before the out of memory occurs.
+
+Running this program will create two files: @file{TestDump001} and
+@file{TestDump001.bytes}.
+
+@example
+import gnu.gcj.util.*;
+import java.util.*;
+
+public class GCDumpTest
+@{
+    static public void main(String args[])
+    @{
+        ArrayList<String> l = new ArrayList<String>(1000);
+        
+        for (int i = 1; i < 1500; i++) @{
+            l.add("This is string #" + i);
+        @}
+        GCInfo.enumerate("TestDump");
+    @}
+@}
+@end example
+
+The memory dump may then be displayed by running:
+
+@example
+gc-analyze -v TestDump001
+@end example
+
+@c FIXME: Add real information here.
+@c This really isn't much more than the --help output.
+
+@c man end
+
+@c man begin OPTIONS gc-analyze
+
+@table @gcctabopt
+@item --verbose
+@itemx -v
+Verbose output.
+
+@item -p @var{tool-prefix}
+Prefix added to the names of the @command{nm} and @command{readelf} commands.
+
+@item -d @var{directory}
+Directory that contains the executable and shared libraries used when
+the dump was generated.
+
+@item --help
+Print a help message, then exit.
+
+@item --version
+Print version information, then exit.
+@end table
+
+@c man end
+
 @node About CNI
 @chapter About CNI
 
Index: libjava/scripts/makemake.tcl
===================================================================
--- libjava/scripts/makemake.tcl	(revision 121878)
+++ libjava/scripts/makemake.tcl	(working copy)
@@ -42,6 +42,7 @@ set package_map(.) package
 # These are ignored in Classpath.
 set package_map(gnu/test) ignore
 set package_map(gnu/javax/swing/plaf/gtk) ignore
+set package_map(gnu/gcj/tools/gc_analyze) ignore
 
 set package_map(gnu/java/awt/peer/swing) bc
 
Index: libjava/configure.ac
===================================================================
--- libjava/configure.ac	(revision 121878)
+++ libjava/configure.ac	(working copy)
@@ -295,6 +295,12 @@ if test "$use_x_awt" != yes; then
    echo gnu/java/awt/peer/x >> standard.omit
 fi
 
+# Tools that need to be compiled against classpath's tools classes
+for package in gnu/gcj/tools/gc_analyze ; do
+    echo $package >> standard.omit
+    echo $package >> vm-tools-packages
+done
+
 if test -z "${with_multisubdir}"; then
    builddotdot=.
 else
@@ -1012,10 +1018,14 @@ else
    if test x"$cross_compiling" = x"no"; then
      AC_CHECK_FILES(/proc/self/exe, [
        AC_DEFINE(HAVE_PROC_SELF_EXE, 1, [Define if you have /proc/self/exe])])
+     AC_CHECK_FILES(/proc/self/maps, [
+       AC_DEFINE(HAVE_PROC_SELF_MAPS, 1,
+         [Define if you have /proc/self/maps])])
    else
      case $host in
      *-linux*)
        AC_DEFINE(HAVE_PROC_SELF_EXE, 1, [Define if you have /proc/self/exe])
+       AC_DEFINE(HAVE_PROC_SELF_MAPS, 1, [Define if you have /proc/self/maps])
        ;;
      esac
    fi
Index: libjava/classpath/tools/Makefile.am
===================================================================
--- libjava/classpath/tools/Makefile.am	(revision 121955)
+++ libjava/classpath/tools/Makefile.am	(working copy)
@@ -155,12 +155,19 @@ $(TOOLS_ZIP): $(TOOLS_JAVA_FILES)
 ##	mkdir classes asm
 	find $(srcdir)/external/asm -name '*.java' -print > asm.lst
 	find $(srcdir)/gnu/classpath/tools -name '*.java' -print > classes.lst
-	cat classes.lst asm.lst > all-classes.lst
+	if [ -f $(top_builddir)/../vm-tools-packages ]; then \
+	    for pkg in `cat $(top_builddir)/../vm-tools-packages`; do \
+	      $(FIND) $(top_srcdir)/../$$pkg -follow -name '*.java' -print >> vm-tools.lst; \
+	    done \
+	else \
+	    echo -n > vm-tools.lst; \
+	fi
+	cat classes.lst asm.lst vm-tools.lst > all-classes.lst
 if JAVA_MAINTAINER_MODE
 ## Compile ASM separately as it is latin-1 encoded.
 	AC=`echo $(JCOMPILER) | sed -e 's/UTF-8/ISO-8859-1/g'`; \
 	  $$AC -g -w -d $(srcdir)/asm @asm.lst
-	$(JCOMPILER) -g -w -d $(srcdir)/classes @classes.lst
+	$(JCOMPILER) -g -w -d $(srcdir)/classes @classes.lst @vm-tools.lst
 ## Copy over tools resource files.
 	@list=`cd $(top_srcdir)/resource && find gnu/classpath/tools -name \*.properties -print`; \
 	for p in $$list; do \
Index: libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/SymbolLookup.java	(revision 0)
@@ -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);
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ObjectMap.java	(revision 0)
@@ -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);
+      }
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/MemoryMap.java	(revision 0)
@@ -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);
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/BlockMap.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/BlockMap.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/BlockMap.java	(revision 0)
@@ -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));
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/BytePtr.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/BytePtr.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/BytePtr.java	(revision 0)
@@ -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());
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ItemList.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ItemList.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ItemList.java	(revision 0)
@@ -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();
+          }
+      }
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/ToolPrefix.java	(revision 0)
@@ -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;
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/MemoryAnalyze.java	(revision 0)
@@ -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;
+  }
+}
Index: libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java
===================================================================
--- libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java	(revision 0)
+++ libjava/gnu/gcj/tools/gc_analyze/SymbolTable.java	(revision 0)
@@ -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();
+  }
+}
Index: libjava/gnu/gcj/util/GCInfo.java
===================================================================
--- libjava/gnu/gcj/util/GCInfo.java	(revision 0)
+++ libjava/gnu/gcj/util/GCInfo.java	(revision 0)
@@ -0,0 +1,79 @@
+/* GCInfo.java -- Support for creating heap dumps.
+   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.util;
+
+public class GCInfo
+{
+  private GCInfo()
+  {
+  }
+
+  /**
+   * @throws SecurityException if there is a SecurityManager installed
+   * and UtilPermission("dumpHeap") is not granted.
+   */
+  private static void checkPermission()
+  {
+    SecurityManager sm = System.getSecurityManager();
+    if (sm != null)
+      sm.checkPermission(new UtilPermission("dumpHeap"));
+  }
+  
+
+  /**
+   * Dump a description of the heap state.
+   *
+   * @param namePrefix The filename prefix for the dump files.
+   *
+   * @throws SecurityException if there is a SecurityManager installed
+   * and UtilPermission("dumpHeap") is not granted.
+   */
+  public static synchronized void dump(String name)
+  {
+    checkPermission();
+    dump0(name);
+  }
+  
+  private static native void dump0(String name);
+
+
+  /**
+   * Create a heap dump.
+   *
+   * @param namePrefix The filename prefix for the dump files.
+   *
+   * @throws SecurityException if there is a SecurityManager installed
+   * and UtilPermission("dumpHeap") is not granted.
+   */
+  public static synchronized void enumerate(String namePrefix)
+  {
+    checkPermission();
+    enumerate0(namePrefix);
+  }
+  
+  private static native void enumerate0(String namePrefix);
+
+  /**
+   * Cause a heap dump if out-of-memory condition occurs.
+   *
+   * @param namePrefix The filename prefix for the dump files.  If
+   * null no dumps are created.
+   *
+   * @throws SecurityException if there is a SecurityManager installed
+   * and UtilPermission("dumpHeap") is not granted.
+   */
+  public static synchronized void setOOMDump(String namePrefix)
+  {
+    checkPermission();
+    setOOMDump0(namePrefix);
+  }
+  
+  private static native void setOOMDump0(String namePrefix);
+}
Index: libjava/gnu/gcj/util/UtilPermission.java
===================================================================
--- libjava/gnu/gcj/util/UtilPermission.java	(revision 0)
+++ libjava/gnu/gcj/util/UtilPermission.java	(revision 0)
@@ -0,0 +1,20 @@
+/* GCInfo.java -- Support for creating heap dumps.
+   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.util;
+
+import java.security.BasicPermission;
+
+public class UtilPermission extends BasicPermission
+{
+  public UtilPermission(String name)
+  {
+    super(name);
+  }
+}
Index: libjava/gnu/gcj/util/natGCInfo.cc
===================================================================
--- libjava/gnu/gcj/util/natGCInfo.cc	(revision 0)
+++ libjava/gnu/gcj/util/natGCInfo.cc	(revision 0)
@@ -0,0 +1,454 @@
+/* natGCInfo.cc -- Native portion of support for creating heap dumps.
+   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.  */
+
+
+#include <config.h>
+
+#include <gcj/cni.h>
+
+#include <gnu/gcj/util/GCInfo.h>
+
+#ifdef HAVE_PROC_SELF_MAPS
+//
+// If /proc/self/maps does not exist we assume we are doomed and do nothing.
+//
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+//
+// Boehm GC includes.
+//
+#ifdef PACKAGE_NAME
+#undef PACKAGE_NAME
+#endif
+
+#ifdef PACKAGE_STRING
+#undef PACKAGE_STRING
+#endif
+
+#ifdef PACKAGE_TARNAME
+#undef PACKAGE_TARNAME
+#endif
+
+#ifdef PACKAGE_VERSION
+#undef PACKAGE_VERSION
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+
+#ifdef FALSE
+#undef FALSE
+#endif
+
+extern "C" {
+#include "private/dbg_mlc.h"
+  int GC_n_set_marks(hdr* hhdr);
+  ptr_t GC_clear_stack(ptr_t p);
+  extern int GC_gcj_kind;
+  extern int GC_gcj_debug_kind;
+}
+
+#endif
+
+#ifdef HAVE_PROC_SELF_MAPS
+
+static int gc_ok = 1;
+
+typedef struct gc_debug_info
+{
+  int used;
+  int free;
+  int wasted;
+  int blocks;
+  FILE* fp;
+};
+
+static void
+GC_print_debug_callback(hblk *h, word user_data)
+{
+  hdr *hhdr = HDR(h);
+  size_t bytes = WORDS_TO_BYTES(hhdr -> hb_sz);
+
+  gc_debug_info *pinfo = (gc_debug_info *)user_data;
+
+  fprintf(pinfo->fp, "ptr = %#lx, kind = %d, size = %zd, marks = %d\n",
+          (unsigned long)h, hhdr->hb_obj_kind, bytes, GC_n_set_marks(hhdr));
+}
+
+/*
+  this next section of definitions shouldn't really be here.
+  copied from boehmgc/allchblk.c
+*/
+
+# define UNIQUE_THRESHOLD 32
+# define HUGE_THRESHOLD 256
+# define FL_COMPRESSION 8
+# define N_HBLK_FLS (HUGE_THRESHOLD - UNIQUE_THRESHOLD)/FL_COMPRESSION \
+                         + UNIQUE_THRESHOLD
+#ifndef USE_MUNMAP
+extern "C" {
+  extern word GC_free_bytes[N_HBLK_FLS+1];
+}
+#endif
+
+# ifdef USE_MUNMAP
+#   define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0)
+# else  /* !USE_MMAP */
+#   define IS_MAPPED(hhdr) 1
+# endif /* USE_MUNMAP */
+
+static void
+GC_print_hblkfreelist_file(FILE *fp)
+{
+  struct hblk * h;
+  word total_free = 0;
+  hdr * hhdr;
+  word sz;
+  int i;
+    
+  fprintf(fp, "---------- Begin free map ----------\n");
+  for (i = 0; i <= N_HBLK_FLS; ++i)
+    {
+      h = GC_hblkfreelist[i];
+#ifdef USE_MUNMAP
+      if (0 != h)
+        fprintf (fp, "Free list %ld:\n", (unsigned long)i);
+#else
+      if (0 != h)
+        fprintf (fp, "Free list %ld (Total size %ld):\n",
+                 (unsigned long)i,
+                 (unsigned long)GC_free_bytes[i]);
+#endif
+      while (h != 0)
+        {
+          hhdr = HDR(h);
+          sz = hhdr -> hb_sz;
+          fprintf (fp, "\t0x%lx size %lu ", (unsigned long)h,
+                   (unsigned long)sz);
+          total_free += sz;
+
+          if (GC_is_black_listed (h, HBLKSIZE) != 0)
+            fprintf (fp, "start black listed\n");
+          else if (GC_is_black_listed(h, hhdr -> hb_sz) != 0)
+            fprintf (fp, "partially black listed\n");
+          else
+            fprintf (fp, "not black listed\n");
+
+          h = hhdr -> hb_next;
+        }
+    }
+#ifndef USE_MUNMAP
+  if (total_free != GC_large_free_bytes)
+    {
+      fprintf (fp, "GC_large_free_bytes = %lu (INCONSISTENT!!)\n",
+               (unsigned long) GC_large_free_bytes);
+    }
+#endif
+  fprintf (fp, "Total of %lu bytes on free list\n", (unsigned long)total_free);
+  fprintf (fp, "---------- End free map ----------\n");
+}
+
+static int GC_dump_count = 1;
+
+static void
+GC_print_debug_info_file(FILE* fp)
+{
+  gc_debug_info info;
+
+  memset(&info, 0, sizeof info);
+  info.fp = fp;
+
+  if (gc_ok)
+    GC_gcollect();
+  fprintf(info.fp, "---------- Begin block map ----------\n");
+  GC_apply_to_all_blocks(GC_print_debug_callback, (word)(void*)(&info));
+  //fprintf(fp, "#Total used %d free %d wasted %d\n", info.used, info.free, info.wasted);
+  //fprintf(fp, "#Total blocks %d; %dK bytes\n", info.blocks, info.blocks*4);
+  fprintf(info.fp, "---------- End block map ----------\n");
+
+  //fprintf(fp, "\n***Free blocks:\n");
+  //GC_print_hblkfreelist();
+}
+
+namespace
+{
+  class  __attribute__ ((visibility ("hidden"))) GC_enumerator
+  {
+  public:
+    GC_enumerator(const char *name);
+    void enumerate();
+  private:
+    FILE* fp;
+    int bytes_fd;
+
+    void print_address_map();
+    void enumerate_callback(struct hblk *h);
+    static void enumerate_callback_adaptor(struct hblk *h, word dummy);
+  };
+}
+
+GC_enumerator::GC_enumerator(const char *name)
+{
+  bytes_fd = -1;
+  fp = fopen (name, "w");
+  if (!fp)
+    {
+      printf ("GC_enumerator failed to open [%s]\n", name);
+      return;
+    }
+  printf ("GC_enumerator saving summary to [%s]\n", name);
+
+  // open heap file
+  char bytes_name[strlen(name) + 10];
+  sprintf (bytes_name, "%s.bytes", name);
+  bytes_fd = open (bytes_name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+  if (bytes_fd <= 0)
+    {
+      printf ("GC_enumerator failed to open [%s]\n", bytes_name);
+      return;
+    }
+  printf ("GC_enumerator saving heap contents to [%s]\n", bytes_name);
+}
+
+/*
+  sample format of /proc/self/maps
+
+  0063b000-00686000 rw-p 001fb000 03:01 81993      /avtrex/bin/dumppropapp
+  00686000-0072e000 rwxp 00000000 00:00 0 
+
+  These are parsed below as:
+  start   -end      xxxx xxxxxxxx  a:b xxxxxxxxxxxxxxx
+
+*/
+
+
+void
+GC_enumerator::print_address_map()
+{
+  FILE* fm;
+  char buffer[128];
+
+  fprintf(fp, "---------- Begin address map ----------\n");
+
+  fm = fopen("/proc/self/maps", "r");
+  if (fm == NULL)
+    {
+      if (0 == strerror_r (errno, buffer, sizeof buffer))
+        fputs (buffer, fp);
+    }
+  else
+    {
+      while (fgets (buffer, sizeof buffer, fm) != NULL)
+        {
+          fputs (buffer, fp);
+          char *dash = strchr(buffer, '-');
+          char *colon = strchr(buffer, ':');
+          if (dash && colon && ((ptrdiff_t)strlen(buffer) > (colon - buffer) + 2))
+            {
+              char *endp;
+              unsigned long start = strtoul(buffer, NULL, 16);
+              unsigned long end   = strtoul(dash + 1, &endp, 16);
+              unsigned long a     = strtoul(colon - 2, NULL, 16);
+              unsigned long b     = strtoul(colon + 1, NULL, 16);
+              // If it is an anonymous mapping 00:00 and both readable
+              // and writeable then dump the contents of the mapping
+              // to the bytes file.  Each block has a header of three
+              // unsigned longs:
+              // 0 - The number sizeof(unsigned long) to detect endianness and
+              //     structure layout.
+              // 1 - The offset in VM.
+              // 2 - The Length in bytes.
+              // Followed by the bytes.
+              if (!a && !b && endp < colon && 'r' == endp[1] && 'w' == endp[2])
+                {
+                  unsigned long t = sizeof(unsigned long);
+                  write(bytes_fd, (void*)&t, sizeof(t));
+                  write(bytes_fd, (void*)&start, sizeof(start));
+                  t = end - start;
+                  write(bytes_fd, (void*)&t, sizeof(t));
+                  write(bytes_fd, (void*)start, (end - start));
+                }
+            }
+        } 
+      fclose(fm);
+    }
+  fprintf(fp, "---------- End address map ----------\n");
+  fflush(fp);
+}
+
+void
+GC_enumerator::enumerate()
+{
+  print_address_map();
+  fprintf(fp, "---------- Begin object map ----------\n");
+  if (gc_ok)
+    GC_gcollect();
+  GC_apply_to_all_blocks(enumerate_callback_adaptor, 
+                         (word)(void*)(this));
+  fprintf(fp, "---------- End object map ----------\n");
+  fflush(fp); 
+
+  GC_print_debug_info_file(fp);
+  fflush(fp); 
+  GC_print_hblkfreelist_file(fp);
+  fflush(fp); 
+
+  close(bytes_fd);
+  fclose(fp);
+
+  GC_clear_stack(0);
+}
+
+void
+GC_enumerator::enumerate_callback_adaptor(struct hblk *h,
+                                          word dummy)
+{
+  GC_enumerator* pinfo = (GC_enumerator*)dummy;
+  pinfo->enumerate_callback(h);
+}
+
+void
+GC_enumerator::enumerate_callback(struct hblk *h)
+{
+  hdr * hhdr = HDR(h);
+  size_t bytes = WORDS_TO_BYTES(hhdr->hb_sz);
+  int i;
+
+  for (i = 0; i == 0 || (i + bytes <= HBLKSIZE); i += bytes)
+    {
+      int inUse = mark_bit_from_hdr(hhdr,BYTES_TO_WORDS(i));  // in use
+      char *ptr = (char*)h+i;                                 // address
+      int kind = hhdr->hb_obj_kind;                           // kind
+      void *klass = 0;
+      void *data = 0;
+      if (kind == GC_gcj_kind
+          || kind == GC_gcj_debug_kind
+          || kind == GC_gcj_debug_kind+1)
+        {
+          void* v = *(void **)ptr;
+          if (v)
+            {
+              klass = *(void **)v;
+              data = *(void **)(ptr + sizeof(void*));
+            }
+        }
+      if (inUse)
+        fprintf (fp, "used = %d, ptr = %#lx, size = %zd, kind = %d, "
+                 "klass = %#lx, data = %#lx\n", 
+                 inUse, (unsigned long)ptr, bytes, kind,
+                 (unsigned long)klass, (unsigned long)data);
+    }
+}
+
+/*
+ * Fill in a char[] with low bytes of the string characters.  These
+ * methods may be called while an OutOfMemoryError is being thrown, so
+ * we cannot call nice java methods to get the encoding of the string.
+ */
+static void
+J2A(::java::lang::String* str, char *dst)
+{
+  jchar * pchars = JvGetStringChars(str);
+  jint len = str->length();
+  int i;
+  for (i=0; i<len; i++)
+    dst[i] = (char)pchars[i];
+  dst[i] = 0;
+}
+
+void
+::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
+{
+  char n[name->length() + 1];
+  J2A(name, n);
+  
+  char temp[name->length() + 20];
+  sprintf(temp, "%s%03d", n, GC_dump_count++);
+  FILE* fp = fopen(temp, "w");
+
+  GC_print_debug_info_file(fp);
+
+  fclose(fp);
+}
+
+void
+::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
+{
+  char n[name->length() + 1];
+  J2A(name, n);
+  char temp[name->length() + 20];
+  sprintf(temp, "%s%03d", n, GC_dump_count++);
+
+  GC_enumerator x(temp);
+  x.enumerate();
+}
+
+static char *oomDumpName = NULL;
+
+static void *
+nomem_handler(size_t size)
+{
+  if (oomDumpName)
+    {
+      char temp[strlen(oomDumpName) + 20];
+      sprintf(temp, "%s%03d", temp, GC_dump_count++);
+      printf("nomem_handler(%zd) called\n", size);
+      gc_ok--;
+      GC_enumerator x(temp);
+      x.enumerate();
+      gc_ok++;
+    }
+  return (void*)0;
+}
+
+void
+::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
+{
+  char *oldName = oomDumpName;
+  oomDumpName = NULL;
+  free (oldName);
+  
+  if (NULL == name)
+    return;
+  
+  char *n = (char *)malloc(name->length() + 1);
+
+  J2A(name, n);
+  oomDumpName = n;
+  GC_oom_fn = nomem_handler;
+}
+
+#else  // HAVE_PROC_SELF_MAPS
+
+void
+::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+void
+::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+void
+::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
+{
+  // Do nothing if dumping not supported.
+}
+
+#endif // HAVE_PROC_SELF_MAPS
+
Index: libjava/Makefile.am
===================================================================
--- libjava/Makefile.am	(revision 121878)
+++ libjava/Makefile.am	(working copy)
@@ -73,7 +73,7 @@ db_pathtail = gcj-$(gcc_version)/$(db_na
 if NATIVE
 bin_PROGRAMS = jv-convert gij grmic grmiregistry gcj-dbtool \
 	gappletviewer gjarsigner gkeytool gjar gjavah gnative2ascii \
-	gorbd grmid gserialver gtnameserv
+	gorbd grmid gserialver gtnameserv gc-analyze
 
 ## It is convenient to actually build and install the default database
 ## when gcj-dbtool is available.
@@ -647,6 +647,20 @@ gcj_dbtool_LDADD = gnu/gcj/tools/gcj_dbt
 ## linking this program.
 gcj_dbtool_DEPENDENCIES = gnu/gcj/tools/gcj_dbtool.lo libgcj.la libgcj.spec
 
+gc_analyze_SOURCES =
+
+## We need -nodefaultlibs because we want to avoid gcj's `-lgcj'.  We
+## need this because we are explicitly using libtool to link using the
+## `.la' file.
+gc_analyze_LDFLAGS = --main=gnu.gcj.tools.gc_analyze.MemoryAnalyze \
+        -rpath $(toolexeclibdir) -shared-libgcc $(THREADLDFLAGS)
+gc_analyze_LINK = $(GCJLINK)
+## See jv_convert_LDADD.
+gc_analyze_LDADD = -L$(here)/.libs libgcj-tools.la libgcj.la
+## Depend on the spec file to make sure it is up to date before
+## linking this program.
+gc_analyze_DEPENDENCIES = libgcj-tools.la libgcj.la libgcj.spec
+
 gij_SOURCES = 
 ## We need -nodefaultlibs because we want to avoid gcj's `-lgcj'.  We
 ## need this because we are explicitly using libtool to link using the
@@ -831,6 +845,7 @@ gnu/gcj/runtime/natSharedLibLoader.cc \
 gnu/gcj/runtime/natSystemClassLoader.cc \
 gnu/gcj/runtime/natStringBuffer.cc \
 gnu/gcj/util/natDebug.cc \
+gnu/gcj/util/natGCInfo.cc \
 gnu/java/lang/natMainThread.cc \
 gnu/java/lang/management/natVMClassLoadingMXBeanImpl.cc \
 gnu/java/lang/management/natVMCompilationMXBeanImpl.cc \

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Patch] Java: Add heap dump and analyze support.
  2007-02-14 23:22           ` David Daney
@ 2007-02-15  0:12             ` Tom Tromey
  0 siblings, 0 replies; 17+ messages in thread
From: Tom Tromey @ 2007-02-15  0:12 UTC (permalink / raw)
  To: David Daney; +Cc: Java Patch List, gcc-patches, Johannes P. Schmidt

>>>>> "David" == David Daney <ddaney@avtrex.com> writes:

David> That should be the last problem with the patch.
David> Initial build looks good.  I will finish testing tonight.
David> OK to commit if no regressions?

Yes.  Thank you very much -- this is an awesome feature and you have
been very patient in dealing with my requests.

If you wouldn't mind, please write a news entry for the gcj web page.
I can check it in if you supply the text.

Tom

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2007-02-15  0:12 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-01-17 23:47 [Patch] Java: Add heap dump and analyze support David Daney
2007-01-23 22:19 ` Tom Tromey
2007-01-23 23:15   ` David Daney
2007-01-24  1:08     ` Joel Dice
2007-01-24  1:52       ` Tom Tromey
2007-01-24  1:58         ` Chris Lattner
2007-01-24  2:32           ` David Daney
2007-01-29 23:58   ` David Daney
     [not found]     ` <BDA38860DCFD334EAEA905E44EE8E7EF6DD409@G3W0067.americas.hpqcorp.net>
2007-01-30 21:57       ` David Daney
2007-01-30 22:21         ` Boehm, Hans
2007-02-07 22:37     ` Tom Tromey
2007-02-13  5:36       ` David Daney
2007-02-13 18:28         ` Tom Tromey
2007-02-13 19:38           ` David Daney
2007-02-13 20:27             ` Tom Tromey
2007-02-14 23:22           ` David Daney
2007-02-15  0:12             ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).