Dynamic Probe Class Library

Dynamic Probe Class Library Programming Guide


Chapter 8. Creating probes

The term probe refers to a software instrumentation code patch that your analysis tool can insert into one or more target application processes. Probes are created by the analysis tool, and therefore are custom designed to perform whatever work is required by the tool. For example, depending on the needs of the analysis tool, probes could be inserted into the target application to collect and report performance information (such as execution time), keep track of pass counts for test coverage tools, or report or modify the contents of variables for debuggers. For an overview of probes, refer to What is a probe?.

For the purposes of this book, a probe is defined as "a probe expression that may optionally call functions". A probe expression (described in more detail in What is a probe expression?) is a simple instruction or sequence of instructions that represents the executable code to be inserted into the target application. An analysis tool can create probe expressions to perform conditional control flow, integer arithmetic, and bitwise operations. For instructions detailing how an analysis tool can create probe expressions, refer to Creating probe expressions.

When more complicated logic is needed (such as iteration, recursion, and complex data structure manipulation), a probe expression can call functions. Specifically, a probe expression can call:

The sections that follow (Creating probe expressions and Creating and calling probe module functions) describe how to create probes that can execute as part of the target application process. For instructions on actually executing the probes, refer to Chapter 9, Executing probes in target application processes.


Creating probe expressions

A probe expression (described in more detail in What is a probe expression?) is a simple instruction or sequence of instructions that represent the executable code to be inserted into one or more target application processes. A probe expression is a type of data structure called an abstract syntax tree (a term we have borrowed from compiler technology). These data structures are called "abstract syntax trees" (as opposed to simply "syntax trees") because they are removed from the syntactic representation of the code. Compilers need to create abstract syntax trees from a program's source code as an intermediary stage before manipulating and converting the data structure into executable instructions. Since the DPCL system also needs to create executable instructions (for insertion into one or more target application processes), it also needs to create these abstract syntax trees. To create a probe expression, you need to:

  1. Determine the basic logic for the probe expression. This is not a task that the analysis tool code preforms -- instead, it is a task that you, the creator of the analysis tool code, should perform. Creating the probe expression that will execute in one or more target application processes can be a "building block" task that involves first creating simple probe expressions and then combining and sequencing them into a single probe expression that represents the complete code patch to be inserted into the target application processes. To do this, it can sometimes be helpful to have a C or pseudocode version of the basic logic you want to build into the probe expression. This C or pseudocode version can then serve as a map of the logic you need to create using probe expressions.
  2. Use ProbeExp objects to represent the various parts of the probe expression logic -- the individual "nodes" of the abstract syntax tree -- and then combine and sequence these ProbeExp objects into a final ProbeExp object that represents the complete code patch to be inserted into the target application process.

The following steps describe these tasks in greater detail. For sample code, see Example: Creating probe expressions.

Step 1: Determine basic logic for the probe expression

The next step (Step 2: Build the probe expression) describes how an analysis tool can create a probe expression that it can later execute within a target application process. The procedure for creating a probe expression can be a "building block" task in which smaller probe expressions are eventually combined and sequenced into the full probe expression.

For example, the analysis tool can create probe expressions representing constant or variable values, and then combine these into more complex probe expressions representing simple operations on the values, or function calls that pass the values as parameters to the function. The analysis tool could then take two of these more complex probe expressions and combine them into a single probe expression that represents a sequence of the two existing expressions. Then the analysis tool could join two such sequences into a longer sequence or combine them into a conditional statement. And so on, depending on the complexity of the probe logic, until the analysis tool has a single probe expression representing the full probe logic.

While the procedure for building a probe expression is the topic of the next step (Step 2: Build the probe expression), you should at this point determine the basic logic that the probe expression will perform. To adequately do this, you should understand the programmatic capabilities of probe expressions. A probe expression can represent:

Since the procedure for creating a full probe expression can be a "building block" task in which simple probe expressions are combined to form more complex ones, you might want to sketch out the logic in C code or pseudocode. This will give you a map for building the probe expression (as described next in Step 2: Build the probe expression).

Step 2: Build the probe expression

Once you have determined the basic logic you want to build into the probe expression, you can create the analysis tool code that builds the probe expression. The analysis tool must first create ProbeExp objects to represent the various parts of the probe expression logic -- the individual "nodes" of the abstract syntax tree -- and combine and sequence these ProbeExp objects into a final ProbeExp object that represents the complete code patch to be inserted into the target application process. To build a probe expression, the analysis tool can:

The following substeps describe these tasks in greater detail.

Step 2a: Create probe expressions to represent temporary or persistent data

Like most programming vehicles, probes require scratch space for both temporary and persistent data. The DPCL system automatically allocates probe temporary data each time a probe expression executes, and deallocates the data when the probe expression completes. Probe persistent data, on the other hand, must be explicitly allocated by the analysis tool code. Creating probe expressions to represent probe temporary data describes how an analysis tool can use ProbeExp class constructors to create probe expressions representing temporary data. Creating probe expressions to represent persistent data describes how an analysis tool can explicitly allocate probe persistent data.

Creating probe expressions to represent probe temporary data

The DPCL system automatically allocates probe temporary data each time the probe expression is executed, and deallocates the data when the probe expression completes. Unlike probe persistent data (described next in Creating probe expressions to represent persistent data), the analysis tool does not need to explicitly allocate memory for the data. To create a probe expression to represent temporary data, the analysis tool uses the ProbeExp class constructor to specify the data type and initial value of the data.

Table 36. Creating probe expressions to represent temporary data
For example, to create a probe expression pe that represents this data type: And has an initial value of: The analysis tool code would be:
int 16
ProbeExp pe32 = ProbeExp(16);


string "jason"
ProbeExp pestr = ProbeExp("jason");


For more information on the ProbeExp class constructor, refer to the DPCL Class Reference.

Creating probe expressions to represent temporary data In the target application

Using the SourceObj::reference function, the analysis tool is able to create a probe expression that references a global or static data variable in the target application. To do this, the analysis tool must navigate the target application's source code structure to identify the variable of interest. (If you are unfamiliar with SourceObj objects and the concept of source hierarchies, you may want to refer to What is the SourceObj class? before reading the following example code.

The following example code:

  1. Calls the Process::get_program_object function to return the top-level source object (SourceObj object) associated with a process.
  2. Identifies the program module that contains the variable. To do this, it uses the SourceObj::child_count function to initialize a for loop and then, within the for loop, uses the SourceObj::child and SourceObj::module_name functions to identify the target module.
  3. Calls the SourceObj::bexpand function to expand the module. This enables the analysis tool to navigate further down the source hierarchy to examine additional program structure (including global data variables). An analysis tool could also expand a module using the asynchronous SourceObj::expand function.
  4. Identifies the variable. To do this, the analysis tool code again uses the SourceObj::child_count function to initialize a for loop, and then, within the for loop, uses the SourceObj::child and SourceObj::get_variable_name functions to identify the target variable.
  5. Calls the SourceObj::reference function to create a probe expression that represents the variable.
