Municipality of the City of Vienna
Ing. Ferry Bolhár-Nordenkampf
Writing SDA Extensions

Writing SDA Extensions

This manual explains how to write SDA extensions.

May 1996

Revision Information: This is a new manual.

OpenVMS Version: VMS V5.5 or higher

Manual Version: V1.0-03


Contents


Preface

This manual explains how to write SDA extensions. It is intended for the system programmer who want to add new commands to SDA and extend its capabilities to format and display the contents of various data structures.

This manual is divided into the following sections:

Chapter 1 contains a short introduction to SDA extensions. It explains the purpose and how they are implemented.

Chapter 2 explains in detail how extensions are activated. It also gives you some hints how to code the extension's initialization routine.

Chapter 3 explains in detail the SDA environment wherein the extension runs and gives a first introduction to SDA's callback routines.

Appendix A shows how to link and debug your extension.

Finally, Appendix B contains a detailed description of all SDA extension callbacks.

It is assumed that the reader is familiar with the SDA utility and with the VMS programming concepts. The following OpenVMS manuals contain information about these topics and may be helpful:

This manual was written and inspected carefully. You may find, however, some errors or have comments and suggestions. Please feel free to mail to the author, any input is very appreciated. The E-mail address is:
Ing. Ferry Bolhár-Nordenkampf
E-mail: bol@adv.magwien.gv.at.

And now: enjoy and good luck when writing your extension!


Note

Addendum to this manual, release date 20-AUG-1997:

As indicated in the OpenVMS V7.2 pre-release notes, writing SDA extensions becomes a supported task under OpenVMS Alpha V7.2 (it will still remain unsupported on OpenVMS VAX). The names of callback routines may change and their behaviour may change as well, to confirm the OpenVMS calling standard and to make the routines callable from any high-level language.

If you plan to write SDA extensions for OpenVMS Alpha V7.2 or higher, you should read carefully the OpenVMS Alpha V7.2 Release Notes as well as the System Dump Analyzer (Alpha) Utility manual, version 7.2. However, if you write your extension for earlier OpenVMS Alpha versions, as well for OpenVMS VAX, this manual contains all information you need.

A description of already known changes is given in Appendix C.



Chapter 1
Introduction to SDA extensions

Starting with VAX/VMS V5.5, SDA (System Dump Analyzer) supports extensions to provide additional, product-specific or customer-specific commands and to increase SDA's capabilities to interpret and display various data structures. This manual explains how to write your own SDA extension tailored to your needs.

When analyzing a dump file or the running system, you need intimate knowledge of data structures that are not known to SDA. For example, a user-written ACP (ancilliary control process) may allocate non-paged pool and record some data or place I/O buffers therein. Without an extension, the only way to examine this data is to create a hexdump with the EXAMINE command and manually decode the contents of the dump.

With a SDA extension, you can make the format of the data structures and buffers known to SDA and provide a way to display their contents in an interpreted, much more understandable way.

DEC's TCP/IP Services for OpenVMS, also known as UCX, is an example for a layered product which provides its own SDA extension to display information about its data structures ¹.

Another example (which exists on Alpha only) might be CLUE(Crash Log Utility Extractor), which allows you to maintain system dump history logs and provides additional commands to display more detailed information about OpenVMS system cells and data structures ².

SDA invokes an extension when the extension's name is entered at the SDA> prompt. Once activated, SDA performs a small version check to ensure that the command and data passing mechanism is still the one the extension expects. SDA then passes control to the extension's initialization (or main) routine. The way SDA invokes an extension is described in detail in Chapter 2.

Once the extension receives control, it may do some initialization work and then execute the given user commands. These commands may be supplied either with the extension name at the SDA> prompt or the extension is activated without additional commands in which case it should prompt for them. If the extension was activated with a command, it should execute the command and return control back to SDA. In the latter case, it should prompt for commands and execute them, until EXIT was entered or [CTRL] [Z] was typed. This is the usual behaviour of most VMS utilities which provide their own subcommands.

The extension should also provide online help, either with a built-in text, or better, by an external help library whose topics may be called with HELP.

Chapter 2 gives some hints how to build an extension 'skeleton' which provides the above functions and commands. Chapter 2 contains code examples as well.

