Next Previous Contents

14.3 Surprise Error System

Introduction

Surprise uses error system based on libcom_err found in e2fsprogs package. Unfortunately original libcom_err implementation can't pass arguments to the errors, due to this fact it doesn't solve localization in any way and also error categorization and flagging isn't possible. We proposed extension of libcom_err which is backwards compatible - any error from libcom_err can be passed to our system (but not vice versa).

Each error has its unique 32-bit number which fits in long C type, in libcom_err defined as errcode_t. Numbering space of this value is unified with standard errno variable. On any place with errcode_t declararion you can pass in values like ENOMEM. Please don't make any assumptions on the value of this variable - unique error number can be quite comfortably even a negative number.

Offered error system packs each generated error event into struct user_error. This structure may be freed in one turn by simple free(3). These error events are collected across functions and reported to the higher levels. On the top levels these errors should be decoded and presented to the user.

Message files .et

Each error has it's unique message (as in libcom_err) and the message text itself is defined in file `foo.et'. Sample file follows:


# hash-ed comment lines, error definition file
        error_table eetb
ec      ET_EETB_MESSAGE_ONE,
        "e^>Some message with %d format string(s)"
ec      ET_EETB_MESSAGE_TWO,
        "E>Another message, emergency level, no errno reported"
ec      ET_EETB_BAD_ERROR_COUNT,
        "e>Invalid count of errors %d"
        end

eetb is table identification which should consist only from exactly 4 alphanumeric characters. Currently there is some tendency to use symbol starting with `S' for all Surprise-specific tables, `SF' for filesystem modules of Surprise (yes, only 2 characters left for differentiation between filesystems). Although these naming conventions are not mandatory, please note that your chosen table name has to be unique across all libraries using libcom_err linked (even dynamically) to your project. Follows the definition of messages, each should start with ET_ prefix (which is ommited in references from .c files, see below).

Each message is preceded by flags which are delimited by character `>' (greater sign). flags themselves is unsorted sequence of characters with the meaning defined as follows:

For most up-to-date debug levels see definition of enum dbglevels in /surprise/include/generic.h.

When such error has to be presented to the user, library tries to localize the message by calling gettext(3) on the message text without the preceding flags and `>' delimiter.

Supportive files and functions

We assume that you have already your basic message file `foo.et' written. Now we will enhance our `Makefile' for proper error system compilation rules:


...
OBJS=object1 object2 error
...
object1.o: error_chket.h error.h
...
include $(TOP)/Makefile.top

Note that although we chose base name `error' for our message definition file, in fact no restrictions are given on this filename (except maintainer-only autogen script, beware). Used dependencies will generate four new files for us:

`Makefile.top' will automatically clean these intermediate files during make clean although they look like sources. Besides this `Makefile' magic each file of our directory using the error system should:

Errors instantiation

Please note about possible macro variants and modifications:

For now we will stop the discussion and provide some hot sample code below. We will assume the error table `foo.et' as shown on the beginning of this document.


#include "generic.h"
#include "foo_chket.h"

char *advanced_errorer(const char *mystring,ERR_PARAM)
{
char *dupstring;
ERR_DECL;

  if (!(dupstring=ESTRDUP(mystring))) /* may return NULL but error will be stored */
    return(NULL);
  free(mystring);
  return(mystring);
}

/* returns 0 for sucess, -1 for error */
int errorer(int howmany,errcode_t which_error,ERR_PARAM)
{
static int inited=0;
char *mystring;
ERR_DECL;

  if (!inited) { /* Assure that we init only once */
    initialize_eetb_error_table();
    inited=1;
    }
  if (howmany<0) /* Report error and return immediately */
    ERR_RETURN_REPORT(EETB_BAD_ERROR_COUNT,howmany);
  while (howmany--)
    ERR_REPORT_CODE(which_error);
  if (!advanced_errorer("my string",ERR_PASS))
    ERR_RETURN();
  puts("We passed advanced_errorer(), yow!");
  ERR_RETURN();
}

We will finally list all the error system constructs:

ERR_PARAM+_EXTERN

Declaration for passing error list reference from higher levels, for details see above.

ERR_DECL+_EXTERN

Error helper declarations, MUST be located exactly between declaration and statement block, for details see above.

ERR_DECL_TOPLEVEL

Use on the function which doesn't report the errors to its upper callers. You should somehow process the errors collected during the function's run - for this processing please use ERR_DECL_DISPOSE or ERR_DECL_DUMP macro variants. With `ERR_DECL_TOPLEVEL;' you are no longer forced to (and you are in fact forbidden to) declaring ERR_PARAM function parameter. All errors generated in all called and sub-called functions will be stored and, of course, they'll get also output to the application log. Beware - ERR_RETURN variants are not functional in ERR_DECL_TOPLEVEL-ed functions, of course!

