[Return to Library]  [TOC]  [PREV]  SECT--  [NEXT]  [INDEX] [Help]

3    Writing Extensions to the kdbx Debugger

To assist in debugging kernel code, you can write an extension to the kdbx debugger. Extensions interact with kdbx and enable you to examine kernel data relevant to debugging the source program. This chapter provides the following:

The Digital UNIX Kernel Debugging Tools subset must be installed on your system before you can create custom extensions to the kdbx debugger. This subset contains header files and libraries needed for building kdbx extensions. See Section 3.1 for more information.


[Return to Library]  [TOC]  [PREV]  SECT--  [NEXT]  [INDEX] [Help]

3.1    Basic Considerations for Writing Extensions

Before writing an extension, consider the following:

Before you write an extension, become familiar with the library routines in the libkdbx.a library. These library routines provide convenient methods of extracting and displaying kernel data. The routines are declared in the /usr/include/kdbx.h header file and described in Section 3.2.

You should also study the extensions that are provided on your system in the /var/kdbx directory. These extensions and the example extensions discussed in Section 3.3 can help you understand what is involved in writing an extension and provide good examples of using the kdbx library functions.


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2    Standard kdbx Library Functions

The kdbx debugger provides a number of library functions that are used by the resident extensions. You can use these functions, which are declared in the ./usr/include/kdbx.h header file, to develop customized extensions for your application. To use the functions, you must include the ./usr/include/kdbx.h header file in your extension.

The sections that follow describe the special data types defined for use in kdbx extensions and the library routines you use in extensions. The library routine descriptions show the routine syntax and describe the routine arguments. Examples included in the descriptions show significant lines in boldface type.


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.1    Special kdbx Extension Data Types

The routines described in this section use the following special data types: StatusType, Status, FieldRec, and DataStruct. The uses of these data types are as follows:


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.2    Converting an Address to a Procedure Name

The addr_to_proc function returns the name of the procedure that begins the address you pass to the function. If the address is not the beginning of a procedure, then a string representation of the address is returned. The return value is dynamically allocated by malloc and should be freed by the extension when it is no longer needed.

This function has the following syntax:

char *addr_to_proc(long addr );


ArgumentInput/OutputDescription
addr  Input  Specifies the address that you want converted to a procedure name 

For example:

conf1 = addr_to_proc((long) bus_fields[3].data);
conf2 = addr_to_proc((long) bus_fields[4].data);
sprintf(buf, "Config 1 - %sConfig 2 - %s", conf1, conf2);
free(conf1);
free(conf2);


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.3    Getting a Representation of an Array Element

The array_element function returns a representation of one element of an array. The function returns non-NULL if it succeeds or NULL if an error occurs. When the value of error is non-NULL, the error argument is set to point to the error message. This function has the following syntax:

DataStruct array_element(DataStruct sym , int i , char ** error );


ArgumentInput/OutputDescription
sym  Input  Names the array 
i  Input  Specifies the index of the element 
error  Output  Returns a pointer to an error message, if the return value is NULL 

You usually use the array_element function with the read_field_vals function. You use the array_element function to get a representation of an array element that is a structure or pointer to a structure. You then pass this representation to the read_field_vals function to get the values of fields inside the structure. For an example of how this is done, see Example 3-4 in Section 3.3.

The first argument of the array_element function is usually the result returned from the read_sym function.


Note

The read_sym, array_element, and read_field_vals functions are often used together to retrieve the values of an array of structures pointed to by a global pointer. (For more information about using these functions, see the description of the read_sym function in Section 3.2.27.)


For example:


