I was having problems with the fork within a thread. So I took the code from binutils and created an integrated function method and name lookup. Release: 3.1 20020224 (experimental) Environment: System: Linux escher 2.4.9-21 #1 Thu Jan 17 14:16:30 EST 2002 i686 unknown Architecture: i686 host: i686-pc-linux-gnu build: i686-pc-linux-gnu target: i686-pc-linux-gnu configured with: ./configure --enable-threads=posix --prefix=/home/tstock/local --disable-shared --enable-languages=c++,java : (reconfigured) How-To-Repeat: It's less of a bug and more of an annoyance. The following code is not working consistantly on my machine and spurred the changes: #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <pthread.h> extern char **environ; void *func( void * arg ) { pid_t pid; int inp[2], outp[2], errp[2], msgp[2]; char **env = NULL; if (pipe (inp)) exit (1); if (pipe (outp)) exit (1); if (pipe (errp)) exit (1); if (pipe (msgp)) exit (1); if (fcntl (msgp[1], F_SETFD, FD_CLOEXEC)) exit (1); if ((pid = fork ()) == -1) exit (2); if (pid == 0) { // Use close and not myclose -- we're in the child, and we // aren't worried about the possible race condition. close (inp[0]); // close (inp[1]); close (errp[0]); // close (errp[1]); // close (outp[0]); close (outp[1]); close (msgp[0]); char *args[4]; args[0] = "c++filt"; args[1] = "-s"; args[2] = "gnu"; args[3] = NULL; char *path_val = getenv ("PATH"); environ = env; if (getenv ("PATH") == NULL) { char *path_env = (char *) malloc (strlen (path_val) + 5 + 1); strcpy (path_env, "PATH="); strcat (path_env, "."); putenv (path_env); } // We ignore errors from dup2 because they should never occur. dup2 (outp[0], 0); dup2 (inp[1], 1); dup2 (errp[1], 2); execvp (args[0], args); char c = errno; // Send the parent notification that the exec failed. write (msgp[1], &c, 1); _exit(127); } // Parent. Close extra file descriptors and mark ours as // close-on-exec. close (outp[0]); close (inp[1]); close (errp[1]); close (msgp[1]); char c; int r = read (msgp[0], &c, 1); if (r == -1) exit (3); else if (r != 0) exit (3); close (msgp[0]); fcntl (outp[1], F_SETFD, 1); fcntl (inp[0], F_SETFD, 1); fcntl (errp[0], F_SETFD, 1); return NULL; } int main (int argc, char **argv) { pthread_t th; pthread_attr_t tha; pthread_attr_init( &tha ); pthread_create( &th, &tha, func, NULL ); }
Fix: I wrote an addr2line class to deal with the name conversion; code from binutils. I also modifed the make file and stuff. I do an external check for the libbfd library to get the bfd routines. If they don't exist then we fall back on the old method. Added programs: -------------------------------------------------------------------------------- // addr2line.cc - Convert addresses to names /* 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 Todd Stock <toddastock@yahoo.com> * @date Feb 24 2002 */ #include <config.h> #ifdef HAVE_LIBBFD #include <jvm.h> #include <gcj/cni.h> #include <addr2line.h> #include <stdio.h> #include <stdlib.h> extern "C" { #include "demangle.h" } void free(void *); static void addr2LineErrorHandler (const char *s, ...) { } Addr2Line::Addr2Line (char *executable) { char **matching; abfd = NULL; bfd_init (); bfd_set_error_handler( addr2LineErrorHandler ); const char *target = TARGET; if (! bfd_set_default_target (target)) { fprintf( stderr, "can't set BFD default target to `%s': %s", target, bfd_errmsg (bfd_get_error ())); return; } abfd = bfd_openr (executable, NULL); if (abfd == NULL) { printErr( executable ); return; } if (bfd_check_format (abfd, bfd_archive)) { fprintf( stderr, "%s: can not get addresses from archive", executable ); bfd_close (abfd); abfd = NULL; return; } if (! bfd_check_format_matches (abfd, bfd_object, &matching)) { printErr ( bfd_get_filename (abfd) ); if (bfd_get_error () == bfd_error_file_ambiguously_recognized) { // list_matching_formats (matching); free(matching); } bfd_close (abfd); abfd = NULL; return; } long storage; long symcount; if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0) { bfd_close (abfd); abfd = NULL; return; } storage = bfd_get_symtab_upper_bound (abfd); if (storage < 0) { printErr ( bfd_get_filename (abfd) ); bfd_close (abfd); abfd = NULL; return; } syms = (asymbol **) _Jv_Malloc(storage); symcount = bfd_canonicalize_symtab (abfd, syms); if (symcount < 0) { printErr ( bfd_get_filename (abfd) ); bfd_close (abfd); abfd = NULL; _Jv_Free (syms); syms = NULL; return; } needFree = found = false; methodName = "??"; fileName = NULL; line = 0; } Addr2Line::~Addr2Line () { if (syms != NULL) { _Jv_Free (syms); syms = NULL; } if (abfd != NULL) { bfd_close (abfd); } } void find_address_in_section ( bfd *abfd, asection *section, void *data) { bfd_vma vma; bfd_size_type size; Addr2Line *x = (Addr2Line *)data; if (x->found) return; if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0) return; vma = bfd_get_section_vma (abfd, section); if (x->pc < vma) return; size = bfd_get_section_size_before_reloc (section); if (x->pc >= vma + size) return; x->found = bfd_find_nearest_line (abfd, section, x->syms, x->pc - vma, &x->fileName, &x->_methodName, (unsigned int *)&x->line); } bool Addr2Line::lookup (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; return _lookup( hex ); } bool Addr2Line::_lookup( char *addr_hex ) { if( needFree ) { free( methodName ); } needFree = false; if (addr_hex == NULL) return false; pc = bfd_scan_vma( addr_hex, NULL, 16 ); found = false; methodName = "??"; fileName = NULL; line = 0; if (abfd == NULL) return false; bfd_map_over_sections( abfd, find_address_in_section, this ); if( found ) { if( _methodName == NULL ) { methodName = "??"; } else { needFree = true; char *res = cplus_demangle( _methodName, DMGL_ANSI | DMGL_PARAMS ); if (res == NULL) { methodName = strdup(_methodName); } else { methodName = res; } } return true; } return false; } void Addr2Line::printErr( char *string ) { const char *errmsg = bfd_errmsg (bfd_get_error ()); if (string) fprintf (stderr, "Addr2Line: %s: %s\n", string, errmsg); else fprintf (stderr, "Addr2Line: %s\n", errmsg); } #endif -------------------------------------------------------------------------------- // addr2line.h - Convert addresses to names (header) /* 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 Todd Stock <toddastock@yahoo.com> * @date Feb 24 2002 */ #ifndef ADDR2LINE #define ADDR2LINE #include <bfd.h> class Addr2Line { public: Addr2Line (char *); ~Addr2Line (); void *operator new (size_t bytes) { return _Jv_Malloc (bytes); } void operator delete (void *mem) { _Jv_Free (mem); } bool lookup (void *); char *methodName; const char *fileName; jlong line; char hex[sizeof (void *) * 2 + 5]; private: friend void find_address_in_section (bfd *, asection *, void *); bool _lookup (char *); void printErr (char *); const char *_methodName; bfd *abfd; asymbol **syms; bfd_vma pc; bool found; bool needFree; }; #endif -------------------------------------------------------------------------------- // CPlusPlusDemangler.java /* Copyright (C) 1998, 1999 Free Software Foundation 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 java.lang; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Serializable; import java.io.OutputStreamWriter; import java.io.OutputStream; import java.io.FilterOutputStream; import java.io.IOException; /** * @author Tom Tromey <tromey@cygnus.com> * @date October 30, 1998 * modified by Todd Stock on Feb 24, 2002 */ /* 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 * 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 { 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; _init( ); } public native void _init (); public native void write (int b) throws IOException; public void close () throws IOException { p.flush (); } } -------------------------------------------------------------------------------- Changes to programs: + cvs -z9 -q diff -u Makefile.am acconfig.h configure.in prims.cc include/config.h.in java/lang/Throwable.java java/lang/natThrowable.cc Index: Makefile.am =================================================================== RCS file: /cvsroot/gcc/gcc/libjava/Makefile.am,v retrieving revision 1.201 diff -u -r1.201 Makefile.am --- Makefile.am 22 Feb 2002 03:21:26 -0000 1.201 +++ Makefile.am 25 Feb 2002 01:01:14 -0000 @@ -82,6 +82,7 @@ JAVAC = $(GCJ_WITH_FLAGS) -C GCC_UNWIND_INCLUDE = @GCC_UNWIND_INCLUDE@ +GCC_TOP_INCLUDE = @GCC_TOP_INCLUDE@ WARNINGS = -W -Wall ## We need _GNU_SOURCE defined for some Linux builds. It doesn't hurt @@ -104,7 +105,7 @@ INCLUDES = -I$(top_srcdir) -Iinclude -I$(top_srcdir)/include \ $(GCINCS) $(THREADINCS) $(INCLTDL) \ - $(GCC_UNWIND_INCLUDE) $(ZINCS) $(LIBFFIINCS) + $(GCC_UNWIND_INCLUDE) $(GCC_TOP_INCLUDE) $(ZINCS) $(LIBFFIINCS) ## ################################################################ @@ -125,7 +126,7 @@ libgcj_la_SOURCES = prims.cc jni.cc exception.cc \ resolve.cc defineclass.cc interpret.cc name-finder.cc verify.cc \ - $(nat_source_files) + $(nat_source_files) addr2line.cc EXTRA_libgcj_la_SOURCES = boehm.cc nogc.cc posix-threads.cc no-threads.cc \ win32-threads.cc posix.cc win32.cc \ $(c_source_files) $(java_source_files) $(built_java_source_files) @@ -1111,6 +1112,7 @@ java/lang/ThreadDeath.java \ java/lang/ThreadGroup.java \ java/lang/ThreadLocal.java \ +java/lang/CPlusPlusDemangler.java \ java/lang/Throwable.java \ java/lang/UnknownError.java \ java/lang/UnsatisfiedLinkError.java \ Index: acconfig.h =================================================================== RCS file: /cvsroot/gcc/gcc/libjava/acconfig.h,v retrieving revision 1.22 diff -u -r1.22 acconfig.h --- acconfig.h 7 Feb 2002 03:24:08 -0000 1.22 +++ acconfig.h 25 Feb 2002 01:01:14 -0000 @@ -152,3 +152,6 @@ /* Define if you are using JVMPI. */ #undef ENABLE_JVMPI + +/* has -lbfd */ +#undef HAVE_LIBBFD Index: configure.in =================================================================== RCS file: /cvsroot/gcc/gcc/libjava/configure.in,v retrieving revision 1.114 diff -u -r1.114 configure.in --- configure.in 12 Feb 2002 05:52:30 -0000 1.114 +++ configure.in 25 Feb 2002 01:01:16 -0000 @@ -13,6 +13,7 @@ LIBGCJ_CONFIGURE(.) AM_CONFIG_HEADER(include/config.h gcj/libgcj-config.h) +AC_DEFINE_UNQUOTED(TARGET, "${target}", [Configured target name.]) # Only use libltdl for native builds. if test -z "${with_cross_host}"; then @@ -325,6 +326,8 @@ AC_SUBST(GCTESTSPEC) AC_LINK_FILES(include/$GCHDR, include/java-gc.h) +LIBERTYLIBS="-L\$(here)/../libiberty -liberty" +AC_SUBST(LIBERTYLIBS) AC_MSG_CHECKING([for thread model used by GCC]) THREADS=`$CC -v 2>&1 | sed -n 's/^Thread model: //p'` @@ -439,6 +442,7 @@ # We're in the tree with gcc, and need to include some of its headers. GCC_UNWIND_INCLUDE='-I$(libgcj_basedir)/../gcc' +GCC_TOP_INCLUDE='-I$(libgcj_basedir)/../include' if test -n "${with_cross_host}"; then # We are being configured with a cross compiler. AC_REPLACE_FUNCS @@ -468,6 +472,7 @@ if test x"$build" != x"$with_cross_host" && x"$build" != x"$target"; then CANADIAN=yes GCC_UNWIND_INCLUDE= + GCC_TOP_INCLUDE= GCJ="${target_alias}-gcj" fi NATIVE=no @@ -670,6 +675,8 @@ if test "$GC" = boehm; then AC_CHECK_LIB(dl, main, SYSTEMSPEC="$SYSTEMSPEC -ldl") fi +# AC_CHECK_LIB(iberty, main) + AC_CHECK_LIB(bfd, main,[AC_DEFINE(HAVE_LIBBFD) LIBS="$LIBS -lbfd $LIBERTYLIBS"],,$LIBERTYLIBS) if test -z "${with_multisubdir}"; then builddotdot=. @@ -742,6 +749,7 @@ AM_CONDITIONAL(USE_LIBDIR, test -z "$with_cross_host") AM_CONDITIONAL(NEEDS_DATA_START, test "$NEEDS_DATA_START" = yes && test "$NATIVE" = yes) AC_SUBST(GCC_UNWIND_INCLUDE) +AC_SUBST(GCC_TOP_INCLUDE) # Determine gcj version number. changequote(<<,>>) Index: prims.cc =================================================================== RCS file: /cvsroot/gcc/gcc/libjava/prims.cc,v retrieving revision 1.71 diff -u -r1.71 prims.cc --- prims.cc 7 Feb 2002 18:59:50 -0000 1.71 +++ prims.cc 25 Feb 2002 01:01:19 -0000 @@ -39,6 +39,9 @@ #define PROCESS_GCJ_PROPERTIES #endif // DISABLE_GETENV_PROPERTIES +#ifdef HAVE_LIBBFD +#include <addr2line.h> +#endif #include <java/lang/Class.h> #include <java/lang/ClassLoader.h> #include <java/lang/Runtime.h> @@ -92,6 +95,11 @@ const char **_Jv_argv; int _Jv_argc; +#ifdef HAVE_LIBBFD +// Name finder code. +Addr2Line *addr2Line = NULL; +#endif + #ifdef ENABLE_JVMPI // Pointer to JVMPI notification functions. void (*_Jv_JVMPI_Notify_OBJECT_ALLOC) (JVMPI_Event *event); @@ -959,6 +967,9 @@ java::lang::Runtime *runtime = NULL; +#ifdef HAVE_LIBBFD + addr2Line = NULL; +#endif #ifdef DISABLE_MAIN_ARGS _Jv_ThisExecutable ("[Embedded App]"); @@ -967,8 +978,14 @@ char exec_name[20]; sprintf (exec_name, "/proc/%d/exe", getpid ()); _Jv_ThisExecutable (exec_name); +#ifdef HAVE_LIBBFD + addr2Line = new Addr2Line( exec_name ); +#endif #else _Jv_ThisExecutable (argv[0]); +#ifdef HAVE_LIBBFD + addr2Line = new Addr2Line( argv[0] ); +#endif #endif /* HAVE_PROC_SELF_EXE */ #endif /* DISABLE_MAIN_ARGS */ Index: include/config.h.in =================================================================== RCS file: /cvsroot/gcc/gcc/libjava/include/config.h.in,v retrieving revision 1.38 diff -u -r1.38 config.h.in --- include/config.h.in 7 Feb 2002 19:25:28 -0000 1.38 +++ include/config.h.in 25 Feb 2002 01:01:20 -0000 @@ -176,6 +176,9 @@ /* Define if you are using JVMPI. */ #undef ENABLE_JVMPI +/* has -lbfd */ +#undef HAVE_LIBBFD + /* Define if you have the access function. */ #undef HAVE_ACCESS @@ -382,6 +385,9 @@ /* Version number of package */ #undef VERSION + +/* Configured target name. */ +#undef TARGET /* Define if the compiler is configured for setjmp/longjmp exceptions. */ #undef SJLJ_EXCEPTIONS Index: java/lang/Throwable.java =================================================================== RCS file: /cvsroot/gcc/gcc/libjava/java/lang/Throwable.java,v retrieving revision 1.10 diff -u -r1.10 Throwable.java --- java/lang/Throwable.java 24 Feb 2001 03:52:49 -0000 1.10 +++ java/lang/Throwable.java 25 Feb 2002 01:01:20 -0000 @@ -28,76 +28,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 _) - { - } - } -} - public class Throwable implements Serializable { public native Throwable fillInStackTrace (); Index: java/lang/natThrowable.cc =================================================================== RCS file: /cvsroot/gcc/gcc/libjava/java/lang/natThrowable.cc,v retrieving revision 1.11 diff -u -r1.11 natThrowable.cc --- java/lang/natThrowable.cc 7 Feb 2002 19:26:06 -0000 1.11 +++ java/lang/natThrowable.cc 25 Feb 2002 01:01:20 -0000 @@ -21,7 +21,9 @@ #include <jvm.h> #include <java/lang/Object.h> #include <java-threads.h> +#include <java/lang/CPlusPlusDemangler.h> #include <java/lang/Throwable.h> +#include <java/lang/StringBuffer.h> #include <java/io/PrintStream.h> #include <java/io/PrintWriter.h> #include <java/io/IOException.h> @@ -30,6 +32,17 @@ #include <stdlib.h> #include <stdio.h> +#include <ctype.h> +#include <libiberty.h> +extern "C" { +#include <demangle.h> +} + +/* +// Declared static in liberty so we can't use.. +extern const char * +standard_symbol_characters PARAMS ((void)); +*/ #include <unistd.h> @@ -39,6 +52,87 @@ #include <name-finder.h> +#ifdef HAVE_LIBBFD +#include <addr2line.h> +extern Addr2Line *addr2Line; +#endif + +#define MBUF_SIZE 32767 +char mbuffer[MBUF_SIZE]; +int mbufferpos = 0; +static int flags = DMGL_PARAMS | DMGL_ANSI; +static enum demangling_styles style = auto_demangling; + +void +java::lang::CPlusPlusDemangler::_init () + { + style = cplus_demangle_name_to_style ("java"); + if( style == unknown_demangling ) + style = auto_demangling; + cplus_demangle_set_style(style); + } + +void +java::lang::CPlusPlusDemangler::write (jint c) + { + + if( c != EOF && ((isascii (c) && isalnum(c)) || strchr(/*standard_symbol_characters ()*/ "_$.", c)) ) + { + if( mbufferpos < MBUF_SIZE-1 ) + { + mbuffer[mbufferpos++] = c; + return; + } + } + if( mbufferpos > 0 ) + { + int skip_first = 0; + + if (mbuffer[0] == '.' || mbuffer[0] == '$') + ++skip_first; + if (/*strip_underscore &&*/ mbuffer[skip_first] == '_') + ++skip_first; + + if (skip_first > mbufferpos) + skip_first = mbufferpos; + + mbuffer[mbufferpos] = 0; + + flags |= (int) style; + char * result = cplus_demangle (mbuffer + skip_first, flags); + if (result) + { + if (mbuffer[0] == '.') + { + p->write ((jchar)'.'); + written++; + } + for( unsigned int j=0; j<strlen(result); j++ ) + { + p->write ((jchar)result[j]); + written++; + } + free (result); + } + else + { + for( unsigned int j=0; j<strlen(mbuffer); j++ ) + { + p->write ((jchar)mbuffer[j]); + written++; + } + } + + p->flush(); + mbufferpos = 0; + } + if (c == EOF) + return; + p->write ((jchar)c); + written++; + p->flush(); + } + /* 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. */ @@ -79,6 +173,28 @@ void *p[depth]; memcpy (p, elements (stackTrace), sizeof p); +#if defined(HAVE_LIBBFD) && !defined(DISABLE_MAIN_ARGS) + for (int i = 0; i < depth; i++) + { + bool found = addr2Line->lookup (p[i]); + wr->print (JvNewStringLatin1 (" at ")); + wr->print (JvNewStringLatin1 (addr2Line->hex)); + if (found) + { + wr->print (JvNewStringLatin1 (": ")); + wr->print (JvNewStringLatin1 (addr2Line->methodName)); + if (addr2Line->fileName) + { + wr->print (JvNewStringLatin1 (" (")); + wr->print (JvNewStringLatin1 (addr2Line->fileName)); + wr->print (JvNewStringLatin1 (":")); + wr->print (addr2Line->line); + wr->print (JvNewStringLatin1 (")")); + } + } + wr->println (); + } +#else _Jv_name_finder finder (_Jv_ThisExecutable ()); for (int i = 0; i < depth; i++) @@ -99,6 +215,7 @@ } wr->println (); } +#endif #endif /* HAVE_BACKTRACE */ wr->flush (); }
From: Todd Stock <toddastock@yahoo.com> To: toddastock@yahoo.com Cc: gcc-gnats@gcc.gnu.org Subject: Re: libgcj/5773: integrate addr2line and c++filt into libjava instead of external process Date: Mon, 25 Feb 2002 11:30:28 -0800 This is a multi-part message in MIME format. --------------050005020800040500010404 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit I forgot to add the code when checking for dynamic loaded objects. -Todd --------------050005020800040500010404 Content-Type: text/plain; name="addr2line.cc" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="addr2line.cc" // addr2line.cc - Convert addresses to names /* 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 Todd Stock <toddastock@yahoo.com> * @date Feb 24 2002 */ #include <config.h> #ifdef HAVE_LIBBFD #include <jvm.h> #include <gcj/cni.h> #include <addr2line.h> #include <stdio.h> #include <stdlib.h> extern "C" { #include "demangle.h" } #ifdef HAVE_DLFCN_H #include <dlfcn.h> #endif void free(void *); static void addr2LineErrorHandler (const char *s, ...) { } Addr2Line::Addr2Line (char *executable) { char **matching; abfd = NULL; bfd_init (); bfd_set_error_handler( addr2LineErrorHandler ); const char *target = TARGET; if (! bfd_set_default_target (target)) { fprintf( stderr, "can't set BFD default target to `%s': %s", target, bfd_errmsg (bfd_get_error ())); return; } abfd = bfd_openr (executable, NULL); if (abfd == NULL) { printErr( executable ); return; } if (bfd_check_format (abfd, bfd_archive)) { fprintf( stderr, "%s: can not get addresses from archive", executable ); bfd_close (abfd); abfd = NULL; return; } if (! bfd_check_format_matches (abfd, bfd_object, &matching)) { printErr ( bfd_get_filename (abfd) ); if (bfd_get_error () == bfd_error_file_ambiguously_recognized) { // list_matching_formats (matching); free(matching); } bfd_close (abfd); abfd = NULL; return; } long storage; long symcount; if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0) { bfd_close (abfd); abfd = NULL; return; } storage = bfd_get_symtab_upper_bound (abfd); if (storage < 0) { printErr ( bfd_get_filename (abfd) ); bfd_close (abfd); abfd = NULL; return; } syms = (asymbol **) _Jv_Malloc(storage); symcount = bfd_canonicalize_symtab (abfd, syms); if (symcount < 0) { printErr ( bfd_get_filename (abfd) ); bfd_close (abfd); abfd = NULL; _Jv_Free (syms); syms = NULL; return; } needFree = found = false; methodName = "??"; fileName = NULL; line = 0; } Addr2Line::~Addr2Line () { if (syms != NULL) { _Jv_Free (syms); syms = NULL; } if (abfd != NULL) { bfd_close (abfd); } } void find_address_in_section ( bfd *abfd, asection *section, void *data) { bfd_vma vma; bfd_size_type size; Addr2Line *x = (Addr2Line *)data; if (x->found) return; if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0) return; vma = bfd_get_section_vma (abfd, section); if (x->pc < vma) return; size = bfd_get_section_size_before_reloc (section); if (x->pc >= vma + size) return; x->found = bfd_find_nearest_line (abfd, section, x->syms, x->pc - vma, &x->fileName, &x->_methodName, (unsigned int *)&x->line); } bool Addr2Line::lookup (void *p) { extern char **_Jv_argv; 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; #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); fileName = &file_name[0]; } if (dl_info.dli_sname) { strncpy (method_name, dl_info.dli_sname, sizeof method_name); methodName = &method_name[0]; } /* 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; } } #endif return _lookup( hex ); } bool Addr2Line::_lookup( char *addr_hex ) { if( needFree ) { free( methodName ); } needFree = false; if (addr_hex == NULL) return false; pc = bfd_scan_vma( addr_hex, NULL, 16 ); found = false; methodName = "??"; fileName = NULL; line = 0; if (abfd == NULL) return false; bfd_map_over_sections( abfd, find_address_in_section, this ); if( found ) { if( _methodName == NULL ) { methodName = "??"; } else { needFree = true; char *res = cplus_demangle( _methodName, DMGL_ANSI | DMGL_PARAMS ); if (res == NULL) { methodName = strdup(_methodName); } else { methodName = res; } } return true; } return false; } void Addr2Line::printErr( char *string ) { const char *errmsg = bfd_errmsg (bfd_get_error ()); if (string) fprintf (stderr, "Addr2Line: %s: %s\n", string, errmsg); else fprintf (stderr, "Addr2Line: %s\n", errmsg); } #endif --------------050005020800040500010404 Content-Type: text/plain; name="addr2line.h" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="addr2line.h" // addr2line.h - Convert addresses to names (header) /* 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 Todd Stock <toddastock@yahoo.com> * @date Feb 24 2002 */ #ifndef ADDR2LINE #define ADDR2LINE #include <bfd.h> class Addr2Line { public: Addr2Line (char *); ~Addr2Line (); void *operator new (size_t bytes) { return _Jv_Malloc (bytes); } void operator delete (void *mem) { _Jv_Free (mem); } bool lookup (void *); char *methodName; const char *fileName; jlong line; char hex[sizeof (void *) * 2 + 5]; private: friend void find_address_in_section (bfd *, asection *, void *); bool _lookup (char *); void printErr (char *); const char *_methodName; bfd *abfd; asymbol **syms; bfd_vma pc; bool found; bool needFree; char file_name[1024]; char method_name[1024]; }; #endif --------------050005020800040500010404-- _________________________________________________________ Do You Yahoo!? Get your free @yahoo.com address at http://mail.yahoo.com
From: Todd Stock <toddastock@yahoo.com> To: toddastock@yahoo.com Cc: gcc-gnats@gcc.gnu.org Subject: Re: libgcj/5773: integrate addr2line and c++filt into libjava instead of external process Date: Mon, 25 Feb 2002 16:15:49 -0800 This is a multi-part message in MIME format. --------------040004010405070307070109 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Small problem cplus_demangler doesn't deal with empty strings. Attached is new patch of natThrowable.cc. -Todd --------------040004010405070307070109 Content-Type: text/plain; name="out" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="out" Index: java/lang/natThrowable.cc =================================================================== RCS file: /cvsroot/gcc/gcc/libjava/java/lang/natThrowable.cc,v retrieving revision 1.11 diff -u -r1.11 natThrowable.cc --- java/lang/natThrowable.cc 7 Feb 2002 19:26:06 -0000 1.11 +++ java/lang/natThrowable.cc 26 Feb 2002 00:13:49 -0000 @@ -21,7 +21,9 @@ #include <jvm.h> #include <java/lang/Object.h> #include <java-threads.h> +#include <java/lang/CPlusPlusDemangler.h> #include <java/lang/Throwable.h> +#include <java/lang/StringBuffer.h> #include <java/io/PrintStream.h> #include <java/io/PrintWriter.h> #include <java/io/IOException.h> @@ -30,6 +32,17 @@ #include <stdlib.h> #include <stdio.h> +#include <ctype.h> +#include <libiberty.h> +extern "C" { +#include <demangle.h> +} + +/* +// Declared static in liberty so we can't use.. +extern const char * +standard_symbol_characters PARAMS ((void)); +*/ #include <unistd.h> @@ -39,6 +52,89 @@ #include <name-finder.h> +#ifdef HAVE_LIBBFD +#include <addr2line.h> +extern Addr2Line *addr2Line; +#endif + +#define MBUF_SIZE 32767 +char mbuffer[MBUF_SIZE]; +int mbufferpos = 0; +static int flags = DMGL_PARAMS | DMGL_ANSI; +static enum demangling_styles style = auto_demangling; + +void +java::lang::CPlusPlusDemangler::_init () + { + style = cplus_demangle_name_to_style ("java"); + if( style == unknown_demangling ) + style = auto_demangling; + cplus_demangle_set_style(style); + } + +void +java::lang::CPlusPlusDemangler::write (jint c) + { + + if( c != EOF && ((isascii (c) && isalnum(c)) || strchr(/*standard_symbol_characters ()*/ "_$.", c)) ) + { + if( mbufferpos < MBUF_SIZE-1 ) + { + mbuffer[mbufferpos++] = c; + return; + } + } + if( mbufferpos > 0 ) + { + int skip_first = 0; + + if (mbuffer[0] == '.' || mbuffer[0] == '$') + ++skip_first; + if (/*strip_underscore &&*/ mbuffer[skip_first] == '_') + ++skip_first; + + if (skip_first > mbufferpos) + skip_first = mbufferpos; + + mbuffer[mbufferpos] = 0; + + flags |= (int) style; + char * result = NULL; + if( skip_first < mbufferpos ) + result = cplus_demangle (mbuffer + skip_first, flags); + if (result) + { + if (mbuffer[0] == '.') + { + p->write ((jchar)'.'); + written++; + } + for( unsigned int j=0; j<strlen(result); j++ ) + { + p->write ((jchar)result[j]); + written++; + } + free (result); + } + else + { + for( unsigned int j=0; j<strlen(mbuffer); j++ ) + { + p->write ((jchar)mbuffer[j]); + written++; + } + } + + p->flush(); + mbufferpos = 0; + } + if (c == EOF) + return; + p->write ((jchar)c); + written++; + p->flush(); + } + /* 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. */ @@ -79,6 +175,28 @@ void *p[depth]; memcpy (p, elements (stackTrace), sizeof p); +#if defined(HAVE_LIBBFD) && !defined(DISABLE_MAIN_ARGS) + for (int i = 0; i < depth; i++) + { + bool found = addr2Line->lookup (p[i]); + wr->print (JvNewStringLatin1 (" at ")); + wr->print (JvNewStringLatin1 (addr2Line->hex)); + if (found) + { + wr->print (JvNewStringLatin1 (": ")); + wr->print (JvNewStringLatin1 (addr2Line->methodName)); + if (addr2Line->fileName) + { + wr->print (JvNewStringLatin1 (" (")); + wr->print (JvNewStringLatin1 (addr2Line->fileName)); + wr->print (JvNewStringLatin1 (":")); + wr->print (addr2Line->line); + wr->print (JvNewStringLatin1 (")")); + } + } + wr->println (); + } +#else _Jv_name_finder finder (_Jv_ThisExecutable ()); for (int i = 0; i < depth; i++) @@ -99,6 +217,7 @@ } wr->println (); } +#endif #endif /* HAVE_BACKTRACE */ wr->flush (); } --------------040004010405070307070109-- _________________________________________________________ Do You Yahoo!? Get your free @yahoo.com address at http://mail.yahoo.com
From: Bryce McKinlay <bryce@waitaki.otago.ac.nz> To: gcc-gnats@gcc.gnu.org, gcc-prs@gcc.gnu.org, toddastock@yahoo.com, gcc-bugs@gcc.gnu.org, nobody@gcc.gnu.org Cc: Subject: Re: libgcj/5773: integrate addr2line and c++filter into libjava instead of using an external process. Date: Sun, 17 Mar 2002 13:18:37 +1200 Unfortunatly we can't link libgcj against BFD due to legal/license issues, ie BFD is GPL ;-( We do have some other ideas about how to do stack traces when addr2line etc arn't available; see the recent discussion on java@gcc.gnu.org http://gcc.gnu.org/cgi-bin/gnatsweb.pl?cmd=view%20audit-trail&database=gcc&pr=5773
This is still a nice thing to do.
confirmed on mainline (20030523).
I think bug 1907 is related.
Closing as won't fix as libgcj (and the java front-end) has been removed from the trunk.