This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Updated throwable and stacktrace printing patch


Hi,

On Wed, 2002-08-21 at 20:18, Andrew Haley wrote:
> Bryce McKinlay writes:
>  > 
>  > I believe that for most GCJ users, it is better that the stack trace 
>  > resemble the traditional VM stack trace as closely as possible.
> 
> I'm not sure that I agree with all of this, but I'm pretty sure that
> if it came to a vote I'd lose.  So, I give up.  However, I reserve the
> right to say "I told you so" the first time we have to ask for more
> information that would have been provided by an uncensored stack
> trace!

Note that this was not the primary reason for the patch. As I wrote in
the original patch submission the goals of the patch were:

    - Simplify and optimize the stack trace printing algorithm which is
    done by refactoring the code and using a StringBuffer. This also 
    solves the bug I mentioned a couple of days ago which was caused by
    forgetting to flush the PrintWriter. Now no temporary PrintWriter
    is created at all.
    - Separating the VM dependent part from the VM independent part by
    moving fillInStackTrace() and getStackTrace() into a new package
    final class VMThrowable. This should make Throwable easier to share
    with other VMs based on GNU Classpath.
    - Do as much as possible in java which makes manipulating the
    StackTraceElement[] that much easier.
    
The sanitizing/censoring of the stack trace in NameFinder.java was added
as an example of that last point. And I do admit that I like the result
since it resembles traditional stack traces. But the actual goal was to
make it easier to tweak the stack trace elements from java, make the VM
dependent and independent parts of Throwable more explicit and optimize
the stack trace algorithm (plus fixing the bug).

>  > It also seems reasonable to display the PC in the 
>  > event that line number information is unavailable. But in the general 
>  > case, the PC is unneccessary information for casual Java developers 
>  > which clutters the stack trace.

If we want to add the addresses to the stack trace when we print them we
can do that by creating a subclass of StackTraceElement that adds a
field holding the hex address string and that overrides the toString()
method.

> "Casual" Java developers?  Are you sure?  :-)
> 
>  > There should certainly be an option to switch on verbose stack traces 
>  > (and in the future we need to do a much better job of documenting 
>  > libgcj's knobs), and perhaps it should be the defualt for embedded 
>  > targets. But by default, I think simple, clean, stack traces should be 
>  > used.
> 
> I disagree, but I bow to the majority.  

You almost convinced me to rename the method and property to censor()
instead of sanitize :) And I have thought long and hard if I could make
the default stack trace less cluttered  but still gcj/cni/C++ like. But
I really don't know enough about signals and C++ runtime/exception
issues to make a better sanitizeStack() method then I have written now.
I hope that the framework that I made is general enough for people to
hack on a better sanitize method later.

> Mark, please check in your patch.

OK. Here is the version I am going to checked in.
It has only two changes from my previous post:

- As you suggested I now use your original code from name-finder.cc to
generate the hex string representing the address.
- Someone on the Classpath mailing list noted that we have to copy the
stack when Throwable.setStackTrace() is called.

2002-08-24  Mark Wielaard <mark@klomp.org>

    * Makefile.am (libgcj_la_SOURCES): Remove name-finder.cc.
    (core_java_source_files): Add VMThrowable.java and NameFinder.java
    (nat_source_files): Remove natThrowable.cc, add natVMThrowable.cc
    and natNameFinder.cc.
    * Makefile.in: Regenerate.
    * prims.cc: Use trace_enabled from VMThrowable.
    * name-finder.cc: Removed.
    * gcj/javaprims.h: Add class VMThrowable.
    * gnu/gcj/runtime/NameFinder.java: New file.
    * gnu/gcj/runtime/natNameFinder.cc: Likewise.
    * include/name-finder.h: Removed.
    * java/lang/Throwable.java (printStackTrace (PrintStream)): Use new
    method stackTraceString().
    (printStackTrace (PrintWriter)): Likewise.
    (stackTraceString): Complete rewrite of old printStackTrace using
    StringBuffer.
    (stackTraceStringBuffer): New helper method for stackTraceString().
    (fillInStackTrace): Delegate to VMTrowable.
    (getStackTrace): Likewise.
    (getStackTrace0): Removed.
    (trace_enabled, stackTraceBytes): Moved to new VMThrowable.java.
    (setStackTrace): Copy given array.
    * java/lang/natThrowable.cc: Removed (replaced by natVMThrowable).
    * java/lang/VMThrowable.java: New class.
    * java/lang/natVMThrowable.cc: New file.

Thanks for all your input.

Cheers,

Mark
Index: Makefile.am
===================================================================
RCS file: /cvs/gcc/gcc/libjava/Makefile.am,v
retrieving revision 1.238
diff -u -r1.238 Makefile.am
--- Makefile.am	21 Aug 2002 18:54:06 -0000	1.238
+++ Makefile.am	24 Aug 2002 22:05:38 -0000
@@ -128,7 +128,7 @@
 x_javao_files = $(x_java_source_files:.java=.lo)
 
 libgcj_la_SOURCES = prims.cc jni.cc exception.cc \
-	resolve.cc defineclass.cc interpret.cc name-finder.cc verify.cc \
+	resolve.cc defineclass.cc interpret.cc verify.cc \
 	$(nat_source_files)
 EXTRA_libgcj_la_SOURCES = boehm.cc nogc.cc posix-threads.cc no-threads.cc \
 	win32-threads.cc posix.cc win32.cc \
@@ -1527,6 +1527,7 @@
 java/lang/VirtualMachineError.java \
 java/lang/VMClassLoader.java \
 java/lang/VMSecurityManager.java \
+java/lang/VMThrowable.java \
 java/lang/Void.java \
 java/io/BufferedInputStream.java \
 java/io/BufferedOutputStream.java \
@@ -1687,6 +1688,7 @@
 gnu/gcj/runtime/FinalizerThread.java \
 gnu/gcj/runtime/FirstThread.java \
 gnu/gcj/runtime/JNIWeakRef.java \
+gnu/gcj/runtime/NameFinder.java \
 gnu/gcj/runtime/SharedLibLoader.java \
 gnu/gcj/runtime/StringBuffer.java \
 gnu/gcj/runtime/VMClassLoader.java \
@@ -2204,6 +2206,7 @@
 gnu/gcj/protocol/core/natCoreInputStream.cc \
 gnu/gcj/runtime/natFinalizerThread.cc \
 gnu/gcj/runtime/natFirstThread.cc \
+gnu/gcj/runtime/natNameFinder.cc \
 gnu/gcj/runtime/natSharedLibLoader.cc \
 gnu/gcj/runtime/natStringBuffer.cc \
 java/io/natFile.cc \
@@ -2223,7 +2226,7 @@
 java/lang/natStringBuffer.cc \
 java/lang/natSystem.cc \
 java/lang/natThread.cc \
-java/lang/natThrowable.cc \
+java/lang/natVMThrowable.cc \
 java/lang/ref/natReference.cc \
 java/lang/reflect/natArray.cc \
 java/lang/reflect/natConstructor.cc \
Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/libjava/Makefile.in,v
retrieving revision 1.258
diff -u -r1.258 Makefile.in
--- Makefile.in	21 Aug 2002 18:54:06 -0000	1.258
+++ Makefile.in	24 Aug 2002 22:05:39 -0000
@@ -195,7 +195,7 @@
 x_javao_files = $(x_java_source_files:.java=.lo)
 
 libgcj_la_SOURCES = prims.cc jni.cc exception.cc \
-	resolve.cc defineclass.cc interpret.cc name-finder.cc verify.cc \
+	resolve.cc defineclass.cc interpret.cc verify.cc \
 	$(nat_source_files)
 
 EXTRA_libgcj_la_SOURCES = boehm.cc nogc.cc posix-threads.cc no-threads.cc \
