natThrowable.cc (printRawStackTrace): removed.
authorMark Wielaard <mark@klomp.org>
Fri, 12 Jul 2002 12:52:44 +0000 (12:52 +0000)
committerMark Wielaard <mark@gcc.gnu.org>
Fri, 12 Jul 2002 12:52:44 +0000 (12:52 +0000)
* java/lang/natThrowable.cc (printRawStackTrace): removed.
(getStackTrace0): new method.
* java/lang/Throwable.java (CPlusPlusDemangler): removed.
(printStackTrace(PrintWriter)): replace with pure java implementation.
(printRawStackTrace): removed.
(getStackTrace0): new method.
* java/lang/StackTraceElement.java (toString): add extra whitespace.
* gcj/javaprims.h: regenerate class list.
* include/name-finder.h (lookup): new returns StackTraceElement*.
(method_name, file_name): fields removed.
(pid2, f2_pipe, b2_pipe, b2_pipe_fd): new fields.
(~_Jv_name_finder): close new descriptors.
* name-finder.cc(_Jv_name_finder): setup c++filt helper process.
(createStackTraceElement): new method.
(lookup): returns StackTraceElement*, uses createStackTraceElement().

From-SVN: r55424

libjava/ChangeLog
libjava/gcj/javaprims.h
libjava/include/name-finder.h
libjava/java/lang/StackTraceElement.java
libjava/java/lang/Throwable.java
libjava/java/lang/natThrowable.cc
libjava/name-finder.cc

index f57f750b8d188b539c0bace16761af468ab86cbf..204836c3aa3ccb4da4fce9761e141c943b2cbf68 100644 (file)
@@ -1,3 +1,21 @@
+2002-07-12  Mark Wielaard  <mark@klomp.org>
+
+       * java/lang/natThrowable.cc (printRawStackTrace): removed.
+       (getStackTrace0): new method.
+       * java/lang/Throwable.java (CPlusPlusDemangler): removed.
+       (printStackTrace(PrintWriter)): replace with pure java implementation.
+       (printRawStackTrace): removed.
+       (getStackTrace0): new method.
+       * java/lang/StackTraceElement.java (toString): add extra whitespace.
+       * gcj/javaprims.h: regenerate class list.
+       * include/name-finder.h (lookup): new returns StackTraceElement*.
+       (method_name, file_name): fields removed.
+       (pid2, f2_pipe, b2_pipe, b2_pipe_fd): new fields.
+       (~_Jv_name_finder): close new descriptors.
+       * name-finder.cc(_Jv_name_finder): setup c++filt helper process.
+       (createStackTraceElement): new method.
+       (lookup): returns StackTraceElement*, uses createStackTraceElement().
+
 2002-07-10  Tom Tromey  <tromey@redhat.com>
 
        * configure: Rebuilt.
index 42104f8566ba4dc339f95d802e7a10893fb10576..99442c5ca2ae009326963732b4f87e3b741b37d8 100644 (file)
@@ -134,7 +134,6 @@ extern "Java"
       class AssertionError;
       class Boolean;
       class Byte;
-      class CPlusPlusDemangler;
       class CharSequence;
       class Character;
       class Character$Subset;
index 805725b4b6968fbe121d0b6f26939fbe5a75b397..67ae0587fb017b0f48e422a7281a6d0fc0099347 100644 (file)
@@ -29,6 +29,8 @@ details.  */
 #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. */
@@ -47,12 +49,26 @@ public:
       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
     }
 
@@ -60,25 +76,21 @@ public:
    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
-   hex will have been correctly filled in with the pointer. 
+   Returns NULL if the lookup fails.  Even if this happens, the field
+   hex will have been correctly filled in with the pointer. */
 
-   The other fields are method_name and file_name, which lookup will
-   attempt to fill appropriately.  If the lookup has failed, these
-   fields contain garbage.*/
-  bool lookup (void *p);
+  java::lang::StackTraceElement* lookup (void *p);
 
-  char method_name[1024];
-  char file_name[1024];
   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;
