mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-30 04:26:45 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			2747 lines
		
	
	
		
			61 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2747 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;
 | ||
| }
 | ||
| 
 | ||
| /*********************************************************************/
 | ||
| /*********************************************************************/
 | ||
| /*********************************************************************/
 | 
