Bug 5773 - integrate addr2line into libjava instead of using an external process.
Summary: integrate addr2line into libjava instead of using an external process.
Status: RESOLVED WONTFIX
Alias: None
Product: gcc
Classification: Unclassified
Component: libgcj (show other bugs)
Version: 3.1
: P3 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on: 1907 12740
Blocks:
  Show dependency treegraph
 
Reported: 2002-02-24 17:26 UTC by toddastock
Modified: 2016-09-30 22:49 UTC (History)
5 users (show)

See Also:
Host: i686-pc-linux-gnu
Target: i686-pc-linux-gnu
Build: i686-pc-linux-gnu
Known to work:
Known to fail:
Last reconfirmed: 2005-09-10 19:31:36


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description toddastock 2002-02-24 17:26:01 UTC
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 );
  }
Comment 1 toddastock 2002-02-24 17:26:01 UTC
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 ();
 }
Comment 2 toddastock 2002-02-25 11:30:28 UTC
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
 

Comment 3 toddastock 2002-02-25 16:15:49 UTC
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
 

Comment 4 Bryce McKinlay 2002-03-17 13:18:37 UTC
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
Comment 5 Andrew Pinski 2003-05-23 22:14:36 UTC
This is still a nice thing to do.
Comment 6 Andrew Pinski 2003-05-24 00:43:10 UTC
confirmed on mainline (20030523).
Comment 7 Andrew Pinski 2003-05-28 01:02:08 UTC
I think bug 1907 is related.
Comment 8 Andrew Pinski 2016-09-30 22:49:34 UTC
Closing as won't fix as libgcj (and the java front-end) has been removed from the trunk.