diff options
author | mark <mark@138bc75d-0d04-0410-961f-82ee72b054a4> | 2002-07-12 12:52:44 +0000 |
---|---|---|
committer | mark <mark@138bc75d-0d04-0410-961f-82ee72b054a4> | 2002-07-12 12:52:44 +0000 |
commit | ec6c77248c565ea404891e933f58ecd7909fa674 (patch) | |
tree | fd16f76f98f4b5e364614153e8902a370d5f36e2 /libjava | |
parent | cb39cd35980128baee5b7a01f68ed21b3f71da4b (diff) |
* 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().
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@55424 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libjava')
-rw-r--r-- | libjava/ChangeLog | 18 | ||||
-rw-r--r-- | libjava/gcj/javaprims.h | 1 | ||||
-rw-r--r-- | libjava/include/name-finder.h | 36 | ||||
-rw-r--r-- | libjava/java/lang/StackTraceElement.java | 2 | ||||
-rw-r--r-- | libjava/java/lang/Throwable.java | 180 | ||||
-rw-r--r-- | libjava/java/lang/natThrowable.cc | 39 | ||||
-rw-r--r-- | libjava/name-finder.cc | 224 |
7 files changed, 339 insertions, 161 deletions
diff --git a/libjava/ChangeLog b/libjava/ChangeLog index f57f750b8d1..204836c3aa3 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -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. diff --git a/libjava/gcj/javaprims.h b/libjava/gcj/javaprims.h index 42104f8566b..99442c5ca2a 100644 --- a/libjava/gcj/javaprims.h +++ b/libjava/gcj/javaprims.h @@ -134,7 +134,6 @@ extern "Java" class AssertionError; class Boolean; class Byte; - class CPlusPlusDemangler; class CharSequence; class Character; class Character$Subset; diff --git a/libjava/include/name-finder.h b/libjava/include/name-finder.h index 805725b4b69..67ae0587fb0 100644 --- a/libjava/include/name-finder.h +++ b/libjava/include/name-finder.h @@ -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) diff --git a/libjava/java/lang/StackTraceElement.java b/libjava/java/lang/StackTraceElement.java index 9c60ab16410..d9e8a31b724 100644 --- a/libjava/java/lang/StackTraceElement.java +++ b/libjava/java/lang/StackTraceElement.java @@ -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 diff --git a/libjava/java/lang/Throwable.java b/libjava/java/lang/Throwable.java index d5488b877e7..56c9d542a50 100644 --- a/libjava/java/lang/Throwable.java +++ b/libjava/java/lang/Throwable.java @@ -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 + * "<<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 + { + 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[]; diff --git a/libjava/java/lang/natThrowable.cc b/libjava/java/lang/natThrowable.cc index e6447086293..c2f7d1b8d8d 100644 --- a/libjava/java/lang/natThrowable.cc +++ b/libjava/java/lang/natThrowable.cc @@ -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 (); } diff --git a/libjava/name-finder.cc b/libjava/name-finder.cc index a09ff0b267e..2d383aaa250 100644 --- a/libjava/name-finder.cc +++ b/libjava/name-finder.cc @@ -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)); } |