Eli   Documents Get Eli: Translator Construction Made Easy at SourceForge.net.
    Fast, secure and Free Open Source software downloads

General Information

 o Eli: Translator Construction Made Easy
 o Global Index
 o Frequently Asked Questions
 o Typical Eli Usage Errors

Tutorials

 o Quick Reference Card
 o Guide For new Eli Users
 o Release Notes of Eli
 o Tutorial on Name Analysis
 o Tutorial on Type Analysis
 o Typical Eli Usage Errors

Reference Manuals

 o User Interface
 o Eli products and parameters
 o LIDO Reference Manual
 o Typical Eli Usage Errors

Libraries

 o Eli library routines
 o Specification Module Library

Translation Tasks

 o Lexical analysis specification
 o Syntactic Analysis Manual
 o Computation in Trees

Tools

 o LIGA Control Language
 o Debugging Information for LIDO
 o Graphical ORder TOol

 o FunnelWeb User's Manual

 o Pattern-based Text Generator
 o Property Definition Language
 o Operator Identification Language
 o Tree Grammar Specification Language
 o Command Line Processing
 o COLA Options Reference Manual

 o Generating Unparsing Code

 o Monitoring a Processor's Execution

Administration

 o System Administration Guide

Mail Home

Execution Monitoring Reference

Previous Chapter Next Chapter Table of Contents


Implementation

This chapter describes some of the implementation of Noosa in detail. Eli users who just want to perform monitoring with existing monitors do not need to read this chapter. It is intended for Eli developers or advanced users who want to extend the capabilities of Noosa.

Monitoring Interfaces

Noosa needs to obtain information from the running program. It uses the program's monitoring interface to do it. A program's monitoring interface is the union of all of the monitoring interfaces of the components making up that program. The contents of the monitoring interface for a component depend on the nature of the component and the information that it wants to make available to the monitoring system.

Monitoring interfaces are described by type-`dapto' files. (See Dapto Grammar, for the syntax of the Dapto language.) Dapto files contain the information described in the following. Examples are taken from the monitoring interface for the string table module in Eli (see the file `pkg/Adt/csm.dapto' in the Eli distribution).

In the following discussion, two pre-defined data types: int and str are used. These correspond to the C data types int and char *, respectively.

Aspects

All elements of a monitoring interface are grouped together into aspects (similar to a module). The names of aspects are used to enable the monitoring system to decide what components are present in the program. Some monitoring commands are only applicable to programs which provide the aspects on which the monitor depends. For example, the Phrase command can only be used on programs that contain parsers. See Monitoring Database, for more details on this mechanism.

An aspect syntactically encloses the interface elements which it contains.

aspect string;
    Interface elements of the string aspect
end;

Event Types

Event types are described in a monitoring interface by giving their names plus the names and types of their parameters. We also enforce the inclusion of documentation strings for each of these entities to enable the user interface to provide readable descriptions of events where necessary.

The string table monitoring interface contains one event, string_stored, which is generated whenever a string is inserted into the table. Consequently we have the following event description in the monitoring interface:

event string_stored* "Storage of a new string in the string table"
    (int index "Index of new string", str string "New string");

Normally event types are assumed to be hidden from the user. If you want the events of a particular type to be visible to the user through the Handlers window, it is necessary to append a * to the name of the type, as is done in the example above.

Operations

Operation signatures are described in the monitoring interface by giving the name of the operation, its parameters (if any), its return type (if any), along with documentation strings. Currently the return type of an operation must be str or there must be no return type.

Here is the signature for the string table get_string and set_string operations:

operation get_string "Look up a string given its index"
    (int index "Index of the string to be looked up") : str

operation set_string "Change the value of a stored string"
    (int index "Index of string to be changed",
     str value "New value for string")

Operation implementations are given in C following the operation signature. Any legal C code can be used in an operation definition, except that C return statements should not be used and to return values from an operation you must use the following macros:

DAPTO_RESULT_STR(char *s)
Append the string s to the result to be returned by this operation.
DAPTO_RESULT_INT(int i)
Append the integer i as a string to the result to be returned by this operation.
DAPTO_RESULT_LONG(long l)
Append the long integer l as a string to the result to be returned by this operation.
DAPTO_RESULT_PTR(void *v)
Append the arbitrary pointer v to the result to be returned by this operation. The value will be passed as a long integer and won't be interpreted by Noosa. To be useful, this value must later be passed back to another part of the monitoring interface where it can be used as a pointer again.
DAPTO_RETURN
Return the current result as the value of this operation.
Use of the DAPTO_RESULT macros sets up a value that is returned when the end of the operation is reached. To return from the middle of an operation use the DAPTO_RETURN macro with no arguments.

