public inbox for java-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Patch: RFC/RFT heap dump and analyze.
@ 2006-05-04 20:46 David Daney
  0 siblings, 0 replies; only message in thread
From: David Daney @ 2006-05-04 20:46 UTC (permalink / raw)
  To: Java Patch List; +Cc: Hans Boehm

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

Here is the long delayed patch to add the heap analyzer to libgcj.

I have only tested this version on i686-pc-linux-gnu.  Part of my reason 
for posting now is to see what changes should be made to allow it to 
work on other platforms before committing.  Earlier versions of the 
patch we use exclusively on mipsel-linux.  I have not tested this exact 
version on mipsel-linux, but will be doing that before I submit the 
final version.

There is a little hackiness down in gnu/gcj/util/natGCInfo.cc.  Any 
comments from Hans Boehm (or others) on how to clean up the interface to 
the GC would be welcome.

Quick HOWTO:

Install the patch and rebuild everything.  Then compile the following 
little test program:

---------8<-----------

import gnu.gcj.util.*;
import java.util.*;

public class GCDumpTest
{
     static public void main(String args[])
     {
         ArrayList l = new ArrayList(1000);

         for (int i = 1; i < 1500; i++) {
             l.add("This is string #" + i);
         }
         GCInfo.enumerate("TestDump");
     }
}

--------8<--------------

This should create two files in the current directory called TestDump001 
and TestDump001.bytes.

Next the fun part, do:

$ gc_analyze -v TestDump001 > j

This will 'think' for several minutes.  The output (redirected to the 
file j), will have an analysis of all objects allocated from the GC at 
the time of the call to GCInfo.enumerate().  There is more we can do in 
the analysis phase, but this is a start.

Note that gc_analyze runs much more quickly on Sun's runtime.  I think 
the culprit is somewhere in our java.util.regex implementation.

FWIW Johannes' paperwork is in progress...

2006-05-04  Johannes Schmidt  <jschmidt@avtrex.com>
	    David Daney  <ddaney@avtrex.com>

	* configure: Regenerated
	* Makefile.in: Regenerated.
	* configure.ac: Added check for /proc/self/maps.
	* sources.am: Regenerated.
	* include/config.h.in: Regenerated.
	* Makefile.am: Added new bin_PROGRAM gc_analyze and its dependencies.
	* gnu/gcj/util/GCInfo.java: New file.
	* gnu/gcj/util/natGCInfo.cc: New file.
	* gnu/gcj/tools/gc_analyze/BlockMap.java: New file.
	* gnu/gcj/tools/gc_analyze/BytePtr.java: New file.
	* gnu/gcj/tools/gc_analyze/ItemList.java: New file.
	* gnu/gcj/tools/gc_analyze/MemoryAnalyze.java: New file.
	* gnu/gcj/tools/gc_analyze/MemoryMap.java: New file.
	* gnu/gcj/tools/gc_analyze/ObjectMap.java: New file.
	* gnu/gcj/tools/gc_analyze/SymbolLookup.java: New file.
	* gnu/gcj/tools/gc_analyze/SymbolTable.java: New file.
	* gnu/gcj/tools/gc_analyze/ToolPrefix.java: New file.

[-- Attachment #2: gcdump.d --]
[-- Type: text/plain, Size: 87414 bytes --]

Index: configure
===================================================================
--- configure	(revision 113530)
+++ configure	(working copy)
@@ -10229,6 +10229,37 @@
 
 fi
 
+     echo "$as_me:$LINENO: checking for /proc/self/maps" >&5
+echo $ECHO_N "checking for /proc/self/maps... $ECHO_C" >&6
+if test "${ac_cv_file__proc_self_maps+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  test "$cross_compiling" = yes &&
+  { { echo "$as_me:$LINENO: error: cannot check for file existence when cross compiling" >&5
+echo "$as_me: error: cannot check for file existence when cross compiling" >&2;}
+   { (exit 1); exit 1; }; }
+if test -r "/proc/self/maps"; then
+  ac_cv_file__proc_self_maps=yes
+else
+  ac_cv_file__proc_self_maps=no
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_file__proc_self_maps" >&5
+echo "${ECHO_T}$ac_cv_file__proc_self_maps" >&6
+if test $ac_cv_file__proc_self_maps = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE__PROC_SELF_MAPS 1
+_ACEOF
+
+
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PROC_SELF_MAPS 1
+_ACEOF
+
+fi
+
    else
      case $host in
      *-linux*)
@@ -10237,6 +10268,11 @@
 #define HAVE_PROC_SELF_EXE 1
 _ACEOF
 
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_PROC_SELF_MAPS 1
+_ACEOF
+
        ;;
      esac
    fi
Index: Makefile.in
===================================================================
--- Makefile.in	(revision 113530)
+++ Makefile.in	(working copy)
@@ -17,7 +17,7 @@
 
 
 
-SOURCES = $(lib_gnu_awt_xlib_la_SOURCES) $(lib_gnu_java_awt_peer_gtk_la_SOURCES) $(lib_gnu_java_awt_peer_qt_la_SOURCES) $(libgcj_la_SOURCES) $(libgcjawt_la_SOURCES) $(libgij_la_SOURCES) $(gcj_dbtool_SOURCES) $(gen_from_JIS_SOURCES) $(gij_SOURCES) $(grmic_SOURCES) $(grmiregistry_SOURCES) $(jv_convert_SOURCES)
+SOURCES = $(lib_gnu_awt_xlib_la_SOURCES) $(lib_gnu_java_awt_peer_gtk_la_SOURCES) $(lib_gnu_java_awt_peer_qt_la_SOURCES) $(libgcj_la_SOURCES) $(libgcjawt_la_SOURCES) $(libgij_la_SOURCES) $(gc_analyze_SOURCES) $(gcj_dbtool_SOURCES) $(gen_from_JIS_SOURCES) $(gij_SOURCES) $(grmic_SOURCES) $(grmiregistry_SOURCES) $(jv_convert_SOURCES)
 
 srcdir = @srcdir@
 top_srcdir = @top_srcdir@
@@ -48,7 +48,7 @@
 @QT_AWT_TRUE@am__append_4 = lib-gnu-java-awt-peer-qt.la
 @NATIVE_TRUE@bin_PROGRAMS = jv-convert$(EXEEXT) gij$(EXEEXT) \
 @NATIVE_TRUE@	grmic$(EXEEXT) grmiregistry$(EXEEXT) \
-@NATIVE_TRUE@	gcj-dbtool$(EXEEXT)
+@NATIVE_TRUE@	gcj-dbtool$(EXEEXT) gc_analyze$(EXEEXT)
 @USING_GCC_TRUE@am__append_5 = $(WARNINGS)
 @USING_BOEHMGC_TRUE@am__append_6 = boehm.cc
 @USING_NOGC_TRUE@am__append_7 = nogc.cc