While activated, the extension operates in SDA's environment and may call SDA's extension callback routines, shorter callbacks. SDA provides over 20 callbacks for various purposes: examining memory, validating queues, executing SDA commands, displaying data in various formats, and much more. Whenever possible, you should use these callbacks instead of native commands or functions provided by your language. This ensures that your extension behaves in an usual manner. For example, in C, you could use the printf() function to display data on the screen. If, however, the user enters SDA's SET OUTPUT command, SDA writes further text into the given output file whereas your extension still would continue to send its data to the screen. For this reason, SDA provides callbacks to display data. By using these callbacks, your extension sends its data always to the expected location.

Another reason for using callbacks is SDA's environment which may change from time to time. Let's assume that you want to access a cell in the process-control (P1) space. Most languages provide a way to declare the name of VMS cells as global symbols whose addresses are substituted by the linker when linked against SYS$SYSTEM:SYS.STB ³. In the program, the cell may be further accessed like any other variable.

The P1 space, however, is process-specific. Every process contains its own, separate P1 space. There is no (easy) way for a program to access the P1 (and P0) space of a different process.

SDA provides the SET PROCESS command for this purpose. This will change SDA's process context; the specified process becomes the current process. By convention, when process-specific data is addressed, it's always the data of the current process. For this reason, SDA provides routines, which, when a P0 or P1 address is given, obtain the data from the current process' address space. In addition, these routines allow you to access data from user mode which would be accessible otherwise from executive or kernel mode only. Finally, the routines contain their own condition handlers, preventing the system to crash when you specify an invalid or non-existent address.

The SDA environment is described further in Chapter 3.

SDA extensions may be written in any high-level language (HLL). However, not all callbacks confirm the OpenVMS calling standard. For example, some routines return results in processor registers or must be called as subroutines, not as procedures. Unless your language provides a way to access processor register directly and call subroutines, jacket routines, mostly written in MACRO-32, must be provided. The purpose of a jacket routine is simple: it calls the appropriate SDA callback in the required fashion and places the returned data in a location where it can be accessed by the HLL. From the HLL, you invoke the jacket routine instead of the corresponding callback.

Appendix A explains how to link and debug SDA extensions. Examples for jackets are provided in the description of the appropriate callback.

Finally, Appendix B contains a detailed description of all SDA extension callbacks as well tables giving an overview about them, their purpose and their calling conventions.


Note

¹ UCX V3.2 or higher only.

² On VAX, CLUE exists as well, however, it is provided as external utility, invoked with the DCL command $ MCR CLUE. Unlike Alpha, VAX CLUE provides no additional displays and is used to create and maintain system dump logs and history files only.
The author of this manual wrote a CLUE SDA extension for OpenVMS VAX; it provides the same (and additional) information as its Alpha counterpart. If you're interested in obtain a copy, please send a mail to bol@adv.magwien.gv.at.

³ or linked with /SYSEXE on Alpha.



Chapter 2
Extension activation and initialization

This chapter explains how extensions are activated and how SDA passes control to them.

2.1 SDA Extension Implementation

SDA extensions are implemented as shareable images, either located in the directory SYS$SHARE or found by translating a logical name. When the extension's name is entered at the SDA> prompt, SDA uses the RTL routine LIB$FIND_IMAGE_SYMBOL to load the extension. See the description in the RTL Library (LIB$) manual for more information about this routine and the VMS run-time image load mechanism.

By convention, SDA extensions are named name $SDA, where name is the extension's name. For example, in case of UCX, the extension would be named UCX$SDA. You may define this string as a logical name pointing to the actual shareable image, or (in this example) the image SYS$SHARE:UCX$SDA.EXE must exist.

SDA tries to activate an extension whenever an unknown command is entered at the SDA> prompt. If the activation fails for some reason, SDA displays this message:

%CLI-W-SYNTAX, error parsing 'name' 

When you enter a command, SDA first looks in its table of built-in commands. If the command (or a valid abbreviation) can be found therein, it is executed, instead of activating an extension with the same name. For example, if you call your extension EXAM$SDA and enter EXAM, SDA assumes that an abbreviation of its EXAMINEcommand was entered, not an extension name. In this case, you must force SDA to interpret the given name as extension by specifying the undocumented DO command:

SDA> DO EXAM

With DO, when the activation fails, SDA displays a different message:

%SDA-E-SUPPNOTINS, name support not installed 

where name is the specified extension name.


Note

