HP OpenVMS Systems

ask the wizard
Content starts here

Tracking lock usage, lock holder?

» close window

The Question is:

 
About a year ago I remember getting info on the latest version of VMS being
having a utility which would allow quick ID of who is caught in a record
lock. Do you know of any such critter?
 
Also, I've been trying to use the lib$getdvi to return the lockID for the
process which locks a record. Two things I've noticed:
1. When I use the channel as a parameter and not the device name, only 0000
gets returned.
2. When I use the device name, a lock ID is returned which doesn't match the
one shown through the SDA "show process/lock/id=PID #" command.
 
 
Are there any routines you know that I can use to ID a blocking process?
 
Thanks
Dave
 


The Answer is :

 
  DECamds is far and away the easiest way to track the current holder of
  a lock, and the processes blocked by the lock.  DECamds is a separately-
  installed kit, shipped with and licensed with OpenVMS.
 
  Work is underway in SDA to add a more direct access to the distributed
  lock management data structures for a release after OpenVMS V7.2.  At
  present, identification of the lock holder involves traversing the lock
  data structures via SDA, including separate traversal of the lock trees
  on each cluster node involved as required.
 
  lib$getdvi and sys$getdvi return the high-level lock names for the
  particular target storage volumes -- these are used for volume-scale
  locking requirements.  Typical RMS-level locking activity is typically
  at a much finer granularity, and involves file level or record level
  locks.
 
  For some background on the lock manager beyond what is in the system
  services and programming concepts manuals, see the VMS File System
  Internals book and the Internals and Data Structures Manuals.  )These
  latter two books are available from Digital Press.)
 
  The attached tool may be of interest:
 
#define MAXDEVICE  200
#define MAXDEVNAMLEN  64
#define MAXDEVLOCKNAM 13
#define MAXFILNAMLEN 80
#define MAXPID 16
/*
** BLOCKING.C, Hein van den Heuvel, Digital, July 1995
**
**   List all locks not granted (converting, waiting) for a specified PID.
**   For each, print blocking lock information, resource name and parent.
**   Have fun,
**	Hein van den Heuvel, July 1995.
*/
 
#include <prvdef>
#include <lkidef>
#include <lckdef>
#include <descrip>
#include <string>
#include <ctype>
#include <stdio>
#include <ssdef>
#include <rms>
#define terminator 0,0,0,0
#define EFN 1
 
static char devnam[MAXDEVICE][MAXDEVNAMLEN];	    /* counted string */
static char devlocknames[MAXDEVICE][MAXDEVLOCKNAM+1];
static int  device_count = 0;
 
typedef struct { short len, cod; void *address; short *retlen; } item;
typedef struct { unsigned int len; void *address; } desc;
 
int	sys$getlkiw(), sys$getjpi(), sys$setprv(), sys$cmexec();
void	dump_data () ;
int	make_device_name_list(), print_filename_and_record() ;
char	*find_device_name();
char	*mode[]={"NL", "CR", "CW", "PR", "PW", "EX", "??"};
 
main(int argc, char *argv[])
 
    int	    stat, s, i, l, parent, grand_parent, lock_id, lock_pid;
    int	    wildcard=0, retlen=0, parent_retlen=0, pid=0, locks=0;
    char    *x;
    struct lkidef lkibuf[100], *lki;
    struct { unsigned all : 16, one : 15, too_small : 1 ;} lkilen;
    int	    privs[] = { PRV$M_WORLD | PRV$M_CMEXEC , 0};
    int	    *search_devnam=0;
    desc    search_devnam_desc;
 
#pragma nostandard /* Using address of variable where constant is standard */
 
    struct  {   char	rqmode, grmode, queue, fill;} lki_state;
    struct  {   int	rms; unsigned short fid_num, fid_seq, fid_rvn;
		char	devlocknam[22] ;} parent_resnam;
    struct  { unsigned int	id, vbn, fill[6];} resnam;
 
    item    getlki_items[] = { 4, LKI$_LOCKID,  &lock_id, 0,
			       4, LKI$_PID,     &lock_pid, 0,
			       3, LKI$_STATE,   &lki_state, 0, terminator};
    item    block_items[] = { 31, LKI$_RESNAM,  &resnam, &retlen,
			       4, LKI$_PARENT,  &parent, 0,
		   sizeof lkibuf, LKI$_BLOCKING,&lkibuf, &lkilen, terminator};
    item    parent_items[] = {31, LKI$_RESNAM,  &parent_resnam, &parent_retlen,
			       4, LKI$_PARENT,  &grand_parent, 0, terminator};
    int
	getlki_args[] = {7, EFN, (int) &wildcard, (int) &getlki_items,0,0,0,0},
	parent_args[] = {7, EFN, (int) &parent, (int) &parent_items,0,0,0,0},
	block_args[] = {7, EFN, (int) &lock_id, (int) &block_items,0,0,0,0};
 