@@ -156,10 +156,11 @@
 	gnu/classpath/jdwp/processor.lo \
 	gnu/classpath/jdwp/transport.lo gnu/classpath/jdwp/util.lo \
 	gnu/gcj.lo gnu/gcj/convert.lo gnu/gcj/io.lo gnu/gcj/runtime.lo \
-	gnu/gcj/util.lo gnu/java/awt.lo gnu/java/awt/color.lo \
-	gnu/java/awt/image.lo gnu/java/awt/peer.lo gnu/java/io.lo \
-	gnu/java/lang.lo gnu/java/lang/reflect.lo gnu/java/locale.lo \
-	gnu/java/math.lo gnu/java/net.lo gnu/java/net/protocol/file.lo \
+	gnu/gcj/tools/gc_analyze.lo gnu/gcj/util.lo gnu/java/awt.lo \
+	gnu/java/awt/color.lo gnu/java/awt/image.lo \
+	gnu/java/awt/peer.lo gnu/java/io.lo gnu/java/lang.lo \
+	gnu/java/lang/reflect.lo gnu/java/locale.lo gnu/java/math.lo \
+	gnu/java/net.lo gnu/java/net/protocol/file.lo \
 	gnu/java/net/protocol/ftp.lo gnu/java/net/protocol/http.lo \
 	gnu/java/net/protocol/https.lo gnu/java/net/protocol/jar.lo \
 	gnu/java/nio.lo gnu/java/nio/channels.lo \
@@ -256,7 +257,7 @@
 	gnu/gcj/runtime/natSharedLibLoader.cc \
 	gnu/gcj/runtime/natSystemClassLoader.cc \
 	gnu/gcj/runtime/natStringBuffer.cc gnu/gcj/util/natDebug.cc \
-	gnu/java/lang/natMainThread.cc \
+	gnu/gcj/util/natGCInfo.cc gnu/java/lang/natMainThread.cc \
 	gnu/java/net/natPlainDatagramSocketImpl.cc \
 	gnu/java/net/natPlainSocketImpl.cc \
 	gnu/java/net/protocol/core/natCoreInputStream.cc \
@@ -297,7 +298,7 @@
 	gnu/gcj/runtime/natSharedLibLoader.lo \
 	gnu/gcj/runtime/natSystemClassLoader.lo \
 	gnu/gcj/runtime/natStringBuffer.lo gnu/gcj/util/natDebug.lo \
-	gnu/java/lang/natMainThread.lo \
+	gnu/gcj/util/natGCInfo.lo gnu/java/lang/natMainThread.lo \
 	gnu/java/net/natPlainDatagramSocketImpl.lo \
 	gnu/java/net/natPlainSocketImpl.lo \
 	gnu/java/net/protocol/core/natCoreInputStream.lo \
@@ -346,6 +347,16 @@
 libgij_la_OBJECTS = $(am_libgij_la_OBJECTS)
 binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
 PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
+am_gc_analyze_OBJECTS = gnu/gcj/tools/gc_analyze/BlockMap.$(OBJEXT) \
+	gnu/gcj/tools/gc_analyze/BytePtr.$(OBJEXT) \
+	gnu/gcj/tools/gc_analyze/ItemList.$(OBJEXT) \
+	gnu/gcj/tools/gc_analyze/MemoryAnalyze.$(OBJEXT) \
+	gnu/gcj/tools/gc_analyze/MemoryMap.$(OBJEXT) \
+	gnu/gcj/tools/gc_analyze/ObjectMap.$(OBJEXT) \
+	gnu/gcj/tools/gc_analyze/SymbolLookup.$(OBJEXT) \
+	gnu/gcj/tools/gc_analyze/SymbolTable.$(OBJEXT) \
+	gnu/gcj/tools/gc_analyze/ToolPrefix.$(OBJEXT)
+gc_analyze_OBJECTS = $(am_gc_analyze_OBJECTS)
 am_gcj_dbtool_OBJECTS = gnu/gcj/tools/gcj_dbtool/Main.$(OBJEXT) \
 	gnu/gcj/tools/gcj_dbtool/natMain.$(OBJEXT)
 gcj_dbtool_OBJECTS = $(am_gcj_dbtool_OBJECTS)
@@ -392,15 +403,17 @@
 	$(lib_gnu_java_awt_peer_gtk_la_SOURCES) \
 	$(lib_gnu_java_awt_peer_qt_la_SOURCES) $(libgcj_la_SOURCES) \
 	$(libgcjawt_la_SOURCES) $(libgij_la_SOURCES) \
-	$(gcj_dbtool_SOURCES) $(gen_from_JIS_SOURCES) $(gij_SOURCES) \
-	$(grmic_SOURCES) $(grmiregistry_SOURCES) $(jv_convert_SOURCES)
+	$(gc_analyze_SOURCES) $(gcj_dbtool_SOURCES) \
+	$(gen_from_JIS_SOURCES) $(gij_SOURCES) $(grmic_SOURCES) \
+	$(grmiregistry_SOURCES) $(jv_convert_SOURCES)
 DIST_SOURCES = $(lib_gnu_awt_xlib_la_SOURCES) \
 	$(lib_gnu_java_awt_peer_gtk_la_SOURCES) \
 	$(lib_gnu_java_awt_peer_qt_la_SOURCES) \
 	$(am__libgcj_la_SOURCES_DIST) $(libgcjawt_la_SOURCES) \
-	$(libgij_la_SOURCES) $(gcj_dbtool_SOURCES) \
-	$(am__gen_from_JIS_SOURCES_DIST) $(gij_SOURCES) \
-	$(grmic_SOURCES) $(grmiregistry_SOURCES) $(jv_convert_SOURCES)
+	$(libgij_la_SOURCES) $(gc_analyze_SOURCES) \
+	$(gcj_dbtool_SOURCES) $(am__gen_from_JIS_SOURCES_DIST) \
+	$(gij_SOURCES) $(grmic_SOURCES) $(grmiregistry_SOURCES) \
+	$(jv_convert_SOURCES)
 RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
 	html-recursive info-recursive install-data-recursive \
 	install-exec-recursive install-info-recursive \
@@ -1212,8 +1225,21 @@
 gnu/gcj/runtime/SystemClassLoader.java
 
 gnu_gcj_runtime_header_files = $(patsubst %.java,%.h,$(gnu_gcj_runtime_source_files))