@@ -1294,6 +1294,7 @@
 java/lang/VirtualMachineError.java \
 java/lang/VMClassLoader.java \
 java/lang/VMSecurityManager.java \
+java/lang/VMThrowable.java \
 java/lang/Void.java \
 java/io/BufferedInputStream.java \
 java/io/BufferedOutputStream.java \
@@ -1449,6 +1450,7 @@
 gnu/gcj/runtime/FinalizerThread.java \
 gnu/gcj/runtime/FirstThread.java \
 gnu/gcj/runtime/JNIWeakRef.java \
+gnu/gcj/runtime/NameFinder.java \
 gnu/gcj/runtime/SharedLibLoader.java \
 gnu/gcj/runtime/StringBuffer.java \
 gnu/gcj/runtime/VMClassLoader.java \
@@ -1965,6 +1967,7 @@
 gnu/gcj/protocol/core/natCoreInputStream.cc \
 gnu/gcj/runtime/natFinalizerThread.cc \
 gnu/gcj/runtime/natFirstThread.cc \
+gnu/gcj/runtime/natNameFinder.cc \
 gnu/gcj/runtime/natSharedLibLoader.cc \
 gnu/gcj/runtime/natStringBuffer.cc \
 java/io/natFile.cc \
@@ -1984,7 +1987,7 @@
 java/lang/natStringBuffer.cc \
 java/lang/natSystem.cc \
 java/lang/natThread.cc \
-java/lang/natThrowable.cc \
+java/lang/natVMThrowable.cc \
 java/lang/ref/natReference.cc \
 java/lang/reflect/natArray.cc \
 java/lang/reflect/natConstructor.cc \
@@ -2123,7 +2126,7 @@
 X_EXTRA_LIBS = @X_EXTRA_LIBS@
 X_PRE_LIBS = @X_PRE_LIBS@
 libgcj_la_OBJECTS =  prims.lo jni.lo exception.lo resolve.lo \
-defineclass.lo interpret.lo name-finder.lo verify.lo gnu/gcj/natCore.lo \
+defineclass.lo interpret.lo verify.lo gnu/gcj/natCore.lo \
 gnu/gcj/convert/JIS0208_to_Unicode.lo \
 gnu/gcj/convert/JIS0212_to_Unicode.lo gnu/gcj/convert/Unicode_to_JIS.lo \
 gnu/gcj/convert/natIconv.lo gnu/gcj/convert/natInput_EUCJIS.lo \
@@ -2131,7 +2134,7 @@
 gnu/gcj/convert/natOutput_SJIS.lo gnu/gcj/io/natSimpleSHSStream.lo \
 gnu/gcj/io/shs.lo gnu/gcj/protocol/core/natCoreInputStream.lo \
 gnu/gcj/runtime/natFinalizerThread.lo gnu/gcj/runtime/natFirstThread.lo \
-gnu/gcj/runtime/natSharedLibLoader.lo \
+gnu/gcj/runtime/natNameFinder.lo gnu/gcj/runtime/natSharedLibLoader.lo \
 gnu/gcj/runtime/natStringBuffer.lo java/io/natFile.lo \
 java/io/natFileDescriptor.lo java/io/natObjectInputStream.lo \
 java/io/natObjectOutputStream.lo java/lang/natCharacter.lo \
@@ -2140,7 +2143,7 @@
 java/lang/natFloat.lo java/lang/natMath.lo java/lang/natObject.lo \
 java/lang/natRuntime.lo java/lang/natString.lo \
 java/lang/natStringBuffer.lo java/lang/natSystem.lo \
-java/lang/natThread.lo java/lang/natThrowable.lo \
+java/lang/natThread.lo java/lang/natVMThrowable.lo \
 java/lang/ref/natReference.lo java/lang/reflect/natArray.lo \
 java/lang/reflect/natConstructor.lo java/lang/reflect/natField.lo \
 java/lang/reflect/natMethod.lo java/net/natInetAddress.lo \
@@ -2246,11 +2249,13 @@
 .deps/gnu/gcj/runtime/FileDeleter.P \
 .deps/gnu/gcj/runtime/FinalizerThread.P \
 .deps/gnu/gcj/runtime/FirstThread.P .deps/gnu/gcj/runtime/JNIWeakRef.P \
+.deps/gnu/gcj/runtime/NameFinder.P \
 .deps/gnu/gcj/runtime/SharedLibLoader.P \
 .deps/gnu/gcj/runtime/StringBuffer.P \
 .deps/gnu/gcj/runtime/VMClassLoader.P \
 .deps/gnu/gcj/runtime/natFinalizerThread.P \
 .deps/gnu/gcj/runtime/natFirstThread.P \
+.deps/gnu/gcj/runtime/natNameFinder.P \
 .deps/gnu/gcj/runtime/natSharedLibLoader.P \
 .deps/gnu/gcj/runtime/natStringBuffer.P .deps/gnu/gcj/xlib/Clip.P \
 .deps/gnu/gcj/xlib/Colormap.P .deps/gnu/gcj/xlib/Display.P \
@@ -2813,8 +2818,9 @@
 .deps/java/lang/UnsupportedClassVersionError.P \
 .deps/java/lang/UnsupportedOperationException.P \
 .deps/java/lang/VMClassLoader.P .deps/java/lang/VMSecurityManager.P \
-.deps/java/lang/VerifyError.P .deps/java/lang/VirtualMachineError.P \
-.deps/java/lang/Void.P .deps/java/lang/dtoa.P .deps/java/lang/e_acos.P \
+.deps/java/lang/VMThrowable.P .deps/java/lang/VerifyError.P \
+.deps/java/lang/VirtualMachineError.P .deps/java/lang/Void.P \
+.deps/java/lang/dtoa.P .deps/java/lang/e_acos.P \
 .deps/java/lang/e_asin.P .deps/java/lang/e_atan2.P \
 .deps/java/lang/e_exp.P .deps/java/lang/e_fmod.P \
 .deps/java/lang/e_log.P .deps/java/lang/e_pow.P \
@@ -2828,7 +2834,7 @@
 .deps/java/lang/natMath.P .deps/java/lang/natObject.P \
 .deps/java/lang/natRuntime.P .deps/java/lang/natString.P \
 .deps/java/lang/natStringBuffer.P .deps/java/lang/natSystem.P \
-.deps/java/lang/natThread.P .deps/java/lang/natThrowable.P \
+.deps/java/lang/natThread.P .deps/java/lang/natVMThrowable.P \
 .deps/java/lang/ref/PhantomReference.P .deps/java/lang/ref/Reference.P \
 .deps/java/lang/ref/ReferenceQueue.P \
 .deps/java/lang/ref/SoftReference.P .deps/java/lang/ref/WeakReference.P \
@@ -3468,17 +3474,17 @@
 .deps/javax/transaction/UserTransaction.P \
 .deps/javax/transaction/xa/XAException.P \
 .deps/javax/transaction/xa/XAResource.P \
