From RTSC-Pedia

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

Extending xdc.runtime Logging/Example 1

A logger that filters Log events

Contents

An Event Filter

In the following sections we develop a simple ILogger that illustrates how to create custom event filters. Suppose, for example, you have a high-density "channel-based" application and you need the ability to display events from just one channel selected at runtime. Although the xdc.runtime package enables you to easily enable (disable) events on a per module basis, what's really needed in this case is the ability to enable (disable) events on a per "channel" basis. Since the only other control dimension offered by the Log interface is an 8-bit user bit-mask, applications with more than 8 channels must use an alternate mechanism to filter events.

In this example there are two separate modules:

  • ChanEvents which defines a set of "channel" events raised by the application, and
  • ChanFilter a module that implements the ILogger interface and provides functions that allow the application to dynamically enable and disable events from a selected channel.

Before showing the implementation of these modules we show how client applications might use the them. Then we look at the implementation of these modules and consider several more advanced capabilities that are easily added.

How to Use the Filter

We begin by showing how the ChanFilter module can be configured atop an existing ILogger to filter out unwanted events. In the example below we show a simple client application that generates events unrelated to "channels". This example illustrates that adding the ChanFilter requires a very small change to an existing configuration file and this filter does not affect those parts of the application unrelated to channels.

app.cfg
 
 
 
 
 
 
 
 
 
1
 
 
 
2
var Diags = xdc.useModule("xdc.runtime.Diags");
var Log = xdc.useModule("xdc.runtime.Log");
 
/* enable runtime control of "info" events for non-modules */
var Main = xdc.useModule("xdc.runtime.Main");
Main.common$.diags_INFO = Diags.RUNTIME_ON;
 
/* use ChanFilter ILogger service provider to filter events */
var ChanFilter = xdc.useModule("examples.runtime.logger.ChanFilter");
Main.common$.logger = ChanFilter.create();
 
/* use LoggerSys ILogger to handle events not filtered by ChanFilter */
var LoggerSys = xdc.useModule("xdc.runtime.LoggerSys");
ChanFilter.common$.logger = LoggerSys.create();

Comparing the configuration file to that in the "hello world" example, we see that we only needed to use the ChanFilter in lieu of LoggerSys 1 and then configure ChanFilter to use the original LoggerSys logger 2. This allows ChanFilter to see all events first and then optionally pass these events on to its logger (in this case, LoggerSys).

The figure below illustrates the "binding" of ILogger instances and clients of the Log module that results from this configuration. Notice that all non-module code references to Log, managed by the xdc.runtime.Main module, pass events to a ChanFilter logger whereas ChanFilter itself references LoggerSys. This allows ChanFilter to "see" all events produced by the application and conditionally pass them on to a "real" logger, in this case, LoggerSys.

Image:RuntimeExtendingLogEx1.png

Since ChanFilter is designed to work "transparently" with existing applications and loggers, it should come as no surprise that the configuration script above has no affect on the output of the "hello world" example.

app.c
 
 
 
 
 
 
 
 
 
 
#include <xdc/std.h>
 
#include <xdc/runtime/Diags.h>
#include <xdc/runtime/Log.h>
 
Int main(Int argc, String argv[])
{
    Log_info0("hello world");
    return (0);
}
Output
 
xdc.runtime.Main: "app.c", line 8: hello world

Now let's look at how the ChanFilter behaves when "channel" events are raised and how an application can control ChanFilter in real-time to select which channels events should be recorded. Let's assume that channels can raise some set of predefined events and each time one of these events is raised the first argument passed is a channel ID. For this example, the set of events is defined in the ChanEvents module, which includes HELLO, GOODBYE, and ERROR events.

Although we could have defined these events in the ChanFilter module, we opted to put these event definitions in a separate module, ChanEvents, to emphasize the point that these events might be defined by a group separate from the group that is developing this ILogger. Because the application below directly references values defined by ChanEvents, we must add an xdc.useModule() of ChanEvents to the application's configuration script. Except for this one line, denoted with + below, the application's configuration script is identical to the one above.

