/* (C) Copyright International Business Machines Corporation 23 January */
/* 1990.  All Rights Reserved. */
/*  */
/* See the file USERAGREEMENT distributed with this software for full */
/* terms and conditions of use. */
/* File: debug.c */
/* Author: David F. Bacon */
#ifndef lint
static char sccsinfo[] = "@(#)debug.c	1.15 3/14/90";
#endif

/*
 * debugging and error messages.
 *
 * For now, we just write to stderr.  In the long run, that has to be replaced
 * with a more coherent error handling strategy capable of handling multiple
 * users and multiple i/o devices.
 *
 * In other words, these are all quick-and-dirty hacks.
 */

#include <stdio.h>

#include "ops.h"
#include "recursiv.h"
#include "accessors.h"

#include "predefined.cd"
#include "interpform.cd"

/* A few global variables that come in handy in dbx */
objectp otmp, otmp1, otmp2, otmp3, otmp4;
valcell vtmp, vtmp1, vtmp2, vtmp3, vtmp4;

#define DEBUGFLAG "HDEBUG"

int debugging_level;		/* this is used to control certain debugging */
				/*  messages etc. */

#define INDENTATION "  "
				/* indent each recursive structure with this */
				/*  string when using the debug print function */


void
init_debug()
{
    char *getenv();

    char *debugstring;


    debugstring = getenv(DEBUGFLAG);

    if (debugstring)
      debugging_level = atoi(debugstring);
    else
      debugging_level = 0;

#ifdef sun
    if (debug_level(20))
      malloc_debug(1);

    if (debug_level(99))
      malloc_debug(2);		/* very low degree of optimism about storage */
				/*  utilization. */
#endif
}


status
debug_level(l)
int l;
{
    return(debugging_level >= l);
}


void
set_debug_level(l)
int l;
{
    debugging_level = l;
}



void
nilerror(routine, msg)
char *routine;			/* name of routine that died. */
char *msg;			/* error message to print. */
{
    void pprocs();

    (void) fprintf(stderr, "hermi(%s) - %s.\n", routine, msg);
}


void
nilperror(routine, msg)
char *routine;			/* name of routine that died. */
char *msg;			/* error message to print. */
{
#ifndef _AIX
    char *sprintf();
#endif

    char msgbuf[256];


    (void) sprintf(msgbuf, "hermi(%s) - %s.  Unix error", routine, msg);
    perror(msgbuf);
}

void
abort_nili(routine)
char *routine;
{
    (void) fprintf(stderr, "hermi(%s) - ABORTING\n", routine);
    exit(1);
}


/* recursive routines to print an object */

NILOP(o_print)
{
    void re_print();


    re_print(stderr, 0, DstObj);
    (void) fflush(stderr);
}


void
oprt(objp)
object *objp;
{
    void re_print();

    re_print(stderr,0,objp);
}


void
vprt(val, dr)
valcell val;
datarep *dr;
{
    void re_print();

    object obj;


    obj.value = val;
    obj.tsdr = dr;
    re_print(stderr, 0, &obj);
}


void
re_print(f, indent, obj)
FILE *f;
int indent;
object *obj;
{
    void indent_for_print();


    indent_for_print(f, indent);
    (void) fprintf(f, "%s: ", obj->tsdr->name);

    Print(f, indent, obj);
}


void
indent_for_print(f, indent)
FILE *f;
int indent;
{
    while (indent-- > 0) 
      (void) fprintf(f, INDENTATION);
}    


/*ARGSUSED*/
void
prt_bottom(f, indent, val)
FILE *f;
int indent;
valcell val;
{
    (void) fprintf(f, "(None)\n");
}



static char *proctypes[6] = {
    "Process",
    "ProcedurePrimaryUsed",
    "ProcedurePrimaryFree",
    "ProcedurePrimaryDead",
    "ProcedureSecondary",
    "CProcess"
  };


void
pprocs()
{
    void showstatus();

    extern schedblock schedb;

    pcb *head, *pcblist;
    status empty = TRUE;

    /* first the ready processes (also count revived) */
    head = pcblist = schedb.ready;
    if (pcblist) {
      empty = FALSE;
      do {
	showstatus(pcblist);
	pcblist = cdr(pcblist);
      } while (pcblist isnt head);
    }
    head = pcblist = schedb.newprocs;
    if (pcblist) {
      empty = FALSE;
      do {
	showstatus(pcblist);
	pcblist = cdr(pcblist);
      } while (pcblist isnt head);
    }
    /* next the blocked processes */
    head = pcblist = schedb.blocked;
    if (pcblist) {
      empty = FALSE;
      do {
	showstatus(pcblist);
	pcblist = cdr(pcblist);
      } while (pcblist isnt head);
    }
    head = pcblist = schedb.blocked_nokey;
    if (pcblist) {
      empty = FALSE;
      do {
	  showstatus(pcblist);
	  pcblist = cdr(pcblist);
      } while (pcblist isnt head);
    }
    if (empty)
      fprintf(stderr, "[No processes]\n");
}


void
showstatus(proc)
pcb *proc;
{
    char *errorname();

    extern schedblock schedb;
    valcell prog;
    objectp Lasterr;
    char state, current;

    if (proc is nil) {
	fprintf(stderr, "[NO PROCESS]\n");
	return;
    }

    current = (proc is schedb.ready ? '*' : ' ');
    switch (proc->state) {
    case READY:
    case REVIVED:
      state = 'R';
      break;
    case BLOCKED:
      state = 'S';
      break;
    case KILLED:
      state = 'K';
    }
    if (proc->type is CProcess)
      (void) fprintf(stderr, "%c%c %-20s  %5d\n",
		     current, state, "C-Process", proc->ip);
    else {
	prog = dcdot(proc->prog, program__LI_PROGRAM);
	Lasterr = & proc->ep.h->data[LASTERROR];
	
	(void) fprintf(stderr, "%c%c %-20s  %5d/%-5d  %-20s  (%s)\n",
		       current, state,
		       drdot(prog, prog__name) isnt &dr_bottom ?
		         vstringval(dot(prog, prog__name)) : "(no name)",
		       proc->ip,
		       dot(prog, prog__code).table->size,
		       errorname(Lasterr),
		       proctypes[(int) proc->type]);
    }
}


static char *builtinerrs[] = {
    "CaseError",
    "ConstraintError",
    "ConstraintFailure",
    "Depletion",
    "Disconnected",
    "DivideByZero",
    "DuplicateKey",
    "InterfaceMismatch",
    "NotFound",
    "PolymorphMismatch",
    "DefinitionError",
    "RangeError",
    "Uncopyable"
  };


char *
errorname(Lasterr)
objectp Lasterr;
{
    switch (case_of(Lasterr->value)) {
      case handler_type__builtin: {
	  return(builtinerrs[dot(Lasterr->value,
				 VARIANT_COMPONENT).enumeration]);
      }

      case handler_type__user: {
	  return("user exception");
      }

      case handler_type__exit: {
	  return("user exit");
      }

      case handler_type__others: {
	  return("none");
      }
    }

    return("(bogus errorname)");
}