#include <dpcl.h>

#include <libgen.h>             // for basename()

// find the variable variable_name defined in the module module_name

// for a given Process p

// return: SourceObj contains the variable.

//         or src_type()==SOT_unknown_type if the variable not found

SourceObj find_variable(Process p,

                        char * module_name,

                        char * variable_name)

{

    // get the source object associated with a process

    SourceObj progobj = p.get_program_object();

    SourceObj ret;              // return variable

    SourceObj mod;              // module object

 

    // identifies the program module that contains the variable

    // we preallocate a buffer to hold the module name

    int mod_name_length=1024;

    char * mod_name = new char [mod_name_length];

    int mod_index;

    for(mod_index = 0;mod_index < progobj.child_count();mod_index++) {

      mod = progobj.child(mod_index);

 

      // enlarge the name buffer if necessary.

      if (mod.module_name_length() >= mod_name_length) {

        // double the length

        while(mod_name_length < mod.module_name_length()) mod_name_length*=2;

        delete [] mod_name;

        mod_name = new char [mod_name_length];

      } /* endif */

 

      // get the module name

      mod.module_name(mod_name,mod_name_length);

 

      // depending on how the target application was compiled, the module

      // name may contain path information; for purposes of illustration, we compare

      // against the basename only.

      if (0 == strcmp(basename(mod_name),module_name)) {        // found

        break;

      } /* endif */

    }

    delete [] mod_name;

    if(mod_index == progobj.child_count())      // module not found

      return ret;

    // now we need to expand the module's SourceObj if necessary.

    // since only one module needs to expand, bexpand() will be easier

    if(mod.child_count() == 0) {                //

      AisStatus sts = mod.bexpand(p);

      if(sts.status()!=ASC_success)             // expand failed

        return ret;

    }

 

    // preallocate the variable buffer to hold the variable

    int var_name_length=1024;

    char * var_name = new char [var_name_length];

    SourceObj var;

    for (int i=0; i<mod.child_count(); i++) {

      var = mod.child(i);

      if(var.src_type()!=SOT_data) continue;

      if (var.get_variable_name_length() >= var_name_length) {

        while(var_name_length < var.get_variable_name_length())

          var_name_length*=2;

        delete [] var_name;

        var_name = new char [var_name_length];

      } /* endif */

      var.get_variable_name(var_name,var_name_length);

      if(0 == strcmp(var_name,variable_name)) { // found the variable

        ret = var;

        break;

      }

    } /* endfor */

    delete [] var_name;

    return ret;

}

 

main() {

    Process p;

    // ...

    SourceObj var = find_variable(p,"hello.c","var");

    if(var.src_type() == SOT_unknown_type) {

      // error report

    }

    ProbeExp pevar = var.reference();

}

Creating probe expressions to represent persistent data

Unlike temporary data, probe persistent data must be explicitly allocated and deallocated by the analysis tool code. If your analysis tool code requires probe data to be persistent from one invocation of the probe to the next, it must allocate memory within the target application process(es). To create a probe expression to allocate persistent data in a single process, the analysis tool can use the Process::alloc_mem or its blocking equivalent Process::balloc_mem. To create a probe expression to allocate persistent data on an application-wide basis (for all Process objects managed by the Application object), the analysis tool can use the functions Application::alloc_mem or Application::balloc_mem.

Table 37. Allocating memory in one or more target application processes
To create a probe expression to allocate persistent data in: A single process (Process object) Multiple processes (all of the Process objects managed by an Application object)
Using the asynchronous function alloc_mem
int initval=0;

ProbeExp pe = P.alloc_mem(int32_type(), 

     &initval, malloc_cb,        

     (GCBTagType) 0, sts);   

if (sts.status() != ASC_success)     

  printf("alloc_mem error: %s\n", 

     sts.status_name());   

else     

  Ais_main_loop();

 

 

void malloc_cb(GCBSysType s, GCBTagType t, 

     GCBObjType o, GCBMsgType m) {

  AisStatus *stsp = (AisStatus *) m;

  if (stsp->status() != ASC_success)

    printf("malloc error: %s\n", 

         stsp->status_name());

 

}

 


int count = A.get_count();

int initval=0;

ProbeExp pe = A.alloc_mem(int32_type(),

        &initval,app_cb,&count,sts);

if(sts.status() != ASC_success){

    printf("alloc_mem: Highest error is %s\n",

            sts.status_name());

}

for(i = 0; i < A.get_count(); i++) {

  if(A.status(i).status()!=ASC_success){

    count -= 1;

  }

}

if(count) Ais_main_loop();

 

//...

 

void app_cb(GCBSysType s, GCBTagType t, 

        GCBObjType o, GCBMsgType m) {

    int* count = (int *) t;

    AisStatus * stsp = (AisStatus *) m;

    if(stsp->status() != ASC_success) {

      // print error

    }

    *count -= 1;

    if(*count == 0) Ais_end_main_loop();

}

 

 


Using the blocking function balloc_mem
int initval=0;

ProbeExp pe = P.balloc_mem(int32_type(),

     &initval, sts);

  if (sts.status() != ASC_success)

    printf("balloc_mem error: %s\n", 

         sts.status_name());


int initval=0;

ProbeExp pe = A.balloc_mem(int32_type(),

        &initval,sts);

if(sts.status() != ASC_success) {

  printf("balloc_mem: %s\n",sts.status_name());

  for(int i=0;i<A.get_count();i++) {

    if(A.status(i).status()!=ASC_success){

      // print error

    }

  }

}


Keep in mind that, as with traditional programming, if your code allocates memory, it must later free that memory or it will create a memory leak. To create a probe expression that frees memory in a single process, the analysis tool can use the Process::free_mem function or its blocking equivalent Process::bfree_mem. To create a probe expression that frees memory on an application-wide basis (in all Process objects managed by an Application object), the analysis tool can use the functions Application::free_mem and Application::bfree_mem.

Table 38. Deallocating memory in one or more target application processes
To create a probe expression to deallocate persistent data in: A single process (Process object) Multiple processes (all of the Process objects managed by an Application object)
Using the asynchronous function free_mem
int count = 1;

sts = P.free_mem(pexp,proc_cb,&count);

if(sts.status() != ASC_success){

    printf("free_mem error %s\n",

            sts.status_name());

    count -= 1;

}

if(count) Ais_main_loop();

 

// ...

 