+gnu_gcj_tools_gc_analyze_source_files = \
+gnu/gcj/tools/gc_analyze/BlockMap.java \
+gnu/gcj/tools/gc_analyze/BytePtr.java \
+gnu/gcj/tools/gc_analyze/ItemList.java \
+gnu/gcj/tools/gc_analyze/MemoryAnalyze.java \
+gnu/gcj/tools/gc_analyze/MemoryMap.java \
+gnu/gcj/tools/gc_analyze/ObjectMap.java \
+gnu/gcj/tools/gc_analyze/SymbolLookup.java \
+gnu/gcj/tools/gc_analyze/SymbolTable.java \
+gnu/gcj/tools/gc_analyze/ToolPrefix.java
+
+gnu_gcj_tools_gc_analyze_header_files = $(patsubst %.java,%.h,$(gnu_gcj_tools_gc_analyze_source_files))
 gnu_gcj_util_source_files = \
-gnu/gcj/util/Debug.java
+gnu/gcj/util/Debug.java \
+gnu/gcj/util/GCInfo.java
 
 gnu_gcj_util_header_files = $(patsubst %.java,%.h,$(gnu_gcj_util_source_files))
 gnu_gcj_xlib_source_files = \
@@ -6275,6 +6301,7 @@
   gnu/gcj/convert.list \
   gnu/gcj/io.list \
   gnu/gcj/runtime.list \
+  gnu/gcj/tools/gc_analyze.list \
   gnu/gcj/util.list \
   gnu/java/awt.list \
   gnu/java/awt/color.list \
@@ -6470,6 +6497,7 @@
   $(gnu_gcj_convert_header_files) \
   $(gnu_gcj_io_header_files) \
   $(gnu_gcj_runtime_header_files) \
+  $(gnu_gcj_tools_gc_analyze_header_files) \
   $(gnu_gcj_util_header_files) \
   $(gnu_java_awt_header_files) \
   $(gnu_java_awt_color_header_files) \
@@ -6717,6 +6745,23 @@
 gcj_dbtool_LINK = $(GCJLINK)
 gcj_dbtool_LDADD = -L$(here)/.libs libgcj.la
 gcj_dbtool_DEPENDENCIES = libgcj.la libgcj.spec
+gc_analyze_SOURCES = \
+gnu/gcj/tools/gc_analyze/BlockMap.java \
+gnu/gcj/tools/gc_analyze/BytePtr.java \
+gnu/gcj/tools/gc_analyze/ItemList.java \
+gnu/gcj/tools/gc_analyze/MemoryAnalyze.java \
+gnu/gcj/tools/gc_analyze/MemoryMap.java \
+gnu/gcj/tools/gc_analyze/ObjectMap.java \
+gnu/gcj/tools/gc_analyze/SymbolLookup.java \
+gnu/gcj/tools/gc_analyze/SymbolTable.java \
+gnu/gcj/tools/gc_analyze/ToolPrefix.java
+
+gc_analyze_LDFLAGS = --main=gnu.gcj.tools.gc_analyze.MemoryAnalyze \
+        -rpath $(toolexeclibdir) -shared-libgcc $(THREADLDFLAGS)
+
+gc_analyze_LINK = $(GCJLINK)
+gc_analyze_LDADD = -L$(here)/.libs libgij.la
+gc_analyze_DEPENDENCIES = libgij.la
 gij_SOURCES = 
 gij_LDFLAGS = -rpath $(toolexeclibdir) -shared-libgcc $(THREADLDFLAGS)
 gij_LINK = $(GCJLINK)
@@ -6754,6 +6799,7 @@
 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/net/natPlainDatagramSocketImpl.cc \
 gnu/java/net/natPlainSocketImpl.cc \
@@ -7100,6 +7146,8 @@
 	@: > gnu/gcj/util/$(DEPDIR)/$(am__dirstamp)
 gnu/gcj/util/natDebug.lo: gnu/gcj/util/$(am__dirstamp) \
 	gnu/gcj/util/$(DEPDIR)/$(am__dirstamp)
+gnu/gcj/util/natGCInfo.lo: gnu/gcj/util/$(am__dirstamp) \
+	gnu/gcj/util/$(DEPDIR)/$(am__dirstamp)
 gnu/java/lang/$(am__dirstamp):
 	@$(mkdir_p) gnu/java/lang
 	@: > gnu/java/lang/$(am__dirstamp)
@@ -7330,6 +7378,42 @@
 	  echo " rm -f $$p $$f"; \
 	  rm -f $$p $$f ; \
 	done
+gnu/gcj/tools/gc_analyze/$(am__dirstamp):
+	@$(mkdir_p) gnu/gcj/tools/gc_analyze
+	@: > gnu/gcj/tools/gc_analyze/$(am__dirstamp)
+gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp):
+	@$(mkdir_p) gnu/gcj/tools/gc_analyze/$(DEPDIR)
+	@: > gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+gnu/gcj/tools/gc_analyze/BlockMap.$(OBJEXT):  \
+	gnu/gcj/tools/gc_analyze/$(am__dirstamp) \
+	gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+gnu/gcj/tools/gc_analyze/BytePtr.$(OBJEXT):  \
+	gnu/gcj/tools/gc_analyze/$(am__dirstamp) \
+	gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+gnu/gcj/tools/gc_analyze/ItemList.$(OBJEXT):  \
+	gnu/gcj/tools/gc_analyze/$(am__dirstamp) \
+	gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+gnu/gcj/tools/gc_analyze/MemoryAnalyze.$(OBJEXT):  \
+	gnu/gcj/tools/gc_analyze/$(am__dirstamp) \
+	gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+gnu/gcj/tools/gc_analyze/MemoryMap.$(OBJEXT):  \
+	gnu/gcj/tools/gc_analyze/$(am__dirstamp) \
+	gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+gnu/gcj/tools/gc_analyze/ObjectMap.$(OBJEXT):  \
+	gnu/gcj/tools/gc_analyze/$(am__dirstamp) \
+	gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+gnu/gcj/tools/gc_analyze/SymbolLookup.$(OBJEXT):  \
+	gnu/gcj/tools/gc_analyze/$(am__dirstamp) \
+	gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+gnu/gcj/tools/gc_analyze/SymbolTable.$(OBJEXT):  \
+	gnu/gcj/tools/gc_analyze/$(am__dirstamp) \
+	gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+gnu/gcj/tools/gc_analyze/ToolPrefix.$(OBJEXT):  \
+	gnu/gcj/tools/gc_analyze/$(am__dirstamp) \
+	gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+gc_analyze$(EXEEXT): $(gc_analyze_OBJECTS) $(gc_analyze_DEPENDENCIES) 
+	@rm -f gc_analyze$(EXEEXT)
+	$(gc_analyze_LINK) $(gc_analyze_LDFLAGS) $(gc_analyze_OBJECTS) $(gc_analyze_LDADD) $(LIBS)
 gnu/gcj/tools/gcj_dbtool/$(am__dirstamp):
 	@$(mkdir_p) gnu/gcj/tools/gcj_dbtool
 	@: > gnu/gcj/tools/gcj_dbtool/$(am__dirstamp)