-.deps/javax/transaction/xa/Xid.P .deps/jni.P .deps/name-finder.P \
-.deps/no-threads.P .deps/nogc.P .deps/org/w3c/dom/Attr.P \
-.deps/org/w3c/dom/CDATASection.P .deps/org/w3c/dom/CharacterData.P \
-.deps/org/w3c/dom/Comment.P .deps/org/w3c/dom/DOMException.P \
-.deps/org/w3c/dom/DOMImplementation.P .deps/org/w3c/dom/Document.P \
-.deps/org/w3c/dom/DocumentFragment.P .deps/org/w3c/dom/DocumentType.P \
-.deps/org/w3c/dom/Element.P .deps/org/w3c/dom/Entity.P \
-.deps/org/w3c/dom/EntityReference.P .deps/org/w3c/dom/NamedNodeMap.P \
-.deps/org/w3c/dom/Node.P .deps/org/w3c/dom/NodeList.P \
-.deps/org/w3c/dom/Notation.P .deps/org/w3c/dom/ProcessingInstruction.P \
-.deps/org/w3c/dom/Text.P .deps/org/w3c/dom/ranges/DocumentRange.P \
+.deps/javax/transaction/xa/Xid.P .deps/jni.P .deps/no-threads.P \
+.deps/nogc.P .deps/org/w3c/dom/Attr.P .deps/org/w3c/dom/CDATASection.P \
+.deps/org/w3c/dom/CharacterData.P .deps/org/w3c/dom/Comment.P \
+.deps/org/w3c/dom/DOMException.P .deps/org/w3c/dom/DOMImplementation.P \
+.deps/org/w3c/dom/Document.P .deps/org/w3c/dom/DocumentFragment.P \
+.deps/org/w3c/dom/DocumentType.P .deps/org/w3c/dom/Element.P \
+.deps/org/w3c/dom/Entity.P .deps/org/w3c/dom/EntityReference.P \
+.deps/org/w3c/dom/NamedNodeMap.P .deps/org/w3c/dom/Node.P \
+.deps/org/w3c/dom/NodeList.P .deps/org/w3c/dom/Notation.P \
+.deps/org/w3c/dom/ProcessingInstruction.P .deps/org/w3c/dom/Text.P \
+.deps/org/w3c/dom/ranges/DocumentRange.P \
 .deps/org/w3c/dom/ranges/Range.P \
 .deps/org/w3c/dom/ranges/RangeException.P \
 .deps/org/w3c/dom/traversal/DocumentTraversal.P \
Index: name-finder.cc
===================================================================
RCS file: name-finder.cc
diff -N name-finder.cc
--- name-finder.cc	12 Jul 2002 12:52:43 -0000	1.12
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,356 +0,0 @@
-// name-finder.cc - Convert addresses to names
-
-/* Copyright (C) 2000, 2002  Free Software Foundation, Inc
-
-   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.  */
-
-/**
- * @author Andrew Haley <aph@cygnus.com>
- * @date Jan 6  2000
- */
-
-/* _Jv_name_finder is a class wrapper around a mechanism that can
-   convert address of methods to their names and the names of files in
-   which they appear.
-
-   Right now, the only implementation of this involves running a copy
-   of addr2line, but at some point it is worth building this
-   functionality into libgcj, if only for embedded systems.  */
-
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE 1
-#endif
-
-#include <config.h>
-
-#include <string.h>
-
-#include <gcj/cni.h>
-#include <jvm.h>
-#include <java/lang/Object.h>
-#include <java-threads.h>
-#include <java/lang/Throwable.h>
-#include <java/io/PrintStream.h>
-#include <java/io/PrintWriter.h>
-
-#include <sys/types.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifdef HAVE_DLFCN_H
-#include <dlfcn.h>
-#endif
-
-#include <name-finder.h>
-
-/* Create a new name finder which will perform address lookups on an
-   executable. */
-
-_Jv_name_finder::_Jv_name_finder (char *executable)
-{
-#if defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP)
-  demangling_error = lookup_error = 0;
-
-  // Initialize file descriptors so that shutdown works properly.
-  f_pipe[0] = -1;
-  f_pipe[1] = -1;
-  b_pipe[0] = -1;
-  b_pipe[1] = -1;
-  b_pipe_fd = NULL;
-
-  f2_pipe[0] = -1;
-  f2_pipe[1] = -1;
-  b2_pipe[0] = -1;
-  b2_pipe[1] = -1;
-  b2_pipe_fd = NULL;
-
-  // addr2line helper process.
-
-  char *argv[5];
-  {
-    int arg = 0;
-#ifdef __ia64__
-    argv[arg++] = "addr2name.awk";
-#else
-    argv[arg++] = "addr2line";
-    argv[arg++] = "-f";
-    argv[arg++] = "-e";
-#endif
-    argv[arg++] = executable;
-    argv[arg] = NULL;
-  }
-
-  lookup_error |= pipe (f_pipe) < 0;
-  lookup_error |= pipe (b_pipe) < 0;
-
-  if (lookup_error)
-    return;
-
-  pid = fork ();
-  if (pid == 0)
-    {
-      close (f_pipe[1]);
-      close (b_pipe[0]);
-      dup2 (f_pipe[0], fileno (stdin));
-      dup2 (b_pipe[1], fileno (stdout));
-      execvp (argv[0], argv);
-      _exit (127);
-    }
-
-  // Close child end of pipes.  Set local descriptors to -1 so we
-  // don't try to close the fd again.
-  close (f_pipe [0]);
-  f_pipe[0] = -1;
-  close (b_pipe [1]);
-  b_pipe[1] = -1;
-
-  if (pid < 0)
-    {
-      lookup_error |= 1; 
-      return;
-    }
-
-  b_pipe_fd = fdopen (b_pipe[0], "r");
-  lookup_error |= !b_pipe_fd;
-
-  if (! lookup_error)
-    {
-      // Don't try to close the fd twice.
-      b_pipe[0] = -1;
-    }
-
-  // c++filt helper process.
-  
-  char *argv2[4];
-  argv2[0] = "c++filt";
-  argv2[1] = "-s";
-  argv2[2] = "java";
-  argv2[3] = NULL;
-
-  demangling_error |= pipe (f2_pipe) < 0;
-  demangling_error |= pipe (b2_pipe) < 0;
-
-  if (demangling_error)
-    return;
-
-  pid2 = fork ();
-  if (pid2 == 0)
-    {
-      close (f2_pipe[1]);
-      close (b2_pipe[0]);
-      dup2 (f2_pipe[0], fileno (stdin));
-      dup2 (b2_pipe[1], fileno (stdout));
-      execvp (argv2[0], argv2);
-      _exit (127);
-    }
-
-  // Close child end of pipes.  Set local descriptors to -1 so we
-  // don't try to close the fd again.
-  close (f2_pipe [0]);
-  f2_pipe[0] = -1;
-  close (b2_pipe [1]);
-  b2_pipe[1] = -1;
-
-  if (pid2 < 0)
-    {
-      demangling_error |= 1; 
-      return;
-    }
-
-  b2_pipe_fd = fdopen (b2_pipe[0], "r");
-  demangling_error |= !b2_pipe_fd;
-
-  if (! demangling_error)
-    {
-      // Don't try to close the fd twice.
-      b2_pipe[0] = -1;
-    }
-#endif
-}
-
-/* Convert a pointer to hex. */
-
-void
-_Jv_name_finder::toHex (void *p)
-{
-  typedef unsigned word_t __attribute ((mode (word)));
-  word_t n = (word_t) p;
-  int digits = sizeof (void *) * 2;
-
-  strcpy (hex, "0x");
-  for (int i = digits - 1; i >= 0; i--)
-    {
-      int digit = n % 16;
-      
-      n /= 16;
-      hex[i+2] = digit > 9 ? 'a' + digit - 10 : '0' + digit; 
-    }
-  hex [digits+2] = 0;
-}   
-
-/* Creates a StackTraceElement given a string and a filename.
-   Splits the given string into the class and method part.
-   The string s will be a demangled to a fully qualified java method string.
-   The string f will be decomposed into a file name and a possible line number.
-   The given strings will be altered.  */
-
-java::lang::StackTraceElement*
-_Jv_name_finder::createStackTraceElement(char *s, char *f)
-{
-  char *c;
-  char *class_name = NULL;
-  char *method_name = NULL;
-
-#if defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP)
-  if (demangling_error)
-    goto fail;
-
-  demangling_error |= write (f2_pipe[1], s, strlen (s)) < 0;
-  if (demangling_error)
-    goto fail;
-  demangling_error |= write (f2_pipe[1], "\n", 1) < 0;
-  if (demangling_error)
-    goto fail;
-
-  char name[1024];
-  demangling_error |= (fgets (name, sizeof name, b2_pipe_fd) == NULL);
-  if (demangling_error)
-    goto fail;
-
-  c = strchr (name, '\n');
-  if (c)
-    *c = 0;
-  s = name;
-#endif
-
-  c = strchr (s, '(');
-  if (c)
-    {
-      while(c-->s)
-	if (*c == '.')
-	  break;
-
-      if (*c == '.')
-	{
-	  *c = 0;
-	  class_name = s;
-	  method_name = c+1;
-	}
-      else
-	{
-	  class_name = NULL;
-	  method_name = s;
-	}
-    }
-  else
-    {
-      class_name = NULL;
-      method_name = s;
-    }
-
-  // Get line number
-  int line_number;
-  c = strrchr (f, ':');
-  if (c)
-    {
-      if (c[1] != 0)
-	line_number = atoi(c+1);
-      else
-	line_number = -1;
-      *c = 0;
-    }
-  else
-    {
-      line_number = -1;
-      c = strchr (f, '\n');
-      if (c)
-	*c = 0;
-    }
-
- fail:
-  return new java::lang::StackTraceElement(
-		  f ? JvNewStringLatin1 (f) : NULL,
-		  line_number,
-		  class_name ? JvNewStringLatin1 (class_name) : NULL,
-		  JvNewStringLatin1 (method_name ? method_name : s),
-		  false);
-}
-
-/* Given a pointer to a function or method, try to convert it into a
-   name and the appropriate line and source file.  The caller passes
-   the code pointer in p.
-
-   Returns false if the lookup fails.  Even if this happens, the field
-   he will have been correctly filled in with the pointer.  */
-
-java::lang::StackTraceElement*
-_Jv_name_finder::lookup (void *p)
-{
-  extern char **_Jv_argv;
-  toHex (p);
-      
-  char name[1024];
-  char file_name[1024];
-
-  file_name[0] = 0;
-
-#if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR)
-  {
-    Dl_info dl_info;
-    
-    if (dladdr (p, &dl_info))
-      {
-        if (dl_info.dli_fname)
-	  strncpy (file_name, dl_info.dli_fname, sizeof file_name);
-	if (dl_info.dli_sname)
-	  strncpy (name, dl_info.dli_sname, sizeof name);
-       
-       /* Don't trust dladdr() if the address is from the main program. */
-       if (dl_info.dli_fname != NULL
-           && dl_info.dli_sname != NULL
-	   && (_Jv_argv == NULL || strcmp (file_name, _Jv_argv[0]) != 0))
-         return createStackTraceElement (name, file_name);
-      }
-  }
-#endif
-
-  memcpy (name, hex, strlen (hex) + 1);
-
-#if defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP)
-  if (lookup_error)
-    goto fail;
-
-  lookup_error |= write (f_pipe[1], hex, strlen (hex)) < 0;
-  if (lookup_error)
-    goto fail;
-  lookup_error |= write (f_pipe[1], "\n", 1) < 0;
-  if (lookup_error)
-    goto fail;
-
-  lookup_error |= (fgets (name, sizeof name, b_pipe_fd) == NULL);
-  if (lookup_error)
-    goto fail;
-  lookup_error |= (fgets (file_name, sizeof file_name, b_pipe_fd) == NULL);
-  if (lookup_error)
-    goto fail;
-
-  {
-    char *newline = strchr (name, '\n');
-    if (newline)
-      *newline = 0;
-  }
-#endif /* defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP) */
-
- fail:
-  return (createStackTraceElement (name, file_name));
-}
Index: prims.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/prims.cc,v
retrieving revision 1.72
diff -u -r1.72 prims.cc
--- prims.cc	10 Mar 2002 03:53:12 -0000	1.72
+++ prims.cc	24 Aug 2002 22:05:40 -0000
@@ -53,6 +53,7 @@
 #include <java/lang/NullPointerException.h>
 #include <java/lang/OutOfMemoryError.h>
 #include <java/lang/System.h>
