monitor Subroutine

Purpose

Starts and stops execution profiling using data areas defined in the function parameters.

Library

Standard C Library (libc.a)

Syntax

#include <mon.h>int monitor (LowProgramCounter,HighProgramCounter,Buffer,BufferSize,NFunction)OR int monitor (NotZeroA,DoNotCareA, Buffer,-1, NFunction)OR int monitor((caddr_t)0)caddr_t LowProgramCounter, HighProgramCounter;HISTCOUNTER *Buffer;int BufferSize, NFunction;caddr_t NotZeroA, DoNotCareA;

Description

The monitor subroutine initializes the buffer area and starts profiling, or else stops profiling and writes out the accumulated profiling data. Profiling, when started, causes periodic sampling and recording of the program location within the program address ranges specified. Profiling also accumulates function call count data compiled with the -p or -pg option.

Executable programs created with the cc -p or cc -pg command automatically include calls to the monitor subroutine (through the monstartup and exit subroutines) to profile the complete user program, including system libraries. In this case, you do not need to call the monitor subroutine.

The monitor subroutine is called by the monstartup subroutine to begin profiling and by the exit subroutine to end profiling. The monitor subroutine requires a global data variable to define which kind of profiling, -p or -pg, is in effect. The monitor subroutine initializes four global variables that are used as parameters to the profil subroutine by the moncontrol subroutine:

The monitor subroutine examines the global data and parameter data in this order:

  1. When the _mondata.prof_type global variable is neither -1 (-p profiling defined) nor +1 (-pg profiling defined), an error is returned, and the function is considered complete.

    The global variable is set to -1 in the mcrt0.o file and to +1 in the gcrt0.o file, and defaults to 0 when the crt0.o file is used.

  2. When the first parameter to the monitor subroutine is 0, profiling is stopped and the data file is written out.

    If -p profiling was in effect, then the file is named mon.out. If -pg profiling was in effect, the file is named gmon.out. The function is complete.

  3. When the first parameter to the monitor subroutine is not , the monitor parameters and the profiling global variable, _mondata.prof_type, are examined to determine how to start profiling.
  4. When the BufferSize parameter is not -1, a single program address range is defined for profiling, and the first monitor definition in the syntax is used to define the single program range.
  5. When the BufferSize parameter is -1, multiple program address ranges are defined for profiling, and the second monitor definition in the syntax is used to define the multiple ranges. In this case, the ProfileBuffer value is the address of an array of prof structures. The size of the prof array is denoted by a zero value for theHighProgramCounter (p_high) field of the last element of the array. Each element in the array, except the last, defines a single programming address range to be profiled. Programming ranges must be in ascending order of the program addresses with ascending order of the prof array index. Program ranges may not overlap.

    The buffer space defined by the p_buff and p_bufsize fields of all of the prof entries must define a single contiguous buffer area. Space for the function-count data is included in the first range buffer. Its size is defined by the NFunction parameter. The p_scale entry in the prof structure is ignored. The prof structure is defined in themon.h file. It contains the following fields:

    caddr_t p_low;        /* low sampling address */
    caddr_t p_high;       /* high sampling address */
    HISTCOUNTER *p_buff;  /* address of sampling buffer */
    int p_bufsize;        /* buffer size- monitor/HISTCOUNTERs,\
                            profil/bytes */
    uint p_scale;         /* scale factor */

Parameters

Item Description
LowProgramCounter (prof name: p_low) Defines the lowest execution-time program address in the range to be profiled. The value of the LowProgramCounter parameter cannot be 0 when using themonitor subroutine to begin profiling.
HighProgramCounter (prof name: p_high) Defines the next address after the highest-execution time program address in the range to be profiled.

The program address parameters may be defined by function names or address expressions. If defined by a function name, then a function name expression must be used to dereference the function pointer to get the address of the first instruction in the function. This is required because the function reference in this context produces the address of the function descriptor. The first field of the descriptor is the address of the function code. See the examples for typical expressions to use.

