Dynamic Probe Class Library

Dynamic Probe Class Library Programming Guide


Chapter 3. A DPCL hello world program

Since this is a programming guide, tradition dictates that our first code example should be a "Hello World" program (a simple program that prints out the string "Hello World"). Unlike, traditional "Hello World" programs, however, our program (hello.c) does not print out the "Hello World" string. Instead, all it does is put itself into an infinite loop. This will be the target application in our example; we will show how to instrument it so that it sends the "Hello World" string back to our analysis tool.

The source code for both the "Hello World" target application and the analysis tool were copied to the directory /usr/lpp/ppe.dpcl/samples/hello when you installed DPCL. If you want to run these programs for illustration, refer to the instructions Compiling, linking, and running the DPCL hello world program.


The hello world target application

The code for our target application is shown below. The reason we have designed it to enter an infinite loop is so that the analysis tool we create in this example will have time to connect to it. Without the infinite loop, this target application would exit before our analysis tool had a chance to instrument it. We also put in some print and sleep statements so its execution will be more visible for this example.

#include <stdio.h>

 

void hello(void);

 

main()

{

    while (1)

    {

    	hello();

    }

}

 

void hello(void)

{

	/* This is where our call to printf("Hello World"); would be */

 

   fprintf(stdout,".");	 /* something to help us see what is going on */

   fflush(stdout);

   sleep(1);

 

   return;

}


The hello world analysis tool

Since the DPCL is a C++ class library, we will use C++ to build our analysis tool. We will call our analysis tool eut_hello and construct it in the file eut_hello.C. In order to instrument our target application to print out the "Hello World" string, our analysis tool needs to:

  1. initialize itself to use the DPCL
  2. connect to the target application
  3. create a probe that will, once installed in the target application, send the "Hello World" string back to the analysis tool
  4. install the probe into the target application
  5. enter the DPCL main event loop

These are the basic steps that DPCL analysis tools will follow to instrument target applications. Note that since we are instrumenting a serial application in this example:

Also be aware that this is a simple DPCL programming example that does not perform rigorous error checking. In general, actual DPCL programs you create should check the status of DPCL function calls and respond to error conditions. Here's the source code for our "Hello World" analysis tool. The rest of this chapter will describe this code in more detail.

#include <stdio.h>

#include <stdlib.h>    // for atoi() call

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

#include <dpcl.h>

 

#define MODNAME "hello.c"

#define FCNNAME "hello"

 

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

 

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 

main(int argc, char *argv[])

