/*******************************************************************************
**
** Source File Name = dbsnap.c  1.7
**
** Licensed Materials - Property of IBM
**
** (C) COPYRIGHT International Business Machines Corp. 1995, 1999 
** All Rights Reserved.
**
** US Government Users Restricted Rights - Use, duplication or
** disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
**
** PURPOSE : to demonstrate the use of DATABASE MONITOR SNAPSHOT APIs
**
** The following illustrates how to issue a snapshot and process the output.
** It prints database lock snapshots. Printing routines for other
** request types are left to the reader to complete as an exercise...
**
** OTHER APIs USED :
**          INSTANCE ATTACH               sqleatin()
**
** EXTERNAL FUNCTIONS USED IN CODE SAMPLE:
**          check_error :     Checks for SQLCODE error, and prints out any
**          [in util.c]          related information available.
**
** 
**    EXTERNAL DEPENDENCIES :
**       - Ensure existence of database for precompile purposes.
**       - Compile and link with the IBM Cset++ compiler (AIX and OS/2)
**         or the Microsoft Visual C++ compiler (Windows) 
**         or the compiler supported on your platform.
**
** For more information about these samples see the README file.
**
** For more information on programming in C, see the:
**   -  "Programming in C and C++" section of the Application Development Guide
** For more information on Building C Applications, see the:
**   -  "Building C Applications" section of the Application Building Guide.
**
** For more information on the SQL language see the SQL Reference.
**
*******************************************************************************/
 /******************************************************************************
    The Database Monitor Snapshot API

#include "sqlca.h"
#include "sqlutil.h"       Defines the data elements returned by sqlmonss
#include "sqlmon.h"
#include "util.h"

int SQL_API_FN sqlmonss(unsigned long     version,
                        char              *reserved,
                        sqlma             *sqlma_ptr,
                        unsigned long     buffer_length,
                        void              *buffer_ptr,
                        sqlm_collected    *collected,
                        struct sqlca      *sqlca
                       );

The 'sqlma' structure specifies the data to snapshot, which is returned
in a *user allocated* buffer, as a series of contiguous data blocks.
A utility function, 'Estimate buffer size (sqlmonsz)'
may be called to estimate how big this buffer should be. REF REFID=XXXXX.

Data is returned on a snapshot, only if the Database Manager is collecting it. A
set of Database Monitor Switches instruct the Database Manager to collect
Monitoring data.
REF REFIX=XXXXX2.

note: It is not necessary to have a database connection to issue a snapshot
      request.

INPUT:

version: SQLM_DBMON_VERSION1    - only return data that would be available
                                  on a DB2 version 1 system
         SQLM_DBMON_VERSION2    - return Version 2 data

sqlma:

   This structure contains an array of sqlm_obj_struct, where each one
   is a request for Monitored data.

   obj_num:  Number of Database monitor requests.
   obj_var:  Array  of requests (sqlm_obj_struct).

   sqlm_obj_struct:
        Specifies a request for information about certain DB2 activities at a
        particular level, and/or for a specific object.  If the request is for a
        specific object, then this object identifier must be provided in the
        object or agent_id field, depending on the request type.

        obj_type:

            To obtain system-wide information:
       CLP equivalent:

                SQLMA_DB2                Information and cumulative counters at
       get snapshot for dbm              the database manager level.
                SQLMA_APPLINFO_ALL       Application identification information
       list applications                 all applications currently connected to
                                         a database that is managed by the DB2
                                         instance.
                SQLMA_DCS_APPLINFO_ALL   Application identification information
       list dcs applications             for all DCS application currently
                                         connected to a database that is managed
                                         by the DB2 instance.
                SQLMA_APPL_ALL           Application level information and
       get snapshot for all apps.        counters.  Returns the same information
                                         as SQLMA_APPL or SQLMA_AGENT_ID.
                SQLMA_DBASE_ALL          Database level information and i
       get snapshot for all databases    counters.
                                         Returns the same information as
                                         SQLMA_DBASE.

            To obtain information about a particular database:

                SQLMA_DBASE              Database level information and counters
       get snapshot for database         for a database. Information is returned
       on 'dbname'                       only if there is at least one
                                         application connected to the database.
                                         note:
    When a Database monitor switch is off, switch controlled data elements
    inside a data block that is returned anyway (e.g., sqlm_dbase) are zeroed.
                SQLMA_DBASE_TABLES       Table activity information
       get snapshot for tables           at the database level; the application
       on 'dbname'                       level for each application connected to
                                         the database; and the table level for
                                         each table that *was accessed* by an
                                         application connected to the database.
                                         THIS INFORMATION IS RETURNED ONLY IF
                                         THE DATABASE MONITOR TABLE SWITCH IS
                                         ON.
                                         REF REFID=XXXXXY
                SQLMA_DBASE_LOCKS        Lock activity information at the
       get snapshot for locks on dbname  database level, and the application
                                         level for each application connected to
                                         the database.
                                         Information is also returned for each
                                         lock that is held by an application.
                                         THIS INFORMATION IS RETURNED ONLY IF
                                         THE DATABASE MONITOR LOCK SWITCH IS ON.
                                         REF REFID=XXXXXY
                SQLMA_DBASE_TABLESPACES  Information about tablespace activity
       get snapshot for tablespaces      the database level; the application
       on 'dbname'                       level for each application connected to
                                         the database; and the tablespace level
                                         for each tablespace that has been
                                         accessed by an application connected to
                                         the database.
                                         This information is returned only if
                                         the Database Monitor BUFFERPOOL switch
                                         is ON.
                                         REF REFID=XXXXXY
                SQLMA_DBASE_APPLINFO     Returns the same information as
       list applications for database dbname
                                         SQLMA_APPL_ALL
                                         (application identification
                                         information), but only for applications
                                         currently connected to the database
                                         specified for 'object'.

            To obtain information about a particular application:

                SQLMA_APPL               Application level information for an
       get snapshot for application applid 'applid'
                                         application identified by application
                                         name, specified in 'object'
                                         Note: Application
                                           names can be listed by issuing an an
                                           SQLMA_APPLINFO_ALL, or
                                           SQLMA_DBASE_APPLINFO request.
                SQLMA_AGENT_ID           Same as SQLMA_APPL, but the application is
       get snapshot for application      specified by agent_id.  The agent_id
       agentid 'agentid'                 can also be obtained from an
                                         SQLMA_APPLINFO_ALL or
                                         SQLMA_DBASE_APPLINFO request, or it may
                                         be obtained by issuing Operating System
                                         specific commands.  For example, on AIX
                                         systems, the agentid is the process id
                                         of the db2agent process running on the
                                         server to service a connection and it
                                         can generally be obtained with the
                                         following command:
                                           'ps -fu userid | grep db2agent'

        agent_id:

            The identifier of the agent to snapshot.
            Required only if obj_type is SQLMA_AGENT_ID, ignored otherwise.

        object:

            A null-terminated string that provides the database alias name
            (REF: List database directory.) if obj_type specifies one of the
            following requests:
               SQLMA_DBASE
               SQLMA_DBASE_APPLS
               SQLMA_DBASE_LOCKS
               SQLMA_DBASE_TABLESPACES
               SQLMA_DBASE_APPLINFO


            A null-terminated string that provides the application name
            (REF: List applications) if obj_type specifies SQLMA_APP

            It is ignored for all other requests.

OUTPUT:

The monitor data is returned in data blocks that are copied contiguously in the
user provided buffer.   The 'collected' output structure indicates the number of
'top-level' blocks that were copied to the buffer.  The first 4 bytes of each
block contain the size (as an unsigned int) of the block, the 5th byte is a char
that indicates its type.

The following table shows the blocks returned for each type of snapshot request.

 Input request type:     Data block returned:               block type:

 SQLMA_DBASE_APPLINFO    sqlm_applinfo for each             SQLM_APPLINFO_SS
                         application connected to
                         the database
                         REF: sqlm_applinfo on page ...
 SQLMA_DB2               sqlm_db2                           SQLM_DB2_SS
 SQLMA_APPLINFO_ALL      sqlm_applinfo for each
                         application connected to a database
                         managed by the db2 instance.
 SQLMA_DCS_APPLINFO_ALL  sqlm_dcs_applinfo for each dcb     SQLM_DCS_APPLINFO_SS
                         application.
 SQLMA_APPL_ALL          sqlm_appl for each application
 SQLMA_DBASE_ALL         sqlm_dbase for each database that  SQLM_DBASE_SS
                         has an active connection.
 SQLMA_DBASE             sqlm_dbase                         SQLM_DBASE_SS
 SQLMA_DBASE_TABLES      sqlm_table_header for the database SQLM_TABLE_HEADER_SS
                         sqlm_table for each table accessed SQLM_TABLE_SS
                         by an application connected to the
                         database
 SQLMA_DBASE_LOCKS       sqlm_dbase_lock for the database   SQLM_DBASE_LOCK_SS
                         sqlm_appl_lock for each app        SQLM_APPL_LOCK_SS
                         application connected to the
                         database
                         Following each sqlm_appl_lock, an  SQLM_LOCK_SS
                         sqlm_lock for each lock held by
                         the application.
 SQLMA_DBASE_TABLESPACES sqlm_tablespace_header        SQLM_TABLESPACE_HEADER_SS
                         sqlm_tablespace for each           SQLM_TABLESPACE_SS
                         tablespace accessed by an
                         application connected to the
                         database
 SQLMA_DBASE_APPLINFO    sqlm_applinfo for each             SQLM_APPLINFO_SS
                         application connected to the
                         database
 SQLMA_APPL              sqlm_appl                          SQLM_APPL_SS
 SQLMA_AGENT_ID          sqlm_appl                          SQLM_APPL_SS

See sqlmon.h for the declaration of each data block.

collected:

   This output structure returns general information about the database manager;
 the status of the Database monitor switches; and the number of 'top-level' data
 blocks returned in the output buffer.

   size - size in bytes of sqlm_collected.  Provided for binary compatibility
 when the DB2 server is not at the same level as the client (A down-level server
 may return a smaller sqlm_collected.)

    db2             - Set to 1 if the buffer contains an sqlm_db2 block.
    databases       - number of sqlm_dbase returned in the buffer.
    table_databases - number of sqlm_table_header returned
    lock_databases  - number of sqlm_dbase_lock returned.
    applications    - number of sqlm_appl returned.
    applinfos       - number of sqlm_applinfo returned.
    dcs_applinfos   - number of sqlm_dcs_applinfo returned.
    tablespace_databases - number of sqlm_tablespace_header returned.

    server_db2_type - DB2 Server Type:
          SQLF_NT_STANDALONE    Client/Server on same machine.
          SQLF_NT_SERVER        Can be both client and server, both inbound and
                                outbound.
          SQLF_NT_REQUESTOR     Outbound only.  Never returned.
          SQLF_NT_STAND_REQ     Standalone requestor, can go outbound or have
                                local connections.
    time_stamp      - time at which Snapshot was taken.  It is expressed as
                      the number of seconds since Jan 1st, 1970 GMT, a form
                      suitable for printing with ctime().
    group_states    - An array of sqlm_recording_group, one for each monitor
                      switch:
         input_state:  - Not applicable. Just ignore this field.
         output_state  - The current state of the switch: 1 = ON, 0 = OFF
         start_time    - A timestamp, the time a which the switch was turned ON,
                         0 if the switch is OFF.
    server_prdid       - Product/version on server. A blank padded string of
                         length SQLM_IDENT_SZ
    server_nname       - Config NAME of server. A blank padded string of length
                         SQLM_IDENT_SZ
    server_instance_name - DB2 instance name. A blank padded string of length
                           SQLM_IDENT_SZ

buffer_ptr:

   User provided buffer where to copy the Database Monitor data blocks.
   The order in which the blocks are returned is not guaranteed, and therefore
 you must check each block type (fifth byte) before reading it.

   There are two types of blocks: 'top-level' blocks, the number of which
   is indicated in the sqlm_collected output structure, and lower-level blocks,
 which are guaranteed to follow their parent block.   Their number of
 lower-level block is indicated in the parent block.

   If the buffer is too small, data is truncated and sqlcode SQLM_RC_BUFFER_FULL
 is returned.

   For example, assuming the existance of a database that has 2 active
 connections, if the user requests SQLMA_DBASE_LOCKS, and SQLMA_DBASE.  The
 following blocks may be returned in the buffer, (provided that the LOCK switch
 is ON):

    buffer:
+---------------+--------------+---------+---------+--------------+---------+----------+
|sqlm_dbase_lock|sqlm_appl_lock|sqlm_lock|sqlm_lock|sqlm_appl_lock|sqlm_lock|sqlm_dbase|
|  num_appls: 2 |  num_locks: 2|         |         | num_locks: 1 |         |          |
+---------------+--------------+---------+---------+--------------+---------+----------+

  Here, there are two 'top-level' structures: sqlm_dbase and sqlm_dbase_lock.
  sqlm_dbase_lock is followed by lower-level structures, which number is
 indicated in their parent structure.

    If the LOCK switch was OFF, the same request would return only the
 sqlm_dbase block:

    buffer:
    +------------+
    | sqlm_dbase |
    |            |
    +------------+

  When a block returned contains data elements that are under switch control,
 which is the case for an sqlm_dbase, those elements are zeroed if their switch
 is OFF.

*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "sqlca.h"
#include "sqlutil.h"
#include "sqlmon.h"
#include "util.h"

#define CHECKERR(CE_STR)    if (check_error (CE_STR, &sqlca) !=0) return 1;

 /******************************************************************************
* Print a Blank Padded String of maximum length SZ
*
* note: strings returned by sqlmonss are NOT NULL-TERMINATED, they are all
*       blank padded up to some maximum length.
*******************************************************************************/
#define dump_BPSTRING(fp, str, SZ) { \
    int k=0; \
    while (str[k]!=' '&&k<SZ) k++; \
    if (k<SZ) str[k]='\0'; \
    fprintf(fp, #str": %0.*s\n", SZ, str); \
}

