/* ----------------------------------------------------------------------------- 
 * default.c
 *
 *     Default signal handler.  Just prints a stack trace and returns.
 * 
 * Author(s) : David Beazley (beazley@cs.uchicago.edu)
 *
 * Copyright (C) 2000.  The University of Chicago. 
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * See the file COPYING for a complete copy of the LGPL.
 * ----------------------------------------------------------------------------- */

#include "wad.h"

static char cvs[] = "$Header: /cvs/projects/SWIG/Tools/WAD/Wad/default.c,v 1.16 2001/06/24 20:01:03 beazley Exp $";

#include <sys/mman.h>


/* This function tries to produce some kind of sensible argument 
   string for a stack frame.  If no debugging information is available,
   we'll just dump the %i0-%i5 registers in hex.  If debugging information
   is available, we'll try to do something a little more sensible */
 
char *wad_arg_string(WadFrame *frame) {
  static char str[1024];
  WadLocal *wp;
  long    *stack;
  long    *nextstack;
  long    *prevstack;
  int     i;
  WadFrame *nf;
  WadFrame *pf;

  nf = frame->next;
  if (nf) 
    nextstack = (long *) nf->stack;
  else
    nextstack = 0;

  pf = frame->prev;
  if (pf)
    prevstack = (long *) pf->stack;
  else
    prevstack = 0;

  str[0] = 0;
  stack = (long *) frame->stack;


#ifdef WAD_LINUX
  if (!nf) {
    return "";
  }
#endif

  if ((frame->debug_nargs < 0) || (0)){
    /* No argument information is available. If we are on SPARC, we'll dump
       the %in registers since these usually hold input parameters.  On
       Linux, we do nothing */
    
#ifdef WAD_SOLARIS
    for (i = 0; i < 6; i++) {
      wad_strcat(str,"0x");
      wad_strcat(str,wad_format_hex((unsigned long) stack[8+i],0));
      if (i < 5)
	wad_strcat(str,",");
    }
#endif
  } else {
    /* We were able to get some argument information out the debugging table */
    wp = frame->debug_args;
    for (i = 0; i < frame->debug_nargs; i++, wp = wp->next) {
      wad_strcat(str,wp->name);
      wad_strcat(str,"=");
      wad_strcat(str,wad_format_var(wp));
      if (i < (frame->debug_nargs-1)) wad_strcat(str,",");
    }
  }
  return str;

}

char *wad_strip_dir(char *name) {
  char *c;
  /*  printf("strip: '%s'\n", name); */
  c = name + strlen(name);
  while (c != name) {
    if (*c == '/') {
      c++;
      return c;
    }
    c--;
  }
  return name;
}



static char *src_file = 0;
static int   src_len = 0;
static char  src_path[1024] = "";

/* Opens up a source file and tries to locate a specific line number */

