/************************************************************************/
/* */
/* Tracery -- Allow access to module trace facilities */
/* */
/* This (slightly misnamed) module helps greatly in the */
/* creation of reusable software. It collects the trace */
/* facilities defined by each module into a coherent */
/* whole that may be easily manipulated by outsiders -- */
/* typically via command-line switches. */
/* */
/* The basic idea is that each module defines one or more */
/* ways of tracing its behaviour and/or reporting data */
/* during operation. The tracing "views" that each module */
/* defines are totally independent -- there is no external */
/* framework imposed by any outsider. This is essential */
/* as no conceivable pre-defined framework could */
/* adequately cover the vast range of software modules */
/* in existence. Having a set of independent views is */
/* more powerful and expressive than having a hierarchy of */
/* trace levels, as the relative importance of the trace */
/* information may be different for the creator and the */
/* user. In addition, the trace selection in a level- */
/* oriented scheme is usually very crude: if you enable */
/* "Level 3" tracing, you usually get "Level 1" and */
/* "Level 2" traces, whether you want them or not. */
/* */
/* Capturing these views within the module implementation */
/* makes the tracing more powerful than traces generated */
/* by debuggers. This is partially because the designer's */
/* intent is captured, and partially because the traces */
/* can be tailored in ways that may be awkward for the */
/* debugger. This reduces the wasteful (and error-prone) */
/* reengineering that would otherwise be incurred by the */
/* maintainer and/or user. The reengineering saved is: */
/* - deciding where and when to observe the system, */
/* - creation of an accurate model of the system, and */
/* - specification of monitor activities to the debugger. */
/* */
/* Another benefit of capturing the traces in the module */
/* is that the trace code probably is more portable than */
/* traces written for a specific debugger. */
/* */
/* Another part of the design is that the trace flags */
/* enable or disable selected blocks of code -- they do */
/* not concern themselves with the processing required */
/* to generate the traces, nor do they proscribe how the */
/* trace is to be presented. This allows the traces to */
/* be far less invasive when present. In addition, each */
/* trace is defined using a macro, so the entire trace */
/* code may be excluded from the compilation simply by */
/* defining the macro(s) to expand to nothing. */
/* */
/* The trace flags themselves are implemented as simply as */
/* possible: each trace flag register is a (32-bit) */
/* longword, and each view is assigned one bit within the */
/* register. Testing whether a trace is to be generated */
/* is merely seeing if any of the views associated with */
/* that trace have bits set in the trace flags register -- */
/* typically 3-5 instructions per trace test. Defining */
/* the register storage is the responsibility of the */
/* module -- typically each instantiation of each object */
/* in the system will have its own trace flags register. */
/* */
/* Once all the views and trace flags have been defined, */
/* the one remaining management problem is providing */
/* access to all the flag registers so that outsiders may */
/* operate the trace facility. This is where this module */
/* comes in: It provides a place for details of all the */
/* flags, together with simple instructions for setting */
/* and clearing flag bits, to be collected together and */
/* operated via a uniform interface. The modules and */
/* objects don't contain any code to set/clear flag bits -- */
/* they merely provide means for the address of each */
/* flag register to be obtained, and details of simple */
/* mapping between ASCII characters and flag bits. */
/* */
/* The main danger of explicitly writing traces in the */
/* sources is that some code may be incorrectly placed */
/* in the trace block which is needed when trace statements */
/* aren't invoked. This can only be found by test and */
/* and inspection, or avoided by reusing trustworthy */
/* code. Hopefully the increase in reuse will offset */
/* this risk. */
/* */
/* So this module, Tracery, collects all the flag register */
/* descriptions, together with the name of each flag */
/* register, then manipulates flags based on commands */
/* from external sources (typically via debug directives */
/* on the command line found by the main program). */
/* */
/* Note that each module does not register itself -- this */
/* is left to the main program to do. This is so that as */
/* modules are reused for different jobs and in different */
/* applications, the name used can be chosen to be relevant */
/* to the job at hand, and namespace collisions can be */
/* avoided. In addition, the registration requires */
/* merely that the module advertise a link function to */
/* be handed to Tracery. This design allows the platform */
/* to hook all the relevant bits of the system together */
/* without needing to know about unnecessary details. */
/* */
/* Specifying flag names and edits is done by a very simple */
/* list of name/mask/set specifications. */
/* */
/* The classification implied by the bits selecting each */
/* piece of trace code are often worth reporting in their */
/* own right. If all else fails, start tracing EVERYTHING */
/* and then use a worthwhile grep(!) to scan the output for */
/* patterns. Tracery caters for this by defining the */
/* variable TRACERY_FLAGS each time a block of code is */
/* expanded. This variable contains all the flags marking */
/* that block -- not merely the bits that matched the */
/* flag register. This variable can either be reported */
/* as a number, or the block can use Decode and Name */
/* to report the flags and the traced object in human- */
/* readable form. Some GCC-specific incantations have been */
/* used, however, to stop warnings if the variable is unused. */
/* */
/* Copyright (C) 1999-2000 Grouse Software. All rights reserved. */
/* Written for Grouse by behoffski (Brenton Hoff). */
/* */
/* Free software: no warranty; use anywhere is ok; spread the */
/* sources; note any mods; share variations and derivatives */
/* (including sending to behoffski@grouse.com.au). */
/* */
/************************************************************************/
#include <compdef.h>
#include <fnmatch.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "tracery.h"
#define TRACERY_DISPLAY_COLUMN 16
#define TRACERY_DISPLAY_RIGHT_MARGIN 77
/*Storage for individual registrations*/
typedef struct Tracery_StructType {
/*Link to next member of list*/
struct Tracery_StructType *pNext;
/*Information provided upon registration*/
CHAR *pName;
UINT NameLength;
CHAR *pDescription;
Tracery_ObjectInfo *pClientInfo;
LWORD DefaultFlags;
Tracery_EditEntry *pEditList;
} Tracery_Entry;
/*Module-wide globals*/
typedef struct {
/*Dummy entry serving as list head*/
Tracery_Entry Head;
} TRACERY_MODULE_CONTEXT;
module_scope TRACERY_MODULE_CONTEXT gTracery;
/************************************************************************/
/* */
/* Init -- Prepare module for operation */
/* */
/************************************************************************/
public_scope void
Tracery_Init(void)
{
/*No members, yet*/
gTracery.Head.pNext = NULL;
} /*Init*/
/************************************************************************/
/* */
/* Register -- Receive flags register and manipulation details */
/* */
/* This function collects the details needed to operate the */
/* trace flags for the specified entity. It returns FALSE */
/* if unable to accept the entry. Once registration is */
/* accepted, the flag register may be modified at any time */
/* by any party calling Tracery (until the flag is */
/* deregistered). The text referenced by pName, and the */
/* bit editing list named by pEditList, needs to be */
/* maintained by the caller: this module merely remembers */
/* the pointers without bothering to make a private copy. */
/* */
/* The edit list must be terminated by TRACERY_EDIT_LIST_END. */
/* An example of calling Register might look a bit like */
/* the following: */
/* */
/* static struct Tracery_EditEntry Image_EditDefs[] = { */
/* {"w", BIT15, 0x00, "Ignore warnings"}. */
/* {"W", BIT15, BIT15 "Trace warnings"}. */
/* {"f", BIT14, 0x00, "Ignore failures"}. */
/* {"F", BIT14, BIT14, "Trace failures"}. */
/* {"m", BIT13, 0x00, "Ignore memory"}. */
/* {"M", BIT13, BIT13, "Trace memory"}. */
/* {"c", BIT12, 0x00, "Ignore calls"}. */
/* {"C", BIT12, BIT12, "Trace calls"}. */
/* {"q", BIT0, 0x00, "Ignore quadtree"}. */
/* {"Q", BIT0, BIT0, "Trace quadtree"}. */
/* {"l", BIT1, 0x00, "Ignore linked list"}. */
/* {"L", BIT1, BIT1, "Trace linked list"}. */
/* {"g", BIT2, 0x00, "Ignore corner stitching"}. */
/* {"G", BIT2, BIT2, "Trace corner stitching"}. */
/* {"Tx", BIT3, BIT3, "Trace network Tx"}. */
/* {"noTx", BIT3, 0x00, "Ignore network Tx"}. */
/* {"Rx", BIT4, BIT4, "Trace network Rx"}. */
/* {"noRx", BIT4, 0x00, "Ignore network Rx"}. */
/* TRACERY_EDIT_LIST_END */
/* }; */
/* */
/* Although the details shown above need to be given to */
/* Tracery, we don't want to burden the outside party */
/* in charge of the registrations with too much detail. */
/* So, the registration below merely takes an object */
/* pointer and a registration function. The object pointer */
/* allows the environment to register traces for either */
/* an entire module (pObject == NULL), or for a single */
/* object created by that module. */
/* */
/* Tracery_Register("ps", "Pixel Server", */
/* NULL, Image_TraceryLink); */
/* */
/************************************************************************/
public_scope BOOL
Tracery_Register(char *pName,
CHAR *pDescription,
void *pObject,
Tracery_RegistrationFn *pRegFn)
{
Tracery_Entry *pSearch;
Tracery_Entry *pNew;
/*Start search at head of list*/
pSearch = &gTracery.Head;
/*Look through list for correct insertion point*/
for (;;) {
/*Have we exhausted the list?*/
if (pSearch->pNext == NULL) {
/*Yes, add item to the end*/
break;
}
/*Is this name already present in the list?*/
if (strcmp(pName, pSearch->pNext->pName) == 0) {
/*Yes, merely update entry with provided details*/
pSearch = pSearch->pNext;
pSearch->pDescription = pDescription;
if (! pRegFn(pObject,
TRACERY_REGCMD_GET_INFO_BLOCK,
&pSearch->pClientInfo)) {
/*Sorry, can't update if no client info*/
return FALSE;
}
pRegFn(pObject,
TRACERY_REGCMD_GET_DEFAULT_FLAGS,
&pSearch->DefaultFlags);
pRegFn(pObject,
TRACERY_REGCMD_GET_EDIT_LIST,
&pSearch->pEditList);
return TRUE;
}
/*Move to the next entry of the list*/
pSearch = pSearch->pNext;
}
/*Create an entry to store this registration*/
pNew = (Tracery_Entry *) malloc(sizeof(Tracery_Entry));
if (pNew == NULL) {
/*Sorry, unable to create entry*/
return FALSE;
}
pNew->pName = pName;
pNew->NameLength = strlen(pName);
pNew->pDescription = pDescription;
/*Now work with registration function to find object's details*/
if (! pRegFn(pObject,
TRACERY_REGCMD_GET_INFO_BLOCK,
&pNew->pClientInfo)) {
/*Sorry, can't update if no client info*/
return FALSE;
}
pRegFn(pObject,
TRACERY_REGCMD_GET_DEFAULT_FLAGS,
&pNew->DefaultFlags);
pRegFn(pObject,
TRACERY_REGCMD_GET_EDIT_LIST,
&pNew->pEditList);
/*Write cross-reference so we can quickly find ourself when asked*/
pNew->pClientInfo->pTraceryRef = pNew;
/*Insert new entry after entry found by search*/
pNew->pNext = pSearch->pNext;
pSearch->pNext = pNew;
/*Registered successfully*/
return TRUE;
} /*Register*/
/************************************************************************/
/* */
/* EditFlags -- Handle detailed flag edit specification */
/* */
/* Edits the entry's flag register according to the edit */
/* specification given. pAfterEnd points to the next */
/* character after the end of the specification. */
/* */
/************************************************************************/
module_scope void
Tracery_EditFlags(Tracery_Entry *pEntry, CHAR *pSpec, CHAR *pAfterEnd)
{
Tracery_EditEntry *pEdits;
LWORD Flags = pEntry->pClientInfo->Flags;
while (pSpec < pAfterEnd) {
/*Search for specification in edit list*/
pEdits = pEntry->pEditList;
for (;;) {
/*Have we reached the end of the list?*/
if (pEdits->pName == NULL) {
/*Yes, nothing matched: try next position*/
pSpec++;
break;
}
/*Does this entry apply?*/
if (strncmp(pSpec,
pEdits->pName,
strlen(pEdits->pName)) == 0) {
/*Yes, edit module-local flags*/
Flags &= ~ pEdits->MaskBits;
Flags |= pEdits->SetBits;
/*Skip past specification*/
pSpec += strlen(pEdits->pName);
break;
}
/*Move to next entry, if any*/
pEdits++;
}
}
/*Write edited flag settings back into client's register*/
pEntry->pClientInfo->Flags = Flags;
} /*EditFlags*/
/************************************************************************/
/* */
/* Configure -- Manipulate flags based on configuration */
/* */
/* This is the main interface used by text-driven interfaces, */
/* including command-line switches. It expects to see a list */
/* of trace module names, separated by commas. The names */
/* may be modified in a couple of ways; the meanings are: */
/* name -- Turn on default trace flags for object */
/* +name -- Turn on all trace flags for object */
/* -name -- Turn off all trace flags for object */
/* name(Chars) -- Set flags based on on/off chars */
/* */
/* Returns FALSE if the configuration string didn't make sense. */
/* */
/************************************************************************/
public_scope BOOL
Tracery_Configure(CHAR *pConfiguration)
{
Tracery_Entry *pSearch;
BOOL Select;
BOOL Invert;
CHAR *pSpecEnd;
CHAR *pAfterName;
CHAR Delim;
CHAR *pCopiedConfig;
CHAR *pConfig;
/*Copy string to private memory as we edit it during parsing*/
pCopiedConfig = strdup(pConfiguration);
if (pCopiedConfig == NULL) {
/*Sorry, not enough memory to copy config string*/
return FALSE;
}
pConfig = pCopiedConfig;
/*Work through configuration text*/
for (;;) {
/*Skip over separators and white space, if any*/
while ((*pConfig == ',') ||
(*pConfig == ' ')) {
pConfig++;
}
/*Does the next name start with '-'?*/
Invert = FALSE;
if (*pConfig == '-') {
/*Yes, user wants to defeat all flags*/
Invert = TRUE;
pConfig++;
}
/*Have we reached the end of the config string?*/
if (*pConfig == '\0') {
/*Yes, finished processing*/
break;
}
/*Find the end of the name*/
pAfterName = pConfig;
while ((*pAfterName != ',') &&
(*pAfterName != '\0') &&
(*pAfterName != '(') &&
(*pAfterName != ' ')) {
pAfterName++;
}
/*Remember name delimiter and replace it with NUL terminator*/
Delim = *pAfterName;
*pAfterName++ = '\0';
/*Look for the facility name in the list*/
for (pSearch = gTracery.Head.pNext;
pSearch != NULL;
pSearch = pSearch->pNext) {
/*Does this name match the specified facility?*/
if (fnmatch(pConfig,
pSearch->pName,
FNM_PERIOD) != 0) {
/*No, move to the next name, if any*/
/*?? We ignore errors for now*/
continue;
}
/*Matched name: are more detailed specifiers given?*/
Select = FALSE;
if (Delim == '(') {
Select = TRUE;
}
/*Apply edits as specified*/
if ((! Select) && (! Invert)) {
/*No selection: enable EVERYTHING*/
pSearch->pClientInfo->Flags = ~0uL;
} else if ((! Select) && (Invert)) {
/*Module disabled: Revert to global*/
pSearch->pClientInfo->Flags = 0;
} else {
/*Find the extent of the detailed selection*/
pSpecEnd = strstr(pAfterName, ")");
if (pSpecEnd == NULL) {
/*Sorry, badly-formed spec*/
free(pCopiedConfig);
return FALSE;
}
/*Handle more detailed spec*/
Tracery_EditFlags(pSearch,
pAfterName,
pSpecEnd);
}
}
/*Note: We don't complain if no-one matches spec*/
/* -- this might be changed if it's too confusing*/
/*Reinstate character delimiting name*/
*--pAfterName = Delim;
pConfig = pAfterName;
/*Move to the next component of the list, if any*/
pConfig = pAfterName;
while ((*pConfig != '\0') &&
(*pConfig != ',')) {
pConfig++;
}
/*Did we hit the end of the list?*/
if (*pConfig == '\0') {
/*Yes, finished specifier*/
break;
}
}
/*Finished implementing configuration*/
free(pCopiedConfig);
return TRUE;
} /*Configure*/
/************************************************************************/
/* */
/* Deregister -- Cancel module registration */
/* */
/* Removes the specified flag register from the list */
/* of registers able to be manipulated by this facility. */
/* Once deregistration is complete, this module will not */
/* generate any more references to the flag register. */
/* Always remember to deregister as otherwise dangling */
/* references may cause havoc! */
/* */
/************************************************************************/
public_scope void
Tracery_Deregister(Tracery_ObjectInfo *pInfoBlock)
{
/*Not implemented yet*/
} /*Deregister*/
/************************************************************************/
/* */
/* Name -- Report the name associated with a flag register */
/* */
/* Returns a pointer to the name of the object associated */
/* with the specified flag register, or a pointer to the */
/* string "(?object)" if the flag register isn't known. */
/* Used where trace action code wishes to report information */
/* using the name assigned to the object or module at */
/* run time. */
/* */
/************************************************************************/
public_scope char *
Tracery_Name(Tracery_ObjectInfo *pInfoBlock)
{
Tracery_Entry *pEntry;
/*Find internal entry assoiated with specified block*/
pEntry = (Tracery_Entry *) pInfoBlock->pTraceryRef;
/*Report object's name obtained when object was registered*/
return pEntry->pName;
} /*Name*/
/************************************************************************/
/* */
/* Decode -- Convert flag bits into text specifiers */
/* */
/* This function finds the list of text specifiers */
/* associated with the specified flag register, and then */
/* uses the specifications to decode the flag bits. */
/* This can be used to display the bits associated with */
/* a trace message in a human-readable format. */
/* */
/* Returns FALSE if unable to fit all the decoded specs */
/* into the supplied string. */
/* */
/************************************************************************/
public_scope BOOL
Tracery_Decode(Tracery_ObjectInfo *pInfoBlock,
LWORD FlagBits, CHAR *pString, UINT StringSize)
{
Tracery_Entry *pEntry;
Tracery_EditEntry *pEdits;
UINT NameLen;
BOOL SpaceOkay = TRUE;
CHAR TempBuf[12];
LWORD ReportedBits = 0;
/*Find internal entry assoiated with specified block*/
pEntry = (Tracery_Entry *) pInfoBlock->pTraceryRef;
/*Now loop through edit specifications, reporting what's set*/
for (pEdits = pEntry->pEditList; pEdits->pName != NULL; pEdits++) {
/*Is this option set?*/
if ((FlagBits & pEdits->MaskBits) == pEdits->SetBits) {
/*Yes, is space available to report it?*/
NameLen = strlen(pEdits->pName);
if (StringSize > NameLen) {
/*Yes, add it to string*/
ReportedBits |= pEdits->MaskBits;
strcpy(pString, pEdits->pName);
pString += NameLen;
StringSize -= NameLen;
} else {
/*Sorry, item didn't fit*/
SpaceOkay = FALSE;
}
}
}
/*Do we have any bits unreported?*/
FlagBits &= ~ReportedBits;
if (FlagBits != 0) {
/*Yes, format them for output as a hex number*/
sprintf(TempBuf, "+0x%08lx", FlagBits);
if (StringSize > strlen(TempBuf)) {
/*Space is available, add number to string*/
strcpy(pString, TempBuf);
pString += strlen(TempBuf);
StringSize -= strlen(TempBuf);
} else {
/*Sorry, didn't fit*/
SpaceOkay = FALSE;
}
}
/*Report whether we decoded everything correctly*/
return SpaceOkay;
} /*Decode*/
/************************************************************************/
/* */
/* DisplayObjectBrief -- Show brief details of object */
/* */
/* Prints out the object's name and lists its edit specifiers. */
/* */
/************************************************************************/
module_scope void
Tracery_DisplayObjectBrief(Tracery_Entry *pEntry)
{
Tracery_EditEntry *pEdits;
UINT Col;
/*Display name*/
printf("%*s", -TRACERY_DISPLAY_COLUMN, pEntry->pName);
if (strlen(pEntry->pName) >= (TRACERY_DISPLAY_COLUMN - 1)) {
/*Long name, wrap description on to next line*/
printf("\n%*s", TRACERY_DISPLAY_COLUMN, "");
}
Col = TRACERY_DISPLAY_COLUMN;
/*Display list of edit specs associated with object*/
for (pEdits = pEntry->pEditList; pEdits->pName != NULL; pEdits++) {
/*Are there items already on the line?*/
if (Col > TRACERY_DISPLAY_COLUMN) {
/*Yes, add a list separator*/
printf(", ");
Col += 2;
}
/*Would this item overrun the right margin?*/
if (((Col + strlen(pEdits->pName)) >
TRACERY_DISPLAY_RIGHT_MARGIN) &&
(Col > TRACERY_DISPLAY_COLUMN)) {
/*Yes, break line now*/
printf("\n%*s", -TRACERY_DISPLAY_COLUMN, "");
Col = TRACERY_DISPLAY_COLUMN;
}
/*Add item to the display*/
printf("%s", pEdits->pName);
Col += strlen(pEdits->pName);
}
printf("\n");
} /*DisplayObjectBrief*/
/************************************************************************/
/* */
/* DisplayObjectFull -- Show full details of object */
/* */
/* Prints out a lot of information about the object's options */
/* and its current state. */
/* */
/************************************************************************/
module_scope void
Tracery_DisplayObjectFull(Tracery_Entry *pEntry)
{
Tracery_EditEntry *pEdits;
CHAR DecodeBuf[4096];
/*Display name*/
printf("%s: %s\n", pEntry->pName, pEntry->pDescription);
printf("%-8s (0x%08lx: \"", "", pEntry->pClientInfo->Flags);
Tracery_Decode(pEntry->pClientInfo, pEntry->pClientInfo->Flags,
DecodeBuf, sizeof(DecodeBuf));
printf("%s\")\n", DecodeBuf);
/*Display list of edit specs associated with object*/
for (pEdits = pEntry->pEditList; pEdits->pName != NULL; pEdits++) {
/*Display each entry, one per line*/
printf("%-8s %-8s %s\n", "",
pEdits->pName,
pEdits->pDescription);
}
printf("\n");
} /*DisplayObjectFull*/
/************************************************************************/
/* */
/* DisplayRegistrations -- Display details of registrations */
/* */
/* Prints out the name of each component registered with */
/* Tracery, along with a summary of the edit specifiers */
/* supported by that object. */
/* */
/* The pFocus parameter provides some control over the level */
/* of detail reported. If pFocus is NULL, a brief listing */
/* of all modules and flags is provided. If you want more */
/* details about one or more modules, specify a name or a */
/* wildcard as the pFocus string. */
/* */
/************************************************************************/
public_scope void
Tracery_DisplayRegistrations(CHAR *pFocus)
{
Tracery_Entry *pSearch;
/*Start search at head of list*/
pSearch = &gTracery.Head;
/*Loop through each registered module*/
for (pSearch = gTracery.Head.pNext;
pSearch != NULL;
pSearch = pSearch->pNext) {
/*Do we want brief details for all modules?*/
if (pFocus == NULL) {
/*Yes, display this object*/
Tracery_DisplayObjectBrief(pSearch);
} else {
/*No, does the caller want to report this object?*/
if (fnmatch(pFocus,
pSearch->pName,
FNM_PERIOD) != 0) {
/*No, move to the next name, if any*/
/*?? We ignore errors for now*/
continue;
}
/*Report full details of object*/
Tracery_DisplayObjectFull(pSearch);
}
}
} /*DisplayRegistrations*/