{

    if (argc != 3) {

        printf("Usage: %s <hostname> <target_pid>\n", argv[0]);

        exit(99);

    }

 

    int apppid = atoi(argv[2]);

 

    Process  P(argv[1], apppid);

 

    Ais_initialize();

 

    printf("Connecting to process %d on node %s\n", apppid, argv[1]);

 

    AisStatus sts = P.bconnect();

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

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

        printf("exiting...\n");

        return 99;

    }

 

    SourceObj myprog = P.get_program_object();

 

    SourceObj mymod;

 

    const int   bufSize = 1024;

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

 

    printf("\n");

    printf("module count = %d\n", myprog.child_count());

    printf("looking for module '%s'\n", MODNAME);

 

    int found = 0;    // flag for whether we find module we want

 

    for (int c = 0; found == 0 && c < myprog.child_count(); c++) {

        mymod = myprog.child(c);

        mymod.module_name(bufmname, bufSize);

        if (strcmp(basename(bufmname), MODNAME) == 0)

            found = 1;

    }

 

    if ( !found ) {

        printf("cannot find module '%s'\n", MODNAME);

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

    printf("\n");

    printf ("found module %s -- expanding....\n", MODNAME);

 

    sts = mymod.bexpand(P);

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

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

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

    printf("\n");

    printf("function count = %d\n", mymod.child_count());

    printf("look for the function '%s'\n", FCNNAME);

 

    SourceObj myfun;

 

    char      bufdname[bufSize];   // buffer for get_demangled_name(..)

 

    found = 0;

    for ( c = 0; found == 0 && c < mymod.child_count(); c++) {

        myfun = mymod.child(c);

        myfun.get_demangled_name(bufdname, bufSize);

        if (strcmp(bufdname, FCNNAME) == 0)

            found = 1;

    }

 

    if ( !found ) {

        printf("cannot find function '%s'\n", FCNNAME);

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

    printf("\n");

    printf("looking for an instrumentation point\n");

 

    InstPoint  mypoint;

 

    printf("point count = %d\n", myfun.exclusive_point_count());

 

    found = 0;

    for ( c = 0; found == 0 && c < myfun.exclusive_point_count(); c++) {

        mypoint = myfun.exclusive_point(c);

        if ( mypoint.get_type() == IPT_function_entry)

            found = 1;

    }

 

    if ( !found ) {

        printf("cannot find entry point for function '%s'\n", FCNNAME);

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

    ProbeExp parms[3];   // Create an array of expressions

 

    char * hello_message = "Hello World";

 

    parms[0] = Ais_msg_handle;

    parms[1] = ProbeExp(hello_message);

    parms[2] = ProbeExp (1 + strlen(hello_message));

 

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

 

    ProbeHandle   myph;

 

    GCBFuncType   mydcb = data_cb;

 

    GCBTagType    mytg = 0;

 

    printf("\n");

    printf("found instrumentation point -- installing probe\n");

    sts = P.binstall_probe(1, &myexp, &mypoint, &mydcb, &mytg, &myph);

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

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

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

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

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

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

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

    printf("\n");

    Ais_main_loop();

}

 

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 

void

data_cb(GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg)

{

    printf("%s\n", (char*) msg);

}

 

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 

Step 1: Initialize tool to use the DPCL system

In order to use the DPCL, all analysis tools must:

Step 2: Connect to the target application

The next step is to create a Process class object that represents the target application process, and then use this Process class object to connect to the target application process. The Process class constructor takes two parameters -- one specifying the host name where the target application is running, and the other specifying its process ID. Our analysis tool will have the user supply this information as command-line arguments when starting the tool; the first argument will specify the host name, and second will be the process ID.

The following statement initializes an object of type Process using the information supplied in the command-line arguments.

Process  P(argv[1], apppid);

Once our analysis tool has a Process class object that identifies the target application process, it connects to that process using the Process::bconnect member function.

    Ais_initialize();

 

    printf("Connecting to process %d on node %s\n", apppid, argv[1]);

 

    AisStatus sts = P.bconnect();

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

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

        printf("exiting...\n");

        return 99;

    }

Step 3: Create hello world probe

Now that it's connected to the target application, our analysis tool needs to create a point probe that will send the string "Hello World" back to our analysis tool each time execution enters the target application's hello function. A simple probe expression will be sufficient to accomplish this; a probe module will not be necessary in this case. To send data back to the analysis tool, the DPCL provides a predefined probe expression (Ais_send). Ais_send is a probe expression representation of a function for sending data back to the analysis tool. The function represented by the Ais_send probe expression takes three parameters -- a message handle for managing where the message is sent, the address of the data to send, and the size of the data being sent. If we were able to hand code this function call into our target application, it would look something like this:

Ais_send(Ais_msg_handle, "Hello World", 12);

Using the ProbeExp class in the DPCL, however, we have to use a slightly different approach to accomplish the same thing. That is because Ais_send is a probe expression representation of the actual function, and each parameter to the function also needs to be a probe expression. Then, all these individual probe expressions need to be combined into a single probe expression that represents the function call with parameters.

First our analysis tool needs to create an array of probe expressions, each representing one of the parameters to the Ais_send function. Note in the following code that Ais_msg_handle is another predefined probe expression supplied by the DPCL. It is specifically designed for the Ais_send function for managing where the message is sent.

    ProbeExp parms[3];   // Create an array of expressions

 

    char * hello_message = "Hello World";

 

    parms[0] = Ais_msg_handle;

    parms[1] = ProbeExp(hello_message);

    parms[2] = ProbeExp (1 + strlen(hello_message));

 

Next the analysis tool can create a probe expression that calls Ais_send using the three parameters defined in the parms array.

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

So now our analysis tool has a probe expression that, once installed as a point probe within the target application's hello function, will, each time execution enters the hello function, send the "Hello World" string back to the analysis tool.

What our analysis tool needs now is a callback routine that will handle those "Hello World" messages sent back from the point probe. In this example, our callback will simply print the string that it receives to standard output. The following code shows our callback definition:

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 

void

data_cb(GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg)

{

    printf("%s\n", (char*) msg);

}

 

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 

Step 4: Install and execute probe in the target application

In the preceding step (Step 3: Create hello world probe), we created a probe that will send the "Hello World" string back to the analysis tool. Now we need to identify the location, or "instrumentation point", within the target application where we can install our probe. When the analysis tool connected to the target application, the DPCL system gathered information about the target application's source structure down to the module (or file) level. Our analysis tool can examine, and expand, this structure to find an appropriate instrumentation point.

First the analysis tool needs to get a reference to the source structure of the target application process. This source structure is represented as a hierarchy of source objects (SourceObj class objects). The top level source object (the root of the hierarchy) is called the "program object". To get a reference to this object, our analysis tool uses the member function Process::get_program_object.

    SourceObj myprog = P.get_program_object();

 

Next our analysis tool needs to search through the list of children contained within the program object. This list of children should be the set of modules contained within the target application. To find the hello.c module, our analysis tool uses the SourceObj::child_count function to determine the number of children (modules) in the program object. Once it knows the number of children, it uses a for loop to check the name of each child module source object of the program object. To get each child module source object of the program object, the analysis tool uses the SourceObj::child function. To check the name of each child, the analysis tool uses the SourceObj::module_name function.

    SourceObj mymod;

 

    const int   bufSize = 1024;

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

 

    printf("\n");

    printf("module count = %d\n", myprog.child_count());

    printf("looking for module '%s'\n", MODNAME);

 

    int found = 0;    // flag for whether we find module we want

 

    for (int c = 0; found == 0 && c < myprog.child_count(); c++) {

        mymod = myprog.child(c);

        mymod.module_name(bufmname, bufSize);

        if (strcmp(basename(bufmname), MODNAME) == 0)

            found = 1;

    }

 

    if ( !found ) {

        printf("cannot find module '%s'\n", MODNAME);

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

    printf("\n");

    printf ("found module %s -- expanding....\n", MODNAME);

 

    sts = mymod.bexpand(P);

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

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

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

Note that once the analysis tool finds the hello.c module object, it must expand it. This is because, when the DPCL system connects to the target application, it retrieves the source hierarchy only down to the module level. To obtain additional information, our analysis tool must expand the specific module it is interested in. In the preceding code example, our analysis tool did this using the SourceObj::bexpand function.

sts = mymod.bexpand(P);

 

Now that the hello.c module object is expanded, our analysis tool can go deeper into the target application's source hierarchy. This next level contains the list of function and global data variables found within the module. In this next segment of code, our analysis tool looks through the list of the hello.c module's children looking for the hello function. To do this, the analysis tool uses the SourceObj::child_count, SourceObj::child, and SourceObj::get_demangled_name member functions.

    printf("\n");

    printf("function count = %d\n", mymod.child_count());

    printf("look for the function '%s'\n", FCNNAME);

 

    SourceObj myfun;

 

    char      bufdname[bufSize];   // buffer for get_demangled_name(..)

 

    found = 0;

    for ( c = 0; found == 0 && c < mymod.child_count(); c++) {

        myfun = mymod.child(c);

        myfun.get_demangled_name(bufdname, bufSize);

        if (strcmp(bufdname, FCNNAME) == 0)

            found = 1;

    }

 

    if ( !found ) {

        printf("cannot find function '%s'\n", FCNNAME);

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

Now that it has a reference to the hello function source object, our analysis tool needs to find an instrumentation point where it can place its point probe. In particular, the analysis tool is looking for an instrumentation point that represents the function entry point. To do this, the analysis tool uses a technique similar to the one used to find the hello function. Instead of the SourceObj::child_count, SourceObj::type, and SourceObj::module_name functions that it used to find the hello function, however, it now uses functions designed to identify instrumentation point information. These functions are the SourceObj::exclusive_point_count, SourceObj::exclusive_point, and SourceObj::get_type functions.

    printf("looking for an instrumentation point\n");

 

    InstPoint  mypoint;

 

    printf("point count = %d\n", myfun.exclusive_point_count());

 

    found = 0;

    for ( c = 0; found == 0 && c < myfun.exclusive_point_count(); c++) {

        mypoint = myfun.exclusive_point(c);

        if ( mypoint.get_type() == IPT_function_entry)

            found = 1;

    }

 

    if ( !found ) {

        printf("cannot find entry point for function '%s'\n", FCNNAME);

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

At this point in the analysis tool's code, it has all it needs to instrument the target application. It has:

Now the analysis tool can actually install the probe expression as a point probe within the target application. The following code installs our probe using the member function Process::binstall_probe which takes, as parameters:

    ProbeHandle   myph;

 

    GCBFuncType   mydcb = data_cb;

 

    GCBTagType    mytg = 0;

 

    printf("\n");

    printf("found instrumentation point -- installing probe\n");

    sts = P.binstall_probe(1, &myexp, &mypoint, &mydcb, &mytg, &myph);

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

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

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

Now the point probe is installed, but it is not yet active. To activate our probe so that its code will execute as if it were part of the target application's code, our analysis tool calls the function Process::bactivate_probe as shown below. Note that this function takes, as a parameter, the probe handle myph (specified when the analysis tool installed the probe).

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

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

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

        printf("disconnecting and exiting...\n");

        P.bdisconnect();

        return 99;

    }

 

    printf("\n");

    Ais_main_loop();

}

Step 5: Entering the DPCL main event loop

At the bottom of our main routine, note that the analysis tool calls the DPCL event handling function (Ais_main_loop). This is necessary because the DPCL system is asynchronous in the way it interfaces with the analysis tool. Calling the Ais_main_loop function places the analysis tool in an event loop for responding asynchronously to messages sent by the DPCL system.

Ais_main_loop();


Compiling, linking, and running the DPCL hello world program

Now that we have described the code contained in both our target application (hello.c) and our analysis tool (eut_hello.C), let's run these programs. When you installed DPCL, the source code for these two programs was copied to the directory /usr/lpp/ppe.dpcl/samples/hello. To run these programs:

  1. Copy the contents of the hello directory to a location where you can update the files. You must do this since root owns the hello directory, and you otherwise will not be able to run the makefiles provided.
    cp /usr/lpp/ppe.dpcl/samples/hello/* mydir
    
    
  2. Change directories to the directory that contains the copied files.
    cd mydir
    
    
  3. Compile and link the target application as you normally would. We have provided a makefile for this step.
    make -f Makefile.hello
    
    
  4. Compile and link the analysis tool. We have provided a makefile for this step.
    make -f Makefile.eut_hello
    
    
  5. Start the target application.
    hello
    
    
  6. In a separate xterm window, start the analysis tool, passing it the host name of the node running the target application and the target application's process ID. You can use the UNIX command ps to ascertain the target application's process ID.
    eut_hello hostname process-ID
    
    

As the analysis tool runs, it prints out informational messages to show where it is in the process of instrumenting the target application. It installs the probe into the target application that, when it executes, sends the "Hello World" string back to the analysis tool. The callback routine for the probe, when activated, prints out the "Hello World" string.


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