int perform_db_snap (char *dbname, sqlm_collected *collected,
   char *buffer_ptr, int buffer_sz);

/* Routines to assist in printing the output returned by "sqlmonss" */
char* appl_status_string(int val);
char* lock_object_type_string(int val);
char* lock_mode_string(int val);
char* lock_status_string(int val);

/* Printing routines. */
void dump_sqlm_dbase_lock(FILE* fp, sqlm_dbase_lock* db_lock);
void dump_sqlm_appl_lock(FILE* fp, sqlm_appl_lock* appl_lock);
void dump_sqlm_lock(FILE* fp, sqlm_lock* lock);

/* Process the output from sqlmonss() */
void dump_snapshot_output(FILE* fp, sqlm_collected collected, char* snap);

int main (int argc, char* argv[]) {
    struct sqlca          sqlca;
    /* sqlmonsz() could be used to estimate required size */
    sqlm_collected collected;
    char  buffer_ptr[2048];

    char dbname[9];
    char userid[9];
    char passwd[19];
    char nodename[9];

    if (argc == 5) {
        strcpy (userid, argv[2]);
        strcpy (passwd, argv[3]);
        strcpy (nodename, argv[4]);
       
      /*************************/
      /* ATTACH API called     */
      /*************************/
        sqleatin (nodename, userid, passwd, &sqlca);
    }
    else if (argc!=2) {
        printf ("\ndbsnap: Take a snapshot of database activity.\n");
        printf ("\tUSAGE: dbsnap dbname [userid password remote_nodename]\n");
        return 1;
    } 

    strcpy (dbname, argv[1]);

    perform_db_snap(dbname, &collected, buffer_ptr, 2048);
    dump_snapshot_output(stdout, collected, buffer_ptr);

    return 0;
}




 /******************************************************************************
*  Perform a snapshot of lock activity for a database
*******************************************************************************/
int perform_db_snap(char            *dbname,
                    sqlm_collected  *collected,
                    char            *buffer_ptr,
                    int              buffer_sz) {
    int rc;
    struct sqlca sqlca;

    /* Request SQLMA_DBASE, and SQLMA_DBASE_LOCKS in the sqlma */
    struct sqlma* sqlma = (struct sqlma *) malloc(SQLMASIZE(2));
    sqlma->obj_num = 2;
    sqlma->obj_var[0].obj_type = SQLMA_DBASE;
    strcpy(sqlma->obj_var[0].object, dbname);
    sqlma->obj_var[1].obj_type = SQLMA_DBASE_LOCKS;
    strcpy(sqlma->obj_var[1].object, dbname);

    /*********************************************\
    * DATABASE SYSTEM MONITOR SNAPSHOT API called *
    \*********************************************/
    rc = sqlmonss(SQLM_DBMON_VERSION2, NULL, sqlma, buffer_sz,
                  (void *) buffer_ptr, collected, &sqlca);
    CHECKERR ("SNAPSHOT");

    free(sqlma);
    return rc;
} /* end perform_db_snap */

