Target Program State Access
in the Alamo Monitor Framework
Wenyi Zhou and Clinton L. Jeffery
Feb. 28, 1996
Technical Report CS-96-5
Abstract
In the Alamo Monitor Framework, execution monitors (EMs) reside in the same address space as the target program (TP). EMs can easily access the local variables and structures in the TP, using the debugging symbol table information from the TP's object file and those registers that are the keys to the TP's state information, such as the current program counter, the frame pointer, and the stack pointer. A set of library functions provide EM authors with direct access to the TP's symbol tables, scopes, addresses and values of variables, and stack frames.
Division of Computer Science
The University of Texas at San Antonio
San Antonio, TX78255
The Alamo Monitor Executive dynamically loads the target program (TP) being monitored along with one or more execution monitors (EMs) as needed. When multiple EMs are executing, they are controlled by a special EM designated as a monitor coordinator (MC). For each TP event that is requested by one or more of the EMs, the TP suspends, and control is transferred to the MC, which forwards event information (and control) to those EMs interested in the event. To acquire further information, EMs require the ability to access the TP's variables and structure information while processing the events. This functionality enables the EM writer to extract more information from the running target program and understand the program behavior better.
EM access to TP state is provided by several means. Since EMs do not have compile-time knowledge of TP types, a generic value datatype and related service functions implement a run-time traversal mechanism for TP values including arrays, structures, and unions. EM global variables corresponding to critical pieces of TP state are automatically assigned during event transmission. EM library functions provide access to TP symbol tables, scopes, addresses and values of variables, and stack traversal.
typedef struct {
struct ctype *type;
void * addr;
} Desc;
Descriptors have two fields: type is a pointer to a
structure that contains the type information. It is not used directly
by EM authors; instead, service functions use this pointer to extract
type information for a given variable.
addr is a void pointer that holds the memory location
where the variable is stored.

C does not allow nested functions, but does allow nested block structures called compound statements inside pairs of curly braces. Variables declared inside each pair of curly braces are in a new scope that is one level deeper than the variables declared outside. The scope is a conceptual term for distinguishing the visibility of variables inside one file or between several files. For each function that is still active, there is a stack frame (or activation record) associated with it as shown in Figure 1. We can have several scopes within each function and all the local variables declared in each scope will be stored in the stack frame if they are alive. The Locals represents the local variables for the function being visited at the scope which is indicated by the program counter value. An example C program is shown in Figure 2:

For the program shown in Figure 2, when the control is transferred at the assignment a = 9, Locals in EM refers to the variables declared inside block 3. Block 2 and block 1 are direct ancestors of block 3. Block 1 is also a direct ancestor of block 4. Block 2 and block 4 are siblings. When the program is inside the block 4, it cannot reference variables declared inside block 2.
Using these three functions, an EM author can obtain information for any given named variable in the current stack frame where the internal frame pointer and program counter are at.
For example, using the implicit structures as the parameter for the
above functions:
int i; Desc temp; char *name; /* get the number of local variables for the current scope*/ i = Num(Locals); /* get the 2nd parameter for the current function */ temp = Elem(Params, 2); /* get the name for the 2nd global variables */ name = Name(Globals, 2); ......In addition to implicit structures, a descriptor can represent an ordinary C type. In the above example, temp which retrieves the 2nd parameter for the current function may be a structure itself. The above functions can also be used to extract similar information from an ordinary C type. The code sample would be:
int i;
Desc d;
Desc temp;
char *string;
/*
* extracts the 2nd parameter, which is a structure, and places it in
* the descriptor temp.
*/
temp = Elem(Params, 2);
/* i contains the number of fields of the structure. */
i = Num(temp)
/* d holds the descriptor for the 2nd field of the structure */
d = Elem(temp,2)
/* string gets the name of the 3rd field of the structure */
string = Name(temp,3)
....
The above three functions enable EMs to access variable information
in the current stack frame of the program indicated by the internal
frame pointer value. In some cases, the EM writer would need to access a
variable that is hidden by the current scope. As an example shown in
Figure 2, when the target program gives control to EM, EM wants to
access the variable a which is declared in block 2.
The function: Desc WhereIs(char *name, int i);
returns a descriptor for the variable named name in the scope
i levels outside the current scope. When i is 0,
this is the current scope. When i is 1,
this is the immediately enclosing
scope, and so on. The code example would be:
Desc d1, d2, d3;
/*
* d1 holds the descriptor for the variable "a" in block 3.
*/
d1 = WhereIs("a", 0);
/*
* d2 holds the descriptor for the variable "a" in block 2.
*/
d2 = WhereIs("a", 1);
/*
* d3 holds the descriptor for the variable "a" in block 1,
* which should be a NULL descriptor value indicating not found.
*/
d3 = WhereIs("a", 2);
Note that a local variable is alive only if it is declared
in a direct ancestor of the current scope. EM authors cannot ask for
information about dead local variables, since their memory is
reused by other variables.
Desc d, temp;
int i, j, typecode;
char *s;
......
for ( i = 0; i < Num(Locals); i ++ ) {
d = Elem(Locals, i);
if ( Type(d) == STRUCT || Type(d) == ARRAY ) {
for ( j = 0; j < Num(d); j ++ ) {
......
/* get the jth field's or element's variable name */
s = Name(d, j);
/* get the jth field's or element's descriptor */
temp = Elem(d, j);
/*
* Or using Var(s) to get the jth field's or
* element's descriptor.
*
* temp = Var(s);
*/
/* get the jth field's variable type */
typecode = Type(temp);
switch (typecode) { /* printing out the values */
case INT:
printf("integer value: %d \n", DerefI(temp));
break;
case CHAR:
printf("char value: %c \n", DerefC(temp));
break;
case DOUBLE:
printf("double value: %f \n", DerefD(temp));
break;
case FLOAT:
printf("float value: %f \n", DerefF(temp));
break;
/*
* Or we can use Image() to get the string image
* of the value for a variable of type INT, CHAR,
* DOUBLE, FLOAT, etc.
*
* printf("var value: %s \n", Image(temp));
*/
......
}
......
}
}
}
......
An example of following a linked list. In C, the statement
like: for ( p = x; p; p = p->next ) is typical. An EM writer
can write similar statements for the same purpose. The difference is
the EM writer usually doesn't know the structure of variable "x". In
order for him to do the same thing, he has to do a little more work
to find out the "next" field for this structure. The code sample would
be:
Desc d;
int i, index;
/*
* find out the "next" field in the structure,
* assign that element's index to variable index
*/
d = Var("x");
for ( i = 0; i < Num(d); i ++ ) {
if ( IsSameType(d, Elem(d, i)) ) {
index = i;
break;
}
}
for ( d = Var("x"); !IsNull(d); d = Elem(d, index) ) {
......
}
An example of querying information about a variable which is not
in the current scope.
char *s;
int i, j;
Desc d;
.....
for ( i = 0; i < Num(Locals); i++ ) {
s = name(Locals, i);
for ( j = 1, d = WhereIs(s, j); !IsNull(d);
j++, d = WhereIs(s, j) ) {
......
}
......
}
An example of traversing the stack frame of TP. Print out the
filename and line number information for the TP.
char *filename;
int line_num;
......
/* get current filename and line number info */
Loc(filename, &line_num);
printf("filename: %s, line_number: %d \n", filename, line_num);
while ( UpStack() != -1 ) {
......
Loc(filename, &line_num);
printf("filename: %s, line_number: %d \n", filename, line_num);
......
}
[Jeff96] Jeffery, C. L., Zhou, W, and Templer, K. S., "The Alamo Monitor Framework", Technical Report 96-7, Division of Computer Science, University of Texas at San Antonio, February, 1996.
[Templ96] Templer, K. S., "A Configurable C Instrumentation Tool", Technical Report 96-6, Division of Computer Science, University of Texas at San Antonio, February, 1996.
[Jeff94] Jeffery, C. L., "A Framework for Execution Monitoring in Icon", Software: Practice and Experience, November, 1994.