view src/debug.c @ 16:c508ee15dfca

switch to automake/autoconf
author carl
date Sun, 19 Feb 2006 18:47:46 -0800
parents
children b88ceb81dba2
line wrap: on
line source

/* Contains the debug functions */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include "define.h"

#ifdef _WIN32
# define vsnprintf _vsnprintf
#endif

struct _debug_item {
  int type;
  char * function;
  unsigned int line;
  char * file;
  char * text;
  struct _debug_item *next;
} *item_head=NULL, *item_tail=NULL, *item_ptr=NULL, *info_ptr=NULL, *temp_list=NULL;

struct _debug_func {
  char * name;
  struct _debug_func *next;
} *func_head=NULL, *func_ptr=NULL;


void _debug_init(char *fname);
void _debug_msg_info (int line, char *file, int type);
void _debug_msg(char* fmt, ...);
void _debug_hexdump(char *x, int y, int cols);
void _debug_func(char *function);
void _debug_func_ret();
void _debug_close();
void _debug_write();
void _debug_write_msg(struct _debug_item *item, char *fmt, va_list *ap, int size);
void _debug_write_hex(struct _debug_item *item, char *buf, int size, int col);
void * xmalloc(size_t size);

// the largest text size we will store in memory. Otherwise we
// will do a debug_write, then create a new record, and write the
// text body directly to the file
#define MAX_MESSAGE_SIZE 4096

void _pst_debug(char *fmt, ...) {
  va_list ap;
  va_start(ap,fmt);
  vfprintf(stderr, fmt, ap);
  va_end(ap);
}

#define NUM_COL 30
void _pst_debug_hexdump(FILE *out, unsigned char *buf, size_t size, int col) {
  int off = 0, toff;
  int count = 0;
  
  if (col == -1) {
    col = NUM_COL;
  }
  fprintf(out, "\n");
  while (off < size) {
    fprintf(out, "%X\t:", off);
    toff = off;
    while (count < col && off < size) {
      fprintf(out, "%02hhx ", buf[off]);
      off++; count++;
    }
    off = toff;
    while (count < col) {
      // only happens at end of block to pad the text over to the text column
      fprintf(out, "   ");
      count++;
    }
    count = 0;
    fprintf(out, ":");
    while (count < col && off < size) {
      fprintf(out, "%c", isgraph(buf[off])?buf[off]:'.');
      off++; count ++;
    }

    fprintf(out, "\n");
    count=0;
  }

  fprintf(out, "\n");
}

void _pst_debug_hexprint(char *data, int size) {
  int i = 0;
  while (i < size) {
    fprintf(stderr, "%02hhX", data[i]);
    i++;
  }
}

FILE *debug_fp = NULL;
unsigned int max_items=DEBUG_MAX_ITEMS, curr_items=0;

void _debug_init(char* fname) {
  unsigned char version = DEBUG_VERSION;
  item_head = item_tail = NULL;
  curr_items = 0;
  if (debug_fp != NULL)
    _debug_close();
  if ((debug_fp = fopen(fname, "wb")) == NULL) {
    fprintf(stderr, "Opening of file %s failed\n", fname);
    exit(1);
  }
  fwrite(&version, 1, sizeof(char), debug_fp);
}

// function must be called before _debug_msg. It sets up the
// structure for the function that follows
void _debug_msg_info(int line, char* file, int type) {
  char *x;
  if (debug_fp == NULL) {
    fprintf(stderr, "debug_fp is NULL\n");
    return;
  }
  info_ptr = (struct _debug_item*) xmalloc(sizeof(struct _debug_item));
  info_ptr->type = type;
  info_ptr->line = line;
  x = (func_head==NULL?"No Function":func_head->name);
  info_ptr->function = (char*) xmalloc(strlen(x)+1);
  strcpy(info_ptr->function, x);
  
  info_ptr->file = (char*) xmalloc(strlen(file)+1);
  strcpy(info_ptr->file, file);
  
  //put the current record on a temp linked list
  info_ptr->next = temp_list;
  temp_list = info_ptr; 
}

