#pragma module MBX$SDA "X-20"
// *************************************************************************
// * *
// * HPE CONFIDENTIAL. This software is confidential proprietary software *
// * licensed by Hewlett Packard Enterprise Development, LP, and is not *
// * authorized to be used, duplicated or disclosed to anyone without the *
// * prior written permission of HPE. *
// * Copyright 2019 Hewlett Packard Enterprise Development, LP *
// * *
// * VMS SOFTWARE, INC. CONFIDENTIAL. This software is confidential *
// * proprietary software licensed by VMS Software, Inc., and is not *
// * authorized to be used, duplicated or disclosed to anyone without *
// * the prior written permission of VMS Software, Inc. *
// * Copyright 2021 VMS Software, Inc. *
// * *
// *************************************************************************
//
// FACILITY:
//
// SDA Extensions
//
//
// ABSTRACT:
//
// This example program demonstrates the use of SDA extension routines.
// This program is compiled and linked as a shareable image (either placed
// in SYS$SHARE or pointed to by a logical name) and will be automatically
// image activated from within SDA.
//
// If you type a command FOO at the SDA command prompt "SDA>" which is
// unknown to SDA, SDA will try to look for shareable image named FOO$SDA,
// and image activate it if found. The main routine entry point must be
// called SDA$EXTEND.
//
// In our case we call our example image MBX$SDA, hence we use the command
// MBX at the SDA prompt. You can pass any additional parameters and
// qualifiers, and your program will parse and act upon.
//
// In our example, if you simply say MBX, the example program walks through
// all mailbox devices on your system and will display certain information
// for them.
// You can also pass any virtual address as a parameter, and it will try to
// display some information like the symbolization, if it falls within a
// known image etc.
//
// You can run this example program against a dumpfile, or on your running
// system, either use ANALYZE/CRASH or ANALYZE/SYSTEM to invoke SDA.
//
// Note: We ignore condition values returned from calls to SDA$PRINT (except
// the first one) and SDA$TYPE. If the first call to SDA$PRINT works, and
// the others don't, there's not a lot we can do (we can't rely on SDA$PRINT
// to print the condition value!). We also ignore condition values returned
// from calls to SDA$REQMEM. Either it works, or the error is signalled and
// the command aborted.
//
// BUILD INSTRUCTIONS:
//
// $ cc mbx$sda+sys$library:sys$lib_c/lib
// $ 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)
// $
// $ define mbx$sda sys$disk:[]mbx$sda
// $ analyze/system
// SDA> mbx
// SDA> mbx
//
//
// AUTHOR:
//
// Christian Moser, Digital Equipment Corporation
//
//
// REVISION HISTORY:
//
// X-20 RAB Richard A. Bishop 13-Apr-2021
// Tweak the display wording to allow for accessing
// X86 primary kernel memory. Fix bugs in instruction
// decode example for x86.
//
// X-19 RAB Richard A. Bishop 06-Apr-2020
// Add an x86 dump to the set of possible environments
//
// X-18 RAB Richard A. Bishop 09-Jun-2019
// Add X86 conditionals
//
// X-17 RAB Richard A. Bishop 13-Jan-2006
// Display the environment we're running in
//
// X-16 CMOS Christian Moser 17-MAY-2005
// UCB$W_UNIT has been promoted to a longword UCB$L_UNIT
// field.
//
// X-15 RAB Richard A. Bishop 09-Feb-2005
// Fix bug displaying image offsets (SDA$GET_IMAGE_OFFSET
// example)
//
// X-14 RAB Richard A. Bishop 11-Jan-2005
// Ensure new page after formatting PCB & PHD. Prevent
// use of MBX commands in process dumps
//
// X-13 RAB Richard A. Bishop 04-Feb-2003
// Allow for IA64 instruction bundles.
//
// X-12 RAB Richard A. Bishop 02-Oct-2002
// New LDRIMG layout.
//
// X-11 GHJ Gregory H. Jordan 2-Jan-2002
// The mailbox driver now uses two longword fields in
// the mailbox ucb extension. Use these fields to
// display the mailbox initial and current buffer
// quotas. Also, increase the size that can be
// displayed for these fields.
//
// X-9 RAB Richard A. Bishop 15-Dec-1999
// Give an example of SDA$FAO by splitting up an
// existing SDA$PRINT.
//
// X-8 RAB Richard A. Bishop 5-Apr-1999
// Use !AF instead of !AC for image names in case the
// name is corrupted
//
// X-7 RAB Richard A. Bishop 12-Dec-1997
// Do #include's properly
//
// X-6 RAB Richard A. Bishop 7-Apr-1997
// Update link instructions to reference the library
// ALPHA$LIBRARY:VMS$VOLATILE_PRIVATE_INTERFACES not
// the object file ALPHA$LIBRARY:SDA_EXTEND_VECTOR.OBJ.
//
// X-5 RAB Richard A. Bishop 3-Apr-1997
// Fix SDA$SYMBOL_VALUE definition/call. Now returns
// uint64 instead of void. This to ensure users allow
// 64-bits for the value.
//
// X-4 RAB Richard A. Bishop 24-Mar-1997
// Add condition code checking, several new examples,
// changes to routine names and definitions
//
// X-3 CMOS Christian Moser 14-Mar-1997
// Implement version control and minor tweaks in
// routine prototypes
//
// X-2 CMOS Christian Moser 20-Feb-1997
// More examples added
//
// X-1 CMOS Christian Moser 16-Jan-1997
// Initial version.
//
// Imported definitions
#define __NEW_STARLET 1
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// Global variables
int sda$extend_version = SDA_FLAGS$K_VERSION;
void mbx$title ( void )
{
sda$print ( "Mailbox UCB RefCount Outstanding Initial Remaining Message Read IO Logical" );
sda$print ( " Unit Address Read/Write Messages Quota Quota Queue Queue Name" );
sda$print ( "-------------------------------------------------------------------------------------------------" );
return;
}
///////////////////////////////////////////////////////////////////////////////
//
// This is the main entry point in SDA extension routine called from
// within SDA.
//
// sda$extend transfer_table, cmd_line, sda_flags
//
// transfer_table - pointer to the routine transfer table
// cmd_line - address of descriptor of the command line passed
// sda_flags - flags
//
void sda$extend (int *transfer_table,
struct dsc$descriptor_s *cmd_line,
SDA_FLAGS sda_flags)
{
int status;
int i;
static DDB *ddb;
static MB_UCB *ucb;
UCB *ucb_addr;
int flag;
static int sysdef_flag;
VOID_PQ ioc$gl_devlist;
static LNMB *lnmb;
static int lnmb_size;
GENERIC_64 longquad;
char hw_name[64];
char buffer[128];
char template[16]; // Only used on IA64
char *dyn_tab;
int16 *dyn_ptr;
LDRIMG *ldrimg;
IMCB *imcb;
KFERES_SECTION *kferes_section; // Only used on Alpha
LDRISD *isd;
COMP_IMG_OFF sda_cio;
int64 offset;
int64 img_info;
int64 subimg_info;
VOID_PQ queue;
VOID_PQ address;
void *temp;
PCB *current_pcb;
PHD *current_phd;
DMP *dmp_header;
uint32 dmp_header_size;
char *errlog_buffer;
uint32 errlog_buffer_size;
uint32 line_count;
uint64 instruction [2]; // IA64 needs entire bundle, x86 needs up to 16 too.
UINT64_PQ istream = &instruction [0]; // X86 expects a quadword pointer to the instruction stream
CPU *cpu_db;
uint32 cpu_id;
char faobuf [81];
char *faoptr;
//
// Initialize the table and establish the condition handler
//
sda$vector_table = transfer_table;
lib$establish ( sda$cond_handler );
//
// If invoked with no parameter, display the announcement and exit
//
if ( cmd_line->dsc$w_length == 0 )
{
status = sda$print ( "MBX commands: 'MBX SUMMARY' and 'MBX '" );
sda$skip_lines (1);
status = sda$print ( "Environment: analyzing !AZ!AZ!AZ",
sda_flags.sda_flags$v_current ?
(sda_flags.sda_flags$v_pkrnl ? "primary kernel memory" :
(sda_flags.sda_flags$v_remote ? "a remote system" :
(sda_flags.sda_flags$v_target ? "an SCD target system" :
"the current system"))) :
(sda_flags.sda_flags$v_process ? "a process dump" :
(sda_flags.sda_flags$v_target ? "an SDD target dump" :
"a system dump")),
sda_flags.sda_flags$v_current ? "" :
(sda_flags.sda_flags$v_ia64 ? " from an OpenVMS I64 system" :
(sda_flags.sda_flags$v_x86_64 ? " from an OpenVMS x86-64 system" :
" from an OpenVMS Alpha system")),
sda_flags.sda_flags$v_current ? "" :
(sda_flags.sda_flags$v_override ? " in override mode" : ""));
return;
}
//
// If invoked when analyzing a dump in override mode complain and exit
//
if ( sda_flags.sda_flags$v_override )
{
status = sda$print ( "MBX cannot be used in override mode" );
return;
}
//
// If invoked when analyzing a process dump complain and exit
//
if ( sda_flags.sda_flags$v_process )
{
status = sda$print ( "MBX cannot be used with process dumps" );
return;
}
//
// Allocate buffers for local data structures. We use static pointers
// since this routine might be called multiple times and we only want
// to allocate the local buffers once per SDA session.
// There is no reason to deallocate them at the end of this routine,
// because the user might interrupt the SDA output with another
// command, hence never finish executing this routine to the end.
// Since we don't know how big the LNMB needs to be, we allocate enough
// for the header for now, and then allocate more if/when we need to.
//
if ( ddb == NULL )
sda$allocate (DDB$K_LENGTH, (void *)&ddb );
if ( ucb == NULL )
sda$allocate (UCB$K_MB_UCBLENGTH, (void *)&ucb );
if ( lnmb == NULL )
{
sda$allocate (sizeof (LNMB), (void *)&lnmb );
lnmb_size = sizeof (LNMB);
}
//
// Read in the SYSDEF symbol file and suppress the informational
// message telling how many symbols were found.
//
if ( sysdef_flag == FALSE )
{
status = sda$read_symfile ( "SDA$READ_DIR:SYSDEF", SDA_OPT$M_READ_NOLOG );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$READ_SYMFILE failed, status = !XL", status);
return;
}
sysdef_flag = TRUE;
}
//
// Check if the input passed along is "summary"
//
if ( (cmd_line->dsc$w_length <= 8) && (strncmp (cmd_line->dsc$a_pointer, " SUMMARY", cmd_line->dsc$w_length) == 0) )
{
//
// Set title for each page (exactly one line, automatically
// underscored). For the table we use a separate routine to display
// the column headers. This routine will be called at each page
// break, and we will also start our output on a new page.
//
sda$format_heading ( "Mailbox Device Information" );
sda$set_heading_routine ( mbx$title );
sda$new_page ();
//
// In order to find all the mailbox unit control blocks (UCB), we
// first need to find the device data block (DDB) of the mailbox
// adapter. There is a global symbol pointing to the start of all
// device adapters.
// After retrieving the value of this symbol, the content of it
// points to the start of the loop.
//
status = sda$symbol_value ( "IOC$GL_DEVLIST", (uint64 *)&ioc$gl_devlist );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$SYMBOL_VALUE (ioc$gl_devlist) failed, status = !XL", status);
return;
}
status = sda$getmem (ioc$gl_devlist,
&ddb->ddb$ps_link,
4);
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$GETMEM (ioc$gl_devlist) failed, status = !XL", status);
return;
}
//
// Initialize the flag and the start of the loop through all device
// adapters.
//
flag = FALSE;
do
{
//
// Read the DDB into local memory and check for the mailbox
// adapter string MBA.
//
status = sda$trymem (ddb->ddb$ps_link, ddb, DDB$K_LENGTH);
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$TRYMEM (ddb$ps_link) failed, status = !XL", status);
return;
}
if ( strncmp (ddb->ddb$t_name_str,"MBA",3) == 0 )
{
//
// Found it, set the flag and quit the loop
//
flag = TRUE;
break;
}
}
//
// All DDB's are linked into a zero-terminated single linked list
//
while ( ddb->ddb$ps_link != NULL );
//
// Return if we couldn't locate the mailbox adapter.
//
if ( flag == FALSE )
{
sda$print ( "Could not find Mailbox Adapter" );
return;
}
//
// Initialize the start of the loop through all mailbox UCB's.
//
ucb->ucb$r_ucb.ucb$l_link = ddb->ddb$ps_ucb;
do
{
//
// Save the pointer to the UCB before reading in the unit control
// block into local memory.
//
ucb_addr = ucb->ucb$r_ucb.ucb$l_link;
status = sda$trymem (ucb_addr, ucb, UCB$K_MB_UCBLENGTH);
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$TRYMEM (ucb$l_link) failed, status = !XL", status);
return;
}
//
// If the mailbox has a logical name associated with it, read in
// the logical name block (LNMB). Since we don't know the exact
// size of it upfornt, we read the first octaword which contains
// the size, followed by another read of the whole LNMB into local
// memory.
//
if ( ucb->ucb$l_mb_logadr != NULL )
{
int new_lnmb_size;
status = sda$trymem ( ucb->ucb$l_mb_logadr, lnmb, sizeof (LNMB) );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$TRYMEM (ucb$l_mb_logadr, header) failed, status = !XL", status);
return;
}
new_lnmb_size = lnmb->lnmb$w_size;
//
// make sure we have a big enough LNMB
//
if (new_lnmb_size > lnmb_size)
{
sda$deallocate ( (void *)lnmb, lnmb_size );
sda$allocate ( new_lnmb_size, (void *)&lnmb );
lnmb_size = new_lnmb_size;
}
status = sda$trymem ( ucb->ucb$l_mb_logadr, lnmb, new_lnmb_size );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$TRYMEM (ucb$l_mb_logadr, body) failed, status = !XL", status);
return;
}
}
else
lnmb->lnmb$l_namelen = 0;
//
// Display the results
//
faoptr = sda$fao ( "!6UL !XL !5UL !5UL",
faobuf, sizeof (faobuf),
ucb->ucb$r_ucb.ucb$l_unit,
ucb_addr,
ucb->ucb$l_mb_readerrefc,
ucb->ucb$l_mb_writerrefc );
sda$fao ( " !5UL !9UL !9UL !XL",
faoptr, sizeof (faobuf) - strlen (faobuf),
ucb->ucb$r_ucb.ucb$w_msgcnt,
ucb->ucb$l_mb_iniquo,
ucb->ucb$l_mb_bufquo,
ucb->ucb$r_ucb.ucb$l_mb_msgqfl );
sda$print ( "!AZ !XL !AF",
faobuf,
ucb->ucb$l_mb_readqfl,
lnmb->lnmb$l_namelen,
&lnmb->lnmb$t_name );
}
//
// All UCB's are linked into a zero-terminated single linked list
//
while ( ucb->ucb$r_ucb.ucb$l_link != NULL );
sda$set_heading_routine ( NULL );
}
else
{
//
// The following has really nothing to do with the above mailbox
// information display, but uses a variety of SDA extension routines
// to demonstrate their use. This is the sole purpose of the potpurri
// following below.
//
//
// Check if we are analyzing a running system or a crashdump file.
// Also display the hardware name of the system.
//
sda$get_hw_name ( hw_name, sizeof (hw_name) );
if ( sda_flags.sda_flags$v_current )
{
if ( sda_flags.sda_flags$v_pkrnl )
sda$format_heading (
"SDA Extension Commands on primary kernel memory of a !AZ",
hw_name );
else
sda$format_heading (
"SDA Extension Commands on a !AZ running system",
hw_name );
}
else
sda$format_heading (
"SDA Extension Commands on a !AZ from a crashdump file",
hw_name );
sda$new_page ();
//
// The following input should be the address of anything.
// We will accept either a longword value (will be sign-extended), a
// quadword value or two longwords (separated by a dot '.').
//
longquad.gen64$q_quadword = 0;
if ( sscanf ( cmd_line->dsc$a_pointer,
"%08X.%08X",
&longquad.gen64$l_longword[1],
&longquad.gen64$l_longword[0] ) != 2 )
{
longquad.gen64$q_quadword = 0;
if ( sscanf ( cmd_line->dsc$a_pointer,
"%016LX",
&longquad ) == 1 )
{
if ( (longquad.gen64$l_longword[1] == 0) &
((longquad.gen64$l_longword[0] & 0x80000000) > 0) )
longquad.gen64$l_longword[1] = 0xFFFFFFFF;
}
else
lib$signal ( SS$_INVARG );
}
//
// display the address passed as input
//
sda$skip_lines (1);
sda$print ( "Input address: !@XQ", &longquad );
//
// Try to read the first octaword in from this address.
// Since we use SDA$GETMEM instead of SDA$TRYMEM, SDA will
// signal any errors.
//
status = sda$getmem ( (VOID_PQ)longquad.gen64$q_quadword,
ucb,
16);
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$GETMEM (!XL.!XL) failed, status = !XL",
longquad.gen64$l_longword[1],
longquad.gen64$l_longword[0],
status);
return;
}
//
// See if this address points to a known data structure type.
// If there is no named type/subtype for a particular structure,
// then an empty string will be returned. This example uses the
// UCB offsets for the type and subtype fields: Any structure
// type would work just as well.
//
sda$get_block_name ( ucb->ucb$r_ucb.ucb$b_type,
ucb->ucb$r_ucb.ucb$b_flck,
buffer,
sizeof (buffer) );
if ( strlen (buffer) == 0 )
sda$print ( "Block type: no named type/subtype" );
else
sda$print ( "Block type: !AZ", buffer );
//
// Now also try to symbolize this address
//
status = sda$symbolize ( longquad.gen64$q_quadword,
buffer,
sizeof (buffer) );
if ( status == SS$_NOTRAN )
sda$print ( "Address could not be symbolized" );
else
sda$print ( "Address symbolization: !XL.!XL !AZ",
longquad.gen64$l_longword[1],
longquad.gen64$l_longword[0],
buffer );
//
// Define a SDA symbol MBX_TEMP containing the address passed
// as input and redo the symbolization to show the diff
//
sda$add_symbol ( "MBX_TEMP", longquad.gen64$q_quadword );
status = sda$symbolize ( longquad.gen64$q_quadword,
buffer,
sizeof (buffer) );
if ( status == SS$_NOTRAN )
sda$print ( "Address could not be symbolized" );
else
sda$print ( "Address symbolization: !XL.!XL !AZ",
longquad.gen64$l_longword[1],
longquad.gen64$l_longword[0],
buffer );
//
// Now try and see if it falls within an execlet, process activated
// or resident image
//
sda_cio = sda$get_image_offset ( (VOID_PQ) longquad.gen64$q_quadword,
&img_info,
&subimg_info,
&offset );
if ( sda_cio.sda_cio$v_valid )
{
if ( sda_cio.sda_cio$v_process )
{
imcb = (IMCB *) img_info;
sda$print ( "Process activated image:" );
#if ALPHA // Verified for x86 port - Richard Bishop
if ( sda_cio.sda_cio$v_sliced )
{
kferes_section = (KFERES_SECTION *) subimg_info;
sda$print ( "Name: !AF Offset: !XL Base: !XL",
imcb->imcb$t_image_name [0],
&imcb->imcb$t_image_name [1],
offset,
kferes_section->kferes$l_va );
}
else
sda$print ( "Name: !AF Offset: !XL Base: !XL",
imcb->imcb$t_image_name [0],
&imcb->imcb$t_image_name [1],
offset,
imcb->imcb$l_base_address );
#endif
#if IA64 // Verified for x86 port - Richard Bishop
isd = (LDRISD *) subimg_info;
sda$print ( "Name: !AF Offset: !XL Base: !XL",
imcb->imcb$t_image_name [0],
&imcb->imcb$t_image_name [1],
offset,
isd->ldrisd$p_base );
#endif
#if X86_64 // Verified for x86 port - Richard Bishop
isd = (LDRISD *) subimg_info;
sda$print ( "Name: !AF Offset: !XL Base: !XL.!XL",
imcb->imcb$t_image_name [0],
&imcb->imcb$t_image_name [1],
offset,
(uint32)((uint64)isd->ldrisd$pq_base >> 32),
(uint32)((uint64)isd->ldrisd$pq_base % 0xFFFFFFFF ));
#endif
}
else
{
ldrimg = (LDRIMG *) img_info;
isd = (LDRISD *) subimg_info;
sda$print ( "System loaded image or resident installed image:" );
#if ALPHA // Verified for x86 port - Richard Bishop
if ( !sda_cio.sda_cio$v_sliced )
sda$print ( "Name: !AF Offset: !XL Base: !XL",
ldrimg->ldrimg$l_imgnamlen,
ldrimg->ldrimg$ps_imgnam,
offset,
ldrimg->ldrimg$l_base );
else
#endif
#if ALPHA || IA64 // Verified for x86 port - Richard Bishop
sda$print ( "Name: !AF Offset: !XL Base: !XL",
ldrimg->ldrimg$l_imgnamlen,
ldrimg->ldrimg$ps_imgnam,
offset,
isd->ldrisd$p_base );
#endif
#if X86_64 // Verified for x86 port - Richard Bishop
sda$print ( "Name: !AF Offset: !XL Base: !XL.!XL",
ldrimg->ldrimg$l_imgnamlen,
ldrimg->ldrimg$pq_imgnam,
offset,
(uint32)((uint64)isd->ldrisd$pq_base >> 32),
(uint32)((uint64)isd->ldrisd$pq_base % 0xFFFFFFFF ));
#endif
}
}
else
sda$print ( "Address does not fall within a known image" );
//
// Now throw in a few queue validation commands
//
sda$new_page ();
status = sda$symbol_value ( "IOC$GQ_MOUNTLST", (uint64 *)&address );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$SYMBOL_VALUE (ioc$gq_mountlst) failed, status = !XL", status);
return;
}
sda$reqmem ( address, &temp, 4 );
queue = temp;
sda$print ( "*** Validate queue/list ioc$gq_mountlst ***" );
sda$validate_queue ( queue, SDA_OPT$M_QUEUE_LISTQUEUE );
sda$skip_lines (1);
status = sda$symbol_value ( "EXE$GQ_PQBIQ", (uint64 *)&queue );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$SYMBOL_VALUE (exe$gq_pqbiq) failed, status = !XL", status);
return;
}
sda$print ( "*** Validate queue/self/list exe$gq_pqbiq ***" );
sda$validate_queue ( queue, SDA_OPT$M_QUEUE_SELF | SDA_OPT$M_QUEUE_LISTQUEUE );
sda$skip_lines (1);
status = sda$symbol_value ( "EXE$GL_NONPAGED", (uint64 *)&address );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$SYMBOL_VALUE (exe$gl_nonpaged) failed, status = !XL", status);
return;
}
address = (VOID_PQ)((int64)address + 4);
sda$reqmem ( address, &queue, 4 );
sda$print ( "*** Validate queue/single/list exe$gl_nonpaged+4 ***" );
sda$validate_queue ( queue, SDA_OPT$M_QUEUE_SINGLINK );
//
// Now some other assorted examples
//
// First, bugcheck message codes:
//
sda$skip_lines (1);
sda$print ( "*** sda$get_bugcheck_msg ***" );
sda$get_bugcheck_msg ( 0x108, buffer, sizeof (buffer) );
sda$print ( "Bugcheck code 108 (hex) =" );
sda$print ( "!_\"!AZ\"", buffer );
//
// Next, sda$ensure & sda$dbg_image_info
//
sda$skip_lines (1);
sda$print ( "*** sda$ensure & sda$dbg_image_info ***" );
sda$ensure (10);
sda$dbg_image_info ();
//
// Demonstrate sda$get_current_pcb and sda$format
//
sda$new_page ();
sda$print ( "*** format pcb & phd ***" );
sda$skip_lines (1);
sda$get_current_pcb ( ¤t_pcb );
sda$format ( current_pcb );
sda$reqmem ( (VOID_PQ)¤t_pcb->pcb$l_phd, ¤t_phd, 4 );
sda$skip_lines (1);
sda$format ( current_phd, SDA_OPT$M_FORMAT_TYPE, "PHD" );
sda$skip_lines (1);
//
// Demonstrate sda$get_header
//
sda$new_page ();
sda$print ( "*** sda$get_header ***" );
sda$get_header (&dmp_header, &dmp_header_size,
(void **)&errlog_buffer, &errlog_buffer_size);
sda$print ("Dump header located at !XL, size !XL bytes",
dmp_header, dmp_header_size );
sda$print ("Error log buffer is at !XL, size !XL bytes",
errlog_buffer, errlog_buffer_size );
//
// Demonstrate sda$get_input
//
sda$skip_lines (1);
sda$print ( "*** sda$get_input ***" );
sda$print ( "Enter any text, it will be echoed until is seen" );
sda$skip_lines (1);
while ( $VMS_STATUS_SUCCESS (status) )
{
status = sda$get_input ( "MBX> ", buffer, sizeof (buffer) );
if ( (!$VMS_STATUS_SUCCESS (status)) && (status != RMS$_EOF) )
{
sda$print ( "SDA$GET_INPUT failed, status = !XL", status);
return;
}
sda$print ( "!AZ", buffer );
}
//
// Demonstrate sda$get_line_count and sda$set_line_count
//
sda$skip_lines (1);
sda$print ( "*** sda$get_line_count & sda$set_line_count ***" );
sda$get_line_count ( &line_count );
sda$print ( "Line count = !SL. Resetting to zero...", line_count );
sda$set_line_count (0);
//
// Demonstrate sda$get_address and sda$set_address
//
sda$skip_lines (1);
sda$print ( "*** sda$get_address & sda$set_address ***" );
sda$get_address ( &address );
sda$print ( "SDA current address = !XL.!XL. Resetting to FFFFFFFF.80102030...",
(int64)address>>32, (int64)address );
sda$set_address ( (VOID_PQ)0xFFFFFFFF80102030 );
//
// Demonstrate sda$parse_command
//
sda$skip_lines (1);
sda$print ( "*** sda$parse_command ***" );
sda$print ( "Executing \"SHOW ADDRESS 80102030\"..." );
sda$parse_command ( "SHOW ADDRESS 80102030" );
//
// Demonstrate sda$get_current_cpu, sda$set_cpu and sda$set_process
//
sda$skip_lines (1);
sda$print ( "*** sda$get_current_cpu, sda$set_cpu & sda$set_process ***" );
sda$get_current_cpu ( &cpu_db );
sda$reqmem (&cpu_db->cpu$l_phy_cpuid, &cpu_id, 4);
sda$print ( "Current CPU database = !XL. Resetting to CPU ID !XL (no change)...",
(int64) cpu_db ,
cpu_id );
sda$set_cpu ( cpu_id );
sda$print ( "Setting current process to SWAPPER...");
sda$set_process ( "SWAPPER" , 0 , 0 );
sda$parse_command ( "SHOW PROCESS" );
//
// Demonstrate sda$get_device_name
//
sda$skip_lines (1);
sda$print ( "*** sda$get_device_name ***" );
sda$parse_command ( "SET OUTPUT NL:" );
sda$parse_command ( "SHOW DEVICE OPA0:" );
sda$parse_command ( "SET OUTPUT TT:" );
sda$symbol_value ( "UCB", (uint64 *)&address );
sda$get_device_name ( (VOID_PQ)address, buffer, 128);
sda$print ( "UCB address: !XL = ""!AZ:""",
address,
buffer);
//
// Demonstrate sda$type
//
sda$skip_lines (1);
sda$print ( "*** sda$type ***" );
sda$print ( "Redirecting output to MBX$SDA.TMP..." );
sda$parse_command ( "SET OUTPUT /NOINDEX MBX$SDA.TMP" );
sda$type ( "Invoking SHOW SUMMARY to output file..." );
sda$parse_command ( "SHOW SUMMARY" );
sda$type ( "Redirecting output to the terminal..." );
sda$parse_command ( "SET OUTPUT TT:" );
//
// Demonstrate sda$instruction_decode
//
sda$skip_lines (1);
sda$print ( "*** sda$instruction_decode ***" );
status = sda$symbol_value ( "EXE_STD$ALLOCATE_C", (uint64 *)&address );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$SYMBOL_VALUE (exe_std$allocate_c) failed, status = !XL", status);
return;
}
if (sda_flags.sda_flags$v_x86_64)
{
//
// For X86_64, do some number of bytes (up to 16, maximum length of an instruction)
//
sda$reqmem ( address, &instruction, 16);
status = sda$instruction_decode ( &istream, buffer, sizeof (buffer), template, sizeof (template) );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$INSTRUCTION_DECODE failed, status = !XL", status);
return;
}
sda$print ( "EXE_STD$ALLOCATE_C: !AZ", buffer );
}
else if (sda_flags.sda_flags$v_ia64)
{
//
// For IA64, do the entire bundle
//
sda$reqmem ( address, &instruction, 16);
status = sda$instruction_decode ( &istream, buffer, sizeof (buffer), template, sizeof (template) );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$INSTRUCTION_DECODE failed, status = !XL", status);
return;
}
sda$print ( " { !AZ", template );
sda$print ( "EXE_STD$ALLOCATE_C: !AZ", buffer );
while (((int)istream & 7) != 0) // local buffer only guaranteed to be quadword aligned
{
status = sda$instruction_decode ( &istream, buffer, sizeof (buffer) );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$INSTRUCTION_DECODE failed, status = !XL", status);
return;
}
sda$print ( " !AZ", buffer );
}
sda$print ( " }" );
}
else
{
//
// For Alpha, do one longword instruction
//
sda$reqmem ( address, &instruction, 4);
status = sda$instruction_decode ( &istream, buffer, sizeof (buffer) );
if ( !$VMS_STATUS_SUCCESS (status) )
{
sda$print ( "SDA$INSTRUCTION_DECODE failed, status = !XL", status);
return;
}
sda$print ( "EXE_STD$ALLOCATE_C: !AZ", buffer );
}
//
// Demonstrate sda$display_help
//
sda$new_page ();
sda$print ( "*** sda$display_help ***" );
sda$print ( "Use to exit help" );
sda$display_help ("SYS$HELP:SDA", "HELP");
sda$skip_lines (1);
//
// And finally show what the condition handler produces if an error
// is signaled
//
sda$new_page ();
sda$print ( "Expect to see %SYSTEM-E-INVARG message, image list, register dump, etc." );
sda$print ( "This is to demonstrate the built-in handler SDA$COND_HANDLER" );
sda$skip_lines (1);
lib$signal ( SS$_INVARG );
}
return;
}