@@ -7423,10 +7507,21 @@
 	-rm -f gnu/gcj/runtime/natStringBuffer.lo
 	-rm -f gnu/gcj/runtime/natSystemClassLoader.$(OBJEXT)
 	-rm -f gnu/gcj/runtime/natSystemClassLoader.lo
+	-rm -f gnu/gcj/tools/gc_analyze/BlockMap.$(OBJEXT)
+	-rm -f gnu/gcj/tools/gc_analyze/BytePtr.$(OBJEXT)
+	-rm -f gnu/gcj/tools/gc_analyze/ItemList.$(OBJEXT)
+	-rm -f gnu/gcj/tools/gc_analyze/MemoryAnalyze.$(OBJEXT)
+	-rm -f gnu/gcj/tools/gc_analyze/MemoryMap.$(OBJEXT)
+	-rm -f gnu/gcj/tools/gc_analyze/ObjectMap.$(OBJEXT)
+	-rm -f gnu/gcj/tools/gc_analyze/SymbolLookup.$(OBJEXT)
+	-rm -f gnu/gcj/tools/gc_analyze/SymbolTable.$(OBJEXT)
+	-rm -f gnu/gcj/tools/gc_analyze/ToolPrefix.$(OBJEXT)
 	-rm -f gnu/gcj/tools/gcj_dbtool/Main.$(OBJEXT)
 	-rm -f gnu/gcj/tools/gcj_dbtool/natMain.$(OBJEXT)
 	-rm -f gnu/gcj/util/natDebug.$(OBJEXT)
 	-rm -f gnu/gcj/util/natDebug.lo
+	-rm -f gnu/gcj/util/natGCInfo.$(OBJEXT)
+	-rm -f gnu/gcj/util/natGCInfo.lo
 	-rm -f gnu/gcj/xlib/lib_gnu_awt_xlib_la-natClip.$(OBJEXT)
 	-rm -f gnu/gcj/xlib/lib_gnu_awt_xlib_la-natClip.lo
 	-rm -f gnu/gcj/xlib/lib_gnu_awt_xlib_la-natColormap.$(OBJEXT)
@@ -7597,9 +7692,19 @@
 @AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/runtime/$(DEPDIR)/natSharedLibLoader.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/runtime/$(DEPDIR)/natStringBuffer.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/runtime/$(DEPDIR)/natSystemClassLoader.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gc_analyze/$(DEPDIR)/BlockMap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gc_analyze/$(DEPDIR)/BytePtr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gc_analyze/$(DEPDIR)/ItemList.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gc_analyze/$(DEPDIR)/MemoryAnalyze.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gc_analyze/$(DEPDIR)/MemoryMap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gc_analyze/$(DEPDIR)/ObjectMap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gc_analyze/$(DEPDIR)/SymbolLookup.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gc_analyze/$(DEPDIR)/SymbolTable.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gc_analyze/$(DEPDIR)/ToolPrefix.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gcj_dbtool/$(DEPDIR)/Main.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/tools/gcj_dbtool/$(DEPDIR)/natMain.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/util/$(DEPDIR)/natDebug.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/util/$(DEPDIR)/natGCInfo.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/xlib/$(DEPDIR)/lib_gnu_awt_xlib_la-natClip.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/xlib/$(DEPDIR)/lib_gnu_awt_xlib_la-natColormap.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@gnu/gcj/xlib/$(DEPDIR)/lib_gnu_awt_xlib_la-natDisplay.Plo@am__quote@
@@ -8290,6 +8395,8 @@
 	-rm -f gnu/gcj/io/$(am__dirstamp)
 	-rm -f gnu/gcj/runtime/$(DEPDIR)/$(am__dirstamp)
 	-rm -f gnu/gcj/runtime/$(am__dirstamp)
+	-rm -f gnu/gcj/tools/gc_analyze/$(DEPDIR)/$(am__dirstamp)
+	-rm -f gnu/gcj/tools/gc_analyze/$(am__dirstamp)
 	-rm -f gnu/gcj/tools/gcj_dbtool/$(DEPDIR)/$(am__dirstamp)
 	-rm -f gnu/gcj/tools/gcj_dbtool/$(am__dirstamp)
 	-rm -f gnu/gcj/util/$(DEPDIR)/$(am__dirstamp)
@@ -8341,7 +8448,7 @@
 
 distclean: distclean-recursive
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-	-rm -rf ./$(DEPDIR) classpath/native/jawt/$(DEPDIR) gnu/classpath/$(DEPDIR) gnu/gcj/$(DEPDIR) gnu/gcj/convert/$(DEPDIR) gnu/gcj/io/$(DEPDIR) gnu/gcj/runtime/$(DEPDIR) gnu/gcj/tools/gcj_dbtool/$(DEPDIR) gnu/gcj/util/$(DEPDIR) gnu/gcj/xlib/$(DEPDIR) gnu/java/lang/$(DEPDIR) gnu/java/net/$(DEPDIR) gnu/java/net/protocol/core/$(DEPDIR) gnu/java/nio/$(DEPDIR) gnu/java/nio/channels/$(DEPDIR) java/io/$(DEPDIR) java/lang/$(DEPDIR) java/lang/ref/$(DEPDIR) java/lang/reflect/$(DEPDIR) java/net/$(DEPDIR) java/nio/$(DEPDIR) java/nio/channels/$(DEPDIR) java/text/$(DEPDIR) java/util/$(DEPDIR) java/util/logging/$(DEPDIR) java/util/zip/$(DEPDIR)
+	-rm -rf ./$(DEPDIR) classpath/native/jawt/$(DEPDIR) gnu/classpath/$(DEPDIR) gnu/gcj/$(DEPDIR) gnu/gcj/convert/$(DEPDIR) gnu/gcj/io/$(DEPDIR) gnu/gcj/runtime/$(DEPDIR) gnu/gcj/tools/gc_analyze/$(DEPDIR) gnu/gcj/tools/gcj_dbtool/$(DEPDIR) gnu/gcj/util/$(DEPDIR) gnu/gcj/xlib/$(DEPDIR) gnu/java/lang/$(DEPDIR) gnu/java/net/$(DEPDIR) gnu/java/net/protocol/core/$(DEPDIR) gnu/java/nio/$(DEPDIR) gnu/java/nio/channels/$(DEPDIR) java/io/$(DEPDIR) java/lang/$(DEPDIR) java/lang/ref/$(DEPDIR) java/lang/reflect/$(DEPDIR) java/net/$(DEPDIR) java/nio/$(DEPDIR) java/nio/channels/$(DEPDIR) java/text/$(DEPDIR) java/util/$(DEPDIR) java/util/logging/$(DEPDIR) java/util/zip/$(DEPDIR)
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-libtool distclean-local distclean-tags
@@ -8372,7 +8479,7 @@
 maintainer-clean: maintainer-clean-recursive
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
 	-rm -rf $(top_srcdir)/autom4te.cache
