From RTSC-Pedia

Jump to: navigation, search
revision tip
—— LANDSCAPE orientation
[printable version]  offline version generated on 04-Aug-2010 21:08 UTC 

Using xdc.runtime Errors/Example 5

Creating a logging error handler

Contents

A Logging Error Handler

In this example we show how to create and "install" a custom error handler that simply logs errors. Each error raised by an application is logged to a special logger. This allows one to easily see a complete list of each error that occurs during the operation of the system without changing or recompiling any components that raise errors.

In the example below, we create a module named ErrLog. This module supplies an Error "raise hook" that logs errors to the logger associated with the ErrLog module itself. So, to trace all errors raised in the system, you simply assign your preferred logger instance to the ErrLog module's logger. Logging Example 1 shows how to set a module's logger.

While it is obvious that an Error raise hook can simply call Log_print or Log_write, this particular Error raise hook uses Log_put to record events that appear to come directly from the caller of Error_raise. To see why this is useful, we first look at the behavior of this hook in a simple application. We then examine the implementation of this error handler.

Using the Logging Error Handler

To illustrate the use of the logging Error handler, ErrLog, we start with a contrived example application, app.c, that uses the Error module to raise and check for errors. The ErrLog module developed in this example provides a single method; an Error raise hook compatible function named ErrLog_raiseHook.

By configuring Error.raiseHook to be this function, without changing any application sources all errors are logged. This has the advantage that you can track all errors that occur in an application and the overhead of capturing these errors is quite small. No formatting or interpretation of errors occurs, the raw data is simply recorded for later analysis.

app.c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <xdc/std.h>
 
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>
 
/* ======== main ======== */
Int main(Int argc, String argv[])
{
    Error_Block eb;
 
    /* must initialize prior to first use */
    Error_init(&eb);
 
    /* call a function that raises an error */
    System_printf("half returned %d\n",
        half(17, &eb));
 
    /* return 1 if an error occurred */
    return (Error_check(&eb) ? 1 : 0);
}
 
/* ======== half ======== */
Int half(Int num, Error_Block *eb)
{
    if ((num & 0x1) != 0) {
        Error_raise(eb, Error_E_generic, 
            "num must be even", NULL);
    }
 
    return (num / 2);
}
app.cfg
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
var Error  = xdc.useModule("xdc.runtime.Error");
var System = xdc.useModule("xdc.runtime.System");
var Diags = xdc.useModule("xdc.runtime.Diags");
 
/* import the ErrLog module */
var ErrLog = xdc.useModule(
    "examples.runtime.errors.ErrLog");
 
/* force all errors to call ErrLog_raiseHook() */
Error.raiseHook = ErrLog.raiseHook;
 
/* use LoggerSys for the application's logs */
var Logger = xdc.useModule("xdc.runtime.LoggerSys");
 
/* configure the default logger (and ErrLog's logger) */ 
var Defaults = xdc.useModule("xdc.runtime.Defaults");
Defaults.common$.logger = Logger.create();

Using the configuration above, the output of running app.c is shown below.

Output
 
 
xdc.runtime.Main: Error 0: errId 0xe20011, modId 0x800b, "app.c", line 39
half returned 8

Notice that the "source" of the Log event reporting the error is not the ErrLog module. Instead the source of the event is the source of the original error, xdc.runtime.Main. The ErrLog module appears to have magically arranged for all Error_raise calls to be immediately proceeded by a call to Log_write. The next section shows how ErrLog really works.

Implementation of the Logging Error Handler

In this section we show the entire implementation of the ErrLog module as well as all package files necessary to build and deliver the ErrLog module to anyone wishing to use it.

The ErrLog module

The ErrLog module.  The ErrLog module specification is shown below.

ErrLog.xdc
 
 
 
 
 
 
 
1
 
 
 
 
 
2
 
import xdc.runtime.Log;
import xdc.runtime.Diags;
import xdc.runtime.Error;
 
module ErrLog
{
    /*! Log Event used to log all errors */
    config Log.Event LOGEVENT = {
        mask: Diags.USER1,
        msg: "Error %d: errId 0x%x, modId 0x%x, \"%s\", line %d"
    };
 
    /*! Error raise hook function that logs errors */
    Void raiseHook(Error.Block *err);
}

