/* FILE: debug.c * * Project: DeBug Utilities * Version: 2.1 * * * This file contains: * * 1. main() * 2. screen_list() * 3. send_status() * * Created: 5/24/89 * Author: Mark Porter (fog) * * * $Revision: 1.1 $ * $Date: 92/05/05 19:56:10 $ * $Author: fog $ * * * Copyright © 1992 if...only Amiga * * Permission is granted to distribute this program's source, executable, * and documentation for non-commercial use only, provided the copyright * and header information are left intact. * */ /*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------* * * * $Log: debug.c,v $ * Revision 1.1 92/05/05 19:56:10 fog * Initial revision * * * *----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ #include #include #include #include #include "trace.h" #include "debug.h" /* The DB_function array contains strings corresponding to functions in */ /* the application which is being debugged. If DB_Screen is db_include */ /* the list contains function names for which we want output. If */ /* DB_Screen is db_exclude the list contains functions for which we do */ /* not want to see output. */ static char DB_Function[ DB_NUM ][ DB_BUFSIZE ]; static DB_Type DB_Screen = db_none; /* Function screening type. */ static int DB_Tos = -1; /* Top-of-Stack for DB_Function. */ static int DB_Level = 0; /* Debug Level for output. */ static BOOL DB_Check = FALSE; /* DB_CHECK message received. */ /* DB_List is used to pass a list of pointers giving tDB access to the */ /* DB_Function List entries. This allows the user to check on which */ /* functions are currently in the list. */ char *DB_List[ DB_NUM ]; /* Version string so the AmigaDOS VERSION command will work. */ char *Version = "$VER: debug version 1.1 07-May-92"; /*------------ main() --------------- * * * FUNCTION: Waits for messages to be received from other tasks and then * prints values. * * ARGUMENTS: Standard command line arguments. * * RETURNS: Nothing. * * COMMENTS: This program is a stand-alone program to be used with the * Trace() function to debug tasks. It allows a task to send * output to the display even though DOS is not active (ie: the * task is not a "process". * * This function will not close down on its own, it will wait * until it receives a message with code = DB_QUIT. If the * program using the Trace() function does not use the DB_QUIT * message I have provided an option for tDB that will close * this program down properly. * */ main( argc,argv ) int argc; char *argv[]; { struct FileHandle *dos_fh; /* Pointer to CON window. */ struct MsgPort *port; /* debug MsgPort. */ Debug_Msg *msg = NULL; /* Pointer to message sent. */ FILE *fd; /* Debug file if necessary. */ long count; /* Length of string to output.*/ long clock; /* Time stamp variable. */ BOOL send = FALSE; /* Send output to window? */ BOOL write = FALSE; /* Write output to file? */ BOOL serial = FALSE; /* Write to serial port? */ BOOL parallel = FALSE; /* Write to parallel port? */ extern char *ctime(); /* Functions used to generate */ extern long time(); /* time stamp on output. */ /* First open the Console window for primary debugging output. */ dos_fh = Open( "CON:0/0/480/100/DB Output: v1.1",MODE_NEWFILE ); /* If there was a command line argument, set debug level to it. */ DB_Level = ( argc < 2 ) ? 0 : atoi( argv[ 1 ] ); /* Create a MsgPort for receiving data from Trace() or tDB. */ if ( port = CreatePort( "Trace.DBPort",0L )) { Write( dos_fh,"Trace.DBPort opened successfully\n",34L ); /* Now loop forever, receiving and processing messages. */ while( TRUE ) { /* If no current message, wait till one gets here, then pull */ /* it from the MsgPort with GetMsg(). */ if (( msg = ( Debug_Msg * )GetMsg( port )) == NULL ) { WaitPort( port ); msg = ( Debug_Msg * )GetMsg( port ); } if ( msg->DB_code == DB_QUIT ) /* Someone is telling us */ { /* to close down, so... */ ReplyMsg( msg ); /* reply to sender... */ break; /* and exit forever loop. */ } send = FALSE; /* Disable output till we know what type of */ /* message has been sent. */ switch( msg->DB_code ) { /* Message codes are fairly self-explanatory. See other */ /* documentation for complete description. */ case DB_WRITE: if (( fd = fopen( msg->DB_file,"a" )) == NULL ) Write( dos_fh,"File open failed\n",18L ); else { write = TRUE; clock = time( NULL ); fprintf( fd,"\n\n\n%s\n",ctime( &clock )); } break; case DB_ENDWRITE: if ( write == TRUE ) { fclose( fd ); write = FALSE; } break; case DB_SER: serial = TRUE; clock = time( NULL ); kprintf( "\n\n\n%s\n",ctime( &clock )); break; case DB_PAR: parallel = TRUE; clock = time( NULL ); dprintf( "\n\n\n%s\n",ctime( &clock )); break; case DB_TOGGLE: if ( DB_Screen == db_include ) DB_Screen = db_exclude; else if ( DB_Screen == db_exclude ) DB_Screen = db_include; break; case DB_LEVEL: DB_Level = msg->DB_level; break; case DB_ENDPAR: parallel = FALSE; break; case DB_ENDSER: serial = FALSE; break; case DB_CHECK: DB_Check = TRUE; break; case DB_ADDFUNC: add_functions( msg ); break; case DB_REMFUNC: rem_functions( msg ); break; case DB_CLEAR: clear_functions(); break; /* DB_CONTINUE value is really the one which will have been */ /* sent from the program being debugged. All previous are */ /* from tDB for adjusting debug internal parameters. In */ /* this case we allow output to be generated after having */ /* looked at the DB_Function List, DB_Level, and DB_Screen. */ /* All that code is handled within screen_list() below. */ case DB_CONTINUE: send = screen_list( msg ); break; /* The default value is an error. In this case change the */ /* DB_code value to inform the sender. */ default: msg->DB_code = DB_UNKNOWN; break; } if ( send == TRUE ) { /* If we have determined that output should be sent, then */ /* we write to each of the devices indicated by the BOOL */ /* variables. */ count = ( long )strlen( msg->DB_function ); Write( dos_fh,msg->DB_function,count ); Write( dos_fh,": ",2L ); Write( dos_fh,msg->DB_string,msg->DB_count ); if ( write == TRUE ) fprintf( fd,"%s: %s",msg->DB_function,msg->DB_string ); if ( serial == TRUE ) kprintf( "%s: %s",msg->DB_function,msg->DB_string ); if ( parallel == TRUE ) dprintf( "%s: %s",msg->DB_function,msg->DB_string ); } ReplyMsg( msg ); /* Reply to sender. */ send_status( port ); /* Send status info if necessary. */ } /* When we exit the loop, first make sure all messages have been */ /* removed from the MsgPort. If this is not done, we guarantee */ /* a system crash very quickly. Cleanup is done within Forbid() */ /* Permit() calls to prevent other message from arriving while */ /* shutdown is occuring. */ Forbid(); { while( msg = ( Debug_Msg * )GetMsg( port )) ReplyMsg( msg ); DeletePort( port ); } Permit(); /* Write info in Console window, delay so user can read it, and */ /* then close the window. */ Write( dos_fh,"Trace DeBugging Ending!\n",25L ); Delay( 50L ); Close( dos_fh ); if ( write == TRUE ) fclose( fd ); /* Close file if necessary. */ } else fprintf( stderr," *** debug Error *** Could not open Trace.DBPort\n" ); } /*------------ add_functions() ------------ * * * FUNCTION: Adds functions to screening function list. * * ARGUMENTS: 1. msg: Debug_Msg received. * * RETURNS: Nothing. * * COMMENTS: DB_function will have been set up by tDB to be an array * of pointers to character strings which we wish to enter * into the DB_Function list. * */ void add_functions( msg ) Debug_Msg *msg; { char **str; int i; str = ( char ** )msg->DB_function; for ( i = 0; str[ i ]; i++ ) { /* If the user attempts to add more functions to the DB_Function */ /* list than there are spaces for, an error has resulted. The */ /* user is informed through the DB_OVERFLOW code returned with */ /* the replied message. */ if ( DB_Tos >= ( DB_NUM - 1 )) { msg->DB_code = DB_OVERFLOW; break; } strcpy( &DB_Function[ ++DB_Tos ][ 0 ],str[ i ] ); } if ( DB_Screen == db_none ) DB_Screen = db_include; } /*------------ rem_functions() ------------ * * * FUNCTION: Removes functions to screening function list. * * ARGUMENTS: 1. msg: Debug_Msg received. * * RETURNS: Nothing. * * COMMENTS: DB_function will have been set up by tDB to be an array * of pointers to character strings which we wish to remove * from the DB_Function list. * */ void rem_functions( msg ) Debug_Msg *msg; { char **str; int i,j,k,last; str = ( char ** )msg->DB_function; for ( i = 0; str[ i ]; i++ ) /* Loop through the pointer array. */ { for ( j = last = DB_Tos; j >= 0; j-- ) /* Loop through list. */ { if ( !strcmp( &DB_Function[ j ][ 0 ],str[ i ] )) { /* If we find a match, we want to remove this function. */ /* If the entry is the last in the list, clear all bytes */ /* within that DB_Function List entry. */ if ( j == last ) { for ( k = 0; k < DB_BUFSIZE; k++ ) DB_Function[ j ][ k ] = '\0'; DB_List[ j ] = NULL; } /* Otherwise copy the last entry in DB_Function List to the */ /* entry we wish to remove. */ else { strcpy( &DB_Function[ j ][ 0 ],&DB_Function[ last ][ 0 ] ); DB_List[ last ] = NULL; } str[ i ] = NULL; /* Remove function pointer in list. */ last--; /* Decrement last for removed entry. */ DB_Tos--; /* The same for the current poition. */ } } } /* If the entire DB_Function list has been cleared, reset DB_Screen. */ if ( DB_Tos == -1 ) DB_Screen = db_none; /* Now loop through all str[ i ] pointers. If any of them are not */ /* NULL, then it means that a function the user wished to remove */ /* from the DB_Function List was not there to begin with. In this */ /* case we send the DB_UNMATCH code back to let tDB know that an */ /* error has occured. */ last = i; /* Last element in original list is */ /* now given by i after above loop. */ for ( i = 0; i < last; i++ ) { if ( str[ i ] != NULL ) { msg->DB_code = DB_UNMATCH; break; } } } /*------------ clear_functions() ------------ * * * FUNCTION: Clears screening function list. * * ARGUMENTS: None. * * RETURNS: Nothing. * * COMMENTS: This just loops through all bytes of DB_Function List and * sets them to zero. * */ void clear_functions() { int i,j; for ( i = 0; i < DB_NUM; i++ ) { for ( j = 0; j < DB_BUFSIZE; j++ ) { DB_Function[ i ][ j ] = '\0'; } DB_List[ i ] = NULL; } DB_Tos = -1; DB_Screen = db_none; } /*------------ screen_list() ------------ * * * FUNCTION: Looks through DB_Function list to determine if output * from a particular function should be enabled or disabled. * * ARGUMENTS: 1. msg: Pointer to Debug_Msg received. * * RETURNS: TRUE or FALSE value determining enabled output. * * COMMENTS: * */ BOOL screen_list( msg ) Debug_Msg *msg; { BOOL send = FALSE; int i; /* If the functions within DB_Function List are to generate debug */ /* output data, then return TRUE only if we find a match. */ if ( DB_Screen == db_include ) { send = FALSE; for( i = 0; i <= DB_Tos; i++ ) { if ( !strcmp( &DB_Function[ i ][ 0 ],msg->DB_function )) { if ( msg->DB_level >= DB_Level ) send = TRUE; break; } } } /* If functions within DB_Function List are not to generate debug */ /* output data, then return TRUE only if we do not find a match. */ else if ( DB_Screen == db_exclude ) { send = ( msg->DB_level >= DB_Level ) ? TRUE : FALSE; for( i = 0; i <= DB_Tos; i++ ) { if ( !strcmp( &DB_Function[ i ][ 0 ],msg->DB_function )) { send = FALSE; break; } } } /* Otherwise there is no specification for allowing output specific */ /* to function names. In this case we allow debug output only if */ /* the debug level is < the debug level given by the message. */ else if ( msg->DB_level >= DB_Level ) send = TRUE; return( send ); } /*------------ send_status() ------------ * * * FUNCTION: Replies to tDB if a DB_CHECK message was received. * * ARGUMENTS: 1. port: Debug reply port. * * RETURNS: Nothing. * * COMMENTS: This function is invoked whenever tDB has sent a DB_CHECK * message to debug to ask for its internal parameters. In * this case we initialize the DB_List array to point to * entries in the DB_Function List, and send a message to * tDB with the information. At this point tDB should be * waiting to receive the message. * */ void send_status( port ) struct MsgPort *port; { struct MsgPort *send_port; Debug_Msg chk; int i; if ( DB_Check && ( send_port = FindPort( "Trace.DBReplyPort" ))) { for ( i = 0; i <= DB_Tos; i++ ) DB_List[ i ] = &DB_Function[ i ][ 0 ]; DB_List[ i ] = NULL; chk.DB_msg.mn_Node.ln_Type = ( UBYTE )NT_MESSAGE; chk.DB_msg.mn_Node.ln_Pri = ( BYTE )0; chk.DB_msg.mn_Node.ln_Name = NULL; chk.DB_msg.mn_ReplyPort = port; chk.DB_msg.mn_Length = ( UWORD )sizeof( Debug_Msg ); chk.DB_code = DB_Screen; chk.DB_function = ( char * )&DB_List[ 0 ]; chk.DB_level = DB_Level; PutMsg( send_port,&chk ); WaitPort( port ); GetMsg( port ); DB_Check = FALSE; } }