natThrowable.cc (printRawStackTrace): removed.

* 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
This commit is contained in:
Mark Wielaard 2002-07-12 12:52:44 +00:00 committed by Mark Wielaard
parent 26af4041b7
commit dc7b1dda60
7 changed files with 337 additions and 159 deletions

View 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.

View File

@ -134,7 +134,6 @@ extern "Java"
class AssertionError;
class Boolean;
class Byte;
class CPlusPlusDemangler;
class CharSequence;
class Character;
class Character$Subset;

View 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)

View 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

View 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
{
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 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[];

View 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 ();
}

View 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;
if (lookup_error)
goto fail;
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;
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;
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;
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 (method_name, '\n');
if (newline)
*newline = 0;
newline = strchr (file_name, '\n');
if (newline)
*newline = 0;
return true;
#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));
}