This specification declares a single function, raiseHook 2, compatible with the Error raise hook configuration parameter and defines a new Log event, creatively named LOGEVENT 1, that is used to log all errors.

Since ErrLog only specifies a single method, ErrLog_errorRaise(), and the declared Log_Event LOGEVENT is automatically defined in the configuration generated C file, the ErrLog module's C implementation (ErrLog.c) is quite small. Except for necessary headers, there are just three lines of functional code!

ErrLog.c
 
 
 
 
 
 
 
 
 
 
 
 
1
 
 
2
 
 
3
 
 
#include <xdc/std.h>
 
#include <xdc/runtime/Log.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/Types.h>
 
#include "package/internal/ErrLog.xdc.h"
 
/* ======== ErrLog_raiseHook ======== */
Void ErrLog_raiseHook(Error_Block *eb)
{
    /* extract call site information from the error block */
    Types_Site *site = Error_getSite(eb);
 
    /* compose an event with the error block's call site information */
    Types_Event evt = Types_makeEvent(Log_getEventId(ErrLog_LOGEVENT), site->mod);
 
    /* "put" this new event into the ErrLog module's log */
    Log_put8(evt, Error_getCode(eb), Error_getId(eb),
        site->mod, (IArg)site->file, site->line, 0, 0, 0);
}

Although the ErrLog module's raiseHook function only has three lines of code, it leverages relatively advanced features of the Log and Types modules. While it's beyond the scope of this example to provide a detailed explanation of how this function works, the following outline should provide sufficient background for those who wish to learn more.

1 - Error_Blocks contain the call site where the call to Error_raise was made, on this line we extract this information via Error_getSite() so that in the next line we can construct a Types_Event that appears to come from the same place.
2 - here we construct a Types_Event, the event type handled by ILoggers. To construct this event we need a call site (from the line above) and an "event id". In this case, we use the Log_getEventId() to get the id of the LOGEVENT event defined by the ErrLog module's specification.
3 - now that we've constructed an event we simply need to put the event, and the arguments assumed by the event, to some event logger. We use Log_put8() to pass the event to the logger associated with the current module, ErrLog.
The package files

The package files.  Since all modules must live in a package we also need to create a two other files:

  1. package.xdc - a file that define the package's name and identifies the modules it contains, and
  2. package.bld - a file that specifies which targets should be used to build the sources contained in this package.

package.xdc
 
 
 
package examples.runtime.errors { 
    module ErrLog; 
}
package.bld
 
 
 
 
 
 
for (var i = 0; i < Build.targets.length; i++) {
    var targ = Build.targets[i];
 
    var lib = Pkg.addLibrary("lib/" + Pkg.name, targ);
    lib.addObjects(["ErrLog.c"]);
}

The package.xdc file is largely self explanatory; it defines the name of the package, examples.runtime.errors, and it specifies the modules contained in the package, ErrLog.

The package.bld file, on the other hand, requires some explanation. This package.bld file specifies that the package contains a library in the lib/ sub-directory containing the compiled version of ErrLog.c for every target supported by the producer's build environment. Remarkably, this package.bld file is sufficient to compile ErrLog.c for every target, archive it into a library, and create a binary release of this package that supports all targets supported by the producer's build environment.

Why create a module?

Why create a module?.  It's easy enough to define a raise hook function whose body matches the body of ErrLog_raiseHook(). Why bother defining a module and package just to define a three line Error "callback" function?

  • By virtue of being a module, ErrLog has its own logger which allows the integrator to easily redirect errors to a separate "error log";
  • ErrLog.LOGEVENT can be configured to give the integrator control over when error logging is enabled as well as the format of all error messages;
  • rather than cut-copy-pasting subtle error hooks into many applications, it's better to share and enhance a common implementation; and, finally
  • since we need to define a distinguished Log event for errors (ErrLog.LOGEVENT) and the only way to define a new Log event is to create a module, we really don't have a choice.

See also

Using xdc.runtime Errors/Example 1 How to use the Error module
Using xdc.runtime Errors/Example 3 Creating a custom error handler
Using xdc.runtime Errors/Example 4 How to create and use custom Error IDs
Using xdc.runtime Logging/Example 1 Classic "hello world" using Log

[printable version]  offline version generated on 04-Aug-2010 21:08 UTC 
Copyright © 2008 The Eclipse Foundation. All Rights Reserved
Views
Personal tools
package reference