app.cfg
 
 
 
 
+
 
 
 
 
 
 
 
 
 
 
 
 
var Diags = xdc.useModule("xdc.runtime.Diags");
var Log = xdc.useModule("xdc.runtime.Log");
 
/* "import" the channel events definitions */
var ChanEvents = xdc.useModule("examples.runtime.logger.ChanEvents");
 
/* enable runtime control of "info" events only for non-modules */
var Main = xdc.useModule("xdc.runtime.Main");
Main.common$.diags_INFO = Diags.RUNTIME_ON;
 
/* use ChanFilter ILogger service provider to filter events */
var ChanFilter = xdc.useModule("examples.runtime.logger.ChanFilter");
Main.common$.logger = ChanFilter.create();
 
/* use LoggerSys ILogger to handle events not filtered by ChanFilter */
var LoggerSys = xdc.useModule("xdc.runtime.LoggerSys");
ChanFilter.common$.logger = LoggerSys.create();

In addition to the methods required by the ILogger interface, ChanFilter also provides a method, ChanFilter_setCurChan() to set the "current channel" ID; ChanFilter filters out all events that are not from this current channel. We use this method in the application (see c1 below) to illustrate how it's possible to enable the acquisition of events from selected channels at runtime in real-time.

app.c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c1
 
 
c2
 
 
c3
 
 
 
 
#include <xdc/std.h>
 
#include <xdc/runtime/Diags.h>
#include <xdc/runtime/Log.h>
 
#include <examples/runtime/logger/ChanEvents.h>
#include <examples/runtime/logger/ChanFilter.h>
 
Int main(Int argc, String argv[])
{
    /* generate simple printf event */
    Log_info0("hello world");
 
    /* generate a channel 0 event */
    Log_write1(ChanEvents_HELLO, 0);
 
    /* enable channel 0 events */
    ChanFilter_setCurChanId(0);
 
    Log_write2(ChanEvents_ERROR, 0, 23);
    Log_write1(ChanEvents_HELLO, 1);
    Log_write1(ChanEvents_GOODBYE, 0);
 
    return (0);
}

The output of this example is shown below.

1
2
3
xdc.runtime.Main: "app.c", line 12: hello world
xdc.runtime.Main: Channel 0: Error: 0x17
xdc.runtime.Main: Channel 0: goodbye

In the example above, notice that:

  1. the "hello world" Log_info event 1 was unaffected by the filter,
  2. the first call to Log_write with a channel event at c1 was dropped; prior to the first call to ChanFilter_setCurChanId() at c2 the current channel is undefined and all channel events are dropped, and
  3. after the call to ChanFilter_setCurChanId(0), only events from channel 0 are displayed; output from c3 does not appear between 2 and 3.

How to Implement the Filter

In this section we look under the covers to see how ChanFilter and ChanEvents are implemented. Not only is the implementation straight forward, it is easy to see how it can be generalized to handle virtually any target-side filtering you can imagine.

We begin by showing the ChanEvents module. Since ChanEvents does not need any runtime code, this module is the easiest to understand. We then look at the ChanFilter module and explore a variety of filtering techniques. Finally, we look at all the other files necessary to create a package to build and house these modules.

The ChanEvents Module

The ChanEvents module simply contains definitions of all "channel" events that are raised by the application. Because we only need to define the events, we only need to create a single .xdc file named ChanEvents.xdc with definition of all of the necessary Log events.

ChanEvents.xdc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
import xdc.runtime.Diags;
import xdc.runtime.Log;
 
module ChanEvents
{
    /*! ======== HELLO ======== */
    config Log.Event HELLO = {
        mask: Diags.INFO,
        msg: "Channel %d: hello"
    };
 
    /*! ======== ERROR ======== */
    config Log.Event ERROR = {
        mask: Diags.INFO,
        msg: "Channel %d: Error: 0x%x"
    };
 