+#include <java/lang/VMThrowable.h>
 #include <java/lang/reflect/Modifier.h>
 #include <java/io/PrintStream.h>
 #include <java/lang/UnsatisfiedLinkError.h>
@@ -910,8 +911,8 @@
   _Jv_InitPrimClass (&_Jv_voidClass,    "void",    'V', 0, &_Jv_voidVTable);
 
   // Turn stack trace generation off while creating exception objects.
-  _Jv_InitClass (&java::lang::Throwable::class$);
-  java::lang::Throwable::trace_enabled = 0;
+  _Jv_InitClass (&java::lang::VMThrowable::class$);
+  java::lang::VMThrowable::trace_enabled = 0;
   
   INIT_SEGV;
 #ifdef HANDLE_FPE
@@ -923,7 +924,7 @@
 
   no_memory = new java::lang::OutOfMemoryError;
 
-  java::lang::Throwable::trace_enabled = 1;
+  java::lang::VMThrowable::trace_enabled = 1;
 
 #ifdef USE_LTDL
   LTDL_SET_PRELOADED_SYMBOLS ();
Index: gcj/javaprims.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gcj/javaprims.h,v
retrieving revision 1.40
diff -u -r1.40 javaprims.h
--- gcj/javaprims.h	12 Jul 2002 12:52:43 -0000	1.40
+++ gcj/javaprims.h	24 Aug 2002 22:05:40 -0000
@@ -211,6 +211,7 @@
       class UnsupportedOperationException;
       class VMClassLoader;
       class VMSecurityManager;
+      class VMThrowable;
       class VerifyError;
       class VirtualMachineError;
       class Void;