The above message may be confusing. SDA displays this message whenever it fails to activate the specified extension. This does not necessarily mean that the extension is not installed; the activation may fail for other reasons as well (e.g., ident mismatches with other shareable images).

To find out what causes the activation to fail, try to invoke the extension with the DCL command RUN. In most cases, you will receive a %IMGACT-F-... message. This is an error message created by the image activator and is also passed to LIB$FIND_IMAGE_SYMBOL as return status. In most cases, this message can help you to isolate the problem.

Also keep in mind that the activation fails when compilation warnings were detected by the linker in one or more extension modules.


2.2 Extension activation

As described in Section 2.1, extensions are activated by calling the RTL routine LIB$FIND_IMAGE_SYMBOL. In addition to the shareable image's name, this routine requires a symbol name to load the image. This symbol must be included in the shareable image's symbol table when linking the extension (Appendix A describes how to compile and link your extension and how to build a symbol table). SDA calls LIB$FIND_IMAGE_SYMBOL twice with the following symbol names:

If these symbols are not found in the shareable image's symbol table, the activation fails with one of the error messages described in Section 2.1.

Section 2.2.1 and Section 2.2.2 describe the purpose and contents of these symbols in detail.

2.2.1 Activation interface version check

SDA $EXTEND _VERSION contains the address of a longword which holds the extension's activation version. This is not the version of the extension; it just determines the version of the activation interface. It consists of two parts, a major and minor version number. Before passing control to the extension, SDA compares its own interface version with the one supplied by the extension to ensure that the interfaces are compatible. Further activation occurs only if:

For example, if the SDA version number would be V1.2, the extension's number may be one of V1.2, V1.3, V1.4 ... V1.9, but not V1.0, V1.1 or V2.0.


Note

The version number of the SDA extension interface is still V1.0 in all current VMS releases. It is recommended that your extension uses the same version number. In the case of an activation interface change, this will prevent your extension from being activated in an unexpected manner.

The following example shows how to set the version number in C:

const int SDA$EXTEND_VERSION = 0x00000001; 

See Appendix A how to include the variable name in the shareable image's symbol table. Note that the variable must be defined in global scope (in C, outside of any function).

2.2.2 Calling the initialization routine

SDA $EXTEND contains the address of the extension's initialization routine. Once the version check is performed, SDA calls the extension at this address. The following two arguments are passed to this routine:

These arguments are now described more in detail.

Note that SDA does not expect a return value from the extension; if the extension supplies one, it is ignored.

2.2.2.1 Callback pointer

The first argument passed to the initialization routine is the base address of an array containing pointers to all callback routines. In order to understand the use of this argument, you should become familiar with SDA's callback mechanism.

Callbacks are internal SDA routines, located at various places in SYS$SYSTEM:SDA.EXE, the system dump analyzer (invoked with ANALYZE/CRASH _DUMP or ANALYZE/SYSTEM). Since these addresses may vary from VMS to VMS version, an array consisting of longword pointers is built. Every longword points to a callback routine in SDA.EXE. Although the base address of this array, which is located itself in SDA.EXE, may change in new VMS versions, the position of a particular routine within the array never changes; new routines are added at the end of the array. In other words, the purpose of this array is very similar to a transfer vector in a shareable image. Note, however, that SDA.EXE is an executable image which can't be used as input to the linker.

The interface to SDA.EXE is the shareable image SYS$SHARE:SDA_EXTEND_VECTOR.EXE ¹ which your extension must be linked against. This image contains jacket routines for every callback in SDA.EXE. When you call one of these jacket routines, it first adds the array offset of the corresponding SDA routine to the array's base address. The resultant value is the absolute address of the longword which points to the callback routine in SDA.EXE. This routine is then invoked.

All jacket routines expect the array's base address in global location SDA $EXTEND _VECTOR _TABLE _ADDR. This is a writable longword in SYS$SHARE:SDA_EXTEND_VECTOR.EXE (VAX) or SYS$SHARE:SDA_EXTEND_VECTOR.OBJ (Alpha). Before invoking the first callback, you must fill this location with the array base address, which is passed as first argument to your extension. An example for this could be:

int globalref SDA$EXTEND_VECTOR_TABLE_ADDR; 
 
void sda$extend(int array_addr, struct dsc$descriptor_s *cmd) 
{ 
 SDA$EXTEND_VECTOR_TABLE_ADDR = array_addr; 
 .
 .
 .
} 

