Using the Counter Tool Prototype

A program which raises a signal during normal usage can be tedious to debug, if the signal happens after a long period of normal execution.

For example, if the signal occurs during the execution of a routine which is called often, a breakpoint on that routine can be hit hundreds or thousands of times before the call which leads to the signal occurs.

There are several ways to fix this. One is to use a Ladebug variable as a counter:

    (ladebug) where
    >0  0x3ff814855e0 in do_SEGVv ...
    #1   :
         :
    (ladebug) set $count = 0
    (ladebug) when quiet in do_SEGV { set $count = $count + 1 }
    [#4: when quiet ...
    (ladebug) run
    Thread received signal SEGV
    stopped at ....
    (ladebug) p $count
    21
    (ladebug) stop in do_SEGV if $count >= 21
    [2] : stop in void do_SEGV ...
    (ladebug) set $count = 0;
    (ladebug) r
    [#2: stopped at [void do_SEGV...
       :

Here do_SEGV is the leaf routine (the ultimate callee) in which the the SEGV happened, or the user routine up the stack just before a call into system libraries.

At the end of this sequence, you are in the routine which caused the signal, and in the actual instance of the call which is about to cause the signal.

Another way is to instrument your application so that it keeps track of call numbers on its own. This is particularly attractive if the program runs for a long time, as the approach above requires at least three runs: one not under debugger control, which make you realize that a signal was being raised; another to count the calls; and a third to get you ready to debug. The instrumentation will determine the necessary count on the first run, and you can debug on the second, instead of the third.

The "counter" tool is an ATOM-based tool which will instrument your program. The inserted routines will count each call of each routine, and can catch signals not handled by your application. When it catches a signal, it will report the counts for recently-called routines:

    == Here's what you had recently executed when
    == your application got signal 11 ('Segmentation fault')

       File c_corefile.c, routine do_SEGV, call number 21
       File c_corefile.c, routine other, call number 2
       File c_corefile.c, routine middle, call number 1

    == A set of breakpoints to get you close to the
    == event which caused the signal would be      
    ==                                             
    ==    set $count = 0                           
    ==    when quiet in do_SEGV { set $count = $count + 1 }
    ==    stop in do_SEGV if $count >= 21                 
    ==                                                

The version here catches SIGSEGV, SIGCHLD and SIGSYS; it should be obvious how to modify the code to catch a different set.

The instrumented version of your application can be used in daily use or in testing. The instrumentation adds about 50% to the run-time of the application and 10 percent to the size of the image, which for many users may not matter.

Please note that "counter" is a prototype. If your application does something which makes normal "C" code unrunable, prevents I/O or destroys the signal-catching mechanism, then this tool will not work correctly.

This prototype is based on the "Ring Tool" prototype, but has not been as thoroughly tested.

How to use the Counter Tool Prototype

Using the prototype requires the following steps:

  1. Cut the source code for the four files from the end of this page, and paste it into files with the names specified in a new directory.
  2. In the directory where the prototype files reside, run the following atom command
    $ atom -gp -A1 your-executable counter.inst.c \
     counter.anal.c -o your-executable.counter
    The processing will take a certain amount of time. For example, to instrument Ladebug itself takes about thirty seconds on an Alpha server.
  3. The instrumentation process writes a file named name_table.dat which your instrumented application will need to function correctly. The source code below assumes that file is in the current directory, but can easily be modified so that the file could be somewhere else: just change the macro NAME_TABLE_NAME in counter.inst.c. This file contains the file and procedure names for the instrumented procedures in your executable.
  4. Run or debug your application as normal, using the new executable, ensuring that the file name_table.dat is available.
  5. When your application signals, look at the output of the counter code for a recommended breakpoint to set before you run the application again under debugger control.


Restrictions

There are several minor restrictions using the Counter Tool Prototype:

Source Code


counter.inst.c

/* Instrument for traceback on signals.
 */

#include <stdio.h>
#include <string.h>
#include <cmplrs/atom.inst.h>  
#include <regex.h>
#include <errno.h>

/* Define the name tables. The table holds pointers to strings,
 * which is one "*"; it's an array, which is two, and this is a
 * pointer to the table, for three "*" in a row.
 */
typedef struct name_table_struct {
    int           size;
    const char ***file_names;
    const char ***proc_names;
} name_table_t;

name_table_t name_table;

#define MAX_NAME_SIZE 300
#define NAME_TABLE_NAME "./name_table.dat"

/* Import the instrumentation patterns specified by the user.  If none
 * were specified, use the empty set defined in the template file. 
 */
#ifdef DEB_COUNTER_PATTERNS_FILE
#include "counter.inst.patterns.h"
#else 
#include "counter.inst.patterns.template.h"
#endif

#define  DEB_COUNTER_REG_ERROR_MSG_LEN 512

static const int    deb_counter_true  = 1;
static const int    deb_counter_false = 0;

static const char * deb_counter_last_file_name_skipped = NULL;
static       int    deb_counter_program_is_threaded    = 0;

/* Set this to 1 to see debugging output
 */
static const int    deb_counter_debug_flag             = 0;


/* This procedure compares a string against an expression pattern.
 * It return deb_counter_true (1) if the string matches the pattern
 * and deb_counter_false (0) if it does not.
 */
static int deb_counter_expression_match( 
    const char *string, 
    const char *pattern)
{
    int      error;
    regex_t  preg;
    char     msg[DEB_COUNTER_REG_ERROR_MSG_LEN];

    error = regcomp( &preg, 
                     pattern, 
		     REG_EXTENDED | REG_NOSUB );

    if (error) {
        regerror(error, &preg, msg, DEB_COUNTER_REG_ERROR_MSG_LEN);
        printf("%s\n", msg);
        return deb_counter_false;
    }

    if( 0 == regexec( &preg, string, 1, 0, 0 ) ) return deb_counter_true;

    return deb_counter_false;
}


/* This procedure compares the name of a procedure with the known
 * set of restrictor patterns in order to determine if instrumentation
 * of said procedure should be skipped.  It returns deb_counter_true (1)
 * if the procedure should be skipped and deb_counter_false (0) if it
 * should be instrumented.
 */
static int deb_counter_skip_procedure( const char *name )
{
    int index = 0;

    if( name == NULL ) return deb_counter_true;

    /* Compare the procedure name against the specified expressions in
     * the procedure patterns array.
     */
    while( deb_counter_proc_patterns[index][0] != ';' ) {

        if( deb_counter_expression_match( name, deb_counter_proc_patterns[index] )) 
            return deb_counter_true;

        index = index + 1;
    }

    return deb_counter_false;
}


/* This procedure compares the name of a file with a known set of
 * restrictor patterns in order to determine if instrumentation of 
 * procedures within said file procedure should be skipped.  It returns 
 * deb_counter_true (1) if the file's procedures should be skipped and 
 * deb_counter_false (0) if they should be instrumented.
 */
static int deb_counter_skip_source_file( const char *name )
{
    int index = 0;

    /* Skip non-user routines, such as those in files not compiled with "-g",
     * like our own calls to demangle names.  We don't have to worry about
     * our use of "printf", though, as printf is in another library ("Object"
     * in ATOM terms).
     */
    if( !name ) return deb_counter_true;     /* No file name means no "-g" */

    /* Because we call this more than once on the same file, we
     * cache the file name pointer for faster compares.
     */
    if( deb_counter_last_file_name_skipped == name ) return deb_counter_true;

    while( deb_counter_file_patterns[index][0] != ';' ) {

        if( deb_counter_expression_match( name, deb_counter_file_patterns[index] )) {
            deb_counter_last_file_name_skipped = name;
            return deb_counter_true;
	}

        index = index + 1;
    }

    return deb_counter_false;
}


int write_table_line( FILE *f, int index, 
                      const char *file_name, 
		      const char *proc_name )
{
    static int am_ok = 1;
    int result;
   
    if (!am_ok) return 0;

    if (!f) {
        am_ok = 0;
        printf( "== No file to write to\n" );
	return 0;
    }

    if (file_name == NULL || proc_name == NULL) {
        am_ok = 0;
        printf( "== Names to write are null\n" );
	return 0;
    }

    if (strlen( file_name ) > MAX_NAME_SIZE ||
        strlen( proc_name ) > MAX_NAME_SIZE ) {
        am_ok = 0;
        printf( "== names are too long\n" );
	return 0;
    }

    result = fprintf( f, "%d %s %s\n", index, file_name, proc_name );

    return (result > 0);    
}

void note_names( int proc_num, const char* filenm, const char* procname )
{
    static FILE *f;
    static int   am_ok = 1;

    if (!am_ok) return; /* Silent after first error */

    if (!f) { 
        f = fopen( NAME_TABLE_NAME, "w" );
        if (!f) {
	    am_ok = 0;
	    printf( "== Unable to open temporary data file, processing\n" );
	    printf( "   will be incomplete. error was: %d\n", errno );
	}
    }

    if (!write_table_line( f, proc_num, filenm, procname )) {
        am_ok = 0;
    }
}


unsigned InstrumentAll(int argc, char **argv) 
{
    Obj   *o; 
    Proc  *p; 
    Block *b; 
    Inst  *i;
    int    proc_num;

    /* Offer threaded programs a -toolargs=-threads option, to profile each
     * thread independently.
     */
    if (argc == 2 && strcmp(argv[1],"-threads") == 0) {
        deb_counter_program_is_threaded = deb_counter_true;
    }
 
    /* Prototypes
     */
    AddCallProto( "ProcTrace( int )" );
    AddCallProto( "MainInit(  int, int, const char* )" );

    /* Traverse the "Objects" and instrument the application.
     */
    proc_num = 0;
    for (o = GetFirstObj(); o != NULL; o = GetNextObj(o)) {  
        const char* objName;
 
        if (BuildObj(o)) return 1;  /* Error */

        objName = GetObjName(o);

        if( !deb_counter_program_is_threaded ) {

            if( objName &&
                deb_counter_expression_match( objName, "libpthread.so$") ) {
	        printf( 
                    "%s %s\n",
                    "Warning:",
                    "application appears to be multi-threaded, but the");
                printf( 
                      "%s %s\n", 
                      "        ",
                      "thread safe option was not specified.");
	    }
        }

        if( GetObjInfo(o, ObjModifyHint) == OBJ_READONLY) {
            if( deb_counter_debug_flag ) {
 	        printf( "Skipped instrumentation processing for object: %s\n", 
                         ( !objName  ? "<unknown>" : objName ) );
	    }
            ReleaseObj(o);
            continue;

        } else {
            if( deb_counter_debug_flag ) {
 	        printf( "Instrumenting object file: %s\n", 
                         ( !objName  ? "<unknown>" : objName ) );
	    }
        }

        deb_counter_last_file_name_skipped = NULL;

        for (p = GetFirstObjProc(o); p != NULL; p = GetNextProc(p)) { 
            const char *name   = ProcName(p);
            const char *filenm = ProcFileName(p);
 
            if( name == NULL ) continue;
 
            if( deb_counter_skip_source_file( filenm ) ||
                deb_counter_skip_procedure  ( name )      ) {

	       if( deb_counter_debug_flag ) {
	           printf( "Skipped proc: %s from file: %s\n", 
                            name, 
                            ( !filenm  ? "<unknown>" : filenm ) );
	        }
	      
                continue;
	    }

	    /* Count this procedure and build up the name vector.
	     */
	    proc_num++;        /* Index starts at 1! */
	    note_names( proc_num, filenm, name );

	    if( deb_counter_debug_flag ) {
                printf( "*Instrument proc %d: %s from file: %s\n",
			proc_num, name, filenm );
	    }

	    /* For user routines, count the calls.
	     */
            AddCallProc(p, ProcBefore, "ProcTrace", proc_num );
        } 

        WriteObj( o );

        deb_counter_last_file_name_skipped = NULL;
    }
  
    /* Add a call to initialize now that we know how
     * many procedures there are.
     */
    AddCallProgram( ProgramBefore, "MainInit", 
                    deb_counter_program_is_threaded,
		    proc_num,
		    NAME_TABLE_NAME );

    return(0);
}



counter.anal.c

/*  Count routines and dump if asked.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmplrs/atom.anal.h>
#include <demangle.h>
#include <errno.h>
#include <signal.h>

/* Names and counts.
 */
static int  proc_count = 0;
static int *counts     = NULL;
const char **file_table_ptr = NULL;
const char **proc_table_ptr = NULL;

/* "Last-few" routines executed.
 */
#define LAST_FEW 100
static int last_index = 0;
static int last_few[LAST_FEW];

static char *no_file = "<unknown file>";
static char *no_proc = "<unknown proc>";

/* True and False values for prototype code
 */
static const int deb_counter_true     = 1;
static const int deb_counter_false    = 0;

/* Set this to 1 to turn on debug printing.
 */
#define DEBUGGING 0

/* Hold buffer for processed filenames
 */
#define WORKBUFLEN 128
static char                   file_name_work_buf[WORKBUFLEN];

/* NULL FILE pointer used in calls to fflush() to force writing
   of output streams
*/
FILE * null_file_ptr = NULL;

#define MAX_NAME_SIZE 300

int read_table_line( FILE *f, int *index_ptr, 
                     const char **file_name_ptr, 
		     const char **proc_name_ptr )
{
    char  buf1[MAX_NAME_SIZE];
    char  buf2[MAX_NAME_SIZE];
    char *n1;
    char *n2;
    int   i;
    int   result;
    int   len;
    static int am_ok = 1;

    if (!am_ok) return;
    
    if (!f) {
        am_ok = 0;
        printf( "== No file to read from\n" );
	return 0;
    }

    if (index_ptr == NULL || file_name_ptr == NULL || proc_name_ptr == NULL) {
	am_ok = 0;
        printf( "== Pointers to write to are null\n" );
	return 0;
    }

    result = fscanf( f, "%d %s %s", index_ptr, buf1, buf2 );

    len = strlen( (char*)&buf1[0]);
    n1 = (char *) malloc( len + 1);
    for (i = 0; i < len; i++) {
        n1[i] = buf1[i];
    }
    n1[len] = '\0';

    len = strlen( (char*)&buf2[0]);
    n2 = (char *) malloc( len + 1 );
    for (i = 0; i < len; i++) {
        n2[i] = buf2[i];
    }
    n2[len] = '\0';

    *file_name_ptr = n1;
    *proc_name_ptr = n2;

    return 1;
}

int read_name_table( int proc_num, const char *name_table_name )
{
    int table_index;
    int i;
    FILE *f = fopen( name_table_name, "r" );

    if (!f) {
	printf( "== Could not open name-table file, errno is %d\n",
		errno );
	return 0;
    }

    file_table_ptr = (const char **) malloc (proc_num * sizeof(const char *));
    proc_table_ptr = (const char **) malloc (proc_num * sizeof(const char *));

    if (DEBUGGING) {
        printf( "-- About to read %d name table records\n", proc_num );
    }

    for (i = 1; i <= proc_num; i++ ){
        file_table_ptr[i] = NULL;
	proc_table_ptr[i] = NULL;
        table_index       = 0;
        read_table_line( f, &table_index,
	 		 &file_table_ptr[i],
			 &proc_table_ptr[i] );

	if (DEBUGGING) {
	    printf( "-- Record %d, \"%s\", \"%s\"\n",
                    table_index, file_table_ptr[i], proc_table_ptr[i] );
	}

        if (i != table_index) {
	    printf( "== Error in reading from table\n" );
	    return;
	}
    }

    if (DEBUGGING)
        printf( "Read %d table entries\n", proc_num );
}


int already_seen( int which )
{
    static int already_been_seen[LAST_FEW];
    static int next_seen = 0;
    int i;

    for( i = 0; i < next_seen; i++ ) {
        if (already_been_seen[i] == which)
	    return 1;  /* already seen */
    }

    /* Haven't seen this one before
     */
    already_been_seen[next_seen++] = which;
    return 0;
}

/* The heart of it all; tell the user the execution counts
 * for recently-executed routines.
 */
void where_was_i( int sig )
{
    int i;
    int which;
    int max_value;
    int max_index;

    printf( "\n\t== Here's what you had recently executed when" );
    printf( "\n\t== your application got signal %d ('", sig );
    printf( "%s')\n\n", __sys_siglist[sig] );
    fflush(stdout);

    max_value = 0;
    max_index = 0;
    for (i = last_index - 1; i >= 0; i-- ) {
        which = last_few[i];

        if (which != 0                 /* Actual record exists     */
        && counts[which] > 0	       /* for which we have counts */
        && !already_seen( which )) {   /* and it's not the N-1th call */
            printf( "\t   File %s, routine %s, call number %d\n",
                    file_table_ptr[which], 
		    proc_table_ptr[which],
		    counts[which] );

            if (counts[which] > max_value) {
	        max_value = counts[which];
		max_index = which;
	    }
	}
    }

    for (i = LAST_FEW - 1; i > last_index; i-- ) {
        which = last_few[i];
        if (which != 0 && counts[which] > 0 && !already_seen( which )) {
            printf( "\t   File %s, rtn %s, call number %d\n",
                    file_table_ptr[which], 
		    proc_table_ptr[which],
		    counts[which] );

            if (counts[which] > max_value) {
	        max_value = counts[which];
		max_index = which;
	    }
	}
    }

    if (max_value == 1) {
        printf( "\n\t== A breakpoint to get you close to the event " );
	printf( "\n\t==  which caused the signal would be          " );
	printf( "\n\t==                                             " );
        printf( "\n\t==    stop in %s",  proc_table_ptr[max_index] );

    } else if (max_value > 1) {
        printf( "\n\t== A set of breakpoints to get you close to the" );
	printf( "\n\t== event which caused the signal would be      " );
	printf( "\n\t==                                             " );
	printf( "\n\t==    set $count = 0                           " );
        printf( "\n\t==    when quiet in %s { set $count = $count + 1 }",
	        proc_table_ptr[max_index] );
	printf( "\n\t==    stop in %s if $count >= %d                 ",
		proc_table_ptr[max_index], max_value );
    }

    printf ("\n\n");
    fflush(stdout);
}


/* Catch, inform and resignal.
 */
struct sigaction old_sigaction;
void sig_handler(int sig, siginfo_t *sip, void *extra)
{
    int ret;

    where_was_i( sip->si_signo );

    /* Restore the old signal action
     */
    ret = sigaction( sig, &old_sigaction, 0);
    if (ret) {
        perror("sigaction");
        exit(1);
    }

    /* Re-signal
     */
    kill( getpid(), sig );
    fflush(stdout);
}


/* Prepare to catch and resignal SIGSEGV, SIGCHLD and SIGSYS, could add others.
 */
void MainInit( int program_is_threaded,
	       int proc_num,
	       const char *name_table_name )
{
    int  i;
    struct sigaction sa;
    int ret;

    /* Initialize counts.
     */
    proc_count = proc_num;
    counts = (int *) malloc ( (proc_num + 1) * sizeof( int * ));
    for (i = 0; i <= proc_num; i++) {
        counts[i] = 0;
    }

    for (i = 0; i < LAST_FEW; i++) {
        last_few[i] = 0;
    }

    /* Initialize tables.
     */
    read_name_table( proc_num, name_table_name );

    /* Catch and resignal SIGSEGV
     */
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = sig_handler;
    sa.sa_flags     = SA_CLDNOTIFY|SA_SIGINFO;
    ret = sigaction( SIGSEGV, &sa, &old_sigaction );
    if (ret) {
        perror("sigaction");
        exit(1);
    }

    /* Catch and resignal SIGCHLD
     */
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = sig_handler;
    sa.sa_flags     = SA_CLDNOTIFY|SA_SIGINFO;
    ret = sigaction( SIGCHLD, &sa, &old_sigaction );
    if (ret) {
        perror("sigaction");
        exit(1);
    }

    /* Catch and resignal SIGSYS
     */
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = sig_handler;
    sa.sa_flags     = SA_CLDNOTIFY|SA_SIGINFO;
    ret = sigaction( SIGSYS, &sa, &old_sigaction );
    if (ret) {
        perror("sigaction");
        exit(1);
    }
}

/* Count a procedure's invocations in a low-overhead way.
 */
void ProcTrace( int my_index )
{
    /* Count
     */
    counts[ my_index ]++;

    /* Remember this was called
     */
    if (last_index >= LAST_FEW) last_index = 0;
    last_few[last_index++] = my_index;
}

/* Now that the user cares, demangle a name.
 *
 * WARNING: this returns a pointer to a static buffer; do NOT
 *          save or write to the result.
 */
#define NAMELEN   200
#define NAMELEN_WITH_GUARD (NAMELEN+1)

static char *deb_counter_demangle_name( char *mangled_name )
{
    static char demangled_name[ NAMELEN_WITH_GUARD ];
    int  status;
    
    if( mangled_name == NULL )
        return no_file;

#ifdef DEB_COUNTER_NO_DEMANGLE

    /* Call to MLD_demangle_string() has proven unsafe to use with
     * threads programs (segv).
     */
    return mangled_name;

#else
    demangled_name[NAMELEN] = (char)0; /* guard */

    status = MLD_demangle_string( mangled_name,
                           demangled_name, NAMELEN,  
                           MLD_SHOW_DEMANGLED_NAME );

    return &demangled_name[0];

#endif
}


count.inst.patterns.h

/* The following text describes which procedures are not instrumented in
 * the Ladebug debugger and why they were excluded.
 *
 * Most memory management related procedures (malloc, free, realloc), 
 * including Ladebug's specialized memory management routines 
 * (ladebugMalloc*, ladebugRealloc* and ladebugFree*) are not instrumented 
 * as they have a dramatic impact on application performance.
 * 
 * The procedure DebugCtl::operator void*(void) (__opPv__8DebugCtlXv) is
 * not instrumented because this routine gets called when Ladebug "calls"
 * the dump routines from the command line, before they can set the "quiet" 
 * flag.  If we didn't do this, the history buffer would start filling up
 * with entries for call/line 12/ret of this routine.
 *
 * Any procedures related to this tool (deb_counter*) are not instrumented
 * as they are not interesting to the user.
 *
 * Any procedures for which the corresponding source file is not known are
 * excluded from instrumentation as they offer little useful information
 * for the user.
 *
 * Any procedures from software outside the Ladebug source pool, such as
 * Rogue Wave, C++ name demangling, libdpi and Standard C++ routines.
 *
 * Any compiler generated procedures deemed generally uninteresting, such
 * as interludes, initialization routines, etc.  Compiler generated "throw"
 * routines are instrumented as they are often interesting.
 *
 * Any Ladebug "give()" procedures are not instrumented as they are trivial
 * in nature and thus uninteresting.
 */


#define  DEB_COUNTER_MAX_PATTERN_LEN   100


/* Add source file name/directory path expressions to this array if you
 * wish to have procedures from these areas skipped during instrumentation. 
 * The delimiter pattern (";") must be kept as the final entry.
 *
 * Note that the file/path names come from the application's Symbol Table 
 * info (man stdump), so the manner in which the application is compiled 
 * (relative vs. absolute path names) can have an effect on directory name 
 * matching.
 */
static char deb_counter_file_patterns[][DEB_COUNTER_MAX_PATTERN_LEN] = { 
               
    "/rogue-alpha-.*/rw/",           /* Ladebug's Rogue Wave files   */
    "/libdpi/",                      /* Ladebug's libdpi usage       */
    "/usr/include/cxx/",             /* C++ iostreams, etc.          */
    "alpha_unwind.c",                /* System context unwind file   */        
    "ots_packed_arith.c",            /* Ladebug's libots usage       */
    "/cvt(ascii|num).c",             /* Ladebug's libcvtas usage     */

    ";"                              /* end of list delimiter        */
};


/* Add procedure name expressions to this array if you wish to have
 * procedures whose name contains the expression skipped during
 * instrumentation.  The delimiter pattern (";") must be kept as the
 * final entry.
 *
 * Note that the procedure names come from the application's Symbol
 * Table info (man stdump), so issues such as C++ name mangling must
 * be addressed in these expressions.
 */
static char deb_counter_proc_patterns[][DEB_COUNTER_MAX_PATTERN_LEN] = { 

    "^__INTER__",                     /* C++ interlude procs          */
    "^__(fini|init)_sti__",           /* Compiler generated procs     */
    "^__t.*ptrthunk__Xv$",            /* C++ compiler generated procs */
    "^free$",                         /* Standard C memory mgmt proc  */
    "^(m|c|re)alloc$",                /* Standard C memory mgmt procs */
    "^ots_",                          /* Ladebug's libots usage       */
    "^longdouble_",                   /* Libcvtas longdouble procs    */
    "^give__.*Xv$",                   /* Ladebug Ptr notation proc    */
    "^__opPv__8DebugCtlXv$",          /* Ladebug DebugCtl operation   */
    "ladebug(Malloc|Free|Realloc)",   /* Ladebug memory mgmt procs    */

    ";"                               /* end of list delimiter        */
};

count.inst.patterns.template.h

/* This file provides a template for creating the counter.inst.patterns.h
 * file used by the Ladebug counter tool.  In the case where a user
 * chooses not to create a counter.inst.patterns.h file, this file is
 * used to provide a default (empty) definition.
 *
 * The counter.inst.patterns.h defines regular expressions which, when
 * encoutered during instrumention of an application, are to be
 * skipped (not instrumented).  

 * There are two sets of expressions that can be specified, those to be
 * matched against filenames and those to be matched against procedure
 * names.
 *
 * To create your own pattern description file for an application,
 * copy this file to "counter.inst.patterns.h" and edit that file
 * to add your own expressions.
 *
 * For an example of the expression definitions used when instrumenting 
 * the Ladebug debugger, see the file counter.inst.patterns.example.h.
 */


#define  DEB_COUNTER_MAX_PATTERN_LEN   100


/* Add source file name/directory path expressions to this array if you
 * wish to have procedures from these areas skipped during instrumentation. 
 * The delimiter pattern (";") must be kept as the final entry.
 *
 * Note that the file/path names come from the application's Symbol Table 
 * info (man stdump), so the manner in which the application is compiled 
 * (relative vs. absolute path names) can have an effect on directory name 
 * matching.
 */
static char deb_counter_file_patterns[][DEB_COUNTER_MAX_PATTERN_LEN] = { 
               
    ";"                              /* end of list delimiter        */
};


/* Add procedure name expressions to this array if you wish to have
 * procedures whose name contains the expression skipped during
 * instrumentation.  The delimiter pattern (";") must be kept as the
 * final entry.
 *
 * Note that the procedure names come from the application's Symbol
 * Table info (man stdump), so issues such as C++ name mangling must
 * be addressed in these expressions.
 */
static char deb_counter_proc_patterns[][DEB_COUNTER_MAX_PATTERN_LEN] = { 

    ";"                                   /* end of list delimiter */
};

Click here to send comments or problems to the Ladebug team.