LibGist: Getting Started

Instrumenting a program for use with Gist involves: Running an instrumented program involves: This section explains these steps.

Instrumentation

  1. Defining events.

    Each event in your code is described by an integer and an associated name. The call Gist_define_event(n,"description") defines event number n with the specified descriptive string.

    All events must be defined before Gist_init() is called.

    If you define alphanumeric symbols to stand for the event numbers, using for example the enum or #define constructs in C, it is helpful to include this symbol in the beginning of the descriptive string, and follow it with a colon. For example:

    typedef enum { START, STOP } gistevent_t;
    main() {
      Gist_define_event(START, "START:starting program");
      Gist_define_event(STOP, "STOP:at the end");
      ...
    
    This gives Gist access to your concise symbolic name for the event. This practice is not required.

  2. Defining states (optional).

    You may define a state to exist between two events that you specify. For instance, if two of your events are
    Gist_define_event(5, "LOCKREQ:Request lock");
    Gist_define_event(6, "LOCKREC:Receive lock");
    
    then you might be concerned with measuring the time spent waiting for the lock. You would include the definition
    Gist_define_state(5, 6, "Waiting for lock");
    
    (There is no need to specify a concise name for the state; Gist will create the concise name "LOCKREQ-LOCKREC" for this state.)

    When Gist finds event LOCKREQ followed immediately by event LOCKREC in a processor's log, it concludes that between the two events, the processor was in the LOCKREQ-LOCKREC state. If the LOCKREQ event is followed by any other event, Gist does not interpret the LOCKREQ as an entry into the state.

    The two events may be the same, as in

    Gist_define_event(8, "LOOP:Begin loop");
    Gist_define_state(8, 8, "Executing loop");
    
    
    for (i=0; i<1000; i++) {
      Gist_log(8, pid);
      ...
    }
    
    although, in this example, the last execution of the loop will not be interpreted as happening in the LOOP-LOOP state.

    All states must be defined before any logging occurs.

  3. Setting the number of processors.

    Before Gist_init() is called, Gist needs to know the number of processors the program will use. You use the Gist_set_nproc(int nproc) call for this. A simple way to do this on an SGI is to say
    Gist_set_nproc(mp_suggested_numthreads(0))
    or
    Gist_set_nproc(mp_numthreads())
    (see the man pages for these SGI library functions).
  4. Starting and finishing.

    The code should call Gist_init("logfile.glg") at the beginning of execution and Gist_finish() at the end. All calls to Gist_define_*() and Gist_set_*() must occur before Gist_init().

    You can use only one log file per execution. Libgist will infer the name logfile.gix for the index file.

  5. Logging events.

    Events are logged with Gist_log(int event, int pid). The pid argument is a designation which may or may not be an actual processor identifier. Other possibilities are thread ID or even a loop iteration number. However, Gist assumes that pid takes on all values from 0 to some maximum, so some uses (say, UNIX process ID) are not appropriate.

Linking with Libgist

A library libgist.a is provided for each supported architecture. Only static linking is supported. For C or C++ programs on a UNIX system, insert the arguments -L/path/to/gist -lgist in your link step.

Header files and macros (C, C++)

You can turn Gist on and off easily. By turning off Gist and recompiling your program, you can (in principle) obtain the same compiled code as you would had you never used Gist.

This feature relies on the C/C++ macro facility. Instead of using the regular Gist calls in your program, invoke the corresponding macros:

Function             Macro
--------             -----
Gist_init            Gist_INIT      
Gist_define_event    Gist_DEFINE_EVENT
Gist_define_state    Gist_DEFINE_STATE
Gist_log             Gist_LOG
Gist_finish          Gist_FINISH
The macros are defined in Gist.h, along with the prototypes for the Gist calls. Including Gist.h does nothing unless the macro Gist_YES is defined. Your program will contain the lines
#define Gist_YES
#include <Gist.h>
If the order of the lines is reversed, then the compiler will produce a Gist-free executable:
#include <Gist.h>
#define Gist_YES

Choosing performance measures

The particular performance measures Gist records during a run are not fixed at compile time. They are determined by an environment variable (in UNIX) or some equivalent mechanism which has yet to be determined (in NT). The performance measures have short alphanumeric names. A list is specified in an environment variable with colon delimiters. For example:
  GIST_MEASURES=sgi1:sgi21:ru_majflt
on an SGI instructs Gist to count instructions, floating-point instructions, and major page faults.

The collection of performance measures is completely different for each architecture. Also, each architecture imposes some constraints upon which measures can be logged in the same run. LibGist checks these constraints at Gist_init(), and prints a warning message for an invalid list. The measures and constraints are listed here, and are also available by the Gist measures command.


Tim Culver
Last modified: Sat May 1 22:55:12 EDT 1999