Version 6 event monitors return their data in a self-describing format. Figure 5 shows the structure of the data stream and Table 1 provides some examples of the logical data groups and data elements that could be returned.
Note: | In the examples and tables descriptive names are used for the identifiers. These names are prefixed by SQLM_ELM_ in the actual data stream. For example, db_event would appear as SQLM_ELM_DB_EVENT in the event monitor output. Types are prefixed with SQLM_TYPE_ in the actual data stream. For example, headers appear as SQLM_TYPE_HEADER in the data stream. |
Figure 5. Event Monitor Data Stream
This header has the same size and type as pre-Version 6 event monitor streams. This allows applications to determine if the event monitor output is self-describing or is in the old static format.
Note: | This data element is extracted by reading sizeof(sqlm_event_log_data_stream) bytes from the data stream. |
Table 1. Sample Event Data Stream
Logical Data Group | Data Stream | Description | ||
---|---|---|---|---|
event_log_ stream_header |
sqlm_little_endian 1000 sqlm_dbmon_version6 |
Byte order of the event monitor data returned. Not used (for compatibility with previous releases). The version of the database manager that returned the data. Only version 6 monitors can write data in the self-describing format. | ||
log_header_event |
100 header log_header_event |
Size of the logical data group. Indicates the start of a logical data group. Name of the logical data group. | ||
4 u32bit byte_order little_endian |
Size of the data stored in Data element type - 32 bit numeric. The name of the data element collected. The collected value for this element. | |||
2 u16bit codepage_id 850 |
Size of the data stored in Data element type - unsigned 16 bit numeric. The name of the data element collected. The collected value for this element. | |||
db_event |
100 header db_event |
Size of the logical data group. Indicates the start of a logical data group. Name of the logical data group. | ||
4 u32bit lock_waits 2 |
Size of the data stored in Data element type - unsigned 32 bit numeric. The name of the data element collected. The collected value for this element. |
The event_log_stream_header identifies the version of the database manager that returned the data. Only Version 6 monitors write their data in the self-describing format.
If you are working with a Version 6 monitor, then you can start processing the self-describing data stream. When reading the trace, you can use the size element to skip a logical data group in the trace.
An event monitor, unlike a snapshot monitor (see Snapshot Output), does not have a size element that returns the total size of the trace. You typically read an event monitor trace until you reach an end of file.
The log header describes the characteristics of the trace, containing information such as the memory model (for example little endian) of the server where the trace was collected, and the codepage of the database. You may have to do byte swapping on numerical values, if the system where you read the trace has a different memory model than the server (for example, Windows NT to UNIX). Codepage translation may also need to be done, if the database is configured in a different language than the machine from which you read the trace.
The following code can be used to read a single record from the event monitor trace.
//------------------------------------------------------------------------------ // Read an event record - returns: 0 (success) or EOF // NOTE: This works for all records except sqlm_event_log_stream_header //------------------------------------------------------------------------------ int read_event_record(EventLog *evtlog, char *buffer) { sqlm_header_info* pHeader = (sqlm_header_info*) buffer; //--------------------------------------------------------------------------- // Read the record header //--------------------------------------------------------------------------- int rc; rc=read_data(evtlog, (char *) pHeader, sizeof(sqlm_header_info)); if (rc) return rc; /* could be at EOF */ if (evtlog->needByteReversal) swapBytes_sqlm_event_rec_header(pHeader); //--------------------------------------------------------------------------- // Read the rest of the data //--------------------------------------------------------------------------- rc=read_data(evtlog, buffer + sizeof(sqlm_header_info), pHeader->size); if (rc==0 && evtlog->needByteReversal) swapBytes(pHeader->type, buffer); return rc; } /* end of read_event_record */
The following routines illustrate how you can open, read, or skip bytes from a PIPE or FILE on a UNIX platform.
//------------------------------------------------------------------------------ // File functions - Using the ANSI C library //------------------------------------------------------------------------------ #include <stdio.h> #include <stdlib.h> #include <errno.h> //------------------------------------------------------------------------------ FILE* openFile(char *file_name) { return fopen(file_name,"rb"); /* returns NULL if file cannot be opened */ } //------------------------------------------------------------------------------ int closeFile(FILE* handle) { return fclose(handle); } //------------------------------------------------------------------------------ int readFromFile(char* buffer, int size, FILE* fp) { int rc=0; // returns 0 (success); EOF; or errno int records_read = fread(buffer, size, 1, fp); if (records_read != 1) { if (feof(fp)) rc = EOF; else rc = errno; } /* end if no data was returned */ return rc; } /* end readFromFile */ //------------------------------------------------------------------------------ // Pipe functions - for AIX //------------------------------------------------------------------------------ #include <unistd.h> /* for pipe functions on AIX */ #include <fcntl.h> /* for definition of O_RDONLY and open() */ //------------------------------------------------------------------------------ int openNamedPipe (char *pipe_name) { return open(pipe_name, O_RDONLY); } //------------------------------------------------------------------------------ int closeNamedPipe (int handle) { return close(handle); } //------------------------------------------------------------------------------ int readFromPipe(int handle, char* buffer, int size) { int rc=0; int num_bytes; num_bytes = read(handle, buffer, size); if (num_bytes != size) { if (num_bytes==0) rc=EOF; else rc = num_bytes; } /* end did not get the expected number of bytes back from read() */ return rc; } /* end readFromPipe */ //------------------------------------------------------------------------------ // Read data from Event Monitor trace (FILE or PIPE) returns 0 (success) or EOF //------------------------------------------------------------------------------ int read_data(EventLog* evtlog, char* buffer, int size) { int rc=0; if (evtlog->type == EVMFile) { rc = readFromFile(buffer, size, evtlog->current_fp); if (rc && rc!=EOF) { fprintf(stderr, "ERROR: Could not read from: %s\n", evtlog->current_fn); exit(1); } /* end cannot read the log header from the file */ } /* end if the Event Monitor Log is read from a file */ else { rc = readFromPipe(evtlog->handle, buffer, size); if (rc && rc!=EOF) { fprintf(stderr, "ERROR: Could not read a data from: %s\n", evtlog->target); exit(2); } /* end cannot read from the pipe */ } /* end else the Event Log is read from a pipe */ return rc; } /* end of read_data */ //------------------------------------------------------------------------------ // Skip n bytes from current position in the trace //------------------------------------------------------------------------------ void skip_data(EventLog* evtlog, int n) { if (evtlog->type == EVMFile) fseek(evtlog->current_fp, n, SEEK_CUR); else if (evtlog->type == EVMPipe) { lseek(evtlog->handle, n, SEEK_CUR); } /* end else pipe event monitor */ } /* end skip_data *//
This code is required when transferring data between systems using different conventions for storing numerical values (for example, UNIX to Windows NT).
#include <sqlmon.h> // DB2 Database Monitor interface //------------------------------------------------------------------------------ // Byte conversion macros //------------------------------------------------------------------------------ #define SWAP2(s) ((((s) >> 8) & 0xFF) | (((s) << 8) & 0xFF00)) #define SWAP4(l) ((((l) >> 24) & 0xFF) | ((((l) & 0xFF0000) >> 8) & 0xFF00) \ | (((l) & 0xFF00) << 8) | ((l) << 24)) //------------------------------------------------------------------------------ void swapBytes_sqlm_event_log_stream_header(sqlm_event_log_stream_header* r) { r->size = SWAP4(r->size); r->version = SWAP4(r->version); } // end of swapBytes_sqlm_event_log_header)
All timestamps in event monitor records are returned in two unsigned 4 byte data elements (seconds and microsec). These represent the GMT time since January 1, 1970.
All strings in event monitor records are padded with blanks, up to their maximum size. Strings returned by DB2 are NOT NULL TERMINATED.