summaryrefslogtreecommitdiff
path: root/libjava/gnu/gcj/util
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/gnu/gcj/util')
-rw-r--r--libjava/gnu/gcj/util/Debug.h57
-rw-r--r--libjava/gnu/gcj/util/Debug.java226
-rw-r--r--libjava/gnu/gcj/util/GCInfo.h45
-rw-r--r--libjava/gnu/gcj/util/GCInfo.java79
-rw-r--r--libjava/gnu/gcj/util/UtilPermission.h32
-rw-r--r--libjava/gnu/gcj/util/UtilPermission.java20
-rw-r--r--libjava/gnu/gcj/util/natDebug.cc118
-rw-r--r--libjava/gnu/gcj/util/natGCInfo.cc458
8 files changed, 1035 insertions, 0 deletions
diff --git a/libjava/gnu/gcj/util/Debug.h b/libjava/gnu/gcj/util/Debug.h
new file mode 100644
index 000000000..3ad59ef6d
--- /dev/null
+++ b/libjava/gnu/gcj/util/Debug.h
@@ -0,0 +1,57 @@
+
+// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*-
+
+#ifndef __gnu_gcj_util_Debug__
+#define __gnu_gcj_util_Debug__
+
+#pragma interface
+
+#include <java/lang/Object.h>
+#include <gcj/array.h>
+
+extern "Java"
+{
+ namespace gnu
+ {
+ namespace gcj
+ {
+ namespace util
+ {
+ class Debug;
+ }
+ }
+ }
+}
+
+class gnu::gcj::util::Debug : public ::java::lang::Object
+{
+
+public: // actually package-private
+ Debug(::java::io::PrintStream *, jint, jint, jboolean);
+ Debug(::java::io::PrintStream *);
+ Debug(jint, jboolean);
+ Debug(jint);
+ Debug();
+private:
+ void indent();
+ static JArray< ::java::lang::reflect::Field * > * getDeclaredFields(::java::lang::Class *);
+ static ::java::lang::Object * getField(::java::lang::Object *, ::java::lang::reflect::Field *);
+ static jlong getAddr(::java::lang::Object *);
+ JArray< ::java::lang::reflect::Field * > * internalGetFields(::java::lang::Class *);
+ static ::java::lang::Class * getItsClass(::java::lang::Object *);
+ void print(::java::lang::Object *);
+ void print0(::java::lang::Object *, ::java::lang::Class *);
+public:
+ virtual void write(::java::lang::Object *);
+private:
+ ::java::io::PrintStream * __attribute__((aligned(__alignof__( ::java::lang::Object)))) p;
+ jint maxdepth;
+ jint maxArrayLength;
+ jboolean printStaticFields;
+ jint depth;
+ ::java::util::IdentityHashMap * h;
+public:
+ static ::java::lang::Class class$;
+};
+
+#endif // __gnu_gcj_util_Debug__
diff --git a/libjava/gnu/gcj/util/Debug.java b/libjava/gnu/gcj/util/Debug.java
new file mode 100644
index 000000000..d19ab694d
--- /dev/null
+++ b/libjava/gnu/gcj/util/Debug.java
@@ -0,0 +1,226 @@
+/* Copyright (C) 2004 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. */
+
+/* Utility methods that allow an object to be converted to a textual
+ representation on an OutputStream. The intention here is that this
+ class be used for debugging, so we provide information about all
+ fields, public or otherwise. */
+
+package gnu.gcj.util;
+
+import java.lang.reflect.*;
+import java.io.*;
+import java.util.*;
+
+class Debug
+{
+ private final PrintStream p;
+ private final int maxdepth;
+ private final int maxArrayLength;
+ private final boolean printStaticFields;
+ private int depth;
+
+ Debug(PrintStream writer, int maxdepth, int maxArrayLength, boolean printStaticFields)
+ {
+ p = writer;
+ this.maxdepth = maxdepth;
+ this.maxArrayLength = maxArrayLength;
+ this.printStaticFields = printStaticFields;
+ }
+
+ Debug(PrintStream writer)
+ {
+ this(writer, 0, 10, false);
+ }
+
+ Debug(int maxdepth, boolean printStaticFields)
+ {
+ this(new PrintStream
+ (new FileOutputStream(FileDescriptor.err), true),
+ maxdepth,
+ maxdepth > 0 ? 1000 : 10, printStaticFields);
+ }
+
+ Debug(int maxdepth)
+ {
+ this(maxdepth, false);
+ }
+
+ Debug()
+ {
+ this(0, false);
+ }
+
+ private final void indent()
+ {
+ for (int i = 0; i < depth; i++)
+ p.print(" ");
+ }
+
+ private final java.util.IdentityHashMap h =
+ new java.util.IdentityHashMap();
+
+ private static native Field[] getDeclaredFields(Class c);
+ private static native Object getField(Object o, Field f);
+ private static native long getAddr(Object o);
+
+ // Return an array containing all the fields of a class and its
+ // superclasses.
+ private Field[] internalGetFields(Class c)
+ {
+ HashSet set = new HashSet();
+ set.addAll(Arrays.asList(getDeclaredFields(c)));
+ Class[] interfaces = c.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++)
+ set.addAll(Arrays.asList(internalGetFields(interfaces[i])));
+ Class superClass = c.getSuperclass();
+ if (superClass != null)
+ set.addAll(Arrays.asList(internalGetFields(superClass)));
+ return (Field[])set.toArray(new Field[set.size()]);
+ }
+
+ // FIXME: We could just use getClass() here, but this is a
+ // workaround for a C++ bug that is causing getClass() to be
+ // miscompiled.
+ static private Class getItsClass(Object O)
+ {
+ return O.getClass();
+ }
+
+ // Print a reasonably readable textual representation of an object
+ // on our OutputStream. Objects are only printed once, no matter
+ // how many references point to them.
+ private void print(Object O)
+ {
+ int savedDepth = depth;
+ h.put(O, O);
+ try
+ {
+ Class C = getItsClass(O);
+ p.print(C.getName() + "@");
+ p.println(Long.toHexString(getAddr(O)));
+
+ if (C.isArray())
+ {
+ indent(); p.println("{");
+ depth++;
+ indent();
+ C = C.getComponentType();
+
+ int len = Array.getLength(O);
+ for (int i = 0; i < len; i++)
+ {
+ Object thing = Array.get(O, i);
+ print0(thing, C);
+ p.print(", ");
+ if (i > maxArrayLength)
+ {
+ p.print("...");
+ break;
+ }
+ }
+ depth--;
+ p.println();
+ indent(); p.print("}");
+ return;
+ }
+
+ indent(); p.println("{");
+ depth++;
+ if (C == java.lang.Class.class)
+ {
+ indent();
+ p.println ("class = " + O.toString() + ",");
+ }
+ else if (C == java.lang.reflect.Field.class)
+ {
+ indent();
+ p.println ("<field> = \"" + O.toString() + "\",");
+ }
+ else if (C == java.lang.String.class)
+ {
+ indent();
+ p.println ("<string> = \"" + O.toString() + "\",");
+ }
+ Field[] f = internalGetFields(C);
+ for (int i = 0; i < f.length; i++)
+ {
+ Class type = f[i].getType();
+ boolean isStatic = (f[i].getModifiers() & Modifier.STATIC) != 0;
+
+ if (isStatic && ! printStaticFields)
+ continue;
+
+ indent();
+ if (isStatic)
+ p.print("static ");
+ p.print(type.getName() +" " +f[i].getName() + " = ");
+ Object thing = getField(O, f[i]);
+ print0(thing, type);
+ p.println(",");
+ }
+ depth--;
+ indent(); p.print("}");
+ }
+ catch (Throwable t)
+ {
+ p.print("error: 0x" + Long.toHexString(getAddr(O)) + ";");
+ depth = savedDepth;
+ }
+ }
+
+ private void print0(Object thing, Class C)
+ {
+ try
+ {
+ if (thing == null)
+ {
+ p.print("null");
+ return;
+ }
+ else if (C == gnu.gcj.RawData.class ||
+ C == gnu.gcj.RawDataManaged.class)
+ {
+ }
+ else if (C.isPrimitive())
+ {
+ if (getItsClass(thing) == Character.class)
+ p.print("'" + thing + "'");
+ else
+ p.print(thing);
+ return;
+ }
+ else if (getItsClass(thing) == String.class)
+ {
+ p.print("\"" + thing + "\"");
+ return;
+ }
+ else if (depth < maxdepth && h.get(thing) == null)
+ {
+ depth++;
+ print(thing);
+ depth--;
+ return;
+ }
+ }
+ catch (Throwable t)
+ {
+ }
+
+ // The default action: just print the address.
+ p.print("0x"+ Long.toHexString(getAddr(thing)));
+ }
+
+ // Print the textual representation of an object on System.err.
+ public void write(Object O)
+ {
+ depth = 0;
+ print(O);
+ p.flush();
+ }
+}
diff --git a/libjava/gnu/gcj/util/GCInfo.h b/libjava/gnu/gcj/util/GCInfo.h
new file mode 100644
index 000000000..06e0dd0d0
--- /dev/null
+++ b/libjava/gnu/gcj/util/GCInfo.h
@@ -0,0 +1,45 @@
+
+// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*-
+
+#ifndef __gnu_gcj_util_GCInfo__
+#define __gnu_gcj_util_GCInfo__
+
+#pragma interface
+
+#include <java/lang/Object.h>
+extern "Java"
+{
+ namespace gnu
+ {
+ namespace gcj
+ {
+ namespace util
+ {
+ class GCInfo;
+ }
+ }
+ }
+}
+
+class gnu::gcj::util::GCInfo : public ::java::lang::Object
+{
+
+ GCInfo();
+ static void checkPermission();
+public:
+ static void dump(::java::lang::String *);
+private:
+ static void dump0(::java::lang::String *);
+public:
+ static void enumerate(::java::lang::String *);
+private:
+ static void enumerate0(::java::lang::String *);
+public:
+ static void setOOMDump(::java::lang::String *);
+private:
+ static void setOOMDump0(::java::lang::String *);
+public:
+ static ::java::lang::Class class$;
+};
+
+#endif // __gnu_gcj_util_GCInfo__
diff --git a/libjava/gnu/gcj/util/GCInfo.java b/libjava/gnu/gcj/util/GCInfo.java
new file mode 100644
index 000000000..73f4718f9
--- /dev/null
+++ b/libjava/gnu/gcj/util/GCInfo.java
@@ -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);
+}
diff --git a/libjava/gnu/gcj/util/UtilPermission.h b/libjava/gnu/gcj/util/UtilPermission.h
new file mode 100644
index 000000000..4c7b22671
--- /dev/null
+++ b/libjava/gnu/gcj/util/UtilPermission.h
@@ -0,0 +1,32 @@
+
+// DO NOT EDIT THIS FILE - it is machine generated -*- c++ -*-
+
+#ifndef __gnu_gcj_util_UtilPermission__
+#define __gnu_gcj_util_UtilPermission__
+
+#pragma interface
+
+#include <java/security/BasicPermission.h>
+extern "Java"
+{
+ namespace gnu
+ {
+ namespace gcj
+ {
+ namespace util
+ {
+ class UtilPermission;
+ }
+ }
+ }
+}
+
+class gnu::gcj::util::UtilPermission : public ::java::security::BasicPermission
+{
+
+public:
+ UtilPermission(::java::lang::String *);
+ static ::java::lang::Class class$;
+};
+
+#endif // __gnu_gcj_util_UtilPermission__
diff --git a/libjava/gnu/gcj/util/UtilPermission.java b/libjava/gnu/gcj/util/UtilPermission.java
new file mode 100644
index 000000000..1ea4cb716
--- /dev/null
+++ b/libjava/gnu/gcj/util/UtilPermission.java
@@ -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);
+ }
+}
diff --git a/libjava/gnu/gcj/util/natDebug.cc b/libjava/gnu/gcj/util/natDebug.cc
new file mode 100644
index 000000000..e2631b14a
--- /dev/null
+++ b/libjava/gnu/gcj/util/natDebug.cc
@@ -0,0 +1,118 @@
+// natDebug -- C++ side of Debug
+
+/* Copyright (C) 2004 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 <stddef.h>
+#include <gcj/cni.h>
+#include <gcj/field.h>
+#include <gcj/javaprims.h>
+#include <java/lang/reflect/Field.h>
+#include <java/lang/Class.h>
+#include <java/lang/Byte.h>
+#include <java/lang/Short.h>
+#include <java/lang/Integer.h>
+#include <java/lang/Long.h>
+#include <java/lang/Float.h>
+#include <java/lang/Double.h>
+#include <java/lang/Boolean.h>
+#include <java/lang/Character.h>
+#include <java/lang/IllegalArgumentException.h>
+
+#include <gnu/gcj/util/Debug.h>
+
+jlong
+gnu::gcj::util::Debug::getAddr (::java::lang::Object *o)
+{
+ return (jlong)(size_t)o;
+}
+
+JArray< ::java::lang::reflect::Field *> *
+gnu::gcj::util::Debug::getDeclaredFields (::java::lang::Class *c)
+{
+ return c->getDeclaredFields (false);
+}
+
+static void *
+getField (::java::lang::Object *obj,
+ ::java::lang::reflect::Field *field)
+{
+ using namespace java::lang::reflect;
+
+ jfieldID fld = _Jv_FromReflectedField (field);
+ _Jv_ushort flags = fld->getModifiers();
+
+ if (flags & Modifier::STATIC)
+ {
+ jclass fldClass = field->getDeclaringClass ();
+ JvInitClass(fldClass);
+ return (void*) fld->u.addr;
+ }
+ else
+ {
+ return (void*) ((char*) obj + fld->getOffset ());
+ }
+}
+
+::java::lang::Object *
+gnu::gcj::util::Debug::getField (::java::lang::Object *o,
+ ::java::lang::reflect::Field *field)
+{
+ void *addr = ::getField (o, field);
+
+ jclass type = field->getType();
+ if (! type->isPrimitive ())
+ return * (jobject*) addr;
+ if (type == JvPrimClass (double))
+ return new ::java::lang::Double (* (jdouble*) addr);
+ if (type == JvPrimClass (float))
+ return new ::java::lang::Float (* (jfloat*) addr);
+ if (type == JvPrimClass (long))
+ return new ::java::lang::Long (* (jlong*) addr);
+ if (type == JvPrimClass (int))
+ return new ::java::lang::Integer (* (jint*) addr);
+ if (type == JvPrimClass (short))
+ return new ::java::lang::Short (* (jshort*) addr);
+ if (type == JvPrimClass (byte))
+ return new ::java::lang::Byte (* (jbyte*) addr);
+ if (type == JvPrimClass (char))
+ return new ::java::lang::Character (* (jchar*) addr);
+ if (type == JvPrimClass (boolean))
+ {
+ _Jv_InitClass (&::java::lang::Boolean::class$);
+ if (* (jboolean*) addr)
+ return ::java::lang::Boolean::TRUE;
+ else
+ return ::java::lang::Boolean::FALSE;
+ }
+ throw new ::java::lang::IllegalArgumentException;
+}
+
+/* A simple method of printing an object that can be called from a
+ debugger. */
+extern "C"
+void
+_Jv_Debug (void *p)
+{
+ (new ::gnu::gcj::util::Debug ())->write ((jobject)p);
+}
+
+extern "C"
+void
+_Jv_DeepDebug (void *p, int depth)
+{
+ (new ::gnu::gcj::util::Debug (depth))->write ((jobject)p);
+}
+
+extern "C"
+void
+_Jv_StaticDeepDebug (void *p, int depth)
+{
+ (new ::gnu::gcj::util::Debug (depth, true))->write ((jobject)p);
+}
diff --git a/libjava/gnu/gcj/util/natGCInfo.cc b/libjava/gnu/gcj/util/natGCInfo.cc
new file mode 100644
index 000000000..a5936174d
--- /dev/null
+++ b/libjava/gnu/gcj/util/natGCInfo.cc
@@ -0,0 +1,458 @@
+/* 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;
+
+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)
+ {
+#ifdef HAVE_STRERROR_R
+ if (0 == strerror_r (errno, buffer, sizeof buffer))
+ fputs (buffer, fp);
+#else
+ fputs (strerror (errno), fp);
+#endif
+ }
+ 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", oomDumpName, 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
+