Buffer (prof name: p_buff) Defines the beginning address of an array of BufferSize HISTCOUNTERs to be used for data collection. This buffer includes the space for the program address-sampling counters and the function-count data areas. In the case of a multiple range specification, the space for the function-count data area is included at the beginning of the first range in the BufferSize specification.
BufferSize (prof name: p_bufsize) Defines the size of the buffer in number of HISTCOUNTERs. Each counter is of type HISTCOUNTER (defined as short in the mon.h file). When the buffer includes space for the function-count data area (single range specification and first range of a multi-range specification) the NFunction parameter defines the space to be used for the function count data, and the remainder is used for program-address sampling counters for the range defined. The scale for the profil call is calculated from the number of counters available for program address-sample counting and the address range defined by the LowProgramCounter and HighProgramCounter parameters. See themon.h file.
NFunction Defines the size of the space to be used for the function-count data area. The space is included as part of the first (or only) range buffer.

When -p profiling is defined, the NFunction parameter defines the maximum number of functions to be counted. The space required for each function is defined to be:

sizeof(struct poutcnt)

The poutcnt structure is defined in the mon.h file. The total function-count space required is:

NFunction * sizeof(struct poutcnt)

When -pg profiling is defined, the NFunction parameter defines the size of the space (in bytes) available for the function-count data structures, as follows:

range = HighProgramCounter - LowProgramCounter; tonum = TO_NUM_ELEMENTS( range ); if ( tonum < MINARCS ) tonum = MINARCS; if ( tonum > TO_MAX-1 ) tonum = TO_MAX-1; tosize = tonum * sizeof( struct tostruct ); fromsize = FROM_STG_SIZE( range ); rangesize = tosize + fromsize + sizeof(struct gfctl);

This is computed and summed for all defined ranges. In this expression, the functions and variables in capital letters as well as the structures are defined in the mon.h file.

NotZeroA Specifies a value of parameter 1, which is any value except 0. Ignored when it is not zero.
DoNotCareA Specifies a value of parameter 2, of any value, which is ignored.

Return Values

The monitor subroutine returns 0 upon successful completion.

Error Codes

If an error is found, the monitor subroutine sends an error message to stderr and returns -1.