Index: gnu/gcj/runtime/NameFinder.java
===================================================================
RCS file: gnu/gcj/runtime/NameFinder.java
diff -N gnu/gcj/runtime/NameFinder.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/gcj/runtime/NameFinder.java	24 Aug 2002 22:05:40 -0000
@@ -0,0 +1,407 @@
+/* NameFinder.java -- Translates addresses to StackTraceElements.
+   Copyright (C) 2002 Free Software Foundation, Inc.
+
+   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.runtime;
+
+import gnu.gcj.RawData;
+
+import java.lang.StringBuffer;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+import java.io.File;
+
+/**
+ * Helper class that translates addresses (represented as longs) to a
+ * StackTraceElement array.
+ *
+ * There are a couple of system properties that can be set to manipulate the
+ * result (all default to true):
+ * <li>
+ * <ul><code>gnu.gcj.runtime.NameFinder.demangle</code>
+ *     Whether names should be demangled.</ul>
+ * <ul><code>gnu.gcj.runtime.NameFinder.sanitize</code></ul>
+ *     Whether calls to initialize exceptions and starting the runtime system
+ *     should be removed from the stack trace. Only done when names are
+ *     demangled.</ul>
+ * <ul><code>gnu.gcj.runtime.NameFinder.remove_unknown</code>
+ *     Wheter calls to unknown functions (class and method names are unknown)
+ *     should be removed from the stack trace. Only done when the stack is
+ *     sanitized.</ul>
+ * <ul><code>gnu.gcj.runtime.NameFinder.use_addr2line</code>
+ *     Wheter an external process (addr2line or addr2name.awk) should be used
+ *     as fallback to convert the addresses to function names when the runtime
+ *     is unable to do it through <code>dladdr</code>.</ul>
+ * </li>
+ *
+ * <code>close()</code> should be called to get rid of all resources.
+ *
+ * This class is used from <code>java.lang.VMThrowable</code>.
+ *
+ * Currently the <code>lookup(long[])</code> method is not thread safe.
+ * It can easily be made thread safe by synchronizing access to all external
+ * processes when used.
+ *   
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+public class NameFinder
+{
+  // Set these to false when not needed.
+  private static final boolean demangle
+	  = Boolean.valueOf(System.getProperty
+		("gnu.gcj.runtime.NameFinder.demangle", "true")
+	    ).booleanValue();
+  private static final boolean sanitize
+	  = Boolean.valueOf(System.getProperty
+		("gnu.gcj.runtime.NameFinder.sanitize", "true")
+	    ).booleanValue();
+  private static final boolean remove_unknown
+	  = Boolean.valueOf(System.getProperty
+		("gnu.gcj.runtime.NameFinder.remove_unknown", "true")
+	    ).booleanValue();
+  private static final boolean use_addr2line
+	  = Boolean.valueOf(System.getProperty
+		("gnu.gcj.runtime.NameFinder.use_addr2line", "true")
+	    ).booleanValue();
+
+  /**
+   * The name of the currently running executable.
+   */
+  private final String executable;
+
+  /**
+   * Process used for demangling names.
+   */
+  private Process cppfilt;
+
+  private BufferedWriter cppfiltOut;
+  private BufferedReader cppfiltIn;
+
+  /**
+   * Process used for translating addresses to function/file names.
+   */
+  private Process addr2line;
+
+  private BufferedWriter addr2lineOut;
+  private BufferedReader addr2lineIn;
+
+  /**
+   * Creates a new NameFinder. Call close to get rid of any resources
+   * created while using the <code>lookup</code> methods.
+   */
+  public NameFinder()
+  {
+    executable = getExecutable();
+    Runtime runtime = Runtime.getRuntime();
+    if (demangle)
+    {
+      try
+	{
+	  String[] exec = new String[] {"c++filt", "-s", "java"};
+	  cppfilt = runtime.exec(exec);
+	  cppfiltIn = new BufferedReader
+			(new InputStreamReader(cppfilt.getInputStream()));
+	  cppfiltOut = new BufferedWriter
+			(new OutputStreamWriter(cppfilt.getOutputStream()));
+	}
+      catch (IOException ioe)
+        {
+	  if (cppfilt != null)
+	    cppfilt.destroy();
+	  cppfilt = null;
+	}
+    }
+
+    if (use_addr2line)
+      {
+	try
+	  {
+	    String[] exec = new String[] {"addr2line", "-f", "-e", executable};
+	    addr2line = runtime.exec(exec);
+	  }
+	catch (IOException ioe)
+	  {
+	    try
+	      {
+		String[] exec = new String[] {"addr2name.awk", executable};
+		addr2line = runtime.exec(exec);
+	      }
+	    catch (IOException ioe2) { addr2line = null; }
+	  }
+
+	if (addr2line != null)
+	  {
+	    try
+	      {
+		addr2lineIn = new BufferedReader
+			(new InputStreamReader(addr2line.getInputStream()));
+		addr2lineOut = new BufferedWriter
+			(new OutputStreamWriter(addr2line.getOutputStream()));
+	      }
+	    catch (IOException ioe)
+	      {  
+		addr2line.destroy();
+		addr2line = null;
+	      }
+	  }
+      }
+  }
+
+  /**
+   * Returns the name of the currently running process.
+   */
+  native private static String getExecutable();
+
+  /**
+   * Tries to use dladdr to create the nth StackTraceElement from the given
+   * addresses. Returns null on failure.
+   */
+  native private StackTraceElement dladdrLookup(RawData addrs, int n);
+
+  /**
+   * Returns the nth element from the stack as a hex encoded String.
+   */
+  native private String getAddrAsString(RawData addrs, int n);
+
+  /**
+   * Creates the nth StackTraceElement from the given native stacktrace.
+   */
+  private StackTraceElement lookup(RawData addrs, int n)
+  {
+    StackTraceElement result;
+
+    result = dladdrLookup(addrs, n);
+    if (result == null)
+      {
+	String name = null;
+	String file = null;
+	String hex = getAddrAsString(addrs, n);
+	
+	if (addr2line != null)
+	  {
+	    try
+	      {
+		addr2lineOut.write(hex);
+		addr2lineOut.newLine();
+		addr2lineOut.flush();
+		name = addr2lineIn.readLine();
+		file = addr2lineIn.readLine();
+	      }
+	    catch (IOException ioe) { addr2line = null; }
+	  }
+
+	if (name == null || "??".equals(name))
+	  name = hex;
+
+	result = createStackTraceElement(name, file);
+      }
+
+    return result;
+  }
+
+  /**
+   * Given an Throwable and a native stacktrace returns an array of
+   * StackTraceElement containing class, method, file and linenumbers.
+   */
+  public StackTraceElement[] lookup(Throwable t, RawData addrs, int length)
+  {
+    StackTraceElement[] elements = new StackTraceElement[length];
+    for (int i=0; i < length; i++)
+      elements[i] = lookup(addrs, i);
+
+    if (demangle && sanitize)
+      return sanitizeStack(elements, t);
+    else
+      return elements;
+  }
+
+  
+  /**
+   * Removes calls to initialize exceptions and the runtime system from
+   * the stack trace including stack frames of which nothing usefull is known.
+   * Throw away the top of the stack till we find the constructor(s)
+   * of this Throwable or at least the contructors of java.lang.Throwable
+   * or the actual fillInStackTrace call.
+   * Also throw away from the top everything before and including a runtime
+   * _Jv_Throw call.
+   */
+  private static StackTraceElement[] sanitizeStack(StackTraceElement[] elements,
+						   Throwable t)
+  {
+    StackTraceElement[] stack;
+
+    String className = t.getClass().getName();
+    String consName;
+    int lastDot = className.lastIndexOf('.');
+    if (lastDot == -1)
+      consName = className + '(';
+    else
+      consName = className.substring(lastDot + 1) + '(';
+
+    int unknown = 0;
+    int last_throw = -1;
+    int length = elements.length;
+    int end = length-1;
+    for (int i = 0; i < length; i++)
+      {
+	String CName = elements[i].getClassName();
+	String MName = elements[i].getMethodName();
+	if ((CName == null && MName != null && MName.startsWith("_Jv_Throw"))
+	  ||
+	   (CName != null
+	    && (CName.equals(className)
+		|| CName.equals("java.lang.Throwable")
+		|| CName.equals("java.lang.VMThrowable"))
+	    && MName != null
+	    && (MName.startsWith(consName)
+		|| MName.startsWith("Throwable(")
+		|| MName.startsWith("fillInStackTrace("))))
+	  last_throw = i;
+	else if (remove_unknown && CName == null 
+		 && (MName == null || MName.startsWith("0x")))
+	  unknown++;
+	else if ("main(java.lang.String[])".equals(MName))
+	  {
+	    end = i;
+	    break;
+	  }
+      }
+    int begin = last_throw+1;
+
+    // Now filter out everything at the start and the end that is not part
+    // of the "normal" user program including any elements that have no
+    // usefull information whatsoever unless that means we filter out all info.
+    int nr_elements = end-begin-unknown+1;
+    if ((begin > 0 || end < length-1 || unknown > 0) && nr_elements > 0)
+      {
+	stack = new StackTraceElement[nr_elements];
+	int pos =0;
+	for (int i=begin; i<=end; i++)
+	  {
+	    String MName;
+	    if (unknown == 0
+		|| !(elements[i].getClassName() == null
+		     && ((MName = elements[i].getMethodName()) == null
+			 || MName.startsWith("0x"))))
+	      {
+		stack[pos] = elements[i];
+		pos++;
+	      }
+	  }
+      }
+    else
+      stack = elements;
+
+    return stack;
+  }
+
+  /**
+   * Creates a StackTraceElement given a string and a filename.
+   * Splits the given string into the class and method part.
+   * The string name will be a demangled to a fully qualified java method
+   * string. The string file will be decomposed into a file name and possibly
+   * a line number. The name should never be null, but the file may be if it
+   * is unknown.
+   */
+  private StackTraceElement createStackTraceElement(String name, String file)
+  {
+    if (!demangle)
+      return new StackTraceElement(file, -1, null, name, false);
+
+    String s = demangleName(name);
+    String methodName = s;
+    String className = null;
+    int bracket = s.indexOf('(');
+    if (bracket > 0)
+      {
+	int dot = s.lastIndexOf('.', bracket);
+	if (dot > 0)
+	  {
+	    className = s.substring(0, dot);
+	    methodName = s.substring(dot+1, s.length());
+	  }
+      }
+
+    String fileName = file;
+    int line = -1;
+    if (fileName != null)
+      {
+	int colon = file.indexOf(':');
+	if (colon > 0)
+	  {
+	    fileName = file.substring(0, colon);
+	    try
+	      {
+		line = Integer.parseInt(file.substring(colon+1, file.length()));
+	      }
+	    catch (NumberFormatException nfe) { /* ignore */ }
+	  }
+
+	if (line == 0)
+	  line =-1;
+
+	if ("".equals(fileName) || "??".equals(fileName))
+	  fileName = null;
+	else if (fileName != null)
+	  {
+	    try
+	      {
+		fileName = new File(fileName).getCanonicalPath();
+	      }
+	    catch (IOException ioe) { /* ignore */ }
+	  }
+      }
+
+    return new StackTraceElement(fileName, line, className, methodName, false);
+  }
+
+  /**
+   * Demangles the given String if possible. Returns the demangled String or
+   * the original string if demangling is impossible.
+   */
+  private String demangleName(String s)
+  {
+    if (cppfilt != null)
+    {
+      try
+	{
+	  cppfiltOut.write(s);
+	  cppfiltOut.newLine();
+	  cppfiltOut.flush();
+	  return cppfiltIn.readLine();
+	}
+      catch (IOException ioe) { cppfilt.destroy(); cppfilt = null; }
+    }
+
+    return s;
+  }
+
+  /**
+   * Releases all resources used by this NameFinder.
+   */
+  public void close()
+  {
+    if (cppfilt != null)
+      cppfilt.destroy();
+
+    if (addr2line != null)
+      addr2line.destroy();
+  }
+
+  /**
+   * Calls close to get rid of all resources.
+   */
+  protected void finalize()
+  {
+    close();
+  }
+}
Index: gnu/gcj/runtime/natNameFinder.cc
===================================================================
RCS file: gnu/gcj/runtime/natNameFinder.cc
diff -N gnu/gcj/runtime/natNameFinder.cc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/gcj/runtime/natNameFinder.cc	24 Aug 2002 22:05:40 -0000
@@ -0,0 +1,84 @@
+// natNameFinder.cc - native helper methods for NameFiner.java
+
+/* Copyright (C) 2002  Free Software Foundation, Inc
+
+   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.  */
+
+/**
+ * @author Mark Wielaard (mark@klomp.org)
+ * Based on the old name-finder.cc by Andrew Haley <aph@cygnus.com>.
+ */
+
+#include <config.h>
+
+#include <gcj/cni.h>
+#include <jvm.h>
+#include <java/lang/String.h>
+#include <java/lang/StackTraceElement.h>
+
+#include <gnu/gcj/runtime/NameFinder.h>
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+java::lang::String*
+gnu::gcj::runtime::NameFinder::getExecutable (void)
+{
+    return JvNewStringLatin1 (_Jv_ThisExecutable ());
+}
+
+java::lang::String*
+gnu::gcj::runtime::NameFinder::getAddrAsString(RawData* addrs, jint n)
+{
+  void **p = (void **) addrs;
+  typedef unsigned word_t __attribute ((mode (word)));
+  word_t w = (word_t) p[n];
+  int digits = sizeof (void *) * 2;
+  char hex[digits+5];
+
+  strcpy (hex, "0x");
+  for (int i = digits - 1; i >= 0; i--)
+    {
+      int digit = w % 16;
+
+      w /= 16;
+      hex[i+2] = digit > 9 ? 'a' + digit - 10 : '0' + digit;
+    }
+  hex [digits+2] = 0;
+
+  return JvNewStringLatin1(hex);
+}
+
+java::lang::StackTraceElement*
+gnu::gcj::runtime::NameFinder::dladdrLookup(RawData* addrs, jint n)
+{
+#if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR)
+  extern char **_Jv_argv;
+  char name[1024];
+  char file_name[1024];
+  void **stack = (void **) addrs;
+  void* p = stack[n];
+  Dl_info dl_info;
+   
+  if (dladdr (p, &dl_info))
+    {
+      if (dl_info.dli_fname)
+        strncpy (file_name, dl_info.dli_fname, sizeof file_name);
+      if (dl_info.dli_sname)
+        strncpy (name, dl_info.dli_sname, sizeof name);
+     
+     /* Don't trust dladdr() if the address is from the main program. */
+     if (dl_info.dli_fname != NULL
+         && dl_info.dli_sname != NULL
+         && (_Jv_argv == NULL || strcmp (file_name, _Jv_argv[0]) != 0))
+       return createStackTraceElement (JvNewStringLatin1 (name),
+				       JvNewStringLatin1 (file_name));
+    }
+#endif
+  return NULL;
+}
Index: include/name-finder.h
===================================================================
RCS file: include/name-finder.h
diff -N include/name-finder.h
--- include/name-finder.h	12 Jul 2002 12:52:44 -0000	1.7
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,103 +0,0 @@
-// name-finder.h - Convert addresses to names
-
-/* Copyright (C) 2000, 2002  Free Software Foundation, Inc
-
-   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.  */
-
-/**
- * @author Andrew Haley <aph@cygnus.com>
- * @date Jan 6  2000
- */
-
-#include <gcj/cni.h>
-#include <jvm.h>
-
-#include <sys/types.h>
-
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-
-#include <string.h>
-#include <stdio.h>
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include <java/lang/StackTraceElement.h>
-
-/* _Jv_name_finder is a class wrapper around a mechanism that can
-   convert addresses of methods to their names and the names of files
-   in which they appear. */
-
-class _Jv_name_finder
-{
-public:  
-  _Jv_name_finder (char *executable);
-  ~_Jv_name_finder ()
-    {
-#if defined (HAVE_PIPE) && defined (HAVE_FORK)
-      myclose (f_pipe[0]);
-      myclose (f_pipe[1]);
-      myclose (b_pipe[0]);
-      myclose (b_pipe[1]);
-      if (b_pipe_fd != NULL)
-	fclose (b_pipe_fd);
-
-      myclose (f2_pipe[0]);
-      myclose (f2_pipe[1]);
-      myclose (b2_pipe[0]);
-      myclose (b2_pipe[1]);
-      if (b2_pipe_fd != NULL)
-	fclose (b2_pipe_fd);
-
-      if (pid >= 0)
-	{
-	  int wstat;
-	  // We don't care about errors here.
-	  waitpid (pid, &wstat, 0);
-	}
-
-      if (pid2 >= 0)
-	{
-	  int wstat;
-	  // We don't care about errors here.
-	  waitpid (pid2, &wstat, 0);
-	}
-#endif
-    }
-
-/* Given a pointer to a function or method, try to convert it into a
-   name and the appropriate line and source file.  The caller passes
-   the code pointer in p.
-
-   Returns NULL if the lookup fails.  Even if this happens, the field
-   hex will have been correctly filled in with the pointer. */
-
-  java::lang::StackTraceElement* lookup (void *p);
-
-  char hex[sizeof (void *) * 2 + 5];
-
-private:
-  void toHex (void *p);
-  java::lang::StackTraceElement* createStackTraceElement(char *s, char *f);
-#if defined (HAVE_PIPE) && defined (HAVE_FORK)
-  pid_t pid, pid2;
-  int f_pipe[2], b_pipe[2], f2_pipe[2], b2_pipe[2];
-  FILE *b_pipe_fd, *b2_pipe_fd;
-  int demangling_error, lookup_error;
-
-  // Close a descriptor only if it has not been closed.
-  void myclose (int fd)
-  {
-    if (fd != -1)
-      close (fd);
-  }
-
-#endif
-};
Index: java/lang/Throwable.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/Throwable.java,v
retrieving revision 1.12
diff -u -r1.12 Throwable.java
--- java/lang/Throwable.java	12 Jul 2002 12:52:44 -0000	1.12
+++ java/lang/Throwable.java	24 Aug 2002 22:05:42 -0000
@@ -46,11 +46,6 @@
 import java.io.IOException;
 import java.io.OutputStream;
 