From this point, you can use all callbacks. They are described in Appendix B.

2.2.2.2 Command pointer

The second argument passed to the initialization routine is the address of a fixed-length string descriptor pointing to the specified command string, if any was given. If no command was given, the length field of the descriptor contains 0.

Note that the extension name (and a preceeding DO) were already removed from the string. For example, if

SDA> MYEXT SHOW ALL

was entered, the string passed in this argument would be SHOW ALL.

As described in Chapter 1, the usual behaviour of an extension providing its own commands is as follows:

It is up to the extension to parse the supplied command string. A good and easy way to do this are the CLI$ routines in conjunction with an object module containing binary language definitions. This module is written in CDL (Command Definition Language) and compiled with CDU (Command Definition Utility). See Section 2.3 for an example.


Note

¹ On Alpha, there is no shareable image, instead, an object file SYS$SHARE:SDA_EXTEND_VECTOR.OBJ is provided for the same purpose.


2.3 Extension code examples

This chapter contains some code examples of:

All relevant coding techniques in the following examples are described in the previous chapters. In the following examples, the extension's name becomes MYEXT.

2.3.1 Initialization routine

The following code fragment, written in C, may be used as initialization routine. It behaves as described in Section 2.2.2.2.

 
 
#include <cli$routines.h>          /* CLI$xxx routine definitions */ 
#include <descrip.h>               /* Descriptor definitions */ 
#include <rmsdef.h>                /* RMS$_xxx status definitions */ 
#include <string.h>                /* strxxx() functions */ 
#include <ssdef.h>                 /* SS$_xxx status definitions */ 
 
 
typedef struct dsc$descriptor_s dsc; 
 
/* SDA callback definitions. */ 
 
void SDA$EXTEND_PRINT(),           /* Print a line of text */ 
     SDA$EXTEND_NEW_PAGE(),        /* Insert new page */ 
     SDA$EXTEND_SKIP_LINES(),      /* Insert blank line(s) */ 
     SDA$EXTEND_GET_INPUT();       /* Get a line from the user */ 
 
int globalref SDA$EXTEND_VECTOR_TABLE_ADDR; /* Callback array base addr. */ 
 
const int SDA$EXTEND_VERSION = 0x00000001;  /* Activation vers. */ 
 
int globalvalue MYEXT$CDL;          /* CDL Module */ (1)
 
int G_exit_flg;                     /* Global exit flag */ (2)
 
dsc* desc();                        /* Descriptor function */ 
 
 
/* ======== Initialization routine starts here. ======== */ 
 
