tags:

views:

171

answers:

3

I'm new to the software engineering side of C development; does anyone have a good guide on how to design an error tracking or error control system for a C project (especially embedded)? Something talking about error tracking for C libraries would be helpful too.

+1  A: 

These are the basic things you will need to define:

  • Trace levels (eg. Debug, Alerts, Warnings, Info, Critical, etc). You should also have a current trace level set, so if the current trace level is INFO, then all of the messages with a trace level above info are printed. If the current trace level is critical, then only critical messages will be printed.

   enum _TraceLevelType
   { 
        INFO = 0,
        DEBUG,
        WARNING,
        ERROR,
        CRITICAL
    } TraceLevelType;
  • Error Codes, I think the best way is to have a big enum. Something like this:

   enum _ErrorType
   {
        //Internal errors 0-100
        APPLICATION_FAILURE = 0,
        ...
        MEMORY_FAULT,
        //UI ERRORS  101-200
        INVALID_OPTION_SELECTED = 101,
        ....
        ...
    }ErrorType;

  • A message queue, You should have a mechanism where everybody could send messages and these messages are properly queued. Since you are working on embedded systems this is critical. You don't want to spend time printing debugging messages on a real time OS. So the thread managing the error codes should have a very low priority and all messages received should be posted to a queue, so when the scheduler decides to call it, then you do the printfs to the console or to whatever output you decide.

So your error method will be something like this:


TraceError(TraceLevelType traceLevel, ErrorType errorType, char *msg)
{
    if(CURRENT_TRACE_LEVEL <= traceLevel)
       /* Ignore message */
    else
       /*Queue the Message*/
}         

You could also have more parameters to indicate which module is sending the error, but I think that is basically it.

Freddy
+5  A: 

In my experience the strategies here fall into a few camps.

  • Use of global variables ala errno. How this works is essentially any function can store an error code in the global variable, so after executing the function you can read the error code to see if it executed properly. There are some obvious issues here when working in a multithreaded environment. Although it appears that POSIX.1c specifies a solution to that problem.

  • Have every function return an error code. For example:

    RESULT_CODE my_function(int param1, int param2);
    
    
    RESULT_CODE error_code = my_function(10, 2);
    

The problem with this approach is that you lose your ability to return values from the function directly.

  • Every function has an extra parameter that stores the result code. For example:

    void my_function(int param1, int param2, RESULT_CODE *err);
    
    
    RESULT_CODE error_code;
    my_function(10, 2, &error_code);
    switch (error_code)
    {
        case RESULT_OK:
            // all OK
            break;
        case RESULT_NO_MEM:
            // out of memory
            break;
        // etc...
    }
    

I've seen this approach used successfully in commercial RTOS, and personally prefer it since I find it the least restrictive. The only potential disadvantage is that you have to explicitly declare a variable to store the error code, even if you don't care about the result. In some sense I actually kind of like that requirement, though, since it forces you to be constantly thinking about how errors are going to be handled.

Gabe