-  int f_pipe[2], b_pipe[2];
-  FILE *b_pipe_fd;
-  int error;
+  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)
index 9c60ab16410378eda5b4f166afe742b57656475d..d9e8a31b72483778df90dde4398c7339d0685f06 100644 (file)
@@ -191,7 +191,7 @@ public class StackTraceElement implements Serializable
       }
     if (methodName != null)
       sb.append(methodName);
-    sb.append('(');
+    sb.append(" (");
     if (fileName != null)
       sb.append(fileName);
     else
index d5488b877e7fcd59cd6b9cdcd84f59cf9e736b20..56c9d542a500e014cc761693b8aa7263f4f32ff1 100644 (file)
@@ -57,76 +57,6 @@ import java.io.OutputStream;
  * bytecode not implemented.  JDK 1.1.
  */
 
-/* A CPlusPlusDemangler sits on top of a PrintWriter.  All input is
- * passed through the "c++filt" program (part of GNU binutils) which
- * demangles internal symbols to their C++ source form.
- *
- * Closing a CPlusPlusDemangler doesn't close the underlying
- * PrintWriter; it does, however close underlying process and flush
- * all its buffers, so it's possible to guarantee that after a
- * CPlusPlusDemangler has been closed no more will ever be written to
- * the underlying PrintWriter.
- *
- * FIXME: This implictly converts data from the input stream, which is
- * a stream of characters, to a stream of bytes.  We need a way of
- * handling Unicode characters in demangled identifiers.  */
-
-class CPlusPlusDemangler extends OutputStream
-{
-  java.io.OutputStream procOut;
-  java.io.InputStream procIn;
-  java.lang.Process proc;
-  PrintWriter p;
-
-  /* The number of bytes written to the underlying PrintWriter.  This
-     provides a crude but fairly portable way to determine whether or
-     not the attempt to exec c++filt worked. */  
-  public int written = 0;
-
-  CPlusPlusDemangler (PrintWriter writer) throws IOException
-  {
-    p = writer;
-    proc = Runtime.getRuntime ().exec ("c++filt -s java");
-    procOut = proc.getOutputStream ();
-    procIn = proc.getInputStream ();
-  }
-
-  public void write (int b) throws IOException
-  {
-    procOut.write (b);
-    while (procIn.available () != 0)
-      {
-       int c = procIn.read ();
-       if (c == -1)
-         break;
-       else
-         {
-           p.write (c);
-           written++;
-         }
-      }
-  }
-  
-  public void close () throws IOException
-  {
-    procOut.close ();
-    int c;
-    while ((c = procIn.read ()) != -1)
-      {
-       p.write (c);
-       written++;
-      }
-    p.flush ();
-    try
-      {
-       proc.waitFor ();
-      }
-    catch (InterruptedException _)
-      {
-      }
-  }    
-}
-
 /**
  * Throwable is the superclass of all exceptions that can be raised.
  *
@@ -219,8 +149,7 @@ public class Throwable implements Serializable
    *         no null entries
    * @since 1.4
    */
-  // XXX Don't initialize this, once fillInStackTrace() does it.
-  private StackTraceElement[] stackTrace = {};
+  private StackTraceElement[] stackTrace;
 
   /**
    * Instantiate this Throwable with an empty message. The cause remains
@@ -449,26 +378,102 @@ public class Throwable implements Serializable
   }
 
   /**
-   * Print a stack trace to the specified PrintWriter. See
-   * {@link #printStackTrace()} for the sample format.
+   * <p>Prints the exception, the detailed message and the stack trace
+   * associated with this Throwable to the given <code>PrintWriter</code>.
+   * The actual output written is implemention specific. Use the result of
+   * <code>getStackTrace()</code> when more precise information is needed.
+   *
+   * <p>This implementation first prints a line with the result of this
+   * object's <code>toString()</code> method.
+   * <br>
+   * Then for all elements given by <code>getStackTrace</code> it prints
+   * a line containing three spaces, the string "at " and the result of calling
+   * the <code>toString()</code> method on the <code>StackTraceElement</code>
+   * object. If <code>getStackTrace()</code> returns an empty array it prints
+   * a line containing three spaces and the string
+   * "&lt;&lt;No stacktrace available&gt;&gt;".
+   * <br>
+   * Then if <code>getCause()</code> doesn't return null it adds a line
+   * starting with "Caused by: " and the result of calling
+   * <code>toString()</code> on the cause.
+   * <br>
+   * Then for every cause (of a cause, etc) the stacktrace is printed the
+   * same as for the top level <code>Throwable</code> except that as soon
+   * as all the remaining stack frames of the cause are the same as the
+   * the last stack frames of the throwable that the cause is wrapped in
+   * then a line starting with three spaces and the string "... X more" is
+   * printed, where X is the number of remaining stackframes.
    *
    * @param w the PrintWriter to write the trace to
    * @since 1.1
    */