-	-rm -rf ./$(DEPDIR) classpath/native/jawt/$(DEPDIR) gnu/classpath/$(DEPDIR) gnu/gcj/$(DEPDIR) gnu/gcj/convert/$(DEPDIR) gnu/gcj/io/$(DEPDIR) gnu/gcj/runtime/$(DEPDIR) gnu/gcj/tools/gcj_dbtool/$(DEPDIR) gnu/gcj/util/$(DEPDIR) gnu/gcj/xlib/$(DEPDIR) gnu/java/lang/$(DEPDIR) gnu/java/net/$(DEPDIR) gnu/java/net/protocol/core/$(DEPDIR) gnu/java/nio/$(DEPDIR) gnu/java/nio/channels/$(DEPDIR) java/io/$(DEPDIR) java/lang/$(DEPDIR) java/lang/ref/$(DEPDIR) java/lang/reflect/$(DEPDIR) java/net/$(DEPDIR) java/nio/$(DEPDIR) java/nio/channels/$(DEPDIR) java/text/$(DEPDIR) java/util/$(DEPDIR) java/util/logging/$(DEPDIR) java/util/zip/$(DEPDIR)
+	-rm -rf ./$(DEPDIR) classpath/native/jawt/$(DEPDIR) gnu/classpath/$(DEPDIR) gnu/gcj/$(DEPDIR) gnu/gcj/convert/$(DEPDIR) gnu/gcj/io/$(DEPDIR) gnu/gcj/runtime/$(DEPDIR) gnu/gcj/tools/gc_analyze/$(DEPDIR) gnu/gcj/tools/gcj_dbtool/$(DEPDIR) gnu/gcj/util/$(DEPDIR) gnu/gcj/xlib/$(DEPDIR) gnu/java/lang/$(DEPDIR) gnu/java/net/$(DEPDIR) gnu/java/net/protocol/core/$(DEPDIR) gnu/java/nio/$(DEPDIR) gnu/java/nio/channels/$(DEPDIR) java/io/$(DEPDIR) java/lang/$(DEPDIR) java/lang/ref/$(DEPDIR) java/lang/reflect/$(DEPDIR) java/net/$(DEPDIR) java/nio/$(DEPDIR) java/nio/channels/$(DEPDIR) java/text/$(DEPDIR) java/util/$(DEPDIR) java/util/logging/$(DEPDIR) java/util/zip/$(DEPDIR)
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -8630,6 +8737,16 @@
 
 -include gnu/gcj/runtime.deps
 
+gnu/gcj/tools/gc_analyze.list: $(gnu_gcj_tools_gc_analyze_source_files)
+	@$(mkinstalldirs) $(dir $@)
+	@for file in $(gnu_gcj_tools_gc_analyze_source_files); do \
+	  if test -f $(srcdir)/$$file; then \
+	    echo $(srcdir)/$$file; \
+	  else echo $$file; fi; \
+	done > gnu/gcj/tools/gc_analyze.list
+
+-include gnu/gcj/tools/gc_analyze.deps
+
 gnu/gcj/util.list: $(gnu_gcj_util_source_files)
 	@$(mkinstalldirs) $(dir $@)
 	@for file in $(gnu_gcj_util_source_files); do \
Index: configure.ac
===================================================================
--- configure.ac	(revision 113530)
+++ configure.ac	(working copy)
@@ -907,10 +907,14 @@
    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: sources.am
===================================================================
--- sources.am	(revision 113530)
+++ sources.am	(working copy)
@@ -579,8 +579,33 @@
 -include gnu/gcj/runtime.deps
 
 
+gnu_gcj_tools_gc_analyze_source_files = \
+gnu/gcj/tools/gc_analyze/BlockMap.java \
+gnu/gcj/tools/gc_analyze/BytePtr.java \
+gnu/gcj/tools/gc_analyze/ItemList.java \
+gnu/gcj/tools/gc_analyze/MemoryAnalyze.java \
+gnu/gcj/tools/gc_analyze/MemoryMap.java \
+gnu/gcj/tools/gc_analyze/ObjectMap.java \
+gnu/gcj/tools/gc_analyze/SymbolLookup.java \
+gnu/gcj/tools/gc_analyze/SymbolTable.java \
+gnu/gcj/tools/gc_analyze/ToolPrefix.java
+
+gnu_gcj_tools_gc_analyze_header_files = $(patsubst %.java,%.h,$(gnu_gcj_tools_gc_analyze_source_files))
+
+gnu/gcj/tools/gc_analyze.list: $(gnu_gcj_tools_gc_analyze_source_files)
+	@$(mkinstalldirs) $(dir $@)
+	@for file in $(gnu_gcj_tools_gc_analyze_source_files); do \
+	  if test -f $(srcdir)/$$file; then \
+	    echo $(srcdir)/$$file; \
+	  else echo $$file; fi; \
+	done > gnu/gcj/tools/gc_analyze.list
+
+-include gnu/gcj/tools/gc_analyze.deps
+
+
 gnu_gcj_util_source_files = \
-gnu/gcj/util/Debug.java
+gnu/gcj/util/Debug.java \
+gnu/gcj/util/GCInfo.java
 
 gnu_gcj_util_header_files = $(patsubst %.java,%.h,$(gnu_gcj_util_source_files))
 
@@ -7849,6 +7874,7 @@
   gnu/gcj/convert.list \
   gnu/gcj/io.list \
   gnu/gcj/runtime.list \
+  gnu/gcj/tools/gc_analyze.list \
   gnu/gcj/util.list \
   gnu/java/awt.list \
   gnu/java/awt/color.list \
@@ -8044,6 +8070,7 @@
   $(gnu_gcj_convert_header_files) \
   $(gnu_gcj_io_header_files) \
   $(gnu_gcj_runtime_header_files) \
+  $(gnu_gcj_tools_gc_analyze_header_files) \
   $(gnu_gcj_util_header_files) \
   $(gnu_java_awt_header_files) \
   $(gnu_java_awt_color_header_files) \
Index: include/config.h.in
===================================================================
--- include/config.h.in	(revision 113530)
+++ include/config.h.in	(working copy)
@@ -211,6 +211,9 @@
 /* Define if you have /proc/self/exe */
 #undef HAVE_PROC_SELF_EXE
 
+/* Define if you have /proc/self/maps */
+#undef HAVE_PROC_SELF_MAPS
+
 /* Define if using POSIX threads that have the mutexattr functions. */
 #undef HAVE_PTHREAD_MUTEXATTR_INIT
 