void proc_cb(GCBSysType s, GCBTagType t, 

        GCBObjType o, GCBMsgType m) {

    int* count = (int *) t;

    AisStatus * stsp = (AisStatus *) m;

    if(stsp->status() != ASC_success) {

      // print error

    }

    *count -= 1;

    if(*count == 0) Ais_end_main_loop();

}

 

 


int count = A.get_count();

AisStatus sts = A.free_mem(pexp,app_cb,&count);

if(sts.status() != ASC_success){

    printf("free_mem: error is %s\n",

           sts.status_name());

}

for(i = 0; i < A.get_count(); i++) {

  if(A.status(i).status()!=ASC_success){

    count -= 1;

  }

}

if(count) Ais_main_loop();

 

// ...

 

void app_cb(GCBSysType s, GCBTagType t, 

        GCBObjType o, GCBMsgType m) {

    int* count = (int *) t;

    AisStatus * stsp = (AisStatus *) m;

    if(stsp->status() != ASC_success) {

      // print error

    }

    *count -= 1;

    if(*count == 0) Ais_end_main_loop();

}

 


Using the blocking function bfree_mem
AisStatus sts = P.bfree_mem(pexp);

if(sts.status() != ASC_success) {

  printf("bfree_mem: %s\n",sts.status_name());

}


AisStatus sts = A.bfree_mem(pexp);

if(sts.status() != ASC_success) {

  printf("bfree_mem: %s\n",sts.status_name());

  for(int i=0;i<A.get_count();i++) {

    if(A.status(i).status()!=ASC_success){

      // print error

    }

  }

}

 


For more information on the functions described in the tables above, refer to their UNIX man pages or their entries in the DPCL Class Reference.

Creating probe expressions to represent actual function parameter values in the target application

Using the ProbeType::get_actual function, the analysis tool can create a probe expression that represents the actual value of a particular function parameter in the target application. When executed within the target application, the probe expression will determine the actual value of the parameter at that point in time. If the analysis tool were a debugger, for example, it might want to display this information to the user. The probe expression representing the actual parameter value can then be combined into a more complex probe expression that determines the actual parameter value and sends that information back to the analysis tool which displays it to the user.

In order to use the ProbeType::get_actual function, the analysis tool must first create a ProbeType object (using the ProbeType::function_type function) that represents the prototype (or type signature) of the function. The DPCL system needs to know this type information in order to correctly identify an actual parameter value for the function; if the ProbeType object created using the ProbeType::function_type function does not match the function prototype in the target application, the ProbeType::get_actual function will not return the correct information.

The following example code calls the ProbeType::function_type function to create a ProbeType object that represents a function prototype. This example then uses the ProbeType::get_actual function to create a probe expression that represents the actual parameter value of one of the function's parameters.

// create a prototype for a function that has an two arguments: and int

// and a pointer to int; and has no return value

 

ProbeType pr_args[2];

pr_args[0] = int32_type();

pr_args[1] = pointer_type(int32_type());

ProbeType proto = function_type(void_type(), 2, pr_args);

 

// create a probe expression representing the first parameter in a call

// to a function having this prototype

 

ProbeExp ap = proto.get_actual(0);

 

Creating a probe expression to represent a call to the Ais_send function expands on this example to show how an analysis tool can create a probe to send the actual parameter information back to the analysis tool.

For more information on the ProbeType object, refer to What is the ProbeType class?. For more information on the ProbeType::function_type or ProbeType::get_actual function, refer to the function's UNIX man page or the DPCL Class Reference

Step 2b: Create probe expressions to represent operations

Writing the analysis tool code that creates probe expressions representing operations is a fairly straightforward task. This is because the ProbeExp class has overloaded common operators so that expressions written within the context of the class do not execute locally, but instead call member functions that create the probe expression. For example, the following line of code:

ProbeExp pe3 = pe1 + pe2;

creates the probe expression pe3 which represents the addition of probe expressions pe1 and pe2. With the exception of the simple assignment operator (=), and the unary address operator (&), the ProbeExp class has overloaded all of the C++ operators in this way. This includes arithmetic operators (+, -, *, /, %), bitwise operators (<<, >>, ~, ^, &, |), relational operators (<, >, ==, !=, <=, >=), logical operators (&&, ||, !), assignment operators (+=, -=, *=, /=, %=, <<=, >>=, ^=, &=, |=), and dereference operators (*, []). Whenever any of these operators are used within the context of a probe expression, they create a probe expression that represents the operation. The operands can either be objects in memory, or probe expressions that evaluate to values. This means that a probe expression representing an operation could itself be used as an operand when creating another probe expression.

As already mentioned, the two operators that are not overloaded by the ProbeExp class are the simple assignment operator (=) and the unary address operator (&). So these two operators retain their original semantics -- "pe2 = pe1" performs the assignment of probe expression pe1 into probe expression pe2 within the analysis tool, and "&pe1" takes the address of the probe expression pe1 within the analysis tool. Instead of overloading the = and & operators, the ProbeExp class instead provides the member functions assign and address. For example, the following line of code:









ProbeExp pea = pe1.assign(pe2);

creates a probe expression that assigns the value computed by the probe expression pe2 into the storage location indicated by pe1, and this next line of code:









ProbeExp peb = pe.address();

creates a probe expression peb that represents the address of the probe expression pe.

The following tables outline the various operations that can be represented in probe expressions. Be aware that not all of the operator functions summarized in these tables are compatible with all operator types. For more information on any of the functions listed in these tables (including information on which types are valid for each overloaded operator function), refer to theDPCL Class Reference.

Table 39. Creating probe expressions to represent arithmetic operations
Operation: Operator: For example, this code: Creates a probe expression exp that represents the:
Addition +
ProbeExp exp = lhs + rhs;


addition of lhs and rhs.
Subtraction -
ProbeExp exp = lhs - rhs;


subtraction of rhs from lhs.
Multiplication *
ProbeExp exp = lhs * rhs;


multiplication of lhs and rhs.
Division /
ProbeExp exp = lhs / rhs;


division of lhs by rhs.
Modulus %
ProbeExp exp = lhs % rhs;


integer division of lhs by rhs in which the remainder rather than the dividend is returned.


Table 40. Creating probe expressions to represent bitwise operations
Operation: Operator: For example, this code: Creates a probe expression exp that represents the:
Bitwise AND &
ProbeExp exp = lhs & rhs;


bitwise AND of lhs and rhs.
Bitwise Inclusive OR |
ProbeExp exp = lhs | rhs;


bitwise inclusive OR of lhs and rhs.
Bitwise Exclusive OR ^
ProbeExp exp = lhs ^ rhs;


bitwise exclusive OR of lhs and rhs.
Bitwise Left Shift <<
ProbeExp exp = lhs << rhs;