#pragma standard
 
    /*
    ** First get some temporary privs for the GETLKI in EXEC mode later on.
    */
    stat = sys$setprv ( 1, privs, 0, 0);
    if (stat != SS$_NORMAL) return (stat & -2);
 
    if (argc > 1) {
	/*
	** First argument, if present, specifies PID to look for. 0 = All.
	*/
	sscanf (argv[1], "%x", &pid);
	if (pid > MAXPID) {
	    printf (" Looking for non-granted locks for PID %8X\n\n", pid);
	    } else {
	    x = "";
	    if (pid > 0) x = "RMS ";
	    printf (" Looking for non-granted %slocks for all PIDs\n\n", x);
	    };
	};
 
    if (argc > 2) {
	/*
	** Second argument, if present, specifies wildcarded device name
	** to look for. For lock on matching devices, the RMS files name
	** and locked record is attempted to be displayed.
	*/
        search_devnam_desc.len = strlen(argv[2]);
        search_devnam_desc.address = (int) argv[2];
        search_devnam = &search_devnam_desc;
        str$upcase (&search_devnam_desc, &search_devnam_desc);
	}
 
    (void) make_device_name_list(search_devnam);
 
 
    /*
    ** Main loop. Get a lock, any lock.
    ** Find out wether it is held by specified process, and waiting.
    */
    stat = sys$cmexec (&sys$getlkiw, &getlki_args);
    while (stat & 1) {
	locks++;
	if ( (lki_state.queue != LKI$C_GRANTED) &&
	     ((pid < MAXPID) ||  (lock_pid == pid)) ) {
	    int rms = 0;
 
	    parent = 0;
	    /*
	    ** Have blocked lock for specified PID, request details.
	    */
	    stat = sys$cmexec (&sys$getlkiw, &block_args);
	    if (!(stat & 1)) break;
	    /*
	    ** Let's see if we stumbled into an rms record or bucket lock...
	    */
	    if (parent!=0) {
		s = sys$cmexec (&sys$getlkiw, &parent_args);
		if ((s & 1) && (grand_parent==0) &&
		    (parent_resnam.rms == 'RMS$')) rms = 1;
		}
 
	    /*
	    ** Print out all locks, or just RMS ?
	    */
	    if ((pid == 0) || (pid > MAXPID) || rms) {
		l = lkilen.all / lkilen.one;
	        printf ("Pid %08X Lock %08X, Rq=%s, Parent %08X, blocked by %d.\n",
		    lock_pid, lock_id, mode[lki_state.rqmode], parent, l);
		dump_data (&resnam, retlen, "Resource Name ");
		lki = &lkibuf;
		for ( i = 0; i < l; i++) {
		    printf ("  PID=%8X, Gr=%s, Rq=%s, LockId=%08X, System=%08\n",
				lki->lki$l_pid, mode[lki->lki$b_grmode],
				mode[lki->lki$b_rqmode],
				lki->lki$l_lkid, lki->lki$l_csid);
		    lki++;
		    }
		if (parent!=0) dump_data (&parent_resnam,
			parent_retlen, "Parent Resource Name");
		printf ("\n");
		}
 
	    if (rms) {
		printf ("  RMS lock! VBN/ID %X/%X, File (%d,%d,%d) on %s\n",
			resnam.vbn, resnam.id,
			parent_resnam.fid_num, parent_resnam.fid_seq,
			parent_resnam.fid_rvn,
			&parent_resnam.devlocknam[1]);
		print_filename_and_record ( &parent_resnam.devlocknam[0],
			&parent_resnam.fid_num, resnam.vbn, resnam.id);
		}
	    }         /* PID   ? */
	if (stat &1) stat = sys$cmexec (&sys$getlkiw, &getlki_args);
	} /* stat ? */
    if (stat == SS$_NOMORELOCK) stat = SS$_NORMAL;
    printf (" Done. Scanned %d locks.\n", locks);
    return stat;
 
 
void dump_data (void *p, short len, char *t)
 
    int	i, j, k, l;
    unsigned char (*x)[72];
    char out[80], *in, c;
    x = in = p;
    l = len;
    if (l > 72) l = 72; /* sizeof out */
    for (i=0; i<l; i++){
	c = *in++;
	if (!isalnum (c)) c = '.';
	out[i]=c;
	}
    out[i]=0;
    printf ("    %s (%d):\n    <%s>\n", t, len, &out[0]);
    j = 0;
 
    for (i=0; i<l;) {
	j += sprintf (&out[j], "%02X ", (*x)[i++]);
	if (!(i%24)) {
	    printf ("    %s\n",&out[0]);
	    j = 0;
	    }
	}
    if (j) printf ("    %s\n",&out[0]);
    return;
 
 
