#pragma module RDB$SDA "X-4" // ****************************************************************************** // * * // * © Copyright 1976, 2003 Hewlett-Packard Development Company, L.P. * // * * // * Confidential computer software. Valid license from HP and/or * // * its subsidiaries required for possession, use, or copying. * // * * // * Consistent with FAR 12.211 and 12.212, Commercial Computer Software, * // * Computer Software Documentation, and Technical Data for Commercial * // * Items are licensed to the U.S. Government under vendor's standard * // * commercial license. * // * * // * Neither HP nor any of its subsidiaries shall be liable for technical * // * or editorial errors or omissions contained herein. The information * // * in this document is provided "as is" without warranty of any kind and * // * is subject to change without notice. The warranties for HP products * // * are set forth in the express limited warranty statements accompanying * // * such products. Nothing herein should be construed as constituting an * // * additional warranty. * // * * // ****************************************************************************** // // // FACILITY: // // SDA Extensions // // // ABSTRACT: // // This module implements Rdb related debugging and tracing // functions. // // // AUTHOR: // // Christian Moser, Hewlett Packard // // // REVISION HISTORY: // // X-4 CMOS Christian Moser 6-JUN-2004 // Make sure EXE$GPQ_HWRPB is a local symbol, otherwise we // accvio. Also recognize root resources for .snp and .rda files. // // X-3 CMOS Christian Moser 30-OCT-2003 // Remove the include of evx_opcodes.h since it isn't needed // and will allow building this utility on IA64. // // X-2 CMOS Christian Moser 21-OCT-2003 // Fix the RDB SHOW ACTIVE display, which had incorrect numbers // for the total locks on a resource tree. // // X-1 CMOS Christian Moser 6-MAY-2003 // Initial version. // // // Imported definitions // #define __NEW_STARLET 1 #include c_asm #include capdef #include cdtdef #include cli$routines #include climsgdef #include clubdef #include cpudef #include csbdef #include dcdef #include descrip #include dvidef #include dvsdef #include dyndef #include efndef #include gen64def #include hwrpbdef #include iledef #include ints #include iosbdef #include jpidef #include lckdef #include lckcpudef #include ldrdef #include ldrimgdef #include libdtdef #include lib$routines #include lkbdef #include lkidef #include lksbdef #include pcbdef #define PCB$S_EPID_PROC 21 #define PCB$V_EPID_NODE_IDX 21 #define PCB$S_EPID_NODE_IDX 8 #define PCB$S_EPID_NODE_SEQ 2 #include pqldef #include prcdef #include rsbdef #include sda_routines #include sbdef #include ssdef #include starlet #include stdio #include stdlib #include string #include stsdef #include syidef #include time #include timeb #include tqedef #include vms_macros #define RAISE_IPL 1 #define SMP_RELEASE 0 #define TOPTREES_NUM 50 #define RDB_REQIDT 1 #define QUOTE(s) #s #define STR(s) QUOTE(s) // // Macro to check if a CLI qualifier was present or negated // #define cli_qualifier( qualifier, flags, value ) \ { \ int status; \ $DESCRIPTOR ( qualifier_desc, qualifier ); \ status = cli$present ( &qualifier_desc ); \ if ( status == CLI$_PRESENT ) flags |= value; \ if ( status == CLI$_NEGATED ) flags &= ~value; \ } #define cli_value( qualifier, value, default_value ) \ { \ int status; \ static int buflen; \ static char buf[16]; \ static struct dsc$descriptor_s buf_desc; \ $DESCRIPTOR ( qualifier_desc, qualifier ); \ status = cli$present ( &qualifier_desc ); \ if ( status == CLI$_PRESENT ) \ { \ init_desc ( buf_desc, sizeof(buf), buf ); \ status = cli$get_value ( &qualifier_desc, &buf_desc, &buflen ); \ if ( !$VMS_STATUS_SUCCESS (status) ) return; \ buf[buflen] = '\0'; \ if ( sscanf ( buf, "%d", &value ) != 1 ) lib$signal ( SS$_INVARG ); \ if ( value < 1 ) value = default_value; \ } \ else \ value = default_value; \ } // // Macro to initialize a descriptor // #define init_desc(name, len, addr) \ { \ name.dsc$b_dtype = DSC$K_DTYPE_T; \ name.dsc$b_class = DSC$K_CLASS_S; \ name.dsc$a_pointer = addr; \ name.dsc$w_length = len; \ } // // structure type declarations // typedef struct _rsb_info { uint64 addr; int size; int tree_size; int csid; int subrsb; int oact; int rsnlen; char resnam[32]; } RSB_INFO; // // Routine declarations // extern int *rdb_cmd; void rdb$show_active_db(); void rdb$show_process(); void rdb$show_lock(); void rdb$show_resource(); void rdb$show_contention(); void rdb$disp_db(); void rdb$check_timoutq(); void rdb$stop(); int decode_rdb_lock(); void lookup_node(); void getdvi_by_vollck(); void process_queues(); void tree_size(); // // Global variables // int sda$extend_version = SDA_FLAGS$K_VERSION; static HWRPB *hwrpb = NULL; // pointer to local HWRPB buffer #pragma __required_pointer_size __save #pragma __required_pointer_size __long typedef struct _lkb$r_lkb *LKB_PQ; typedef struct _rsb *RSB_PQ; #pragma __required_pointer_size __restore static int lck$gl_maxid; static uint64 lck$gl_htblsiz; static UINT64_PQ lck$gq_idtbl; static UINT64_PQ lck$gq_timoutq; static UINT64_PQ lck$gq_hashtbl; static uint64 exe$gpq_hwrpb; static int init_done = 0; static uint64 timoutq_lkb; static GENERIC_64 rdb_interval_time; int current = 0; // current system or dumpfile? int state_flags; int lock_type; int valblk_flag; static LKB$R_LKB *lkb = NULL; static LKB$R_LKB *p1_lkb = NULL; static LKB$R_LKB *p2_lkb = NULL; static LKB$R_LKB *p3_lkb = NULL; static RSB *rsb = NULL; static RSB *p1_rsb = NULL; static RSB *p2_rsb = NULL; static RSB *p3_rsb = NULL; static RSB *p4_rsb = NULL; static PCB *pcb = NULL; char disp_rdb_info[96]; char disp_rdb_db[96]; char disp_lck_queue[16]; char disp_valblk[96]; uint64 disp_lkb; uint64 disp_rsb; int disp_lockid; int disp_parid; int disp_pid; // // Declare string descriptors, which are commonly used within many routines // $DESCRIPTOR ( address_desc, "ADDRESS" ); $DESCRIPTOR ( index_desc, "INDEX" ); $DESCRIPTOR ( id_desc, "IDENTIFICATION" ); $DESCRIPTOR ( interval_desc, "INTERVAL" ); $DESCRIPTOR ( type_desc, "TYPE" ); // // Pointers to string arrays, needed during facility and function decoding // static char *rq_gr_string[] = { "NL", "CR", "CW", "PR", "PW", "EX", " ", "??" }; static char *queue_string[] = { "Waiting", "Convert", "Granted", " " }; static char *type_fields[] = { "", "Fields", "Index Segments", "Indices", "Relations", "Relation Fields", "Field Versions", "Schema", "Constraint", "Constraint Relations", "Detail", "Blobs", "View Relations", "Storage Maps", "Storage Map Areas", "Inter Relations", "Collations", "Triggers", "Relation Contraints", "Relation Constraint Fields", "Privileges", "Modules", "Routines", "Parameters", "Query Outlines", "Sequences", "Profiles", "Granted Profiles", "Types", "Type Fields" }; // // Trace heading title routine // void decode_rdb_title ( void ) { sda$print ( " LKB Address Lockid ParentId GR RQ Queue Rdb Information"); sda$print ( "----------------- -------- -------- -- -- ------- ------------------------------------------------------------------------------" ); return; } void contention_rdb_title ( void ) { sda$print ( " Timestamp LKB Address Lockid ParentId PID GR RQ Queue Rdb Information"); sda$print ( "--------------- ----------------- -------- -------- -------- -- -- ------- ------------------------------------------------------" ); return; } void resource_title ( void ) { sda$print ( " RSB Address Rdb Information LKB Address PID Node Lockid GR RQ Queue " ); sda$print ( "----------------- -------------------------------------------------------- ----------------- -------- ------ -------- -- -- -------" ); return; } void tree_title ( void ) { sda$print ( "RSB Address Tot Locks SubRSB Act Node Resource Name" ); sda$print ( "----------------- --------- ------ ----- ------ -------------------------------" ); return; } /////////////////////////////////////////////////////////////////////////////// // // This is the main entry point in SDA extension routine called from // within SDA. // // sda$extend transfer_table, cmd_line, sda_flags // // transfer_table - pointer to the routine transfer table // cmd_line - address of descriptor of the command line passed // sda_flags - flags // void sda$extend (int *transfer_table, struct dsc$descriptor_s *cmd_line, SDA_FLAGS sda_flags) { int status; int i,k,n; int flag; uint64 ldr$gq_image_list; static LDRIMG *ldrimg; int ldrimg_ptr; uint64 temp; int arglist[8]; // // Initialize the table and establish the condition handler // sda$vector_table = transfer_table; lib$establish ( sda$cond_handler ); // // Give a quick command overview if no verb passed along // if ( cmd_line->dsc$w_length == 0 ) { sda$format_heading ( "RDB Utility (V1.0-002) - Quick Help Information" ); sda$new_page(); sda$print ( "RDB commands:" ); sda$print ( "" ); sda$print ( " RDB SHOW ACTIVE_DB - displays database with lock activity" ); sda$print ( "" ); sda$print ( " RDB SHOW PROCESS - displays Rdb lock information for a given process" ); sda$print ( " [/IDENTIFICATION=n]- process identified by its PID" ); sda$print ( " [/INDEX=n] - process identified by its index" ); sda$print ( " [/ADDRESS=n] - process identified by its PCB address" ); sda$print ( " [/GRANTED] - displays only granted locks " ); sda$print ( " [/WAITING] - displays only locks on the wait queue" ); sda$print ( " (waiting I- and T-locks are ignored)" ); sda$print ( " [/CONVERT] - displays only locks on the conversion queue" ); sda$print ( " [/VALBLK] - decode and display lock value block information" ); sda$print ( "" ); sda$print ( " RDB SHOW CONTENTION - monitors lock timeout queue for Rdb locks stalling" ); sda$print ( " [/INTERVAL=n] - interval check in seconds (fraction of second possible, i.e. /INT=0.2)" ); sda$print ( "" ); sda$print ( " RDB SHOW LOCK - displays Rdb information for a given lock" ); sda$print ( " [/IDENTIFICATION=n]- lock specified by its lockid" ); sda$print ( " [/ADDRESS=n] - lock specified by its LKB address" ); sda$print ( " [/GRANTED] - displays only granted locks " ); sda$print ( " [/WAITING] - displays only locks on the wait queue" ); sda$print ( " (waiting I- and T-locks are ignored)" ); sda$print ( " [/CONVERT] - displays only locks on the conversion queue" ); sda$print ( " [/TYPE=n] - lock specified by its type/name" ); sda$print ( " [/VALBLK] - decode and display lock value block information" ); sda$print ( "" ); sda$print ( " RDB SHOW RESOURCE - displays Rdb information for a given resource" ); sda$print ( " [/IDENTIFICATION=n]- resource specified by any of locks with a given lockid" ); sda$print ( " [/ADDRESS=n] - resource specified by its RSB address" ); sda$print ( " [/TYPE=n] - resource specified by its type/name" ); sda$print ( " [/VALBLK] - decode and display lock value block information" ); sda$print ( "" ); sda$print ( " RDB STOP - stops any pending timers etc." ); sda$print ( "" ); return; } // // Read the HWRPB into local memory // if ( hwrpb == NULL ) { hwrpb = (HWRPB *) malloc ( HWRPB$K_LENGTH ); sda$symbol_value ("EXE$GPQ_HWRPB", &exe$gpq_hwrpb); status = sda$getmem ( (void *) exe$gpq_hwrpb, &exe$gpq_hwrpb, 8 ); if ( !$VMS_STATUS_SUCCESS (status) ) return; status = sda$getmem ( (void *) exe$gpq_hwrpb, hwrpb, HWRPB$K_LENGTH ); if ( !$VMS_STATUS_SUCCESS (status) ) return; } // // Read lckmgr related cells // if ( init_done == 0 ) { sda$symbol_value ("LCK$GQ_IDTBL", (uint64 *)&lck$gq_idtbl); status = sda$getmem ( (void *) lck$gq_idtbl, &lck$gq_idtbl, 8 ); if ( !$VMS_STATUS_SUCCESS (status) ) return; sda$symbol_value ("LCK$GQ_HASHTBL", (uint64 *)&lck$gq_hashtbl); status = sda$getmem ( (void *) lck$gq_hashtbl, &lck$gq_hashtbl, 8 ); if ( !$VMS_STATUS_SUCCESS (status) ) return; sda$symbol_value ("LCK$GQ_TIMOUTQ", (uint64 *)&lck$gq_timoutq); sda$symbol_value ( "LCK$GL_MAXID", (uint64 *)&lck$gl_maxid ); status = sda$trymem ( (void *) lck$gl_maxid, &lck$gl_maxid, 4); if ( !$VMS_STATUS_SUCCESS (status) ) return; sda$symbol_value ( "LCK$GL_HTBLSIZ", (uint64 *)&lck$gl_htblsiz ); status = sda$trymem ( (void *) lck$gl_htblsiz, &lck$gl_htblsiz, 4); if ( !$VMS_STATUS_SUCCESS (status) ) return; if ( lkb == NULL ) sda$allocate ( LKB$K_LENGTH, (void *) &lkb); if ( p1_lkb == NULL ) sda$allocate ( LKB$K_LENGTH, (void *) &p1_lkb); if ( p2_lkb == NULL ) sda$allocate ( LKB$K_LENGTH, (void *) &p2_lkb); if ( p3_lkb == NULL ) sda$allocate ( LKB$K_LENGTH, (void *) &p3_lkb); if ( rsb == NULL ) sda$allocate ( RSB$K_LENGTH, (void *) &rsb); if ( p1_rsb == NULL ) sda$allocate ( RSB$K_LENGTH, (void *) &p1_rsb); if ( p2_rsb == NULL ) sda$allocate ( RSB$K_LENGTH, (void *) &p2_rsb); if ( p3_rsb == NULL ) sda$allocate ( RSB$K_LENGTH, (void *) &p3_rsb); if ( pcb == NULL ) sda$allocate ( PCB$K_LENGTH, (void *) &pcb); init_done = 1; } // // initialize data cells // state_flags = 0; valblk_flag = 0; // // Are we analyzing the running system or a dump // if ( sda_flags.sda_flags$v_current ) current = 1; // // Now parse any commands given and dispatch to the appropriate // routine. // status = cli$dcl_parse ( cmd_line, &rdb_cmd ); if ( !$VMS_STATUS_SUCCESS (status) ) return; status = cli$dispatch (); if ( !$VMS_STATUS_SUCCESS (status) ) return; // // Done, get out of here // return; } /////////////////////////////////////////////////////////////////////////////// // // This routine will display information about each active Rdb database // void rdb$show_active_db() { int i,m,n; int status; int t_size; int tr_size; int top_trees; int vaxcluster; static int buflen; uint64 *p; char nodename[8]; char tmpbuf[132]; #pragma __required_pointer_size __save #pragma __required_pointer_size 64 UINT64_PQ temp; UINT64_PQ lck$gq_rrsfl; RSB *rsb_ptr; #pragma __required_pointer_size __restore static RSB *rsb; RSB_INFO top_tree[TOPTREES_NUM]; struct dsc$descriptor_s devnam_desc; struct dsc$descriptor_s file_desc; // // Check if /TOP was specified // cli_value ( "TOPTREES", top_trees, TOPTREES_NUM ); if ( top_trees > TOPTREES_NUM ) top_trees = TOPTREES_NUM; // // Are we a cluster member or not? // sda$symbol_value ( "CLU$GB_VAXCLUSTER", (uint64 *)&vaxcluster ); status = sda$getmem ( (VOID_PQ) vaxcluster, &vaxcluster, 4); if ( !$VMS_STATUS_SUCCESS (status) ) return; // // Allocate local storage // if ( rsb == NULL ) sda$allocate ( RSB$K_LENGTH, (void *) &rsb ); // // Initialize tree and local nodename, write title // for ( i=0; irsb$q_rrsfl); status = sda$trymem ( rsb_ptr, rsb, RSB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return; p = (uint64 *) &rsb->rsb$t_resnam[0]; if ( (*p == 0x00000044000000DD) || (*p == 0x00000069000000DD) ) { tree_size ( (uint64)rsb_ptr + (uint64)&((RSB *)0)->rsb$q_srsfl, &tr_size ); if ( vaxcluster ) t_size = rsb->rsb$w_oact; else t_size = rsb->rsb$w_nact; if ( t_size != 0 ) { if ( t_size >= top_tree[top_trees-1].size ) { for ( m=0; m<(top_trees-1); m++ ) top_tree[m] = top_tree[m+1]; top_tree[top_trees-1].size = t_size; top_tree[top_trees-1].tree_size = tr_size; top_tree[top_trees-1].addr = (uint64) rsb_ptr; top_tree[top_trees-1].rsnlen = rsb->rsb$b_rsnlen; top_tree[top_trees-1].csid = rsb->rsb$l_csid; top_tree[top_trees-1].subrsb = rsb->rsb$l_refcnt; top_tree[top_trees-1].oact = t_size; memcpy ( &top_tree[top_trees-1].resnam, &rsb->rsb$t_resnam, rsb->rsb$b_rsnlen ); } else if ( t_size > top_tree[0].size ) { for ( n=0; nrsb$b_rsnlen; top_tree[n-1].csid = rsb->rsb$l_csid; top_tree[n-1].subrsb = rsb->rsb$l_refcnt; top_tree[n-1].oact = t_size; memcpy ( &top_tree[n-1].resnam, &rsb->rsb$t_resnam, rsb->rsb$b_rsnlen ); break; } } } } } // // Grab the next element in the root RSB queue // status = sda$getmem ( temp, &temp, 8); if ( !$VMS_STATUS_SUCCESS (status) ) return; } // // Display the results // for ( i=(top_trees-1); i>=0; i-- ) { if ( top_tree[i].size != 0 ) { lookup_node ( top_tree[i].csid, 0, nodename ); sda$print ( "!XL.!XL !9UL !6UL !5UL !6AZ !AF", top_tree[i].addr >> 32, top_tree[i].addr & 0xffffffff, top_tree[i].tree_size, top_tree[i].subrsb, top_tree[i].oact, nodename, top_tree[i].rsnlen, &top_tree[i].resnam ); getdvi_by_vollck ( &top_tree[i].resnam[8], &devnam_desc ); init_desc ( file_desc, 80, tmpbuf ); status = lib$fid_to_name ( &devnam_desc, (USHORT_PQ) &top_tree[i].resnam[22], &file_desc, &buflen ); if ( $VMS_STATUS_SUCCESS (status) ) sda$print ( "!58* File: !AF", buflen, &tmpbuf ); } } return; } /////////////////////////////////////////////////////////////////////////////// // // This routine will fetch the filename for a given Rdb database. // void rdb$disp_db ( char *db_resnam ) { int status; static int buflen; uint64 *p; char tmpbuf[132]; struct dsc$descriptor_s file_desc; struct dsc$descriptor_s devnam_desc; p = (uint64 *) &db_resnam[0]; if ( (*p == 0x00000044000000DD) || (*p == 0x00000069000000DD) ) { getdvi_by_vollck ( &db_resnam[8], &devnam_desc ); init_desc ( file_desc, 80, disp_rdb_db ); status = lib$fid_to_name ( &devnam_desc, (USHORT_PQ) &db_resnam[22], &file_desc, &buflen ); if ( $VMS_STATUS_SUCCESS (status) ) disp_rdb_db[buflen] = '\0'; } return; } /////////////////////////////////////////////////////////////////////////////// // // This routine will show Rdb locks for a given process // void rdb$show_process() { int status; int temp2; uint64 temp; uint64 lkb_ptr; uint64 queue_head; struct dsc$descriptor_s buf_desc; static char buf[64]; static int buflen; GENERIC_64 longquad; // // Init bufferdescriptor // init_desc ( buf_desc, sizeof(buf), buf ); cli_qualifier ( "VALBLK", valblk_flag, 1 ); // // Handle the /ADDRESS=xxxx case // status = cli$present ( &address_desc ); if ( status == CLI$_PRESENT ) { status = cli$get_value ( &address_desc, &buf_desc, &buflen ); if ( !$VMS_STATUS_SUCCESS (status) ) return; buf[buflen] = '\0'; // // Decode the passed address into a hexadecimal address, // we either passed an !XL.!XL or a !XL!XL address. // longquad.gen64$q_quadword = 0; if ( sscanf ( buf, "%08X.%08X", &longquad.gen64$l_longword[1], &longquad.gen64$l_longword[0] ) != 2 ) { longquad.gen64$q_quadword = 0; if ( sscanf ( buf, "%016LX", &longquad ) == 1 ) { if ( (longquad.gen64$l_longword[1] == 0) & ((longquad.gen64$l_longword[0] & 0x80000000) > 0) ) longquad.gen64$l_longword[1] = 0xFFFFFFFF; } else { sda$print ( "%RDB$SDA-E-INVARG, invalid PCB address specified" ); return; } } // // Set process context based on passed PCB address // status = sda$set_process ( 0, 0, longquad.gen64$q_quadword ); if ( !$VMS_STATUS_SUCCESS (status) ) lib$signal ( status ); } // // Handle the /INDEX=xxxx case // status = cli$present ( &index_desc ); if ( status == CLI$_PRESENT ) { status = cli$get_value ( &index_desc, &buf_desc, &buflen ); if ( !$VMS_STATUS_SUCCESS (status) ) return; buf[buflen] = '\0'; // // Decode the passed index into a numeric value // temp2 = -1; status = sscanf (buf, "%X", &temp2 ); if ( status != 1 ) { sda$print ( "%RDB$SDA-W-INVVAL, invalid process index" ); return; } // // Set process context based on passed process index // status = sda$set_process ( 0, temp2, 0 ); if ( !$VMS_STATUS_SUCCESS (status) ) lib$signal ( status ); } // // Check to see if /IDENTIFICATION=xxxx is passed // status = cli$present ( &id_desc ); if ( status == CLI$_PRESENT ) { status = cli$get_value ( &id_desc, &buf_desc, &buflen ); if ( !$VMS_STATUS_SUCCESS (status) ) return; buf[buflen] = '\0'; // // Decode the passed PID into a numeric value // temp2 = -1; status = sscanf (buf, "%X", &temp2 ); if ( status != 1 ) { sda$print ( "%RDB$SDA-W-INVVAL, invalid process identification" ); return; } // // Set process context based on passed external/internal PID // status = sda$set_process ( 0, temp2, 0 ); if ( !$VMS_STATUS_SUCCESS (status) ) lib$signal ( status ); } // // Read PCB into local memory // sda$symbol_value ( "PCB", (uint64 *)&queue_head ); status = sda$trymem ( (VOID_PQ) queue_head, pcb, PCB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return; // // Parse the state queue qualifiers // cli_qualifier("WAITING", state_flags, (1<<(LKB$K_WAITING+1)) ); cli_qualifier("CONVERT", state_flags, (1<<(LKB$K_CONVERT+1)) ); cli_qualifier("GRANTED", state_flags, (1<<(LKB$K_GRANTED+1)) ); // // Write page heading and title // sda$format_heading ( "Rdb Lock Information: Process index: !XW Name: !AC Extended PID: !XL", pcb->pcb$l_pid, &pcb->pcb$t_lname, pcb->pcb$l_epid ); sda$set_heading_routine ( decode_rdb_title ); sda$new_page (); // // Mark the lock queue header in the PCB // queue_head = queue_head + (uint64)&((PCB *)0)->pcb$q_lockqfl; temp = queue_head; // // Loop through all locks on the lock queue // while ( 1 ) { // // Get address of queue entry and return if we're done // status = sda$trymem ( (VOID_PQ) temp, &temp, 8); if ( !$VMS_STATUS_SUCCESS (status) ) return; if ( temp == queue_head ) return; // // Need to subtract the state queue flink offset to get to the LKB // address, then read the lock block into local memory // lkb_ptr = temp - (uint64)&((LKB$R_LKB *)0)->lkb$q_ownqfl; // // Verify if this is a Rdb lock and if true, display the results // status = decode_rdb_lock ( lkb_ptr, 0 ); if ( $VMS_STATUS_SUCCESS (status) ) { sda$print ( "!XL.!XL !XL !XL !AZ !AZ", disp_lkb>>32, disp_lkb & 0xffffffff, disp_lockid, disp_parid, disp_lck_queue, disp_rdb_info ); if ( valblk_flag && ((int8)disp_valblk[0] != 0) ) sda$print ( "!53* !AZ", disp_valblk ); } } return; } /////////////////////////////////////////////////////////////////////////////// // // This routine will show Rdb lock information // void rdb$show_lock() { int i; int status; int temp2; int64 lkb_ptr; uint64 temp; uint64 queue_head; uint8 *b; struct dsc$descriptor_s buf_desc; static char buf[64]; static int buflen; GENERIC_64 longquad; // // Initialize data // lkb_ptr = 0; cli_qualifier ( "VALBLK", valblk_flag, 1 ); // // Handle the /ADDRESS=xxxx case // status = cli$present ( &address_desc ); if ( status == CLI$_PRESENT ) { init_desc ( buf_desc, sizeof(buf), buf ); status = cli$get_value ( &address_desc, &buf_desc, &buflen ); if ( !$VMS_STATUS_SUCCESS (status) ) return; buf[buflen] = '\0'; // // Decode the passed address into a hexadecimal address, // we either passed an !XL.!XL or a !XL!XL address. // longquad.gen64$q_quadword = 0; if ( sscanf ( buf, "%08X.%08X", &longquad.gen64$l_longword[1], &longquad.gen64$l_longword[0] ) != 2 ) { longquad.gen64$q_quadword = 0; if ( sscanf ( buf, "%016LX", &longquad ) == 1 ) { if ( (longquad.gen64$l_longword[1] == 0) & ((longquad.gen64$l_longword[0] & 0x80000000) > 0) ) longquad.gen64$l_longword[1] = 0xFFFFFFFF; } else { sda$print ( "%RDB$SDA-E-INVARG, invalid LKB address specified" ); return; } } lkb_ptr = longquad.gen64$q_quadword; } // // Check to see if /IDENTIFICATION=xxxx is passed // status = cli$present ( &id_desc ); if ( status == CLI$_PRESENT ) { init_desc ( buf_desc, sizeof(buf), buf ); status = cli$get_value ( &id_desc, &buf_desc, &buflen ); if ( !$VMS_STATUS_SUCCESS (status) ) return; buf[buflen] = '\0'; // // Decode the passed lock id into a numeric value // temp2 = -1; status = sscanf (buf, "%X", &temp2 ); if ( status != 1 ) { sda$print ( "%RDB$SDA-W-INVVAL, invalid lock identification" ); return; } temp2 = temp2 & 0x00ffffff; if ( temp2 > lck$gl_maxid ) { sda$print ( "%RDB$SDA-W-INVVAL, invalid lock identification" ); return; } temp = (uint64)lck$gq_idtbl + (sizeof(uint64) * temp2); status = sda$trymem ( (VOID_PQ) temp, &lkb_ptr, 8); if ( !$VMS_STATUS_SUCCESS (status) ) return; } // // Set title and page heading // sda$format_heading ( "Rdb Lock Information:" ); sda$set_heading_routine ( decode_rdb_title ); sda$new_page (); // // Display information for a single lock // if ( lkb_ptr != 0 ) { status = decode_rdb_lock ( lkb_ptr, 0 ); if ( $VMS_STATUS_SUCCESS (status) ) { sda$print ( "!XL.!XL !XL !XL !AZ !AZ", disp_lkb>>32, disp_lkb & 0xffffffff, disp_lockid, disp_parid, disp_lck_queue, disp_rdb_info ); if ( valblk_flag && ((int8)disp_valblk[0] != 0) ) sda$print ( "!53* !AZ", disp_valblk ); sda$print ( "!53* Database !AZ", disp_rdb_db ); } return; } // // Parse the state queue qualifiers // cli_qualifier("WAITING", state_flags, (1<<(LKB$K_WAITING+1)) ); cli_qualifier("CONVERT", state_flags, (1<<(LKB$K_CONVERT+1)) ); cli_qualifier("GRANTED", state_flags, (1<<(LKB$K_GRANTED+1)) ); // // Check to see if /TYPE=n is passed // lock_type = 0; status = cli$present ( &type_desc ); if ( status == CLI$_PRESENT ) { init_desc ( buf_desc, sizeof(buf), buf ); status = cli$get_value ( &type_desc, &buf_desc, &buflen ); if ( !$VMS_STATUS_SUCCESS (status) ) return; buf[buflen] = '\0'; b = (uint8 *) &buf[0]; lock_type = (int) *b; } // // Loop through the whole lock id table // for ( i=1; i<=lck$gl_maxid; i++ ) { temp = (uint64)lck$gq_idtbl + (sizeof(uint64) * i); status = sda$trymem ( (VOID_PQ) temp, &lkb_ptr, 8); if ( !$VMS_STATUS_SUCCESS (status) ) return; // // if a slot with a LKB is found check to see if it is an Rdb lock // and if true decode and display the results // if ( lkb_ptr < 0 ) { status = decode_rdb_lock ( lkb_ptr, 0 ); if ( $VMS_STATUS_SUCCESS (status) ) { sda$print ( "!XL.!XL !XL !XL !AZ !AZ", disp_lkb>>32, disp_lkb & 0xffffffff, disp_lockid, disp_parid, disp_lck_queue, disp_rdb_info ); if ( valblk_flag && ((int8)disp_valblk[0] != 0) ) sda$print ( "!53* !AZ", disp_valblk ); } } } return; } /////////////////////////////////////////////////////////////////////////////// // // This routine will show Rdb resource information // void rdb$show_resource() { int i,k; int status; int temp2; uint64 temp; uint64 lkb_ptr; uint64 rsb_ptr; uint64 rsb_ptr2; uint64 queue_head; uint8 *b; struct dsc$descriptor_s buf_desc; static char buf[64]; static int buflen; GENERIC_64 longquad; // // Initialize data // rsb_ptr = 0; cli_qualifier ( "VALBLK", valblk_flag, 1 ); // // Handle the /ADDRESS=xxxx case // status = cli$present ( &address_desc ); if ( status == CLI$_PRESENT ) { init_desc ( buf_desc, sizeof(buf), buf ); status = cli$get_value ( &address_desc, &buf_desc, &buflen ); if ( !$VMS_STATUS_SUCCESS (status) ) return; buf[buflen] = '\0'; // // Decode the passed address into a hexadecimal address, // we either passed an !XL.!XL or a !XL!XL address. // longquad.gen64$q_quadword = 0; if ( sscanf ( buf, "%08X.%08X", &longquad.gen64$l_longword[1], &longquad.gen64$l_longword[0] ) != 2 ) { longquad.gen64$q_quadword = 0; if ( sscanf ( buf, "%016LX", &longquad ) == 1 ) { if ( (longquad.gen64$l_longword[1] == 0) & ((longquad.gen64$l_longword[0] & 0x80000000) > 0) ) longquad.gen64$l_longword[1] = 0xFFFFFFFF; } else { sda$print ( "%RDB$SDA-E-INVARG, invalid RSB address specified" ); return; } } rsb_ptr = longquad.gen64$q_quadword; } // // Check to see if /IDENTIFICATION=xxxx is passed // status = cli$present ( &id_desc ); if ( status == CLI$_PRESENT ) { init_desc ( buf_desc, sizeof(buf), buf ); status = cli$get_value ( &id_desc, &buf_desc, &buflen ); if ( !$VMS_STATUS_SUCCESS (status) ) return; buf[buflen] = '\0'; // // Decode the passed lock id into a numeric value // temp2 = -1; status = sscanf (buf, "%X", &temp2 ); if ( status != 1 ) { sda$print ( "%RDB$SDA-W-INVVAL, invalid lock identification" ); return; } temp2 = temp2 & 0x00ffffff; if ( temp2 > lck$gl_maxid ) { sda$print ( "%RDB$SDA-W-INVVAL, invalid lock identification" ); return; } // // Read the LKB into local memory, then find the RSB address // temp = (uint64)lck$gq_idtbl + (sizeof(uint64) * temp2); status = sda$trymem ( (VOID_PQ) temp, &lkb_ptr, 8); if ( !$VMS_STATUS_SUCCESS (status) ) return; status = sda$trymem ( (VOID_PQ) lkb_ptr, lkb, LKB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return; rsb_ptr = (uint64) lkb->lkb$q_rsb; } // // Set title and page heading // sda$format_heading ( "Rdb Resource Information:" ); sda$set_heading_routine ( resource_title ); sda$new_page (); // // Display results for a single resource // if ( rsb_ptr != 0 ) { status = decode_rdb_lock ( 0, rsb_ptr ); if ( $VMS_STATUS_SUCCESS (status) ) { sda$print ( "!XL.!XL !AZ", disp_rsb>>32, disp_rsb & 0xffffffff, disp_rdb_info ); if ( valblk_flag && ((int8)disp_valblk[0] != 0) ) sda$print ( "!19* !AZ", disp_valblk ); sda$print ( "!19* Database !AZ", disp_rdb_db ); // // Display the lock information from the granted, conversion and waiting queues // process_queues ( LKB$K_GRANTED, (uint64)disp_rsb + (uint64)&((RSB *)0)->rsb$q_grqfl ); process_queues ( LKB$K_CONVERT, (uint64)disp_rsb + (uint64)&((RSB *)0)->rsb$q_cvtqfl ); process_queues ( LKB$K_WAITING, (uint64)disp_rsb + (uint64)&((RSB *)0)->rsb$q_wtqfl ); } return; } // // Check to see if /TYPE=n is passed // lock_type = 0; status = cli$present ( &type_desc ); if ( status == CLI$_PRESENT ) { init_desc ( buf_desc, sizeof(buf), buf ); status = cli$get_value ( &type_desc, &buf_desc, &buflen ); if ( !$VMS_STATUS_SUCCESS (status) ) return; buf[buflen] = '\0'; b = (uint8 *) &buf[0]; lock_type = (int) *b; } // // Now loop through the whole resource hash table // for ( i=0; i>32, disp_rsb & 0xffffffff, disp_rdb_info ); if ( valblk_flag && ((int8)disp_valblk[0] != 0) ) sda$print ( "!19* !AZ", disp_valblk ); // // Display the lock information from the granted, conversion and waiting queues // process_queues ( LKB$K_GRANTED, (uint64)disp_rsb + (uint64)&((RSB *)0)->rsb$q_grqfl ); process_queues ( LKB$K_CONVERT, (uint64)disp_rsb + (uint64)&((RSB *)0)->rsb$q_cvtqfl ); process_queues ( LKB$K_WAITING, (uint64)disp_rsb + (uint64)&((RSB *)0)->rsb$q_wtqfl ); sda$skip_lines ( 1 ); } // // See, if there is another RSB in the hash chain // status = sda$trymem ( (VOID_PQ) rsb_ptr, &rsb_ptr, 8); if ( !$VMS_STATUS_SUCCESS (status) ) return; } } return; } /////////////////////////////////////////////////////////////////////////////// // // This routine is the guts of the whole utility. It has the knowledge // about the Rdb resource naming convention and will verify, if a given // LKB or RSB is indeed a Rdb lock or resource. // int decode_rdb_lock ( LKB_PQ lkb_ptr, RSB_PQ rsb_ptr ) { int i; uint64 temp; int status; uint64 p1_rsb_ptr; uint64 p2_rsb_ptr; uint64 p3_rsb_ptr; uint64 p4_rsb_ptr; int *p; int *pd; int *p1; int *p2; uint64 *pq; int *l1; int *l2; int8 *b; int16 *w; int16 *w1; int16 *w2; int16 *w3; int16 *w4; char *c; char tmpbuf[96]; static int buflen; struct dsc$descriptor_s buf_desc; $DESCRIPTOR ( fao_desc, "'!AF' " ); int pos; int flag; // // Check if a LKB is passed in // if ( lkb_ptr != NULL ) { // // Read the LKB into a local buffer and ignore if it is a cached // LKB or if the state isn't what we are looking for // status = sda$trymem ( (VOID_PQ) lkb_ptr, lkb, LKB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return status; if ( lkb->lkb$l_status & LKB$M_CACHED ) return SS$_IVLOCKID; if ( (state_flags != 0) && (!(state_flags & (1<<(((int8)lkb->lkb$b_state)+1)))) ) return SS$_IVLOCKID; // // Read the RSB into the local buffer and fix the mode fields // for the display of the results // status = sda$trymem ( (VOID_PQ) lkb->lkb$q_rsb, rsb, RSB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return status; switch ( (int8)lkb->lkb$b_state ) { case LKB$K_GRANTED: lkb->lkb$b_rqmode = LCK$K_EXMODE + 1; break; case LKB$K_CONVERT: break; case LKB$K_WAITING: lkb->lkb$b_grmode = LCK$K_EXMODE + 1; break; default: break; } // // Read the parent LKB into local memory // if ( lkb->lkb$q_parent != 0 ) { status = sda$trymem ( (VOID_PQ) lkb->lkb$q_parent, p1_lkb, LKB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return status; } // // save the LKB address away for the display and mark that an // LKB has been passed in // disp_lkb = (uint64) lkb_ptr; flag = 1; } // // Check if a RSB was passed in // if ( rsb_ptr != NULL ) { // // Read the RSB into local memory // status = sda$trymem ( (VOID_PQ) rsb_ptr, rsb, RSB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return status; // // save the RSB address away for the display and mark that an // RSB has been passed in // disp_rsb = (uint64) rsb_ptr; flag = 0; } // // initialize a few pointers to parts of the resource name // p = (int *)&rsb->rsb$t_resnam; pd = (int *)&rsb->rsb$t_resnam[4]; l1 = (int *)&rsb->rsb$t_resnam[4]; w1 = (int16 *)&rsb->rsb$t_resnam[4]; w = (int16 *)&rsb->rsb$t_resnam; pq = (uint64 *)&rsb->rsb$t_resnam; disp_valblk[0] = '\0'; // // if we passed in a /TYPE=n qualifier, check for the special // Monitor and Termination resource types // if ( lock_type != 0 ) { switch ( lock_type ) { case 'M': case 'T': if ( (lock_type != *pd) || (0x00DD != *w) ) return SS$_IVLOCKID; break; default: if ( lock_type != *p ) return SS$_IVLOCKID; break; } } // // it cannot be an Rdb lock if no parent and not a database, monitor // or termination lock // if ( rsb->rsb$q_parent == 0 ) { // // all Rdb root resources must have a 0x00DD in the first word // of the resource // if ( *w != 0xDD ) return SS$_IVLOCKID; switch ( *pd ) { case 'D': rdb$disp_db ( rsb->rsb$t_resnam ); if ( rsb->rsb$b_rsnlen == 28 ) sprintf ( disp_rdb_info, "D %-25s %s", "Database (global)", disp_rdb_db ); // LOCK$K_DB (global) else { w2 = (int16 *)&rsb->rsb$t_resnam[28]; sprintf ( disp_rdb_info, "D %-25s %s", "Database (local)", disp_rdb_db ); // LOCK$K_DB (local) } break; case 'M': p1 = (int *)&rsb->rsb$t_resnam[8]; c = (char *)&rsb->rsb$t_resnam[12]; rsb->rsb$t_resnam[16] = '\0'; sprintf ( disp_rdb_info, "M %-25s csid %08X variant %s", "unique monitor", *p1, c ); // LOCK$K_MONITOR break; case 'T': if ( state_flags != 0 ) return SS$_IVLOCKID; p1 = (int *)&rsb->rsb$t_resnam[8]; p2 = (int *)&rsb->rsb$t_resnam[12]; sprintf ( disp_rdb_info, "T %-25s pid %08X sid %u", "termination", *p1, *p2 ); // LOCK$K_TRM sprintf ( disp_valblk, "TRM_ok %02x", (uint8)rsb->rsb$q_valblk[0] ); break; default: sprintf ( disp_rdb_info, "? %-25s %08X", "?????", *p ); break; } // // Clear the value block info, if the lock value block was not valid // if ( rsb->rsb$v_valinvld ) disp_valblk[0] = '\0'; // // save the intersting pieces away for later display. // disp_parid = 0; if ( flag ) { disp_lockid = lkb->lkb$l_lkid; disp_pid = lkb->lkb$l_pid; sprintf ( disp_lck_queue, "%2s %2s %-7s", rq_gr_string[lkb->lkb$b_grmode], rq_gr_string[lkb->lkb$b_rqmode], queue_string[(int8)lkb->lkb$b_state+1] ); } return SS$_NORMAL; } // // Grab parent RSB into a local buffer // status = sda$trymem ( (VOID_PQ) rsb->rsb$q_parent, p1_rsb, RSB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return status; // // If the parent is the database resource, decode the interesting pieces // of the RSB for later display // pq = (uint64 *)&p1_rsb->rsb$t_resnam; if ( (*pq == 0x00000044000000DD) || (*p == 0x00000069000000DD) ) // LOCK$K_DB { switch ( *p ) { case 'A': sprintf ( disp_rdb_info, "A %-25s %u", "AIJ journal", *w1 ); // LOCK$K_AIJ sprintf ( disp_valblk, "CSID %08X LEOF %u PEOF %u Bytes_used %u", rsb->rsb$q_valblk[0], rsb->rsb$q_valblk[1], rsb->rsb$q_valblk[2], (uint16)rsb->rsb$q_valblk[3] ); break; case 'C': sprintf ( disp_rdb_info, "C %-25s %u", "Snapshot cursor", *w1 ); // LOCK$K_SAC sprintf ( disp_valblk, "Next_PNO %u", rsb->rsb$q_valblk[0] ); break; case 'E': sprintf ( disp_rdb_info, "E %-25s %u", "FILID entry", *w1 ); // LOCK$K_FILID sprintf ( disp_valblk, "Version %u", rsb->rsb$q_valblk[0] ); break; case 'F': sprintf ( disp_rdb_info, "F %-25s", "Database freeze" ); // LOCK$K_FRZ break; case 'G': sprintf ( disp_rdb_info, "G %-25s %u", "TSN block", *w1 ); // LOCK$K_TSNBLK sprintf ( disp_valblk, "Version %u Oldest_TSN %d:%d WIPS %04X", rsb->rsb$q_valblk[0], (int)rsb->rsb$q_valblk[2], (int)rsb->rsb$q_valblk[1], (uint16)rsb->rsb$q_valblk[3] ); break; case 'H': sprintf ( disp_rdb_info, "H %-25s", "RTUPB list" ); // LOCK$K_RTUPB sprintf ( disp_valblk, "Version %u", rsb->rsb$q_valblk[0] ); break; case 'I': if ( state_flags != 0 ) return SS$_IVLOCKID; sprintf ( disp_rdb_info, "I %-25s %u", "Remote monitor", *w1 ); // LOCK$K_MONID break; case 'J': sprintf ( disp_rdb_info, "J %-25s %08X", "AIJ backup", *l1 ); // LOCK$K_BCKAIJ ? break; case 'K': sprintf ( disp_rdb_info, "K %-25s", "Database key scope" ); // LOCK$K_DBK_SCOPE break; case 'N': sprintf ( disp_rdb_info, "N %-25s", "ACTIVE bitmap" ); // LOCK$K_ACTIVE sprintf ( disp_valblk, "Version %u", rsb->rsb$q_valblk[0] ); break; case 'O': sprintf ( disp_rdb_info, "O %-25s %u", "Database close", *w1 ); // LOCK$K_CLOSE break; case 'Q': sprintf ( disp_rdb_info, "Q %-25s", "Quiet" ); // LOCK$K_QUIET break; case 'R': sprintf ( disp_rdb_info, "R %-25s %u", "SEQBLK block", *w1 ); // LOCK$K_SEQBLK sprintf ( disp_valblk, "Version %u Next_Seq %08X.%08X (%u:%u)", rsb->rsb$q_valblk[0], rsb->rsb$q_valblk[2], rsb->rsb$q_valblk[1], rsb->rsb$q_valblk[2], rsb->rsb$q_valblk[1] ); break; case 'S': sprintf ( disp_rdb_info, "S %-25s %u", "Area", *w1 ); // LOCK$K_STAREA break; case 'U': init_desc ( buf_desc, 80, tmpbuf ); // LOCK$K_CLIENT c = (char *)&rsb->rsb$t_resnam[4]; status = sys$fao ( &fao_desc, (unsigned short *)&buflen, &buf_desc, rsb->rsb$b_rsnlen-4, c ); if ( !$VMS_STATUS_SUCCESS (status) ) return status; tmpbuf[buflen] = '\0'; pos = buflen; for ( i=rsb->rsb$b_rsnlen-1; i>3; i-- ) { b = (int8 *) & rsb->rsb$t_resnam[i]; pos += sprintf ( &tmpbuf[pos], "%02X", *b ); } if ( *l1 < 30 ) { p1 = (int *)&rsb->rsb$t_resnam[8]; sprintf ( disp_rdb_info, "U %-25s %s %s %d", "Client", tmpbuf, type_fields[*l1], *p1 ); } else sprintf ( disp_rdb_info, "U %-25s %s", "Client", tmpbuf ); sprintf ( disp_valblk, "Version %u", rsb->rsb$q_valblk[0] ); break; case 'V': sprintf ( disp_rdb_info, "V %-25s", "Cluster membership" ); // LOCK$K_MEMBIT break; case 'W': sprintf ( disp_rdb_info, "W %-25s", "Database access" ); // LOCK$K_ACCESS break; case 'X': sprintf ( disp_rdb_info, "X %-25s", "nowait signal" ); // LOCK$K_NOWAIT break; case 'Y': sprintf ( disp_rdb_info, "Y %-25s", "KROOT block" ); // LOCK$K_KROOT sprintf ( disp_valblk, "Version %u", rsb->rsb$q_valblk[0] ); break; case 'Z': sprintf ( disp_rdb_info, "Z %-25s", "Bugcheck" ); // LOCK$K_BUGCHK sprintf ( disp_valblk, "PID %08X", rsb->rsb$q_valblk[0] ); break; case 'a': sprintf ( disp_rdb_info, "a %-25s %u", "AIJ journal open", *w1 ); // LOCK$K_AIJOPEN break; case 'b': sprintf ( disp_rdb_info, "b %-25s", "Corrupt page table" ); // LOCK$K_CPT break; case 'c': sprintf ( disp_rdb_info, "c %-25s", "Global checkpoint" ); // LOCK$K_GBL_CKPT break; case 'd': sprintf ( disp_rdb_info, "d %-25s", "Snap truncation L1" ); // LOCK$K_RO_L1 break; case 'e': sprintf ( disp_rdb_info, "e %-25s", "Snap truncation L2" ); // LOCK$K_RW_L2 break; case 'f': sprintf ( disp_rdb_info, "f %-25s", "AIJ journal control" ); // LOCK$K_AIJDB sprintf ( disp_valblk, "Version %u", rsb->rsb$q_valblk[0] ); break; case 'g': sprintf ( disp_rdb_info, "g %-25s %u", "AIJ journal info", *w1 ); // LOCK$K_AIJFB break; case 'h': sprintf ( disp_rdb_info, "h %-25s", "Fast incremental backup" ); // LOCK$K_FIB break; case 'j': sprintf ( disp_rdb_info, "j %-25s", "AIJ journal switch" ); // LOCK$K_AIJSWITCH break; case 'k': sprintf ( disp_rdb_info, "k %-25s", "Utility client" ); // LOCK$K_RMUCLIENT sprintf ( disp_valblk, "Version %u", rsb->rsb$q_valblk[0] ); break; case 'l': sprintf ( disp_rdb_info, "l %-25s", "AIJ log shipping" ); // LOCK$K_AIJLOGSHIP break; case 'm': sprintf ( disp_rdb_info, "m %-25s %u", "Record cache entry", *w1 ); // LOCK$K_RCACHE sprintf ( disp_valblk, "Version %u", rsb->rsb$q_valblk[0] ); break; case 'n': sprintf ( disp_rdb_info, "n %-25s", "AIJ log message" ); // LOCK$K_AIJLOGMSG sprintf ( disp_valblk, "MSN %u. PID %08X", rsb->rsb$q_valblk[0], rsb->rsb$q_valblk[1] ); break; case 'o': sprintf ( disp_rdb_info, "o %-25s %u", "RUJBLK entry", *w1 ); // LOCK$K_RUJBLK break; case 'q': sprintf ( disp_rdb_info, "q %-25s", "ALS activation" ); // LOCK$K_ALS break; case 'r': sprintf ( disp_rdb_info, "r %-25s", "Release areas" ); // LOCK$K_REL_AREAS break; case 's': sprintf ( disp_rdb_info, "s %-25s %d", "Global BPT slot", *l1 ); // LOCK$K_GBPT_SLOT break; case 't': sprintf ( disp_rdb_info, "t %-25s %u", "Channel", *w1 ); // LOCK$K_CHAN break; case 'u': sprintf ( disp_rdb_info, "u %-25s", "Utility" ); // LOCK$K_UTILITY break; case 'v': sprintf ( disp_rdb_info, "v %-25s", "Dashboard" ); // LOCK$K_DASHBOARD break; case 'w': sprintf ( disp_rdb_info, "w %-25s", "Database recovery" ); // LOCK$K_DBR break; case 'x': sprintf ( disp_rdb_info, "x %-25s", "Client sequence block" ); // LOCK$K_CLTSEQ break; case 'y': sprintf ( disp_rdb_info, "y %-25s", "AIP larea-synch" ); // LOCK$K_AIPQHD break; case 'z': c = (char *)&rsb->rsb$t_resnam[4]; sprintf ( disp_rdb_info, "z %-25s %1s", "RCS submit/request", c ); // LOCK$K_RCSREQUEST break; case '[': l2 = (int *)&rsb->rsb$t_resnam[8]; sprintf ( disp_rdb_info, "[ %-25s Id %u Pid %08X", "RCS synch request", *l1, *l2 ); // LOCK$K_RCSWAITRQST sprintf ( disp_valblk, "RCS_Waitrqst_Status %08X", rsb->rsb$q_valblk[0] ); break; case 92: sprintf ( disp_rdb_info, "\\ %-25s", "Statistics request" ); // LOCK$K_STATRQST sprintf ( disp_valblk, "Issue_PID %08X Map_Gblsec %02X Req_PID %08X Req_STID %08X", rsb->rsb$q_valblk[2], rsb->rsb$q_valblk[3], rsb->rsb$q_valblk[0], rsb->rsb$q_valblk[1] ); break; case ']': sprintf ( disp_rdb_info, "] %-25s %08X", "AIJ backup suspend", *l1 ); // LOCK$K_BCKAIJ_SPD ? break; case '^': sprintf ( disp_rdb_info, "^ %-25s", "AIJ high water mark" ); // LOCK$K_AIJHWM break; case 95: sprintf ( disp_rdb_info, "_ %-25s", "Row cache GRIC request" ); // LOCK$K_REL_GRIC_REQST break; case 96: sprintf ( disp_rdb_info, "` %-25s", "Row Cache GRIC complete" ); // LOCK$K_REL_GRIC_CMPLT break; default: sprintf ( disp_rdb_info, "? %-25s %08X", "?????", *p ); break; } // // Clear the value block info, if the lock value block was not valid // if ( rsb->rsb$v_valinvld ) disp_valblk[0] = '\0'; // // if we passed in a LKB save interesting pieces of the lock // for later display // if ( flag ) { disp_lockid = lkb->lkb$l_lkid; disp_parid = p1_lkb->lkb$l_lkid; disp_pid = lkb->lkb$l_pid; sprintf ( disp_lck_queue, "%2s %2s %-7s", rq_gr_string[lkb->lkb$b_grmode], rq_gr_string[lkb->lkb$b_rqmode], queue_string[(int8)lkb->lkb$b_state+1] ); } // // and also find the filename of the Rdb database // rdb$disp_db ( p1_rsb->rsb$t_resnam ); return SS$_NORMAL; } // // if the parent resource wasn't a database lock, then we are at // least a level deeper, but if there is no grand parent, this // ain't going to be a Rdb resource // if ( p1_rsb->rsb$q_parent == 0 ) return SS$_IVLOCKID; // // Keep some pointers to part of the parent resourcename // p1 = (int *)&p1_rsb->rsb$t_resnam; w2 = (int16 *)&p1_rsb->rsb$t_resnam[4]; // // Read the grand parent resource into local memory // status = sda$trymem ( (VOID_PQ) p1_rsb->rsb$q_parent, p2_rsb, RSB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return status; if ( (p1_rsb->rsb$b_rsnlen == 6) && (*p1 == 'S') ) { switch ( *p ) { case 'B': // LOCK$K_LAREA sprintf ( disp_rdb_info, "B %-25s %u:%u", "Logical area", *w2, *w1 ); // sarea:larea break; case 'L': // LOCK$K_PLN l1 = (int *) &rsb->rsb$t_resnam[6]; w3 = (int16 *) &rsb->rsb$t_resnam[10]; w4 = (int16 *) &rsb->rsb$t_resnam[4]; sprintf ( disp_rdb_info, "L %-25s %d:%d:%d sarea %d", "Record", *w3, *l1, *w4, *w2 ); // dbid:pno:lno sarea break; case 'P': // LOCK$K_PNO l1 = (int *) &rsb->rsb$t_resnam[4]; sprintf ( disp_rdb_info, "P %-25s %d:%d", "Page", *w2, *l1 ); // sarea:pno break; case 'p': sprintf ( disp_rdb_info, "p %-25s %d:%d", "Page owner", *w2, *w1 ); // LOCK$K_UNUSED break; default: sprintf ( disp_rdb_info, "? %-25s %08X", "?????", *p ); break; } // // if we passed in a LKB save interesting pieces of the lock // for later display // if ( flag ) { disp_lockid = lkb->lkb$l_lkid; disp_parid = p1_lkb->lkb$l_lkid; disp_pid = lkb->lkb$l_pid; sprintf ( disp_lck_queue, "%2s %2s %-7s", rq_gr_string[lkb->lkb$b_grmode], rq_gr_string[lkb->lkb$b_rqmode], queue_string[(int8)lkb->lkb$b_state+1] ); } // // and also find the filename of the Rdb database // rdb$disp_db ( p2_rsb->rsb$t_resnam ); return SS$_NORMAL; } // // if the grand parent resource wasn't a S-lock, then we are even // deeper, but if there is no grand grand parent, this ain't going // to be a Rdb resource // if ( p2_rsb->rsb$q_parent == 0 ) return SS$_IVLOCKID; // // Read the grand grand parent resource into local memory // status = sda$trymem ( (VOID_PQ) p2_rsb->rsb$q_parent, p3_rsb, RSB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return status; if ( (p1_rsb->rsb$b_rsnlen == 6) && ((*p1 == 'B') || (*p1 == 'p')) ) { switch ( *p ) { case 'L': // LOCK$K_PLN l1 = (int *) &rsb->rsb$t_resnam[6]; w3 = (int16 *) &rsb->rsb$t_resnam[10]; w4 = (int16 *) &p2_rsb->rsb$t_resnam[4]; sprintf ( disp_rdb_info, "L %-25s %d:%d:%d sarea %d", "Record", *w3, *l1, *w1, *w4 ); // dbid:pno:lno sarea break; case 'P': // LOCK$K_PNO l1 = (int *) &rsb->rsb$t_resnam[4]; sprintf ( disp_rdb_info, "P %-25s %d:%d", "Page", *w2, *l1 ); // sarea:pno break; default: sprintf ( disp_rdb_info, "? %-25s %08X", "?????", *p ); break; } // // if we passed in a LKB save interesting pieces of the lock // for later display // if ( flag ) { disp_lockid = lkb->lkb$l_lkid; disp_parid = p1_lkb->lkb$l_lkid; disp_pid = lkb->lkb$l_pid; sprintf ( disp_lck_queue, "%2s %2s %-7s", rq_gr_string[lkb->lkb$b_grmode], rq_gr_string[lkb->lkb$b_rqmode], queue_string[(int8)lkb->lkb$b_state+1] ); } // // and also find the filename of the Rdb database // rdb$disp_db ( p3_rsb->rsb$t_resnam ); return SS$_NORMAL; } // // We shouldn't end up here at all, but if we do print out some // debug info, because I just might have goofed and missed something // sda$print ( "*** p2 par !@XQ", &p2_rsb->rsb$q_parent ); sda$print ( "*** lkb_ptr !@XQ", &lkb_ptr ); if ( flag ) sda$print ( "**** !XL lockid !XL", *p, lkb->lkb$l_lkid ); return SS$_IVLOCKID; } /////////////////////////////////////////////////////////////////////////////// // // This routine walks through all locks on any of the RSB state queues // (granted, conversion or waiting) and display relevant information. // void process_queues ( int queue_type, uint64 queue_head ) { uint64 temp; int status; int display_flag; char nodename[20]; uint64 lkb_ptr; // // Mark the queue header // temp = queue_head; while ( 1 ) { // // Get address of queue entry and return if we're done // status = sda$trymem ( (VOID_PQ) temp, &temp, 8); if ( !$VMS_STATUS_SUCCESS (status) ) return; if ( temp == queue_head ) return; // // Need to subtract the state queue flink offset to get to the LKB // address, then read the lock block into local memory // lkb_ptr = temp - (uint64)&((LKB$R_LKB *)0)->lkb$q_sqfl; status = sda$trymem ( (VOID_PQ) lkb_ptr, lkb, LKB$K_LENGTH); if ( !$VMS_STATUS_SUCCESS (status) ) return; // // For the granted lock blank out the requested mode, and for the // waiting lock blank out the granted mode, so we do not display // false information; this also makes for better readability // switch ( queue_type ) { case LKB$K_GRANTED: lkb->lkb$b_rqmode = LCK$K_EXMODE + 1; break; case LKB$K_CONVERT: break; case LKB$K_WAITING: lkb->lkb$b_grmode = LCK$K_EXMODE + 1; break; default: break; } // // Lookup the nodename for the display, and if are mastering // the lock, zero out the CSID. // if ( !lkb->lkb$v_mstcpy ) lkb->lkb$l_csid = 0; lookup_node ( lkb->lkb$l_csid, 0, nodename ); // // Display the relevant lock information // sda$print ( "!76* !XL.!XL !XL !6AZ !XL !2AZ !2AZ !7AZ", (uint64)lkb_ptr>>32, (uint64)lkb_ptr & 0xffffffff, lkb->lkb$l_pid, nodename, lkb->lkb$l_lkid, rq_gr_string[lkb->lkb$b_grmode], rq_gr_string[lkb->lkb$b_rqmode], queue_string[queue_type+1] ); } return; } /////////////////////////////////////////////////////////////////////////////// // // For a given CSID, CSB or CDT, find and decode it into a verbose // nodename. // void lookup_node ( int csid, int csb_cdt, void *nodename ) { int status; int i; uint64 temp; char *ch; static char tmpbuf[20]; static char tmpbuf2[20]; static VOID_PQ scs$gb_nodename; static uint64 clu$gl_clusvec = 0; static int prev_csid = -1; static int prev_csb_cdt = -1; static int flag2 = FALSE; // // Check if we passed in either a CDT or CSB // if ( csb_cdt == 0 ) { // // Translate a given CSID to a nodename, a zero CSID means the // local nodename. For an optimization, if we asked for this // same csid last time, just return the nodename we saved during // the last round. // if ( prev_csid == csid ) { strcpy ( nodename, tmpbuf ); return; } // // Save the csid for the optimization check. Assume invalid // nodename. // prev_csid = csid; strcpy ( nodename, "??????" ); // // Check if local or remote node // if ( csid == 0 ) { // // Grab the local nodename out of SCS$GB_NODENAME, and make it // a zero terminated string. // if ( scs$gb_nodename == NULL ) sda$symbol_value ( "SCS$GB_NODENAME", (uint64 *)&scs$gb_nodename ); status = sda$trymem ( scs$gb_nodename, tmpbuf, 16); if ( !$VMS_STATUS_SUCCESS (status) ) return; ch = strchr ( tmpbuf, ' ' ); if ( ch != NULL ) *ch = '\0'; strcpy ( nodename, tmpbuf ); } else { // // This is a remote node, so grab the index into the cluster // vector to find the CSB address. Then grab the pointer to // the system block (SB), and take the nodename from there. // i = (csid & 0x0000ffff) * 4; if ( clu$gl_clusvec == 0 ) { sda$symbol_value ( "CLU$GL_CLUSVEC", (uint64 *)&clu$gl_clusvec ); status = sda$trymem ( (VOID_PQ) clu$gl_clusvec, &clu$gl_clusvec, 4); if ( !$VMS_STATUS_SUCCESS (status) ) return; } temp = i + clu$gl_clusvec; status = sda$trymem ( (VOID_PQ) temp, &temp, 4); if ( !$VMS_STATUS_SUCCESS (status) ) return; temp = temp + (uint64)&((CSB *)0)->csb$l_sb; status = sda$trymem ( (VOID_PQ) temp, &temp, 4); if ( !$VMS_STATUS_SUCCESS (status) ) return; temp = temp + (uint64)&((SB *)0)->sb$t_nodename + 1; status = sda$trymem ( (VOID_PQ) temp, tmpbuf, 15); if ( !$VMS_STATUS_SUCCESS (status) ) return; strcpy ( nodename, tmpbuf ); } } else { // // Optimization check to see if we asked for the same CSB/CDT // last time through this. // if ( (prev_csb_cdt == csb_cdt) && flag2 ) { strcpy ( nodename, tmpbuf2 ); return; } // // Save the CSB/CDT for the optimization check, and assume invalid // nodename. // prev_csb_cdt = csb_cdt; flag2 = FALSE; strcpy ( nodename, "??????" ); // // Grab the size/type/subtype longword out of the structure, since // we need to figure out if we are dealing with a CSB or CDT // structure. // temp = csb_cdt + 8; status = sda$trymem ( (VOID_PQ) temp, &temp, 4); if ( !$VMS_STATUS_SUCCESS (status) ) return; temp = (temp >> 16) & 0xffff; i = (DYN$C_SCS_CDT<<8) | DYN$C_SCS; if ( temp == i ) { // // It's a CDT, so grab the CSB address out of CDT$L_AUXSTRUC // Note: we should probably check if CDT$L_LPROCNAM matches // 'VMS$VAXcluster', because CDT's can be re-used for shutdown // and reboots. // temp = csb_cdt + (uint64)&((CDT *)0)->cdt$l_auxstruc; status = sda$trymem ( (VOID_PQ) temp, &temp, 4); if ( !$VMS_STATUS_SUCCESS (status) ) return; if ( temp == 0 ) return; // closed connection } else { i = (DYN$C_CLU_CSB<<8) | DYN$C_CLU; if ( temp == i ) { // // This is already a CSB address // temp = csb_cdt; } else { // // Bogus structure. // return; } } // // Now we have a good CSB address, get the SB pointer // and grab the nodename from there. // temp = temp + (uint64)&((CSB *)0)->csb$l_sb; status = sda$trymem ( (VOID_PQ) temp, &temp, 4); if ( !$VMS_STATUS_SUCCESS (status) ) return; temp = temp + (uint64)&((SB *)0)->sb$t_nodename + 1; status = sda$trymem ( (VOID_PQ) temp, tmpbuf2, 15); if ( !$VMS_STATUS_SUCCESS (status) ) return; strcpy ( nodename, tmpbuf2 ); flag2 = TRUE; } return; } /////////////////////////////////////////////////////////////////////////////// // // For a given volume lock find the corresponding device // void getdvi_by_vollck ( char *vollck, struct dsc$descriptor_s *device ) { int i; int status; int shdw_member; int devclass = DC$_DISK; int *p; IOSB iosb; ILE3 devscan_itm[2]; ILE3 dvi_itm[4]; char devlocknam[13]; static char prev_vollck[13]; static char devnam[64]; static struct dsc$descriptor_s devnam_desc; $DESCRIPTOR ( gdevnam, "*" ); GENERIC_64 context = {0}; unsigned short retlen; // // Return the string, if it is the same device again // if ( strncmp(prev_vollck,vollck,13) == 0 ) { *device = devnam_desc; return; } // // save the label away for next time // memcpy ( prev_vollck, vollck, 13 ); // // Init things for the device scan and $GETDVI call // init_desc ( devnam_desc, 64, devnam ); context.gen64$q_quadword = 0; devscan_itm[0].ile3$w_length = 4; devscan_itm[0].ile3$w_code = DVS$_DEVCLASS; devscan_itm[0].ile3$ps_bufaddr = &devclass; devscan_itm[0].ile3$ps_retlen_addr = 0; devscan_itm[1].ile3$w_length = 0; devscan_itm[1].ile3$w_code = 0; dvi_itm[0].ile3$w_length = 13; dvi_itm[0].ile3$w_code = DVI$_DEVLOCKNAM; dvi_itm[0].ile3$ps_bufaddr = &devlocknam; dvi_itm[0].ile3$ps_retlen_addr = 0; dvi_itm[1].ile3$w_length = 4; dvi_itm[1].ile3$w_code = DVI$_SHDW_MEMBER; dvi_itm[1].ile3$ps_bufaddr = &shdw_member; dvi_itm[1].ile3$ps_retlen_addr = 0; dvi_itm[2].ile3$w_length = 0; dvi_itm[2].ile3$w_code = 0; // // Loop through all devices to find the correct volume // while ( 1 ) { status = sys$device_scan ( &devnam_desc, &retlen, &gdevnam, &devscan_itm, &context ); if ( status == SS$_NOMOREDEV ) return; if ( !$VMS_STATUS_SUCCESS (status) ) return; status = sys$getdviw ( 0, 0, &devnam_desc, &dvi_itm, &iosb, 0, 0, 0 ); if ( !$VMS_STATUS_SUCCESS (status) ) return; if ( !$VMS_STATUS_SUCCESS (iosb.iosb$w_status) ) return; if ( (shdw_member == 0) && (devlocknam[0] == '\02') ) { if ( strncmp(vollck,devlocknam,13) == 0 ) { *device = devnam_desc; return; } } } return; } /////////////////////////////////////////////////////////////////////////////// // // This routine displays information about Rdb resource contentions // void rdb$show_contention() { int status; uint64 temp; unsigned int cvt_op = LIB$K_DELTA_SECONDS_F; float interval; struct dsc$descriptor_s buf_desc; static char buf[64]; static int buflen; // // Make sure we do not work on a dumpfile // if ( !current ) { sda$print ( "This command is only possible on a running system" ); return; } // // Make sure the timer is not running // status = sys$cantim ( RDB_REQIDT, 0 ); // // Fetch the interval value, which can be a fraction of seconds // status = cli$present ( &interval_desc ); if ( status == CLI$_PRESENT ) { init_desc ( buf_desc, sizeof(buf), buf ); status = cli$get_value ( &interval_desc, &buf_desc, &buflen ); if ( !$VMS_STATUS_SUCCESS (status) ) return; buf[buflen] = '\0'; // // Decode the passed PID into a numeric value // status = sscanf (buf, "%f", &interval ); if ( status != 1 ) { sda$print ( "%RDB$SDA-W-INVVAL, invalid interval specified" ); return; } } // // Convert it to an internal 64-bit time // status = lib$cvtf_to_internal_time ( &cvt_op, &interval, (UINT64_PQ) &rdb_interval_time ); if ( !$VMS_STATUS_SUCCESS (status) ) return; // // Grab the first LKB on the lock timeout queue // status = sda$trymem ( (VOID_PQ) lck$gq_timoutq, &timoutq_lkb, 8 ); if ( !$VMS_STATUS_SUCCESS (status) ) return; // // Setup title and page heading // sda$format_heading ( "Rdb Contention Information:" ); sda$set_heading_routine ( contention_rdb_title ); sda$new_page (); // // Now fire the timer to do the real work // status = sys$setimr ( 0, &rdb_interval_time, rdb$check_timoutq, RDB_REQIDT, 0 ); if ( !$VMS_STATUS_SUCCESS (status) ) return; return; } /////////////////////////////////////////////////////////////////////////////// // // This routine will monitor the lock timeout queue // void rdb$check_timoutq() { int status; uint64 temp; // // grab the head of the lock timeout queue // status = sda$trymem ( (VOID_PQ) lck$gq_timoutq, &temp, 8 ); if ( !$VMS_STATUS_SUCCESS (status) ) return; // // skip, if queue is empty // if ( temp != (uint64)lck$gq_timoutq ) { // // only interested if the same LKB is still there. If so, check // if it is a Rdb lock and if true decode and display results // if ( timoutq_lkb == temp ) { temp = temp - (uint64)&((LKB$R_LKB *)0)->lkb$q_timoutqfl; status = decode_rdb_lock ( (LKB_PQ) temp, 0 ); if ( $VMS_STATUS_SUCCESS (status) ) sda$print ( "!6%D !8%T !XL.!XL !XL !XL !XL !AZ !AZ", 0, 0, disp_lkb>>32, disp_lkb & 0xffffffff, disp_lockid, disp_parid, disp_pid, disp_lck_queue, disp_rdb_info ); sda$set_line_count ( 0 ); } else timoutq_lkb = temp; } // // Fire another timer // status = sys$setimr ( 0, &rdb_interval_time, rdb$check_timoutq, RDB_REQIDT, 0 ); if ( !$VMS_STATUS_SUCCESS (status) ) return; return; } /////////////////////////////////////////////////////////////////////////////// // // This routine will stop any possible running timers // void rdb$stop() { int status; status = sys$cantim ( RDB_REQIDT, 0 ); return; } /////////////////////////////////////////////////////////////////////////////// // // This routine will count the number of locks on a resource tree // void tree_size ( uint64 queue_head, int *lckcnt ) { uint64 temp; int status; uint64 rsb_lckcnt_ptr; int cnt; // // Mark the queue header // temp = queue_head; // // Assume we did not find a master copy block // *lckcnt = 0; while ( 1 ) { // // Get address of queue entry and return if we're done // status = sda$trymem ( (VOID_PQ) temp, &temp, 8); if ( !$VMS_STATUS_SUCCESS (status) ) return; if ( temp == queue_head ) return; // // Need to subtract the state queue flink offset to get to the LKB // address, then read the lock block into local memory // rsb_lckcnt_ptr = ( temp - (uint64)&((RSB *)0)->rsb$q_srsfl ) + (uint64)&((RSB *)0)->rsb$w_lckcnt; status = sda$trymem ( (VOID_PQ) rsb_lckcnt_ptr, &cnt, 4); if ( !$VMS_STATUS_SUCCESS (status) ) return; *lckcnt += (cnt & 0xffff); } return; }