-  public void printStackTrace (PrintWriter wr)
+  public void printStackTrace (PrintWriter pw)
   {
-    try
+    // First line
+    pw.println(toString());
+
+    // The stacktrace
+    StackTraceElement[] stack = getStackTrace();
+    if (stack == null || stack.length == 0)
       {
-       CPlusPlusDemangler cPlusPlusFilter = new CPlusPlusDemangler (wr);
-       PrintWriter writer = new PrintWriter (cPlusPlusFilter);
-       printRawStackTrace (writer);
-       writer.close ();
-       if (cPlusPlusFilter.written == 0) // The demangler has failed...
-         printRawStackTrace (wr);
+       pw.println("   <<No stacktrace available>>");
+       return;
       }
-    catch (Exception e1)
+    else
+      {
+       for (int i = 0; i < stack.length; i++)
+         pw.println("   at " + stack[i]);
+      }
+
+    // The cause(s)
+    Throwable cause = getCause();
+    while (cause != null)
       {
-       printRawStackTrace (wr);
+        // Cause first line
+        pw.println("Caused by: " + cause);
+
+        // 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]);
+         }
+       else
+         {
+           boolean equal = false; // Is rest of stack equal to parent frame?
+           for (int i = 0; i < stack.length && ! equal; i++)
+             {
+               // 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[element].equals(parentStack[parentElement]))
+                     {
+                       element++;
+                       parentElement++;
+                     }
+                   else
+                     equal = false;
+                 }
+               // Print stacktrace element or indicate the rest is equal 
+               if (! equal)
+                 pw.println("   at " + stack[i]);
+               else
+                 pw.println("   ..." + remaining + " more");
+             }
+         }
+        cause = cause.getCause();
       }
   }
 
@@ -493,6 +498,9 @@ public class Throwable implements Serializable
    */
   public StackTraceElement[] getStackTrace()
   {
+    if (stackTrace == null)
+      stackTrace = getStackTrace0();
+
     return stackTrace;
   }
 
@@ -513,8 +521,8 @@ public class Throwable implements Serializable
     this.stackTrace = stackTrace;
   }
 
-  private native final void printRawStackTrace (PrintWriter wr);
-  
+  private native final StackTraceElement[] getStackTrace0 ();
+
   // Setting this flag to false prevents fillInStackTrace() from running.
   static boolean trace_enabled = true;
   private transient byte stackTraceBytes[];
index e64470862938969bfb4ae711ab235718b91e1da5..c2f7d1b8d8dc3d02371415270b3edbe72cf3d61e 100644 (file)
@@ -22,6 +22,7 @@ details.  */
 #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>
@@ -67,38 +68,32 @@ java::lang::Throwable::fillInStackTrace (void)
   return this;
 }
 