-/**
- * @author Tom Tromey <tromey@cygnus.com>
- * @date October 30, 1998 
- */
-
 /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
  * "The Java Language Specification", ISBN 0-201-63451-1
  * Status: Sufficient for compiled code, but methods applicable to
@@ -116,7 +111,7 @@
  * @author Tom Tromey
  * @author Eric Blake <ebb9@email.byu.edu>
  * @since 1.0
- * @status still missing 1.4 functionality
+ * @status updated to 1.4
  */
 public class Throwable implements Serializable
 {
@@ -130,7 +125,7 @@
    *
    * @serial specific details about the exception, may be null
    */
-  private String detailMessage;
+  private final String detailMessage;
 
   /**
    * The cause of the throwable, including null for an unknown or non-chained
@@ -374,7 +369,7 @@
    */
   public void printStackTrace(PrintStream s)
   {
-    printStackTrace(new PrintWriter(s));
+    s.print(stackTraceString());
   }
 
   /**
@@ -409,72 +404,88 @@
    */
   public void printStackTrace (PrintWriter pw)
   {
-    // First line
-    pw.println(toString());
+    pw.print(stackTraceString());
+  }
 
-    // The stacktrace
+  private static final String nl = System.getProperty("line.separator");
+  // Create whole stack trace in a stringbuffer so we don't have to print
+  // it line by line. This prevents printing multiple stack traces from
+  // different threads to get mixed up when written to the same PrintWriter.
+  private String stackTraceString()
+  {
+    StringBuffer sb = new StringBuffer();
+
+    // Main stacktrace
     StackTraceElement[] stack = getStackTrace();
-    if (stack == null || stack.length == 0)
-      {
-	pw.println("   <<No stacktrace available>>");
-	return;
-      }
-    else
-      {
-	for (int i = 0; i < stack.length; i++)
-	  pw.println("   at " + stack[i]);
-      }
+    stackTraceStringBuffer(sb, this.toString(), stack, 0);
 
     // The cause(s)
     Throwable cause = getCause();
     while (cause != null)
       {
-        // Cause first line
-        pw.println("Caused by: " + cause);
+	// Cause start first line
+        sb.append("Caused by: ");
 
         // Cause stacktrace
         StackTraceElement[] parentStack = stack;
         stack = cause.getStackTrace();
-	if (stack == null || stack.length == 0)
-	  {
-	    pw.println("   <<No stacktrace available>>");
-	  }
-	else if (parentStack == null || parentStack.length == 0)
-	  {
-	    for (int i = 0; i < stack.length; i++)
-	      pw.println("   at " + stack[i]);
-	  }
+	if (parentStack == null || parentStack.length == 0)
+	  stackTraceStringBuffer(sb, cause.toString(), stack, 0);
 	else
 	  {
-	    boolean equal = false; // Is rest of stack equal to parent frame?
-	    for (int i = 0; i < stack.length && ! equal; i++)
+	    int equal = 0; // Count how many of the last stack frames are equal
+	    int frame = stack.length-1;
+	    int parentFrame = parentStack.length-1;
+	    while (frame > 0 && parentFrame > 0)
 	      {
-		// Check if we already printed the rest of the stack
-		// since it was the tail of the parent stack
-		int remaining = stack.length - i;
-		int element = i;
-		int parentElement = parentStack.length - remaining;
-		equal = parentElement >= 0
-		      && parentElement < parentStack.length; // be optimistic
-		while (equal && element < stack.length)
+		if (stack[frame].equals(parentStack[parentFrame]))
 		  {
-		    if (stack[element].equals(parentStack[parentElement]))
-		      {
-			element++;
-			parentElement++;
-		      }
-		    else
-		      equal = false;
+		    equal++;
+		    frame--;
+		    parentFrame--;
 		  }
-		// Print stacktrace element or indicate the rest is equal 
-		if (! equal)
-		  pw.println("   at " + stack[i]);
 		else
-		  pw.println("   ..." + remaining + " more");
+		  break;
 	      }
+	    stackTraceStringBuffer(sb, cause.toString(), stack, equal);
 	  }
         cause = cause.getCause();
       }
+
+    return sb.toString();
+  }
+
+  // Adds to the given StringBuffer a line containing the name and
+  // all stacktrace elements minus the last equal ones.
+  private static void stackTraceStringBuffer(StringBuffer sb, String name,
+					StackTraceElement[] stack, int equal)
+  {
+    // (finish) first line
+    sb.append(name);
+    sb.append(nl);
+
+    // The stacktrace
+    if (stack == null || stack.length == 0)
+      {
+	sb.append("   <<No stacktrace available>>");
+	sb.append(nl);
+      }
+    else
+      {
+	for (int i = 0; i < stack.length-equal; i++)
+	  {
+	    sb.append("   at ");
+	    sb.append(stack[i] == null ? "<<Unknown>>" : stack[i].toString());
+	    sb.append(nl);
+	  }
+	if (equal > 0)
+	  {
+	    sb.append("   ...");
+	    sb.append(equal);
+	    sb.append(" more");
+	    sb.append(nl);
+	  }
+      }
   }
 
   /**
@@ -483,7 +494,13 @@
    * @return this same throwable
    * @see #printStackTrace()
    */
-  public native Throwable fillInStackTrace();
+  public Throwable fillInStackTrace()
+  {
+    vmState = VMThrowable.fillInStackTrace(this);
+    stackTrace = null; // Should be regenerated when used.
+
+    return this;
+  }
 
   /**
    * Provides access to the information printed in {@link #printStackTrace()}.
@@ -499,7 +516,13 @@
   public StackTraceElement[] getStackTrace()
   {
     if (stackTrace == null)
-      stackTrace = getStackTrace0();
+      if (vmState == null)
+	stackTrace = new StackTraceElement[0];
+      else 
+	{
+	  stackTrace = vmState.getStackTrace(this);
+	  vmState = null; // No longer needed
+	}
 
     return stackTrace;
   }
@@ -508,6 +531,10 @@
    * Change the stack trace manually. This method is designed for remote
    * procedure calls, which intend to alter the stack trace before or after
    * serialization according to the context of the remote call.
+   * <p>
+   * The contents of the given stacktrace is copied so changes to the
+   * original * array do not change the stack trace elements of this
+   * throwable.
    *
    * @param stackTrace the new trace to use
    * @throws NullPointerException if stackTrace is null or has null elements
@@ -515,15 +542,22 @@
    */
   public void setStackTrace(StackTraceElement[] stackTrace)
   {
-    for (int i = stackTrace.length; --i >= 0; )
+    int i = stackTrace.length;
+    StackTraceElement[] st = new StackTraceElement[i];
+
+    while (--i >= 0)
       if (stackTrace[i] == null)
-        throw new NullPointerException();
-    this.stackTrace = stackTrace;
-  }
+	throw new NullPointerException();
+      else
+	st[i] = stackTrace[i];
 
-  private native final StackTraceElement[] getStackTrace0 ();
+    this.stackTrace = st;
+  }
 