@@ -351,6 +354,9 @@
 /* Define to 1 if you have the file `AC_File'. */
 #undef HAVE__PROC_SELF_EXE
 
+/* Define to 1 if you have the file `AC_File'. */
+#undef HAVE__PROC_SELF_MAPS
+
 /* Define as const if the declaration of iconv() needs const. */
 #undef ICONV_CONST
 
Index: Makefile.am
===================================================================
--- Makefile.am	(revision 113530)
+++ Makefile.am	(working copy)
@@ -74,7 +74,7 @@
 
 ## For now, only on native systems.  FIXME.
 if NATIVE
-bin_PROGRAMS = jv-convert gij grmic grmiregistry gcj-dbtool
+bin_PROGRAMS = jv-convert gij grmic grmiregistry gcj-dbtool gc_analyze
 
 ## It is convenient to actually build and install the default database
 ## when gcj-dbtool is available.
@@ -731,6 +731,29 @@
 ## linking this program.
 gcj_dbtool_DEPENDENCIES = libgcj.la libgcj.spec
 
+gc_analyze_SOURCES = \
+gnu/gcj/tools/gc_analyze/BlockMap.java \
+gnu/gcj/tools/gc_analyze/BytePtr.java \
+gnu/gcj/tools/gc_analyze/ItemList.java \
+gnu/gcj/tools/gc_analyze/MemoryAnalyze.java \
+gnu/gcj/tools/gc_analyze/MemoryMap.java \
+gnu/gcj/tools/gc_analyze/ObjectMap.java \
+gnu/gcj/tools/gc_analyze/SymbolLookup.java \
+gnu/gcj/tools/gc_analyze/SymbolTable.java \
+gnu/gcj/tools/gc_analyze/ToolPrefix.java
+
+## 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 libgij.la
+## Depend on the spec file to make sure it is up to date before
+## linking this program.
+gc_analyze_DEPENDENCIES = libgij.la
+
 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
@@ -786,6 +809,7 @@
 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/net/natPlainDatagramSocketImpl.cc \
 gnu/java/net/natPlainSocketImpl.cc \

New files follow...