You are forced to use ERR_DECL_DISPOSE or ERR_DECL_DUMP before returning from such function as otherwise memory leak from generated errors may ocur. Of course, you need to do this cleanup only when some error could be generated only. ERR_OCCURED() will no longer return correct value after the error messages were processed.

ERR_DECL_DISPOSE

Usable only in the function with ERR_DECL_TOPLEVEL declaration. Please read the previous ERR_DECL_TOPLEVEL section for further information.

This function will completely discard any collected errors. You can only take the application log as the actions souvenir.

ERR_DECL_DUMP

Usable only in the function with ERR_DECL_TOPLEVEL declaration. Please read the previous ERR_DECL_TOPLEVEL section for further information.

Any collected errors will get dumped to FILE * output separated by '\n' newline character.

ERR_DECL_DUMP_OUT

Shortcut for ERR_DECL_DUMP(stdout).

ERR_PASS

Symbol to pass reference to our error list context to called lower-level functions, for details see above.

ERR_REPORT(code,args...)

Main error reporting function, instantiates error as found in already initialized message catalogs by code substituing any args values. All strings referenced by `%s' (or its length-limited variant) are duplicated during error instantion and as such can be safely freed immediately after ERR_REPORT. Please note that ET_ prefix is added automatically to the passed code symbol value. All errors reported by this function are automatically passed to log(severity,message).

log(severity,format,args...)

Report the given text message in the application log together with source file name and source line number where the event was triggered. These messages are usually not shown to casual user.

ERR_REPORT_CODE(code)

Error reporting of foreign error code. You may report symbols from <errno.h> (like ENOMEM) here or errors returned by libraries using simple original libcom_err. Please don't use ERR_REPORT_CODE(errno) construct, it is always better to use standard ERR_REPORT with error message marked by ^ errno-inclusing flag. By this message you can give much better advice during which operation such error occured.

ERR_OCCURED()

Returns TRUE if any report was generated during the run of this function. This includes all the errors generated even in called sub-functions and it doesn't include any error generated to the same error list but prior to calling our function where ERR_OCCURED() is used. This function cannot be used in ERR_DECL_STDERR-ed functions.

ERR_RETURN()

Does C-equivalent of `return(error);' where error is the value representing whether error occured during run of our function. error is 0 for ERR_DECL_STDERR-ed functions and for normal ERR_DECL-ed functions when no error occurs. error is -1 when some error occured, according to ERR_OCCURED() (see previous item description). Note that ERR_RETURN() can be used only for functions with convention of returning success/failure code as int type. If your function returns some kind of pointer etc., you have to handle the `return(...);' yourself from case to case.

ERR_RETURN_REPORT(code,args...)

Simple shortcut to calling of ERR_REPORT(code,args...) immediately followed by ERR_RETURN(). The same int type return of the function restrictions apply as in ERR_RETURN(). See definitions of both macros above.

ERR_NULL_RETURN_REPORT(code,args...)

Simple shortcut to calling of ERR_RETURN_REPORT(code,args...) immediately followed by return NULL.