void _debug_msg_text(char* fmt, ...) {
  va_list ap;
  int f, g;
  char x[2];
  struct _debug_item *temp;
  if (debug_fp == NULL)
    return;
  va_start(ap, fmt);
  // get the record off of the temp_list
  info_ptr = temp_list;
  if (info_ptr != NULL)
    temp_list = info_ptr->next;
  else {
    fprintf(stderr, "NULL info_ptr. ERROR!!\n");
    exit(-2);
  }
  // according to glibc 2.1, this should return the req. number of bytes for 
  // the string
#ifdef _WIN32
  // vsnprintf trick doesn't work. must use function called _vscprintf
  // cannot find much documentation about this on internet or anywhere.
  // I assume it isn't a standard function, but only in VisualC++
  f = _vscprintf(fmt, ap);
#else
  f = vsnprintf(x, 1, fmt, ap);
#endif

  if (f > 0 && f < MAX_MESSAGE_SIZE) {
    info_ptr->text = (char*) xmalloc(f+1);
    if ((g = vsnprintf(info_ptr->text, f, fmt, ap)) == -1) {
      fprintf(stderr, "_debug_msg: Dieing! vsnprintf returned -1 for format \"%s\"\n", fmt);
      exit(-2);
    }
    info_ptr->text[g] = '\0';
    if (f != g) {
      fprintf(stderr, "_debug_msg: f != g\n");
    }
  } else if (f > 0) { // it is over the max_message_size then
    f += strlen(info_ptr->file)+strlen(info_ptr->function);
    temp = info_ptr;
    _debug_write(); // dump the current messages
    info_ptr = temp;
    _debug_write_msg(info_ptr, fmt, &ap, f);
    free(info_ptr->function);
    free(info_ptr->file);
    free(info_ptr);
    info_ptr = NULL;
    return;
  } else {
    fprintf(stderr, "_debug_msg: error getting requested size of debug message\n");
    info_ptr->text = "ERROR Saving\n";
  }
  va_end(ap);

  if (item_head == NULL)
    item_head = info_ptr;

  info_ptr->next = NULL;
  if (item_tail != NULL)
    item_tail->next = info_ptr;
  item_tail = info_ptr;

  if (++curr_items == max_items) {
    // here we will jump off and save the contents
    _debug_write();
    info_ptr = NULL;
  }
}

void _debug_hexdump(char *x, int y, int cols) {
  struct _debug_item *temp;
  if (debug_fp == NULL)
    return;
  info_ptr = temp_list;
  if (info_ptr != NULL)
    temp_list = info_ptr->next;
  temp = info_ptr;
  _debug_write();
  info_ptr = temp;
  _debug_write_hex(info_ptr, x, y, cols);
  free(info_ptr->function);
  free(info_ptr->file);
  free(info_ptr);
  info_ptr = NULL;
}

void _debug_func(char *function) {
  func_ptr = xmalloc (sizeof(struct _debug_func));
  func_ptr->name = xmalloc(strlen(function)+1);
  strcpy(func_ptr->name, function);
  func_ptr->next = func_head;
  func_head = func_ptr;
}

void _debug_func_ret() {
  //remove the head item
  func_ptr = func_head;
  if (func_head != NULL) {
    func_head = func_head->next;
    free(func_ptr->name);
    free(func_ptr);
  } else {
    DIE(("function list is empty!\n"));
  }
}

void _debug_close(void) {
  _debug_write();
  while (func_head != NULL) {
    func_ptr = func_head;
    func_head = func_head->next;
    free(func_ptr->name);
    free(func_ptr);
  }

  if (debug_fp != NULL)
    fclose(debug_fp);
  debug_fp = NULL;

  if (func_head != NULL)
    while (func_head != NULL) {
      printf("function '%s' still on stack\n", func_head->name);
      func_head = func_head->next;
    }
}