-  // Setting this flag to false prevents fillInStackTrace() from running.
-  static boolean trace_enabled = true;
-  private transient byte stackTraceBytes[];
+  /**
+   * VM state when fillInStackTrace was called.
+   * Used by getStackTrace() to get an array of StackTraceElements.
+   * Cleared when no longer needed.
+   */
+  private transient VMThrowable vmState;
 }
Index: java/lang/VMThrowable.java
===================================================================
RCS file: java/lang/VMThrowable.java
diff -N java/lang/VMThrowable.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ java/lang/VMThrowable.java	24 Aug 2002 22:05:42 -0000
@@ -0,0 +1,97 @@
+/* java.lang.VMThrowable -- VM support methods for Throwable.
+   Copyright (C) 1998, 1999, 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.lang;
+
+import gnu.gcj.runtime.NameFinder;
+
+/**
+ * VM dependant state and support methods Throwabele.
+ * It is deliberately package local and final and should only be accessed
+ * by the Throwable class.
+ * <p>
+ * This is the version used by libgcj (http://gcc.gnu.org/java/).
+ *
+ * @author Mark Wielaard (mark@klomp.org)
+ */
+final class VMThrowable
+{
+  private gnu.gcj.RawData stackTraceAddrs;
+  private int length;
+
+  /**
+   * Private contructor, create VMThrowables with fillInStackTrace();
+   */
+  private VMThrowable() { }
+
+  /**
+   * Fill in the stack trace with the current execution stack.
+   * Called by <code>Throwable.fillInStackTrace()</code> to get the state of
+   * the VM. Can return null when the VM does not support caputing the VM
+   * execution state.
+   *
+   * @return a new VMThrowable containing the current execution stack trace.
+   * @see Throwable#fillInStackTrace()
+   */
+  static native VMThrowable fillInStackTrace(Throwable t);
+
+  /**
+   * Returns an <code>StackTraceElement</code> array based on the execution
+   * state of the VM as captured by <code>fillInStackTrace</code>.
+   * Called by <code>Throwable.getStackTrace()</code>.
+   *
+   * @return a non-null but possible zero length array of StackTraceElement.
+   * @see Throwable#getStackTrace()
+   */
+  StackTraceElement[] getStackTrace(Throwable t)
+  {
+    StackTraceElement[] result;
+    if (stackTraceAddrs != null)
+      {
+	NameFinder nameFinder = new NameFinder();
+	result = nameFinder.lookup(t, stackTraceAddrs, length);
+	nameFinder.close();
+      }
+    else
+      result = new StackTraceElement[0];
+
+    return result;
+  }
+
+  // Setting this flag to false prevents fillInStackTrace() from running.
+  static boolean trace_enabled = true;
+}
Index: java/lang/natThrowable.cc
===================================================================
RCS file: java/lang/natThrowable.cc
diff -N java/lang/natThrowable.cc
--- java/lang/natThrowable.cc	12 Jul 2002 12:52:44 -0000	1.13
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,99 +0,0 @@
-// natThrowable.cc - Superclass for all exceptions.
-
-/* Copyright (C) 2000  Free Software Foundation, Inc
-
-   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.  */
-
-/**
- * @author Andrew Haley <aph@cygnus.com>
- * @date Jan 6  2000
- */
-
-#include <config.h>
-
-#include <string.h>
-
-#include <gcj/cni.h>
-#include <jvm.h>
-#include <java/lang/Object.h>
-#include <java-threads.h>
-#include <java/lang/Throwable.h>
-#include <java/lang/StackTraceElement.h>
-#include <java/io/PrintStream.h>
-#include <java/io/PrintWriter.h>
-#include <java/io/IOException.h>
-
-#include <sys/types.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <unistd.h>
-
-#ifdef HAVE_EXECINFO_H
-#include <execinfo.h>
-#endif
-
-#include <name-finder.h>
-
-/* FIXME: size of the stack trace is limited to 128 elements.  It's
-   undoubtedly sensible to limit the stack trace, but 128 is rather
-   arbitrary.  It may be better to configure this.  */
-
-java::lang::Throwable *
-java::lang::Throwable::fillInStackTrace (void)
-{
-  if (! trace_enabled)
-    return this;
-#if defined (HAVE_BACKTRACE)
-  void *p[128];
-  
-  // We subtract 1 from the number of elements because we don't want
-  // to include the call to fillInStackTrace in the trace.
-  int n = backtrace (p, 128) - 1;  
-
-  if (n > 0)
-    {
-      // We copy the array below to deal with alignment issues.
-      stackTraceBytes = JvNewByteArray (n * sizeof p[0]);
-      memcpy (elements (stackTraceBytes), p+1, (n * sizeof p[0]));
-    }
-
-#endif
-
-  return this;
-}
-
-JArray<java::lang::StackTraceElement*> *
-java::lang::Throwable::getStackTrace0 ()
-{
-#ifdef HAVE_BACKTRACE
-  if (!stackTraceBytes)
-    return NULL;
-
-  int depth = stackTraceBytes->length / sizeof (void *);
-  void *p[depth];
-  // This memcpy is esential; it ensures that the array of void* is
-  // correctly aligned.
-  memcpy (p, elements (stackTraceBytes), sizeof p);
-
-  JArray<java::lang::StackTraceElement*> *result;
-  java::lang::StackTraceElement** el;
-  result = reinterpret_cast <JArray<java::lang::StackTraceElement *>*>
-    (JvNewObjectArray (depth, &java::lang::StackTraceElement::class$, NULL));
-  el = elements (result);
-
-  _Jv_name_finder finder (_Jv_ThisExecutable ());
-
-  for (int i = 0; i < depth; i++)
-    el[i] = finder.lookup (p[i]);
-
-  return result;
-#else
-  return NULL;
-#endif /* HAVE_BACKTRACE */
-}
Index: java/lang/natVMThrowable.cc
===================================================================
RCS file: java/lang/natVMThrowable.cc
diff -N java/lang/natVMThrowable.cc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ java/lang/natVMThrowable.cc	24 Aug 2002 22:05:42 -0000
@@ -0,0 +1,73 @@
+// natVMThrowable.cc - native helper methods for Throwable
+
+/* Copyright (C) 2000, 2002  Free Software Foundation, Inc
+
+   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.  */
+
+/**
+ * @author Andrew Haley <aph@cygnus.com>
+ * @author Mark Wielaard <mark@klomp.org>
+ *
+ * Native helper methods for VM specific Throwable support.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <jvm.h>
+#include <gcj/cni.h>
+#include <gnu/gcj/RawData.h>
+#include <java/lang/Object.h>
+#include <java-threads.h>
+#include <java/lang/Throwable.h>
+#include <java/lang/VMThrowable.h>
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+
+#include <unistd.h>
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+/* FIXME: size of the stack trace is limited to 128 elements.  It's
+   undoubtedly sensible to limit the stack trace, but 128 is rather
+   arbitrary.  It may be better to configure this.  */
+
+java::lang::VMThrowable *
+java::lang::VMThrowable::fillInStackTrace (java::lang::Throwable* t)
+{
+  if (! trace_enabled)
+    return NULL;
+#if defined (HAVE_BACKTRACE)
+  VMThrowable* state = new VMThrowable;
+  void *p[128];
+  
+  // We subtract 1 from the number of elements because we don't want
+  // to include the calls to fillInStackTrace in the trace.
+  int n = backtrace (p, 128) - 1;  
+
+  void **addrs;
+  if (n > 0)
+    {
+      state->length = n;
+      addrs = (void **) _Jv_Malloc (n * sizeof p[0]);
+      while (n--)
+	addrs[n] = p[n];
+    }
+  else
+    addrs = NULL;
+
+  state->stackTraceAddrs = reinterpret_cast<gnu::gcj::RawData *> (addrs);
+
+  return state;
+#endif
+  return NULL;
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]