/*
 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[20];

  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_symtab_upper_bound (abfd);
		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;
}

/*********************************************************************/
/*********************************************************************/
/*********************************************************************/
