Previous | Contents | Index |
This chapter describes how to write, debug, and invoke an SDA
Extension. This chapter also describes the routines available to an SDA
Extension.
10.1 Introduction
When analysis of a dump file or a running system requires intimate knowledge of data structures that are not known to the System Dump Analyzer, the functionality of SDA can be extended by the addition of new commands into which the necessary knowledge has been built. Note that in this description, whenever a reference is made to accessing a dump file (ANALYZE/CRASH_DUMP), this also includes accessing memory in the running system (ANALYZE/SYSTEM).
For example, a user-written device driver allocates nonpaged pool and records additional data about the device there (logging different types of I/O, perhaps), and a pointer to the new structure is saved in the device-specific extension of the UCB. After a system crash, the only way to look at the data from SDA is to do the following:
An SDA extension that knows the layout of the nonpaged pool structure,
and where to find the pointer to it in the UCB, could output the data
in a formatted display that alerts the user to unexpected data patterns.
10.2 Description
The following discussion uses an example of an SDA extension that invokes the MBX command to output a formatted display of the status of the mailbox devices in the system. The source file, MBX$SDA.C, is provided in SYS$EXAMPLES.
An SDA extension consists of a shareable image, in this case MBX$SDA.EXE, either located in the directory SYS$LIBRARY or found by translating the logical name MBX$SDA. It contains two universal symbols: SDA$EXTEND, the entry point; and SDA$EXTEND_VERSION, the address of a longword that contains the version of the interface used (in the format of major/minor ident), which allows SDA to confirm it has activated a compatible extension. The image contains at least two modules: MBX$SDA, the user-written module that defines the two symbols and provides the code and data necessary to produce the desired formatted output; and SDA_EXTEND_VECTOR, which provides jackets for all of the callable SDA routines, and is found in SYS$LIBRARY:VMS$VOLATILE_PRIVATE_INTERFACES.OLB. The user-written portion can be split into multiple modules.
Whenever SDA receives an unrecognized command, like "SDA> MBX", it attempts to activate the shareable image MBX$SDA at the SDA$EXTEND entry point. If you choose a command name that matches the abbreviation of an existing command, SDA can be forced to activate the extension using the "DO" command. For example, if you had an SDA extension called VAL$SDA, you could not activate it with a command like "SDA> VAL" as SDA would interpret that as an abbreviation of its VALIDATE command. But VAL$SDA can be activated by issuing "SDA> DO VAL".
With or without the "DO" prefix, the rest of the command line is passed to the extension; it is up to the extension to parse it. The example extension MBX$SDA includes support for commands of the form "SDA> MBX SUMMARY" and "SDA> MBX <address>" to demonstrate this. If the extension is invoked with no arguments, it should do no more than display a simple announcement message, or prompt for input. This assists in the debugging of the extension, as described in Section 10.3.
Section 10.2.1 describes how to compile, link, and invoke an SDA
extension, and describes what an SDA extension should contain.
10.2.1 Compiling and Linking an SDA Extension
The user-written module is only supported when written in HP C (minimum Version 5.2), following the pattern of the example extension, MBX$SDA.C. It should be compiled and linked using commands of the following form:
$cc mbx$sda + sys$library:sys$lib_c /library $link /share - mbx$sda.obj, - sys$library:vms$volatile_private_interfaces /library, - sys$input /option symbol_vector = (sda$extend=procedure) symbol_vector = (sda$extend_version=data) |
1. You can include the qualifier /INSTRUCTION=NOFLOAT on the compile command line if floating-point instructions are not needed. 2. The + SYS$LIBRARY:SYS$LIB_C /LIBRARY is not needed on the compile command line if the logical name DECC$TEXT_LIBRARY is defined and translates to SYS$LIBRARY:SYS$LIB_C.TLB. 3. If the user-written extension needs to signal SDA condition codes, or output their text with $PUTMSG, you should add the qualifier /INCLUDE=SDAMSG to the parameter SYS$LIBRARY:VMS$VOLATILE_PRIVATE_INTERFACES /LIBRARY . |
You can invoke the SDA extension as follows:
$define mbx$sda sys$disk:[]mbx$sda $analyze /system SDA>mbx summary SDA>mbx <address> |
At a minimum, the user-written module must contain:
int sda$extend_version = SDA_FLAGS$K_VERSION; |
Optionally, the user-written module may also contain the statement:
#define __NEW_STARLET |
You should use this option because it provides type checking of function arguments and gives consistency in casing and naming conventions.
The entry point in the user-written module, SDA$EXTEND, is called as a routine with three arguments and no return value. The declaration is as follows:
void sda$extend ( int *transfer_table, struct dsc$descriptor_s *cmd_line, SDA_FLAGS sda_flags) |
The arguments in this code example have the following meanings:
Line of Code | Meaning | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
transfer_table | Address of the vector table in the base image. The user-written routine SDA$EXTEND must copy this to SDA$VECTOR_TABLE (declared in SDA_ROUTINES.H) before any SDA routines can be called. | ||||||||||||||||
cmd_line | Address of the descriptor of the command line as entered by the user, less the name of the extension. So, if you enter "SDA> MBX" or "SDA> DO MBX", the command line is a zero length string. If you enter the command "SDA> MBX 80102030", the command line is " 80102030" (the separating space is not stripped). | ||||||||||||||||
sda_flags |
Definition for the following four bits in this structure:
|
The first executable statement of the routine must be to copy TRANSFER_TABLE to SDA$VECTOR_TABLE (which is declared in SDA_ROUTINES.H):
sda$vector_table = transfer_table; |
If this is not done, you cannot call any of the routines described below. Any attempts to call the routines receive a status return of SDA$_VECNOTINIT. (For routines defined not to return a status, this value can be found only by examining the return value directly.)
The next statement should be one to establish a condition handler, as it is often difficult to track down errors in extensions such as access violations because the extension is activated dynamically with LIB$FIND_IMAGE_SYMBOL. A default condition handler, SDA$COND_HANDLER, is provided that outputs the following information in the event of an error:
You can establish this condition handler as follows:
lib$establish (sda$cond_handler); |
The error condition, signal array, and register dump are output directly to SYS$OUTPUT and/or SYS$ERROR, and are not affected by the use of the SDA commands SET OUTPUT and SET LOG. |
Thus, a minimal extension would be:
#define __NEW_STARLET 1 #include <descrip.h> #include <sda_routines.h> int sda$extend_version = SDA_FLAGS$K_VERSION; void sda$extend (int *transfer_table, struct dsc$descriptor_s *cmd_line, SDA_FLAGS sda_flags) { sda$vector_table = transfer_table; lib$establish (sda$cond_handler); sda$print ("hello, world"); return; } |
In addition to the "after-the-fact" information provided by the condition handler, you can debug SDA extensions using the OpenVMS Debugger. A second copy of the SDA image, SDA_DEBUG.EXE, is provided in SYS$SYSTEM. By defining the logical name SDA to reference this image, you can debug SDA extensions as follows:
An example of the preceding steps is as follows:
$ cc /debug /noopt mbx$sda + sys$library:sys$lib_c /library $ link /debug /share - mbx$sda.obj, - sys$library:vms$volatile_private_interfaces /library, - sys$input /option symbol_vector = (sda$extend=procedure) symbol_vector = (sda$extend_version=data) $ ! $ define mbx$sda sys$disk:[]mbx$sda $ define sda sda_debug $ analyze /system ... DBG> set break start_extension DBG> go ... SDA> mbx break at routine START\START_EXTENSION ... DBG> set image mbx$sda DBG> set language c DBG> set break /exception DBG> go MBX commands: 'MBX SUMMARY' and 'MBX <address>' SDA> mbx summary ... SDA> mbx <address> ... %DEBUG-I-DYNMODSET, setting module MBX$SDA %SYSTEM-E-INVARG, invalid argument ... DBG> |
The user-written routine may call SDA routines to accomplish any of the following tasks:
Note the following points before using the callable routines described here:
VOID_PQ start /* 64-bit pointer */ |
void *dest /* 32-bit pointer */ |
The following sections describe the SDA extension callable routines.
Adds a symbol to SDA's local symbol table.
void sda$add_symbol (char *symbol_name, uint64 symbol_value);
symbol_name
OpenVMS usage char_string type character string access read only mechanism by reference
Address of symbol name string (zero-terminated).symbol_value
OpenVMS usage quadword_unsigned type quadword (unsigned) access read only mechanism by value
The symbol value.
SDA maintains a list of symbols and the corresponding values. SDA$ADD_SYMBOL is used to insert additional symbols into this list, so that they can be used in expressions and during symbolization.
None
sda$add_symbol ("MBX", 0xFFFFFFFF80102030); |
This call defines the symbol MBX to the hexadecimal value FFFFFFFF80102030.
Allocates dynamic memory.
void sda$allocate (uint32 size, void **ptr_block);
size
OpenVMS usage longword_unsigned type longword (unsigned) access read only mechanism by value
Size of block to allocate (in bytes).ptr_block
OpenVMS usage address type longword (unsigned) access write only mechanism by reference
Address of longword to receive address of block.
The requested memory is allocated and the address returned. Note that this is the only supported mechanism for allocation of dynamic memory.SDA$DEALLOCATE
None
If no memory is available, the error is signaled and the SDA session aborted.
PCB *local_pcb; ... sda$allocate (PCB$C_LENGTH, (void *)&local_pcb); |
This call allocates a block of heap storage for a copy of a PCB, and stores its address in the pointer LOCAL_PCB.
Performs a Boolean operation on a pair of CBBs.
int sda$cbb_boolean_oper (CBB_PQ input_cbb, CBB_PQ output_cbb, int operation);
input_cbb
OpenVMS usage address type CBB structure access read only mechanism by reference
The address of the first (input) CBB structure.output_cbb
OpenVMS usage address type CBB structure access read/write mechanism by reference
The address of the second (output) CBB structure.operation
OpenVMS usage longword type longword (unsigned) access read only mechanism by value
The desired operation from the following list:
CBB$C_OR The logical sum of the two CBBs is performed and the result (B = A | B) is written to the output CBB. CBB$C_BIC The logical product with complement of the two CBBs is performed and the result (B = B & ~A) is written to the output CBB.
The desired Boolean operation is performed on the two CBB structures, and the result is written to the second (output) structure.
SS$_BADPARAM The number of valid bits in the input and output CBBs is different. SS$_WASCLR All bits in the resulting output CBB are clear. SS$_WASSET At least one bit in the resulting output CBB is set.
int status; extern CBB active_set, configure_set; CBB inactive_set; sda$cbb_copy (&configure_set, &inactive_set); status = sda$cbb_boolean_oper (&active_set, &inactive_set, CBB$C_BIC); if (status == SS$_WASSET) sda$print ("There are inactive CPUs in the system"); |
This example shows how the set of active CPUs and the set of configured CPUs can be manipulated to create a set of inactive CPUs.
Clears the specified bit in a CBB.
int sda$cbb_clear_bit (CBB_PQ cbb, int bit);
cbb
OpenVMS usage address type CBB structure access read/write mechanism by reference
The address of the CBB structure to be modified.bit
OpenVMS usage longword type longword (unsigned) access read only mechanism by value
The bit in the CBB to be cleared. If the bit number is -1, clears all bits.
The specified bit (or all bits) in the CBB is cleared.
SS$NORMAL Successful completion SS$BADPARAM The bit number is out of range
int status; extern int next; extern CBB active_set; status = sda$cbb_clear_bit (&active_set, next); if (!(status & 1)) sda$print ("Bad CPU specified: !XL", next); |
This example shows how a bit in a CBB is cleared.
Copies the contents of one CBB to another.
int sda$cbb_copy (CBB_PQ input_cbb, CBB_PQ output_cbb);
input_cbb
OpenVMS usage address type CBB structure access read only mechanism by reference
The address of the CBB structure to be copied.output_cbb
OpenVMS usage address type CBB structure access write only mechanism by reference
The address of the CBB structure to receive the copy.
The specified CBB is copied.
None
Locates the first clear bit in a CBB.
int sda$cbb_ffc (CBB_PQ cbb, int start_bit);
cbb
OpenVMS usage address type CBB structure access read only mechanism by reference
The address of the CBB structure to be searched.start_bit
OpenVMS usage longword type longword (unsigned) access read only mechanism by value
The first bit in the CBB to be checked.
The CBB structure is searched, starting at the specified bit, for a clear bit.
bit_number If a clear bit is found, its bit number is returned. If no clear bit is found (all bits from start_bit to cbb->cbb$l_valid_bits are set), then the number of valid bits is returned.
int bit; extern int start; extern CBB active_set; bit = sda$cbb_ffc (&active_set, start); if (bit >= active_set.cbb$l_valid_bits) sda$print ("No clear bits in active set"); else sda$print ("First clear bit in active set = !XL", bit); |
This example shows how the next clear bit in a CBB can be located.
Locates the first set bit in a CBB.
int sda$cbb_ffs (CBB_PQ cbb, int start_bit);
cbb
OpenVMS usage address type CBB structure access read only mechanism by reference
The address of the CBB structure to be searched.start_bit
OpenVMS usage longword type longword (unsigned) access read only mechanism by value
The first bit in the CBB to be checked.
The CBB structure is searched for a set bit, starting at the specified bit.
bit_number If a set bit is found, its bit number is returned. If no set bit is found (all bits from start_bit to cbb->cbb$l_valid_bits are clear), then the number of valid bits is returned.
int bit; extern int start; extern CBB active_set; bit = sda$cbb_ffs (&active_set, start); if (bit >= active_set.cbb$l_valid_bits) sda$print ("No set bits in active set"); else sda$print ("First set bit in active set = !XL", bit); |
This example shows how the next set bit in a CBB can be located.
Initializes a CBB structure to a known state.
void sda$cbb_init (CBB_PQ cbb);
cbb
OpenVMS usage address type CBB structure access read only mechanism by reference
The address of the CBB structure to be initialized.
The fields of the CBB that describe its layout are initialized as necessary for a CPU CBB. The actual bitmask is zeroed.
None
Sets the specified bit in a CBB.
int sda$cbb_set_bit (CBB_PQ cbb,int bit);
cbb
OpenVMS usage address type CBB structure access read/write mechanism by reference
The address of the CBB structure to be modified.bit
OpenVMS usage longword type longword (unsigned) access read only mechanism by value
The bit in the CBB to be set. If the bit number is -1, set all bits.
The specified bit (or all bits) in the CBB is set.
SS$NORMAL Successful completion. SS$BADPARAM The bit number is out of range.
int status; extern int next; extern CBB active_set; status = sda$cbb_set_bit (&active_set, next); if (!(status & 1)) sda$print ("Bad CPU specified: !XL", next); |
This example shows how a bit in a CBB is set.
Tests the specified bit in a CBB.
int sda$cbb_test_bit (CBB_PQ cbb,int bit);
cbb
OpenVMS usage address type CBB structure access read only mechanism by reference
The address of the CBB structure to be tested.bit
OpenVMS usage longword type longword (unsigned) access read only mechanism by value
The bit in the CBB to be tested.
The specified bit in the CBB is tested and its value returned.
SS$_WASSET The specified bit was set. SS$_WASCLR The specified bit was clear. SS$_BADPARAM The bit number is out of range.
int status; extern int next; extern CBB active_set; status = sda$cbb_test_bit (&active_set, next); if (!(status & 1)) sda$print ("Bad CPU specified: !XL", next); else if (status == SS$_WASSET) sda$print ("CPU !XL was set", next); else sda$print ("CPU !XL was clear", next); |
This example shows how a bit in a CBB is tested.
Previous | Next | Contents | Index |