www.pudn.com > gandalf.1.zip > gan_err_trace.c
/** * File: $RCSfile: gan_err_trace.c,v $ * Module: Error trace * Part of: Gandalf Exception Handling * * Revision: $Revision: 1.11 $ * Last edited: $Date: 2005/10/18 16:30:47 $ * Author: $Author: pm $ * Copyright: (c) 2000 Industrial Research Limited * * Notes: * * * * Errors are stored for later retrieval by storing their details onto * a stack, implemented as a linked list. A record of the linked list * is the struct ET_TRACE defined in gan_exception_trace.h. Stack * contents are arranged in order of age, starting with the most recent * error at the top. The stack is referenced by the module level * variable et_trace. */ /* 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 */ #include/* malloc(), free() */ #include /* malloc(), free() */ #include /* strlen(), strcpy() */ #include #include /* Gan_ErrorTrace */ /** * \addtogroup Common * \{ */ /** * \addtogroup CommonError * \{ */ /* * \brief Register a deep error. * \param et_top Pointer to top record of error trace * \param func_name Name of function in which error occurs * \param file_name Name of file in which error occurs * \param line_number Line in file at which error occurs * \param number Number attached to error * \return No value. * * Register a deep error. A deep error indicates that an error occurred within * this module. A deep error will be written into the record at the top of the * stack. The deep error message is static to this module. * * The error code registered will be #GAN_EC_DFT_DEEP_ERROR. * * \warning \c f_static_message will be set to #GAN_ET_YES. */ static void et_deep_error ( Gan_ErrorTrace *et_top, const char *func_name, const char *file_name, int line_number, int number ) { et_top->f_spare = GAN_ET_NO; /* This error record is now used */ et_top->func_name = func_name; et_top->err_code = GAN_EC_DFT_DEEP_ERROR; et_top->file_name = file_name; et_top->line_number = line_number; et_top->number = number; et_top->f_static_message = GAN_ET_YES; et_top->message = "Deep Error: Memory allocation error occured in exception module."; return; } /* et_deep_error() */ /* * \brief Allocate memory for a new error record for error trace. * \return Pointer to new record. \c NULL is error. * * Allocate memory for a new error record for error trace. * \warning Field f_static_message is specified at point message is provided. */ static Gan_ErrorTrace * et_new_record(void) { Gan_ErrorTrace *new_record; if ( (new_record = gan_malloc_object(Gan_ErrorTrace)) != NULL ) { new_record->f_static_record = GAN_ET_NO; new_record->f_spare = GAN_ET_YES; new_record->func_name = NULL; new_record->err_code = GAN_EC_DFT_SPARE; new_record->file_name = NULL; new_record->line_number = 0; new_record->f_static_message = GAN_ET_YES; new_record->message = NULL; new_record->number = 0; } return new_record; /* NULL if malloc fails */ } /* et_new_record() */ /* * \brief Write error information into second record of trace. * \param et_top Pointer to top record of error trace * \param func_name Name of function in which error occurs * \param err_code Numeric code of error * \param file_name Name of file in which error occurs * \param line_number Line in file at which error occurs * \param message Message string describing error * \param number Number attached to error * \return No value. * * Write error information in the second to top error record of stack. * (Top of stack reserved for deep error). * * \warning If error occurs inside this function (only possibility is * malloc() error) then do the best to write error information into * second to top error record, and invoke deep error sequence. * If error information is successfully written, malloc a new top of * stack record. * * The second to top error record is assumed to exist. Check using * et_get_number_spare(). * \sa et_get_number_spare() */ static Gan_ErrorTrace * et_register_error ( Gan_ErrorTrace *et_top, const char *func_name, int err_code, const char *file_name, int line_number, const char *message, int number ) { int n_chars; /* Size of string */ et_top->prev->f_spare = GAN_ET_NO; /* No longer spare */ et_top->prev->func_name = func_name; et_top->prev->file_name = file_name; et_top->prev->line_number = line_number; et_top->prev->err_code = err_code; et_top->prev->number = number; /* Malloc space for message string, +1 for '\0' termination char */ n_chars = strlen( message ) + 1; et_top->prev->message = gan_malloc_array ( char, n_chars ); if ( et_top->prev->message == NULL ) { /* malloc error is a deep error */ printf("FAILED\n"); et_deep_error(et_top, "et_register_error", __FILE__, __LINE__, number); } else { /* Success, mark not spare, alloc new record */ Gan_ErrorTrace * a_record = NULL; /* Copy contents of the message string */ strcpy(et_top->prev->message, message); et_top->prev->f_static_message = GAN_ET_NO; /* Not a static message */ /** Allocate new record and put on top of trace **/ if ((a_record = et_new_record()) != NULL ) { /* Successful */ a_record->prev = et_top; /* Add new record to top of stack */ et_top = a_record; } else { /* Unable to allocate new record */ et_deep_error(et_top, "et_register_error", __FILE__, __LINE__, number); return et_top; } } return et_top; } /* et_register_error() */ /* * Function: Get the number of successive 'spare' records from and including * specified starting point. * Arguments: et_record Pointer to a record of error trace * Returns: Count of spare records * Description: Get the number of successive 'spare' records from and including * specified starting point. * Typically et_record is set to top of stack. * Warnings: Non existent records are not counted as 'spare'. * If a record is observed to be 'not spare', then all later * records in the stack are assumed 'not spare'. * See Also: * */ static int et_get_number_spare ( Gan_ErrorTrace *et_record ) { int n = 0; while ( et_record != NULL ) { if ( et_record->f_spare == GAN_ET_YES ) n++; /* "I see that spare" */ else break; /* 'Not spare' found: stop counting */ et_record = et_record->prev; } return n; } /* et_get_number_spare() */ /** * \brief Pushes a new error recorded onto error trace. * \param et_top Pointer to top record of error trace * \param func_name Name of function in which error occurs * \param err_code Numeric code of error * \param file_name Name of file in which error occurs * \param line_number Line in file at which error occurs * \param message Message string describing error * \param number Number attached to error * \return Pointer to new top of list. * \note * * A memory error may occur inside gan_et_push() because this function * calls malloc(): this is called a deep error. Two records are * reserved at the top of the trace to register the occurrence of a deep * error (top) and to register the error (second to top) that * identified by the error code GAN_EC_DFT_DEEP_ERROR. * * A deep error is treated no differently from any other error, except * that the reserved records are used. Therefore, even if the trace * registers a deep error, gan_et_push() will attempt to register * subsequent errors if memory allows. Each time a deep error occurs, * gan_et_push() will attempt to register a deep error. Deep error that * follow a normal error will always succeed in being registered. A * deep error that follows a deep error may not succeed in being * registered because of memory constraints -- however, the top of the * trace will nevertheless show that the most recent error was a deep * error. Errors requested to be registered while no system memory * exists will be lost permanently. * * The oldest two records of the LIFO stack are statically * allocated. This ensures that at any time a deep error can be * registered. * * gan_et_push() or any other function in the exception handling module * makes no attempt to free memory for the purpose of avoid a deep * error. Memory management firmly rest with an external component * (i.e. library or application). Such behaviour is consistent with the * policy that the application (or library) is the master and the * exception handling module is the slave. The role of the exception * module is to provide full error information to the application, so * that the application has the opportunity to handle the error in a * contextually meaningful way. * * * The psuedo code for handling deep errors is as follows: * * ----------------------------------------------------------------------- * * gan_et_push() * Have 2 records spare? * yes: register_error * no: try to allocate records so that we have 2 spare * on error: deep_error * register_error * * ----------------------------------------------------------------------- * * register_error() (into second spare) * do the best to furnish info into 2nd spare record * on error: * leave the error message part blank * deep_error() * mark 2nd record as 'not spare' * allocate a new record and put at top of stack * on error: deep_error() * * ----------------------------------------------------------------------- * * deep_error() * Have 1 spare record (in which to write deep error) * yes: write deep error into spare record * no : do nothing, return * */ Gan_ErrorTrace * gan_et_push ( Gan_ErrorTrace *et_top, const char *func_name, int err_code, const char *file_name, int line_number, const char *message, int number ) { Gan_ErrorTrace * a_record = NULL; int n_spare = 0; /* Number of spare records at top of trace */ /**** * Need (at least) 2 records spare at top of stack to register an * error. If we can't get 2 spare records, register deep error. */ for ( n_spare = et_get_number_spare(et_top); n_spare < 2; ) { /** Allocate new record **/ if ( (a_record = et_new_record()) != NULL ) { /* Successful */ a_record->prev = et_top; /* Add to top of stack */ et_top = a_record; n_spare++; } else { /* Unable to allocate new record */ et_deep_error(et_top, func_name, file_name, line_number, number); return et_top; } } /* Creating 2 spare records */ /* yes: register_error. Must have at least 2 spare on top of trace * before et_register_error() can be called. */ a_record = et_register_error ( et_top, func_name, err_code, file_name, line_number, message, number ); return a_record; } /* gan_et_push() */ /** * \brief Counts number of records in trace from specified starting point. * \param et_record Pointer to top record of error trace * \return The number of records in trace from and including \a et_record to * bottom of trace. * * Counts number of \em non-spare records in trace from and including * \a et_record to the last (oldest) record in stack. Total number of * non-spare records in error trace is returned when the top of error trace * is specified by \a et_record. */ int gan_et_get_record_count ( Gan_ErrorTrace * et_record ) { int n = 0; et_record = gan_et_get_record_first( et_record ); while( et_record != NULL ) { n++; et_record = gan_et_get_record_next( et_record ); } return n; } /* gan_et_get_record_count() */ /** * \brief Returns pointer to next non-spare error record after et_record. * \param et_record Pointer to the Error record preceding the one * that will be returned * \return Returns pointer to next non-spare error record after \a et_record. * * \warning If \a et_record is \c NULL, this function returns \c NULL. */ Gan_ErrorTrace * gan_et_get_record_next ( Gan_ErrorTrace *et_record ) { if ( et_record != NULL ) return gan_et_get_record_first(et_record->prev); else return NULL; } /* gan_et_get_record_next() */ /** * \brief Returns pointer to first non-spare error record from \a et_record. * \param et_record Pointer to error record in error trace for which error * code is to be read. * \return Error code of specified error record, or \c NULL if \a et_record is * \c NULL, or no non-spare records found. * * Returns pointer to first non-spare error record from and including * \a et_record. */ Gan_ErrorTrace * gan_et_get_record_first ( Gan_ErrorTrace *et_record ) { while ( et_record != NULL ) { if ( et_record->f_spare == GAN_ET_YES ) et_record = et_record->prev; /* Skip if spare */ else break; } return et_record; } /* gan_et_get_record_first() */ /** * \brief Flush all errors in error trace. * \param a_record The top record in error trace * \return Pointer to new top of error trace. * * Flush all errors in error trace. * \warning \a et_record must be top record in trace, or else residual * trace will malformed (i.e. not \c NULL terminated). * * \note Traverse error trace from start to finish deleting all records, * except leaving the final two which are reserved to store deep error * information. */ Gan_ErrorTrace * gan_et_flush_trace ( Gan_ErrorTrace *a_record ) { Gan_ErrorTrace *free_me = NULL; Gan_ErrorTrace *et_top = NULL; /* New top of trace */ while ( a_record != NULL ) { free_me = a_record; a_record = a_record->prev; /**** * Free text of error message, so long it is not a deep error, which * uses a static error message. */ if ( (free_me->f_static_message != GAN_ET_YES) && (free_me->message != NULL) ) { free( free_me->message ); free_me->message = NULL; } /**** * Free the record, so long it is not a statically allocated record. */ if (free_me->f_static_record != GAN_ET_YES) { free(free_me); free_me = NULL; } else { /** Don't free free_me, but mark it spare, and continue */ free_me->f_spare = GAN_ET_YES; if (et_top == NULL) /* et_top is topmost static record */ et_top = free_me; } } return et_top; } /* gan_et_flush_trace() */ /** * \} */ /** * \} */