bitwise left shift of lhs by rhs places.
Bitwise Right Shift >>
ProbeExp exp = lhs >> rhs;


bitwise right shift of lhs by rhs places.
Complement (bitwise inversion) ~
ProbeExp exp =  ~ rhs;


bitwise inversion of rhs.


Table 41. Creating probe expressions to represent logical operations
Operation: Operator: For example, this code: Creates a probe expression exp that represents the:
Logical AND &&
ProbeExp exp = lhs && rhs;


logical AND of rhs and lhs.
Logical OR ||
ProbeExp exp = lhs || rhs;


logical OR of lhs and rhs.
Logical Negation !
ProbeExp exp = ! rhs;


logical negation of rhs.


Table 42. Creating probe expressions to represent relational operations
Operation: Operator: For example, this code: Creates a probe expression exp that represents the:
Equality Comparison ==
ProbeExp exp = lhs == rhs;


comparison of lhs and rhs where 1 will be returned if the values are equal and 0 will be returned if they are not.
Inequality Comparison !=
ProbeExp exp = lhs != rhs;


comparison of lhs and rhs where 0 will be returned if the values are equal and 1 will be returned if they are not.
Greater Than Comparison >
ProbeExp exp = lhs > rhs;


relative size comparison of lhs and rhs where 1 is returned if lhs is greater than rhs and 0 is returned otherwise.
Less Than Comparison <
ProbeExp exp = lhs < rhs;


relative size comparison of lhs and rhs where 1 will be returned if lhs is less that rhs and 0 will be returned otherwise.
Greater Than or Equal To Comparison >=
ProbeExp exp = lhs >= rhs;


relative size comparison of lhs and rhs where 1 will be returned if lhs is greater than or equal to rhs, and 0 will be returned otherwise.
Less Than or Equal To Comparison <=
ProbeExp exp = lhs <= rhs;


relative size comparison of lhs and rhs where 1 will be returned if lhs is less than or equal to rhs, and 0 will be returned otherwise.


Table 43. Creating probe expressions to represent assignment operations
Operation: Operator: For example, this code: Creates a probe expression exp that:
Assignment None. The = operator could not be overloaded without causing simple expression manipulation to become unwieldy. Instead, use the assign function
ProbeExp exp = pe1.assign(pe2);


represents the assignment of the value represented by pe2 into the storage location indicated by pe1.
Addition Assignment +=
ProbeExp exp = lhs += rhs;


represents the addition of lhs and rhs and the subsequent assignment of the result into the storage location indicated by lhs.
Subtraction Assignment -=
ProbeExp exp = lhs -= rhs;


represents the subtraction of rhs from lhs and the subsequent assignment of the result into the storage location indicated by lhs.
Multiplication Assignment *=
ProbeExp exp = lhs *= rhs;


represents the multiplication of lhs and rhs and the subsequent assignment of the result into the storage location indicated by lhs.
Division Assignment /=
ProbeExp exp = lhs /= rhs;


represents the division of lhs by rhs and the subsequent assignment of the result into the storage location indicated by lhs.
Modulus Assignment %=
ProbeExp exp = lhs %= rhs;


represents the integer division of lhs by rhs in which the remainder rather than the dividend is returned and is subsequently assigned into the storage location indicated by lhs.
Bitwise AND and Assignment &=
ProbeExp exp = lhs &= rhs;


represents a bitwise AND of lhs and rhs, and the subsequent assignment of the result into the storage location indicated by lhs.
Bitwise Inclusive OR and Assignment |=
ProbeExp exp = lhs |= rhs;


represents a bitwise inclusive OR of lhs and rhs, and the subsequent assignment of the result into the storage location indicated by lhs.
Bitwise Exclusive OR and Assignment ^=
ProbeExp exp = lhs ^= rhs;


represents a bitwise exclusive OR of lhs and rhs, and the subsequent storage of the result into the storage location indicated by lhs.
Left Shift and Assignment <<=
ProbeExp exp = lhs <<= rhs;


represents a bitwise left shift of lhs by rhs places, and the subsequent storage of the result into the storage location indicated by lhs.
Right Shift and Assignment >>=
ProbeExp exp = lhs >>= rhs;


represents a bitwise right shift of lhs by rhs places, and the subsequent storage of the result into the storage location indicated by lhs.


Table 44. Creating probe expressions to represent pointer operations
Operation: Operator: For example, this code: Creates a probe expression exp that represents the:
Address None. The & operator could not be overloaded because it is used in passing arguments to functions that use call by reference. Instead, use the address function.
ProbeExp exp = pe.address();


referencing of the probe expression pe.
Dereference *
ProbeExp exp = * rhs;


dereferencing of the pointer value rhs.
Index and Dereference []
ProbeExp exp = lhs [ rhs ];


addition of rhs to lhs, and the subsequent dereferencing of the result.

Once the analysis tool has created a probe expression that represents an operation, it can:

Step 2c: Create probe expressions to represent a sequence of instructions

The preceding substep (Step 2b: Create probe expressions to represent operations) illustrates how an analysis tool can create probe expressions that represent operations. This substep now shows how an analysis tool can combine two probe expressions into a single probe expression that represents a sequence of the two operations. Such sequence probe expressions can then themselves be combined to form even longer sequences of instructions. To combine two probe expressions into a sequence, the analysis tool uses the ProbeExp::sequence function. For example, say the logic that an analysis tool wants to build into a probe expression can be represented in C code as:

pe1 = pe1 + 1;

fcn(pe1);

pe2 = pe1;

send(pe2);

To mimic this logic in a single probe expression, the analysis tool first creates probe expressions that represent the four basic operations:

ProbeExp stmt1 = pe1.assign(pe1 + ProbeExp(1));

ProbeExp stmt2 = fcn.call(1, &pe1);

ProbeExp stmt3 = pe2.assign(pe1);

args[0] = Ais_msg_handle;

args[1] = pe2.address();

args[2] = ProbeExp(4);

ProbeExp stmt4 = Ais_send.call(3, args);

Next, these four probe expressions are combined into two probe expressions -- each representing the sequence of two operations.

ProbeExp seq1 = stmt1.sequence(stmt2);

ProbeExp seq2 = stmt3.sequence(stmt4);

Finally, these two sequence probe expressions are combined into a longer sequence representing the entire probe logic.

ProbeExp seqall = seq1.sequence(seq2);

Once the analysis tool has created a sequence probe expression, it can:

Step 2d: Create probe expressions to represent conditional logic

This substep shows how an analysis tool can create a probe expression to perform conditional logic. To do this the analysis tool:

  1. creates probe expressions to represent the test condition, the code to execute if the condition tests true, and, optionally, the code to execute if the condition tests false.
  2. uses the ProbeExp::ifelse function to combine the three probe expressions into a probe expression representing a conditional statement.