    /*! ======== GOODBYE ======== */
    config Log.Event GOODBYE = {
        mask: Diags.INFO,
        msg: "Channel %d: goodbye"
    };
}

The ChanFilter Module

The ChanFilter module must implement the ILogger interface and it must support configuration time instance creation. While this requires fairly advanced features of the RTSC model, the files required are quite straight forward. Moreover, once the basics are in place it is very easy to extend or generalize the ChanFilter module for virtually any application.

ChanFilter.xdc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/* ChanFilter implements ILogger */
module ChanFilter inherits xdc.runtime.ILogger {
 
    /*! Set the channel to allow events from */
    Int setCurChanId(Int newChanId);
 
instance: /* this module manages instances */
 
    /* per-instance creation parameter */
    config String onError = null;  
 
internal: /* remainder is private internal implementation */
 
    struct Instance_State { /* static instances must declare instance state */
        String onError;
        Bool enabled;
    };
}

The implementation of this specification is shown below.

ChanFilter.c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <xdc/std.h>
 
#include <xdc/runtime/Log.h>
#include <xdc/runtime/Types.h>
#include <examples/runtime/logger/ChanEvents.h>
 
#include "package/internal/ChanFilter.xdc.h"
 
static Int curChanId = -1;
 
/* Initialize runtime created instance objects */
Void ChanFilter_Instance_init(ChanFilter_Object *filter, 
                              const ChanFilter_Params *params)
{
    filter->onError = params->onError;
    filter->enabled = TRUE;
}
 
/* ======== disable ======== */
Bool ChanFilter_disable(ChanFilter_Object *filter)
{
    Bool prev = filter->enabled;
    filter->enabled = FALSE;
    return (prev);
}
 
/* ======== enable ======== */
Bool ChanFilter_enable(ChanFilter_Object *filter)
{
    Bool prev = filter->enabled;
    filter->enabled = TRUE;
    return (prev);
}
 
/* ======== setCurChanId ======== */
Int ChanFilter_setCurChanId(Int newChanId)
{
    Int oldChanId = curChanId;
    curChanId = newChanId;
    return (oldChanId);
}
 
/* ======== write0 ========= */
Void ChanFilter_write0(ChanFilter_Object *filter, Log_Event evt, 
                       Types_ModuleId mid)
{
    /* It can't be a channel event if it has no arguments. */
    Log_put0(evt, mid);
}
 
/* ======== write1 ========= */
Void ChanFilter_write1(ChanFilter_Object *filter, Log_Event evt, 
                       Types_ModuleId mid, IArg a1)
{
    ChanFilter_write8(filter, evt, mid, a1, 0, 0, 0, 0, 0, 0, 0);
}
 
/* ======== write2 ========= */
Void ChanFilter_write2(ChanFilter_Object *filter, Log_Event evt, 
                       Types_ModuleId mid, IArg a1, IArg a2)
{
    ChanFilter_write8(filter, evt, mid, a1, a2, 0, 0, 0, 0, 0, 0);
}
 
/* ======== write4 ========= */
Void ChanFilter_write4(ChanFilter_Object *filter, Log_Event evt,
                       Types_ModuleId mid, IArg a1, IArg a2, 
                       IArg a3, IArg a4)
{
    ChanFilter_write8(filter, evt, mid, a1, a2, a3, a4, 0, 0, 0, 0);
}
 
/* ======== write8 ========= */
Void ChanFilter_write8(ChanFilter_Object *filter, Log_Event evt,
                       Types_ModuleId mid, IArg a1, IArg a2, IArg a3, 
                       IArg a4, IArg a5, IArg a6, IArg a7, IArg a8)
{
    Log_EventId eid = Log_getEventId(evt);
 
    /* check event id against range of all channel events */
    if (eid >= Log_getEventId(ChanEvents_HELLO)
        && eid <= Log_getEventId(ChanEvents_GOODBYE)) {
 
        if (curChanId != a1) {
            return; /* drop events from channels != curChanId */
        }
    }
 
    /* call underlying logger */
    Log_put8(evt, mid, a1, a2, a3, a4, a5, a6, a7, a8);
}

