2000-01-17 16:45:24 +01:00
|
|
|
// name-finder.cc - Convert addresses to names
|
|
|
|
|
2002-03-23 17:14:28 +01:00
|
|
|
/* Copyright (C) 2000, 2002 Free Software Foundation, Inc
|
2000-01-17 16:45:24 +01:00
|
|
|
|
|
|
|
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 Andrew Haley <aph@cygnus.com>
|
|
|
|
* @date Jan 6 2000
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* _Jv_name_finder is a class wrapper around a mechanism that can
|
|
|
|
convert address of methods to their names and the names of files in
|
|
|
|
which they appear.
|
|
|
|
|
|
|
|
Right now, the only implementation of this involves running a copy
|
|
|
|
of addr2line, but at some point it is worth building this
|
|
|
|
functionality into libgcj, if only for embedded systems. */
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef _GNU_SOURCE
|
|
|
|
#define _GNU_SOURCE 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <gcj/cni.h>
|
|
|
|
#include <jvm.h>
|
|
|
|
#include <java/lang/Object.h>
|
|
|
|
#include <java-threads.h>
|
|
|
|
#include <java/lang/Throwable.h>
|
|
|
|
#include <java/io/PrintStream.h>
|
|
|
|
#include <java/io/PrintWriter.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_DLFCN_H
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <name-finder.h>
|
|
|
|
|
|
|
|
/* Create a new name finder which will perform address lookups on an
|
|
|
|
executable. */
|
|
|
|
|
|
|
|
_Jv_name_finder::_Jv_name_finder (char *executable)
|
|
|
|
{
|
|
|
|
#if defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP)
|
2002-07-12 14:52:44 +02:00
|
|
|
demangling_error = lookup_error = 0;
|
2000-01-17 16:45:24 +01:00
|
|
|
|
2002-03-23 17:14:28 +01:00
|
|
|
// Initialize file descriptors so that shutdown works properly.
|
|
|
|
f_pipe[0] = -1;
|
|
|
|
f_pipe[1] = -1;
|
|
|
|
b_pipe[0] = -1;
|
|
|
|
b_pipe[1] = -1;
|
|
|
|
b_pipe_fd = NULL;
|
|
|
|
|
2002-07-12 14:52:44 +02:00
|
|
|
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];
|
2000-01-17 16:45:24 +01:00
|
|
|
{
|
|
|
|
int arg = 0;
|
2000-05-19 19:55:34 +02:00
|
|
|
#ifdef __ia64__
|
|
|
|
argv[arg++] = "addr2name.awk";
|
|
|
|
#else
|
2000-01-17 16:45:24 +01:00
|
|
|
argv[arg++] = "addr2line";
|
|
|
|
argv[arg++] = "-f";
|
|
|
|
argv[arg++] = "-e";
|
2000-05-19 19:55:34 +02:00
|
|
|
#endif
|
2000-01-17 16:45:24 +01:00
|
|
|
argv[arg++] = executable;
|
|
|
|
argv[arg] = NULL;
|
|
|
|
}
|
|
|
|
|
2002-07-12 14:52:44 +02:00
|
|
|
lookup_error |= pipe (f_pipe) < 0;
|
|
|
|
lookup_error |= pipe (b_pipe) < 0;
|
2000-05-31 01:26:02 +02:00
|
|
|
|
2002-07-12 14:52:44 +02:00
|
|
|
if (lookup_error)
|
2000-01-17 16:45:24 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
pid = fork ();
|
|
|
|
if (pid == 0)
|
|
|
|
{
|
|
|
|
close (f_pipe[1]);
|
|
|
|
close (b_pipe[0]);
|
|
|
|
dup2 (f_pipe[0], fileno (stdin));
|
|
|
|
dup2 (b_pipe[1], fileno (stdout));
|
|
|
|
execvp (argv[0], argv);
|
|
|
|
_exit (127);
|
|
|
|
}
|
2000-05-31 01:26:02 +02:00
|
|
|
|
2002-03-23 17:14:28 +01:00
|
|
|
// Close child end of pipes. Set local descriptors to -1 so we
|
|
|
|
// don't try to close the fd again.
|
2000-01-17 16:45:24 +01:00
|
|
|
close (f_pipe [0]);
|
2002-03-23 17:14:28 +01:00
|
|
|
f_pipe[0] = -1;
|
2000-01-17 16:45:24 +01:00
|
|
|
close (b_pipe [1]);
|
2002-03-23 17:14:28 +01:00
|
|
|
b_pipe[1] = -1;
|
2000-05-31 01:26:02 +02:00
|
|
|
|
2000-01-17 16:45:24 +01:00
|
|
|
if (pid < 0)
|
|
|
|
{
|
2002-07-12 14:52:44 +02:00
|
|
|
lookup_error |= 1;
|
2000-01-17 16:45:24 +01:00
|
|
|
return;
|
|
|
|
}
|
2000-05-31 01:26:02 +02:00
|
|
|
|
2000-01-17 16:45:24 +01:00
|
|
|
b_pipe_fd = fdopen (b_pipe[0], "r");
|
2002-07-12 14:52:44 +02:00
|
|
|
lookup_error |= !b_pipe_fd;
|
2002-03-23 17:14:28 +01:00
|
|
|
|
2002-07-12 14:52:44 +02:00
|
|
|
if (! lookup_error)
|
2002-03-23 17:14:28 +01:00
|
|
|
{
|
|
|
|
// Don't try to close the fd twice.
|
|
|
|
b_pipe[0] = -1;
|
|
|
|
}
|
2002-07-12 14:52:44 +02:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2000-01-17 16:45:24 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert a pointer to hex. */
|
|
|
|
|
|
|
|
void
|
|
|
|
_Jv_name_finder::toHex (void *p)
|
|
|
|
{
|
2002-02-18 03:52:44 +01:00
|
|
|
typedef unsigned word_t __attribute ((mode (word)));
|
|
|
|
word_t n = (word_t) p;
|
2000-01-17 16:45:24 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-07-12 14:52:44 +02:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2000-01-17 16:45:24 +01:00
|
|
|
/* 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.
|
|
|
|
|
|
|
|
Returns false if the lookup fails. Even if this happens, the field
|
|
|
|
he will have been correctly filled in with the pointer. */
|
|
|
|
|
2002-07-12 14:52:44 +02:00
|
|
|
java::lang::StackTraceElement*
|
2000-01-17 16:45:24 +01:00
|
|
|
_Jv_name_finder::lookup (void *p)
|
|
|
|
{
|
2000-10-09 03:54:50 +02:00
|
|
|
extern char **_Jv_argv;
|
2000-01-17 16:45:24 +01:00
|
|
|
toHex (p);
|
|
|
|
|
2002-07-12 14:52:44 +02:00
|
|
|
char name[1024];
|
|
|
|
char file_name[1024];
|
|
|
|
|
|
|
|
file_name[0] = 0;
|
|
|
|
|
2000-01-18 15:42:14 +01:00
|
|
|
#if defined (HAVE_DLFCN_H) && defined (HAVE_DLADDR)
|
2000-01-17 16:45:24 +01:00
|
|
|
{
|
|
|
|
Dl_info dl_info;
|
|
|
|
|
|
|
|
if (dladdr (p, &dl_info))
|
|
|
|
{
|
2001-08-26 12:09:59 +02:00
|
|
|
if (dl_info.dli_fname)
|
|
|
|
strncpy (file_name, dl_info.dli_fname, sizeof file_name);
|
2001-10-16 09:36:12 +02:00
|
|
|
if (dl_info.dli_sname)
|
2002-07-12 14:52:44 +02:00
|
|
|
strncpy (name, dl_info.dli_sname, sizeof name);
|
2000-10-09 03:54:50 +02:00
|
|
|
|
|
|
|
/* Don't trust dladdr() if the address is from the main program. */
|
2001-08-26 12:09:59 +02:00
|
|
|
if (dl_info.dli_fname != NULL
|
2001-10-16 09:36:12 +02:00
|
|
|
&& dl_info.dli_sname != NULL
|
2001-08-26 12:09:59 +02:00
|
|
|
&& (_Jv_argv == NULL || strcmp (file_name, _Jv_argv[0]) != 0))
|
2002-07-12 14:52:44 +02:00
|
|
|
return createStackTraceElement (name, file_name);
|
2000-01-17 16:45:24 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2002-07-12 14:52:44 +02:00
|
|
|
memcpy (name, hex, strlen (hex) + 1);
|
|
|
|
|
2000-01-17 16:45:24 +01:00
|
|
|
#if defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP)
|
2002-07-12 14:52:44 +02:00
|
|
|
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;
|
2000-01-17 16:45:24 +01:00
|
|
|
|
2002-07-12 14:52:44 +02:00
|
|
|
{
|
|
|
|
char *newline = strchr (name, '\n');
|
|
|
|
if (newline)
|
|
|
|
*newline = 0;
|
|
|
|
}
|
2000-01-17 16:45:24 +01:00
|
|
|
#endif /* defined (HAVE_PIPE) && defined (HAVE_FORK) && defined (HAVE_EXECVP) */
|
2002-07-12 14:52:44 +02:00
|
|
|
|
|
|
|
fail:
|
|
|
|
return (createStackTraceElement (name, file_name));
|
2000-01-17 16:45:24 +01:00
|
|
|
}
|