For example, the following is the full definition of the get_string operation:

operation get_string "Look up a string given its index"
    (int index "Index of the string to be looked up") : str
{ 
    if ((index < 0) || (index >= numstr)) {
        DAPTO_RESULT_STR ("*** Illegal string table index ***");
    } else {
        char *s = string[index];
        if (s == (char *) 0) {
            DAPTO_RESULT_STR ("*** No string at this index ***");
        } else {
            DAPTO_RESULT_STR (s);
        }
    }
}

The DAPTO_RESULT macros for integer, long and pointer values should only be used with arguments whose addresses can be taken. For other values (e.g., return values from function calls or the values of expressions) there are analogous macros whose names are formed by appending VAL to the macro name. For example, the first of the following calls will not compile; the second must be used.

DAPTO_RESULT_INT (i + 1);
DAPTO_RESULT_INTVAL (i + 1);

The VAL forms of the macros can always be used, but they incur the cost of an extra copy compared to the non-VAL form.

Header Files

When writing the operation and translation parts of a monitoring interface it is often necessary to refer to C entities exported by other modules. To enable the implementation of the monitoring interface to access these other interfaces it is necessary to include them in the monitoring interface description. Interfaces are included by simply naming the header files which contain them.

The string table monitoring interface uses some standard C library functions, C string functions and entities made available by the string table module. Consequently the interface also includes the following lines:

<stdlib.h>
<string.h>
"csm.h"

Non-standard types

By default, Dapto can handle the built-in types int and str. If you want to pass a value of some other type to an operation or receive such a value as an event parameter you need to tell the system about it. If you don't do anything then the values will be passed as the string "unknown".

Even if you do not add new operations or events involving non-standard types you probably want to provide proper monitoring support for them anyway. The reason is that other parts of the system may need to report values of these types to Noosa. Most notably, the attribute evaluator generates events whenever attributes are evaluated. If you want to be able to monitor attributes of non-standard types then you must add proper monitoring support for these types or the attribute values will be reported as "unknown".

The rest of this section explains what you need to do to monitor values of a non-standard type. It talks about the monitoring interface and associated support. The next section describes how you might go about displaying values in the Noosa transcript window for user browsing.