The heart of the filtering code is in the implementation of the ILogger.write8() method ChanFilter_write8(). In the example above, we first extract the event ID and compare this to the range of event IDs that correspond to the events defined by the ChanEvent module. This check is possible because Log event IDs are assigned in order for each module in the order that they are declared in the module specification. Since all events declared in the ChanEvents module require that the first argument is the channel ID of the channel raising the event, we can simply compare this argument against the curChanId value set by the application.

To complete the implementation, we need to add two meta-domain functions: instance$static$init and module$use.

ChanFilter.xs
 
 
 
 
 
 
 
 
 
 
 
 
/* Initialize static instance objects */
function instance$static$init(obj, params)
{
    obj.onError = params.onError;
    obj.enabled = true;
}
/* Declare modules used in ChanFilter.c */
function module$use()
{
    xdc.useModule("xdc.runtime.Log");
    xdc.useModule("examples.runtime.logger.ChanEvents");
}

The instance$static$init function is required to ensure that the static instances created by the ChanFilter module (as part of the user's configuration script) have all their fields initialized. The module$use function, on the other hand, ensures that all modules used in the implementation of ChanFilter are included in both the configuration process and in the link of the application using ChanFilter. By adding these statements, the user of ChanFilter does not need to add xdc.useModule statements to their configuration script for modules that are not directly referenced by their application. After all, the implementation of ChanFilter may change to require other modules and we don't want the client of ChanFilter to have to change anything to use this new version.

Extending ChanFilter

Extending ChanFilter.  Of course much more sophisticated filtering criteria can easily be added to this function. One can even imagine using the occurrence of certain events to trigger (in real-time) actions that facilitate diagnosis of system problems. For example, the following code can be added to ChanFilter_write8() to enable/disable event generation whenever a ChanEvent_Error event occurs.

 
 
 
 
/* change diagnostic settings on occurrence of error */
if (filter->onError && (eid == Log_getEventId(ChanEvents_ERROR))) {
    Diags_setMask(filter->onError);
}

This can be useful when events are buffered locally (to keep event logging fast and deterministic) and space is limited. In order to preserve the state of the event buffers, so they can be analyzed well after the error condition is detected, we need to disable the collection of all events.

Finally one can even generalize the ChanFilter_write8() function to simply call a user supplied function. Rather than saving an onError string in each ChanFilter instance, we can save a pointer to a user supplied function (userFxn) to execute. This would allow applications to define filtering functions in any part of their code base without having to understand how to implement a RTSC interface or create a RTSC package.

 
 
 
 
 
 
 
 
 
Void ChanFilter_write8(ChanFilter_Object *filter, Log_Event evt, Types_ModuleId mid,
                   IArg a1, IArg a2, IArg a3, IArg a4, 
                   IArg a5, IArg a6, IArg a7, IArg a8)
{
    if (!filter->userFxn(evt, mid, a1, a2, a3, a4, a5, a6, a7, a8)) {
        /* pass event to underlying logger if not handled by userFxn */
        Log_raise8(evt, mid, a1, a2, a3, a4, a5, a6, a7, a8);
    }
}

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.logger { 
    module ChanEvents; 
    module ChanFilter; 
}
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(["ChanFilter.c"]);
}

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

The package.bld file, on the other hand, requires some explanation. The existence of package.bld indicates that the package is (re)buildable - unless the end-user is expected to build the package, deployed packages omit this file. The contents of this package.bld file indicate that the package contains a library in the lib/ sub-directory containing the compiled version of ChanFilter.c for every target supported by the producer's build environment. Remarkably, this package.bld file is sufficient to compile ChanFilter.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.

[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