if((ele = array_element(sz_softc, cntrl, &error)) == NULL){
  fprintf(stderr, "Couldn't get %d'th element of sz_softc:\n, cntrl");
  fprintf(stderr, "%s\n", error);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.4    Retrieving an Array Element Value

The array_element_val function returns the value of an array element. It returns the integer value if the data type of the array element is an integer data type. It returns the pointer value if the data type of the array element is a pointer data type.

This function returns TRUE if it is successful, FALSE otherwise. When the return value is FALSE, an error message is returned in an argument to the function.

This function has the following syntax:

Boolean array_element_val(DataStruct sym , int i , long * ele_ret , char ** error );


ArgumentInput/OutputDescription
sym  Input  Names the array 
i  Input  Specifies the index of the element 
ele_ret  Output  Returns the value of the pointer 
error  Output  Returns a pointer to an error message if the return value is FALSE 

You use the array_element_val function when the array element is of a basic C type. You also use this function if the array element is of a pointer type and the pointer value is what you actually want. This function returns a printable value. The first argument of the array_element_val function usually comes from the returned result of the read_sym function.

For example:

static char get_ele(array, i)
DataStruct array;
int i;
{
  char *error, ret;
  long val;

  if(!array_element_val(array, i, &val, &error)){
    fprintf(stderr, "Couldn't read array element:\n");
    fprintf(stderr, "%s\n", error);
    quit(1);
  }
  ret = val;
  return(ret);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.5    Returning the Size of an Array

The array_size function returns the size of the specified array. This function has the following syntax:

unsigned int array_size(DataStruct sym , char**error );


ArgumentInput/OutputDescription
sym  Input  Names the array 
error  Output  Returns a pointer to an error message if the return value is non-NULL 

For example:

busses = read_sym("bus_list");

if((n = array_size(busses, &error)) == -1){
  fprintf(stderr, "Couldn't call array_size:\n");
  fprintf(stderr, "%s\n", error);
  quit(1);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.6    Casting a Pointer to a Data Structure

The cast function casts the pointer to a structure as a structure data type and returns the structure. This function has the following syntax:

Boolean cast(long addr, char * type, DataStruct * ret_type, char ** error);


ArgumentInput/OutputDescription
addr  Input  Specifies the address of the data structure you want returned 
type  Input  Specifies the datatype of the data structure 
ret_type  Output  Returns the name of the data structure 
error  Output  Returns a pointer to an error message if the return value is FALSE 

You usually use the cast function with the read_field_vals function. Given the address of a structure, you call the cast function to convert the pointer from the type long to the type DataStruct. Then, you pass the result to the read_field_vals function, as its first argument, to retrieve the values of data fields in the structure.

For example:


if(!cast(addr, "struct file", &fil, &error)){
  fprintf(stderr, "Couldn't cast address to a file:\n");
  fprintf(stderr, "%s\n", error);
  quit(1);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.7    Checking Arguments Passed to an Extension

The check_args function checks the arguments passed to an extension or displays a help message. The function displays a help message when the user specifies the -help flag on the command line.

This function has the following syntax:

void check_args(int argc, char ** argv, char * help_string);


ArgumentInput/OutputDescription
argc  Input  Passes in the first argument to the command 
argv  Input  Passes in the second argument to the command 
help_string  Input  Specifies the help message to be displayed to the user 

You should include the check_args function early in your extension to be sure that arguments are correct.

For example:

check_args(argc, argv, help_string);
if(!check_fields("struct sz_softc", fields, NUM_FIELDS, NULL)){
  field_errors(fields, NUM_FIELDS);
  quit(1);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.8    Checking the Fields in a Structure

The check_fields function verifies that the specified function consists of the expected number of fields and that those fields have the correct data type. If the function is successful, TRUE is returned; otherwise, the error parts of the affected fields are filled in with errors, and FALSE is returned.

This function has the following syntax:

Boolean check_fields(char * symbol, FieldRec * fields, int nfields, char ** hints);


ArgumentInput/OutputDescription
symbol  Input  Names the structure to be checked 
fields  Input  Describes the fields to be checked 
nfields  Input  Specifies the size of the fields argument 
hints  Input  Unused and should always be set to NULL 

You should check the structure type using the check_fields function before using the read_field_vals function to read field values.

For example:

FieldRec fields[] = {
  {  ".sc_sysid", NUMBER, NULL, NULL },
  {  ".sc_aipfts", NUMBER, NULL, NULL },
  {  ".sc_lostarb", NUMBER, NULL, NULL },
  {  ".sc_lastid", NUMBER, NULL, NULL },
  {  ".sc_active", NUMBER, NULL, NULL }
};

check_args(argc, argv, help_string);
if(!check_fields("struct sz_softc", fields, NUM_FIELDS, NULL)){
  field_errors(fields, NUM_FIELDS);
  quit(1);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.9    Setting the kdbx Context

The context function sets the context to user context or proc context. If the context is set to the user context, aliases defined in the extension affect user aliases.

This function has the following syntax:

void context(Boolean user);


ArgumentInput/OutputDescription
user  Input  Sets the context to user if TRUE or proc if FALSE 

For example:

if(head) print(head);
context(True);
for(i=0;i<len;i++){

.
.
.


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.10    Passing Commands to the dbx Debugger

The dbx function passes a command to the dbx debugger. The function has an argument, expect_output, that controls when it returns. If you set the expect_output argument to TRUE, the function returns after the command is sent, and expects the extension to read the output from dbx. If you set the expect_output argument to FALSE, the function waits for the command to complete execution, reads the acknowledgement from kdbx, and then returns.

void dbx(char * command, Boolean expect_output);


ArgumentInput/OutputDescription
command  Input  Specifies the command to be passed to dbx 
expect_output  Input  Indicates whether the extension expects output and determines when the function returns 

For example:

dbx(out, True);
if((buf = read_response(&status)) == NULL){
  print_status("main", &status);
  quit(1);
}
else {
  process_buf(buf);
  quit(0);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.11    Dereferencing a Pointer

The deref_pointer function returns a representation of the object pointed to by a pointer. The function displays an error message if the data argument passed is not a valid address.

This function has the following syntax:

DataStruct deref_pointer(DataStruct data);


ArgumentInput/OutputDescription
data  Input  Names the data structure that is being dereferenced 

For example:
structure = deref_pointer(struct_pointer);


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.12    Displaying the Error Messages Stored in Fields

The field_errors function displays the error messages stored in fields by the check_fields function. This function has the following syntax:

void field_errors(FieldRec * fields, int nfields);


ArgumentInput/OutputDescription
fields  Input  Names the fields that contain the error messages 
nfields  Input  Specifies the size of the fields argument 

For example:

if(!read_field_vals(proc, fields, NUM_FIELDS)){
  field_errors(fields, NUM_FIELDS);
  return(False);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.13    Converting a Long Address to a String Address

The format_addr function converts a 64-bit address of type long into a 32-bit address of type char. This function has the following syntax:

extern char *format_addr(long addr, char * buffer);


ArgumentInput/OutputDescription
addr  Input  Specifies the address to be converted 
buffer  Output  Returns the converted address and must be at least 12 characters long 

Use this function to save space on the output line. For example, the 64-bit address 0xffffffff12345678 is converted into v0x12345678.

For example:

static Boolean prfile(DataStruct ele, long vn_addr, long socket_addr)
{
  char *error, op_buf[12], *ops, buf[256], address[12], cred[12], data[12];
  if(!read_field_vals(ele, fields, NUM_FIELDS)){
    field_errors(fields, NUM_FIELDS);
    return(False);
  }
  if((long) fields[1].data == 0) return(True);
  if((long) (fields[5].data) == 0) ops = "  *Null*  ";
  else if((long) (fields[5].data) == vn_addr) ops = "   vnops   ";
  else if((long) (fields[5].data) == socket_addr) ops = " socketops ";
  else format_addr((long) fields[5].data, op_buf);
  format_addr((long) struct_addr(ele), address);
  format_addr((long) fields[2].data, cred);
  format_addr((long) fields[3].data, data);
  sprintf(buf, "%s %s %4d %4d %s %s %s %6d   %s%s%s%s%s%s%s%s%s",
          address, get_type((int) fields[0].data), fields[1].data,
          fields[2].data, ops, cred, data, fields[6].data,
          ((long) fields[7].data) & FREAD ? " read" : ,
          ((long) fields[7].data) & FWRITE ? " write" : ,
          ((long) fields[7].data) & FAPPEND ? " append" : ,
          ((long) fields[7].data) & FNDELAY ? " ndelay" : ,
          ((long) fields[7].data) & FMARK ? " mark" : ,
          ((long) fields[7].data) & FDEFER ? " defer" : ,
          ((long) fields[7].data) & FASYNC ? " async" : ,
          ((long) fields[7].data) & FSHLOCK ? " shlck" : ,
          ((long) fields[7].data) & FEXLOCK ? " exlck" : );
  print(buf);
  return(True);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.14    Freeing Memory

The free_sym function releases the memory held by a specified symbol. This function has the following syntax:

void free_sym(DataStruct sym);


ArgumentInput/OutputDescription
sym  Input  Names the symbol that is using memory that can be freed 

For example:

free_sym(rec->data);


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.15    Passing Commands to the kdbx Debugger

The krash function passes a command to kdbx for execution. You specify the command you want passed to kdbx as the first argument to the krash function. The second argument allows you to pass quotation marks (""), apostrophes ('), and backslash characters (\) to kdbx. The function has an argument, expect_output, which controls when it returns. If you set the expect_output argument to TRUE, the function returns after the command is sent, and expects the extension to read the output from dbx. If you set the expect_output argument to FALSE, the function waits for the command to complete execution, reads the acknowledgement from kdbx, and then returns.

This function has the following syntax:

void krash(char * command, Boolean quote, Boolean expect_output);


ArgumentInput/OutputDescription
command  Input  Names the command to be executed 
quote  Input  If set to TRUE causes the quote character, apostrophe, and backslash to be appropriately quoted so that they are treated normally, instead of as special characters 
expect_output  Input  Indicates whether the extension expects output and determines when the function returns 

For example:

do  {
     :
  if(doit){
    format(command, buf, type, addr, last, i, next);
    context(True);

    krash(buf, False, True);
    while((line = read_line(&status)) != NULL){
      print(line);
      free(line);
  }
  :
addr = next;
i++;
Suppose the preceding example is used to list the addresses of each node in the system mount table, which is a linked list. The following list describes the arguments to the format function in this case:


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.16    Getting the Address of an Item in a Linked List

The list_nth_cell function returns the address of one of the items in a linked list. This function has the following format:

Boolean list_nth_cell(long addr, char * type, int n,char * next_field, Boolean do_check, long * val_ret, char ** error );


ArgumentInput/OutputDescription
addr  Input  Specifies the starting address of the linked list 
type  Input  Specifies the data type of the item for which you are requesting an address 
n  Input  Supplies a number indicating which list item's address is being requested 
next_field  Input  Gives the name of the field that points to the next item in the linked list 
do_check  Input  Determines whether kdbx checks the arguments to ensure that correct information is being sent (TRUE setting) 
val_ret  Output  Returns the address of the requested list item 
error  Output  Returns a pointer to an error message if the return value is FALSE 

For example:

long root_addr, addr;
if (!read_sym_val("rootfs", NUMBER, &root_addr, &error)){
 
.
.
.
} if(!list_nth_cell(root_addr, "struct mount", i, "m_next", True, &addr, &error)){ fprintf(stderr, "Couldn't get %d'th element of mount table\n", i); fprintf(stderr, "%s\n", error); quit(1); }


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.17    Passing an Extension to kdbx

The new_proc function directs kdbx to execute a proc command with arguments specified in args. The args arguments can name a Digital-supplied extension or an extension that you create.

This function has the following syntax:

void new_proc(char * args, char ** output_ret);


ArgumentInput/OutputDescription
args  Input  Names the extensions to be passed to kdbx 
output_ret  Output  Returns the output from the extension, if it is non-NULL 

For example:

static void prmap(long addr)
{
  char cast_addr[36], buf[256], *resp;

  sprintf(cast_addr, "((struct\ vm_map_t\ *)\ 0x%p)", addr);
  sprintf(buf, "printf
          cast_addr);
  new_proc(buf, &resp);
  print(resp);
  free(resp);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.18    Getting the Next Token as an Integer

The next_number function converts the next token in a buffer to an integer. The function returns TRUE if successful, or FALSE if there was an error.

This function has the following syntax:

Boolean next_number(char * buf, char ** next, long * ret);


ArgumentInput/OutputDescription
buf  Input  Names the buffer containing the value to be converted 
next  Output  Returns a pointer to the next value in the buffer, if that value is non-NULL 
ret  Output  Returns the integer value 

For example:

resp = read_response_status();
next_number(resp, NULL, &size);
ret->size = size;


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.19    Getting the Next Token as a String

The next_token function returns a pointer to the next token in the specified pointer to a string. A token is a sequence of nonspace characters. This function has the following syntax:

char *next_token(char * ptr, int * len_ret, char ** next_ret);


ArgumentInput/OutputDescription
ptr  Input  Specifies the name of the pointer 
len_ret  Output  Returns the length of the next token, if non-NULL 
next_ret  Output  Returns a pointer to the first character after, but not included in the current token, if non-NULL 

You use this function to extract words or other tokens from a character string. A common use, as shown in the example that follows, is to extract tokens from a string of numbers. You can then cast the tokens to a numerical data type, such as the long data type, and use them as numbers.

For example:

static long *parse_memory(char *buf, int offset, int size)
{
  long *buffer, *ret;
  int index, len;
  char *ptr, *token, *next;
  NEW_TYPE(buffer, offset + size, long, long *, "parse_memory");
  ret = buffer;
  index = offset;
  ptr = buf;
  while(index < offset + size){
    if((token = next_token(ptr, &len, &next)) == NULL){
      ret = NULL;
      break;
    }
    ptr = next;
    if(token[len - 1] == ':') continue;
    buffer[index] = strtoul(token, &ptr, 16);
    if(ptr != &token[len]){
      ret = NULL;
      break;
    }
    index++;
  }
  if(ret == NULL) free(buffer);
  return(ret);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.20    Displaying a Message

The print function displays a message on the terminal screen. Because of the input and output redirection done by kdbx, all output to stdout from a kdbx extension goes to dbx. As a result, a kdbx extension cannot use normal C output functions such as printf and fprintf(stdout,...) to display information on the screen. Although the fprintf(stderr,...) function is still available, the recommended method is to first use the sprintf function to print the output into a character buffer and then use the kdbx library function print to display the contents of the buffer to the screen.

The print function automatically displays a newline character at the end of the output, it fails if it detects a newline character at the end of the buffer.

This function has the following format:

void print(char * message);


ArgumentInput/OutputDescription
message  Input  The message to be displayed 

For example:

if(do_short){
  if(!check_fields("struct mount", short_mount_fields,
                   NUM_SHORT_MOUNT_FIELDS, NULL)){
    field_errors(short_mount_fields, NUM_SHORT_MOUNT_FIELDS);
    quit(1);
  }
  print("SLOT  MAJ  MIN TYPE                    DEVICE  MOUNT POINT");
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.21    Displaying Status Messages

The print_status function displays a status message that you supply and a status message supplied by the system. This function has the following format:

void print_status(char *message, Status * status);


ArgumentInput/OutputDescription
message  Input  Specifies the extension-defined status message 
status  Input  Specifies the status returned from another library routine 

For example:

if(status.type != OK){
  print_status("read_line failed", &status);
  quit(1);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.22    Exiting from an Extension

The quit function sends a quit command to kdbx. This function has the following format:

void quit(int i);


ArgumentInput/OutputDescription
i  Input  The status at the time of the exit from the extension 

For example:

if (!read_sym_val("vm_swap_head", NUMBER, &end, &error)) {
  fprintf(stderr, "Couldn't read vm_swap_head:\n");
  fprintf(stderr, "%s\n", error);
  quit(1);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.23    Reading the Values in Structure Fields

The read_field_vals function reads the value of fields in the specified structure. If this function is successful, then the data parts of the fields are filled in and TRUE is returned; otherwise, the error parts of the affected fields are filled in with errors and FALSE is returned.

This function has the following format:

Boolean read_field_vals(DataStruct data, FieldRec *fields, int nfields);


ArgumentInput/OutputDescription
data  Input  Names the structure that contains the field to be read 
fields  Input  Describes the fields to be read 
nfields  Input  Contains the size of the field array 

For example:


if(!read_field_vals(pager, fields, nfields)){
  field_errors(fields, nfields);
  return(False);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.24    Returning a Line of kdbx Output

The read_line function returns the next line of the output from the last kdbx command executed. If the end of the output is reached, this function returns NULL and a status of OK. If the status is something other than OK when the function returns NULL, an error occurred.

This function has the following format:

char *read_line(Status * status);


ArgumentInput/OutputDescription
status  Output  Contains the status of the request, which is OK for successful requests 

For example:

while((line = read_line(&status)) != NULL){
  print(line);
  free(line);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.25    Reading an Area of Memory

The read_memory function reads an area of memory starting at the address you specify and running for the number of bytes you specify. The read_memory function returns TRUE if successful and FALSE if there was an error.

This function has the following format:

Boolean read_memory(long start_addr, int n, char *buf, char ** error)


ArgumentInput/OutputDescription
start_addr  Input  Specifies the starting address for the read 
n  Input  Specifies the number of bytes to read 
buf  Output  Returns the memory contents 
error  Output  Returns a pointer to an error message if the return value is FALSE 

You can use this function to look up any type of value, however it is most useful for retrieving the value of pointers that point to other pointers.

For example:

start_addr = (long) ((long *)utask_fields[7].data + i-NOFILE_IN_U);
if(!read_memory(start_addr , sizeof(long *), (char *)&val1, &error) ||
  !read_memory((long)utask_fields[8].data , sizeof(long *), (char *)&val2,
     &error)){
  fprintf(stderr, "Couldn't read_memory\n");
  fprintf(stderr, "%s\n", error);
  quit(1);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.26    Reading the Response to a kdbx Command

The read_response function reads the response to the last kdbx command entered. If any errors occurred, NULL is returned and the status argument is filled in.

This function has the following syntax:

char *read_response(Status * status);


ArgumentInput/OutputDescription
status  Output  Contains the status of the last kdbx command 

For example:

if(!*argv) Usage();
command = argv;
if(size == 0){
  sprintf(buf, "print sizeof(*((%s) 0))", type);
  dbx(buf, True);

  if((resp = read_response(&status)) == NULL){
    print_status("Couldn't read sizeof", &status);
    quit(1);
  }
  size = strtoul(resp, &ptr, 0);
  if(ptr == resp){

    fprintf(stderr, "Couldn't parse sizeof(%s):\n", type);
    quit(1);
  }
  free(resp);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.27    Reading Symbol Representations

The read_sym function returns a representation of the named symbol. This function has the following format:

DataStruct read_sym(char * name);


ArgumentInput/OutputDescription
name  Input  Names the symbol, which is normally a pointer to a structure or an array of structures inside the kernel 

Often you use the result returned by the read_sym function as the input argument of the array_element, array_element_val, or read_field_vals function.

For example:

busses = read_sym("bus_list");


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.28    Reading a Symbol's Address

The read_sym_addr function reads the address of the specified symbol. This function has the following format:

Boolean read_sym_addr(char * name, long * ret_val, char ** error);


ArgumentInput/OutputDescription
name  Input  Names the symbol for which an address is required 
ret_val  Output  Returns the address of the symbol 
error  Output  Returns a pointer to an error message when the return status is FALSE 

For example:

if(argc == 0) fil = read_sym("file");
if(!read_sym_val("nfile", NUMBER, &nfile, &error) ||
   !read_sym_addr("vnops", &vn_addr, &error) ||
   !read_sym_addr("socketops", &socket_addr, &error)){
  fprintf(stderr, "Couldn't read nfile:\n");
  fprintf(stderr, "%s\n", error);
  quit(1);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.29    Reading the Value of a Symbol

The read_sym_val function returns the value of the specified symbol. This function has the following format:

Boolean read_sym_val(char * name, int type, long * ret_val, char ** error);


ArgumentInput/OutputDescription
name  Input  Names the symbol for which a value is needed 
type  Input  Specifies the data type of the symbol 
ret_val  Output  Returns the value of the symbol 
error  Output  Returns a pointer to an error message when the status is FALSE 

You use the read_sym_val function to retrieve the value of a global variable. The value returned by the read_sym_val function has the type long, unlike the value returned by the read_sym function which has the type DataStruct.

For example:

if(argc == 0) fil = read_sym("file");
if(!read_sym_val("nfile", NUMBER, &nfile, &error) ||
   !read_sym_addr("vnops", &vn_addr, &error) ||
   !read_sym_addr("socketops", &socket_addr, &error)){
  fprintf(stderr, "Couldn't read nfile:\n");
  fprintf(stderr, "%s\n", error);
  quit(1);
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.30    Getting the Address of a Data Representation

The struct_addr function returns the address of a data representation. This function has the following format:

char *struct_addr(DataStruct data);


ArgumentInput/OutputDescription
data  Input  Specifies the structure for which an address is needed 

For example:

if(bus_fields[1].data != 0){
  sprintf(buf, "Bus #%d (0x%p): Name - \"%s\"\tConnected to - \"%s\,
          i, struct_addr(bus), bus_fields[1].data, bus_fields[2].data);
  print(buf);
  sprintf(buf, "\tConfig 1 - %s\tConfig 2 - %s",
          addr_to_proc((long) bus_fields[3].data),
          addr_to_proc((long) bus_fields[4].data));
  print(buf);
  if(!prctlr((long) bus_fields[0].data)) quit(1);
  print();
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.2.31    Converting a String to a Number

The to_number function converts a string to a number. The function returns TRUE if successful, or FALSE if conversion was not possible.

This function has the following format:

Boolean to_number(char * str, long * val);


ArgumentInput/OutputDescription
str  Input  Contains the string to be converted 
val  Output  Contains the numerical equivalent of the string 

This function returns TRUE if successful, FALSE if conversion was not possible.

For example:

check_args(argc, argv, help_string);
if(argc < 5) Usage();
size = 0;
type = argv[1];
if(!to_number(argv[2], &len)) Usage();
addr = strtoul(argv[3], &ptr, 16);
if(*ptr != '\0'){
  if(!read_sym_val(argv[3], NUMBER, &addr, &error)){
    fprintf(stderr, "Couldn't read %s:\n", argv[3]);
    fprintf(stderr, "%s\n", error);
    Usage();
  }
}


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.3    Examples of kdbx Extensions

This section contains examples of the three types of extensions provided by the kdbx debugger:


Example 3-1: Template Extension Using Lists
#include <stdio.h>
#include <kdbx.h>
static char *help_string =

"<Usage info goes here>                                \\\n\ (1)
";
FieldRec fields[] = {

  { ".<name of next field>", NUMBER, NULL, NULL }, (2)

  <data fields>
};

#define NUM_FIELDS (sizeof(fields)/sizeof(fields[0]))

main(argc, argv)
int argc;
char **argv;
{
  DataStruct head;
  unsigned int next;
  char buf[256], *func, *error;

  check_args(argc, argv, help_string);
  if(!check_fields("<name of list structure>", fields, NUM_FIELDS, NULL)){ (3)
    field_errors(fields, NUM_FIELDS);
    quit(1);
  }

  if(!read_sym_val("<name of list head>", NUMBER, (caddr_t *) &next, &error)){ (4)
    fprintf(stderr, "%s\n", error);
    quit(1);
  }
  sprintf(buf, "<table header>"); (5)
  print(buf);
  do {
    if(!cast(next, "<name of list structure>", &head, &error)){ (6)
      fprintf(stderr, "Couldn't cast to a <struct>:\n"); (7)
      fprintf(stderr, "%s:\n", error);
    }
    if(!read_field_vals(head, fields, NUM_FIELDS)){
      field_errors(fields, NUM_FIELDS);
      break;
    }
    <print data in this list cell> (8)
    next = (int) fields[0].data;
  } while(next != 0);
  quit(0);
}

  1. The help string is output by the check_args function if the user enters the help extension_name command at the kdbx prompt.The first line of the help string should be a one-line description of the extension. The rest should be a complete description of the arguments. Also, each line should end with the string \\\n\.

  2. Every structure field to be extracted needs an entry. The first field is the name of the next extracted field; the second field is the type. The last two fields are for output and initialize to NULL.

  3. Specifies the type of the list that is being traversed.

  4. Specifies the variable that holds the head of the list.

  5. Specifies the table header string.

  6. Specifies the type of the list that is being traversed.

  7. Specifies the structure type.

  8. Extracts, formats, and prints the field information.


Example 3-2: Extension That Uses Linked Lists: callout.c
#include <stdio.h>
#include <errno.h>
#include <kdbx.h>

#define KERNEL
#include <sys/callout.h>

static char *help_string =
"callout - print the callout table                              \\\n\
    Usage : callout [cpu]                                    \\\n\
";

FieldRec processor_fields[] = {
  { ".calltodo.c_u.c_ticks", NUMBER, NULL, NULL },
  { ".calltodo.c_arg",  NUMBER, NULL, NULL },
  { ".calltodo.c_func", NUMBER, NULL, NULL },
  { ".calltodo.c_next", NUMBER, NULL, NULL },
  { ".lbolt",           NUMBER,  NULL, NULL },
  { ".state",           NUMBER,  NULL, NULL },
};

FieldRec callout_fields[] = {
  { ".c_u.c_ticks", NUMBER, NULL, NULL },
  { ".c_arg",  NUMBER, NULL, NULL },
  { ".c_func", NUMBER, NULL, NULL },
  { ".c_next", NUMBER, NULL, NULL },
};

#define NUM_PROCESSOR_FIELDS 
(sizeof(processor_fields)/sizeof(processor_fields[0]))
#define NUM_CALLOUT_FIELDS (sizeof(callout_fields)/sizeof(callout_fields[0]))

main(int argc, char **argv)
{
  DataStruct processor_ptr, processor, callout;
  long next, ncpus, ptr_val, i;
  char buf[256], *func, *error, arg[13];
  int cpuflag = 0, cpuarg = 0;

  long headptr;
  Status status;
  char *resp;

  if ( !(argc == 1 || argc == 2) ) {
    fprintf(stderr, "Usage: callout [cpu]\n");
    quit(1);
  }

  check_args(argc, argv, help_string);

  if (argc == 2)  {
    cpuflag = 1; 
    errno = 0;
    cpuarg = atoi(argv[1]);
    if (errno != 0)
      fprintf(stderr, "Invalid argument value for the cpu number.\n");
  }

  if(!check_fields("struct processor", processor_fields, NUM_PROCESSOR_FIELDS, 
NULL)){
    field_errors(processor_fields, NUM_PROCESSOR_FIELDS);
    quit(1);
  }  

  if(!check_fields("struct callout", callout_fields, NUM_CALLOUT_FIELDS, NULL)){
    field_errors(callout_fields, NUM_CALLOUT_FIELDS);
    quit(1);
  }  

  /* This gives the same result as "(kdbx) p processor_ptr" */
  if(!read_sym_addr("processor_ptr", &headptr, &error)){
    fprintf(stderr, "%s\n", error);
    quit(1);
  }

  /* get ncpus */
  if(!read_sym_val("ncpus", NUMBER, &ncpus, &error)){
    fprintf(stderr, "Couldn't read ncpus:\n");
    fprintf(stderr, "%s\n", error);
    quit(1);
  }

  for (i=0; i < ncpus; i++) {

    /* if user wants only one cpu and this is not the one, skip */
    if (cpuflag)
      if (cpuarg != i) continue;

    /* get the ith pointer (values) in the array */ 

    sprintf(buf, "set $hexints=0");
    dbx(buf, False);
    sprintf(buf, "p \*(long \*)0x%lx", headptr+8*i);
    dbx(buf, True);
    if((resp = read_response(&status)) == NULL){
      print_status("Couldn't read value of processor_ptr[i]:", &status);
      quit(1);
    }
    ptr_val = strtoul(resp, (char**)NULL, 10);
    free(resp);

    if (! ptr_val)  continue;  /* continue if this slot is disabled */

    if(!cast(ptr_val, "struct processor", &processor, &error)){
      fprintf(stderr, "Couldn't cast to a processor:\n");
      fprintf(stderr, "%s:\n", error);
      quit(1);
    }

    if(!read_field_vals(processor, processor_fields, NUM_PROCESSOR_FIELDS)){
      field_errors(processor_fields, NUM_PROCESSOR_FIELDS);
      quit(1);
    }

    if (processor_fields[5].data == 0) continue;

    print("");
    sprintf(buf, "Processor:                         %10u", i);
    print(buf);
    sprintf(buf, "Current time (in ticks):           %10u", 
processor_fields[4].data ); /*lbolt*/
    print(buf);


    /* for first element, we are interested in time only */

    print("");
  
    sprintf(buf, "        FUNCTION                   ARGUMENT    TICKS(delta)");
    print(buf);
    print(       "=============================    ============  ============");

    /* walk through the rest of the list */
    next = (long) processor_fields[3].data;
    while(next != 0) {
      if(!cast(next, "struct callout", &callout, &error)){
        fprintf(stderr, "Couldn't cast to a callout:\n");
        fprintf(stderr, "%s:\n", error);
      }
      if(!read_field_vals(callout, callout_fields, NUM_CALLOUT_FIELDS)){
        field_errors(callout_fields, NUM_CALLOUT_FIELDS);
        break;
      }
      func = addr_to_proc((long) callout_fields[2].data);
      format_addr((long) callout_fields[1].data, arg);
      sprintf(buf, "%-32.32s %12s %12d", func, arg,
	    ((long)callout_fields[0].data & CALLTODO_TIME) - 
(long)processor_fields[4].data);
      print(buf);
      next = (long) callout_fields[3].data;
    } 
 

  }  /* end of for */

  quit(0);

}  /* end of main() */


Example 3-3: Template Extensions Using Arrays
#include <stdio.h>
#include <kdbx.h>

static char *help_string =
"<Usage info>                                                \\\n\ (1)
";

FieldRec fields[] = {
  <data fields> (2)
};
#define NUM_FIELDS (sizeof(fields)/sizeof(fields[0]))
main(argc, argv)
int argc;
char **argv;
{
  int i, size;
  char *error, *ptr;
  DataStruct head, ele;
  check_args(argc, argv, help_string);

  if(!check_fields("<array element type>", fields, NUM_FIELDS, NULL)){ (3)
    field_errors(fields, NUM_FIELDS);
    quit(1);
  }

  if(argc == 0) head = read_sym("<file>");  (4)

  if(!read_sym_val("<symbol containing size of array>", NUMBER, (5)
		   (caddr_t *) &size, &error) ||
    fprintf(stderr, "Couldn't read size:\n");
    fprintf(stderr, "%s\n", error);
    quit(1);
  }
  <print header> (6)
  if(argc == 0){
    for(i=0;i<size;i++){
      if((ele = array_element(head, i, &error)) == NULL){
	fprintf(stderr, "Couldn't get array element\n");
	fprintf(stderr, "%s\n", error);
	return(False);
      }
      <print fields in this element> (7)
    }
  }
}

  1. The help string is output by the check_args function if the user enters the help extension_name command at the kdbx prompt. The first line of the help string should be a one-line description of the extension. The rest should be a complete description of the arguments. Also, each line should end with the string \\\n\.

  2. Every structure field to be extracted needs an entry. The first field is the name of the next extracted field; the second field is the type. The last two fields are for output and initialize to NULL.

  3. Specifies the type of the element in the array.

  4. Specifies the variable containing the beginning address of the array.

  5. Specifies the variable containing the size of the array. Note that reading variables is only one way to access this information. Other methods include the following:

    • Defining the array size with a #define macro call. If you use this method, you need to include the appropriate header file and use the macro in the extension.

    • Querying dbx for the array size as follows:
      dbx("print sizeof(array//sizeof(array[0]")

    • Hard coding the array size.

  6. Specifies the string to be displayed as the table header.

  7. Extracts, formats, and prints the field information.


Example 3-4: Extension That Uses Arrays: file.c
#include <stdio.h>
#include <sys/fcntl.h>
#include <kdbx.h>
#include <nlist.h>
#define SHOW_UTT
#include <sys/user.h>
#define KERNEL_FILE
#include <sys/file.h>
#include <sys/proc.h>

static char *help_string =
"file - print out the file table                                          \\\n\
    Usage : file [addresses...]                                           \\\n\
    If no arguments are present, all file entries with non-zero reference \\\n\
    counts are printed. Otherwise, the file entries named by the addresses\\\n\
    are printed.                                                          \\\n\
";

char  buffer[256];

/* *** Implement addresses *** */

FieldRec fields[] = {
  { ".f_type", NUMBER, NULL, NULL },
  { ".f_count", NUMBER, NULL, NULL },
  { ".f_msgcount", NUMBER, NULL, NULL },
  { ".f_cred", NUMBER, NULL, NULL },
  { ".f_data", NUMBER, NULL, NULL },
  { ".f_ops", NUMBER, NULL, NULL },
  { ".f_u.fu_offset", NUMBER, NULL, NULL },
  { ".f_flag", NUMBER, NULL, NULL }
};

FieldRec fields_pid[] = {
  { ".pe_pid", NUMBER, NULL, NULL },
  { ".pe_proc", NUMBER, NULL, NULL },
};

FieldRec utask_fields[] = {
  { ".uu_file_state.uf_lastfile", NUMBER, NULL, NULL }, /* 0 */
  { ".uu_file_state.uf_ofile", ARRAY, NULL, NULL },     /* 1 */
  { ".uu_file_state.uf_pofile", ARRAY, NULL, NULL },    /* 2 */
  { ".uu_file_state.uf_ofile_of", NUMBER, NULL, NULL }, /* 3 */
  { ".uu_file_state.uf_pofile_of", NUMBER, NULL, NULL },/* 4 */
  { ".uu_file_state.uf_of_count", NUMBER, NULL, NULL }, /* 5 */
};

#define NUM_FIELDS (sizeof(fields)/sizeof(fields[0]))
#define NUM_UTASK_FIELDS (sizeof(utask_fields)/sizeof(utask_fields[0]))

static char *get_type(int type)
{
  static char buf[5];

  switch(type){
  case 1: return("file");
  case 2: return("sock");
  case 3: return("npip");
  case 4: return("pipe");
  default:
    sprintf(buf, "*%3d", type);
    return(buf);
  }
}

long vn_addr, socket_addr;
int proc_size; /* will be obtained from dbx */

static Boolean prfile(DataStruct ele)
{
  char *error, op_buf[12], *ops, buf[256], address[12], cred[12], data[12];

  if(!read_field_vals(ele, fields, NUM_FIELDS)){
    field_errors(fields, NUM_FIELDS);
    return(False);
  }
  if((long) fields[1].data == 0) return(True);
  if((long) (fields[5].data) == 0) ops = " *Null*";
  else if((long) (fields[5].data) == vn_addr) ops = "  vnops";
  else if((long) (fields[5].data) == socket_addr) ops = "sockops";
  else format_addr((long) fields[5].data, op_buf);
  format_addr((long) struct_addr(ele), address);
  format_addr((long) fields[3].data, cred);
  format_addr((long) fields[4].data, data);
  sprintf(buf, "%s %s %4d %4d %s %11s %11s %6d%s%s%s%s%s%s%s%s%s",
	  address, get_type((int) fields[0].data), fields[1].data,
	  fields[2].data, ops, data, cred, fields[6].data,
	  ((long) fields[7].data) & FREAD ? " r" : "",
	  ((long) fields[7].data) & FWRITE ? " w" : "",
	  ((long) fields[7].data) & FAPPEND ? " a" : "",
	  ((long) fields[7].data) & FNDELAY ? " nd" : "",
	  ((long) fields[7].data) & FMARK ? " m" : "",
	  ((long) fields[7].data) & FDEFER ? " d" : "",
	  ((long) fields[7].data) & FASYNC ? " as" : "",
	  ((long) fields[7].data) & FSHLOCK ? " sh" : "",
	  ((long) fields[7].data) & FEXLOCK ? " ex" : "");
  print(buf);
  return(True);
}

static Boolean prfiles(DataStruct fil, int n)
{
  DataStruct ele;
  char *error;

  if((ele = array_element(fil, n, &error)) == NULL){
    fprintf(stderr, "Couldn't get array element\n");
    fprintf(stderr, "%s\n", error);
    return(False);
  }
  return(prfile(ele));
}

static void Usage(void){
  fprintf(stderr, "Usage : file [addresses...]\n");
  quit(1);
}

main(int argc, char **argv)
{
  int i;
  long nfile, addr;
  char *error, *ptr, *resp;
  DataStruct fil;
  Status status;

  check_args(argc, argv, help_string);
  argv++;
  argc--;

  if(!check_fields("struct file", fields, NUM_FIELDS, NULL)){
    field_errors(fields, NUM_FIELDS);
    quit(1);
  }
  if(!check_fields("struct pid_entry", fields_pid, 2, NULL)){
    field_errors(fields, 2);
    quit(1);
  }
  if(!check_fields("struct utask", utask_fields,  NUM_UTASK_FIELDS, NULL)){
    field_errors(fields, NUM_UTASK_FIELDS);
    quit(1);
  }
  
  if(!read_sym_addr("vnops", &vn_addr, &error) ||
     !read_sym_addr("socketops", &socket_addr, &error)){
    fprintf(stderr, "Couldn't read vnops or socketops:\n");
    fprintf(stderr, "%s\n", error);
    quit(1);
  }
  print("Addr        Type  Ref  Msg Fileops      F_data        Cred Offset 
Flags");
  print("=========== ====  ===  === ======= =========== =========== ====== 
=====");
  if(argc == 0){
    /*
     * New code added to access open files in processes, in
     * the absence of static file table, file, nfile, etc..
     */

    /*
     * get the size of proc structure
     */
    sprintf(buffer, "set $hexints=0");
    dbx(buffer, False);
    sprintf(buffer, "print sizeof(struct proc)");
    dbx(buffer, True);
    if((resp = read_response(&status)) == NULL){
      print_status("Couldn't read sizeof proc", &status);
      proc_size = sizeof(struct proc);
    }
    else
      proc_size = strtoul(resp, (char**)NULL, 10);
    free(resp);

    if ( get_all_open_files_from_active_processes() ) {
      fprintf(stderr, "Couldn't get open files from processes:\n"); 
      quit(1);
    }
  }
  else {
    while(*argv){
      addr = strtoul(*argv, &ptr, 16);
      if(*ptr != '\0'){
	fprintf(stderr, "Couldn't parse %s to a number\n", *argv);
	quit(1);
      }
      if(!cast(addr, "struct file", &fil, &error)){
	fprintf(stderr, "Couldn't cast address to a file:\n");
	fprintf(stderr, "%s\n", error);
	quit(1);
      }
      if(!prfile(fil)) 
	fprintf(stderr, "Continuing with next file address.\n");
      argv++;
    }
  }
  quit(0);
}

/*
 * Figure out the location of the utask structure in the supertask
 * #define proc_to_utask(p) (long)(p+sizeof(struct proc))
 */


/*
 * Figure out if this a system with the capability of
 * extending the number of open files per process above 64
 */
#ifdef NOFILE_IN_U
#  define OFILE_EXTEND
#else
#  define NOFILE_IN_U NOFILE
#endif

/*
 * Define a generic NULL pointer
 */
#define NIL_PTR(type) (type *) 0x0

get_all_open_files_from_active_processes()
{

 long pidtab_base;       /* Start address of the process table          */
 long npid;              /* Number of processes in the process table    */
 char *error;

 if (!read_sym_val("pidtab",  NUMBER, &pidtab_base, &error) ||
     !read_sym_val("npid", NUMBER, &npid, &error) ){
   fprintf(stderr, "Couldn't read pid or npid:\n");
   fprintf(stderr, "%s\n", error);
   quit(1);

 }

 if ( check_procs (pidtab_base, npid) ) 
   return(0);
 else
   return(1);
}


check_procs(pidtab_base, npid)
  long pidtab_base;
  long npid;
{
  int i, index, first_file;
  long addr;
  DataStruct pid_entry_struct, pid_entry_ele, utask_struct, fil;
  DataStruct ofile, pofile;
  char *error;
  long addr_of_proc, start_addr, val1, fp, last_fp;
  char  buf[256];


  /*
   * Walk the pid table
   */
  pid_entry_struct = read_sym("pidtab");
  
  for (index = 0; index < npid; index++)
  {
    if((pid_entry_ele = array_element(pid_entry_struct, index, &error))==NULL){
      fprintf(stderr, "Couldn't get pid array element %d\n", index);
      fprintf(stderr, "%s\n", error);
      continue;
    }
    if(!read_field_vals(pid_entry_ele, fields_pid, 2)) {
      fprintf(stderr, "Couldn't get values of pid array element %d\n", index);
      field_errors(fields_pid, 2);
      continue;
    }
    addr_of_proc = (long)fields_pid[1].data;
    if (addr_of_proc == 0)
      continue;
    first_file = True;
    addr = addr_of_proc + proc_size;

    if(!cast(addr, "struct utask", &utask_struct, &error)){
      fprintf(stderr, "Couldn't cast address to a utask (bogus?):\n");
      fprintf(stderr, "%s\n", error);
      continue;
    } 
    if(!read_field_vals(utask_struct, utask_fields, 3)) {
      fprintf(stderr, "Couldn't read values of utask:\n");
      field_errors(fields_pid, 3);
      continue;
    }
    addr = (long) utask_fields[1].data;
    if (addr == NULL)
      continue;

    for(i=0;i<=(int)utask_fields[0].data;i++){
      if(i>=NOFILE_IN_U){
	if (utask_fields[3].data == NULL) 
	  continue;
	start_addr = (long)((long *)utask_fields[3].data + i-NOFILE_IN_U) ;
	if(!read_memory(start_addr , sizeof(struct file *), (char *)&val1, 
&error)) {
	  fprintf(stderr,"Start addr:0x%lx bytes:%d\n", start_addr, sizeof(long 
*));
	  fprintf(stderr, "Couldn't read memory for extn files: %s\n", error);
	  continue;
	}
      }
      else {
	ofile = (DataStruct) utask_fields[1].data;
	pofile = (DataStruct) utask_fields[2].data;      
      } 
      if (i < NOFILE_IN_U)
	if(!array_element_val(ofile, i, &val1, &error)){
	  fprintf(stderr,"Couldn't read %d'th element of ofile|pofile:\n", i);
	  fprintf(stderr, "%s\n", error);
	  continue;
	}
      fp = val1;
      if(fp == 0) continue;
      if(fp == last_fp) continue; /* eliminate duplicates */
      last_fp = fp;
      if(!cast(fp, "struct file", &fil, &error)){
	fprintf(stderr, "Couldn't cast address to a file:\n");
	fprintf(stderr, "%s\n", error);
	quit(1);
      }
      if (first_file) {
	sprintf(buf, "[Process ID: %d]",  fields_pid[0].data);
	print(buf);
	first_file = False;
      }
      if(!prfile(fil)) 
	fprintf(stderr, "Continuing with next file address.\n");
    }
  } /* for loop */

  return(True);
} /* end */


Example 3-5: Extension That Uses Global Symbols: sum.c
#include <stdio.h>
#include <kdbx.h>

static char *help_string =
"sum - print a summary of the system                                      \\\n\
    Usage : sum                                                           \\\n\
";

static void read_var(name, type, val)
char *name;
int type;
long *val;
{
  char *error;
  long n;

  if(!read_sym_val(name, type, &n, &error)){
    fprintf(stderr, "Reading %s:\n", name);
    fprintf(stderr, "%s\n", error);
    quit(1);
  }
  *val = n;
}

main(argc, argv)
int argc;
char **argv;
{
  DataStruct utsname, cpup, time;
  char buf[256], *error, *resp, *sysname, *release, *version, *machine;
  long avail, secs, tmp;

  check_args(argc, argv, help_string);
  read_var("utsname.nodename", STRING, &resp);
  sprintf(buf, "Hostname : %s", resp);
  print(buf);
  free(resp);
  read_var("ncpus", NUMBER, &avail);
/*
 * cpup no longer exists, emmulate platform_string(),
 * a.k.a. get_system_type_string().
  read_var("cpup.system_string", STRING, &resp);
 */
  read_var("rpb->rpb_vers", NUMBER, &tmp);
  if (tmp < 5)
      resp = "Unknown System Type";
  else
      read_var(
"(char *)rpb + rpb->rpb_dsr_off + "
"((struct rpb_dsr *)"
 " ((char *)rpb + rpb->rpb_dsr_off))->rpb_sysname_off + sizeof(long)",
	       STRING, &resp);
  sprintf(buf, "cpu: %s\tavail: %d", resp, avail);
  print(buf);
  free(resp);
  read_var("boottime.tv_sec", NUMBER, &secs);
  sprintf(buf, "Boot-time:\t%s", ctime(&secs));
  buf[strlen(buf) - 1] = '\0';
  print(buf);
  read_var("time.tv_sec", NUMBER, &secs);
  sprintf(buf, "Time:\t%s", ctime(&secs));
  buf[strlen(buf) - 1] = '\0';
  print(buf);
  read_var("utsname.sysname", STRING, &sysname);
  read_var("utsname.release", STRING, &release);
  read_var("utsname.version", STRING, &version);
  read_var("utsname.machine", STRING, &machine);
  sprintf(buf, "Kernel : %s release %s version %s (%s)", sysname, release,
	  version, machine);
  print(buf);
  quit(0);
}	


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.4    Compiling Custom Extensions

After you have written the extension, you need to compile it. To compile the extension, enter the following command:

% cc -o test test.c -lkdbx

This cc command compiles an extension named test.c. The kdbx.a library is linked with the extensions, as specified by the -l flag. The output from this command is named test, as specified by the -o flag.

Once the extension compiles successfully, you should test it and, if necessary, debug it as described in Section 3.5.

When the extension is ready for use, place it in a directory that is accessible to other users. Digital UNIX extensions are located in the /var/kdbx directory.

The following example shows how to invoke the test extension from within the kdbx debugger:


# kdbx -k /vmunix
dbx version 3.12.1
 Type 'help' for help.


(kdbx) test
Hostname : system.dec.com
cpu: DEC3000 - M500     avail: 1
Boot-time:      Fri Nov  6 16:09:10 1992
Time:   Mon Nov  9 10:51:48 1992
Kernel : OSF1 release 1.2 version 1.2 (alpha)
(kdbx)


[Return to Library]  [TOC]  [PREV]  --SECT  SECT--  [NEXT]  [INDEX] [Help]

3.5    Debugging Custom Extensions

The kdbx debugger and the dbx debugger include the capability to communicate with each other using two named pipes. The task of debugging an extension is easier if you use a workstation with two windows or two terminals. In this way, you can dedicate one window or terminal to the kdbx debugger and one window or terminal to the dbx debugger. However, you can debug an extension from a single terminal. This section explains how to begin your kdbx and dbx sessions when you have two windows or terminals and when you have a single terminal. The examples illustrate debugging the test extension that was compiled in Section 3.4.

If you are using a workstation with two windows or have two terminals, perform the following steps to set up your kdbx and dbx debugging sessions:

  1. Open two sessions: one running kdbx on the running kernel and the other running dbx on the source file for the custom extension test as follows:

    Begin the kdbx session:

    
    # kdbx -k /vmunix
    dbx version 3.12.1
    Type 'help' for help.
    
    stopped at [thread_block:1440 ,0xfffffc00002de5b0]   Source not available

    Begin the dbx session:

    
    # dbx test
    dbx version 3.12.1
    Type 'help' for help.
    
    (dbx)

  2. Set up kdbx and dbx to communicate with each other. In the kdbx session, enter the procpd alias to create the files /tmp/pipein and /tmp/pipeout as follows:
    
    (kdbx) procpd

    The file pipein directs output from the dbx session to the kdbx session. The file pipeout directs output from the kdbx session to the dbx session.

  3. In the dbx session, enter the run command to execute the test extension in the kdbx session, specifying the files /tmp/pipein and /tmp/pipeout on the command line as follows:
    
    (dbx) run < /tmp/pipeout > /tmp/pipein

  4. As you step through the extension in the dbx session, you see the results of any action in the kdbx session. At this point, you can use the available dbx commands and options.

If you are using one terminal, perform the following steps to set up your kdbx and dbx sessions:

  1. Issue the following command to invoke kdbx with the debugging environment:
    
    # echo 'procpd' | kdbx -k /vmunix &
    dbx version 3.12.1
    Type 'help' for help.
    
    stopped at  [thread_block:1403 ,0xfffffc000032d860]    Source not available
    
    #

  2. Invoke the dbx debugger as follows:
    
    # dbx test
    dbx version 3.12.1
    Type 'help' for help.
    
    (dbx)

  3. As you step through the extension in the dbx session, you see the results of any action in the kdbx session. At this point, you can use the available dbx commands and options.