/* * PROGRAM: JRD Lock Manager * MODULE: print.c * DESCRIPTION: Lock Table printer * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. * * 2002.10.27 Sean Leyne - Completed removal of obsolete "DELTA" port * 2002.10.27 Sean Leyne - Code Cleanup, removed obsolete "Ultrix" port * * 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "SGI" port * * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port * */ #include "firebird.h" #include "../jrd/ib_stdio.h" #include #include #include "../jrd/common.h" #include "../jrd/file_params.h" #include "../jrd/jrd.h" #include "../jrd/lck.h" #include "../jrd/isc.h" #include "../jrd/jrd_time.h" #include "../lock/lock.h" #include "../jrd/gdsassert.h" #include "../jrd/gds_proto.h" #include "../jrd/isc_proto.h" #include "../jrd/isc_s_proto.h" #ifdef HAVE_UNISTD_H #include #endif #include #if !(defined WIN_NT) #include #endif #if (defined WIN_NT) #include #include #include #endif #ifndef FPRINTF #define FPRINTF ib_fprintf #endif typedef IB_FILE *OUTFILE; #define SW_I_ACQUIRE 1 #define SW_I_OPERATION 2 #define SW_I_TYPE 4 #define SW_I_WAIT 8 struct waitque { USHORT waitque_depth; PTR waitque_entry[30]; }; static void prt_lock_activity(OUTFILE, const lhb*, USHORT, USHORT, USHORT); static void prt_lock_init(void); static void prt_history(OUTFILE, const lhb*, PTR, const SCHAR*); static void prt_lock(OUTFILE, const lhb*, LBL, USHORT); static void prt_owner(OUTFILE, const lhb*, OWN, bool, bool); static void prt_owner_wait_cycle(OUTFILE, const lhb*, OWN, USHORT, struct waitque*); static void prt_request(OUTFILE, const lhb*, LRQ); static void prt_que(OUTFILE, const lhb*, const SCHAR*, const srq*, USHORT); static void prt_que2(OUTFILE, const lhb*, const SCHAR*, const srq*, USHORT); static const TEXT history_names[][10] = { "n/a", "ENQ", "DEQ", "CONVERT", "SIGNAL", "POST", "WAIT", "DEL_PROC", "DEL_LOCK", "DEL_REQ", "DENY", "GRANT", "LEAVE", "SCAN", "DEAD", "ENTER", "BUG", "ACTIVE", "CLEANUP", "DEL_OWNER" }; static const UCHAR compatibility[] = { /* Shared Prot Shared Prot none null Read Read Write Write Exclusive */ /* none */ 1, 1, 1, 1, 1, 1, 1, /* null */ 1, 1, 1, 1, 1, 1, 1, /* SR */ 1, 1, 1, 1, 1, 1, 0, /* PR */ 1, 1, 1, 1, 0, 0, 0, /* SW */ 1, 1, 1, 0, 1, 0, 0, /* PW */ 1, 1, 1, 0, 0, 0, 0, /* EX */ 1, 1, 0, 0, 0, 0, 0 }; #define COMPATIBLE(st1, st2) compatibility [st1 * LCK_max + st2] int CLIB_ROUTINE main( int argc, char *argv[]) { /************************************** * * m a i n * ************************************** * * Functional description * Check switches passed in and prepare to dump the lock table * to ib_stdout. * **************************************/ #ifdef SERVICE_REDIRECT SLONG redir_in; SLONG redir_out; SLONG redir_err; #endif OUTFILE outfile = ib_stdout; /* Perform some special handling when run as an Interbase service. The first switch can be "-svc" (lower case!) or it can be "-svc_re" followed by 3 file descriptors to use in re-directing ib_stdin, ib_stdout, and ib_stderr. */ if (argc > 1 && !strcmp(argv[1], "-svc")) { argv++; argc--; } // // BRS: 15-Sep-2003 // This code could not be used actually (see SVC_attach, comment by Dmitry) // Until a more detailed analysis is made it is preserved under an ifdef // #ifdef SERVICE_REDIRECT else if (argc > 4 && !strcmp(argv[1], "-svc_re")) { redir_in = atol(argv[2]); redir_out = atol(argv[3]); redir_err = atol(argv[4]); #ifdef WIN_NT redir_in = _open_osfhandle(redir_in, 0); redir_out = _open_osfhandle(redir_out, 0); redir_err = _open_osfhandle(redir_err, 0); #endif if (redir_in != 0) if (dup2((int) redir_in, 0)) close((int) redir_in); if (redir_out != 1) if (dup2((int) redir_out, 1)) close((int) redir_out); if (redir_err != 2) if (dup2((int) redir_err, 2)) close((int) redir_err); argv += 4; argc -= 4; } #endif //const int orig_argc = argc; //SCHAR** orig_argv = argv; /* Handle switches, etc. */ argv++; bool sw_consistency = false; bool sw_waitlist = false; bool sw_file = false; bool sw_requests = false; bool sw_locks = false; bool sw_history = false; bool sw_nobridge = false; bool sw_owners = true; USHORT sw_interactive; // Those variables should be signed to accept negative values from atoi SSHORT sw_series; SSHORT sw_intervals; SSHORT sw_seconds; sw_series = sw_interactive = sw_intervals = sw_seconds = 0; TEXT* lock_file = NULL; while (--argc) { SCHAR* p = *argv++; if (*p++ != '-') { FPRINTF(outfile, "Valid switches are: -o, -p, -l, -r, -a, -h, -n, -s , -c, -i \n"); exit(FINI_OK); } SCHAR c; while (c = *p++) switch (c) { case 'o': case 'p': sw_owners = true; break; case 'c': sw_nobridge = true; sw_consistency = true; break; case 'l': sw_locks = true; break; case 'r': sw_requests = true; break; case 'a': sw_locks = true; sw_owners = true; sw_requests = true; sw_history = true; break; case 'h': sw_history = true; break; case 's': if (argc > 1) sw_series = atoi(*argv++); if (sw_series <= 0) { FPRINTF(outfile, "Please specify a positive value following option -s\n"); exit(FINI_OK); } --argc; break; case 'n': sw_nobridge = true; break; case 'i': while (c = *p++) switch (c) { case 'a': sw_interactive |= SW_I_ACQUIRE; break; case 'o': sw_interactive |= SW_I_OPERATION; break; case 't': sw_interactive |= SW_I_TYPE; break; case 'w': sw_interactive |= SW_I_WAIT; break; default: FPRINTF(outfile, "Valid interactive switches are: a, o, t, w\n"); exit(FINI_OK); break; } if (!sw_interactive) sw_interactive = (SW_I_ACQUIRE | SW_I_OPERATION | SW_I_TYPE | SW_I_WAIT); sw_nobridge = true; sw_seconds = sw_intervals = 1; if (argc > 1) { sw_seconds = atoi(*argv++); --argc; if (argc > 1) { sw_intervals = atoi(*argv++); --argc; } if (!(sw_seconds > 0) || (sw_intervals < 0)) { FPRINTF(outfile, "Please specify 2 positive values for option -i\n"); exit(FINI_OK); } } --p; break; case 'w': sw_nobridge = true; sw_waitlist = true; break; case 'f': sw_nobridge = true; sw_file = true; if (argc > 1) { lock_file = *argv++; --argc; } else { FPRINTF(outfile, "Usage: -f \n"); exit(FINI_OK); }; break; default: FPRINTF(outfile, "Valid switches are: -o, -p, -l, -r, -a, -h, -n, -s , -c, -i \n"); exit(FINI_OK); break; } } TEXT buffer[MAXPATHLEN]; if (!sw_file) { gds__prefix_lock(buffer, LOCK_FILE); lock_file = buffer; } SH_MEM_T shmem_data; #ifdef UNIX shmem_data.sh_mem_semaphores = 0; #endif SLONG LOCK_size_mapped = DEFAULT_SIZE; #ifdef UNIX LOCK_size_mapped = 0; /* Use length of existing segment */ #else LOCK_size_mapped = DEFAULT_SIZE; /* length == 0 not supported by all non-UNIX */ #endif ISC_STATUS_ARRAY status_vector; lhb* LOCK_header = (lhb*) ISC_map_file(status_vector, lock_file, (void (*)(void*, sh_mem*, int)) prt_lock_init, 0, -LOCK_size_mapped, /* Negative to NOT truncate file */ &shmem_data); TEXT expanded_lock_filename[MAXPATHLEN]; TEXT hostname[64]; sprintf(expanded_lock_filename, lock_file, ISC_get_host(hostname, sizeof(hostname))); /* Make sure the lock file is valid - if it's a zero length file we * can't look at the header without causing a BUS error by going * off the end of the mapped region. */ if (LOCK_header && shmem_data.sh_mem_length_mapped < (SLONG) sizeof(struct lhb)) { /* Mapped file is obviously too small to really be a lock file */ FPRINTF(outfile, "Unable to access lock table - file too small.\n%s\n", expanded_lock_filename); exit(FINI_OK); } if (LOCK_header && LOCK_header->lhb_length > shmem_data.sh_mem_length_mapped) { #if (!(defined UNIX) || (defined HAVE_MMAP)) SLONG length = LOCK_header->lhb_length; LOCK_header = (lhb*) ISC_remap_file(status_vector, &shmem_data, length, FALSE); #endif } if (!LOCK_header) { FPRINTF(outfile, "Unable to access lock table.\n%s\n", expanded_lock_filename); gds__print_status(status_vector); exit(FINI_OK); } LOCK_size_mapped = shmem_data.sh_mem_length_mapped; /* if we can't read this version - admit there's nothing to say and return. */ if ((LOCK_header->lhb_version != SS_LHB_VERSION) && (LOCK_header->lhb_version != CLASSIC_LHB_VERSION)) { FPRINTF(outfile, "\tUnable to read lock table version %d.\n", LOCK_header->lhb_version); exit(FINI_OK); } /* Print lock activity report */ if (sw_interactive) { prt_lock_activity(outfile, LOCK_header, sw_interactive, (USHORT) sw_seconds, (USHORT) sw_intervals); exit(FINI_OK); } lhb* header = NULL; if (sw_consistency) { /* To avoid changes in the lock file while we are dumping it - make * a local buffer, lock the lock file, copy it, then unlock the * lock file to let processing continue. Printing of the lock file * will continue from the in-memory copy. */ header = (lhb*) gds__alloc(LOCK_header->lhb_length); if (!header) { FPRINTF(outfile, "Insufficient memory for consistent lock statistics.\n"); FPRINTF(outfile, "Try omitting the -c switch.\n"); exit(FINI_OK); } ISC_mutex_lock(LOCK_header->lhb_mutex); memcpy(header, LOCK_header, LOCK_header->lhb_length); ISC_mutex_unlock(LOCK_header->lhb_mutex); LOCK_header = header; } /* Print lock header block */ FPRINTF(outfile, "LOCK_HEADER BLOCK\n"); FPRINTF(outfile, "\tVersion: %d, Active owner: %6"SLONGFORMAT", Length: %6"SLONGFORMAT ", Used: %6"SLONGFORMAT"\n", LOCK_header->lhb_version, LOCK_header->lhb_active_owner, LOCK_header->lhb_length, LOCK_header->lhb_used); #ifdef MANAGER_PROCESS int manager_pid = 0; if (LOCK_header->lhb_manager) { OWN manager = (OWN) ABS_PTR(LOCK_header->lhb_manager); manager_pid = manager->own_process_id; } FPRINTF(outfile, "\tLock manager pid: %6d\n", manager_pid); #endif FPRINTF(outfile, "\tSemmask: 0x%"XLONGFORMAT", Flags: 0x%04X\n", LOCK_header->lhb_mask, LOCK_header->lhb_flags); FPRINTF(outfile, "\tEnqs: %6"ULONGFORMAT", Converts: %6"ULONGFORMAT ", Rejects: %6"ULONGFORMAT", Blocks: %6"ULONGFORMAT"\n", LOCK_header->lhb_enqs, LOCK_header->lhb_converts, LOCK_header->lhb_denies, LOCK_header->lhb_blocks); FPRINTF(outfile, "\tDeadlock scans: %6"ULONGFORMAT", Deadlocks: %6"ULONGFORMAT ", Scan interval: %3"ULONGFORMAT"\n", LOCK_header->lhb_scans, LOCK_header->lhb_deadlocks, LOCK_header->lhb_scan_interval); FPRINTF(outfile, "\tAcquires: %6"ULONGFORMAT", Acquire blocks: %6"ULONGFORMAT ", Spin count: %3"ULONGFORMAT"\n", LOCK_header->lhb_acquires, LOCK_header->lhb_acquire_blocks, LOCK_header->lhb_acquire_spins); if (LOCK_header->lhb_acquire_blocks) { float bottleneck = (float) ((100. * LOCK_header->lhb_acquire_blocks) / LOCK_header->lhb_acquires); FPRINTF(outfile, "\tMutex wait: %3.1f%%\n", bottleneck); } else FPRINTF(outfile, "\tMutex wait: 0.0%%\n"); SLONG hash_total_count = 0; SLONG hash_max_count = 0; SLONG hash_min_count = 10000000; const srq* slot; USHORT i = 0; for (slot = LOCK_header->lhb_hash; i < LOCK_header->lhb_hash_slots; slot++, i++) { SLONG hash_lock_count = 0; for (const srq* que = (SRQ) ABS_PTR(slot->srq_forward); que != slot; que = (SRQ) ABS_PTR(que->srq_forward)) { ++hash_total_count; ++hash_lock_count; } if (hash_lock_count < hash_min_count) hash_min_count = hash_lock_count; if (hash_lock_count > hash_max_count) hash_max_count = hash_lock_count; } FPRINTF(outfile, "\tHash slots: %4d, ", LOCK_header->lhb_hash_slots); FPRINTF(outfile, "Hash lengths (min/avg/max): %4"SLONGFORMAT"/%4"SLONGFORMAT "/%4"SLONGFORMAT"\n", hash_min_count, (hash_total_count / LOCK_header->lhb_hash_slots), hash_max_count); shb* a_shb = NULL; if (LOCK_header->lhb_secondary != LHB_PATTERN) { a_shb = (shb*) ABS_PTR(LOCK_header->lhb_secondary); FPRINTF(outfile, "\tRemove node: %6"SLONGFORMAT", Insert queue: %6"SLONGFORMAT ", Insert prior: %6"SLONGFORMAT"\n", a_shb->shb_remove_node, a_shb->shb_insert_que, a_shb->shb_insert_prior); } prt_que(outfile, LOCK_header, "\tOwners", &LOCK_header->lhb_owners, OFFSET(OWN, own_lhb_owners)); prt_que(outfile, LOCK_header, "\tFree owners", &LOCK_header->lhb_free_owners, OFFSET(OWN, own_lhb_owners)); prt_que(outfile, LOCK_header, "\tFree locks", &LOCK_header->lhb_free_locks, OFFSET(LBL, lbl_lhb_hash)); prt_que(outfile, LOCK_header, "\tFree requests", &LOCK_header->lhb_free_requests, OFFSET(LRQ, lrq_lbl_requests)); /* Print lock ordering option */ FPRINTF(outfile, "\tLock Ordering: %s\n", (LOCK_header-> lhb_flags & LHB_lock_ordering) ? "Enabled" : "Disabled"); FPRINTF(outfile, "\n"); /* Print known owners */ if (sw_owners) { const srq* que; #ifdef SOLARIS_MT /* The Lock Starvation recovery code on Solaris rotates the owner queue once per acquire. This makes it difficult to read the printouts when multiple runs are made. So we scan the list of owners once to find the lowest owner_id, and start the printout from there. */ PTR least_owner_id = 0x7FFFFFFF; const srq* least_owner_ptr = &LOCK_header->lhb_owners; QUE_LOOP(LOCK_header->lhb_owners, que) { OWN this_owner = (OWN) ((UCHAR*) que - OFFSET(OWN, own_lhb_owners)); if (REL_PTR(this_owner) < least_owner_id) { least_owner_id = REL_PTR(this_owner); least_owner_ptr = que; } } que = least_owner_ptr; do { if (que != &LOCK_header->lhb_owners) prt_owner(outfile, LOCK_header, (OWN) ((UCHAR*) que - OFFSET(OWN, own_lhb_owners)), sw_requests, sw_waitlist); que = QUE_NEXT((*que)); } while (que != least_owner_ptr); #else QUE_LOOP(LOCK_header->lhb_owners, que) { prt_owner(outfile, LOCK_header, (OWN) ((UCHAR*) que - OFFSET(OWN, own_lhb_owners)), sw_requests, sw_waitlist); } #endif /* SOLARIS_MT */ } /* Print known locks */ if (sw_locks || sw_series) for (slot = LOCK_header->lhb_hash, i = 0; i < LOCK_header->lhb_hash_slots; slot++, i++) { for (const srq* que = (SRQ) ABS_PTR(slot->srq_forward); que != slot; que = (SRQ) ABS_PTR(que->srq_forward)) { prt_lock(outfile, LOCK_header, (LBL) ((UCHAR *) que - OFFSET(LBL, lbl_lhb_hash)), sw_series); } } if (sw_history) prt_history(outfile, LOCK_header, LOCK_header->lhb_history, "History"); if (LOCK_header->lhb_secondary != LHB_PATTERN) prt_history(outfile, LOCK_header, a_shb->shb_history, "Event log"); if (header) gds__free(header); exit(FINI_OK); return (FINI_OK); // make compiler happy } static void prt_lock_activity( OUTFILE outfile, const lhb* header, USHORT flag, USHORT seconds, USHORT intervals) { /************************************** * * p r t _ l o c k _ a c t i v i t y * ************************************** * * Functional description * Print a time-series lock activity report * **************************************/ struct tm d; ULONG i; time_t clock = time(NULL); d = *localtime(&clock); FPRINTF(outfile, "%02d:%02d:%02d ", d.tm_hour, d.tm_min, d.tm_sec); if (flag & SW_I_ACQUIRE) FPRINTF(outfile, "acquire/s acqwait/s %%acqwait acqrtry/s rtrysuc/s "); if (flag & SW_I_OPERATION) FPRINTF(outfile, "enqueue/s convert/s downgrd/s dequeue/s readata/s wrtdata/s qrydata/s "); if (flag & SW_I_TYPE) FPRINTF(outfile, " dblop/s rellop/s pagelop/s tranlop/s relxlop/s idxxlop/s misclop/s "); if (flag & SW_I_WAIT) { FPRINTF(outfile, " wait/s reject/s timeout/s blckast/s dirsig/s indsig/s "); FPRINTF(outfile, " wakeup/s dlkscan/s deadlck/s avlckwait(msec)"); } FPRINTF(outfile, "\n"); lhb base = *header; lhb prior = *header; if (intervals == 0) memset(&base, 0, sizeof(base)); for (i = 0; i < intervals; i++) { #ifdef WIN_NT Sleep((DWORD) seconds * 1000); #else sleep(seconds); #endif clock = time(NULL); d = *localtime((time_t*)&clock); FPRINTF(outfile, "%02d:%02d:%02d ", d.tm_hour, d.tm_min, d.tm_sec); if (flag & SW_I_ACQUIRE) { FPRINTF(outfile, "%9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" %9"ULONGFORMAT" ", (header->lhb_acquires - prior.lhb_acquires) / seconds, (header->lhb_acquire_blocks - prior.lhb_acquire_blocks) / seconds, (header->lhb_acquires - prior.lhb_acquires) ? (100 * (header->lhb_acquire_blocks - prior.lhb_acquire_blocks)) / (header->lhb_acquires - prior.lhb_acquires) : 0, (header->lhb_acquire_retries - prior.lhb_acquire_retries) / seconds, (header->lhb_retry_success - prior.lhb_retry_success) / seconds); prior.lhb_acquires = header->lhb_acquires; prior.lhb_acquire_blocks = header->lhb_acquire_blocks; prior.lhb_acquire_retries = header->lhb_acquire_retries; prior.lhb_retry_success = header->lhb_retry_success; } if (flag & SW_I_OPERATION) { FPRINTF(outfile, "%9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" ", (header->lhb_enqs - prior.lhb_enqs) / seconds, (header->lhb_converts - prior.lhb_converts) / seconds, (header->lhb_downgrades - prior.lhb_downgrades) / seconds, (header->lhb_deqs - prior.lhb_deqs) / seconds, (header->lhb_read_data - prior.lhb_read_data) / seconds, (header->lhb_write_data - prior.lhb_write_data) / seconds, (header->lhb_query_data - prior.lhb_query_data) / seconds); prior.lhb_enqs = header->lhb_enqs; prior.lhb_converts = header->lhb_converts; prior.lhb_downgrades = header->lhb_downgrades; prior.lhb_deqs = header->lhb_deqs; prior.lhb_read_data = header->lhb_read_data; prior.lhb_write_data = header->lhb_write_data; prior.lhb_query_data = header->lhb_query_data; } if (flag & SW_I_TYPE) { FPRINTF(outfile, "%9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" ", (header->lhb_operations[LCK_database] - prior.lhb_operations[LCK_database]) / seconds, (header->lhb_operations[LCK_relation] - prior.lhb_operations[LCK_relation]) / seconds, (header->lhb_operations[LCK_bdb] - prior.lhb_operations[LCK_bdb]) / seconds, (header->lhb_operations[LCK_tra] - prior.lhb_operations[LCK_tra]) / seconds, (header->lhb_operations[LCK_rel_exist] - prior.lhb_operations[LCK_rel_exist]) / seconds, (header->lhb_operations[LCK_idx_exist] - prior.lhb_operations[LCK_idx_exist]) / seconds, (header->lhb_operations[0] - prior.lhb_operations[0]) / seconds); prior.lhb_operations[LCK_database] = header->lhb_operations[LCK_database]; prior.lhb_operations[LCK_relation] = header->lhb_operations[LCK_relation]; prior.lhb_operations[LCK_bdb] = header->lhb_operations[LCK_bdb]; prior.lhb_operations[LCK_tra] = header->lhb_operations[LCK_tra]; prior.lhb_operations[LCK_rel_exist] = header->lhb_operations[LCK_rel_exist]; prior.lhb_operations[LCK_idx_exist] = header->lhb_operations[LCK_idx_exist]; prior.lhb_operations[0] = header->lhb_operations[0]; } if (flag & SW_I_WAIT) { FPRINTF(outfile, "%9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" ", (header->lhb_waits - prior.lhb_waits) / seconds, (header->lhb_denies - prior.lhb_denies) / seconds, (header->lhb_timeouts - prior.lhb_timeouts) / seconds, (header->lhb_blocks - prior.lhb_blocks) / seconds, (header->lhb_direct_sigs - prior.lhb_direct_sigs) / seconds, (header->lhb_indirect_sigs - prior.lhb_indirect_sigs) / seconds, (header->lhb_wakeups - prior.lhb_wakeups) / seconds, (header->lhb_scans - prior.lhb_scans) / seconds, (header->lhb_deadlocks - prior.lhb_deadlocks) / seconds, (header->lhb_waits - prior.lhb_waits) ? (header->lhb_wait_time - prior.lhb_wait_time) / (header->lhb_waits - prior.lhb_waits) : 0); prior.lhb_waits = header->lhb_waits; prior.lhb_denies = header->lhb_denies; prior.lhb_timeouts = header->lhb_timeouts; prior.lhb_blocks = header->lhb_blocks; prior.lhb_direct_sigs = header->lhb_direct_sigs; prior.lhb_indirect_sigs = header->lhb_indirect_sigs; prior.lhb_wakeups = header->lhb_wakeups; prior.lhb_scans = header->lhb_scans; prior.lhb_deadlocks = header->lhb_deadlocks; prior.lhb_wait_time = header->lhb_wait_time; } FPRINTF(outfile, "\n"); } ULONG factor = seconds * intervals; if (factor < 1) factor = 1; FPRINTF(outfile, "\nAverage: "); if (flag & SW_I_ACQUIRE) { FPRINTF(outfile, "%9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" %9"ULONGFORMAT" ", (header->lhb_acquires - base.lhb_acquires) / (factor), (header->lhb_acquire_blocks - base.lhb_acquire_blocks) / (factor), (header->lhb_acquires - base.lhb_acquires) ? (100 * (header->lhb_acquire_blocks - base.lhb_acquire_blocks)) / (header->lhb_acquires - base.lhb_acquires) : 0, (header->lhb_acquire_retries - base.lhb_acquire_retries) / (factor), (header->lhb_retry_success - base.lhb_retry_success) / (factor)); } if (flag & SW_I_OPERATION) { FPRINTF(outfile, "%9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT" %9" ULONGFORMAT" ", (header->lhb_enqs - base.lhb_enqs) / (factor), (header->lhb_converts - base.lhb_converts) / (factor), (header->lhb_downgrades - base.lhb_downgrades) / (factor), (header->lhb_deqs - base.lhb_deqs) / (factor), (header->lhb_read_data - base.lhb_read_data) / (factor), (header->lhb_write_data - base.lhb_write_data) / (factor), (header->lhb_query_data - base.lhb_query_data) / (factor)); } if (flag & SW_I_TYPE) { FPRINTF(outfile, "%9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" ", (header->lhb_operations[LCK_database] - base.lhb_operations[LCK_database]) / (factor), (header->lhb_operations[LCK_relation] - base.lhb_operations[LCK_relation]) / (factor), (header->lhb_operations[LCK_bdb] - base.lhb_operations[LCK_bdb]) / (factor), (header->lhb_operations[LCK_tra] - base.lhb_operations[LCK_tra]) / (factor), (header->lhb_operations[LCK_rel_exist] - base.lhb_operations[LCK_rel_exist]) / (factor), (header->lhb_operations[LCK_idx_exist] - base.lhb_operations[LCK_idx_exist]) / (factor), (header->lhb_operations[0] - base.lhb_operations[0]) / (factor)); } if (flag & SW_I_WAIT) { FPRINTF(outfile, "%9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" %9"ULONGFORMAT" %9"ULONGFORMAT " %9"ULONGFORMAT" ", (header->lhb_waits - base.lhb_waits) / (factor), (header->lhb_denies - base.lhb_denies) / (factor), (header->lhb_timeouts - base.lhb_timeouts) / (factor), (header->lhb_blocks - base.lhb_blocks) / (factor), (header->lhb_direct_sigs - base.lhb_direct_sigs) / (factor), (header->lhb_indirect_sigs - base.lhb_indirect_sigs) / (factor), (header->lhb_wakeups - base.lhb_wakeups) / (factor), (header->lhb_scans - base.lhb_scans) / (factor), (header->lhb_deadlocks - base.lhb_deadlocks) / (factor), (header->lhb_waits - base.lhb_waits) ? (header->lhb_wait_time - base.lhb_wait_time) / (header->lhb_waits - base. lhb_waits) : 0); } FPRINTF(outfile, "\n"); } static void prt_lock_init(void) { /************************************** * * l o c k _ i n i t * ************************************** * * Functional description * Initialize a lock table to looking -- i.e. don't do * nuthin. * **************************************/ } static void prt_history( OUTFILE outfile, const lhb* LOCK_header, PTR history_header, const SCHAR* title) { /************************************** * * p r t _ h i s t o r y * ************************************** * * Functional description * Print history list of lock table. * **************************************/ FPRINTF(outfile, "%s:\n", title); for (HIS history = (HIS) ABS_PTR(history_header); true; history = (HIS) ABS_PTR(history->his_next)) { if (history->his_operation) FPRINTF(outfile, " %s:\towner = %6"ULONGFORMAT", lock = %6"ULONGFORMAT ", request = %6"ULONGFORMAT"\n", history_names[history->his_operation], history->his_process, history->his_lock, history->his_request); if (history->his_next == history_header) break; } } static void prt_lock( OUTFILE outfile, const lhb* LOCK_header, LBL lock, USHORT sw_series) { /************************************** * * p r t _ l o c k * ************************************** * * Functional description * Print a formatted lock block * **************************************/ if (sw_series && lock->lbl_series != sw_series) return; FPRINTF(outfile, "LOCK BLOCK %6"SLONGFORMAT"\n", REL_PTR(lock)); FPRINTF(outfile, "\tSeries: %d, Parent: %6"ULONGFORMAT ", State: %d, size: %d length: %d data: %"ULONGFORMAT"\n", lock->lbl_series, lock->lbl_parent, lock->lbl_state, lock->lbl_size, lock->lbl_length, lock->lbl_data); if (lock->lbl_length == 4) { SLONG key; UCHAR *p, *q, *end; p = (UCHAR *) & key; q = lock->lbl_key; for (end = q + 4; q < end; q++) *p++ = *q; FPRINTF(outfile, "\tKey: %06"SLONGFORMAT",", key); } else { UCHAR temp[512]; UCHAR* p = temp; const UCHAR* q = lock->lbl_key; const UCHAR* const end = q + lock->lbl_length; for (; q < end; q++) { UCHAR c = *q; if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '/') *p++ = c; else { sprintf((char*) p, "<%d>", c); while (*p) p++; } } *p = 0; FPRINTF(outfile, "\tKey: %s,", temp); } FPRINTF(outfile, " Flags: 0x%02X, Pending request count: %6d\n", lock->lbl_flags, lock->lbl_pending_lrq_count); prt_que(outfile, LOCK_header, "\tHash que", &lock->lbl_lhb_hash, OFFSET(LBL, lbl_lhb_hash)); prt_que(outfile, LOCK_header, "\tRequests", &lock->lbl_requests, OFFSET(LRQ, lrq_lbl_requests)); const srq* que; QUE_LOOP(lock->lbl_requests, que) { const lrq* request = (lrq*) ((UCHAR*) que - OFFSET(LRQ, lrq_lbl_requests)); FPRINTF(outfile, "\t\tRequest %6"SLONGFORMAT", Owner: %6"SLONGFORMAT ", State: %d (%d), Flags: 0x%02X\n", REL_PTR(request), request->lrq_owner, request->lrq_state, request->lrq_requested, request->lrq_flags); } FPRINTF(outfile, "\n"); } static void prt_owner(OUTFILE outfile, const lhb* LOCK_header, OWN owner, bool sw_requests, bool sw_waitlist) { /************************************** * * p r t _ o w n e r * ************************************** * * Functional description * Print a formatted owner block. * **************************************/ SRQ que; FPRINTF(outfile, "OWNER BLOCK %6"SLONGFORMAT"\n", REL_PTR(owner)); FPRINTF(outfile, "\tOwner id: %6"ULONGFORMAT ", type: %1d, flags: 0x%02X, pending: %6"SLONGFORMAT", semid: %6d ", owner->own_owner_id, owner->own_owner_type, (owner->own_flags | (UCHAR) owner->own_ast_flags), owner->own_pending_request, owner->own_semaphore & ~OWN_semavail); if (owner->own_semaphore & OWN_semavail) FPRINTF(outfile, "(available)\n"); else FPRINTF(outfile, "\n"); FPRINTF(outfile, "\tProcess id: %6d, UID: 0x%X %s\n", owner->own_process_id, owner->own_process_uid, ISC_check_process_existence(owner->own_process_id, owner->own_process_uid, FALSE) ? "Alive" : "Dead"); #ifdef SOLARIS_MT FPRINTF(outfile, "\tLast Acquire: %6d (%6d ago)", owner->own_acquire_time, LOCK_header->lhb_acquires - owner->own_acquire_time); #ifdef DEV_BUILD FPRINTF(outfile, " sec %9ld (%3d sec ago)", owner->own_acquire_realtime, time(NULL) - owner->own_acquire_realtime); #endif /* DEV_BUILD */ FPRINTF(outfile, "\n"); #endif /* SOLARIS_MT */ { UCHAR tmp; tmp = (owner->own_flags | (UCHAR) owner->own_ast_flags | (UCHAR) owner->own_ast_hung_flags); FPRINTF(outfile, "\tFlags: 0x%02X ", tmp); FPRINTF(outfile, " %s", (tmp & OWN_hung) ? "hung" : " "); FPRINTF(outfile, " %s", (tmp & OWN_blocking) ? "blkg" : " "); FPRINTF(outfile, " %s", (tmp & OWN_starved) ? "STRV" : " "); FPRINTF(outfile, " %s", (tmp & OWN_signal) ? "sgnl" : " "); FPRINTF(outfile, " %s", (tmp & OWN_wakeup) ? "wake" : " "); FPRINTF(outfile, " %s", (tmp & OWN_scanned) ? "scan" : " "); FPRINTF(outfile, "\n"); } prt_que(outfile, LOCK_header, "\tRequests", &owner->own_requests, OFFSET(LRQ, lrq_own_requests)); prt_que(outfile, LOCK_header, "\tBlocks", &owner->own_blocks, OFFSET(LRQ, lrq_own_blocks)); if (sw_waitlist) { struct waitque owner_list; owner_list.waitque_depth = 0; prt_owner_wait_cycle(outfile, LOCK_header, owner, 8, &owner_list); } FPRINTF(outfile, "\n"); if (sw_requests) QUE_LOOP(owner->own_requests, que) prt_request(outfile, LOCK_header, (LRQ) ((UCHAR *) que - OFFSET(LRQ, lrq_own_requests))); } static void prt_owner_wait_cycle( OUTFILE outfile, const lhb* LOCK_header, OWN owner, USHORT indent, struct waitque *waiters) { /************************************** * * p r t _ o w n e r _ w a i t _ c y c l e * ************************************** * * Functional description * For the given owner, print out the list of owners * being waited on. The printout is recursive, up to * a limit. It is recommended this be used with * the -c consistency mode. * **************************************/ USHORT i; for (i = indent; i; i--) FPRINTF(outfile, " "); /* Check to see if we're in a cycle of owners - this might be a deadlock, or might not, if the owners haven't processed their blocking queues */ for (i = 0; i < waiters->waitque_depth; i++) if (REL_PTR(owner) == waiters->waitque_entry[i]) { FPRINTF(outfile, "%6"SLONGFORMAT" (potential deadlock).\n", REL_PTR(owner)); return; }; FPRINTF(outfile, "%6"SLONGFORMAT" waits on ", REL_PTR(owner)); if (!owner->own_pending_request) FPRINTF(outfile, "nothing.\n"); else { SRQ que; LRQ owner_request; LBL lock; USHORT counter; bool owner_conversion; if (waiters->waitque_depth > FB_NELEM(waiters->waitque_entry)) { FPRINTF(outfile, "Dependency too deep\n"); return; }; waiters->waitque_entry[waiters->waitque_depth++] = REL_PTR(owner); FPRINTF(outfile, "\n"); owner_request = (LRQ) ABS_PTR(owner->own_pending_request); assert(owner_request->lrq_type == type_lrq); owner_conversion = (owner_request->lrq_state > LCK_null); lock = (LBL) ABS_PTR(owner_request->lrq_lock); assert(lock->lbl_type == type_lbl); counter = 0; QUE_LOOP(lock->lbl_requests, que) { OWN lock_owner; LRQ lock_request; if (counter++ > 50) { for (i = indent + 6; i; i--) FPRINTF(outfile, " "); FPRINTF(outfile, "printout stopped after %d owners\n", counter - 1); break; } lock_request = (LRQ) ((UCHAR *) que - OFFSET(LRQ, lrq_lbl_requests)); assert(lock_request->lrq_type == type_lrq); if (LOCK_header->lhb_flags & LHB_lock_ordering && !owner_conversion) { /* Requests AFTER our request can't block us */ if (owner_request == lock_request) break; if (COMPATIBLE(owner_request->lrq_requested, MAX(lock_request->lrq_state, lock_request->lrq_requested))) continue; } else { /* Requests AFTER our request CAN block us */ if (lock_request == owner_request) continue; if (COMPATIBLE (owner_request->lrq_requested, lock_request->lrq_state)) continue; }; lock_owner = (OWN) ABS_PTR(lock_request->lrq_owner); prt_owner_wait_cycle(outfile, LOCK_header, lock_owner, indent + 4, waiters); } waiters->waitque_depth--; } } static void prt_request(OUTFILE outfile, const lhb* LOCK_header, LRQ request) { /************************************** * * p r t _ r e q u e s t * ************************************** * * Functional description * Print a format request block. * **************************************/ FPRINTF(outfile, "REQUEST BLOCK %6"SLONGFORMAT"\n", REL_PTR(request)); FPRINTF(outfile, "\tOwner: %6"SLONGFORMAT", Lock: %6"SLONGFORMAT ", State: %d, Mode: %d, Flags: 0x%02X\n", request->lrq_owner, request->lrq_lock, request->lrq_state, request->lrq_requested, request->lrq_flags); FPRINTF(outfile, "\tAST: 0x%p, argument: 0x%p\n", request->lrq_ast_routine, request->lrq_ast_argument); prt_que2(outfile, LOCK_header, "\tlrq_own_requests", &request->lrq_own_requests, OFFSET(LRQ, lrq_own_requests)); prt_que2(outfile, LOCK_header, "\tlrq_lbl_requests", &request->lrq_lbl_requests, OFFSET(LRQ, lrq_lbl_requests)); prt_que2(outfile, LOCK_header, "\tlrq_own_blocks ", &request->lrq_own_blocks, OFFSET(LRQ, lrq_own_blocks)); FPRINTF(outfile, "\n"); } static void prt_que( OUTFILE outfile, const lhb* LOCK_header, const SCHAR* string, const srq* que, USHORT que_offset) { /************************************** * * p r t _ q u e * ************************************** * * Functional description * Print the contents of a self-relative que. * **************************************/ SLONG count, offset; SRQ next; offset = REL_PTR(que); if (offset == que->srq_forward && offset == que->srq_backward) { FPRINTF(outfile, "%s: *empty*\n", string); return; } count = 0; QUE_LOOP((*que), next) ++ count; FPRINTF(outfile, "%s (%ld):\tforward: %6"SLONGFORMAT ", backward: %6"SLONGFORMAT"\n", string, count, que->srq_forward - que_offset, que->srq_backward - que_offset); } static void prt_que2( OUTFILE outfile, const lhb* LOCK_header, const SCHAR* string, const srq* que, USHORT que_offset) { /************************************** * * p r t _ q u e 2 * ************************************** * * Functional description * Print the contents of a self-relative que. * But don't try to count the entries, as they might be invalid * **************************************/ SLONG offset = REL_PTR(que); if (offset == que->srq_forward && offset == que->srq_backward) { FPRINTF(outfile, "%s: *empty*\n", string); return; } FPRINTF(outfile, "%s:\tforward: %6"SLONGFORMAT ", backward: %6"SLONGFORMAT"\n", string, que->srq_forward - que_offset, que->srq_backward - que_offset); }