The following information is based on the monitoring support for environment values in the current Eli system. The environment module has the following monitoring interface containing a couple of events and an operation (see the file `pkg/Name/envmod.dapto' in the Eli distribution).

aspect envmod;

"envmod.h"

event env_created* "An environment value has been created"
    (Environment env "The environment that was created",
     Environment parent "The parent environment (if any)");

event binding_made* "A binding has been made in an environment"
    (Environment env "The environment in which the binding was made",
     int idn "The identifier that was bound",
     DefTableKey key "The key to which the identifier was bound");

operation get_scope_info
    "Return the parent environment of an environment and its idn-key bindings"
    (Environment env "The environment to be searched") : str
{
    Scope s;

    DAPTO_RESULT_PTR (env->parent);
    for (s = env->relate; s != NoScope; s = s->nxt) {
        DAPTO_RESULT_INT (s->idn);
        DAPTO_RESULT_PTR (s->key);
    }   
}
 
end;

As is conventional in a monitoring interface, the events are used to notify Noosa of important changes to the environment values as they occur. The operation is used to allow Noosa to get the complete contents of an environment. Providing both events and operations in this style is a good idea because the events allow fine-grained control via breakpoints and handlers while the operation can be used to implement value browsing.

Note that the operation implementation can use any C code it likes to determine the appropriate information and return it to Noosa. In this case we use the fields provided by the environment module to return the parent environment and all of the integer-key pairs.

Since Environment and DefTableKey values are passed as event and operation parameters we need to tell Dapto how to pass them. In the following we just talk about environment values. Support for definition table keys is similar.

When Dapto generates the event generation code for an event parameter of unknown type it attempts to use a macro of the form DAPTO_RESULTx where x is the name of the parameter type. Thus to get the value passed correctly you need to define this macro. Usually the definition is placed in the header file that defines the type itself. E.g., `envmod.h' contains the following definition.

#define DAPTO_RESULTEnvironment(e) DAPTO_RESULT_PTR (e)
which says that an environment value should be sent from the running program to Noosa as a pointer (since it is a pointer).

Similarly, to permit values of this type to be sent from Noosa to the running program (as operation parameters) you need to define a macro whose name is DAPTO_ARGx. For example, for environments we define the following macro.

#define DAPTO_ARGEnvironment(e)    DAPTO_ARG_PTR (e, Environment)
which says that it should be received as a pointer. In the definition of the macro, the second parameter is the type of the value. It is used to cast the received value to the appropriate type.

Browsing non-standard types

Once you have Noosa and the running program correctly passing values of a non-standard type back and forth, you usually want to see those values in the Noosa transcript. If the values are structured, you will also want to add browsing support for them.

Adding browsing support for a non-standard type involves writing Tcl code that will be invoked whenever a value of this type is browsed. The procedure can be automatically loaded into Noosa by placing its definition in a startup file (see User Initialisation). Alternatively, it can be placed in a file of type tcl and included in your specifications. At startup Noosa will load all files of this type.

The Noosa transcript is a general text display area, so you can use n_say to display whatever you like (it always displays at the end). As a special case if you display something of the form t:v where t is the name of a type which has browsing support, then the value v will also be browsable. In general it's a good idea to arrange for values to be prefixed by their type in this way even if no browsing support is currently available. The type provides a valuable clue to the user and if browsing support is added later it will available here without you having to do anything.

Here is a slightly simplified version of the Tcl support used by Eli to support browsing of environment values.

set n(Environment,desc) "Identifier scoping environment"

proc n_Environment_say {env} {
    n_say "Environment:0x[n_dectohex $env]"
}

proc n_Environment_open {text env} {
    n_say "$text"
    if {$env == 0} {
        n_say "\n  NoEnv\n"
    } else {
        set env [n_hextodec $env]
        set r [n_send get_scope_info $env]
        if {[lindex $r 0] != 0} {
            n_say " (parent: "
            n_Environment_say [lindex $r 0]
            n_say ")"
        }
        set r [lreplace $r 0 0]
        n_say "\n"
        set c 0
        foreach {i j} $r {
            n_say "  "
            n_say_val DefTableKey $j
            set s [n_send get_string $i]
            n_say " $s\n"
            incr c
        }
        if {$c == 0} {
            n_say "  No bindings\n"
        }
    }
}

The first set command sets a documentation string that will be used to display an information message at the bottom of the Noosa window whenever the user moves the mouse over a value of this type in the transcript window. In general, for a type x you need to set the array element n(x,desc) in the global scope.

The procedure n_Environment_say is used by Noosa to display values of this type. Since Environment values are pointers, the code displays them in hex to facilitate cross-referencing with values displayed by a source-level debugger. The Noosa library procedure n_dectohex is used to obtain the hexadecimal representation of the value. If n_Environment_say did not exist, values would be displayed in the style t:v where t is the type and v is the value in decimal.

The procedure n_Environment_open is invoked whenever the user clicks on a value of this type in the transcript window. In general, the procedure name must be n_x_open where x is the type name. The existence of this procedure is taken by Noosa as an indication that values of type x should be browsable. The procedure gets two parameters; the first is the complete text that the user clicked on (which includes the type name) and the second is the value part of that text. In this case the second parameter will be the environment value of interest.

The implementation of this procedure first displays the clicked-on text to identify the subsequent output because the browsable value may be a long distance from the bottom of the transcript where the output will be displayed. A null environment is displayed in a standard way to match the user's view of the module.

Non-null environments are converted by n_hextodec into decimal before being passed to the get_scope_info operation defined in the environment module monitoring interface (see Non-standard types). This operation gets the parent environment and the integer-key pairs as a Tcl list. The Noosa procedure n_send is used to invoke the operation with the environment value as the sole parameter.

When the get_scope_info operation returns, the n_Environment_open procedure goes on to display various information in the Noosa transcript window. Strings are displayed using n_say. The parent environment (if there is one) is displayed using n_Environment_say so that it is displayed in a style consistent with other environments.

All of the integer-key pairs in the environment are displayed. The routine n_say_val is used to display the keys. It is passed the type of the value and the value itself. n_say_val separates the decision about how to display keys from other code. n_say_val just dispatches to n_DefTableKey_say if it exists.

Note that we don't display the integers as-is, we use the get_string operation from the string storage module to convert them to strings which is generally more helpful. Note: arguably this is a bug since it's possible to use the environment module with integers that are not string table indexes.

Implementing Monitoring Interfaces

A type-`dapto' file defines the monitoring interface of a component. (See Dapto Grammar, for the syntax of the Dapto language.) The dapto program turns these interfaces into code that can be incorporated into a program that we want to be able to monitor. Dapto does two main things:

  1. Generates a type-`c' file and a type-`h' file containing an implementation of the monitoring interface given by its input type-`dapto' file.

    The type-`c' file will contain routines to enable the monitoring system to invoke data operations and receive the results. The mechanisms by which this happens are beyond the scope of this manual.

    Also contained in the type-`c' file will be one function definition for each event type defined in the monitoring interface. For each event type X there will be a function _dapto_X that has parameters corresponding to the parameters of X. (See Adding Monitoring Support To A Component, for details on how to use this function.)

    The type-`h' file generated by dapto will contain the externally visible interface of the type-`c' file.

  2. Generates a type-`db' file containing a monitoring database with information about the monitoring interface. This file is a TCL script that sets up data structures for use by the monitoring system. It is used to let the monitoring system know which aspects are provided by the monitoring interface and which events are contained in those aspects. See Monitoring Database, for more information on how the database is used.

The names of the generated files depend on the name of the input file; `csm.dapto' will produce `csm_dapto.c', `csm_dapto.h' and `csm_dapto.db'.

Monitoring Database

A monitoring database is generated by Dapto from a monitoring interface description (see Implementing Monitoring Interfaces). The concatenation of the monitoring databases for all of the components present in a program comprises the monitoring database for the program.

The monitoring database is simply a TCL file which, when loaded by Noosa, provides information about the aspects and events of the monitoring interface. For example, the monitoring database for the string table monitoring interface (see Monitoring Interfaces) yields the following database (reformatted slightly):

lappend n(aspects) string
lappend n(events) \
    [list string_stored "Storage of a new string in the string table" \
        { index "Index of new string" string "New string" } 1]
The global TCL lists n(aspects) and n(events) are used to store the database information. n(aspects) contains a list of the all of the aspect names contained in the program. n(events) is a list of lists; each sub-list contains the name and documentation strings for a single event type and its parameters, plus a flag which is 1 if the event is visible to the user and 0 otherwise.

Adding Monitoring Support To A Component

Once you have a monitoring interface implementation for a component you must add monitoring support to the component itself. This support consists entirely of calls to the event generation routines for any events you have in your interface (see Monitoring Interfaces and see Implementing Monitoring Interfaces). If you have no events in your interface, the code of the component does not need to be changed.

Adding event generation to a component is a matter of adding calls to event generation routines at the appropriate places. The details of this will depend on the component, but the idea is to insert the calls at places where the action which the event represents can be said to have taken place. Any necessary event parameters should be passed to the event generation routine.

To enable a monitoring-free version of the component to be easily produced, the convention is that all additions purely for the purpose of monitoring be conditionalised by

#ifdef MONITOR
...
#endif

The following examples are based on monitoring support for the Eli string table component. The component must be modified to include the C interface to the monitoring interface:

#ifdef MONITOR
#include "csm_dapto.h"
#endif

Then we must identify places in the code where string_stored events must be generated. There is only one of these, at the end of the routine stostr, so we add the following code to generate the event with the appropriate parameter values:

#ifdef MONITOR
    _dapto_string_stored (numstr, string[numstr]);
#endif

When the component is compiled by Eli with the -DMONITOR compiler option (implied by the +monitor parameter), this monitoring support will be included.

Supporting Profiling

Noosa contains support for two kinds of profiles (see Frequency Profiles and see Time Profiles). To support profiling of a component it is necessary to add extra event generation to a component. It is necessary to generate an enter event whenever execution enters the code of the component and a leave event whenever execution leaves the code of the component. These events have the following signatures:

event enter "Enter a program component"
    (str name "Name of component");
event leave "Leave a program component"
    (str name "Name of component");

For the string table component we would add the following code to the beginning of each string table routine:

#ifdef MONITOR
  _dapto_enter ("string");
#endif
and the following code at each exit point of each string table routine:
#ifdef MONITOR
  _dapto_leave ("string");
#endif
The event parameter ("string" in this case) is used by the profile monitoring code to identify the component.

Dapto Grammar

The following context-free grammar defines the syntax of the Dapto language. ident is an identifier in the C style. Identifier definitions are required to be unique within a specification and within event and operation blocks. str and bstr are strings delimited by double quotes and angled brackets, respectively. text is arbitrary text delimited by braces.

spec: aspects.

aspects: aspect_stmt / aspects aspect_stmt.
aspect_stmt: `aspect' iddef `;' sigs `end' `;'.

sigs: sig / sigs sig.
sig: event_sig / operation_sig / str / bstr.

event_sig: `event' iddef export str event_block `;'.
event_block: `(' optattrs `)'.
export: `*' / /* empty */.
optattrs: /* empty */ / attrs.
attrs: attr / attrs `,' attr.
attr: typeid iddef str.

operation_sig: `operation' iddef str operation_block text /
               `operation' iddef str operation_block `:' typeid text.
operation_block: `(' optparams `)'.
optparams: /* empty */ / params.
params: param / params `,' param.
param: typeid iddef str.

iddef: ident.
iduse: ident.
typeid: ident.


Previous Chapter Next Chapter Table of Contents