/* * Copyright (C) 1998 NCSA HDF * All rights reserved. * * Purpose: Provides error handling in the form of a stack. The * FUNC_ENTER() macro clears the error stack whenever an API * function is entered. When an error is detected, an entry is * pushed onto the stack. As the functions unwind additional * entries are pushed onto the stack. The API function will * return some indication that an error occurred and the * application can print the error stack. * * Certain API functions in the H5E package (such as H5Eprint()) * do not clear the error stack. Otherwise, any function which * doesn't have an underscore immediately after the package name * will clear the error stack. For instance, H5Fopen() clears * the error stack while H5F_open() does not. * * An error stack has a fixed maximum size. If this size is * exceeded then the stack will be truncated and only the * inner-most functions will have entries on the stack. This is * expected to be a rare condition. * * Each thread has its own error stack, but since * multi-threading has not been added to the library yet, this * package maintains a single error stack. The error stack is * statically allocated to reduce the complexity of handling * errors within the H5E package. * */ #include /* Generic Functions */ #include /* IDs */ #include /* Private error routines */ #include /* Memory management */ #define PABLO_MASK H5E_mask static const H5E_major_mesg_t H5E_major_mesg_g[] = { {H5E_NONE_MAJOR, "No error"}, {H5E_ARGS, "Function arguments"}, {H5E_RESOURCE, "Resource unavailable"}, {H5E_INTERNAL, "Internal HDF5 error"}, {H5E_FILE, "File interface"}, {H5E_IO, "Low-level I/O layer"}, {H5E_FUNC, "Function entry/exit"}, {H5E_ATOM, "Atom layer"}, {H5E_CACHE, "Meta data cache layer"}, {H5E_BTREE, "B-tree layer"}, {H5E_SYM, "Symbol table layer"}, {H5E_HEAP, "Heap layer"}, {H5E_OHDR, "Object header layer"}, {H5E_DATATYPE, "Datatype interface"}, {H5E_DATASPACE, "Dataspace interface"}, {H5E_DATASET, "Dataset interface"}, {H5E_STORAGE, "Data storage layer"}, {H5E_PLIST, "Property list interface"}, {H5E_ATTR, "Attribute layer"}, {H5E_PLINE, "Data filters layer"}, {H5E_EFL, "External file list"}, {H5E_RAGGED, "Ragged array layer"}, }; static const H5E_minor_mesg_t H5E_minor_mesg_g[] = { {H5E_NONE_MINOR, "No error"}, {H5E_UNINITIALIZED, "Information is uninitialized"}, {H5E_UNSUPPORTED, "Feature is unsupported"}, {H5E_BADTYPE, "Inappropriate type"}, {H5E_BADRANGE, "Out of range"}, {H5E_BADVALUE, "Bad value"}, {H5E_NOSPACE, "No space available for allocation"}, {H5E_CANTCOPY, "Unable to copy object"}, {H5E_FILEEXISTS, "File already exists"}, {H5E_FILEOPEN, "File already open"}, {H5E_CANTCREATE, "Unable to create file"}, {H5E_CANTOPENFILE, "Unable to open file"}, {H5E_CANTOPENOBJ, "Unable to open object"}, {H5E_NOTHDF5, "Not an HDF5 file"}, {H5E_BADFILE, "Bad file ID accessed"}, {H5E_TRUNCATED, "File has been truncated"}, {H5E_MOUNT, "File mount error"}, {H5E_SEEKERROR, "Seek failed"}, {H5E_READERROR, "Read failed"}, {H5E_WRITEERROR, "Write failed"}, {H5E_CLOSEERROR, "Close failed"}, {H5E_OVERFLOW, "Address overflowed"}, {H5E_CANTINIT, "Unable to initialize"}, {H5E_ALREADYINIT, "Object already initialized"}, {H5E_BADATOM, "Unable to find atom information (already closed?)"}, {H5E_CANTREGISTER, "Unable to register new atom"}, {H5E_CANTFLUSH, "Unable to flush data from cache"}, {H5E_CANTLOAD, "Unable to load meta data into cache"}, {H5E_PROTECT, "Protected meta data error"}, {H5E_NOTCACHED, "Meta data not currently cached"}, {H5E_NOTFOUND, "Object not found"}, {H5E_EXISTS, "Object already exists"}, {H5E_CANTENCODE, "Unable to encode value"}, {H5E_CANTDECODE, "Unable to decode value"}, {H5E_CANTSPLIT, "Unable to split node"}, {H5E_CANTINSERT, "Unable to insert object"}, {H5E_CANTLIST, "Unable to list node"}, {H5E_LINKCOUNT, "Bad object header link count"}, {H5E_VERSION, "Wrong version number"}, {H5E_ALIGNMENT, "Alignment error"}, {H5E_BADMESG, "Unrecognized message"}, {H5E_COMPLEN, "Name component is too long"}, {H5E_CWG, "Problem with current working group"}, {H5E_LINK, "Link count failure"}, {H5E_SLINK, "Symbolic link error"}, }; /* Interface initialization? */ static intn interface_initialize_g = FALSE; #define INTERFACE_INIT NULL const hbool_t H5E_clearable_g = TRUE; /* DO NOT CHANGE */ /* * The error stack. Eventually we'll have some sort of global table so each * thread has it's own stack. The stacks will be created on demand when the * thread first calls H5E_push(). */ H5E_t H5E_stack_g[1]; #define H5E_get_my_stack() (H5E_stack_g+0) /* * Automatic error stack traversal occurs if the traversal callback function * is non null and an API function is about to return an error. These should * probably be part of the error stack so they're local to a thread. */ herr_t (*H5E_auto_g)(void*) = (herr_t(*)(void*))H5Eprint; void *H5E_auto_data_g = stderr; /*------------------------------------------------------------------------- * Function: H5Eset_auto * * Purpose: Turns on or off automatic printing of errors. When turned on * (non-null FUNC pointer) any API function which returns an * error indication will first call FUNC passing it CLIENT_DATA * as an argument. * * The default values before this function is called are * H5Eprint() with client data being the standard error stream, * stderr. * * Automatic stack traversal is always in the H5E_WALK_DOWNWARD * direction. * * See Also: H5Ewalk() * * Return: Success: SUCCEED * * Failure: FAIL * * Programmer: Robb Matzke * Friday, February 27, 1998 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5Eset_auto(H5E_auto_t func, void *client_data) { FUNC_ENTER (H5Eset_auto, FAIL); H5TRACE2("e","xx",func,client_data); H5E_auto_g = func; H5E_auto_data_g = client_data; FUNC_LEAVE (SUCCEED); } /*------------------------------------------------------------------------- * Function: H5Eget_auto * * Purpose: Returns the current settings for the automatic error stack * traversal function and its data. Either (or both) arguments * may be null in which case the value is not returned. * * Return: Success: SUCCEED * * Failure: FAIL * * Programmer: Robb Matzke * Saturday, February 28, 1998 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5Eget_auto(H5E_auto_t *func, void **client_data) { FUNC_ENTER (H5Eget_auto, FAIL); H5TRACE2("e","*x*x",func,client_data); if (func) *func = H5E_auto_g; if (client_data) *client_data = H5E_auto_data_g; FUNC_LEAVE (SUCCEED); } /*------------------------------------------------------------------------- * Function: H5Eclear * * Purpose: Clears the error stack for the current thread. * * Return: Success: SUCCEED * * Failure: FAIL. This function can fail if there are * problems initializing the library. * * Programmer: Robb Matzke * Friday, February 27, 1998 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5Eclear(void) { FUNC_ENTER (H5Eclear, FAIL); H5TRACE0("e",""); /* FUNC_ENTER() does all the work */ FUNC_LEAVE (SUCCEED); } /*------------------------------------------------------------------------- * Function: H5Eprint * * Purpose: Prints the error stack in some default way. This is just a * convenience function for H5Ewalk() with a function that * prints error messages. Users are encouraged to write there * own more specific error handlers. * * Return: Success: SUCCEED * * Failure: FAIL * * Programmer: Robb Matzke * Friday, February 27, 1998 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5Eprint(FILE *stream) { H5E_t *estack = H5E_get_my_stack (); hbool_t H5E_clearable_g = FALSE; /*override global*/ herr_t status = FAIL; FUNC_ENTER (H5Eprint, FAIL); /*NO TRACE*/ if (!stream) stream = stderr; fprintf (stream, "HDF5-DIAG: Error detected in thread 0."); if (estack && estack->nused>0) fprintf (stream, " Back trace follows."); HDfputc ('\n', stream); status = H5E_walk (H5E_WALK_DOWNWARD, H5Ewalk_cb, (void*)stream); FUNC_LEAVE (status); } /*------------------------------------------------------------------------- * Function: H5Ewalk * * Purpose: Walks the error stack for the current thread and calls some * function for each error along the way. * * Return: Success: SUCCEED * * Failure: FAIL. This function can fail if there are * problems initializing the library. * * Programmer: Robb Matzke * Friday, February 27, 1998 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5Ewalk(H5E_direction_t direction, H5E_walk_t func, void *client_data) { hbool_t H5E_clearable_g = FALSE; /*override global*/ herr_t status = FAIL; FUNC_ENTER (H5Ewalk, FAIL); H5TRACE3("e","Edxx",direction,func,client_data); status = H5E_walk (direction, func, client_data); FUNC_LEAVE (status); } /*------------------------------------------------------------------------- * Function: H5Ewalk_cb * * Purpose: This is a default error stack traversal callback function * that prints error messages to the specified output stream. * It is not meant to be called directly but rather as an * argument to the H5Ewalk() function. This function is called * also by H5Eprint(). Application writers are encouraged to * use this function as a model for their own error stack * walking functions. * * N is a counter for how many times this function has been * called for this particular traversal of the stack. It always * begins at zero for the first error on the stack (either the * top or bottom error, or even both, depending on the traversal * direction and the size of the stack). * * ERR_DESC is an error description. It contains all the * information about a particular error. * * CLIENT_DATA is the same pointer that was passed as the * CLIENT_DATA argument of H5Ewalk(). It is expected to be a * file pointer (or stderr if null). * * Return: Success: SUCCEED * * Failure: FAIL * * Programmer: Robb Matzke * Friday, December 12, 1997 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5Ewalk_cb(int n, H5E_error_t *err_desc, void *client_data) { FILE *stream = (FILE *)client_data; const char *maj_str = NULL; const char *min_str = NULL; const int indent = 2; /*NO TRACE*/ /* Check arguments */ assert (err_desc); if (!client_data) client_data = stderr; /* Get descriptions for the major and minor error numbers */ maj_str = H5Eget_major (err_desc->maj_num); min_str = H5Eget_minor (err_desc->min_num); /* Print error message */ fprintf (stream, "%*s#%03d: %s line %u in %s(): %s\n", indent, "", n, err_desc->file_name, err_desc->line, err_desc->func_name, err_desc->desc); fprintf (stream, "%*smajor(%02d): %s\n", indent*2, "", err_desc->maj_num, maj_str); fprintf (stream, "%*sminor(%02d): %s\n", indent*2, "", err_desc->min_num, min_str); return SUCCEED; } /*------------------------------------------------------------------------- * Function: H5Eget_major * * Purpose: Given a major error number return a constant character string * that describes the error. * * Return: Success: Ptr to a character string. * * Failure: Ptr to "Invalid major error number" * * Programmer: Robb Matzke * Friday, February 27, 1998 * * Modifications: * *------------------------------------------------------------------------- */ const char * H5Eget_major (H5E_major_t n) { uintn i; /* * WARNING: Do not call the FUNC_ENTER() or FUNC_LEAVE() macros since * they might interact badly with the error stack. We are * probably calling this function during an error stack * traversal and adding/removing entries as the result of an * error would most likely mess things up. */ for (i=0; inusedslot[estack->nused].maj_num = maj_num; estack->slot[estack->nused].min_num = min_num; estack->slot[estack->nused].func_name = function_name; estack->slot[estack->nused].file_name = file_name; estack->slot[estack->nused].line = line; estack->slot[estack->nused].desc = desc; estack->nused++; } return SUCCEED; /*don't use FUNC_LEAVE() here */ } /*------------------------------------------------------------------------- * Function: H5E_clear * * Purpose: Clears the error stack for the current thread. * * Return: Success: SUCCEED * * Failure: FAIL. This function can fail if there are * problems initializing the library. * * Programmer: Robb Matzke * Friday, February 27, 1998 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5E_clear(void) { H5E_t *estack = H5E_get_my_stack (); FUNC_ENTER(H5E_clear, FAIL); if (estack) estack->nused = 0; FUNC_LEAVE(SUCCEED); } /*------------------------------------------------------------------------- * Function: H5E_walk * * Purpose: Walks the error stack, calling the specified function for * each error on the stack. The DIRECTION argument determines * whether the stack is walked from the inside out or the * outside in. The value H5E_WALK_UPWARD means begin with the * most specific error and end at the API; H5E_WALK_DOWNWARD * means to start at the API and end at the inner-most function * where the error was first detected. * * The function pointed to by FUNC will be called for each error * in the error stack. It's arguments will include an index * number (beginning at zero regardless of stack traversal * direction), an error stack entry, and the CLIENT_DATA pointer * passed to H5E_print. * * Return: Success: SUCCEED * * Failure: FAIL. This function can fail if there are * problems initializing the library. * * Programmer: Robb Matzke * Friday, December 12, 1997 * * Modifications: * *------------------------------------------------------------------------- */ herr_t H5E_walk (H5E_direction_t direction, H5E_walk_t func, void *client_data) { H5E_t *estack = H5E_get_my_stack (); int i; herr_t status; FUNC_ENTER(H5E_walk, FAIL); /* check args, but rather than failing use some default value */ if (direction!=H5E_WALK_UPWARD && direction!=H5E_WALK_DOWNWARD) { direction = H5E_WALK_UPWARD; } /* walk the stack */ assert (estack); if (func && H5E_WALK_UPWARD==direction) { for (i=0, status=SUCCEED; inused && status>=0; i++) { status = (func)(i, estack->slot+i, client_data); } } else if (func && H5E_WALK_DOWNWARD==direction) { for (i=estack->nused-1, status=SUCCEED; i>=0 && status>=0; --i) { status = (func)(estack->nused-(i+1), estack->slot+i, client_data); } } FUNC_LEAVE(SUCCEED); }