This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
StackTraceElement support
- From: Mark Wielaard <mark at klomp dot org>
- To: java-patches at gcc dot gnu dot org
- Date: 06 Jul 2002 23:04:17 +0200
- Subject: StackTraceElement support
Hi,
The following patch gives us real StackTraceElement support including
printing of chained exceptions.
For this to work I needed to parse the output of addr2line and c++filt
in name-finder so I removed the CPlusPlusDemangler from Throwable.java
and added (something similar) to name-finder.cc. I must say that parsing
strings in C(++) is a challenge and I don't want to admit how long it
took to get it all right or how many seg faults I created... If someone
could go over it and see if I didn't make some stupid mistakes that
would be greatly appreciated. I also just copied most of the process
creating/piping stuff, so that also needs careful inspection. (These
things are so much easier in java!)
This gives a pure java way of printing stack traces, but we lose the hex
address of the method at the moment. Does anybody really use that
address? We could add it back by having out own subclass of
StackTraceElement that stores it and adds it to the toString() method if
needed.
2002-07-06 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.
* gcj/javaprims.h: regenerate class list.
* include/name-finder.h (lookup): 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().
OK to commit?
Cheers,
Mark
Index: name-finder.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/name-finder.cc,v
retrieving revision 1.11
diff -u -r1.11 name-finder.cc
--- name-finder.cc 23 Mar 2002 16:14:27 -0000 1.11
+++ name-finder.cc 6 Jul 2002 20:40:15 -0000
@@ -68,14 +68,21 @@
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
@@ -121,6 +128,53 @@
// 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;
+
+ error |= pipe (f2_pipe) < 0;
+ error |= pipe (b2_pipe) < 0;
+
+ if (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)
+ {
+ error |= 1;
+ return;
+ }
+
+ b2_pipe_fd = fdopen (b2_pipe[0], "r");
+ error |= !b2_pipe_fd;
+
+ if (! error)
+ {
+ // Don't try to close the fd twice.
+ b2_pipe[0] = -1;
+ }
#endif
}
@@ -144,6 +198,93 @@
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;
+
+#if defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP)
+ if (error)
+ return NULL;
+
+ error |= write (f2_pipe[1], s, strlen (s)) < 0;
+ if (error)
+ return NULL;
+ error |= write (f2_pipe[1], "\n", 1) < 0;
+ if (error)
+ return NULL;
+
+ char name[1024];
+ error |= (fgets (name, sizeof name, b2_pipe_fd) == NULL);
+ if (error)
+ return NULL;
+
+ c = strchr (name, '\n');
+ if (c)
+ *c = 0;
+ s = name;
+#endif
+
+ char *class_name;
+ char *method_name;
+ 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)
+ {
+ *c = 0;
+ if ((c-f) < (int)strlen(f))
+ line_number = atoi(c+1);
+ else
+ line_number = -1;
+ }
+ else
+ {
+ line_number = -1;
+ c = strchr (f, '\n');
+ if (c)
+ *c = 0;
+ }
+
+ return new java::lang::StackTraceElement(
+ f ? JvNewStringLatin1 (f) : NULL,
+ line_number,
+ class_name ? JvNewStringLatin1 (class_name) : NULL,
+ method_name ? JvNewStringLatin1 (method_name) : NULL,
+ 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 +292,15 @@
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];
+
#if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR)
{
Dl_info dl_info;
@@ -166,45 +310,42 @@
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
#if defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP)
if (error)
- return false;
+ return NULL;
error |= write (f_pipe[1], hex, strlen (hex)) < 0;
if (error)
- return false;
+ return NULL;
error |= write (f_pipe[1], "\n", 1) < 0;
if (error)
- return false;
+ return NULL;
- error |= (fgets (method_name, sizeof method_name, b_pipe_fd) == NULL);
+ error |= (fgets (name, sizeof name, b_pipe_fd) == NULL);
if (error)
- return false;
+ return NULL;
error |= (fgets (file_name, sizeof file_name, b_pipe_fd) == NULL);
if (error)
- return false;
+ return NULL;
- char *newline = strchr (method_name, '\n');
- if (newline)
- *newline = 0;
- newline = strchr (file_name, '\n');
+ char *newline = strchr (name, '\n');
if (newline)
*newline = 0;
- return true;
+ return (createStackTraceElement (name, file_name));
#else
- return false;
+ return NULL;
#endif /* defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP) */
}
Index: gcj/javaprims.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gcj/javaprims.h,v
retrieving revision 1.39
diff -u -r1.39 javaprims.h
--- gcj/javaprims.h 18 Jun 2002 17:06:42 -0000 1.39
+++ gcj/javaprims.h 6 Jul 2002 20:40:16 -0000
@@ -134,7 +134,6 @@
class AssertionError;
class Boolean;
class Byte;
- class CPlusPlusDemangler;
class CharSequence;
class Character;
class Character$Subset;
@@ -364,6 +363,10 @@
class JarInputStream;
class JarOutputStream;
class Manifest;
+ };
+
+ namespace regex
+ {
};
namespace zip
Index: include/name-finder.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/include/name-finder.h,v
retrieving revision 1.6
diff -u -r1.6 name-finder.h
--- include/name-finder.h 23 Mar 2002 16:14:28 -0000 1.6
+++ include/name-finder.h 6 Jul 2002 20:40:16 -0000
@@ -29,6 +29,8 @@
#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 @@
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,24 +76,20 @@
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;
+ 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 error;
// Close a descriptor only if it has not been closed.
Index: java/lang/StackTraceElement.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/StackTraceElement.java,v
retrieving revision 1.1
diff -u -r1.1 StackTraceElement.java
--- java/lang/StackTraceElement.java 24 May 2002 10:40:43 -0000 1.1
+++ java/lang/StackTraceElement.java 6 Jul 2002 20:40:17 -0000
@@ -191,7 +191,7 @@
}
if (methodName != null)
sb.append(methodName);
- sb.append('(');
+ sb.append(" (");
if (fileName != null)
sb.append(fileName);
else
Index: java/lang/Throwable.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/Throwable.java,v
retrieving revision 1.11
diff -u -r1.11 Throwable.java
--- java/lang/Throwable.java 24 May 2002 10:40:42 -0000 1.11
+++ java/lang/Throwable.java 6 Jul 2002 20:40:17 -0000
@@ -57,76 +57,6 @@
* 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 @@
* 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 @@
}
/**
- * 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
+ * "<<No stacktrace available>>".
+ * <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
{
- printRawStackTrace (wr);
+ for (int i = 0; i < stack.length; i++)
+ pw.println(" at " + stack[i]);
+ }
+
+ // The cause(s)
+ Throwable cause = getCause();
+ while (cause != null)
+ {
+ // 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 StackTraceElement[] getStackTrace()
{
+ if (stackTrace == null)
+ stackTrace = getStackTrace0();
+
return stackTrace;
}
@@ -513,8 +521,8 @@
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: java/lang/natThrowable.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natThrowable.cc,v
retrieving revision 1.12
diff -u -r1.12 natThrowable.cc
--- java/lang/natThrowable.cc 24 May 2002 22:31:23 -0000 1.12
+++ java/lang/natThrowable.cc 6 Jul 2002 20:40:17 -0000
@@ -22,6 +22,7 @@
#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,30 @@
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];
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 ();
}