int print_filename_and_record ( char *devlocknam,
	unsigned short (*fid)[3], unsigned int vbn, unsigned int id)
 
    struct FAB fab;
    struct NAM nam;
    struct RAB rab;
    int  device_number, stat, i, l;
    char buf[512];
static	char filnam[MAXFILNAMLEN+1];
    desc devnam_desc, filnam_desc = {0, filnam};
 
    for (device_number=0;
	 strcmp ((char *) devlocknames[device_number], devlocknam);
	 device_number++) if (device_number == device_count) return;
 
    devnam_desc.len = devnam[device_number][0];
    devnam_desc.address = &devnam[device_number][1];
    filnam_desc.len = MAXFILNAMLEN;
    stat = lib$fid_to_name(&devnam_desc, fid, &filnam_desc, &filnam_desc);
    filnam[filnam_desc.len] = 0;
    printf ( "  %s %s\n", &devnam[device_number][1], filnam);
/*
**  Now that we've printed out the file name, and previously the record
**  or bucket data, lets try get display the data being waited for.
**  I suppose this could end up becoming blocked for the very same lock!
**  (VBN lock code is not at all tested as RMS does not normally block there)
*/
    fab = cc$rms_fab;
    rab = cc$rms_rab;
    nam = cc$rms_nam;
    rab.rab$l_fab = &fab;
    fab.fab$l_nam = &nam;
    fab.fab$l_fop = FAB$M_NAM;		/* will use dev+id, not name */
    if (!vbn) fab.fab$b_fac = FAB$M_BIO;/* block IO if not an RFA */
    fab.fab$b_shr = FAB$M_SHRUPD;	/* allow writers */
    for (i=0; i<3; i++) nam.nam$w_fid[i] = (*fid)[i];
    for (i=0; i<devnam[device_number][0]; i++)
		nam.nam$t_dvi[i] = devnam[device_number][i];
    nam.nam$t_dvi[0]--;			/* strip colon */
    rab.rab$l_ubf = buf;
    rab.rab$w_usz = sizeof buf;
 
    stat = sys$open ( &fab );
        if (!(stat&1)) return stat;
    stat = sys$connect ( &rab );
        if (!(stat&1)) return stat;
    if (vbn) {
	rab.rab$b_rac = RAB$C_RFA;
	rab.rab$v_rrl = 1;
	rab.rab$l_rfa0 = vbn;
	rab.rab$w_rfa4 = id;
	stat = sys$get ( &rab );
	l = rab.rab$w_rsz;
	if (stat == RMS$_RTB) {
	    l = rab.rab$l_stv;
	    stat = 1;
	    }
	} else {
	rab.rab$l_bkt = id;
	stat = sys$read ( &rab );
	}
	dump_data (rab.rab$l_rbf, l, "  Record ");
	return stat;
 
 
int make_device_name_list(desc *search_devnam)
    {
 
#include <dvidef>
#include <dcdef>
/* #include <dvsdef> does not exist, hand coded defines follow */
#define DVS$_DEVCLASS 1
#define DVS$_DEVTYPE  2
#define SS$_NOMOREDEV 2648
 
    int i, stat, context[] = {0,0}, devclass = DC$_DISK;
    short int retlen;
    desc devnam_desc;
    item getdvi_items[] = { MAXDEVLOCKNAM, DVI$_DEVLOCKNAM,0, 0, terminator};
    item device_items[] = { 4, DVS$_DEVCLASS,   &devclass, 0, terminator};
    /*
    ** Build two arrays with DISK device names and their lock names.
    */
    for (i=0;  i<MAXDEVICE;  i++) {
	devnam_desc.len = MAXDEVNAMLEN-1;
	devnam_desc.address = &devnam[i][1];
	stat = sys$device_scan (&devnam_desc, &devnam_desc,
	    search_devnam, device_items, context);
	devnam[i][0]=devnam_desc.len;
	if (stat & 1) {
	    devnam[i][devnam_desc.len+1]=0;    /* Null terminate */
	    getdvi_items[0].address = devlocknames[i];
	    getdvi_items[0].retlen = &retlen;
 
	    stat = sys$getdvi (0,0,&devnam_desc,getdvi_items,0,0,0,0);
	    if (stat != SS$_NORMAL) return stat;
	    devlocknames[i][retlen] = 0;
	}
	else {
	    if (stat == SS$_NOMOREDEV) {
		device_count = i;
		i = MAXDEVICE;
	    }
	    break;
	}
    }
    return stat;
 

answer written or last revised on ( 28-JAN-2000 )

» close window