void _debug_write() {
  size_t size, ptr, funcname, filename, text, end;
  char *buf, rec_type;
  long index_pos = ftell (debug_fp), file_pos = index_pos;
  // add 2. One for the pointer to the next index, 
  // one for the count of this index
  int index_size = ((curr_items+2) * sizeof(int));
  int *index;
  int index_ptr = 0;
  struct _debug_file_rec_m mfile_rec;
  struct _debug_file_rec_l lfile_rec;

  if (curr_items == 0)
    // no items to write.
    return; 
  index = (int*) xmalloc(index_size);
  file_pos += index_size;
  // write the index first, we will re-write it later, but
  // we want to allocate the space
  fwrite(index, index_size, 1, debug_fp);
  index[index_ptr++] = curr_items;

  item_ptr = item_head;
  while (item_ptr != NULL) {
    file_pos = ftell(debug_fp);
    index[index_ptr++] = file_pos;
    size = strlen(item_ptr->function)+strlen(item_ptr->file)+
      strlen(item_ptr->text) + 3; //for the three \0s
    buf = xmalloc(size+1);
    ptr = 0;
    funcname=ptr;
    ptr += sprintf(&(buf[ptr]), "%s", item_ptr->function)+1;
    filename=ptr;
    ptr += sprintf(&(buf[ptr]), "%s", item_ptr->file)+1;
    text=ptr;
    ptr += sprintf(&(buf[ptr]), "%s", item_ptr->text)+1;
    end=ptr;
    if (end > USHRT_MAX) { // bigger than can be stored in a short
      rec_type = 'L';
      fwrite(&rec_type, 1, sizeof(char), debug_fp);
      lfile_rec.type = item_ptr->type;
      lfile_rec.line = item_ptr->line;
      lfile_rec.funcname = funcname;
      lfile_rec.filename = filename;
      lfile_rec.text = text;
      lfile_rec.end = end;
      fwrite(&lfile_rec, sizeof(lfile_rec), 1, debug_fp);
    } else {
      rec_type = 'M';
      fwrite(&rec_type, 1, sizeof(char), debug_fp);
      mfile_rec.type = item_ptr->type;
      mfile_rec.line = item_ptr->line;
      mfile_rec.funcname = funcname;
      mfile_rec.filename = filename;
      mfile_rec.text = text;
      mfile_rec.end = end;
      fwrite(&mfile_rec, sizeof(mfile_rec), 1, debug_fp);
    }
    fwrite(buf, 1, ptr, debug_fp);
    item_head = item_ptr->next;
    free(item_ptr->function);
    free(item_ptr->file);
    free(item_ptr->text);
    free(item_ptr);
    item_ptr = item_head;
  }
  curr_items = 0;
  index[index_ptr] = ftell(debug_fp);

  // we should now have a complete index
  fseek(debug_fp, index_pos, SEEK_SET);
  fwrite(index, index_size, 1, debug_fp);
  fseek(debug_fp, 0, SEEK_END);
  item_ptr = item_head = item_tail = NULL;
  free(index);
}