Examples

  1. This example shows how to profile the main load module of a program with -p profiling:
    #include <sys/types.h>
    #include <mon.h>
    main()
    {
    extern caddr_t etext;  /*system end of main module text symbol*/
    extern int start();    /*first function in main program*/
    extern struct monglobal _mondata; /*profiling global variables*/
    struct desc {         /*function descriptor fields*/
        caddr_t begin;    /*initial code address*/
        caddr_t toc;      /*table of contents address*/
        caddr_t env;      /*environment pointer*/
    } ;                  /*function descriptor structure*/
    struct desc *fd;       /*pointer to function descriptor*/
    int rc;               /*monitor return code*/
    int range;             /*program address range for profiling*/
    int numfunc;           /*number of functions*/
    HISTCOUNTER *buffer;    /*buffer address*/
    int numtics;        /*number of program address sample counters*/
    int BufferSize;  /*total buffer size in numbers of HISTCOUNTERs*/
    fd = (struct desc*)start; /*init descriptor pointer to start\
     function*/
    numfunc = 300;           /*arbitrary number for example*/
    range = etext - fd->begin; /*compute program address range*/
    numtics =NUM_HIST_COUNTERS(range); /*one counter for each 4 byte\
     inst*/
    BufferSize = numtics + ( numfunc*sizeof (struct poutcnt) \
     HIST_COUNTER_SIZE );     /*allocate buffer space*/
    buffer = (HISTCOUNTER *) malloc (BufferSize * HIST_COUNTER_SIZE);
    if ( buffer == NULL )  /*didn't get space, do error recovery\
     here*/
       return(-1);
    _mondata.prof_type = _PROF_TYPE_IS_P; /*define -p profiling*/
    rc = monitor( fd->begin, (caddr_t)etext, buffer, BufferSize, \
     numfunc);
    /*start*/
    if ( rc != 0 ) /*profiling did not start, do error recovery\
     here*/
       return(-1);
    /*other code for analysis*/
    rc = monitor( (caddr_t)0);  /*stop profiling and write data file\
     mon.out*/
    if ( rc != 0 ) /*did not stop correctly, do error recovery here*/
       return (-1);
    }
  2. This example profiles the main program and the libc.a shared library with -p profiling. The range of addresses for the shared libc.a is assumed to be:
    low = d0300000
    high = d0312244 

    These two values can be determined from the loadquery subroutine at execution time, or by using a debugger to view the loaded programs' execution addresses and the loader map.

    #include <sys/types.h>
    #include <mon.h>
    main()
    {
    extern caddr_t etext;   /*system end of text symbol*/
    extern int start();    /*first function in main program*/
    extern struct monglobal _mondata; /*profiling global variables*/
    struct prof pb[3];     /*prof array of 3 to define 2 ranges*/
    int rc;               /*monitor return code*/
    int range;            /*program address range for profiling*/
    int numfunc;          /*number of functions to count (max)*/
    int numtics;          /*number of sample counters*/
    int num4fcnt;   /*number of HISTCOUNTERs used for fun cnt space*/
    int BufferSize1;      /*first range BufferSize*/
    int BufferSize2;      /*second range BufferSize*/
    caddr_t liblo=0xd0300000;  /*lib low address (example only)*/
    caddr_t libhi=0xd0312244;  /*lib high address (example only)*/
    numfunc = 400;           /*arbitrary number for example*/
    /*compute first range buffer size*/
    range = etext - *(uint *) start;   /*init range*/
    numtics = NUM_HIST_COUNTERS( range );
    /*one counter for each 4 byte inst*/
    num4fcnt = numfunc*sizeof( struct poutcnt )/HIST_COUNTER_SIZE;
    BufferSize1 = numtics + num4fcnt;
    /*compute second range buffer size*/
    range = libhi-liblo;
    BufferSize2 = range / 12; /*counter for every 12 inst bytes for\
     a change*/
    /*allocate buffer space - note: must be single contiguous\
     buffer*/
    pb[0].p_buff = (HISTCOUNTER *)malloc( (BufferSize1 +BufferSize2)\
     *HIST_COUNTER_SIZE);
    if ( pb[0].p_buff == NULL )   /*didn't get space - do error\
     recovery here* ;/
        return(-1);
    /*set up the first range values*/
    pb[0].p_low = *(uint*)start;       /*start of main module*/
    pb[0].p_high = (caddr_t)etext;     /*end of main module*/
    pb[0].p_BufferSize = BufferSize1;  /*prog addr cnt space + \
    func cnt space*/
    /*set up last element marker*/
    pb[2].p_high = (caddr_t)0;
    _mondata.prof_type = _PROF_TYPE_IS_P;   /*define -p\
    profiling*/
    rc = monitor( (caddr_t)1, (caddr_t)1, pb, -1, numfunc); \
     /*start*/
    if ( rc != 0 )   /*profiling did not start - do error recovery\
     here*/
       return (-1);
    /*other code for analysis ...*/
    rc = monitor( (caddr_t)0);   /*stop profiling and write data \
    file mon.out*/
    if ( rc != 0 )   /*did not stop correctly - do error recovery\
     here*/
        return (-1);
  3. This example shows how to profile contiguously loaded functions beginning at zit up to but not including zot with -pg profiling:
    #include <sys/types.h>
    #include <mon.h>
    main()
    {
    extern zit();           /*first function to profile*/
    extern zot();           /*upper bound function*/
    extern struct monglobal _mondata; /*profiling global variables*/
    int rc;                /*monstartup return code*/
    _mondata.prof_type = _PROF_TYPE_IS_PG; /*define -pg profiling*/
    /*Note cast used to obtain function code addresses*/
    rc = monstartup(*(uint *)zit,*(uint *)zot); /*start*/
    if ( rc != 0 ) /*profiling did not start, do error recovery\
     here*/
       return(-1);
    /*other code for analysis ...*/
    exit(0);    /*stop profiling and write data file gmon.out*/
    }

Files

Item Description
mon.out Data file for -p profiling.
gmon.out Data file for -pg profiling.
/usr/include/mon.h Defines the _mondata.prof_type global variable in the monglobal data structure, the prof structure, and the functions referred to in the previous examples.