mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-28 02:43:03 +01:00
1234 lines
36 KiB
C++
1234 lines
36 KiB
C++
/*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#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 <unistd.h>
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
# include <sys/param.h>
|
|
#endif
|
|
|
|
#ifdef WIN_NT
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#ifndef FPRINTF
|
|
#define FPRINTF fprintf
|
|
#endif
|
|
|
|
typedef FILE* OUTFILE;
|
|
|
|
const USHORT SW_I_ACQUIRE = 1;
|
|
const USHORT SW_I_OPERATION = 2;
|
|
const USHORT SW_I_TYPE = 4;
|
|
const USHORT SW_I_WAIT = 8;
|
|
|
|
#define SRQ_BASE ((UCHAR*) LOCK_header)
|
|
|
|
struct waitque {
|
|
USHORT waitque_depth;
|
|
SRQ_PTR waitque_entry[30];
|
|
};
|
|
|
|
static void prt_lock_activity(OUTFILE, const lhb*, USHORT, USHORT, USHORT);
|
|
static void prt_lock_init(void*, sh_mem*, bool);
|
|
static void prt_history(OUTFILE, const lhb*, SRQ_PTR, const SCHAR*);
|
|
static void prt_lock(OUTFILE, const lhb*, LBL, USHORT);
|
|
static void prt_owner(OUTFILE, const lhb*, const own*, bool, bool);
|
|
static void prt_owner_wait_cycle(OUTFILE, const lhb*, const own*, USHORT, waitque*);
|
|
static void prt_request(OUTFILE, const lhb*, const 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 stdout.
|
|
*
|
|
**************************************/
|
|
OUTFILE outfile = 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 stdin, stdout, and stderr. */
|
|
|
|
if (argc > 1 && !strcmp(argv[1], "-svc")) {
|
|
argv++;
|
|
argc--;
|
|
}
|
|
else if (argc > 4 && !strcmp(argv[1], "-svc_re")) {
|
|
long redir_in = atol(argv[2]);
|
|
long redir_out = atol(argv[3]);
|
|
long 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;
|
|
}
|
|
//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 <n>, -c, -i <n> <n>\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 <filename>\n");
|
|
exit(FINI_OK);
|
|
};
|
|
break;
|
|
|
|
default:
|
|
FPRINTF(outfile,
|
|
"Valid switches are: -o, -p, -l, -r, -a, -h, -n, -s <n>, -c, -i <n> <n>\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,
|
|
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(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) SRQ_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"UQUADFORMAT", Converts: %6"UQUADFORMAT
|
|
", Rejects: %6"UQUADFORMAT", Blocks: %6"UQUADFORMAT"\n",
|
|
LOCK_header->lhb_enqs, LOCK_header->lhb_converts,
|
|
LOCK_header->lhb_denies, LOCK_header->lhb_blocks);
|
|
|
|
FPRINTF(outfile,
|
|
"\tDeadlock scans: %6"UQUADFORMAT", Deadlocks: %6"UQUADFORMAT
|
|
", Scan interval: %3"ULONGFORMAT"\n",
|
|
LOCK_header->lhb_scans, LOCK_header->lhb_deadlocks,
|
|
LOCK_header->lhb_scan_interval);
|
|
|
|
FPRINTF(outfile,
|
|
"\tAcquires: %6"UQUADFORMAT", Acquire blocks: %6"UQUADFORMAT
|
|
", 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) {
|
|
// CVC: MSVC up to v6 couldn't convert UINT64 to double.
|
|
const float bottleneck =
|
|
(float) ((100. * (SINT64) LOCK_header->lhb_acquire_blocks) /
|
|
(SINT64) 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;
|
|
USHORT i = 0;
|
|
for (const srq* slot = LOCK_header->lhb_hash; i < LOCK_header->lhb_hash_slots;
|
|
slot++, i++)
|
|
{
|
|
SLONG hash_lock_count = 0;
|
|
for (const srq* que_inst = (SRQ) SRQ_ABS_PTR(slot->srq_forward); que_inst != slot;
|
|
que_inst = (SRQ) SRQ_ABS_PTR(que_inst->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);
|
|
|
|
const shb* a_shb = NULL;
|
|
if (LOCK_header->lhb_secondary != LHB_PATTERN) {
|
|
a_shb = (shb*) SRQ_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_inst;
|
|
|
|
#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. */
|
|
SRQ_PTR least_owner_id = 0x7FFFFFFF;
|
|
const srq* least_owner_ptr = &LOCK_header->lhb_owners;
|
|
SRQ_LOOP(LOCK_header->lhb_owners, que_inst) {
|
|
OWN this_owner = (OWN) ((UCHAR*) que_inst - OFFSET(OWN, own_lhb_owners));
|
|
if (SRQ_REL_PTR(this_owner) < least_owner_id) {
|
|
least_owner_id = SRQ_REL_PTR(this_owner);
|
|
least_owner_ptr = que_inst;
|
|
}
|
|
}
|
|
que_inst = least_owner_ptr;
|
|
do {
|
|
if (que_inst != &LOCK_header->lhb_owners)
|
|
prt_owner(outfile, LOCK_header,
|
|
(OWN) ((UCHAR*) que_inst - OFFSET(OWN, own_lhb_owners)),
|
|
sw_requests, sw_waitlist);
|
|
que_inst = SRQ_NEXT((*que_inst));
|
|
} while (que_inst != least_owner_ptr);
|
|
#else
|
|
SRQ_LOOP(LOCK_header->lhb_owners, que_inst) {
|
|
prt_owner(outfile, LOCK_header,
|
|
(OWN) ((UCHAR*) que_inst - OFFSET(OWN, own_lhb_owners)),
|
|
sw_requests, sw_waitlist);
|
|
}
|
|
#endif /* SOLARIS_MT */
|
|
}
|
|
|
|
/* Print known locks */
|
|
|
|
if (sw_locks || sw_series) {
|
|
USHORT i2 = 0;
|
|
for (const srq* slot = LOCK_header->lhb_hash;
|
|
i2 < LOCK_header->lhb_hash_slots; slot++, i2++)
|
|
{
|
|
for (const srq* que_inst = (SRQ) SRQ_ABS_PTR(slot->srq_forward); que_inst != slot;
|
|
que_inst = (SRQ) SRQ_ABS_PTR(que_inst->srq_forward))
|
|
{
|
|
prt_lock(outfile, LOCK_header,
|
|
(LBL) ((UCHAR *) que_inst - 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
|
|
*
|
|
**************************************/
|
|
ULONG i;
|
|
|
|
time_t clock = time(NULL);
|
|
tm 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(&clock);
|
|
|
|
FPRINTF(outfile, "%02d:%02d:%02d ", d.tm_hour, d.tm_min, d.tm_sec);
|
|
|
|
if (flag & SW_I_ACQUIRE) {
|
|
FPRINTF(outfile, "%9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" %9"UQUADFORMAT" ",
|
|
(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"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" ",
|
|
(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"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" ",
|
|
(header->lhb_operations[Jrd::LCK_database] -
|
|
prior.lhb_operations[Jrd::LCK_database]) / seconds,
|
|
(header->lhb_operations[Jrd::LCK_relation] -
|
|
prior.lhb_operations[Jrd::LCK_relation]) / seconds,
|
|
(header->lhb_operations[Jrd::LCK_bdb] -
|
|
prior.lhb_operations[Jrd::LCK_bdb]) / seconds,
|
|
(header->lhb_operations[Jrd::LCK_tra] -
|
|
prior.lhb_operations[Jrd::LCK_tra]) / seconds,
|
|
(header->lhb_operations[Jrd::LCK_rel_exist] -
|
|
prior.lhb_operations[Jrd::LCK_rel_exist]) / seconds,
|
|
(header->lhb_operations[Jrd::LCK_idx_exist] -
|
|
prior.lhb_operations[Jrd::LCK_idx_exist]) / seconds,
|
|
(header->lhb_operations[0] -
|
|
prior.lhb_operations[0]) / seconds);
|
|
|
|
prior.lhb_operations[Jrd::LCK_database] =
|
|
header->lhb_operations[Jrd::LCK_database];
|
|
prior.lhb_operations[Jrd::LCK_relation] =
|
|
header->lhb_operations[Jrd::LCK_relation];
|
|
prior.lhb_operations[Jrd::LCK_bdb] = header->lhb_operations[Jrd::LCK_bdb];
|
|
prior.lhb_operations[Jrd::LCK_tra] = header->lhb_operations[Jrd::LCK_tra];
|
|
prior.lhb_operations[Jrd::LCK_rel_exist] =
|
|
header->lhb_operations[Jrd::LCK_rel_exist];
|
|
prior.lhb_operations[Jrd::LCK_idx_exist] =
|
|
header->lhb_operations[Jrd::LCK_idx_exist];
|
|
prior.lhb_operations[0] = header->lhb_operations[0];
|
|
}
|
|
|
|
if (flag & SW_I_WAIT) {
|
|
FPRINTF(outfile, "%9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" ",
|
|
(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"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" %9"UQUADFORMAT" ",
|
|
(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"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT" %9"
|
|
UQUADFORMAT" ",
|
|
(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"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" ",
|
|
(header->lhb_operations[Jrd::LCK_database] -
|
|
base.lhb_operations[Jrd::LCK_database]) / (factor),
|
|
(header->lhb_operations[Jrd::LCK_relation] -
|
|
base.lhb_operations[Jrd::LCK_relation]) / (factor),
|
|
(header->lhb_operations[Jrd::LCK_bdb] -
|
|
base.lhb_operations[Jrd::LCK_bdb]) / (factor),
|
|
(header->lhb_operations[Jrd::LCK_tra] -
|
|
base.lhb_operations[Jrd::LCK_tra]) / (factor),
|
|
(header->lhb_operations[Jrd::LCK_rel_exist] -
|
|
base.lhb_operations[Jrd::LCK_rel_exist]) / (factor),
|
|
(header->lhb_operations[Jrd::LCK_idx_exist] -
|
|
base.lhb_operations[Jrd::LCK_idx_exist]) / (factor),
|
|
(header->lhb_operations[0] -
|
|
base.lhb_operations[0]) / (factor));
|
|
}
|
|
|
|
if (flag & SW_I_WAIT) {
|
|
FPRINTF(outfile, "%9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" %9"UQUADFORMAT" %9"UQUADFORMAT
|
|
" %9"UQUADFORMAT" ",
|
|
(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*, sh_mem*, bool)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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,
|
|
SRQ_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 (const his* history = (HIS) SRQ_ABS_PTR(history_header); true;
|
|
history = (HIS) SRQ_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", SRQ_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 = (UCHAR *) &key;
|
|
const UCHAR* q = lock->lbl_key;
|
|
for (const UCHAR* const 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++) {
|
|
const 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_inst;
|
|
SRQ_LOOP(lock->lbl_requests, que_inst) {
|
|
const lrq* request = (lrq*) ((UCHAR*) que_inst - OFFSET(LRQ, lrq_lbl_requests));
|
|
FPRINTF(outfile,
|
|
"\t\tRequest %6"SLONGFORMAT", Owner: %6"SLONGFORMAT
|
|
", State: %d (%d), Flags: 0x%02X\n",
|
|
SRQ_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,
|
|
const own* owner,
|
|
bool sw_requests,
|
|
bool sw_waitlist)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p r t _ o w n e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Print a formatted owner block.
|
|
*
|
|
**************************************/
|
|
FPRINTF(outfile, "OWNER BLOCK %6"SLONGFORMAT"\n", SRQ_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: %6"UQUADFORMAT" (%6"UQUADFORMAT" ago)",
|
|
owner->own_acquire_time,
|
|
LOCK_header->lhb_acquires - owner->own_acquire_time);
|
|
#ifdef DEV_BUILD
|
|
FPRINTF(outfile, " sec %9"ULONGFORMAT" (%3"ULONGFORMAT" sec ago)",
|
|
owner->own_acquire_realtime,
|
|
time(NULL) - owner->own_acquire_realtime);
|
|
#endif /* DEV_BUILD */
|
|
FPRINTF(outfile, "\n");
|
|
#endif /* SOLARIS_MT */
|
|
{
|
|
const UCHAR 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) {
|
|
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) {
|
|
const srq* que_inst;
|
|
SRQ_LOOP(owner->own_requests, que_inst)
|
|
prt_request(outfile, LOCK_header,
|
|
(LRQ) ((UCHAR *) que_inst -
|
|
OFFSET(LRQ, lrq_own_requests)));
|
|
}
|
|
}
|
|
|
|
|
|
static void prt_owner_wait_cycle(
|
|
OUTFILE outfile,
|
|
const lhb* LOCK_header,
|
|
const own* owner,
|
|
USHORT indent, 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 (SRQ_REL_PTR(owner) == waiters->waitque_entry[i]) {
|
|
FPRINTF(outfile, "%6"SLONGFORMAT" (potential deadlock).\n",
|
|
SRQ_REL_PTR(owner));
|
|
return;
|
|
};
|
|
|
|
FPRINTF(outfile, "%6"SLONGFORMAT" waits on ", SRQ_REL_PTR(owner));
|
|
|
|
if (!owner->own_pending_request)
|
|
FPRINTF(outfile, "nothing.\n");
|
|
else {
|
|
if (waiters->waitque_depth > FB_NELEM(waiters->waitque_entry)) {
|
|
FPRINTF(outfile, "Dependency too deep\n");
|
|
return;
|
|
};
|
|
|
|
waiters->waitque_entry[waiters->waitque_depth++] = SRQ_REL_PTR(owner);
|
|
|
|
FPRINTF(outfile, "\n");
|
|
const lrq* owner_request = (LRQ) SRQ_ABS_PTR(owner->own_pending_request);
|
|
fb_assert(owner_request->lrq_type == type_lrq);
|
|
const bool owner_conversion = (owner_request->lrq_state > LCK_null);
|
|
|
|
const lbl* lock = (LBL) SRQ_ABS_PTR(owner_request->lrq_lock);
|
|
fb_assert(lock->lbl_type == type_lbl);
|
|
|
|
int counter = 0;
|
|
const srq* que_inst;
|
|
SRQ_LOOP(lock->lbl_requests, que_inst) {
|
|
|
|
if (counter++ > 50) {
|
|
for (i = indent + 6; i; i--)
|
|
FPRINTF(outfile, " ");
|
|
FPRINTF(outfile, "printout stopped after %d owners\n",
|
|
counter - 1);
|
|
break;
|
|
}
|
|
|
|
const lrq* lock_request =
|
|
(LRQ) ((UCHAR *) que_inst - OFFSET(LRQ, lrq_lbl_requests));
|
|
fb_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;
|
|
};
|
|
const own* lock_owner = (OWN) SRQ_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, const 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", SRQ_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_inst, USHORT que_offset)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p r t _ q u e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Print the contents of a self-relative que.
|
|
*
|
|
**************************************/
|
|
const SLONG offset = SRQ_REL_PTR(que_inst);
|
|
|
|
if (offset == que_inst->srq_forward && offset == que_inst->srq_backward) {
|
|
FPRINTF(outfile, "%s: *empty*\n", string);
|
|
return;
|
|
}
|
|
|
|
SLONG count = 0;
|
|
const srq* next;
|
|
SRQ_LOOP((*que_inst), next)
|
|
++count;
|
|
|
|
FPRINTF(outfile, "%s (%ld):\tforward: %6"SLONGFORMAT
|
|
", backward: %6"SLONGFORMAT"\n", string, count,
|
|
que_inst->srq_forward - que_offset, que_inst->srq_backward - que_offset);
|
|
}
|
|
|
|
|
|
static void prt_que2(
|
|
OUTFILE outfile,
|
|
const lhb* LOCK_header,
|
|
const SCHAR* string, const srq* que_inst, 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
|
|
*
|
|
**************************************/
|
|
const SLONG offset = SRQ_REL_PTR(que_inst);
|
|
|
|
if (offset == que_inst->srq_forward && offset == que_inst->srq_backward) {
|
|
FPRINTF(outfile, "%s: *empty*\n", string);
|
|
return;
|
|
}
|
|
|
|
FPRINTF(outfile, "%s:\tforward: %6"SLONGFORMAT
|
|
", backward: %6"SLONGFORMAT"\n", string,
|
|
que_inst->srq_forward - que_offset, que_inst->srq_backward - que_offset);
|
|
}
|
|
|