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.
Identify the kernel variables and symbols that you need to examine.
Display the information so that anyone who needs to use it can read and understand it.
As with any good program, it is important to provide informational error messages in the extension.
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.
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.
The following is the type definition for the StatusType data type:
typedef enum { OK, Comm, Local } StatusType;
The following is the type definition for the Status data type:
typedef struct { StatusType type; union { int comm; int local; } u; } Status;
The values in comm and local provide the error code interpreted by print_status.
The following is the type definition for the FieldRec data type:
typedef struct { char *name; int type; caddr_t data; char *error; } FieldRec;
The char *name declaration is the name of the field in question. The int type declaration is the type of the field, for example, NUMBER, STRUCTURE, POINTER. The caddr_t data and char *error declarations are initially set to NULL. The read_field_vals function fills in these values.
The following is the type definition for the DataStruct data type:
typedef long DataStruct;
This function has the following syntax:
char *addr_to_proc(long addr );
Argument | Input/Output | Description |
---|---|---|
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);
DataStruct array_element(DataStruct sym , int i , char ** error );
Argument | Input/Output | Description |
---|---|---|
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.
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.) Note
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); }
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 );
Argument | Input/Output | Description |
---|---|---|
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); }
unsigned int array_size(DataStruct sym , char**error );
Argument | Input/Output | Description |
---|---|---|
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); }
Boolean cast(long addr, char * type, DataStruct * ret_type, char ** error);
Argument | Input/Output | Description |
---|---|---|
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); }
This function has the following syntax:
void check_args(int argc, char ** argv, char * help_string);
Argument | Input/Output | Description |
---|---|---|
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 |
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); }
This function has the following syntax:
Boolean check_fields(char * symbol, FieldRec * fields, int nfields, char ** hints);
Argument | Input/Output | Description |
---|---|---|
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); }
This function has the following syntax:
void context(Boolean user);
Argument | Input/Output | Description |
---|---|---|
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++){
.
.
.
void dbx(char * command, Boolean expect_output);
Argument | Input/Output | Description |
---|---|---|
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); }
This function has the following syntax:
DataStruct deref_pointer(DataStruct data);
Argument | Input/Output | Description |
---|---|---|
data | Input | Names the data structure that is being dereferenced |
structure = deref_pointer(struct_pointer);
void field_errors(FieldRec * fields, int nfields);
Argument | Input/Output | Description |
---|---|---|
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); }
extern char *format_addr(long addr, char * buffer);
Argument | Input/Output | Description |
---|---|---|
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); }
void free_sym(DataStruct sym);
Argument | Input/Output | Description |
---|---|---|
sym | Input | Names the symbol that is using memory that can be freed |
For example:
free_sym(rec->data);
This function has the following syntax:
void krash(char * command, Boolean quote, Boolean expect_output);
Argument | Input/Output | Description |
---|---|---|
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:
p ((struct mount *) 0xffffffff8196db30).m_next
Boolean list_nth_cell(long addr, char * type, int n,char * next_field, Boolean do_check, long * val_ret, char ** error );
Argument | Input/Output | Description |
---|---|---|
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); }
This function has the following syntax:
void new_proc(char * args, char ** output_ret);
Argument | Input/Output | Description |
---|---|---|
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); }
This function has the following syntax:
Boolean next_number(char * buf, char ** next, long * ret);
Argument | Input/Output | Description |
---|---|---|
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;
char *next_token(char * ptr, int * len_ret, char ** next_ret);
Argument | Input/Output | Description |
---|---|---|
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); }
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);
Argument | Input/Output | Description |
---|---|---|
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"); }
void print_status(char *message, Status * status);
Argument | Input/Output | Description |
---|---|---|
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); }
void quit(int i);
Argument | Input/Output | Description |
---|---|---|
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); }
This function has the following format:
Boolean read_field_vals(DataStruct data, FieldRec *fields, int nfields);
Argument | Input/Output | Description |
---|---|---|
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); }
This function has the following format:
char *read_line(Status * status);
Argument | Input/Output | Description |
---|---|---|
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); }
This function has the following format:
Boolean read_memory(long start_addr, int n, char *buf, char ** error)
Argument | Input/Output | Description |
---|---|---|
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); }
This function has the following syntax:
char *read_response(Status * status);
Argument | Input/Output | Description |
---|---|---|
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); }
DataStruct read_sym(char * name);
Argument | Input/Output | Description |
---|---|---|
name | Input | Names the symbol, which is normally a pointer to a structure or an array of structures inside the kernel |
For example:
busses = read_sym("bus_list");
Boolean read_sym_addr(char * name, long * ret_val, char ** error);
Argument | Input/Output | Description |
---|---|---|
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); }
Boolean read_sym_val(char * name, int type, long * ret_val, char ** error);
Argument | Input/Output | Description |
---|---|---|
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); }
char *struct_addr(DataStruct data);
Argument | Input/Output | Description |
---|---|---|
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(); }
This function has the following format:
Boolean to_number(char * str, long * val);
Argument | Input/Output | Description |
---|---|---|
str | Input | Contains the string to be converted |
val | Output | Contains the numerical equivalent of the string |
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(); } }
#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); }
#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() */
#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) } } }
dbx("print sizeof(array//sizeof(array[0]")
#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 */
#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); }
% 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)
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:
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)
(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.
(dbx) run < /tmp/pipeout > /tmp/pipein
If you are using one terminal, perform the following steps to set up your kdbx and dbx sessions:
# echo 'procpd' | kdbx -k /vmunix & dbx version 3.12.1 Type 'help' for help. stopped at [thread_block:1403 ,0xfffffc000032d860] Source not available #
# dbx test dbx version 3.12.1 Type 'help' for help. (dbx)