/*-----------------------------------------------------------------------------

  The routines below are to assist in printing the output returned
  by sqlmonss.

-----------------------------------------------------------------------------*/

 /******************************************************************************
* Map Application Status to a printable string
*
* note: These #define may be found in sqlmon.h
*******************************************************************************/
char* appl_status_string(int val) {
    switch (val) {
    case SQLM_CONNECTPEND:        return "SQLM_CONNECTPEND";
    case SQLM_CONNECTED:          return "SQLM_CONNECTED";
    case SQLM_UOWEXEC:            return "SQLM_UOWEXEC";
    case SQLM_UOWWAIT:            return "SQLM_UOWWAIT";
    case SQLM_LOCKWAIT:           return "SQLM_LOCKWAIT";
    case SQLM_COMMIT_ACT:         return "SQLM_COMMIT_ACT";
    case SQLM_ROLLBACK_ACT:       return "SQLM_ROLLBACK_ACT";
    case SQLM_RECOMP:             return "SQLM_RECOMP";
    case SQLM_COMP:               return "SQLM_COMP";
    case SQLM_INTR:               return "SQLM_INTR";
    case SQLM_DISCONNECTPEND:     return "SQLM_DISCONNECTPEND";
    case SQLM_TPREP:              return "SQLM_TPREP";
    case SQLM_THCOMT:             return "SQLM_THCOMT";
    case SQLM_THABRT:             return "SQLM_THABRT";
    case SQLM_TEND:               return "SQLM_TEND";
    case SQLM_CREATE_DB:          return "SQLM_CREATE_DB";
    case SQLM_RESTART:            return "SQLM_RESTART";
    case SQLM_RESTORE:            return "SQLM_RESTORE";
    case SQLM_BACKUP:             return "SQLM_BACKUP";
    case SQLM_LOAD:               return "SQLM_LOAD";
    case SQLM_UNLOAD:             return "SQLM_UNLOAD";
    case SQLM_IOERROR_WAIT:       return "SQLM_IOERROR_WAIT";
    case SQLM_QUIESCE_TABLESPACE: return "SQLM_QUIESCE_TABLESPACE";
    }
    return "";
} /* end of appl_status_string */

 /******************************************************************************
* Map lock_object_type to a printable string
*******************************************************************************/
char* lock_object_type_string(int val) {
    switch (val) {
    case SQLM_TABLE_LOCK:        return "SQLM_TABLE_LOCK";
    case SQLM_ROW_LOCK:          return "SQLM_ROW_LOCK";
    case SQLM_INTERNAL_LOCK:     return "SQLM_INTERNAL_LOCK";
    case SQLM_TABLESPACE_LOCK:   return "SQLM_TABLESPACE_LOCK";
    case 0:                      return "No lock wait";
    }
    return "";
} /* end of lock_object_type_string */

 /******************************************************************************
* Map lock_mode to a printable string
*******************************************************************************/
char* lock_mode_string(int val) {
    switch (val) {
    case SQLM_LNON: return "NO";
    case SQLM_LOIS: return "IS";
    case SQLM_LOIX: return "IX";
    case SQLM_LOOS: return "S";
    case SQLM_LSIX: return "SIX";
    case SQLM_LOOX: return "X";
    case SQLM_LOIN: return "IN";
    case SQLM_LOOZ: return "Z";
    case SQLM_LOOU: return "U";
    }
    return "";
}

 /******************************************************************************
* Map lock_status to a printable string
*******************************************************************************/
char* lock_status_string(int val) {
    switch (val) {
    case SQLM_LRBGRNT: return "Granted";
    case SQLM_LRBCONV: return "Converting";
    }
    return "";
}

 /*----------------------------------------------------------------------------

  Printing routines.

-----------------------------------------------------------------------------*/

 /******************************************************************************
* Print an sqlm_dbase_lock
*******************************************************************************/
void dump_sqlm_dbase_lock(FILE* fp, sqlm_dbase_lock* db_lock) {
    fprintf(fp,"\nsqlm_dbase_lock contains: \n");
    fprintf(fp,"db_lock->info_type: %s\n",        "SQLM_DBASE_LOCK_SS");
    fprintf(fp,"db_lock->locks_held: %ld\n",      db_lock->locks_held);
    fprintf(fp,"db_lock->appls_cur_cons: %ld\n",  db_lock->appls_cur_cons);
    fprintf(fp,"db_lock->num_appls: %ld\n",       db_lock->num_appls);
    fprintf(fp,"db_lock->locks_waiting: %ld\n",   db_lock->locks_waiting);
    dump_BPSTRING(fp, db_lock->input_db_alias, SQLM_DBPATH_SZ);
    dump_BPSTRING(fp, db_lock->db_name, SQLM_IDENT_SZ);
    dump_BPSTRING(fp, db_lock->db_path, SQLM_DBPATH_SZ);
} /* end of dump_sqlm_dbase_lock */

 /******************************************************************************
* Print an sqlm_appl_lock
*******************************************************************************/
void dump_sqlm_appl_lock(FILE* fp, sqlm_appl_lock* appl_lock) {
    fprintf(fp,"\nsqlm_appl_lock contains: \n");
    fprintf(fp,"appl_lock->info_type: %s\n",    "SQLM_APPL_LOCK_SS");
    fprintf(fp,"appl_lock->agent_id: %6.6ld\n", appl_lock->agent_id);
    fprintf(fp,"appl_lock->appl_status: %s\n",
                                   appl_status_string(appl_lock->appl_status));
    fprintf(fp,"appl_lock->codepage_id: %ld\n", appl_lock->codepage_id);
    fprintf(fp,"appl_lock->locks_held: %ld\n",  appl_lock->locks_held);
    fprintf(fp,"appl_lock->num_locks: %ld\n",   appl_lock->num_locks);

    /* Print the status change time, only if non-zero */
    if (appl_lock->status_change_time.seconds) {
        fprintf(fp,"appl_lock->status_change_time: %s\n",
                  ctime((time_t*) &appl_lock->status_change_time.seconds));
    } /* end if status changed */

    dump_BPSTRING(fp, appl_lock->appl_id,         SQLM_APPLID_SZ);
    dump_BPSTRING(fp, appl_lock->sequence_no,     SQLM_SEQ_SZ);
    dump_BPSTRING(fp, appl_lock->appl_name,       SQLM_IDENT_SZ);
    dump_BPSTRING(fp, appl_lock->auth_id,         SQLM_IDENT_SZ);
    dump_BPSTRING(fp, appl_lock->client_db_alias, SQLM_IDENT_SZ);

    /* The following information is returned only if the application is in
       a lock wait (otherwise it is zeroed, or set to blank spaces if a
       string) */
    if (appl_lock->appl_status==SQLM_LOCKWAIT) {
        fprintf(fp,"appl_lock->lock_object_name: %ld\n",
                   appl_lock->lock_object_name);
        fprintf(fp,"appl_lock->agent_id_holding_lk: %6.6ld\n",
                   appl_lock->agent_id_holding_lk);
        fprintf(fp,"appl_lock->lock_object_type: %s\n",
                  lock_object_type_string( appl_lock->lock_object_type));
        fprintf(fp,"appl_lock->table_file_id: %ld\n", appl_lock->table_file_id);
        dump_BPSTRING(fp, appl_lock->appl_id_holding_lk,     SQLM_APPLID_SZ);
        dump_BPSTRING(fp, appl_lock->sequence_no_holding_lk, SQLM_SEQ_SZ);
        dump_BPSTRING(fp, appl_lock->table_name,      SQLM_IDENT_SZ);
        dump_BPSTRING(fp, appl_lock->table_schema,    SQLM_IDENT_SZ);
        dump_BPSTRING(fp, appl_lock->tablespace_name, SQLM_IDENT_SZ);
    } /* end if this application is in a lock_wait */
} /* end of dump_sqlm_appl_lock */

 /******************************************************************************
* Print an sqlm_lock
*******************************************************************************/
void dump_sqlm_lock(FILE* fp, sqlm_lock* lock) {
    fprintf(fp,"\nsqlm_lock contains: \n");
    fprintf(fp,"lock->info_type: %s\n", "SQLM_LOCK_SS");
    fprintf(fp,"lock->lock_object_type: %s\n",
                        lock_object_type_string(lock->lock_object_type));

    /* Print the object of this lock */
    switch (lock->lock_object_type) {
    case SQLM_ROW_LOCK:
    case SQLM_TABLE_LOCK:
        dump_BPSTRING(fp, lock->table_name,   SQLM_IDENT_SZ);
        dump_BPSTRING(fp, lock->table_schema, SQLM_IDENT_SZ);
        fprintf(fp,"lock->table_file_id: %ld\n", lock->table_file_id);
        break;
    case SQLM_TABLESPACE_LOCK:
        dump_BPSTRING(fp, lock->tablespace_name, SQLM_IDENT_SZ);
        break;
    case SQLM_INTERNAL_LOCK:
        break;
    }
    fprintf(fp,"lock->lock_mode: %s\n",  lock_mode_string(lock->lock_mode));
    fprintf(fp,"lock->lock_status: %s\n",lock_status_string(lock->lock_status));
    fprintf(fp,"lock->lock_object_name: %ld\n", lock->lock_object_name);
} /* end of dump_sqlm_lock */

 /*----------------------------------------------------------------------------

  Process the output from sqlmonss()

-----------------------------------------------------------------------------*/

 /******************************************************************************
* Process the snapshot output - print it to file
*******************************************************************************/
void dump_snapshot_output(FILE* fp, sqlm_collected collected, char* snap) {

    /* Compute the number of top-level data blocks returned */
    int num_top_lvl_structs = collected.db2 + collected.databases +
                       collected.table_databases + collected.lock_databases +
                       collected.applications + collected.applinfos +
                       collected.dcs_applinfos + collected.tablespace_databases;

    /* Process each block */
    while (num_top_lvl_structs--) {
        /* Check the block type, (5th byte of any top-level block) */
        switch ((unsigned char) *(snap+4)) {
        case SQLM_DB2_SS:
            {
                sqlm_db2* db2_snap = (sqlm_db2*)  snap;
                /* ... dump_sqlm_db2(fp, db2_snap); */
                snap+=db2_snap->size;
            }
            break;
        case SQLM_DBASE_SS:
            {
                sqlm_dbase* db_snap = (sqlm_dbase*) snap;
                /* ... dump_sqlm_dbase(fp, db_snap); */
                snap+=db_snap->size;
            }
            break;
        case SQLM_APPL_SS:
            {
                sqlm_appl* appl_snap = (sqlm_appl*) snap;
                /* ... dump_sqlm_appl(fp, appl_snap); */
                snap+=appl_snap->size;
            }
            break;
        case SQLM_TABLE_HEADER_SS:
            {
                int numtabs;
                sqlm_table_header*   tabh_snap;
                tabh_snap=(sqlm_table_header*) snap;
                numtabs = tabh_snap->num_tables;
                /* ... dump_sqlm_table_header(fp, tabh_snap); */
                snap+=tabh_snap->size;

                /* dump all table entries, which always immediately
                   follow the header */
                while (numtabs--) {
                    sqlm_table* tab_snap;
                    tab_snap = (sqlm_table*) snap;
                    /* .. dump_sqlm_table(fp, tab_snap); */
                    snap+=tab_snap->size;
                } /* end of for each sqlm_table */
            }
            break;
        case SQLM_DBASE_LOCK_SS:
            {
                int numaplocks;  /* number of apps connected to this db */
                sqlm_dbase_lock* dblock_snap = (sqlm_dbase_lock*) snap;
                dump_sqlm_dbase_lock(fp, dblock_snap);
                numaplocks = dblock_snap->num_appls;
                snap+=dblock_snap->size;

              /* dump all appl lock entries, which always follow the db entry */
                while (numaplocks--) {
                    sqlm_appl_lock*   alock_snap;
                    int numlocks; /* number of locks held by this application */
                    alock_snap = (sqlm_appl_lock*) snap;
                    dump_sqlm_appl_lock(fp, alock_snap);
                    numlocks=alock_snap->num_locks;
                    snap+=alock_snap->size;

                    /* dump all lock entries, which follow the appl entry */
                    while (numlocks--) {
                        sqlm_lock*   lock_snap;
                        lock_snap =  (sqlm_lock*) snap;
                        dump_sqlm_lock(fp, lock_snap);
                        snap+=lock_snap->size;
                    }
                }
            }
            break;
       case SQLM_APPLINFO_SS:
            {
                sqlm_applinfo* ainfo_snap = (sqlm_applinfo*) snap;
                /* ... dump_sqlm_applinfo(fp, ainfo_snap); */
                snap+=ainfo_snap->size;
            }
            break;
        case SQLM_DCS_APPLINFO_SS:
            {
                sqlm_dcs_applinfo* dcsainfo_snap;
                dcsainfo_snap = (sqlm_dcs_applinfo*) snap;
                /* ... dump_sqlm_dcs_applinfo(fp, dcsainfo_snap); */
                snap+=dcsainfo_snap->size;
            }
            break;
        case SQLM_TABLESPACE_HEADER_SS:
            {
                sqlm_tablespace_header* tbh_snap=(sqlm_tablespace_header*) snap;
                int numtspaces;
                /* ... dump_sqlm_tablespace_header(fp, tbh_snap); */
                numtspaces=tbh_snap->num_tablespaces;
                snap+=tbh_snap->size;
                while (numtspaces--) {
                    sqlm_tablespace* ts_snap = (sqlm_tablespace*) snap;
                    /* ... dump_sqlm_tablespace(fp, ts_snap); */
                    snap+=ts_snap->size;
                }
            }
            break;
        default:
            printf("%s:%d - %s\n", __FILE__, __LINE__,
                   "Unexpected data in snapshot output buffer!");
       } /* end switch on structure type */
    } /* end while there are top-level structs in the snapshot output buffer */
} /* end of dump_snapshot_output */