mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
4c8d52aa80
client/mysql.cc: few harmless warnings from automated code-checking tools fixed cleanup
2746 lines
61 KiB
C
2746 lines
61 KiB
C
/*
|
||
pstack.c -- asynchronous stack trace of a running process
|
||
Copyright (c) 1999 Ross Thompson
|
||
Author: Ross Thompson <ross@whatsis.com>
|
||
Critical bug fix: Tim Waugh
|
||
*/
|
||
|
||
/*
|
||
This file is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 2 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to the Free Software
|
||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
*/
|
||
|
||
/* RESTRICTIONS:
|
||
|
||
pstack currently works only on Linux, only on an x86 machine running
|
||
32 bit ELF binaries (64 bit not supported). Also, for symbolic
|
||
information, you need to use a GNU compiler to generate your
|
||
program, and you can't strip symbols from the binaries. For thread
|
||
information to be dumped, you have to use the debug-aware version
|
||
of libpthread.so. (To check, run 'nm' on your libpthread.so, and
|
||
make sure that the symbol "__pthread_threads_debug" is defined.)
|
||
|
||
The details of pulling stuff out of ELF files and running through
|
||
program images is very platform specific, and I don't want to
|
||
try to support modes or machine types I can't test in or on.
|
||
If someone wants to generalize this to other architectures, I would
|
||
be happy to help and coordinate the activity. Please send me whatever
|
||
changes you make to support these machines, so that I can own the
|
||
central font of all truth (at least as regards this program).
|
||
|
||
Thanks
|
||
*/
|
||
|
||
#include <sys/types.h>
|
||
#include <sys/stat.h>
|
||
#include <sys/wait.h>
|
||
#include <sys/ptrace.h>
|
||
#include <asm/ptrace.h>
|
||
|
||
#include <assert.h>
|
||
#include <fcntl.h>
|
||
#include <link.h>
|
||
#include <malloc.h>
|
||
#include <string.h>
|
||
#include <stdarg.h>
|
||
#include <unistd.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <errno.h>
|
||
#include <signal.h>
|
||
#include <pthread.h>
|
||
#include <limits.h> /* PTHREAD_THREADS_MAX */
|
||
|
||
|
||
#include <bfd.h>
|
||
|
||
#include "libiberty.h"
|
||
|
||
#include "pstack.h" /* just one function */
|
||
#include "budbg.h" /* binutils stuff related to debugging symbols. */
|
||
#include "bucomm.h" /* some common stuff */
|
||
#include "debug.h" /* and more binutils stuff... */
|
||
#include "budbg.h"
|
||
#include "linuxthreads.h" /* LinuxThreads specific stuff... */
|
||
|
||
|
||
/*
|
||
* fprintf for file descriptors :) NOTE: we have to use fixed-size buffer :)(
|
||
* due to malloc's unavalaibility.
|
||
*/
|
||
int
|
||
fdprintf( int fd,
|
||
const char* fmt,...)
|
||
{
|
||
char xbuf[2048];// FIXME: enough?
|
||
va_list ap;
|
||
int r;
|
||
if (fd<0)
|
||
return -1;
|
||
va_start(ap, fmt);
|
||
r = vsnprintf(xbuf, sizeof(xbuf), fmt, ap);
|
||
va_end(ap);
|
||
return write(fd, xbuf, r);
|
||
}
|
||
|
||
int
|
||
fdputc( char c,
|
||
int fd)
|
||
{
|
||
if (fd<0)
|
||
return -1;
|
||
return write(fd, &c, sizeof(c));
|
||
}
|
||
|
||
int
|
||
fdputs( const char* s,
|
||
int fd)
|
||
{
|
||
if (fd<0)
|
||
return -1;
|
||
return write(fd, s, strlen(s));
|
||
}
|
||
|
||
/*
|
||
* Use this function to open log file.
|
||
* Flags: truncate on opening.
|
||
*/
|
||
static const char* path_format = "stack-trace-on-segv-%d.txt";
|
||
static int
|
||
open_log_file( const pthread_t tid,
|
||
const pid_t pid)
|
||
{
|
||
char fname[PATH_MAX];
|
||
int r;
|
||
snprintf(fname, sizeof(fname), path_format, tid, pid);
|
||
r = open(fname, O_WRONLY|O_CREAT|O_TRUNC,
|
||
S_IRUSR|S_IWUSR);
|
||
if (r<0)
|
||
perror("open");
|
||
return r;
|
||
}
|
||
/*
|
||
* Add additional debugging information for functions.
|
||
*/
|
||
|
||
/*
|
||
* Lineno
|
||
*/
|
||
typedef struct {
|
||
int lineno;
|
||
bfd_vma addr;
|
||
} debug_lineno_t;
|
||
|
||
/*
|
||
* Block - a {} pair.
|
||
*/
|
||
typedef struct debug_block_st {
|
||
bfd_vma begin_addr; /* where did it start */
|
||
bfd_vma end_addr; /* where did it end */
|
||
struct debug_block_st* parent;
|
||
struct debug_block_st* childs;
|
||
int childs_count;
|
||
} debug_block_t;
|
||
|
||
/*
|
||
* Function parameter.
|
||
*/
|
||
typedef struct {
|
||
bfd_vma offset; /* Offset in the stack */
|
||
const char* name; /* And name. */
|
||
} debug_parameter_t;
|
||
|
||
/*
|
||
* Extra information about functions.
|
||
*/
|
||
typedef struct {
|
||
asymbol* symbol; /* mangled function name, addr */
|
||
debug_lineno_t* lines;
|
||
int lines_count;
|
||
int max_lines_count;
|
||
const char* name;
|
||
const char* filename;/* a file name it occured in... */
|
||
debug_block_t* block; /* each function has a block, or not, you know */
|
||
debug_parameter_t* argv; /* argument types. */
|
||
int argc;
|
||
int max_argc;
|
||
} debug_function_t;
|
||
|
||
/* This is the structure we use as a handle for these routines. */
|
||
struct pr_handle
|
||
{
|
||
/* File to print information to. */
|
||
FILE *f;
|
||
/* Current indentation level. */
|
||
unsigned int indent;
|
||
/* Type stack. */
|
||
struct pr_stack *stack;
|
||
/* Parameter number we are about to output. */
|
||
int parameter;
|
||
debug_block_t* block; /* current block */
|
||
debug_function_t* function; /* current function */
|
||
debug_function_t* functions; /* all functions */
|
||
int functions_size; /* current size */
|
||
int functions_maxsize; /* maximum size */
|
||
};
|
||
|
||
/* The type stack. */
|
||
|
||
struct pr_stack
|
||
{
|
||
/* Next element on the stack. */
|
||
struct pr_stack *next;
|
||
/* This element. */
|
||
char *type;
|
||
/* Current visibility of fields if this is a class. */
|
||
enum debug_visibility visibility;
|
||
/* Name of the current method we are handling. */
|
||
const char *method;
|
||
};
|
||
|
||
static void indent PARAMS ((struct pr_handle *));
|
||
static boolean push_type PARAMS ((struct pr_handle *, const char *));
|
||
static boolean prepend_type PARAMS ((struct pr_handle *, const char *));
|
||
static boolean append_type PARAMS ((struct pr_handle *, const char *));
|
||
static boolean substitute_type PARAMS ((struct pr_handle *, const char *));
|
||
static boolean indent_type PARAMS ((struct pr_handle *));
|
||
static char *pop_type PARAMS ((struct pr_handle *));
|
||
static void print_vma PARAMS ((bfd_vma, char *, boolean, boolean));
|
||
static boolean pr_fix_visibility
|
||
PARAMS ((struct pr_handle *, enum debug_visibility));
|
||
|
||
static boolean pr_start_compilation_unit PARAMS ((PTR, const char *));
|
||
static boolean pr_start_source PARAMS ((PTR, const char *));
|
||
static boolean pr_empty_type PARAMS ((PTR));
|
||
static boolean pr_void_type PARAMS ((PTR));
|
||
static boolean pr_int_type PARAMS ((PTR, unsigned int, boolean));
|
||
static boolean pr_float_type PARAMS ((PTR, unsigned int));
|
||
static boolean pr_complex_type PARAMS ((PTR, unsigned int));
|
||
static boolean pr_bool_type PARAMS ((PTR, unsigned int));
|
||
static boolean pr_enum_type
|
||
PARAMS ((PTR, const char *, const char **, bfd_signed_vma *));
|
||
static boolean pr_pointer_type PARAMS ((PTR));
|
||
static boolean pr_function_type PARAMS ((PTR, int, boolean));
|
||
static boolean pr_reference_type PARAMS ((PTR));
|
||
static boolean pr_range_type PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma));
|
||
static boolean pr_array_type
|
||
PARAMS ((PTR, bfd_signed_vma, bfd_signed_vma, boolean));
|
||
static boolean pr_set_type PARAMS ((PTR, boolean));
|
||
static boolean pr_offset_type PARAMS ((PTR));
|
||
static boolean pr_method_type PARAMS ((PTR, boolean, int, boolean));
|
||
static boolean pr_const_type PARAMS ((PTR));
|
||
static boolean pr_volatile_type PARAMS ((PTR));
|
||
static boolean pr_start_struct_type
|
||
PARAMS ((PTR, const char *, unsigned int, boolean, unsigned int));
|
||
static boolean pr_struct_field
|
||
PARAMS ((PTR, const char *, bfd_vma, bfd_vma, enum debug_visibility));
|
||
static boolean pr_end_struct_type PARAMS ((PTR));
|
||
static boolean pr_start_class_type
|
||
PARAMS ((PTR, const char *, unsigned int, boolean, unsigned int, boolean,
|
||
boolean));
|
||
static boolean pr_class_static_member
|
||
PARAMS ((PTR, const char *, const char *, enum debug_visibility));
|
||
static boolean pr_class_baseclass
|
||
PARAMS ((PTR, bfd_vma, boolean, enum debug_visibility));
|
||
static boolean pr_class_start_method PARAMS ((PTR, const char *));
|
||
static boolean pr_class_method_variant
|
||
PARAMS ((PTR, const char *, enum debug_visibility, boolean, boolean,
|
||
bfd_vma, boolean));
|
||
static boolean pr_class_static_method_variant
|
||
PARAMS ((PTR, const char *, enum debug_visibility, boolean, boolean));
|
||
static boolean pr_class_end_method PARAMS ((PTR));
|
||
static boolean pr_end_class_type PARAMS ((PTR));
|
||
static boolean pr_typedef_type PARAMS ((PTR, const char *));
|
||
static boolean pr_tag_type
|
||
PARAMS ((PTR, const char *, unsigned int, enum debug_type_kind));
|
||
static boolean pr_typdef PARAMS ((PTR, const char *));
|
||
static boolean pr_tag PARAMS ((PTR, const char *));
|
||
static boolean pr_int_constant PARAMS ((PTR, const char *, bfd_vma));
|
||
static boolean pr_float_constant PARAMS ((PTR, const char *, double));
|
||
static boolean pr_typed_constant PARAMS ((PTR, const char *, bfd_vma));
|
||
static boolean pr_variable
|
||
PARAMS ((PTR, const char *, enum debug_var_kind, bfd_vma));
|
||
static boolean pr_start_function PARAMS ((PTR, const char *, boolean));
|
||
static boolean pr_function_parameter
|
||
PARAMS ((PTR, const char *, enum debug_parm_kind, bfd_vma));
|
||
static boolean pr_start_block PARAMS ((PTR, bfd_vma));
|
||
static boolean pr_end_block PARAMS ((PTR, bfd_vma));
|
||
static boolean pr_end_function PARAMS ((PTR));
|
||
static boolean pr_lineno PARAMS ((PTR, const char *, unsigned long, bfd_vma));
|
||
|
||
static const struct debug_write_fns pr_fns =
|
||
{
|
||
pr_start_compilation_unit,
|
||
pr_start_source,
|
||
pr_empty_type,
|
||
pr_void_type,
|
||
pr_int_type,
|
||
pr_float_type,
|
||
pr_complex_type,
|
||
pr_bool_type,
|
||
pr_enum_type,
|
||
pr_pointer_type,
|
||
pr_function_type,
|
||
pr_reference_type,
|
||
pr_range_type,
|
||
pr_array_type,
|
||
pr_set_type,
|
||
pr_offset_type,
|
||
pr_method_type,
|
||
pr_const_type,
|
||
pr_volatile_type,
|
||
pr_start_struct_type,
|
||
pr_struct_field,
|
||
pr_end_struct_type,
|
||
pr_start_class_type,
|
||
pr_class_static_member,
|
||
pr_class_baseclass,
|
||
pr_class_start_method,
|
||
pr_class_method_variant,
|
||
pr_class_static_method_variant,
|
||
pr_class_end_method,
|
||
pr_end_class_type,
|
||
pr_typedef_type,
|
||
pr_tag_type,
|
||
pr_typdef,
|
||
pr_tag,
|
||
pr_int_constant,
|
||
pr_float_constant,
|
||
pr_typed_constant,
|
||
pr_variable,
|
||
pr_start_function,
|
||
pr_function_parameter,
|
||
pr_start_block,
|
||
pr_end_block,
|
||
pr_end_function,
|
||
pr_lineno
|
||
};
|
||
|
||
|
||
/* Indent to the current indentation level. */
|
||
|
||
static void
|
||
indent (info)
|
||
struct pr_handle *info;
|
||
{
|
||
unsigned int i;
|
||
|
||
for (i = 0; i < info->indent; i++)
|
||
TRACE_PUTC ((' ', info->f));
|
||
}
|
||
|
||
/* Push a type on the type stack. */
|
||
|
||
static boolean
|
||
push_type (info, type)
|
||
struct pr_handle *info;
|
||
const char *type;
|
||
{
|
||
struct pr_stack *n;
|
||
|
||
if (type == NULL)
|
||
return false;
|
||
|
||
n = (struct pr_stack *) xmalloc (sizeof *n);
|
||
memset (n, 0, sizeof *n);
|
||
|
||
n->type = xstrdup (type);
|
||
n->visibility = DEBUG_VISIBILITY_IGNORE;
|
||
n->method = NULL;
|
||
n->next = info->stack;
|
||
info->stack = n;
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Prepend a string onto the type on the top of the type stack. */
|
||
|
||
static boolean
|
||
prepend_type (info, s)
|
||
struct pr_handle *info;
|
||
const char *s;
|
||
{
|
||
char *n;
|
||
|
||
assert (info->stack != NULL);
|
||
|
||
n = (char *) xmalloc (strlen (s) + strlen (info->stack->type) + 1);
|
||
sprintf (n, "%s%s", s, info->stack->type);
|
||
free (info->stack->type);
|
||
info->stack->type = n;
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Append a string to the type on the top of the type stack. */
|
||
|
||
static boolean
|
||
append_type (info, s)
|
||
struct pr_handle *info;
|
||
const char *s;
|
||
{
|
||
unsigned int len;
|
||
|
||
if (s == NULL)
|
||
return false;
|
||
|
||
assert (info->stack != NULL);
|
||
|
||
len = strlen (info->stack->type);
|
||
info->stack->type = (char *) xrealloc (info->stack->type,
|
||
len + strlen (s) + 1);
|
||
strcpy (info->stack->type + len, s);
|
||
|
||
return true;
|
||
}
|
||
|
||
/* We use an underscore to indicate where the name should go in a type
|
||
string. This function substitutes a string for the underscore. If
|
||
there is no underscore, the name follows the type. */
|
||
|
||
static boolean
|
||
substitute_type (info, s)
|
||
struct pr_handle *info;
|
||
const char *s;
|
||
{
|
||
char *u;
|
||
|
||
assert (info->stack != NULL);
|
||
|
||
u = strchr (info->stack->type, '|');
|
||
if (u != NULL)
|
||
{
|
||
char *n;
|
||
|
||
n = (char *) xmalloc (strlen (info->stack->type) + strlen (s));
|
||
|
||
memcpy (n, info->stack->type, u - info->stack->type);
|
||
strcpy (n + (u - info->stack->type), s);
|
||
strcat (n, u + 1);
|
||
|
||
free (info->stack->type);
|
||
info->stack->type = n;
|
||
|
||
return true;
|
||
}
|
||
|
||
if (strchr (s, '|') != NULL
|
||
&& (strchr (info->stack->type, '{') != NULL
|
||
|| strchr (info->stack->type, '(') != NULL))
|
||
{
|
||
if (! prepend_type (info, "(")
|
||
|| ! append_type (info, ")"))
|
||
return false;
|
||
}
|
||
|
||
if (*s == '\0')
|
||
return true;
|
||
|
||
return (append_type (info, " ")
|
||
&& append_type (info, s));
|
||
}
|
||
|
||
/* Indent the type at the top of the stack by appending spaces. */
|
||
|
||
static boolean
|
||
indent_type (info)
|
||
struct pr_handle *info;
|
||
{
|
||
unsigned int i;
|
||
|
||
for (i = 0; i < info->indent; i++)
|
||
{
|
||
if (! append_type (info, " "))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Pop a type from the type stack. */
|
||
|
||
static char *
|
||
pop_type (info)
|
||
struct pr_handle *info;
|
||
{
|
||
struct pr_stack *o;
|
||
char *ret;
|
||
|
||
assert (info->stack != NULL);
|
||
|
||
o = info->stack;
|
||
info->stack = o->next;
|
||
ret = o->type;
|
||
free (o);
|
||
|
||
return ret;
|
||
}
|
||
|
||
/* Print a VMA value into a string. */
|
||
|
||
static void
|
||
print_vma (vma, buf, unsignedp, hexp)
|
||
bfd_vma vma;
|
||
char *buf;
|
||
boolean unsignedp;
|
||
boolean hexp;
|
||
{
|
||
if (sizeof (vma) <= sizeof (unsigned long))
|
||
{
|
||
if (hexp)
|
||
sprintf (buf, "0x%lx", (unsigned long) vma);
|
||
else if (unsignedp)
|
||
sprintf (buf, "%lu", (unsigned long) vma);
|
||
else
|
||
sprintf (buf, "%ld", (long) vma);
|
||
}
|
||
else
|
||
{
|
||
buf[0] = '0';
|
||
buf[1] = 'x';
|
||
sprintf_vma (buf + 2, vma);
|
||
}
|
||
}
|
||
|
||
/* Start a new compilation unit. */
|
||
|
||
static boolean
|
||
pr_start_compilation_unit (p, filename)
|
||
PTR p;
|
||
const char *filename;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
assert (info->indent == 0);
|
||
/*
|
||
TRACE_FPRINTF( (info->f, "%s:\n", filename));
|
||
*/
|
||
return true;
|
||
}
|
||
|
||
/* Start a source file within a compilation unit. */
|
||
|
||
static boolean
|
||
pr_start_source (p, filename)
|
||
PTR p;
|
||
const char *filename;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
assert (info->indent == 0);
|
||
/*
|
||
TRACE_FPRINTF( (info->f, " %s:\n", filename));
|
||
*/
|
||
return true;
|
||
}
|
||
|
||
/* Push an empty type onto the type stack. */
|
||
|
||
static boolean
|
||
pr_empty_type (p)
|
||
PTR p;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
return push_type (info, "<undefined>");
|
||
}
|
||
|
||
/* Push a void type onto the type stack. */
|
||
|
||
static boolean
|
||
pr_void_type (p)
|
||
PTR p;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
return push_type (info, "void");
|
||
}
|
||
|
||
/* Push an integer type onto the type stack. */
|
||
|
||
static boolean
|
||
pr_int_type (p, size, unsignedp)
|
||
PTR p;
|
||
unsigned int size;
|
||
boolean unsignedp;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char ab[10];
|
||
|
||
sprintf (ab, "%sint%d", unsignedp ? "u" : "", size * 8);
|
||
return push_type (info, ab);
|
||
}
|
||
|
||
/* Push a floating type onto the type stack. */
|
||
|
||
static boolean
|
||
pr_float_type (p, size)
|
||
PTR p;
|
||
unsigned int size;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char ab[10];
|
||
|
||
if (size == 4)
|
||
return push_type (info, "float");
|
||
else if (size == 8)
|
||
return push_type (info, "double");
|
||
|
||
sprintf (ab, "float%d", size * 8);
|
||
return push_type (info, ab);
|
||
}
|
||
|
||
/* Push a complex type onto the type stack. */
|
||
|
||
static boolean
|
||
pr_complex_type (p, size)
|
||
PTR p;
|
||
unsigned int size;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
if (! pr_float_type (p, size))
|
||
return false;
|
||
|
||
return prepend_type (info, "complex ");
|
||
}
|
||
|
||
/* Push a boolean type onto the type stack. */
|
||
|
||
static boolean
|
||
pr_bool_type (p, size)
|
||
PTR p;
|
||
unsigned int size;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char ab[10];
|
||
|
||
sprintf (ab, "bool%d", size * 8);
|
||
|
||
return push_type (info, ab);
|
||
}
|
||
|
||
/* Push an enum type onto the type stack. */
|
||
|
||
static boolean
|
||
pr_enum_type (p, tag, names, values)
|
||
PTR p;
|
||
const char *tag;
|
||
const char **names;
|
||
bfd_signed_vma *values;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
unsigned int i;
|
||
bfd_signed_vma val;
|
||
|
||
if (! push_type (info, "enum "))
|
||
return false;
|
||
if (tag != NULL)
|
||
{
|
||
if (! append_type (info, tag)
|
||
|| ! append_type (info, " "))
|
||
return false;
|
||
}
|
||
if (! append_type (info, "{ "))
|
||
return false;
|
||
|
||
if (names == NULL)
|
||
{
|
||
if (! append_type (info, "/* undefined */"))
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
val = 0;
|
||
for (i = 0; names[i] != NULL; i++)
|
||
{
|
||
if (i > 0)
|
||
{
|
||
if (! append_type (info, ", "))
|
||
return false;
|
||
}
|
||
|
||
if (! append_type (info, names[i]))
|
||
return false;
|
||
|
||
if (values[i] != val)
|
||
{
|
||
char ab[20];
|
||
|
||
print_vma (values[i], ab, false, false);
|
||
if (! append_type (info, " = ")
|
||
|| ! append_type (info, ab))
|
||
return false;
|
||
val = values[i];
|
||
}
|
||
|
||
++val;
|
||
}
|
||
}
|
||
|
||
return append_type (info, " }");
|
||
}
|
||
|
||
/* Turn the top type on the stack into a pointer. */
|
||
|
||
static boolean
|
||
pr_pointer_type (p)
|
||
PTR p;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *s;
|
||
|
||
assert (info->stack != NULL);
|
||
|
||
s = strchr (info->stack->type, '|');
|
||
if (s != NULL && s[1] == '[')
|
||
return substitute_type (info, "(*|)");
|
||
return substitute_type (info, "*|");
|
||
}
|
||
|
||
/* Turn the top type on the stack into a function returning that type. */
|
||
|
||
static boolean
|
||
pr_function_type (p, argcount, varargs)
|
||
PTR p;
|
||
int argcount;
|
||
boolean varargs;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char **arg_types;
|
||
unsigned int len;
|
||
char *s;
|
||
|
||
assert (info->stack != NULL);
|
||
|
||
len = 10;
|
||
|
||
if (argcount <= 0)
|
||
{
|
||
arg_types = NULL;
|
||
len += 15;
|
||
}
|
||
else
|
||
{
|
||
int i;
|
||
|
||
arg_types = (char **) xmalloc (argcount * sizeof *arg_types);
|
||
for (i = argcount - 1; i >= 0; i--)
|
||
{
|
||
if (! substitute_type (info, ""))
|
||
return false;
|
||
arg_types[i] = pop_type (info);
|
||
if (arg_types[i] == NULL)
|
||
return false;
|
||
len += strlen (arg_types[i]) + 2;
|
||
}
|
||
if (varargs)
|
||
len += 5;
|
||
}
|
||
|
||
/* Now the return type is on the top of the stack. */
|
||
|
||
s = (char *) xmalloc (len);
|
||
strcpy (s, "(|) (");
|
||
|
||
if (argcount < 0)
|
||
{
|
||
#if 0
|
||
/* Turn off unknown arguments. */
|
||
strcat (s, "/* unknown */");
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < argcount; i++)
|
||
{
|
||
if (i > 0)
|
||
strcat (s, ", ");
|
||
strcat (s, arg_types[i]);
|
||
}
|
||
if (varargs)
|
||
{
|
||
if (i > 0)
|
||
strcat (s, ", ");
|
||
strcat (s, "...");
|
||
}
|
||
if (argcount > 0)
|
||
free (arg_types);
|
||
}
|
||
|
||
strcat (s, ")");
|
||
|
||
if (! substitute_type (info, s))
|
||
return false;
|
||
|
||
free (s);
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Turn the top type on the stack into a reference to that type. */
|
||
|
||
static boolean
|
||
pr_reference_type (p)
|
||
PTR p;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
assert (info->stack != NULL);
|
||
|
||
return substitute_type (info, "&|");
|
||
}
|
||
|
||
/* Make a range type. */
|
||
|
||
static boolean
|
||
pr_range_type (p, lower, upper)
|
||
PTR p;
|
||
bfd_signed_vma lower;
|
||
bfd_signed_vma upper;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char abl[20], abu[20];
|
||
|
||
assert (info->stack != NULL);
|
||
|
||
if (! substitute_type (info, ""))
|
||
return false;
|
||
|
||
print_vma (lower, abl, false, false);
|
||
print_vma (upper, abu, false, false);
|
||
|
||
return (prepend_type (info, "range (")
|
||
&& append_type (info, "):")
|
||
&& append_type (info, abl)
|
||
&& append_type (info, ":")
|
||
&& append_type (info, abu));
|
||
}
|
||
|
||
/* Make an array type. */
|
||
|
||
/*ARGSUSED*/
|
||
static boolean
|
||
pr_array_type (p, lower, upper, stringp)
|
||
PTR p;
|
||
bfd_signed_vma lower;
|
||
bfd_signed_vma upper;
|
||
boolean stringp;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *range_type;
|
||
char abl[20], abu[20], ab[50];
|
||
|
||
range_type = pop_type (info);
|
||
if (range_type == NULL)
|
||
return false;
|
||
|
||
if (lower == 0)
|
||
{
|
||
if (upper == -1)
|
||
sprintf (ab, "|[]");
|
||
else
|
||
{
|
||
print_vma (upper + 1, abu, false, false);
|
||
sprintf (ab, "|[%s]", abu);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
print_vma (lower, abl, false, false);
|
||
print_vma (upper, abu, false, false);
|
||
sprintf (ab, "|[%s:%s]", abl, abu);
|
||
}
|
||
|
||
if (! substitute_type (info, ab))
|
||
return false;
|
||
|
||
if (strcmp (range_type, "int") != 0)
|
||
{
|
||
if (! append_type (info, ":")
|
||
|| ! append_type (info, range_type))
|
||
return false;
|
||
}
|
||
|
||
if (stringp)
|
||
{
|
||
if (! append_type (info, " /* string */"))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Make a set type. */
|
||
|
||
/*ARGSUSED*/
|
||
static boolean
|
||
pr_set_type (p, bitstringp)
|
||
PTR p;
|
||
boolean bitstringp;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
if (! substitute_type (info, ""))
|
||
return false;
|
||
|
||
if (! prepend_type (info, "set { ")
|
||
|| ! append_type (info, " }"))
|
||
return false;
|
||
|
||
if (bitstringp)
|
||
{
|
||
if (! append_type (info, "/* bitstring */"))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Make an offset type. */
|
||
|
||
static boolean
|
||
pr_offset_type (p)
|
||
PTR p;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *t;
|
||
|
||
if (! substitute_type (info, ""))
|
||
return false;
|
||
|
||
t = pop_type (info);
|
||
if (t == NULL)
|
||
return false;
|
||
|
||
return (substitute_type (info, "")
|
||
&& prepend_type (info, " ")
|
||
&& prepend_type (info, t)
|
||
&& append_type (info, "::|"));
|
||
}
|
||
|
||
/* Make a method type. */
|
||
|
||
static boolean
|
||
pr_method_type (p, domain, argcount, varargs)
|
||
PTR p;
|
||
boolean domain;
|
||
int argcount;
|
||
boolean varargs;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
unsigned int len;
|
||
char *domain_type;
|
||
char **arg_types;
|
||
char *s;
|
||
|
||
len = 10;
|
||
|
||
if (! domain)
|
||
domain_type = NULL;
|
||
else
|
||
{
|
||
if (! substitute_type (info, ""))
|
||
return false;
|
||
domain_type = pop_type (info);
|
||
if (domain_type == NULL)
|
||
return false;
|
||
if (strncmp (domain_type, "class ", sizeof "class " - 1) == 0
|
||
&& strchr (domain_type + sizeof "class " - 1, ' ') == NULL)
|
||
domain_type += sizeof "class " - 1;
|
||
else if (strncmp (domain_type, "union class ",
|
||
sizeof "union class ") == 0
|
||
&& (strchr (domain_type + sizeof "union class " - 1, ' ')
|
||
== NULL))
|
||
domain_type += sizeof "union class " - 1;
|
||
len += strlen (domain_type);
|
||
}
|
||
|
||
if (argcount <= 0)
|
||
{
|
||
arg_types = NULL;
|
||
len += 15;
|
||
}
|
||
else
|
||
{
|
||
int i;
|
||
|
||
arg_types = (char **) xmalloc (argcount * sizeof *arg_types);
|
||
for (i = argcount - 1; i >= 0; i--)
|
||
{
|
||
if (! substitute_type (info, ""))
|
||
return false;
|
||
arg_types[i] = pop_type (info);
|
||
if (arg_types[i] == NULL)
|
||
return false;
|
||
len += strlen (arg_types[i]) + 2;
|
||
}
|
||
if (varargs)
|
||
len += 5;
|
||
}
|
||
|
||
/* Now the return type is on the top of the stack. */
|
||
|
||
s = (char *) xmalloc (len);
|
||
if (! domain)
|
||
*s = '\0';
|
||
else
|
||
strcpy (s, domain_type);
|
||
strcat (s, "::| (");
|
||
|
||
if (argcount < 0)
|
||
strcat (s, "/* unknown */");
|
||
else
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < argcount; i++)
|
||
{
|
||
if (i > 0)
|
||
strcat (s, ", ");
|
||
strcat (s, arg_types[i]);
|
||
}
|
||
if (varargs)
|
||
{
|
||
if (i > 0)
|
||
strcat (s, ", ");
|
||
strcat (s, "...");
|
||
}
|
||
if (argcount > 0)
|
||
free (arg_types);
|
||
}
|
||
|
||
strcat (s, ")");
|
||
|
||
if (! substitute_type (info, s))
|
||
return false;
|
||
|
||
free (s);
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Make a const qualified type. */
|
||
|
||
static boolean
|
||
pr_const_type (p)
|
||
PTR p;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
return substitute_type (info, "const |");
|
||
}
|
||
|
||
/* Make a volatile qualified type. */
|
||
|
||
static boolean
|
||
pr_volatile_type (p)
|
||
PTR p;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
return substitute_type (info, "volatile |");
|
||
}
|
||
|
||
/* Start accumulating a struct type. */
|
||
|
||
static boolean
|
||
pr_start_struct_type (p, tag, id, structp, size)
|
||
PTR p;
|
||
const char *tag;
|
||
unsigned int id;
|
||
boolean structp;
|
||
unsigned int size;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
info->indent += 2;
|
||
|
||
if (! push_type (info, structp ? "struct " : "union "))
|
||
return false;
|
||
if (tag != NULL)
|
||
{
|
||
if (! append_type (info, tag))
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
char idbuf[20];
|
||
|
||
sprintf (idbuf, "%%anon%u", id);
|
||
if (! append_type (info, idbuf))
|
||
return false;
|
||
}
|
||
|
||
if (! append_type (info, " {"))
|
||
return false;
|
||
if (size != 0 || tag != NULL)
|
||
{
|
||
char ab[30];
|
||
|
||
if (! append_type (info, " /*"))
|
||
return false;
|
||
|
||
if (size != 0)
|
||
{
|
||
sprintf (ab, " size %u", size);
|
||
if (! append_type (info, ab))
|
||
return false;
|
||
}
|
||
if (tag != NULL)
|
||
{
|
||
sprintf (ab, " id %u", id);
|
||
if (! append_type (info, ab))
|
||
return false;
|
||
}
|
||
if (! append_type (info, " */"))
|
||
return false;
|
||
}
|
||
if (! append_type (info, "\n"))
|
||
return false;
|
||
|
||
info->stack->visibility = DEBUG_VISIBILITY_PUBLIC;
|
||
|
||
return indent_type (info);
|
||
}
|
||
|
||
/* Output the visibility of a field in a struct. */
|
||
|
||
static boolean
|
||
pr_fix_visibility (info, visibility)
|
||
struct pr_handle *info;
|
||
enum debug_visibility visibility;
|
||
{
|
||
const char *s;
|
||
char *t;
|
||
unsigned int len;
|
||
|
||
assert (info->stack != NULL);
|
||
|
||
if (info->stack->visibility == visibility)
|
||
return true;
|
||
|
||
assert (info->stack->visibility != DEBUG_VISIBILITY_IGNORE);
|
||
|
||
switch (visibility)
|
||
{
|
||
case DEBUG_VISIBILITY_PUBLIC:
|
||
s = "public";
|
||
break;
|
||
case DEBUG_VISIBILITY_PRIVATE:
|
||
s = "private";
|
||
break;
|
||
case DEBUG_VISIBILITY_PROTECTED:
|
||
s = "protected";
|
||
break;
|
||
case DEBUG_VISIBILITY_IGNORE:
|
||
s = "/* ignore */";
|
||
break;
|
||
default:
|
||
abort ();
|
||
return false;
|
||
}
|
||
|
||
/* Trim off a trailing space in the struct string, to make the
|
||
output look a bit better, then stick on the visibility string. */
|
||
|
||
t = info->stack->type;
|
||
len = strlen (t);
|
||
assert (t[len - 1] == ' ');
|
||
t[len - 1] = '\0';
|
||
|
||
if (! append_type (info, s)
|
||
|| ! append_type (info, ":\n")
|
||
|| ! indent_type (info))
|
||
return false;
|
||
|
||
info->stack->visibility = visibility;
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Add a field to a struct type. */
|
||
|
||
static boolean
|
||
pr_struct_field (p, name, bitpos, bitsize, visibility)
|
||
PTR p;
|
||
const char *name;
|
||
bfd_vma bitpos;
|
||
bfd_vma bitsize;
|
||
enum debug_visibility visibility;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char ab[20];
|
||
char *t;
|
||
|
||
if (! substitute_type (info, name))
|
||
return false;
|
||
|
||
if (! append_type (info, "; /* "))
|
||
return false;
|
||
|
||
if (bitsize != 0)
|
||
{
|
||
print_vma (bitsize, ab, true, false);
|
||
if (! append_type (info, "bitsize ")
|
||
|| ! append_type (info, ab)
|
||
|| ! append_type (info, ", "))
|
||
return false;
|
||
}
|
||
|
||
print_vma (bitpos, ab, true, false);
|
||
if (! append_type (info, "bitpos ")
|
||
|| ! append_type (info, ab)
|
||
|| ! append_type (info, " */\n")
|
||
|| ! indent_type (info))
|
||
return false;
|
||
|
||
t = pop_type (info);
|
||
if (t == NULL)
|
||
return false;
|
||
|
||
if (! pr_fix_visibility (info, visibility))
|
||
return false;
|
||
|
||
return append_type (info, t);
|
||
}
|
||
|
||
/* Finish a struct type. */
|
||
|
||
static boolean
|
||
pr_end_struct_type (p)
|
||
PTR p;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *s;
|
||
|
||
assert (info->stack != NULL);
|
||
assert (info->indent >= 2);
|
||
|
||
info->indent -= 2;
|
||
|
||
/* Change the trailing indentation to have a close brace. */
|
||
s = info->stack->type + strlen (info->stack->type) - 2;
|
||
assert (s[0] == ' ' && s[1] == ' ' && s[2] == '\0');
|
||
|
||
*s++ = '}';
|
||
*s = '\0';
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Start a class type. */
|
||
|
||
static boolean
|
||
pr_start_class_type (p, tag, id, structp, size, vptr, ownvptr)
|
||
PTR p;
|
||
const char *tag;
|
||
unsigned int id;
|
||
boolean structp;
|
||
unsigned int size;
|
||
boolean vptr;
|
||
boolean ownvptr;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *tv = NULL;
|
||
|
||
info->indent += 2;
|
||
|
||
if (vptr && ! ownvptr)
|
||
{
|
||
tv = pop_type (info);
|
||
if (tv == NULL)
|
||
return false;
|
||
}
|
||
|
||
if (! push_type (info, structp ? "class " : "union class "))
|
||
return false;
|
||
if (tag != NULL)
|
||
{
|
||
if (! append_type (info, tag))
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
char idbuf[20];
|
||
|
||
sprintf (idbuf, "%%anon%u", id);
|
||
if (! append_type (info, idbuf))
|
||
return false;
|
||
}
|
||
|
||
if (! append_type (info, " {"))
|
||
return false;
|
||
if (size != 0 || vptr || ownvptr || tag != NULL)
|
||
{
|
||
if (! append_type (info, " /*"))
|
||
return false;
|
||
|
||
if (size != 0)
|
||
{
|
||
char ab[20];
|
||
|
||
sprintf (ab, "%u", size);
|
||
if (! append_type (info, " size ")
|
||
|| ! append_type (info, ab))
|
||
return false;
|
||
}
|
||
|
||
if (vptr)
|
||
{
|
||
if (! append_type (info, " vtable "))
|
||
return false;
|
||
if (ownvptr)
|
||
{
|
||
if (! append_type (info, "self "))
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (! append_type (info, tv)
|
||
|| ! append_type (info, " "))
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (tag != NULL)
|
||
{
|
||
char ab[30];
|
||
|
||
sprintf (ab, " id %u", id);
|
||
if (! append_type (info, ab))
|
||
return false;
|
||
}
|
||
|
||
if (! append_type (info, " */"))
|
||
return false;
|
||
}
|
||
|
||
info->stack->visibility = DEBUG_VISIBILITY_PRIVATE;
|
||
|
||
return (append_type (info, "\n")
|
||
&& indent_type (info));
|
||
}
|
||
|
||
/* Add a static member to a class. */
|
||
|
||
static boolean
|
||
pr_class_static_member (p, name, physname, visibility)
|
||
PTR p;
|
||
const char *name;
|
||
const char *physname;
|
||
enum debug_visibility visibility;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *t;
|
||
|
||
if (! substitute_type (info, name))
|
||
return false;
|
||
|
||
if (! prepend_type (info, "static ")
|
||
|| ! append_type (info, "; /* ")
|
||
|| ! append_type (info, physname)
|
||
|| ! append_type (info, " */\n")
|
||
|| ! indent_type (info))
|
||
return false;
|
||
|
||
t = pop_type (info);
|
||
if (t == NULL)
|
||
return false;
|
||
|
||
if (! pr_fix_visibility (info, visibility))
|
||
return false;
|
||
|
||
return append_type (info, t);
|
||
}
|
||
|
||
/* Add a base class to a class. */
|
||
|
||
static boolean
|
||
pr_class_baseclass (p, bitpos, virtual, visibility)
|
||
PTR p;
|
||
bfd_vma bitpos;
|
||
boolean virtual;
|
||
enum debug_visibility visibility;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *t;
|
||
const char *prefix;
|
||
char ab[20];
|
||
char *s, *l, *n;
|
||
|
||
assert (info->stack != NULL && info->stack->next != NULL);
|
||
|
||
if (! substitute_type (info, ""))
|
||
return false;
|
||
|
||
t = pop_type (info);
|
||
if (t == NULL)
|
||
return false;
|
||
|
||
if (strncmp (t, "class ", sizeof "class " - 1) == 0)
|
||
t += sizeof "class " - 1;
|
||
|
||
/* Push it back on to take advantage of the prepend_type and
|
||
append_type routines. */
|
||
if (! push_type (info, t))
|
||
return false;
|
||
|
||
if (virtual)
|
||
{
|
||
if (! prepend_type (info, "virtual "))
|
||
return false;
|
||
}
|
||
|
||
switch (visibility)
|
||
{
|
||
case DEBUG_VISIBILITY_PUBLIC:
|
||
prefix = "public ";
|
||
break;
|
||
case DEBUG_VISIBILITY_PROTECTED:
|
||
prefix = "protected ";
|
||
break;
|
||
case DEBUG_VISIBILITY_PRIVATE:
|
||
prefix = "private ";
|
||
break;
|
||
default:
|
||
prefix = "/* unknown visibility */ ";
|
||
break;
|
||
}
|
||
|
||
if (! prepend_type (info, prefix))
|
||
return false;
|
||
|
||
if (bitpos != 0)
|
||
{
|
||
print_vma (bitpos, ab, true, false);
|
||
if (! append_type (info, " /* bitpos ")
|
||
|| ! append_type (info, ab)
|
||
|| ! append_type (info, " */"))
|
||
return false;
|
||
}
|
||
|
||
/* Now the top of the stack is something like "public A / * bitpos
|
||
10 * /". The next element on the stack is something like "class
|
||
xx { / * size 8 * /\n...". We want to substitute the top of the
|
||
stack in before the {. */
|
||
s = strchr (info->stack->next->type, '{');
|
||
assert (s != NULL);
|
||
--s;
|
||
|
||
/* If there is already a ':', then we already have a baseclass, and
|
||
we must append this one after a comma. */
|
||
for (l = info->stack->next->type; l != s; l++)
|
||
if (*l == ':')
|
||
break;
|
||
if (! prepend_type (info, l == s ? " : " : ", "))
|
||
return false;
|
||
|
||
t = pop_type (info);
|
||
if (t == NULL)
|
||
return false;
|
||
|
||
n = (char *) xmalloc (strlen (info->stack->type) + strlen (t) + 1);
|
||
memcpy (n, info->stack->type, s - info->stack->type);
|
||
strcpy (n + (s - info->stack->type), t);
|
||
strcat (n, s);
|
||
|
||
free (info->stack->type);
|
||
info->stack->type = n;
|
||
|
||
free (t);
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Start adding a method to a class. */
|
||
|
||
static boolean
|
||
pr_class_start_method (p, name)
|
||
PTR p;
|
||
const char *name;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
assert (info->stack != NULL);
|
||
info->stack->method = name;
|
||
return true;
|
||
}
|
||
|
||
/* Add a variant to a method. */
|
||
|
||
static boolean
|
||
pr_class_method_variant (p, physname, visibility, constp, volatilep, voffset,
|
||
context)
|
||
PTR p;
|
||
const char *physname;
|
||
enum debug_visibility visibility;
|
||
boolean constp;
|
||
boolean volatilep;
|
||
bfd_vma voffset;
|
||
boolean context;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *method_type;
|
||
char *context_type;
|
||
|
||
assert (info->stack != NULL);
|
||
assert (info->stack->next != NULL);
|
||
|
||
/* Put the const and volatile qualifiers on the type. */
|
||
if (volatilep)
|
||
{
|
||
if (! append_type (info, " volatile"))
|
||
return false;
|
||
}
|
||
if (constp)
|
||
{
|
||
if (! append_type (info, " const"))
|
||
return false;
|
||
}
|
||
|
||
/* Stick the name of the method into its type. */
|
||
if (! substitute_type (info,
|
||
(context
|
||
? info->stack->next->next->method
|
||
: info->stack->next->method)))
|
||
return false;
|
||
|
||
/* Get the type. */
|
||
method_type = pop_type (info);
|
||
if (method_type == NULL)
|
||
return false;
|
||
|
||
/* Pull off the context type if there is one. */
|
||
if (! context)
|
||
context_type = NULL;
|
||
else
|
||
{
|
||
context_type = pop_type (info);
|
||
if (context_type == NULL)
|
||
return false;
|
||
}
|
||
|
||
/* Now the top of the stack is the class. */
|
||
|
||
if (! pr_fix_visibility (info, visibility))
|
||
return false;
|
||
|
||
if (! append_type (info, method_type)
|
||
|| ! append_type (info, " /* ")
|
||
|| ! append_type (info, physname)
|
||
|| ! append_type (info, " "))
|
||
return false;
|
||
if (context || voffset != 0)
|
||
{
|
||
char ab[20];
|
||
|
||
if (context)
|
||
{
|
||
if (! append_type (info, "context ")
|
||
|| ! append_type (info, context_type)
|
||
|| ! append_type (info, " "))
|
||
return false;
|
||
}
|
||
print_vma (voffset, ab, true, false);
|
||
if (! append_type (info, "voffset ")
|
||
|| ! append_type (info, ab))
|
||
return false;
|
||
}
|
||
|
||
return (append_type (info, " */;\n")
|
||
&& indent_type (info));
|
||
}
|
||
|
||
/* Add a static variant to a method. */
|
||
|
||
static boolean
|
||
pr_class_static_method_variant (p, physname, visibility, constp, volatilep)
|
||
PTR p;
|
||
const char *physname;
|
||
enum debug_visibility visibility;
|
||
boolean constp;
|
||
boolean volatilep;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *method_type;
|
||
|
||
assert (info->stack != NULL);
|
||
assert (info->stack->next != NULL);
|
||
assert (info->stack->next->method != NULL);
|
||
|
||
/* Put the const and volatile qualifiers on the type. */
|
||
if (volatilep)
|
||
{
|
||
if (! append_type (info, " volatile"))
|
||
return false;
|
||
}
|
||
if (constp)
|
||
{
|
||
if (! append_type (info, " const"))
|
||
return false;
|
||
}
|
||
|
||
/* Mark it as static. */
|
||
if (! prepend_type (info, "static "))
|
||
return false;
|
||
|
||
/* Stick the name of the method into its type. */
|
||
if (! substitute_type (info, info->stack->next->method))
|
||
return false;
|
||
|
||
/* Get the type. */
|
||
method_type = pop_type (info);
|
||
if (method_type == NULL)
|
||
return false;
|
||
|
||
/* Now the top of the stack is the class. */
|
||
|
||
if (! pr_fix_visibility (info, visibility))
|
||
return false;
|
||
|
||
return (append_type (info, method_type)
|
||
&& append_type (info, " /* ")
|
||
&& append_type (info, physname)
|
||
&& append_type (info, " */;\n")
|
||
&& indent_type (info));
|
||
}
|
||
|
||
/* Finish up a method. */
|
||
|
||
static boolean
|
||
pr_class_end_method (p)
|
||
PTR p;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
info->stack->method = NULL;
|
||
return true;
|
||
}
|
||
|
||
/* Finish up a class. */
|
||
|
||
static boolean
|
||
pr_end_class_type (p)
|
||
PTR p;
|
||
{
|
||
return pr_end_struct_type (p);
|
||
}
|
||
|
||
/* Push a type on the stack using a typedef name. */
|
||
|
||
static boolean
|
||
pr_typedef_type (p, name)
|
||
PTR p;
|
||
const char *name;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
return push_type (info, name);
|
||
}
|
||
|
||
/* Push a type on the stack using a tag name. */
|
||
|
||
static boolean
|
||
pr_tag_type (p, name, id, kind)
|
||
PTR p;
|
||
const char *name;
|
||
unsigned int id;
|
||
enum debug_type_kind kind;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
const char *t, *tag;
|
||
char idbuf[30];
|
||
|
||
switch (kind)
|
||
{
|
||
case DEBUG_KIND_STRUCT:
|
||
t = "struct ";
|
||
break;
|
||
case DEBUG_KIND_UNION:
|
||
t = "union ";
|
||
break;
|
||
case DEBUG_KIND_ENUM:
|
||
t = "enum ";
|
||
break;
|
||
case DEBUG_KIND_CLASS:
|
||
t = "class ";
|
||
break;
|
||
case DEBUG_KIND_UNION_CLASS:
|
||
t = "union class ";
|
||
break;
|
||
default:
|
||
abort ();
|
||
return false;
|
||
}
|
||
|
||
if (! push_type (info, t))
|
||
return false;
|
||
if (name != NULL)
|
||
tag = name;
|
||
else
|
||
{
|
||
sprintf (idbuf, "%%anon%u", id);
|
||
tag = idbuf;
|
||
}
|
||
|
||
if (! append_type (info, tag))
|
||
return false;
|
||
if (name != NULL && kind != DEBUG_KIND_ENUM)
|
||
{
|
||
sprintf (idbuf, " /* id %u */", id);
|
||
if (! append_type (info, idbuf))
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Output a typedef. */
|
||
|
||
static boolean
|
||
pr_typdef (p, name)
|
||
PTR p;
|
||
const char *name;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *s;
|
||
|
||
if (! substitute_type (info, name))
|
||
return false;
|
||
|
||
s = pop_type (info);
|
||
if (s == NULL)
|
||
return false;
|
||
/*
|
||
indent (info);
|
||
TRACE_FPRINTF( (info->f, "typedef %s;\n", s));
|
||
*/
|
||
free (s);
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Output a tag. The tag should already be in the string on the
|
||
stack, so all we have to do here is print it out. */
|
||
|
||
/*ARGSUSED*/
|
||
static boolean
|
||
pr_tag (p, name)
|
||
PTR p;
|
||
const char *name;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *t;
|
||
|
||
t = pop_type (info);
|
||
if (t == NULL)
|
||
return false;
|
||
/*
|
||
indent (info);
|
||
TRACE_FPRINTF( (info->f, "%s;\n", t));
|
||
*/
|
||
free (t);
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Output an integer constant. */
|
||
|
||
static boolean
|
||
pr_int_constant (p, name, val)
|
||
PTR p;
|
||
const char *name;
|
||
bfd_vma val;
|
||
{
|
||
/*
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char ab[20];
|
||
indent (info);
|
||
print_vma (val, ab, false, false);
|
||
TRACE_FPRINTF( (info->f, "const int %s = %s;\n", name, ab));
|
||
*/
|
||
return true;
|
||
}
|
||
|
||
/* Output a floating point constant. */
|
||
|
||
static boolean
|
||
pr_float_constant (p, name, val)
|
||
PTR p;
|
||
const char *name;
|
||
double val;
|
||
{
|
||
/*
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
indent (info);
|
||
TRACE_FPRINTF( (info->f, "const double %s = %g;\n", name, val));
|
||
*/
|
||
return true;
|
||
}
|
||
|
||
/* Output a typed constant. */
|
||
|
||
static boolean
|
||
pr_typed_constant (p, name, val)
|
||
PTR p;
|
||
const char *name;
|
||
bfd_vma val;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *t;
|
||
|
||
t = pop_type (info);
|
||
if (t == NULL)
|
||
return false;
|
||
/*
|
||
char ab[20];
|
||
indent (info);
|
||
print_vma (val, ab, false, false);
|
||
TRACE_FPRINTF( (info->f, "const %s %s = %s;\n", t, name, ab));
|
||
*/
|
||
free (t);
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Output a variable. */
|
||
|
||
static boolean
|
||
pr_variable (p, name, kind, val)
|
||
PTR p;
|
||
const char *name;
|
||
enum debug_var_kind kind;
|
||
bfd_vma val;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *t;
|
||
char ab[20];
|
||
(void)ab;
|
||
|
||
if (! substitute_type (info, name))
|
||
return false;
|
||
|
||
t = pop_type (info);
|
||
if (t == NULL)
|
||
return false;
|
||
|
||
#if 0
|
||
indent (info);
|
||
switch (kind)
|
||
{
|
||
case DEBUG_STATIC:
|
||
case DEBUG_LOCAL_STATIC:
|
||
TRACE_FPRINTF( (info->f, "static "));
|
||
break;
|
||
case DEBUG_REGISTER:
|
||
TRACE_FPRINTF( (info->f, "register "));
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
print_vma (val, ab, true, true);
|
||
TRACE_FPRINTF( (info->f, "%s /* %s */;\n", t, ab));
|
||
#else /* 0 */
|
||
#if 0
|
||
if (kind==DEBUG_STATIC || kind==DEBUG_LOCAL_STATIC) {
|
||
print_vma (val, ab, true, true);
|
||
TRACE_FPRINTF( (info->f, "STATIC_VAR: %s /* %s */;\n", t, ab));
|
||
}
|
||
#endif /* 0 */
|
||
#endif /* !0 */
|
||
|
||
free (t);
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Start outputting a function. */
|
||
|
||
static boolean
|
||
pr_start_function (p, name, global)
|
||
PTR p;
|
||
const char *name;
|
||
boolean global;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char *t;
|
||
|
||
if (! substitute_type (info, name))
|
||
return false;
|
||
|
||
t = pop_type (info);
|
||
if (t == NULL)
|
||
return false;
|
||
|
||
#if 0
|
||
indent (info);
|
||
if (! global)
|
||
TRACE_FPRINTF( (info->f, "static "));
|
||
TRACE_FPRINTF( (info->f, "%s (", t));
|
||
info->parameter = 1;
|
||
#else /* 0 */
|
||
if (info->functions_size==info->functions_maxsize) {
|
||
info->functions_maxsize *= 2;
|
||
info->functions = xrealloc(info->functions,
|
||
info->functions_maxsize*sizeof(debug_function_t));
|
||
assert(info->functions!=0);
|
||
}
|
||
/* info->functions[info->functions_size] = xmalloc(sizeof(debug_function_t)); */
|
||
info->function = &info->functions[info->functions_size];
|
||
++info->functions_size;
|
||
info->function->symbol = NULL;
|
||
info->function->lines = NULL;
|
||
info->function->lines_count = 0;
|
||
info->function->max_lines_count = 0;
|
||
info->function->name = t;
|
||
info->function->filename = NULL;
|
||
info->function->block = NULL;
|
||
info->function->argv = NULL;
|
||
info->function->argc = 0;
|
||
info->function->max_argc = 0;
|
||
#endif /* !0 */
|
||
return true;
|
||
}
|
||
|
||
/* Output a function parameter. */
|
||
|
||
static boolean
|
||
pr_function_parameter (p, name, kind, val)
|
||
PTR p;
|
||
const char *name;
|
||
enum debug_parm_kind kind;
|
||
bfd_vma val;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
debug_function_t* f = info->function;
|
||
char *t;
|
||
char ab[20];
|
||
(void)ab;
|
||
|
||
if (kind == DEBUG_PARM_REFERENCE
|
||
|| kind == DEBUG_PARM_REF_REG)
|
||
{
|
||
if (! pr_reference_type (p))
|
||
return false;
|
||
}
|
||
|
||
if (! substitute_type (info, name))
|
||
return false;
|
||
|
||
t = pop_type (info);
|
||
if (t == NULL)
|
||
return false;
|
||
|
||
#if 0
|
||
if (info->parameter != 1)
|
||
TRACE_FPRINTF( (info->f, ", "));
|
||
|
||
if (kind == DEBUG_PARM_REG || kind == DEBUG_PARM_REF_REG)
|
||
TRACE_FPRINTF( (info->f, "register "));
|
||
|
||
print_vma (val, ab, true, true);
|
||
TRACE_FPRINTF( (info->f, "%s /* %s */", t, ab));
|
||
free (t);
|
||
++info->parameter;
|
||
#else /* 0 */
|
||
assert(f!=NULL);
|
||
if (f->argv==NULL) {
|
||
f->max_argc = 7; /* rarely anyone has more than that many args... */
|
||
f->argv = xmalloc(sizeof(debug_parameter_t)*f->max_argc);
|
||
} else if (f->argc==f->max_argc) {
|
||
f->max_argc *= 2;
|
||
f->argv = realloc(f->argv,sizeof(debug_parameter_t)*f->max_argc);
|
||
}
|
||
f->argv[f->argc].offset = val;
|
||
f->argv[f->argc].name = t;
|
||
++f->argc;
|
||
#endif /* !0 */
|
||
return true;
|
||
}
|
||
|
||
/* Start writing out a block. */
|
||
|
||
static boolean
|
||
pr_start_block (p, addr)
|
||
PTR p;
|
||
bfd_vma addr;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char ab[20];
|
||
debug_block_t* block = 0;
|
||
(void)ab;
|
||
#if 0
|
||
if (info->parameter > 0)
|
||
{
|
||
TRACE_FPRINTF( (info->f, ")\n"));
|
||
info->parameter = 0;
|
||
}
|
||
indent (info);
|
||
print_vma (addr, ab, true, true);
|
||
TRACE_FPRINTF( (info->f, "{ /* %s */\n", ab));
|
||
info->indent += 2;
|
||
#else
|
||
if (info->block) {
|
||
if (info->block->childs_count==0)
|
||
info->block->childs = xmalloc(sizeof(debug_block_t));
|
||
else
|
||
info->block->childs = xrealloc(info->block->childs,
|
||
info->block->childs_count*sizeof(debug_block_t));
|
||
block = &info->block->childs[info->block->childs_count];
|
||
} else {
|
||
block = xmalloc(sizeof(debug_block_t));
|
||
info->function->block = block;
|
||
}
|
||
block->begin_addr = addr;
|
||
block->end_addr = 0;
|
||
block->parent = info->block;
|
||
block->childs = NULL;
|
||
block->childs_count = 0;
|
||
info->block = block;
|
||
#endif
|
||
return true;
|
||
}
|
||
|
||
/* Write out line number information. */
|
||
|
||
static boolean
|
||
pr_lineno (p, filename, lineno, addr)
|
||
PTR p;
|
||
const char *filename;
|
||
unsigned long lineno;
|
||
bfd_vma addr;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
char ab[20];
|
||
debug_function_t* f = info->function;
|
||
(void)ab;
|
||
|
||
#if 0
|
||
indent (info);
|
||
print_vma (addr, ab, true, true);
|
||
TRACE_FPRINTF( (info->f, "/* file %s line %lu addr %s */\n", filename, lineno, ab));
|
||
#else /* 0 */
|
||
if (f==NULL) /* FIXME: skips junk silently. */
|
||
return true;
|
||
/* assert(f!=NULL); */
|
||
if (f->filename==NULL) {
|
||
f->filename = filename;
|
||
assert(f->lines==0);
|
||
f->max_lines_count = 4;
|
||
f->lines = xmalloc(sizeof(debug_lineno_t)*f->max_lines_count);
|
||
}
|
||
if (f->lines_count==f->max_lines_count) {
|
||
f->max_lines_count *= 2;
|
||
f->lines = xrealloc(f->lines, sizeof(debug_lineno_t)*f->max_lines_count);
|
||
}
|
||
f->lines[f->lines_count].lineno = lineno;
|
||
f->lines[f->lines_count].addr = addr;
|
||
++f->lines_count;
|
||
#endif /* !0 */
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Finish writing out a block. */
|
||
|
||
static boolean
|
||
pr_end_block (p, addr)
|
||
PTR p;
|
||
bfd_vma addr;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
|
||
#if 0
|
||
char ab[20];
|
||
|
||
info->indent -= 2;
|
||
indent (info);
|
||
print_vma (addr, ab, true, true);
|
||
TRACE_FPRINTF( (info->f, "} /* %s */\n", ab));
|
||
#else /* 0 */
|
||
assert(info->block!=0);
|
||
info->block->end_addr = addr;
|
||
info->block = info->block->parent;
|
||
#endif /* !0 */
|
||
|
||
return true;
|
||
}
|
||
|
||
/* Finish writing out a function. */
|
||
|
||
/*ARGSUSED*/
|
||
static boolean
|
||
pr_end_function (p)
|
||
PTR p;
|
||
{
|
||
struct pr_handle *info = (struct pr_handle *) p;
|
||
assert(info->block==0);
|
||
info->function = NULL;
|
||
return true;
|
||
}
|
||
|
||
/* third parameter to segv_action. */
|
||
/* Got it after a bit of head scratching and stack dumping. */
|
||
typedef struct {
|
||
u_int32_t foo1; /* +0x00 */
|
||
u_int32_t foo2;
|
||
u_int32_t foo3;
|
||
u_int32_t foo4; /* usually 2 */
|
||
u_int32_t foo5; /* +0x10 */
|
||
u_int32_t xgs; /* always zero */
|
||
u_int32_t xfs; /* always zero */
|
||
u_int32_t xes; /* always es=ds=ss */
|
||
u_int32_t xds; /* +0x20 */
|
||
u_int32_t edi;
|
||
u_int32_t esi;
|
||
u_int32_t ebp;
|
||
u_int32_t esp; /* +0x30 */
|
||
u_int32_t ebx;
|
||
u_int32_t edx;
|
||
u_int32_t ecx;
|
||
u_int32_t eax; /* +0x40 */
|
||
u_int32_t foo11; /* usually 0xe */
|
||
u_int32_t foo12; /* usually 0x6 */
|
||
u_int32_t eip; /* instruction pointer */
|
||
u_int32_t xcs; /* +0x50 */
|
||
u_int32_t foo21; /* usually 0x2 */
|
||
u_int32_t foo22; /* second stack pointer?! Probably. */
|
||
u_int32_t xss;
|
||
u_int32_t foo31; /* +0x60 */ /* usually 0x0 */
|
||
u_int32_t foo32; /* usually 0x2 */
|
||
u_int32_t fault_addr; /* Address which caused a fault */
|
||
u_int32_t foo41; /* usually 0x2 */
|
||
} signal_regs_t;
|
||
|
||
signal_regs_t* ptrace_regs = 0; /* Tells my_ptrace to "ptrace" current process" */
|
||
/*
|
||
* my_ptrace: small wrapper around ptrace.
|
||
* Act as normal ptrace if ptrace_regs==0.
|
||
* Read data from current process if ptrace_regs!=0.
|
||
*/
|
||
static int
|
||
my_ptrace( int request,
|
||
int pid,
|
||
int addr,
|
||
int data)
|
||
{
|
||
if (ptrace_regs==0)
|
||
return ptrace(request, pid, addr, data);
|
||
/* we are tracing ourselves! */
|
||
switch (request) {
|
||
case PTRACE_ATTACH: return 0;
|
||
case PTRACE_CONT: return 0;
|
||
case PTRACE_DETACH: return 0;
|
||
case PTRACE_PEEKUSER:
|
||
switch (addr / 4) {
|
||
case EIP: return ptrace_regs->eip;
|
||
case EBP: return ptrace_regs->ebp;
|
||
default: assert(0);
|
||
}
|
||
case PTRACE_PEEKTEXT: /* FALLTHROUGH */
|
||
case PTRACE_PEEKDATA: return *(int*)(addr);
|
||
default: assert(0);
|
||
}
|
||
errno = 1; /* what to do here? */
|
||
return 1; /* failed?! */
|
||
}
|
||
|
||
#define MAXARGS 6
|
||
|
||
/*
|
||
* To minimize the number of parameters.
|
||
*/
|
||
typedef struct {
|
||
asymbol** syms; /* Sorted! */
|
||
int symcount;
|
||
debug_function_t** functions;
|
||
int functions_size;
|
||
} symbol_data_t;
|
||
|
||
/*
|
||
* Perform a search. A binary search for a symbol.
|
||
*/
|
||
static void
|
||
decode_symbol( symbol_data_t* symbol_data,
|
||
const unsigned long addr,
|
||
char* buf,
|
||
const int bufsize)
|
||
{
|
||
asymbol** syms = symbol_data->syms;
|
||
const int symcount = symbol_data->symcount;
|
||
int bottom = 0;
|
||
int top = symcount - 1;
|
||
int i;
|
||
if (symcount==0) {
|
||
sprintf(buf, "????");
|
||
return;
|
||
}
|
||
while (top>bottom+1) {
|
||
i = (top+bottom) / 2;
|
||
if (bfd_asymbol_value(syms[i])==addr) {
|
||
sprintf(buf, "%s", syms[i]->name);
|
||
return;
|
||
} else if (bfd_asymbol_value(syms[i]) > addr)
|
||
top = i;
|
||
else
|
||
bottom = i;
|
||
}
|
||
i = bottom;
|
||
if (addr<bfd_asymbol_value(syms[i]) || addr>(syms[i]->section->vma+syms[i]->section->_cooked_size))
|
||
sprintf(buf, "????");
|
||
else
|
||
sprintf(buf, "%s + 0x%lx", syms[i]->name, addr-bfd_asymbol_value(syms[i]));
|
||
}
|
||
|
||
/*
|
||
* 1. Perform a binary search for an debug_function_t.
|
||
* 2. Fill buf/bufsize with name, parameters and lineno, if found
|
||
* Or with '????' otherwise.
|
||
*/
|
||
static debug_function_t*
|
||
find_debug_function_t( symbol_data_t* symbol_data,
|
||
const pid_t pid,
|
||
const unsigned long fp, /* frame pointer */
|
||
const unsigned long addr,
|
||
char* buf, /* string buffer */
|
||
const int bufsize)/* FIXME: not used! */
|
||
{
|
||
debug_function_t** syms = symbol_data->functions;
|
||
debug_function_t* f = NULL;
|
||
debug_block_t* block = NULL;
|
||
debug_lineno_t* lineno = NULL;
|
||
const int symcount = symbol_data->functions_size;
|
||
int bottom = 0;
|
||
int top = symcount - 1;
|
||
int i;
|
||
char* bufptr = buf;
|
||
|
||
if (symcount==0) {
|
||
sprintf(buf, "????");
|
||
return NULL;
|
||
}
|
||
while (top>bottom+1) {
|
||
i = (top+bottom) / 2;
|
||
if (syms[i]->block->begin_addr==addr) {
|
||
f = syms[i];
|
||
break;
|
||
} else if (syms[i]->block->begin_addr > addr)
|
||
top = i;
|
||
else
|
||
if (syms[i]->block->end_addr >= addr) {
|
||
f = syms[i];
|
||
break;
|
||
} else
|
||
bottom = i;
|
||
}
|
||
i = bottom;
|
||
if (f!=0)
|
||
block = f->block;
|
||
else {
|
||
block = syms[i]->block;
|
||
if (block->begin_addr>=addr && block->end_addr<=addr)
|
||
f = syms[i];
|
||
}
|
||
if (f==0)
|
||
sprintf(buf, "????");
|
||
else {
|
||
/*
|
||
* Do the backtrace the GDB way...
|
||
*/
|
||
unsigned long arg;
|
||
/* assert(f->lines_count>0); */
|
||
if (f->lines_count>0) {
|
||
lineno = &f->lines[f->lines_count-1];
|
||
for (i=1; i<f->lines_count; ++i)
|
||
if (f->lines[i].addr>addr) {
|
||
lineno = &f->lines[i-1];
|
||
break;
|
||
}
|
||
}
|
||
bufptr[0] = 0;
|
||
bufptr += sprintf(bufptr, "%s+0x%lx (", f->name, addr-block->begin_addr);
|
||
for (i=0; i<f->argc; ++i) {
|
||
bufptr += sprintf(bufptr, "%s = ", f->argv[i].name);
|
||
/* FIXME: better parameter printing */
|
||
errno = 0;
|
||
arg = my_ptrace(PTRACE_PEEKDATA, pid, fp+f->argv[i].offset, 0);
|
||
assert(errno==0);
|
||
bufptr += sprintf(bufptr, "0x%x", arg);
|
||
if (i!=f->argc-1)
|
||
bufptr += sprintf(bufptr, ", ");
|
||
}
|
||
if (lineno!=0)
|
||
bufptr += sprintf(bufptr, ") at %s:%d", f->filename, lineno->lineno);
|
||
}
|
||
return f;
|
||
}
|
||
|
||
/*
|
||
* Advance through the stacks and display frames as needed.
|
||
*/
|
||
static int
|
||
my_crawl( int pid,
|
||
symbol_data_t* symbol_data,
|
||
int fout)
|
||
{
|
||
unsigned long pc = 0;
|
||
unsigned long fp = 0;
|
||
unsigned long nextfp;
|
||
unsigned long nargs;
|
||
unsigned long i;
|
||
unsigned long arg;
|
||
char buf[8096]; // FIXME: enough?
|
||
debug_function_t* f = 0;
|
||
|
||
errno = 0;
|
||
|
||
pc = my_ptrace(PTRACE_PEEKUSER, pid, EIP * 4, 0);
|
||
if (!errno)
|
||
fp = my_ptrace(PTRACE_PEEKUSER, pid, EBP * 4, 0);
|
||
|
||
if (!errno) {
|
||
#if 1
|
||
f = find_debug_function_t(symbol_data, pid, fp, pc, buf, sizeof(buf));
|
||
fdprintf(fout,"0x%08lx: %s", pc, buf);
|
||
for ( ; !errno && fp; ) {
|
||
nextfp = my_ptrace(PTRACE_PEEKDATA, pid, fp, 0);
|
||
if (errno)
|
||
break;
|
||
|
||
if (f==0) {
|
||
nargs = (nextfp - fp - 8) / 4;
|
||
if (nargs > MAXARGS)
|
||
nargs = MAXARGS;
|
||
if (nargs > 0) {
|
||
fdputs(" (", fout);
|
||
for (i = 1; i <= nargs; i++) {
|
||
arg = my_ptrace(PTRACE_PEEKDATA, pid, fp + 4 * (i + 1), 0);
|
||
if (errno)
|
||
break;
|
||
fdprintf(fout,"%lx", arg);
|
||
if (i < nargs)
|
||
fdputs(", ", fout);
|
||
}
|
||
fdputc(')', fout);
|
||
nargs = nextfp - fp - 8 - (4 * nargs);
|
||
if (!errno && nargs > 0)
|
||
fdprintf(fout," + %lx\n", nargs);
|
||
else
|
||
fdputc('\n', fout);
|
||
} else
|
||
fdputc('\n', fout);
|
||
} else
|
||
fdputc('\n', fout);
|
||
|
||
if (errno || !nextfp)
|
||
break;
|
||
pc = my_ptrace(PTRACE_PEEKDATA, pid, fp + 4, 0);
|
||
fp = nextfp;
|
||
if (errno)
|
||
break;
|
||
f = find_debug_function_t(symbol_data, pid, fp, pc, buf, sizeof(buf));
|
||
fdprintf(fout,"0x%08lx: %s", pc, buf);
|
||
}
|
||
#else /* 1 */
|
||
decode_symbol(symbol_data, pc, buf, sizeof(buf));
|
||
fdprintf(fout,"0x%08lx: %s", pc, buf);
|
||
for ( ; !errno && fp; ) {
|
||
nextfp = my_ptrace(PTRACE_PEEKDATA, pid, fp, 0);
|
||
if (errno)
|
||
break;
|
||
|
||
nargs = (nextfp - fp - 8) / 4;
|
||
if (nargs > MAXARGS)
|
||
nargs = MAXARGS;
|
||
if (nargs > 0) {
|
||
fputs(" (", fout);
|
||
for (i = 1; i <= nargs; i++) {
|
||
arg = my_ptrace(PTRACE_PEEKDATA, pid, fp + 4 * (i + 1), 0);
|
||
if (errno)
|
||
break;
|
||
fdprintf(fout,"%lx", arg);
|
||
if (i < nargs)
|
||
fputs(", ", fout);
|
||
}
|
||
fdputc(')', fout);
|
||
nargs = nextfp - fp - 8 - (4 * nargs);
|
||
if (!errno && nargs > 0)
|
||
fdprintf(fout," + %lx\n", nargs);
|
||
else
|
||
fdputc('\n', fout);
|
||
} else
|
||
fdputc('\n', fout);
|
||
|
||
if (errno || !nextfp)
|
||
break;
|
||
pc = my_ptrace(PTRACE_PEEKDATA, pid, fp + 4, 0);
|
||
fp = nextfp;
|
||
if (errno)
|
||
break;
|
||
decode_symbol(symbol_data, pc, buf, sizeof(buf));
|
||
fdprintf(fout,"0x%08lx: %s", pc, buf);
|
||
}
|
||
#endif /* !1 */
|
||
}
|
||
if (errno)
|
||
perror("my_crawl");
|
||
return errno;
|
||
}
|
||
|
||
/* layout from /usr/src/linux/arch/i386/kernel/process.c */
|
||
static void
|
||
show_regs( signal_regs_t* regs,
|
||
int fd)
|
||
{
|
||
/* long cr0 = 0L, cr2 = 0L, cr3 = 0L; */
|
||
|
||
fdprintf(fd,"\n");
|
||
fdprintf(fd,"FAULT ADDR: %08x\n", regs->fault_addr);
|
||
fdprintf(fd,"EIP: %04x:[<%08x>]",0xffff & regs->xcs,regs->eip);
|
||
if (regs->xcs & 3)
|
||
fdprintf(fd," ESP: %04x:%08x",0xffff & regs->xss,regs->esp);
|
||
/*fdprintf(fd," EFLAGS: %08lx\n",regs->eflags); */
|
||
fdprintf(fd, "\n");
|
||
fdprintf(fd,"EAX: %08x EBX: %08x ECX: %08x EDX: %08x\n",
|
||
regs->eax,regs->ebx,regs->ecx,regs->edx);
|
||
fdprintf(fd,"ESI: %08x EDI: %08x EBP: %08x",
|
||
regs->esi, regs->edi, regs->ebp);
|
||
fdprintf(fd," DS: %04x ES: %04x\n",
|
||
0xffff & regs->xds,0xffff & regs->xes);
|
||
/*
|
||
__asm__("movl %%cr0, %0": "=r" (cr0));
|
||
__asm__("movl %%cr2, %0": "=r" (cr2));
|
||
__asm__("movl %%cr3, %0": "=r" (cr3));
|
||
fprintf(stderr,"CR0: %08lx CR2: %08lx CR3: %08lx\n", cr0, cr2, cr3); */
|
||
}
|
||
|
||
/*
|
||
* Load a BFD for an executable based on PID. Return 0 on failure.
|
||
*/
|
||
static bfd*
|
||
load_bfd( const int pid)
|
||
{
|
||
char filename[512];
|
||
bfd* abfd = 0;
|
||
|
||
/* Get the contents from procfs. */
|
||
#if 1
|
||
sprintf(filename, "/proc/%d/exe", pid);
|
||
#else
|
||
sprintf(filename, "crashing");
|
||
#endif
|
||
|
||
if ((abfd = bfd_openr (filename, 0))== NULL)
|
||
bfd_nonfatal (filename);
|
||
else {
|
||
char** matching;
|
||
assert(bfd_check_format(abfd, bfd_archive)!=true);
|
||
|
||
/*
|
||
* There is no indication in BFD documentation that it should be done.
|
||
* God knows why...
|
||
*/
|
||
if (!bfd_check_format_matches (abfd, bfd_object, &matching)) {
|
||
bfd_nonfatal (bfd_get_filename (abfd));
|
||
if (bfd_get_error () == bfd_error_file_ambiguously_recognized) {
|
||
list_matching_formats (matching);
|
||
free (matching);
|
||
}
|
||
}
|
||
}
|
||
return abfd;
|
||
}
|
||
|
||
/*
|
||
* Those are for qsort. We need only function addresses, so all the others don't count.
|
||
*/
|
||
/*
|
||
* Compare two BFD::asymbol-s.
|
||
*/
|
||
static int
|
||
compare_symbols(const void* ap,
|
||
const void* bp)
|
||
{
|
||
const asymbol *a = *(const asymbol **)ap;
|
||
const asymbol *b = *(const asymbol **)bp;
|
||
if (bfd_asymbol_value (a) > bfd_asymbol_value (b))
|
||
return 1;
|
||
else if (bfd_asymbol_value (a) < bfd_asymbol_value (b))
|
||
return -1;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Compare two debug_asymbol_t-s.
|
||
*/
|
||
static int
|
||
compare_debug_function_t(const void* ap,
|
||
const void* bp)
|
||
{
|
||
const debug_function_t *a = *(const debug_function_t **)ap;
|
||
const debug_function_t *b = *(const debug_function_t **)bp;
|
||
assert(a->block!=0);
|
||
assert(b->block!=0);
|
||
{
|
||
const bfd_vma addr1 = a->block->begin_addr;
|
||
const bfd_vma addr2 = b->block->begin_addr;
|
||
if (addr1 > addr2)
|
||
return 1;
|
||
else if (addr2 > addr1)
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Filter out (in place) symbols that are useless for stack tracing.
|
||
* COUNT is the number of elements in SYMBOLS.
|
||
* Return the number of useful symbols.
|
||
*/
|
||
|
||
static long
|
||
remove_useless_symbols( asymbol** symbols,
|
||
long count)
|
||
{
|
||
asymbol** in_ptr = symbols;
|
||
asymbol** out_ptr = symbols;
|
||
|
||
while (--count >= 0) {
|
||
asymbol *sym = *in_ptr++;
|
||
|
||
if (sym->name == NULL || sym->name[0] == '\0' || sym->value==0)
|
||
continue;
|
||
if (sym->flags & (BSF_DEBUGGING))
|
||
continue;
|
||
if (bfd_is_und_section (sym->section) || bfd_is_com_section (sym->section))
|
||
continue;
|
||
*out_ptr++ = sym;
|
||
}
|
||
return out_ptr - symbols;
|
||
}
|
||
|
||
/*
|
||
* Debugging information.
|
||
*/
|
||
static bfd* abfd = 0;
|
||
static PTR dhandle = 0;
|
||
static asymbol** syms = 0;
|
||
static long symcount = 0;
|
||
static asymbol** sorted_syms = 0;
|
||
static long sorted_symcount = 0;
|
||
static debug_function_t** functions = 0;
|
||
static int functions_size = 0;
|
||
static int sigreport = SIGUSR1;
|
||
static pthread_t segv_tid; /* What thread did SEGV? */
|
||
static pid_t segv_pid;
|
||
|
||
/*
|
||
* We'll get here after a SIGSEGV. But you can install it on other signals, too :)
|
||
* Because we are in the middle of the SIGSEGV, we are on our own. We can't do
|
||
* any malloc(), any fopen(), nothing. The last is actually a sin. We event can't
|
||
* fprintf(stderr,...)!!!
|
||
*/
|
||
static void
|
||
segv_action(int signo, siginfo_t* siginfo, void* ptr)
|
||
{
|
||
symbol_data_t symbol_data;
|
||
int fd = -1;
|
||
|
||
segv_pid = getpid();
|
||
segv_tid = pthread_self();
|
||
fd = open_log_file(segv_tid, segv_pid);
|
||
/* signal(SIGSEGV, SIG_DFL); */
|
||
ptrace_regs = (signal_regs_t*)ptr;
|
||
assert(ptrace_regs!=0);
|
||
|
||
/* Show user how guilty we are. */
|
||
fdprintf(fd,"--------- SEGV in PROCESS %d, THREAD %d ---------------\n", segv_pid, pthread_self());
|
||
show_regs(ptrace_regs, fd);
|
||
|
||
/* Some form of stack trace, too. */
|
||
fdprintf(fd, "STACK TRACE:\n");
|
||
|
||
symbol_data.syms = sorted_syms;
|
||
symbol_data.symcount = sorted_symcount;
|
||
symbol_data.functions = functions;
|
||
symbol_data.functions_size = functions_size;
|
||
my_crawl(segv_pid, &symbol_data, fd);
|
||
//fflush(stdout);
|
||
close(fd);
|
||
linuxthreads_notify_others(sigreport);
|
||
}
|
||
|
||
|
||
static void
|
||
report_action(int signo, siginfo_t* siginfo, void* ptr)
|
||
{
|
||
const int pid = getpid();
|
||
pthread_t tid = pthread_self();
|
||
symbol_data_t symbol_data;
|
||
int fd;
|
||
if (pthread_equal(tid, segv_tid)) {
|
||
/* We have already printed our stack trace... */
|
||
return;
|
||
}
|
||
|
||
fd = open_log_file(tid, pid);
|
||
fdprintf(fd, "REPORT: CURRENT PROCESS:%d, THREAD:%d\n", getpid(), pthread_self());
|
||
/* signal(SIGSEGV, SIG_DFL); */
|
||
ptrace_regs = (signal_regs_t*)ptr;
|
||
assert(ptrace_regs!=0);
|
||
|
||
/* Show user how guilty we are. */
|
||
fdprintf(fd,"--------- STACK TRACE FOR PROCESS %d, THREAD %d ---------------\n", pid, pthread_self());
|
||
show_regs(ptrace_regs, fd);
|
||
|
||
/* Some form of stack trace, too. */
|
||
fdprintf(fd, "STACK TRACE:\n");
|
||
|
||
symbol_data.syms = sorted_syms;
|
||
symbol_data.symcount = sorted_symcount;
|
||
symbol_data.functions = functions;
|
||
symbol_data.functions_size = functions_size;
|
||
my_crawl(pid, &symbol_data, fd);
|
||
//fflush(stdout);
|
||
close(fd);
|
||
/* Tell segv_thread to proceed after pause(). */
|
||
/*pthread_kill(segv_tid, sigreport);
|
||
kill(segv_pid, sigreport);
|
||
pthread_cancel(tid); */
|
||
}
|
||
|
||
/*
|
||
* Main library routine. Just call it on your program.
|
||
*/
|
||
int
|
||
pstack_install_segv_action( const char* path_format_)
|
||
{
|
||
const int pid = getpid();
|
||
struct sigaction act;
|
||
|
||
/* Store what we have to for later usage. */
|
||
path_format = path_format_;
|
||
|
||
/* We need a signal action for SIGSEGV and sigreport ! */
|
||
sigreport = SIGUSR1;
|
||
act.sa_handler = 0;
|
||
sigemptyset(&act.sa_mask);
|
||
act.sa_flags = SA_SIGINFO|SA_ONESHOT; /* Just one SIGSEGV. */
|
||
act.sa_sigaction = segv_action;
|
||
act.sa_restorer = NULL;
|
||
if (sigaction(SIGSEGV, &act, NULL)!=0) {
|
||
perror("sigaction");
|
||
return 1;
|
||
}
|
||
act.sa_sigaction = report_action;
|
||
act.sa_flags = SA_SIGINFO; /* But many sigreports. */
|
||
if (sigaction(sigreport, &act, NULL)!=0) {
|
||
perror("sigaction");
|
||
return 1;
|
||
}
|
||
|
||
/* And a little setup for libiberty. */
|
||
program_name = "crashing";
|
||
xmalloc_set_program_name (program_name);
|
||
|
||
/* Umm, and initialize BFD, too */
|
||
bfd_init();
|
||
#if 0
|
||
list_supported_targets(0, stdout);
|
||
set_default_bfd_target();
|
||
#endif /* 0 */
|
||
|
||
if ((abfd = load_bfd(pid))==0)
|
||
fprintf(stderr, "BFD load failed..\n");
|
||
else {
|
||
long storage_needed= (bfd_get_file_flags(abfd) & HAS_SYMS) ?
|
||
bfd_get_symtab_upper_bound (abfd) : 0;
|
||
long i;
|
||
(void)i;
|
||
|
||
if (storage_needed < 0)
|
||
fprintf(stderr, "Symbol table size estimation failure.\n");
|
||
else if (storage_needed > 0) {
|
||
syms = (asymbol **) xmalloc (storage_needed);
|
||
symcount = bfd_canonicalize_symtab (abfd, syms);
|
||
|
||
TRACE_FPRINTF((stderr, "TOTAL: %ld SYMBOLS.\n", symcount));
|
||
/* We need debugging info, too! */
|
||
if (symcount==0 || (dhandle = read_debugging_info (abfd, syms, symcount))==0)
|
||
fprintf(stderr, "NO DEBUGGING INFORMATION FOUND.\n");
|
||
|
||
/* We make a copy of syms to sort. We don't want to sort syms
|
||
because that will screw up the relocs. */
|
||
sorted_syms = (asymbol **) xmalloc (symcount * sizeof (asymbol *));
|
||
memcpy (sorted_syms, syms, symcount * sizeof (asymbol *));
|
||
|
||
#if 0
|
||
for (i=0; i<symcount; ++i)
|
||
if (syms[i]->name!=0 && strlen(syms[i]->name)>0 && syms[i]->value!=0)
|
||
printf("%08lx T %s\n", syms[i]->section->vma + syms[i]->value, syms[i]->name);
|
||
#endif
|
||
sorted_symcount = remove_useless_symbols (sorted_syms, symcount);
|
||
TRACE_FPRINTF((stderr, "SORTED: %ld SYMBOLS.\n", sorted_symcount));
|
||
|
||
/* Sort the symbols into section and symbol order */
|
||
qsort (sorted_syms, sorted_symcount, sizeof (asymbol *), compare_symbols);
|
||
#if 0
|
||
for (i=0; i<sorted_symcount; ++i)
|
||
if (sorted_syms[i]->name!=0 && strlen(sorted_syms[i]->name)>0 && sorted_syms[i]->value!=0)
|
||
printf("%08lx T %s\n", sorted_syms[i]->section->vma + sorted_syms[i]->value, sorted_syms[i]->name);
|
||
#endif
|
||
/* We have symbols, we need debugging info somehow sorted out. */
|
||
if (dhandle==0) {
|
||
fprintf(stderr, "STACK TRACE WILL BE UNCOMFORTABLE.\n");
|
||
} else {
|
||
/* Start collecting the debugging information.... */
|
||
struct pr_handle info;
|
||
|
||
info.f = stdout;
|
||
info.indent = 0;
|
||
info.stack = NULL;
|
||
info.parameter = 0;
|
||
info.block = NULL;
|
||
info.function = NULL;
|
||
info.functions_size = 0;
|
||
info.functions_maxsize = 1000;
|
||
info.functions = (debug_function_t*)xmalloc(sizeof(debug_function_t)*info.functions_maxsize);
|
||
debug_write (dhandle, &pr_fns, (PTR) &info);
|
||
TRACE_FPRINTF((stdout, "\n%d DEBUG SYMBOLS\n", info.functions_size));
|
||
assert(info.functions_size!=0);
|
||
functions = xmalloc(sizeof(debug_function_t*)*info.functions_size);
|
||
functions_size = info.functions_size;
|
||
for (i=0; i<functions_size; ++i)
|
||
functions[i] = &info.functions[i];
|
||
/* Sort the symbols into section and symbol order */
|
||
qsort (functions, functions_size, sizeof(debug_function_t*),
|
||
compare_debug_function_t);
|
||
#if 0
|
||
for (i=0; i<info.functions_size; ++i)
|
||
fprintf(stdout, "%08lx T %s\n", info.functions[i].block->begin_addr, info.functions[i].name);
|
||
#endif
|
||
fflush(stdout);
|
||
}
|
||
} else /* storage_needed == 0 */
|
||
fprintf(stderr, "NO SYMBOLS FOUND.\n");
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*********************************************************************/
|
||
/*********************************************************************/
|
||
/*********************************************************************/
|