void _debug_write_msg(struct _debug_item *item, char *fmt, va_list *ap, int size) {
  struct _debug_file_rec_l lfile_rec;
  struct _debug_file_rec_m mfile_rec;
  unsigned char rec_type;
  int index_size = 3 * sizeof(int);
  int *index = malloc(index_size);
  int index_pos, file_pos;
  char zero='\0';
  unsigned int end;
  index[0] = 1; //only one item in this index
  index_pos = ftell(debug_fp);
  fwrite(index, index_size, 1, debug_fp);

  index[1] = ftell(debug_fp);
  
  if (size > USHRT_MAX) { // bigger than can be stored in a short
    rec_type = 'L';
    fwrite(&rec_type, 1, sizeof(char), debug_fp);
    lfile_rec.type = item->type;
    lfile_rec.line = item->line;
    lfile_rec.funcname = 0;
    lfile_rec.filename = strlen(item->function)+1;
    lfile_rec.text = lfile_rec.filename+strlen(item->file)+1;
    fwrite(&lfile_rec, sizeof(lfile_rec), 1, debug_fp);
  } else {
    rec_type = 'M';
    fwrite(&rec_type, 1, sizeof(char), debug_fp);
    mfile_rec.type = item->type;
    mfile_rec.line = item->line;
    mfile_rec.funcname = 0;
    mfile_rec.filename = strlen(item->function)+1;
    mfile_rec.text = mfile_rec.filename+strlen(item->file)+1;
    fwrite(&mfile_rec, sizeof(mfile_rec), 1, debug_fp);
  }
  file_pos = ftell(debug_fp);
  fwrite(item->function, strlen(item->function)+1, 1, debug_fp);
  fwrite(item->file, strlen(item->file)+1, 1, debug_fp);
  vfprintf(debug_fp, fmt, *ap);
  fwrite(&zero, 1, 1, debug_fp);

  end = ftell(debug_fp)-file_pos;

  index[2] = ftell(debug_fp);
  fseek(debug_fp, index_pos, SEEK_SET);
  fwrite(index, index_size, 1, debug_fp);
  if (size > USHRT_MAX) {
    fwrite(&rec_type, 1, sizeof(char), debug_fp);
    lfile_rec.end = end;
    fwrite(&lfile_rec, sizeof(lfile_rec), 1, debug_fp);
  } else {
    fwrite(&rec_type, 1, sizeof(char), debug_fp);
    mfile_rec.end = end;
    fwrite(&mfile_rec, sizeof(mfile_rec), 1, debug_fp);
  }
  fseek(debug_fp, 0, SEEK_END);
  // that should do it...
}

void _debug_write_hex(struct _debug_item *item, char *buf, int size, int col) {
  struct _debug_file_rec_l lfile_rec;
  unsigned char rec_type;
  int index_size = 3 * sizeof(int);
  int *index = malloc(index_size);
  int index_pos, file_pos;
  char zero='\0';
  index[0] = 1; // only one item in this index run
  index_pos = ftell(debug_fp);
  fwrite(index, index_size, 1, debug_fp);
  index[1] = ftell(debug_fp);

  // always use the long
  rec_type = 'L';
  fwrite(&rec_type, 1, sizeof(char), debug_fp);
  lfile_rec.type = item->type;
  lfile_rec.line = item->line;
  lfile_rec.funcname = 0;
  lfile_rec.filename = strlen(item->function)+1;
  lfile_rec.text = lfile_rec.filename+strlen(item->file)+1;
  fwrite(&lfile_rec, sizeof(lfile_rec), 1, debug_fp);

  file_pos = ftell(debug_fp);
  fwrite(item->function, strlen(item->function)+1, 1, debug_fp);
  fwrite(item->file, strlen(item->file)+1, 1, debug_fp);
  
  _pst_debug_hexdump(debug_fp, buf, size, col);
  fwrite(&zero, 1, 1, debug_fp);
  lfile_rec.end = ftell(debug_fp)-file_pos;

  index[2] = ftell(debug_fp);
  fseek(debug_fp, index_pos, SEEK_SET);
  fwrite(index, index_size, 1, debug_fp);
  fwrite(&rec_type, 1, sizeof(char), debug_fp);
  fwrite(&lfile_rec, sizeof(lfile_rec), 1, debug_fp);
  fseek(debug_fp, 0, SEEK_END);
}
  
void * xmalloc(size_t size) {
  void *mem = malloc(size);
  if (mem == NULL) {
    fprintf(stderr, "xMalloc: Out Of memory [req: %ld]\n", (long)size);
    exit(1);
  }
  return mem;
}