ERR_RETURN_REPORT_CODE(code)

Simple shortcut to calling of ERR_REPORT_CODE(code) immediately followed by ERR_RETURN(). The same int type return of the function restrictions apply as in ERR_RETURN(). See definitions of both macros above.

ERR_NULL_RETURN_REPORT_CODE(code)

Simple shortcut to calling of ERR_RETURN_REPORT_CODE(code) immediately followed by return NULL.

ERR_PREF

You may use it to prevent very painful carrying of the ERR_PARAM / ERR_PASS through all the functions. It may get very convenient during callbacked own code by foreign libraries.

It has additional the very-first parameter to specify string which should be preceding any generated variables/parameters. You will probably find it useful during global declarations for passing errors by system-callbacked functions.

eterr

Simple local variable of type errcode_t prepared for you locally in each function using error system. You will typically assign to it result values of system functions, potentially ERR_REPORT_CODE(eterr)-ing them when it is checked (by you) that the error really occured.

Errors retrieval

Until now we were all errors only storing to so-called error list. Here we will introduce your chance to get hands on these valuable stored events. Three error-retrieval functions exist:

struct user_error *err_get_struct(GList **param)

This function will pop the first (oldest) error found in error list and returns it formatted into struct user_error. Error event is permanently removed from the error list, returned structure can be deallocated by simple free(3) on the structure memory block itself. When NULL is returned, no more error events exist. And don't be confused by GList **param type, you will have to deal with error list implementation anyway.

char *err_struct_to_string(struct user_error *err,ERR_PARAM)

Format the given struct user_error to user-presentable error strings and return it malloc(3)-ated. Any errno stored in the structure (not the actual one!) gets properly substituted. Given struct user_error is not free(3)-d by this funtion, you have to do it manually when you are done with its processing. This function can generate errors, please deal with them completely separately from the error event being parsed.

char *err_get_string(GList **param,ERR_PARAM)

Simple wrapper around previous two functions err_get_struct() and err_struct_to_string(). Event is removed from the queue, processed and intermediate struct user_error free(3)-d immediately. Returned string is malloc(3)-ated.

During error processing you shouldn't need too much to deal with ERR_PARAM yourself. If you can't avoid it, currently this symbol is defined as `GList ** surerr_param'. One additional pointer is due to the fact the list itself has to be passed across functions by reference. All newly created errors are appended to the end of the GList *, processed are from the beginning (ordering is preserved).

Performance note

Due to compatibility with old GCC releases we had to choose a worse solution for checking of given variable arguments to ERR_REPORT whether they match to the formatstring (as asserted by GCC __attribute__((format(printf,1,2)))). Currently the runtime overhead is added to achieve compilation-time checks together with checking of missing flagging specifiers. After your program gets debugged and the final product is to be deployed, please define NDEBUG symbol which will remove this overhead (and thus even parameters checking, of course). NDEBUG commonly disables all assert(3) checks which are good to turn off for production release anyway.

Error system FAQ

My errors are reported as `Unknown code boom 2'. What is screwed up?

You haven't called initialize_CODE_error_table(), please see above.

What's the difference between ERR_PARAM and ERR_PASS?

ERR_PARAM is a declaration, ERR_PASS does real value passing. Simply in function declaration header (and in header files excluding inlined functions generally) you have to always use ERR_PARAM, in function bodies when calling any function use always ERR_PASS. When in doubt, see their definitions in surprise/src/include/generic.h.

Errors occur when i write ERR_RETURN_CODE(ETAB_ERROR_1); to return statical (no printf(3) format marks) string.

Please use simply ERR_RETURN(ETAB_ERROR_1); as it works even when no format marks are used (as in error `Cannot continue optimization'). ERR_RETURN_CODE(errcode) is used when errno-type variable or symbol from <errno.h> (like ENOMEM) has to be reported to the error system.


Next Previous Contents