Using the ProbeExp::ifelse function, the analysis tool is able to mimic an If or If/Else expression in C.

For example, say the logic that an analysis tool wants to build into a probe expression can be represented in C code as the If expression:

if (pe1 > 0) send(pe1);

To mimic this logic in a probe expression, the analysis tool first creates probe expressions to represent the test condition and the code to execute if the condition tests true.

// first the test condition

 

ProbeExp ce = pe1 > ProbeExp(0);

 

// now the then clause

 

args[0] = Ais_msg_handle;

args[1] = pe1.address();

args[2] = ProbeExp(4);

ProbeExp te = Ais_send.call(3, args);

Next, the probe expression representing the test condition calls the ifelse function. The probe expression to execute if the condition tests true is supplied as a parameter to the function. The ifelse function returns a single probe expression that represents the entire conditional statement.

ProbeExp exp = ce.ifelse(te);

So in the above example, if the probe expression ce evaluates to a non-zero value, the probe expression te executes. If the probe expression ce evaluates to zero, however, te does not execute. Instead, execution continues past the conditional statement. In the above example, this entire conditional logic is stored in the ProbeExp object exp.

The above example illustrates how a probe expression can mimic an If statement. The analysis tool can also use the ProbeExp::ifelse function to create a probe expression that mimics an If/Else statement. All it needs to do is supply an additional parameter to the ifelse function -- one representing the code to execute if the test condition tests false. For example, say the logic that an analysis tool wants to build into a probe expression can be represented in C code as the If/Else expression:

if (pe1 > 0) 

   send(pe1); 

else 

   send(pe2);

To mimic this logic in a probe expression, the analysis tool first creates probe expressions to represent the test condition, the code to execute if the condition tests true, and the code to execute if the condition tests false.

// first the test condition

 

ProbeExp ce = pe1 > ProbeExp(0);

 

// now the then clause

 

args[0] = Ais_msg_handle;

args[1] = pe1.address();

args[2] = ProbeExp(4);

ProbeExp te = Ais_send.call(3, args);

 

// and the else clause

 

args[0] = Ais_msg_handle;

args[1] = pe2.address();

args[2] = ProbeExp(4);

ProbeExp ee = Ais_send.call(3, args);

Next, the probe expression representing the test condition calls the ifelse function. The probe expression to execute if the condition tests true, and the probe expression to execute if the condition tests false are both supplied as parameters to the function. The ifelse function returns a single probe expression that represents the entire conditional statement.

ProbeExp exp = ce.ifelse(te, ee);

So in the above example, if the probe expression ce evaluates to a non-zero value, the probe expression te executes. If the probe expression ce evaluates to zero, the probe expression ee executes. In the above example, this entire conditional logic is stored in the ProbeExp object exp.

Once the analysis tool has created a conditional probe expression, it can:

Step 2e: Create probe expressions to represent function calls

This substep shows how an analysis tool can create a probe expression that represents a function call. The analysis tool can create such a probe expression to represent a call to:

In all four of these situations, the analysis tool uses the ProbeExp::call function to create a probe expression that represents a function call. For more information on the ProbeExp::call function, refer to its UNIX man page, or its entry in the DPCL Class Reference.

Once the analysis tool has created a probe expression that represents a function call, it can:

Creating a probe expression to represent a call to the Ais_send function

The Ais_send function enables a probe to send data back to the analysis tool. The Ais_send function takes three parameters -- a message handle for managing where the data is sent, the address of the data to send, and the size of the data being sent. To send the data located at the address &pcount back to the analysis tool, the call to the Ais_send function, if written in C code, would be:

Ais_send(handle, &pcount, 4);

To mimic this function call in a probe expression, the analysis tool first creates an array of probe expressions, with each expression in the array representing one of the parameters to the Ais_send function. As described in Step 2b: Create probe expressions to represent operations, the ProbeExp class did not overload the unary address operator (&). Note that the following code calls the ProbeExp::address function to mimic the unary address operator used in the preceding C code.

ProbeExp parms[3];

parms[0] = Ais_msg_handle;

parms[1] = pcount.address();

parms[2] = ProbeExp (4);

Next the analysis tool creates a probe expression that represents the call to Ais_send. The parameter values intended for the Ais_send function are passed as the probe expression array parms to the ProbeExp::call function. The first parameter to the ProbeExp::call function indicates the number of probe expressions in the array. In this example, there are three probe expressions in the array.

ProbeExp sendexp = Ais_send.call(3, parms);

Creating probe expressions to represent actual function parameter values in the target application contains example code that shows how the analysis tool can create a probe expression that, when executed within a target application process, will determine the actual value of a function parameter at that point in time. The following example code expands on this earlier example to show how the analysis tool can create a probe expression that, when executed within a target application process, will call the Ais_send function to send this actual parameter information back to the analysis tool.

// allocate an integer variable

 

ProbeExp v = P.balloc_mem(int32_type(), NULL, sts);

 

// create a prototype for a function that has an two arguments: and int

// and a pointer to int; and has no return value

 

ProbeType pr_args[2];

pr_args[0] = int32_type();

pr_args[1] = pointer_type(int32_type());

ProbeType proto = function_type(void_type(), 2, pr_args);

 

// create a probe expression representing the first parameter in a call

// to a function having this prototype

 

ProbeExp ap = proto.get_actual(0);

 

// create a probe expression to copy the value of the actual parameter to

// the variable we allocated and then send that value back

 

ProbeExp p1 = v.assign(ap);

 

ProbeExp sendargs[3];

sendargs[0] = Ais_msg_handle;

sendargs[1] = v.address();

sendargs[2] = ProbeExp(4);

ProbeExp p2 = Ais_send.call(3, sendargs);

 

ProbeExp p3 = p1.sequence(p2);

 

For more information on the Ais_send function, refer to its UNIX man page, or its entry in the DPCL Class Reference.

Creating a probe expression to represent a call to a UNIX function

The analysis tool can also use the ProbeExp::call function to create a probe expression that represents a call to a UNIX function like getrusage, times, or vtimes. The ability to call UNIX functions enables a probe to get performance and system-resource information for a target application process. For example, say that, in order to get system resource usage for a target application process, the analysis tool needs to create a probe expression that calls the UNIX function getrusage. In C code, a call to this function would look like:

#include <sys/resources.h>

struct rusage ru;

getrusage(RUSAGE_SELF, &ru);

To mimic this call in a probe expression, the analysis tool first creates an array of probe expressions, with each expression in the array representing one of the parameters to the getrusage function.

#include <sys/resources.h>

ProbeExp buf = P.balloc_mem(

     unspecified_type(sizeof(struct rusage)), NULL, sts);