void sda$extend(int array_addr, dsc *cmd) 
{ 
 int status;                       /* CLI return status */ 
 
 $DESCRIPTOR(prp,"MYEXT$SDA>" );   /* Our prompt */ 
 
 /* First, store array base address in shareable image. */ 
 
 SDA$EXTEND_VECTOR_TABLE_ADDR = array_addr; 
 
 if (cmd->dsc$w_length)            /* If command was given */ (3)
 { 
  status = cli$dcl_parse(cmd,MYEXT$CDL);  /* Parse it */ (4)
 
  /* If parsing was OK, execute the command. */ 
 
  if (status == CLI$_NORMAL) status = cli$dispatch(); 
 
  return;                          /* Return back to SDA *. 
 } 
 else                              /* Otherwise */ 
 { 
  SDA$EXTEND_NEW_PAGE();           /* Insert new page */ (5)
 
  /* Display welcome message. */ 
 
  SDA$EXTEND_PRINT(desc("Welcome to MYEXT, my nice SDA extension!")); 
 
  SDA$EXTEND_SKIP_LINES(2);        /* Insert two lines */ 
 
  /* Prompt for command - use SDA callback instead of LIB$GET_INPUT 
     to read the command. */ 
 
  status = cli$dcl_parse(0,MYEXT$CDL,SDA$EXTEND_GET_INPUT, (6)
                         SDA$EXTEND_GET_INPUT,&prp); 
 
  while (status != RMS$_EOF)       /* Loop until EOF (CTRL-Z) */ (7)
  { 
   if (status == CLI$_NORMAL) status = cli$dispatch();  /* Execute command */ 
 
   if (G_exit_flg) return;         /* If EXIT, terminate */ (8)
 
   status = cli$dcl_parse(0,MYEXT$CDL,SDA$EXTEND_GET_INPUT, /* Prompt for */ 
                          SDA$EXTEND_GET_INPUT,&prp);       /* next command */ 
  } 
  return;                          /* Return to SDA */ 
 } 
} 
 

Notes to this example:

  1. The module MYEXT$CDL contains the definition of all known extension commands. It was written in CDL (Command Definition Language) and built with SET COMMAND/OBJECT.
  2. This global location initially contains 0. It is set to 1 by the extension's EXIT command (see Section 2.3.2).
  3. The length of the command given when the extension was activated is tested. If the length is not 0, the extension was activated with one of its commands.
  4. We parse the command using the CDL module and dispatch to it. After this, we return back to SDA.
  5. If no command was given, we must prompt for one. First, let's clear the screen and display a nice welcome message. This is done with SDA callbacks. Given a null-terminated string, the function desc() returns a fixed-length descriptor pointing to this string. This is required since most callbacks, when strings are supplied, expect pointers to descriptors.
  6. Prompt for a command and parse it. In the arguments to CLI$DCL_PARSE, we use SDA callbacks as prompt and read routines, instead of LIB$GET_INPUT. This gives access to SDA's command recall buffer and predefined keys.
  7. If [CTRL] [Z] was typed, we can leave the loop. Otherwise, we execute the command and prompt for a new one.
  8. This location is set to 1 by the EXIT command. If so, we must leave the command loop as well.

The code for the desc() function follows:

dsc *desc(char *str) 
{ 
 static $DESCRIPTOR(x,""); 
 
 x.dsc$w_length = strlen(str); 
 x.dsc$a_pointer= str; 
 
 return &x; 
} 

Since the descriptor must survive the function's exit, it must be declared as 'static' (in C).

2.3.2 EXIT command

If the extension was invoked without a command, it must prompt for them until the EXIT command is entered. As you can see in the code example in Section 2.3.1, this is indicated by setting the global variable G_exit_flag to 1. So the only thing we must do here is setting this variable:

void myext_exit() 
{ 
 G_exit_flg = 1; 
} 

If your favorite language does not support global variables, you could indicate the execution of the EXIT command by a special return status. In the main command loop, you could check the status returned by CLI$DISPATCH and terminate the loop if this special status is encountered.

2.3.3 HELP command

Every program which supports more than one command should provide online-help. Although you can give this help by a built-in text, a more friendly and VMS-conform way is to provide a HELP command and a help text library. By entering HELP without a topic, the user receives a general help text, including a list of all topics for which help is available. He may further select a topic to obtain more information about it.

You can create help text files with any text editor. You use LIBRARY/HELP/CREATE to create your help library and LIBRARY/HELP/INSERT to insert help topics into your library. See the Command Definition, Librarian and Message Utilities manual for more information.

This is the code fragment of the HELP command in the command definition file:

define verb HELP routine MYEXT_HELP 
parameter P1, prompt="Topic", value(type=$rest_of_line) 

And here comes the help routine:

#include <descrip.h> 
 
int cli$get_value(); 
 
void SDA$EXTEND_DISPLAY_HELP(); 
 
void myext_help() 
{ 
 char dcl_buf[120]; 
 
 short len; 
 
 $DESCRIPTOR(dcl,dcl_buf); 
 
 $DESCRIPTOR(dcl_p1,"P1"); 
 $DESCRIPTOR(lib,"MYEXT_HELP"); 
 
 cli$get_value(&dcl_p1,&dcl,&len);     /* Get topic */ 
 
 dcl.dsc$w_length = len;               /* Update length */ 
 
 SDA$EXTEND_DISPLAY_HELP(&lib,&hlp);   /* Display help */ 
} 

In this example, the name of the help library is MYEXT_HELP. This must be either a logical name pointing to the help library, or the library is expected to be SYS$HELP:MYEXY_HELP.HLB.

As you can see, SDA even provides a callback to display online-help which you should use instead of the appropriate VMS routine LBR$OUTPUT_HELP.


Chapter 3
Running in SDA's environment

Once activated, the extension runs under control of SDA. SDA establishes a default environment from which it interprets certain commands. Your extension should behave exactly in the same manner, so it's important to understand in which environment you're running and how commands may be interpreted.

SDA's environment consists of the following:


Next | Contents