char *wad_load_source(char *path, int line) {
  int fd;
  char *c;
  char *start;
  int   n;

  if (strcmp(src_path,path)) {
    if (src_file) {
      munmap(src_file, src_len);
      src_file = 0;
      src_len = 0;
    }
    fd = open(path, O_RDONLY);
    if (fd < 0) return 0;
    src_len = lseek(fd, 0, SEEK_END);
    lseek(fd,0,SEEK_SET);
    src_file = (char *)mmap(NULL,src_len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    if (src_file == MAP_FAILED) {
      close(fd);
      return 0;
    }
    close(fd);
    wad_strcpy(src_path,path);
  } 
  n = 0;
  start = src_file;
  c = src_file;
  while (n < src_len) {
    if (*c == '\n') {
      line--;
      if (line == 0) {
	return start;
      }
      start = c+1;
    }
    c++;
    n++;
  }
  return 0;
}

void wad_release_source() {
  if (src_file) {
    munmap(src_file,src_len);
    src_file = 0;
    src_len = 0;
    src_path[0] = 0;
  }
}

/* -----------------------------------------------------------------------------
 * wad_debug_src_code(WadFrame *f)
 *
 * Get source code for a frame 
 * ----------------------------------------------------------------------------- */

char *wad_debug_src_string(WadFrame *f, int window) {
  static char temp[16384];

  if (f->loc_srcfile && strlen(f->loc_srcfile) && (f->loc_line > 0)) {
    char *line, *c;
    int   i;
    int   first, last;
    first = f->loc_line - window;
    last  = f->loc_line + window;
    if (first < 1) first = 1;
    line = wad_load_source(f->loc_srcfile,first);
    if (line) {
      wad_strcpy(temp,f->loc_srcfile);
      wad_strcat(temp,", line ");
      wad_strcat(temp,wad_format_signed(f->loc_line,-1));
      wad_strcat(temp,"\n\n");
      for (i = first; i <= last; i++) {
	if (i == f->loc_line) wad_strcat(temp," => ");
	else                  wad_strcat(temp,"    ");
	c = strchr(line,'\n');
	if (c) {
	  *c = 0;
	  wad_strcat(temp,line);
	  wad_strcat(temp,"\n");
	  *c = '\n';
	} else {
	  wad_strcat(temp,line);
	  wad_strcat(temp,"\n");
	  break;
	}
	line = c+1;
      }
      f->debug_srcstr = wad_strdup(temp);
      return f->debug_srcstr;
    }
  }
  f->debug_srcstr = 0;
  return 0;
}

/* -----------------------------------------------------------------------------
 * wad_debug_make_strings(WadFrame *f)
 *
 * This function walks the stack trace and tries to generate a debugging string
 * ----------------------------------------------------------------------------- */

void 
wad_debug_make_strings(WadFrame *f) {
  static char msg[16384];
  while (f) {
    wad_strcpy(msg,"#");
    wad_strcat(msg,wad_format_signed(f->frameno,3));
    wad_strcat(msg," 0x");
    wad_strcat(msg,wad_format_hex(f->pc,1));
    wad_strcat(msg," in ");
    wad_strcat(msg, f->sym_name ? f->sym_name : "?");
    wad_strcat(msg,"(");
    wad_strcat(msg,wad_arg_string(f));
    wad_strcat(msg,")");
    if (f->loc_srcfile && strlen(f->loc_srcfile)) {
      wad_strcat(msg," in '");
      wad_strcat(msg, wad_strip_dir(f->loc_srcfile));
      wad_strcat(msg,"'");
      if (f->loc_line > 0) {
	wad_strcat(msg,", line ");
	wad_strcat(msg,wad_format_signed(f->loc_line,-1));
	/* Try to locate the source file */
	wad_debug_src_string(f, WAD_SRC_WINDOW);
      }
    } else {
      if (f->loc_objfile && strlen(f->loc_objfile)) {
	wad_strcat(msg," from '");
	wad_strcat(msg, wad_strip_dir(f->loc_objfile));
	wad_strcat(msg,"'");
      }
    }
    wad_strcat(msg,"\n");
    f->debug_str = wad_strdup(msg);
    f = f->next;
  }
}

/* Dump trace to a file */
void wad_dump_trace(int fd, int signo, WadFrame *f, char *ret) {
  static char buffer[128];
  char  *srcstr = 0;

  switch(signo) {
  case SIGSEGV:
    write(fd,"WAD: Segmentation fault.\n", 25);
    break;
  case SIGBUS:
    write(fd,"WAD: Bus error.\n",17);
    break;
  case SIGABRT:
    write(fd,"WAD: Abort.\n",12);
    break;
  case SIGFPE:
    write(fd,"WAD: Floating point exception.\n", 31);
    break;
  case SIGILL:
    write(fd,"WAD: Illegal instruction.\n", 26);
    break;
  default:
    sprintf(buffer,"WAD: Signal %d\n", signo);
    write(fd,buffer,strlen(buffer));
    break;
  }
  /* Find the last exception frame */

  while (f && !(f->last)) {
    f = f->next;
  }

  while (f) {
    write(fd,f->debug_str,strlen(f->debug_str));
    if (f->debug_srcstr) {
      srcstr = f->debug_srcstr;
    }
    f = f->prev;
  }
  if (srcstr) {
    write(fd,"\n",1);
    write(fd,srcstr,strlen(srcstr));
    write(fd,"\n",1);
  }
}

/* -----------------------------------------------------------------------------
 * Default callback
 * ----------------------------------------------------------------------------- */

void wad_default_callback(int signo, WadFrame *f, char *ret) {
  wad_dump_trace(2,signo,f,ret);
}