if (sts.status() != ASC_success)

  printf("balloc_mem error: %s\n", sts.status_name();

else {

  ProbeExp params[2];

  params[0] = ProbeExp(RUSAGE_SELF);

  params[1] = buf.address();

}

Next the analysis tool creates a probe expression that represents a call to the getrusage function. In this example, moduleobj is the module in the source tree that contains the getrusage function, and i is the index of the getrusage function in that module.

SourceObj getrusage_fcn = moduleobj.child(i);

ProbeExp getrusage_ref = getrusage_fcn.reference();

ProbeExp the_call = getrusage_ref.call(2, params);

For more information about the UNIX function getrusage, refer to its UNIX man page.

Creating a probe expression to represent a call to a probe module function

The analysis tool can also use the ProbeExp::call function to create a probe expression that represents a call to a function contained in a probe module (a compiled object file containing one or more functions written in C) that has been loaded into a target application process. Once an analysis tool loads a particular probe module into a target application process, a probe expression is able to call any of the functions contained in the module. See Creating and calling probe module functions for more information on creating probe modules.

Say that a probe module you have created contains a function count that is designed to count the number of times the subroutine is called. This probe module function takes one parameter -- the predefined global variable Ais_msg_handle (which is used by the DPCL system when the probe sends data back to the analysis tool). In C code, a call to this function would look like:

count(handle);

To mimic this call in a probe expression, the analysis tool:

  1. calls the ProbeModule::get_reference function to create a probe expression that represents a reference to the count function. The analysis tool supplies the ProbeModule::get_reference function with the index of the count function within the probe module; the ProbeModule::get_reference function returns a probe expression representing a reference to the count function. To identify the count function within the probe module, the analysis tool can use the ProbeModule::get_count and ProbeModule::get_name functions.
    char name[128];
    
     
    
    ProbeModule pm("my_module");
    
    int fcncount = pm.get_count();
    
    int found = 0;
    
    for (int i=0; !found && i<fcncount; ++i) {
    
      pm.get_name(i, name, 128);
    
      if ( strcmp(name, "count") == 0)
    
        found = 1;
    
      }
    
     
    
      if ( found == 0 )
    
        printf("function 'count' not found in probe module\n");
    
      else {
    
        ProbeExp count_ref = pm.get_reference(i);
    
     
    
    

    For more information on the ProbeModule::get_reference, ProbeModule::get_count, and ProbeModule::get_name function, refer to their UNIX man pages, or their entries in the DPCL Class Reference.

  2. creates an array of probe expressions, with each expression in the array representing one of the parameters to the function. Since the count function has only one input parameter, this array has only one expression.
    ProbeExp parms[1];
    
    parms[0] = Ais_msg_handle;
    
    
  3. combines these probe expressions (which represent a reference to the probe module function count and the parameters to supply to that function) into a single expression that represents the function call. The parameter value intended for the count function is passed as the probe expression array args to the ProbeExp::call function. The first parameter to the ProbeExp::call function indicates the number of probe expressions in the array. In this example, there is only one probe expression in the array.
    ProbeExp the_call = count_ref.call(1, parms);
    
    

Creating a probe expression to represent a call to a target application function

Using the SourceObj::reference function, the analysis tool is able to create a probe expression that represents a function in the target application. This probe expression can then be combined by the analysis tool into a probe expression representing a call to that function. To create a probe expression that represents a target application function, however, the analysis tool must first navigate the target application's source code structure to identify the function of interest. If you are unfamiliar with SourceObj objects and the concept of source hierarchies, you may want to refer to What is the SourceObj class? before reading the following example code.

The following example code:

  1. Calls the Process::get_program_object function to return the top-level source object (SourceObj object) associated with a process.
  2. Identifies the program module that contains the function. To do this, it uses the SourceObj::child_count function to initialize a for loop and then, within the for loop, use the SourceObj::child and SourceObj::module_name functions to identify the target module.
  3. Calls the SourceObj::bexpand function to expand the module. This enables the analysis tool to navigate further down the source hierarchy to examine additional program structure (including functions). The analysis tool code could also expand a module using the asynchronous SourceObj::expand function.
  4. Identifies the function. To do this, the analysis tool code again uses the SourceObj::child_count function to initialize a for loop, and then, within the for loop, uses the SourceObj::child and SourceObj::get_demangled_name functions to identify the target function.
  5. Calls the SourceObj::reference function to create a probe expression that represents the function.
  6. Uses the ProbeExp::call function to combine the probe expression representing the function into a more complex probe expression representing a function call.
#include <dpcl.h>

#include <libgen.h>             // for basename()

// find the function function_name defined in the module module_name

// for a given Process p

// return: SourceObj contains the function.

//         or src_type()==SOT_unknown_type if the function is not found

SourceObj find_function(Process p,

                        char * module_name,

                        char * function_name)

{

    // get the source object associated with a process

    SourceObj progobj = p.get_program_object();

    SourceObj ret;              // return object

    SourceObj mod;              // module object

 

    // identifies the program module that contains the function

    // we preallocate a buffer to hold the module name

    int mod_name_length=1024;

    char * mod_name = new char [mod_name_length];

    int mod_index;

    for(mod_index = 0;mod_index < progobj.child_count();mod_index++) {

      mod = progobj.child(mod_index);

 

      // enlarge the name buffer if necessary.

      if (mod.module_name_length() >= mod_name_length) {

        // double the length

        while(mod_name_length < mod.module_name_length()) mod_name_length*=2;

        delete [] mod_name;

        mod_name = new char [mod_name_length];

      } /* endif */

 

      // get the module name

      mod.module_name(mod_name,mod_name_length);

 

      // depending on how the target application was compiled, the module

      // name may contain path information; for purposes of illustration, we compare

      // against the basename only.

      if (0 == strcmp(basename(mod_name),module_name)) {        // found

        break;

      } /* endif */

    }

    delete [] mod_name;

    if(mod_index == progobj.child_count())      // module not found

      return ret;

    // now we need to expand the module's SourceObj if necessary.

    // since only one module needs to expand, bexpand() will be easier

    if(mod.child_count() == 0) {                //

      AisStatus sts = mod.bexpand(p);

      if(sts.status()!=ASC_success)             // expand failed

        return ret;

    }

 

    // preallocate the function buffer to hold the function name

    int fun_name_length=1024;

    char * fun_name = new char [fun_name_length];

    SourceObj fun;

    for (int i=0; i<mod.child_count(); i++) {

      fun = mod.child(i);

      if(fun.src_type()!=SOT_function) continue;

      if (fun.get_demangled_name_length() >= fun_name_length) {

        while(fun_name_length < fun.get_demangled_name_length())

          fun_name_length*=2;

        delete [] fun_name;

        fun_name = new char [fun_name_length];

      } /* endif */

      fun.get_demangled_name(fun_name,fun_name_length);

      if(0 == strcmp(fun_name,function_name)) { // found the function

        ret = fun;

        break;

      }

    } /* endfor */

    delete [] fun_name;

    return ret;

}

 

main() {

    Process p;

    // ....

    SourceObj fun = find_function(p,"hello.c","foo");

    if(fun.src_type() == SOT_unknown_type) {

      // error report

    }

    ProbeExp foo = fun.reference();

    ProbeExp parms[1];

    // parms[0]=...

    ProbeExp the_call=foo.call(1,parms);

}

Example: Creating probe expressions

The following sample code creates a probe expression pass counter. To do this, it:

  1. Calls the Application::balloc_mem function to allocate a variable in all processes in the application. The result of this call is a probe expression pcount that represents the variable.
  2. Combines the probe expression pcount into a more complex probe expression addexp that represents the operation pcount = pcount + 1;.
  3. Creates another probe expression that represents an Ais_send function call that sends the value of pcount back to the analysis tool.
  4. Combines the two probe expressions into a single probe expression that represents a sequence of the two expressions.

Since this example uses the Ais_send function, note that it also provides a data callback routine for handling the data sent. Refer to Chapter 10, Creating data callback routines for more information on data callback routines.

// define a pass-counter probe

 

  ProbeExp pcount = A.balloc_mem(int32_type(), NULL, sts);

  if (sts.status() != ASC_success)

    printf("error from balloc_mem: %s\n", sts.status_name());

  else {

    ProbeExp addexpr = pcount.assign(pcount + ProbeExp(1));

    ProbeExp parms[3];

    parms[0] = Ais_msg_handle;

    parms[1] = pcount.address();

    parms[2] = ProbeExp(4);

    ProbeExp send_call = Ais_send.call(3, parms);

    ProbeExp pass_ctr = addexpr.sequence(send_call);

 

    // install the probe

 

    GCBFuncType cbarr[1];

    GCBTagType tagarr[1];

    ProbeHandle ph;

 

    cbarr[0] = count_cb;

    tagarr[0] = (GCBTagType) 0;

 

    // assume that point has already been set

 

    sts = A.binstall_probe(1, &pass_ctr, &point, cbarr, tagarr, &ph);

    if (sts.status() != ASC_success)

      printf("error from binstall_probe: %s\n", sts.status_name());

 

    .

    .

    .

} // end of program

 

// the callback function

 

void count_cb(GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg)

{

  Process *P = (Process *) obj;

  int *count = (int *) msg;

  int task = P->get_task();

 

  printf("task %d count = %d\n", task, *count);

}


Creating and calling probe module functions

A probe module is a compiled object file containing one or more functions written in C. Once an analysis tool loads a particular probe module into a target application, a probe expression is able to call any of the functions contained in the module. It is often preferable for a probe expression to call a probe module function rather than try to create the same probe logic with probe expressions alone. This is because probe modules:

In addition to these advantages, be aware that, if the probe is to be executed as a phase probe, than its logic must be contained in a probe module function. This is because phase probes, unlike point probes and one-shot probes, cannot be a simple probe expression that does not call a probe module function. To create a probe module and call one of its functions, you must:

  1. Create the probe module function.
  2. Compile the probe module.
  3. Instantiate a ProbeModule class object to represent the probe module.
  4. Load the probe module into one or more processes.
  5. Create a probe expression to call the probe module function(s).
  6. Create a data callback function to respond to any data message that can be sent by the probe.

The following steps describe these tasks in more detail. For sample code, see Example: Creating and calling a probe module function.

Step 1: Create probe module function

The first step in creating and calling a probe module function is to write the actual function. To send data back to the analysis tool, the probe module can call the built-in DPCL function Ais_send. The Ais_send function takes three parameters -- a message handle for managing where the data is sent, the address of the data to send, and the size of the data being sent. In order to make the prototype of the Ais_send function available to the compiler, your analysis tool code should include the header file dpclExt.h.

For example, the following probe module function is a generic pass counter that, when installed within a subroutine in a target application process, will count the number of times the subroutine is called. Each time the counter is incremented 10 times, the probe will call the Ais_send function to send a message back to the analysis tool.

#include <dpclExt.h>

 

count(void *msg_handle)

{

  static int pcount = 0;

  char msg[100];

 

  pcount++;

 

  if ((pcount % 10) == 0)

  {

    sprintf(msg, "I have been called %d times\n", pcount);

    Ais_send(msg_handle, (void *) msg, 1 + strlen(msg));

  }

}

If your probe module function calls the Ais_send function as in this example, your analysis tool code will need to include a data callback routine to respond to data sent by the probe module. Refer to Chapter 10, Creating data callback routines for more information on data callback routines. For more information on the Ais_send function, refer to its UNIX man page, or its entry in the DPCL Class Reference.

Step 2: Compile the probe module

Once you have written your probe module function, you'll need to compile it into an object file. This object file is the probe module. Before compiling the probe module, you'll need to create an export file to export its functions. What's more, if the probe module function calls any functions outside of the probe module, you'll need to create an import file. For example, in the preceding step, we encapsulated a simple pass counter into a function called count. Here's our export file:

* Any line started with a '*' is a comment

* We have a single function to export

count

 

Also, since the count function calls the built-in DPCL function Ais_send, we also create an import file:

#! .

Ais_send

 

Finally, we compile the file. Our export file is named count.exp, and our import file is named count.imp.

cc -o count count.c -bE:count.exp -bI:count.imp -bnoentry -I/usr/lpp/ppe.dpcl/include

Step 3: Instantiate a ProbeModule class object to represent the probe module

In order to load a probe module into one or more target application processes, the analysis tool must create a ProbeModule class object that represents the probe module. The probe module class is defined in the header file ProbeModule.h. To assign a probe module file name to a ProbeModule class object, you can use a non-default constructor, a non-default constructor with a copy constructor, or the default constructor with an assignment operator.

Table 45. Instantiating a ProbeModule object
To create a ProbeModule class object, the analysis tool can: For example:
Use a default constructor and an assignment operator to assign the file name of the probe module to the ProbeModule object.
ProbeModule my_probe_mod;

my_probe_mod = ProbeModule("count");


Use a non-default constructor to directly assign the file name of the probe module to the ProbeModule object.
ProbeModule my_probe_mod("count");


Use a non-default constructor and a copy constructor to assign the file name of the probe module to the ProbeModule object.
ProbeModule my_probe_mod = ProbeModule("count");


For more information on the probe module class and its constructors, refer to the DPCL Class Reference.

Step 4: Load probe module into Process class object(s)

In order for a probe expression to call a probe module function, the probe module function must be loaded into the same target application process(es) within which the probe expression will execute. To load a probe module on a single process basis, the analysis tool can use the asynchronous function Process::load_module or its blocking equivalent Process::bload_module. To load a probe module on an application-wide basis (for all Process objects managed by an Application object), the analysis tool can use the functions Application::load_module or Application::bload_module.

Table 46. Loading a probe module into one or more target application processes
To load a probe module: In a single process (Process object) In multiple processes (all of the Process objects managed by an Application object)
Using the asynchronous function load_module
int count = 1;

sts = P.load_module(&pmodule,proc_cb,&count);

if(sts.status() != ASC_success){

    printf("load_module error %s\n",

            sts.status_name());

    count -= 1;

}

if(count) Ais_main_loop();

 

// ...

 

void proc_cb(GCBSysType s, GCBTagType t, 

        GCBObjType o, GCBMsgType m) {

    int* count = (int *) t;

    AisStatus * stsp = (AisStatus *) m;

    if(stsp->status() != ASC_success) {

      // print error

    }

    *count -= 1;

    if(*count == 0) Ais_end_main_loop();

}

 

 


int count = A.get_count();

AisStatus sts = A.load_module(&pmodule,app_cb,

        &count);

if(sts.status() != ASC_success){

    printf("load_module: error is %s\n",

            sts.status_name());

}

for(i = 0; i < A.get_count(); i++) {

  if(A.status(i).status()!=ASC_success){

    count -= 1;

  }

}

if(count) Ais_main_loop();

 

 

 

void app_cb(GCBSysType s, GCBTagType t, 

        GCBObjType o, GCBMsgType m) {

    int* count = (int *) t;

    AisStatus * stsp = (AisStatus *) m;

    if(stsp->status() != ASC_success) {

      // print error

    }

    *count -= 1;

    if(*count == 0) Ais_end_main_loop();

}

 

 


Using the blocking function bload_module
AisStatus sts = P.bload_module(&pmodule);

if(sts.status() != ASC_success) {

  printf("bload_module: %s\n",

          sts.status_name());

}


AisStatus sts = A.bload_module(&pmodule);

if(sts.status() != ASC_success) {

  printf("bload_module: %s\n",sts.status_name());

  for(int i=0;i<A.get_count();i++) {

    if(A.status(i).status()!=ASC_success){

      // print error

    }

  }

}


For more information on the Process::load_module, Process::bload_module, Application::load_module, and Application::bload_module functions, refer to their UNIX man pages, or their entries in the DPCL Class Reference.

Step 5: Create probe expression to reference or call the probe module function

To execute a probe module function as a probe, your analysis tool code must create a probe expression that calls or references the probe module function.

For additional information on the ProbeExp::call and ProbeModule::get_reference functions, refer to their UNIX man pages, or their entries in the DPCL Class Reference.

Step 6: Create data callback function to respond to messages from the probe

As described in Step 1: Create probe module function, a probe module function can send data back to the analysis tool by calling the DPCL system-defined function Ais_send. If your probe module function does call the Ais_send function, then the analysis tool code must contain a data callback function that will handle the data that the Ais_send function will send. See Chapter 10, Creating data callback routines for more information on how to do this.

Example: Creating and calling a probe module function

The following example code:

count.c

#include <dpclExt.h>

count(void *msg_handle)

 

{

  static int pcount = 0;

  char msg[100];

 

  pcount++;

 

  if ((pcount % 10) == 0)

  {

    sprintf(msg, "I have been called %d times\n", pcount);

    Ais_send(msg_handle, (void *) msg, 1 + strlen(msg));

  }

}

count.exp

* Any line started with a '*' is a comment

* We have a single function to export

count

 

count.imp

#! .

Ais_send

 

compilation command:

cc -o count count.c -bE:count.exp -bI:count.imp -bnoentry -I/usr/lpp/ppe.dpcl/include

analysis tool code:

#include <dpcl.h>

void count_cb(GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg);

 

main(int argc, char *argv[]) {

 

    Process         P("phantom.pok.ibm.com", 12345);

 

    Ais_initialize();

 

    ProbeModule	    my_probe_mod("count");

 

    AisStatus sts = P.bconnect();  

 

    sts = P.bload_module(&my_probe_mod);

 

    // look for a specific function object in the probe module

    ProbeExp  count_fun;

    const int bufSize = 128;

    char      bufname[bufSize];   // buffer for module_name(..)

 

    for (int i=0; i < my_probe_mod.get_count(); i++)

    {

        char *funct_name = my_probe_mod.get_name(i, bufname, bufSize);

 

        // is this function count ??

        if ( strcmp(funct_name, "count") == 0 )

        {

            count_fun = my_probe_mod.get_reference(i);

            break; // yes.

        }

    }

 

    ProbeExp args[1];

    args[0] = Ais_msg_handle;

    ProbeExp count_call = count_fun.call(1, args);

 

    // now search the source obj tree for a particular function

    SourceObj  my_program = P.get_program_object();

    

    // Look for the correct Module

    SourceObj my_module;

    char      bufmname[bufSize];      // buffer for module_name(..)

 

    for (i=0; i < my_program.child_count(); i++)

    {

        my_module = my_program.child(i);

 

        char *mod_name = my_module.module_name(bufmname,bufSize);

 

        if ( strcmp(mod_name, "stencil.f") == 0 )

        {

            // expand the module

            sts = my_module.bexpand(P);

            break;

         }	

    }

 

    InstPoint  one_point;

    ProbeHandle   phandle;

    char buffname[bufSize];

    GCBFuncType cbarr[1];

    GCBTagType tagarr[1];

    cbarr[0] = count_cb;

    tagarr[0] = 0;

 

    for ( i=0; i < my_module.inclusive_point_count(); i++)

    {

        one_point = my_module.inclusive_point(i);

                        

        if ( (one_point.get_type() == IPT_function_call) &&

             (one_point.get_location() == IPL_before)     )

        {

            char *funct_name = one_point.get_demangled_name(buffname, bufSize);

                                                         

	         if ( strcmp(funct_name, "compute_stencil") == 0 )

            {

    	    	    // Install the trace probe at the function call site 

            	 sts = P.binstall_probe( 1, &count_call, &one_point, 

				                     cbarr, tagarr, &phandle);

 

               //activate the probe

               sts = P.bactivate_probe(1, &phandle);

	          }

        }

    }

 

    Ais_main_loop();

}

 

// collect data from the callback

void count_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg)

{                                                      

 

    // the message being send is a string

 

    char * count_message = (char *) msg;

 

    printf("%s\n", count_message);

 

}


[ Top of Page | Previous Page | Next Page | Table of Contents | Index ]