-void 
-java::lang::Throwable::printRawStackTrace (java::io::PrintWriter *wr)
+JArray<java::lang::StackTraceElement*> *
+java::lang::Throwable::getStackTrace0 ()
 {
-  wr->println (toString ());
 #ifdef HAVE_BACKTRACE
   if (!stackTraceBytes)
-    return;
+    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++)
-    {
-      bool found = finder.lookup (p[i]);
-      wr->print (JvNewStringLatin1 ("   at "));
-      wr->print (JvNewStringLatin1 (finder.hex));
-      if (found)
-       {
-         wr->print (JvNewStringLatin1 (": "));
-         wr->print (JvNewStringLatin1 (finder.method_name));
-         if (finder.file_name[0])
-           {
-             wr->print (JvNewStringLatin1 (" ("));
-             wr->print (JvNewStringLatin1 (finder.file_name));
-             wr->print (JvNewStringLatin1 (")"));
-           }
-       }
-      wr->println ();
-    }
+    el[i] = finder.lookup (p[i]);
+
+  return result;
+#else
+  return NULL;
 #endif /* HAVE_BACKTRACE */
-  wr->flush ();
 }
index a09ff0b267e6590c39d9e32b2e8a1073bb588bbb..2d383aaa2508f5c1e5c928910e21fedd0ad5c290 100644 (file)
@@ -59,7 +59,7 @@ details.  */
 _Jv_name_finder::_Jv_name_finder (char *executable)
 {
 #if defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP)
-  error = 0;
+  demangling_error = lookup_error = 0;
 
   // Initialize file descriptors so that shutdown works properly.
   f_pipe[0] = -1;
@@ -68,14 +68,21 @@ _Jv_name_finder::_Jv_name_finder (char *executable)
   b_pipe[1] = -1;
   b_pipe_fd = NULL;
 
-  char *argv[6];
+  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++] = "-C";
     argv[arg++] = "-f";
     argv[arg++] = "-e";
 #endif
@@ -83,10 +90,10 @@ _Jv_name_finder::_Jv_name_finder (char *executable)
     argv[arg] = NULL;
   }
 
-  error |= pipe (f_pipe) < 0;
-  error |= pipe (b_pipe) < 0;
+  lookup_error |= pipe (f_pipe) < 0;
+  lookup_error |= pipe (b_pipe) < 0;
 
-  if (error)
+  if (lookup_error)
     return;
 
   pid = fork ();
@@ -109,18 +116,65 @@ _Jv_name_finder::_Jv_name_finder (char *executable)
 
   if (pid < 0)
     {
-      error |= 1; 
+      lookup_error |= 1; 
       return;
     }
 
   b_pipe_fd = fdopen (b_pipe[0], "r");
-  error |= !b_pipe_fd;
+  lookup_error |= !b_pipe_fd;
 
-  if (! error)
+  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
 }
 
@@ -144,6 +198,94 @@ _Jv_name_finder::toHex (void *p)
   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.
@@ -151,12 +293,17 @@ _Jv_name_finder::toHex (void *p)
    Returns false if the lookup fails.  Even if this happens, the field
    he will have been correctly filled in with the pointer.  */
 
-bool
+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;
@@ -166,45 +313,44 @@ _Jv_name_finder::lookup (void *p)
         if (dl_info.dli_fname)
          strncpy (file_name, dl_info.dli_fname, sizeof file_name);
        if (dl_info.dli_sname)
-         strncpy (method_name, dl_info.dli_sname, sizeof method_name);
+         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 true;
+         return createStackTraceElement (name, file_name);
       }
   }
 #endif
 
+  memcpy (name, hex, strlen (hex) + 1);
+
 #if defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP)
-  if (error)
-    return false;
-
-  error |= write (f_pipe[1], hex, strlen (hex)) < 0;
-  if (error)
-    return false;
-  error |= write (f_pipe[1], "\n", 1) < 0;
-  if (error)
-    return false;
-
-  error |= (fgets (method_name, sizeof method_name, b_pipe_fd) == NULL);
-  if (error)
-    return false;
-  error |= (fgets (file_name, sizeof file_name, b_pipe_fd) == NULL);
-  if (error)
-    return false;
-
-  char *newline = strchr (method_name, '\n');
-  if (newline)
-    *newline = 0;
-  newline = strchr (file_name, '\n');
-  if (newline)
-    *newline = 0;
-
-  return true;
+  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;
 
-#else
-  return false;
+  {
+    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));
 }