--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/util/GCInfo.java	2006-05-04 10:18:31.000000000 -0700
@@ -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);
+  
+}
--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/util/natGCInfo.cc	2006-05-04 10:31:33.000000000 -0700
@@ -0,0 +1,461 @@
+/* natGCInfo.cc -- Native portion of 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.  */
+
+
+#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
+
--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/tools/gc_analyze/BlockMap.java	2006-05-04 10:15:42.000000000 -0700
@@ -0,0 +1,225 @@
+/* BlockMap.java -- Container for information on GC maintained memory blocks.
+   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.tools.gc_analyze;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+class BlockMap
+{
+  static final int HBLKSIZE = 4096;
+
+  class SizeKind implements Comparable
+  {
+    int size;
+    int kind;
+
+    public SizeKind(int size, int kind)
+    {
+      this.size = size;
+      this.kind = kind;
+    }
+
+    public int compareTo(Object b)
+    {
+      SizeKind ska = this;
+      SizeKind skb = (SizeKind)b;
+      if (ska.size != skb.size)
+        return ska.size - skb.size;
+      return ska.kind - skb.kind;
+    }
+  }
+
+  class PtrMarks
+  {
+    int ptr;
+    int marks;
+    public PtrMarks(int ptr, int marks)
+    {
+      this.ptr = ptr;
+      this.marks = marks;
+    }
+  }
+
+  private TreeMap map = new TreeMap();
+
+  public Iterator iterator()
+  {
+    return map.entrySet().iterator();
+  }
+
+  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(",");
+                int 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 = Integer.parseInt(last.substring(2), 16);
+                        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 m = (ArrayList)map.get(sk);
+                if (m == null) {
+                  m = new ArrayList();
+                  map.put(sk, m);
+                }
+                PtrMarks pm = new PtrMarks(ptr, marks);
+                m.add(pm);
+              } // inner loop
+          } // started inner loop
+      } // outer loop - finding begin
+  } // memoryMap
+
+  public void dump()
+  {
+    System.out.println();
+    System.out.println();
+    System.out.println("*** Used Blocks ***\n");
+    System.out.println();
+    System.out.println("  Size     Kind            Blocks     Used       Free       Wasted");
+    System.out.println("-------  -------------    ------- ---------- ----------    -------");
+
+    int total_blocks = 0, total_used = 0, total_free = 0, total_wasted = 0;
+
+    for (Iterator it=map.entrySet().iterator(); it.hasNext(); )
+      {
+        Map.Entry me = (Map.Entry)it.next();
+        SizeKind sk = (SizeKind)me.getKey();
+        //
+        System.out.println(MemoryAnalyze.format(sk.size,7) + "  "
+                           + MemoryAnalyze.KindToName(sk.kind));
+
+        int sub_blocks = 0, sub_used = 0, sub_free = 0, sub_wasted = 0;
+        int sub_count = 0;
+
+        ArrayList v = (ArrayList)me.getValue();
+        for (Iterator it2=v.iterator(); it2.hasNext(); )
+          {
+            PtrMarks pm = (PtrMarks)it2.next();
+            //
+            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);
+          }
+        //System.out.println();
+      } // 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));
+  }
+}
--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/tools/gc_analyze/BytePtr.java	2006-05-04 10:15:42.000000000 -0700
@@ -0,0 +1,110 @@
+/* BytePtr.java -- Container for bytes from a memory image.
+   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.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 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());
+  }
+}
--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/tools/gc_analyze/ItemList.java	2006-05-04 10:15:42.000000000 -0700
@@ -0,0 +1,73 @@
+/* ItemList.java -- Maps all objects keyed by their addresses.
+   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.tools.gc_analyze;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+class ItemList
+{
+  public ItemList()
+  {
+  }
+
+  private TreeMap map;
+
+  public void add(ObjectMap.ObjectItem item)
+  {
+    if (map == null)
+      map = new TreeMap();
+    Long x = new Long(item.klass);
+    HashMap list = (HashMap)map.get(x);;
+    if (list == null)
+      {
+        list = new HashMap();
+        map.put(x, list);
+      }
+    Integer count = (Integer)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 (Iterator it = map.entrySet().iterator(); it.hasNext(); )
+      {
+        Map.Entry me = (Map.Entry)it.next();
+        HashMap list = (HashMap)me.getValue();
+        boolean first = true;
+        for (Iterator it2 = list.entrySet().iterator(); it2.hasNext(); )
+          {
+            Map.Entry me2 = (Map.Entry)it2.next();
+            ObjectMap.ObjectItem item = (ObjectMap.ObjectItem)me2.getKey();
+            Integer count = (Integer)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();
+          }
+      }
+  }
+}
--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/tools/gc_analyze/MemoryAnalyze.java	2006-05-04 10:15:42.000000000 -0700
@@ -0,0 +1,438 @@
+/* MemoryAnalyze.java -- Analyzes a libgcj heap dump.
+   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.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 list,
+                            Comparator comparator)
+  {
+    System.out.println("*** " + description + " ***");
+    System.out.println();
+    System.out.println(                "  Total Size       Count       Size    Description");
+    System.out.println(                "--------------     -----    --------   -----------------------------------");
+    Collections.sort(list, comparator);
+    for (Iterator it = list.iterator(); it.hasNext(); )
+      {
+        String v = (String)it.next();
+        System.out.println(stripend(v));
+      }
+    System.out.println(                "--------------     -----    --------   -----------------------------------");
+    System.out.println(format(total_space, 14));
+    System.out.println();
+    System.out.println();
+  }
+
+  private static String stripend(String s)
+  {
+    int n = s.lastIndexOf(" /");
+    if (n > 0)
+      return s.substring(0,n);
+    return s;
+  }
+
+  static void usage()
+  {
+    System.out.println("usage: MemoryAnalyze [-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
+  {
+    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(Object o1, Object o2)
+    {
+      String s1 = (String)o1;
+      if (end == 0)
+        s1 = s1.substring(begin);
+      else
+        s1 = s1.substring(begin, end);
+      String s2 = (String)o2;
+      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 directory = ".";
+        String filename = null;
+        for (int i=0; i<args.length; i++) {
+          if (args[i].equals("-h"))
+            usage();
+          if (args[i].equals("-d"))
+            {
+              directory = 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,
+                                               directory, 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 (Iterator it = objectMap.iterator(); it.hasNext(); )
+          {
+            Map.Entry me = (Map.Entry)it.next();
+            //                int ptr = ((Integer)me.getKey()).intValue();
+            ObjectMap.ObjectItem item = (ObjectMap.ObjectItem)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.getSymbol(vtable - 8);
+                    if (sym != null)
+                      {
+                        item.typeName = SymbolTable.demangleVTName(sym);
+                      }
+                    else if (vtable != 0)
+                      {
+                        // get klass from vtable
+                        p = lookup.getBytePtr(vtable, 4);
+                        if (p!=null) {
+                          int klass = p.getInt(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);
+                int count = p.getInt(3);
+                int hash = p.getInt(4);
+                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)
+                  {
+                    //System.out.println("string=["+sb+"] old="+hash+" new="+
+                    //                   newhash);
+                    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
+              System.out.println();
+          }
+
+
+        HashMap m_map = new HashMap();
+        for (Iterator it = objectMap.iterator(); it.hasNext(); )
+          {
+            Map.Entry me = (Map.Entry)it.next();
+            //                int ptr = ((Integer)me.getKey()).intValue();
+            ObjectMap.ObjectItem item = (ObjectMap.ObjectItem)me.getValue();
+            String name = getSymbolPretty(lookup, item, true);
+            Info info = (Info)m_map.get(name);
+            if (info == null)
+              {
+                info = new Info();
+                info.count = 0;
+                info.size = item.size;
+                m_map.put(name, info);
+              }
+            info.count++;
+            total_space += item.size;
+          }
+
+        ArrayList list = new ArrayList();
+        for (Iterator it = m_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());
+            // System.out.println(info.m_count + " * " + name);
+          }
+
+        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 (Iterator it = objectMap.iterator(); it.hasNext(); )
+              {
+                Map.Entry me = (Map.Entry)it.next();
+                long ptr = ((Long)me.getKey()).longValue();
+                ObjectMap.ObjectItem item
+                  = (ObjectMap.ObjectItem)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/4; i++)
+                    {
+                      int maybe_ptr = p.getInt(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 (Iterator it = objectMap.iterator(); it.hasNext(); ) {
+              Map.Entry me = (Map.Entry)it.next();
+              long ptr = ((Long)me.getKey()).longValue();
+              ObjectMap.ObjectItem item = (ObjectMap.ObjectItem)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)
+      {
+        name = KindToName(item.kind);
+      }
+    if (item.kind==6)
+      name += "[" + format(item.data, 0) + "]";
+    if (bsize)
+      name = name + " / " + format(item.size, 7);
+    return name;
+  }
+}
--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/tools/gc_analyze/MemoryMap.java	2006-05-04 10:15:42.000000000 -0700
@@ -0,0 +1,344 @@
+/* MemoryMap.java -- Maps address ranges to their data.
+   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.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.Iterator;
+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 (new)
+
+*/
+class MemoryMap
+{
+  static class RangeComparator implements Comparator
+  {
+    public int compare(Object arg0, Object arg1)
+    {
+      return compare((Range)arg0, (Range)arg1);
+    }
+    
+    public static 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;
+    }
+  }
+
+  // String filename -> Range
+  TreeSet map = new TreeSet(new RangeComparator());
+  HashMap symbolTables = new HashMap();
+  String prefix;
+  ByteOrder byteOrder;
+  int wordSize;
+
+  public MemoryMap(BufferedReader reader,
+                   String prefix,
+                   String rawFileName) throws IOException
+  {
+    this.prefix = prefix;
+
+    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;
+                  }
+                long address = Long.parseLong(s.substring(0,8), 16);
+                long address2 = Long.parseLong(s.substring(9,17), 16);
+                long offset;
+                try
+                  {
+                    offset = Long.parseLong(s.substring(23, 23+8), 16);
+                  }
+                catch (Exception e)
+                  {
+                    offset = 0;
+                  }
+                int end = s.indexOf('/');
+
+                if (end > 0)
+                  {
+                    String file = s.substring(end);
+                    if (file.startsWith("/dev/"))
+                      {
+                        // System.out.println("skipping: "+file);
+                        continue;
+                      }
+                    Range r = new Range(address, address2, file, offset);
+                    if (offset == 0)
+                      {
+                        // Read the file's symbol table
+                        try {
+                          File f = 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 (Iterator it=map.iterator(); it.hasNext(); )
+      {
+        Range r = (Range)it.next();
+
+        System.out.println(Long.toHexString(r.begin) + "-"
+                           + Long.toHexString(r.end) + " -> "
+                           + r.filename + " offset "
+                           + Long.toHexString(r.offset));
+      }
+  }
+
+  Range getRange(long addr)
+  {
+    Range r = new Range();
+    r.begin = addr;
+    SortedSet t = map.tailSet(r);
+    if (t.isEmpty())
+      return null;
+    Range c = (Range)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;
+  }
+
+  File fileForName(String filename)
+  {
+    File f = new File(prefix + 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;
+  }
+  
+  
+  // returns bytearray which includes given address
+  BytePtr getBytePtr(long addr, int length) throws IOException
+  {
+    Range r = getRange(addr);
+    
+    if (null == r)
+      return null;
+
+    File f = 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 = (SymbolTable)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;
+  }
+}
--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/tools/gc_analyze/ObjectMap.java	2006-05-04 10:15:42.000000000 -0700
@@ -0,0 +1,145 @@
+/* ObjectMap.java -- Contains a map of all objects keyed by their addresses.
+   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.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
+{
+
+  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 m_reference; // object at m_reference points to this
+
+    ItemList points_to = new ItemList();
+    ItemList pointed_by = new ItemList();
+
+    // for persistance
+    int m_offset; // address when reloaded (can't be zero -- special value)
+  }
+
+  private TreeMap map = new TreeMap();
+
+  public Iterator iterator()
+  {
+    return map.entrySet().iterator();
+  }
+
+  public ObjectItem get(long ptr)
+  {
+    ObjectItem item = (ObjectItem)map.get(new Long(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 = Long.parseLong(last.substring(2),16);
+                      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  = Long.parseLong(last.substring(2),16);
+                      else
+                	item.klass  = Integer.parseInt(last,16);
+                      break;
+                    case 5:
+                      try
+                        {
+                          item.data =
+                            Integer.parseInt(last.substring(2), 16);
+                        }
+                      catch (Exception e)
+                        {
+                          // System.out.println("last = "+last);
+                          item.data = 0;
+                        }
+                      break;
+                    }
+                }
+              item.ptr = ptr;
+              map.put(new Long(ptr), item);
+            } // inner loop
+        } // started inner loop
+      } // outer loop - finding begin
+    for (Iterator it= this.iterator(); it.hasNext(); )
+      {
+        Map.Entry me = (Map.Entry)it.next();
+        //            int ptr = ((Integer)me.getKey()).intValue();
+        ObjectItem item = (ObjectItem)me.getValue();
+        if (item.data != 0) {
+          // see if data is a pointer to a block
+          ObjectItem referenced =
+            (ObjectItem)map.get(new Long(item.data));
+          if (referenced != null)
+            {
+              referenced.m_reference = item;
+            }
+        }
+      }
+  } // memoryMap
+
+  public void dump()
+  {
+    for (Iterator it = iterator(); it.hasNext(); )
+      {
+        Map.Entry me = (Map.Entry)it.next();
+        int ptr = ((Integer)me.getKey()).intValue();
+        ObjectItem item = (ObjectItem)me.getValue();
+        System.out.println("ptr = " + Integer.toHexString(ptr)
+                           + ", size = " + item.size
+                           + ", klass = " + Long.toHexString(item.klass)
+                           + ", kind = " + item.kind
+                           + ", data = " + item.data);
+      }
+  }
+}
--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/tools/gc_analyze/SymbolLookup.java	2006-05-04 10:15:42.000000000 -0700
@@ -0,0 +1,110 @@
+/* SymbolLookup.java -- Finds class names by analyzing memory.
+   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.tools.gc_analyze;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+class SymbolLookup
+{
+  MemoryMap memoryMap;
+
+  public SymbolLookup(BufferedReader reader,
+                      String prefix,
+                      String rawFileName)
+    throws IOException
+  {
+    memoryMap = new MemoryMap(reader, prefix, rawFileName);
+  }
+
+  public String decodeUTF8(long address) throws IOException
+  {
+    if (address == 0)
+      return null;
+    
+    BytePtr utf8 = memoryMap.getBytePtr(address, 64);
+    
+    if (utf8 == null)
+      return null;
+    
+    int len = utf8.getShort(1);
+    
+    if (len <= 0)
+      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
+      }
+    
+    return sb.toString();
+  }
+  
+  /**
+   * 
+   * @param address
+   * @return
+   * @throws IOException
+   */
+  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);
+  }
+}
--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/tools/gc_analyze/SymbolTable.java	2006-05-04 10:15:42.000000000 -0700
@@ -0,0 +1,213 @@
+/* SymbolTable.java -- Maintains a mapping of addresses to names.
+   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.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.Iterator;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+class SymbolTable
+{
+  // Long address->String name
+  private HashMap map = new HashMap();
+
+  // Reverse
+  // String name -> Long address
+  // used for RelocateImage
+  private HashMap reverse = new HashMap();
+  
+  long loadAddr;
+  long relocation;
+
+  static Matcher interestingSymbol =
+    Pattern.compile("^([0-9a-fA-F]+)\\s+\\S+\\s+(_Z\\S+)").matcher("");
+  static Matcher readelfLoadMatcher =
+    Pattern.compile("^\\s+LOAD\\s+(\\S+)\\s+(\\S+)\\s.*").matcher("");
+ 
+  public SymbolTable(String filename) throws IOException {
+
+    Process p = Runtime.getRuntime().exec(ToolPrefix.toolPrefix
+                                          + "nm " + filename);
+    InputStream es = p.getErrorStream();
+    InputStream is = p.getInputStream();
+
+    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+    int count = 0;
+
+    String line;
+    while ((line = reader.readLine()) != null)
+      {
+        interestingSymbol.reset(line);
+        if (interestingSymbol.matches())
+          {
+            try
+              {
+                String name = interestingSymbol.group(2);
+                String addr = interestingSymbol.group(1);
+                if (name.startsWith("_ZTVN") || name.endsWith("6class$E")) {
+                  long address = Long.parseLong(addr, 16);
+                  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 (Iterator it = map.entrySet().iterator(); it.hasNext(); )
+      {
+        Map.Entry me = (Map.Entry)it.next();
+        long address = ((Long)me.getKey()).intValue();
+        String symbol = (String)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)));
+          }
+      }
+  }
+  /*
+    public int addressOf(String symbol)
+    {
+    for (Iterator it = m_map.entrySet().iterator(); it.hasNext(); ) {
+    Map.Entry me = (Map.Entry)it.next();
+    int address = ((Integer)me.getKey()).intValue();
+    if (getSymbol(address).compareTo(symbol) == 0)
+    return address;
+    }
+    return 0;
+    }
+  */
+
+  
+  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 = (String)map.get(new Long(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 = (Long)reverse.get(symbol);
+    if (address == null)
+      return -1;
+    return address.longValue();
+  }
+}
--- /dev/null	2006-04-22 03:37:59.501056688 -0700
+++ gnu/gcj/tools/gc_analyze/ToolPrefix.java	2006-05-04 10:15:42.000000000 -0700
@@ -0,0 +1,15 @@
+/* ToolPrefix.java -- Container of the toolPrefix String.
+   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.tools.gc_analyze;
+
+class ToolPrefix
+{
+  static String toolPrefix = "";
+}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2006-05-04 20:46 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-05-04 20:46 Patch: RFC/RFT heap dump and analyze David Daney

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).