Dynamic Probe Class Library

Dynamic Probe Class Library Programming Guide


Appendixes


Appendix A. A DPCL test coverage tool

The following sample program (eut_testcov.C) was copied to the directory (/usr/lpp/ppe.dpcl/samples/testcov) when you installed DPCL. It illustrates how you can use the DPCL system to create a simple test coverage tool that periodically reports the frequency of function calls in a particular module.

First let's look at how this DPCL test coverage tool appears from the end user's point of view, and then we will examine its source code in more detail. First of all, depending on the program arguments, you can:

Here's a sample run of the eut_testcov program. In this sample run, the arguments specify that the eut_testcov program should start a particular POE application running as 4 processes. Since the program is designed to show the test coverage for a particular module in a particular process, the user is first prompted to select a particular process.

Enter the process number to probe (0 to 3)

 

0

 

Using process 0

 

Once the particular process is entered, the user is then prompted to select a module in the process.

Enter the source file name to check test coverage.

  type !Help to list source files

 

prod_cons.c

 

Enter the name of the final output file.

 

cov_out

 

Once the module is selected, the DPCL test coverage tool will then display the frequency of function calls in that module at intervals of 15 seconds until the end user interrupts this by pressing <Control>-c. When the user presses <Control>-c, the program then prints the final test coverage information to a file. Here is some sample output showing the kind of information displayed. Note here that some of the output is coming from the test coverage tool, while the produce/consume messages are coming from our sample target application. For clarity, the following sample output shows the analysis-tool messages in bold.

bcreate() was successful.

Using Process 25648 on host pe02.pok.ibm.com...

Module prod_cons.c found... expanding

bexpand() was successful.

 

function "f2" found

function "produce" found

function "consume" found

function "main" found

function "alarm_handler" found

function "consume_data" found

function "domath" found

function "produce_data" found

 

*Display interval set at every 15 seconds*

*Enter <ctrl>-c to exit*

bstart() was successful

 Compute 3: checking in

produce 3 0

Control 0: #nodes = 4

Control: expect to receive 1200 messages

Compute 1: checking in

produce 1 0

Compute 2: checking in

produce 2 0

consume 3 0

produce 3 1

produce 2 1

produce 1 1

consume 2 0

produce 3 2

produce 2 2

produce 1 2

consume 1 0

produce 3 3

produce 2 3

 

** Number of times each function was executed:

** f2		0

** produce		0

** consume		1

** main		1

** alarm_handler		0

** consume_data		3

** domath		3

** produce_data		0

 

** Test coverage = 44 %

produce 1 3

consume 3 1

produce 3 4

produce 2 4

produce 1 4

consume 2 1

produce 3 5

produce 2 5

produce 1 5

produce 3 6

consume 1 1

produce 2 6

produce 1 6

produce 3 7

consume 3 2

produce 2 7

produce 1 7

 

<ctrl>-c

 

** Number of times each function was executed:

** f2		0

** produce		0

** consume		1

** main		1

** alarm_handler		0

** consume_data		7

** domath		7

** produce_data		0

 

** Test coverage = 44 %

*Please check "/u/cywong/public/eut_testcov.res" for the final result.

produce 3 8

consume 2 2

battach() was successful

bdestroy() was successful

Now let's look at the source code for the DPCL test coverage tool. First, let's take a look at the complete source, then we'll describe its parts in more detail.

// IBM_PROLOG_BEGIN_TAG 

// This is an automatically generated prolog. 

//  

//  

//  

// Licensed Materials - Property of IBM 

//  

// Restricted Materials of IBM 

//  

// (C) COPYRIGHT International Business Machines Corp. 1999 

// All Rights Reserved 

//  

// US Government Users Restricted Rights - Use, duplication or 

// disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 

//  

// IBM_PROLOG_END_TAG 

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

/*  

 

    Program: eut_testcov

    Program function:

       Periodically shows the frequency of function calls in a module 

    Usage:

       eut_testcov <host name> <pid> <module name>

          OR

       eut_testcov <host name> poe_pid <poe pid>

          OR

       eut_testcov <host name> path </full path/executable>

          OR

       eut_testcov <host name> poe_path </full path/poe> </full path/executable>

 

       - specify "d" for <host name> to use default host

 

*/

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

const int INTERVAL=15;      //time interval for display information

const int BUFLEN=80;        //size of buffer for storing file and module name

char FILEOUT[128];

// --------------------------------------------------------------------------

#include <stdio.h>                                                

#include <stdlib.h>                                                

#include <string.h>                                                

 

#include <AisGlobal.h>                                                

#include <AisInit.h>

#include <AisMainLoop.h>

#include <PoeAppl.h>

#include <Process.h>

#include <InstPoint.h>

#include <ProbeExp.h>

#include <Application.h>

#include <ProbeType.h>

#include <ProbeHandle.h>

#include <InstPoint.h>

#include <SourceObj.h>

#include <AisStatus.h>

#include <LogSystem.h>

#include <AisHandler.h>

 

#include <iostream.h>

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

Process     P;

PoeAppl     A;

struct fun_info{

    char funname[256];

    int pcount;

};

 

int fun_count=0;             

int fun_num;             

int lflag=0;    //Ais_main_loop() started flag

 

fun_info *fun_arr = NULL;

 

int num_installed = 0;

char filename[BUFLEN];

 

void write_results(void);

void ck_process(void);

void print_fun_info(void);

void get_Process(void);

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

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

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

int sighandler(int signal);

int sighandler_ALRM(int signal);

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

main(int argc, char *argv[]) 

{

    const char USAGE1[]="\nUSAGE: eut_testcov <host name> pid <pid>\n";

    const char USAGE2[]="\t\tOR\n\teut_testcov <host name> poe_pid <poe pid>\n";

    const char USAGE3[]="\t\tOR\n\teut_testcov <host name> path </full path/executable\n";

    const char USAGE4[]="\t\tOR\n\teut_testcov <host name> poe_path </full"

      "path/poe> </full path/executable>\n";

    const char USAGE5[]="\t*specify \"d\" for <host name> to use default host\n";

 

    const int bufSize = 128;

    int hostname_length;

    

    char        hostname[bufSize];      // buffer for get_host_name()

    if (argc < 4){

      cout<<"\n**Incorrect numbers of argument"

      "entered**"<<USAGE1<<USAGE2<<USAGE3<<USAGE4<<USAGE5<<endl;

      exit(0);

    }

 

    char **argvv=argv+3;

 

    Ais_initialize();

    Ais_add_signal(SIGINT,sighandler);

    Ais_add_signal(SIGALRM,sighandler_ALRM);

 

    if(strcmp(argv[1],"d")==0){

      gethostname(hostname,BUFLEN);

      printf("  *Running on \"%s\"\n",hostname);

    } else

      strcpy(hostname,argv[1]);

 

    if (strcmp("poe_pid",argv[2]) == 0) {   //POE

 

      AisStatus sts = A.binit_procs(hostname, atoi(argv[3]));

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

        printf("binit_procs() was not successful...  %s\n",sts.status_name());

        exit(0);

      } else {

        printf("binit_procs() was successful.\n");

        sts = A.bconnect();

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

          printf("bconnect() was not successful...  %s\n",sts.status_name());

          exit(0);

        } else {

          printf("bconnect() was successful.\n");

        }

        get_Process();

      }

      sts=A.battach();   //stop the application

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

         printf("battach() was not successful...  %s\n",sts.status_name());

         exit(0);

      } else {

         printf("battach() was successful.\n");

     }

 

 

    } else if (strcmp("pid",argv[2]) == 0){     //Not POE

      P = Process(hostname, atoi(argv[3]));

      AisStatus sts = P.bconnect();

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

        printf("bconnect() was not successful...  %s\n",sts.status_name());

        exit(0);

      } else {

        printf("bconnect() was successful.\n");

      }

      sts=P.battach();   //stop the application

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

        printf("battach() was not successful...  %s\n",sts.status_name());

        exit(0);

      } else {

        printf("battach() was successful.\n");

      }

 

 

    } else if (strcmp("path",argv[2]) == 0){      //start a new appl

      AisStatus sts = P.bcreate(hostname,argv[3],argvv,NULL,

                      stdout_cb,NULL, stderr_cb,NULL);

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

        printf("bcreate() was not successful...  %s\n",sts.status_name());

        exit(0);

      } else {

        printf("bcreate() was successful.\n");

      }

    

     } else if (strcmp("poe_path",argv[2]) == 0){

      AisStatus sts=A.bcreate(hostname,argv[3],argvv,NULL,

            stdout_cb, NULL, stderr_cb, NULL);

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

        printf("bcreate() was not successful...  %s\n",sts.status_name());

        exit(0);

      } else {

        printf("bcreate() was successful.\n");

      }

 

      get_Process();

 

     } else {

      cout<<USAGE1<<USAGE2<<USAGE3<<USAGE4<<USAGE5<<endl;

      exit(0);

    }

 

 

    printf("Using Process %d",P.get_pid());

 

    P.get_host_name(hostname,bufSize);

    printf(" on host %s...\n",hostname);

 

    SourceObj myprog = P.get_program_object();

    SourceObj mymod;

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

 

    char name[BUFLEN];

    int found=0;

    

    while(found==0)

    {

      cout<<"Enter the source file name to check test coverage."<<endl;

      cout<<"type !Help to list source files"<<endl;

 

      cin>>filename;

 

      if(strcmp(filename,"!Help")==0)

      {

        //  loop through source files to list

        cout<<"Source files:"<<endl;

        int count=myprog.child_count();

        for(int x=0;x<count;x++)

        {

          mymod=myprog.child(x);

          mymod.module_name(name,BUFLEN);

          cout<<x<<" "<<name<<endl;

        }

      } else

       {

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

        {

          mymod = myprog.child(c);

          const char * modname = mymod.module_name(bufmname,bufSize);

 

	  if (strcmp(modname, filename) ==0) 

          {

            printf("Module %s found... expanding\n",filename);

	    AisStatus sts = mymod.bexpand(P);

            if (sts.status() != ASC_success)

            {

              printf("bexpand() was not successful.. %s\n",sts.status_name());

              exit(0);

            }

	    else

	    {

              printf("bexpand() was successful.\n");

            }

            found=1;

	    break;

	  } 

        }

      //}

      if (found == 0)

           cout<<"\""<<filename<<"\" not found.\n";

      }

                         

    }                         //end while    

 

    cout<<"Enter the name of the final output file."<<endl;

    cin>>FILEOUT; 

 

    ProbeExp parms[3];

    parms[0]=Ais_msg_handle;

    parms[1]=ProbeExp("xxx");    //send dummy information to application

    parms[2]=ProbeExp(4);

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

 

    SourceObj myfun;

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

    

#ifdef DEBUG

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

#endif

 

    fun_count=mymod.child_count();

    fun_arr = new fun_info[fun_count];

    

    printf("\n");

    for ( int c = 0; c < fun_count; c++)

    {

   	myfun = mymod.child(c);

	const char * funname = myfun.get_demangled_name(bufdname,bufSize);

        if (funname == NULL) continue;

        printf("function \"%s\" found\n",funname);

 

        strcpy(fun_arr[fun_num].funname,funname);

        fun_arr[fun_num].pcount=0;

        fun_num++;

 

        InstPoint  mypoint;

 

        for ( int d = 0; d < myfun.exclusive_point_count(); d++)

        {

           mypoint = myfun.exclusive_point(d);

 

          if ( mypoint.get_type() == IPT_function_entry)

          {

 

#ifdef DEBUG

            printf("  Found function entry point.\n");

#endif

 

            ProbeHandle   myph;

            GCBFuncType   mydcb = data_cb;

            GCBTagType    mytg = (GCBTagType) (fun_num-1);

 

            AisStatus sts = P.binstall_probe(1, &mysend, &mypoint,

                          &mydcb, &mytg,

                          &myph);

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

	      printf("  binstall_probe() was not successful.. "

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

              exit(0);

            } else {

              ++num_installed;

 

#ifdef DEBUG  

              printf("  binstall_probe() was successful\n");

#endif

 

            }

 

 

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

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

              printf("  bactivate_probe() was not successful.. %s\n",sts.status_name());

              exit(0);

            } else {

 

#ifdef DEBUG

              printf("  bactivate_probe() was successful\n");

#endif

 

            }

            

            break;

          }

        }

    }

 

    printf("\n*Display interval set at every %d seconds*\n",INTERVAL);

    printf("*Enter <CTRL>-c to exit*\n");

    sleep(3);

 

    if(strcmp(argv[2],"path")==0){

      AisStatus sts=P.bstart();

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

        printf("bstart() was successful\n");

      } else {

        printf("bstart() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    }

 

    if (strcmp(argv[2],"poe_path")==0)

    {

      AisStatus sts=A.bstart();

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

        printf("bstart() was successful\n");

      } else {

        printf("bstart() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    } else if(strcmp(argv[2],"poe_pid")==0){

      AisStatus sts=A.bresume();

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

        printf("bresume() was successful\n");

      } else {

        printf("bresume() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    } else if(strcmp(argv[2],"pid")==0){

      AisStatus sts=P.bresume();

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

        printf("bresume() was successful\n");

      } else {

        printf("bresume() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    }

 

 

    alarm(INTERVAL);

    lflag=1;

    Ais_main_loop();

 

    if(strcmp(argv[2],"path")==0){

      AisStatus sts=P.battach();

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

        printf("battach() was successful\n");

      } else {

        printf("battach() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

      sts=P.bdestroy();

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

        printf("bdestroy() was successful\n");

      } else {

        printf("bdestroy() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    }

 

    if (strcmp(argv[2],"poe_path")==0)

    {

      AisStatus sts=A.battach();

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

        printf("battach() was successful\n");

      } else {

        printf("battach() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

      sts=A.bdestroy();

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

        printf("bdestroy() was successful\n");

      } else {

        printf("bdestroy() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    }

 

 

}

 

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

void print_fun_info(void)

{

  int num_tested=0;

  if (fun_count == 0){

      printf(" No function found!!\n");

  } else {

     printf("\n** Number of times each function was executed:\n");

     for (int i=0; i< num_installed; i++){

         printf("** %s\t\t%d\n",fun_arr[i].funname,fun_arr[i].pcount);

         if (fun_arr[i].pcount >0) num_tested++;

     }

     printf("\n** Test coverage = %d %%\n",(num_tested*100)/num_installed);

     fflush(stdout);

  }

}

 

void

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

{

        Process *p = (Process *)obj;

 

        int     i = (int) tag;

#ifdef DEBUG

        printf("Task %d sent the function number %d\n", p->get_task(),i);

#endif

        fun_arr[i].pcount++;

 

}

int sighandler(int s){

  if (lflag == 1){

    Ais_end_main_loop();

    write_results(); 

  } else {

    printf("*\"<CTRL>-c\" was entered.  Exiting..\n");

    exit(0);

  } 

  return(0);

}

int sighandler_ALRM(int signal){

   ck_process();

 

   print_fun_info();

   sleep(5);

   alarm(15);

   return(0);

}

void

stdout_cb(GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg) {

    //  callback to receive standard out from application and display

 

    char *p = (char *) msg;

    printf("stdout_cb: ");

    for (int i=0; i<sys.msg_size; i++) {

        printf("%c",*p);

        p++;

    }

    printf("\n");

    fflush(stdout);

}

void

stderr_cb(GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg) {

    //  callback to receive standard error from application and display

 

    char *p = (char *) msg;

    printf("stder_cb: ");

    for (int i=0; i<sys.msg_size; i++) {

        printf("%c",*p);

        p++;

    }

    printf("\n");

    fflush(stdout);

}

void get_Process(void){

  char procname[256];

  int procno,proccnt;

  proccnt = A.get_count() -1;

  cout << "Enter the process number to probe (0 to "<<proccnt<<")"<<endl;

  cin>>procname;

  procno=atoi(procname);

  cout<<"Using process "<<procno<<endl;

 

  P = A.get_process(procno);

}

void ck_process(void){

   //check to see if the process was destroyed

 

   ConnectState *state=new ConnectState;

    AisStatus sts = P.query_state(state);

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

      if (*state == PRC_destroyed){

          cout<<"Process terminated.  Exiting...\n";

          write_results();

          exit(0);

      }

    }

 

}

void write_results(void){

    FILE *fileout=fopen(FILEOUT,"w");

    int num_tested=0;

    fprintf(fileout,"Source file \"%s\":\n",filename);

    if (fun_count == 0){

      fprintf(fileout," No function found!!\n");

    } else {

      fprintf(fileout,"\nNumber of times each function was executed:\n");

      for (int i=0; i< num_installed; i++){

         fprintf(fileout," %s\t\t%d\n",fun_arr[i].funname,fun_arr[i].pcount);

         if (fun_arr[i].pcount >0) num_tested++;

      }

      fprintf(fileout," Test coverage = %d %%\n",(num_tested*100)/num_installed);

    }

    fclose(fileout);

    printf("*Please check \"%s\" for the final result.\n",FILEOUT);

}


Now let's examine this program in greater detail. The first thing it does is initialize itself to use the DPCL system. Among other, general, initialization tasks (such as declaring variables), our program:

  1. includes the DPCL header files.
    #include <AisGlobal.h>                                                
    
    #include <AisInit.h>
    
    #include <AisMainLoop.h>
    
    #include <PoeAppl.h>
    
    #include <Process.h>
    
    #include <InstPoint.h>
    
    #include <ProbeExp.h>
    
    #include <Application.h>
    
    #include <ProbeType.h>
    
    #include <ProbeHandle.h>
    
    #include <InstPoint.h>
    
    #include <SourceObj.h>
    
    #include <AisStatus.h>
    
    #include <LogSystem.h>
    
    #include <AisHandler.h>
    
    
  2. calls the DPCL initialization routine (Ais_initialize) to initialize the DPCL system.
    Ais_initialize();
    
    
  3. calls the Ais_add_signal function twice to specify that the DPCL system should add the SIGINT and SIGALRM signals to the list of signals it manages. When one of these signals occurs, the DPCL system will call its respective handler. The handler for the SIGINT signal will respond to the end user pressing <Control>-c by printing the final coverage information to a file. Handling the SIGALRM signal enables our program to print the coverage information to the screen at 15 second intervals; the SIGALRM signal will be detected by the DPCL system at those intervals and it will call the appropriate handler.
    Ais_add_signal(SIGINT,sighandler);
    
    Ais_add_signal(SIGALRM,sighandler_ALRM);
    
    

Next, our program will either connect to or create the target application process(es) based on the arguments that the user passed to the program.
If the user wants to: Then our analysis tool:
connect to a running POE application
  1. calls the PoeAppl::binit_procs function to initialize the PoeAppl object A to contain Process objects that represent the POE application's processes.
  2. calls the Application::bconnect function to connect to the POE processes represented by the Process objects managed by the PoeAppl object A.

connect to a single running process
  1. initializes the Process object P with information (host name and process ID) that identifies the UNIX process.
  2. calls the Process::bconnect function to connect to the process.

start a single-process application running calls the Process::bcreate function to create the process. The process is created in a "stopped state"; its execution is stopped before the first executable instruction. The analysis tool will later, after point probes are installed, start this process running by calling the Process::bstart function.
start the multiple processes of a POE application running calls the PoeAppl::bcreate function to create the processes. The processes are created in a "stopped state"; their execution is stopped before the first executable instruction. The analysis tool will later, after point probes are installed, start these processes running by calling the Application::bstart function.

    if(strcmp(argv[1],"d")==0){

      gethostname(hostname,BUFLEN);

      printf("  *Running on \"%s\"\n",hostname);

    } else

      strcpy(hostname,argv[1]);

 

    if (strcmp("poe_pid",argv[2]) == 0) {   //POE

 

      AisStatus sts = A.binit_procs(hostname, atoi(argv[3]));

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

        printf("binit_procs() was not successful...  %s\n",sts.status_name());

        exit(0);

      } else {

        printf("binit_procs() was successful.\n");

        sts = A.bconnect();

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

          printf("bconnect() was not successful...  %s\n",sts.status_name());

          exit(0);

        } else {

          printf("bconnect() was successful.\n");

        }

        get_Process();

      }

      sts=A.battach();   //stop the application

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

         printf("battach() was not successful...  %s\n",sts.status_name());

         exit(0);

      } else {

         printf("battach() was successful.\n");

     }

 

 

    } else if (strcmp("pid",argv[2]) == 0){     //Not POE

      P = Process(hostname, atoi(argv[3]));

      AisStatus sts = P.bconnect();

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

        printf("bconnect() was not successful...  %s\n",sts.status_name());

        exit(0);

      } else {

        printf("bconnect() was successful.\n");

      }

      sts=P.battach();   //stop the application

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

        printf("battach() was not successful...  %s\n",sts.status_name());

        exit(0);

      } else {

        printf("battach() was successful.\n");

      }

 

 

    } else if (strcmp("path",argv[2]) == 0){      //start a new appl

      AisStatus sts = P.bcreate(hostname,argv[3],argvv,NULL,

                      stdout_cb,NULL, stderr_cb,NULL);

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

        printf("bcreate() was not successful...  %s\n",sts.status_name());

        exit(0);

      } else {

        printf("bcreate() was successful.\n");

      }

    

     } else if (strcmp("poe_path",argv[2]) == 0){

      AisStatus sts=A.bcreate(hostname,argv[3],argvv,NULL,

            stdout_cb, NULL, stderr_cb, NULL);

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

        printf("bcreate() was not successful...  %s\n",sts.status_name());

        exit(0);

      } else {

        printf("bcreate() was successful.\n");

      }

If our analysis tool has connected to or created a POE application, note that, in the preceding code, it calls the get_Process program function to prompt the user to select a single process in the POE application to instrument. The program function get_Process:

  1. calls the Application::get_count function to determine the number of processes in the POE application represented by the PoeAppl object A.
  2. calls the Application::get_process function to get the Process object that represents the process identified by the user. The Process object returned by the Application::get_process function is assigned to the Process object variable P.
void get_Process(void){

  char procname[256];

  int procno,proccnt;

  proccnt = A.get_count() -1;

  cout << "Enter the process number to probe (0 to "<<proccnt<<")"<<endl;

  cin>>procname;

  procno=atoi(procname);

  cout<<"Using process "<<procno<<endl;

 

  P = A.get_process(procno);

}

From this point on, the target application is mainly concerned with a single process represented by the Process object P. The analysis tool prints the process ID and the name of the host machine running the process to standard output. It gets this information by calling the Process::get_pid and Process::get_host_name functions.

printf("Using Process %d",P.get_pid());

 

P.get_host_name(hostname,bufSize);

printf(" on host %s...\n",hostname);

Next, our analysis tool needs to get a reference to the source structure of the selected target application process. This source object 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", and our analysis tool gets a reference to it using the Program::get_program_object function. The SourceObj object returned by the Process::get_program_object function is assigned to the SourceObj object variable myprog. An additional SourceObj object (mymod) is instantiated to represent an individual module in the source hierarchy.

SourceObj myprog = P.get_program_object();

SourceObj mymod;

Since our analysis tool is designed to show the test coverage for a particular module only, it next prompts the user to select a module (source file). The user can either enter a source file name, or type !Help and press <Enter> to list the source files for the process. To list the source files, our analysis tool uses the SourceObj::child_count function to determine the number of modules objects under the program object. Using this number to initialize a for loop, the program then assigns each module object in turn to the SourceObj object variable mymod, and identifies the module's name using the SourceObj::module_name function. If the user enters a module name, the analysis tool code uses a similar loop to assign the appropriate module-level source object to the SourceObj object mymod, and then expands the module using the SourceObj::bexpand function. The analysis tool must expand the module because, when the DPCL system connects with a target application process, it retrieves the source hierarchy only down to the module level. Expanding the module with the SourceObj::expand or, in this case, the blocking function SourceObj::bexpand enables our analysis tool to go deeper into the source hierarchy.

    while(found==0)

    {

      cout<<"Enter the source file name to check test coverage."<<endl;

      cout<<"type !Help to list source files"<<endl;

 

      cin>>filename;

 

      if(strcmp(filename,"!Help")==0)

      {

        //  loop through source files to list

        cout<<"Source files:"<<endl;

        int count=myprog.child_count();

        for(int x=0;x<count;x++)

        {

          mymod=myprog.child(x);

          mymod.module_name(name,BUFLEN);

          cout<<x<<" "<<name<<endl;

        }

      } else

       {

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

        {

          mymod = myprog.child(c);

          const char * modname = mymod.module_name(bufmname,bufSize);

 

	  if (strcmp(modname, filename) ==0) 

          {

            printf("Module %s found... expanding\n",filename);

	    AisStatus sts = mymod.bexpand(P);

            if (sts.status() != ASC_success)

            {

              printf("bexpand() was not successful.. %s\n",sts.status_name());

              exit(0);

            }

	    else

	    {

              printf("bexpand() was successful.\n");

            }

            found=1;

	    break;

	  } 

        }

      //}

      if (found == 0)

           cout<<"\""<<filename<<"\" not found.\n";

      }

                         

    }                         //end while  

Now that is has a particular module to work with, our analysis tool can get to its real work -- instrumenting the target application to periodically show the frequency of function calls in the module. Basically, our analysis tool will do this by installing a point probe at the function entry point of each function in the module. Each time execution enters a function the probe will execute, sending a message back to the analysis tool. The probe's data callback will process these incoming messages, keeping track of how many times each of the functions was called.

At 15-second intervals, a SIGALRM signal handler will take coverage information collected by the probe's data callback routine and will print it to standard output. The SIGALRM signal was, as you may recall, one of the signals that the analysis tool added (using the Ais_add_signal function) to the list of signals managed by the DPCL system. When the SIGALRM signal is received, the DPCL system will call the handler specified by the analysis tool when it called the Ais_add_signal function. The other signal that the analysis tool added to the list of those managed by the DPCL system was the SIGINT signal. When the user presses <Control>-c, the DPCL system will detect the signal and will call another signal handler in the analysis tool. This one will print a final report of the information collected by the probe's data callback routine.

That's basically how our analysis tool will instrument the target application; now let's look at the specifics. First of all, the analysis tool needs to create the probe expression that, when installed as a point probe at the various function entry points, will send a message back to the analysis tool. To send data back to the analysis tool, 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 data being sent. In our case, the contents of the message is not important since a tag value passed to the data callback will track which function was called. Since the content of the message is not important, our probe will send the string "xxx". If we were able to hand code this function call into our target application, it would look like this:

Ais_send(Ais_msg_handle, "xxx", 4);

Using the ProbeExp class in 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 DPCL. It is specifically designed for the Ais_send function for managing where the message is sent.

ProbeExp parms[3];

parms[0]=Ais_msg_handle;

parms[1]=ProbeExp("xxx");    //send dummy information to application

parms[2]=ProbeExp(4);

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

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

Now that the analysis tool has created the probe expression, it can install it as point probes at the function entry point for each function within the selected module. To do this, our analysis tool uses a series of nested loops as shown below. First it dives one level deeper into the module's source hierarchy to identify the function-level SourceObj objects within the module-level SourceObj object mymod. It does this by initializing the first for loop to the number of child SourceObj objects in the SourceObj object mymod. To get the number of child SourceObj objects in mymod, the analysis tool calls the SourceObj::child_count function. For each child SourceObj object in mymod, the analysis tool then uses the Source::get_demangled_name function to determine if the SourceObj object represents a function in the source hierarchy. If the SourceObj object does not represent a function, the SourceObj::get_demangled_name function will return 0, and the for loop will ignore this child SourceObj object and continue with its next iteration. If the SourceObj object does represent a function, it uses a for loop to identify the instrumentation point representing the function entry point. The analysis tool uses the SourceObj::exclusive_point_count function to identify the number of instrumentation points in the function-level SourceObj object. It then uses the SourceObj::exclusive_point function to get a reference to a particular instrumentation point for the current iteration of the loop and uses the InstPoint::get_type reference to determine if the instrumentation point represents the function entry site. (The IPT_function_entry enumeration constant of the InstPtType enumeration type indicates that the point is the function entry site.) When the analysis tool identifies a function entry site, it installs and activates its probe expression as a point probe at that instrumentation point using the Process::install_probe and Process::activate_probe functions.

    SourceObj myfun;

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

    

#ifdef DEBUG

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

#endif

 

    fun_count=mymod.child_count();

    fun_arr = new fun_info[fun_count];

    

    printf("\n");

    for ( int c = 0; c < fun_count; c++)

    {

   	myfun = mymod.child(c);

	const char * funname = myfun.get_demangled_name(bufdname,bufSize);

        if (funname == NULL) continue;

        printf("function \"%s\" found\n",funname);

 

        strcpy(fun_arr[fun_num].funname,funname);

        fun_arr[fun_num].pcount=0;

        fun_num++;

 

        InstPoint  mypoint;

 

        for ( int d = 0; d < myfun.exclusive_point_count(); d++)

        {

           mypoint = myfun.exclusive_point(d);

 

          if ( mypoint.get_type() == IPT_function_entry)

          {

 

#ifdef DEBUG

            printf("  Found function entry point.\n");

#endif

 

            ProbeHandle   myph;

            GCBFuncType   mydcb = data_cb;

            GCBTagType    mytg = (GCBTagType) (fun_num-1);

 

            AisStatus sts = P.binstall_probe(1, &mysend, &mypoint,

                          &mydcb, &mytg,

                          &myph);

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

	      printf("  binstall_probe() was not successful.. "

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

              exit(0);

            } else {

              ++num_installed;

 

#ifdef DEBUG  

              printf("  binstall_probe() was successful\n");

#endif

 

            }

 

 

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

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

              printf("  bactivate_probe() was not successful.. %s\n",sts.status_name());

              exit(0);

            } else {

 

#ifdef DEBUG

              printf("  bactivate_probe() was successful\n");

#endif

 

            }

            

            break;

          }

        }

    }

In the preceding example, consider for a moment the fact that this probe expression is potentially going to be installed and activated at multiple function entry points within this same process. When execution reaches the activated probe, it will send its message back to the analysis tool where its data callback routine is triggered. Here the same data callback routine will be triggered for all the installed point probes. The data callback needs some way of knowing which function entry point has been reached. To identify the function that triggers the data callback, our analysis tool code uses the data callback's tag and the program variable fun_num. The data callback can then use this tag value to identify the function that was called. Here's the data callback code; it uses the array fun_arr to keep track of the number of times each function was called.

void

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

{

        Process *p = (Process *)obj;

 

        int     i = (int) tag;

#ifdef DEBUG

        printf("Task %d sent the function number %d\n", p->get_task(),i);

#endif

        fun_arr[i].pcount++;

 

}

Getting back to the main routine, we see that the analysis tool has a few final actions to make before entering the DPCL main loop (using the Ais_main_loop function) to process events asynchronously. It instructs the end user to press <Control>-c to exit, and, if it created one or more processes (using the Process::create or PoeAppl::create function), it now starts them (using the Process::start or Application::start function). It also schedules a SIGALRM signal to be delivered to the analysis tool process after 15 seconds.

    printf("\n*Display interval set at every %d seconds*\n",INTERVAL);

    printf("*Enter <CTRL>-c to exit*\n");

    sleep(3);

 

    if(strcmp(argv[2],"path")==0){

      AisStatus sts=P.bstart();

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

        printf("bstart() was successful\n");

      } else {

        printf("bstart() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    }

 

    if (strcmp(argv[2],"poe_path")==0)

    {

      AisStatus sts=A.bstart();

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

        printf("bstart() was successful\n");

      } else {

        printf("bstart() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    } else if(strcmp(argv[2],"poe_pid")==0){

      AisStatus sts=A.bresume();

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

        printf("bresume() was successful\n");

      } else {

        printf("bresume() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    } else if(strcmp(argv[2],"pid")==0){

      AisStatus sts=P.bresume();

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

        printf("bresume() was successful\n");

      } else {

        printf("bresume() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    }

 

 

    alarm(INTERVAL);

    lflag=1;

    Ais_main_loop();

Once in the Ais_main_loop, the analysis tool process will respond to events asynchronously. As the target process runs and the installed probes execute, they will send data back to the analysis tool to trigger the data callback. Each message will indicate that a function was called, and, as already shown, the callback will use its tag value and an array to keep track of this information. After 15 seconds, the SIGALRM signal is delivered to the analysis tool process; it is detected by the DPCL system which calls the following signal handler (sighandler_ALRM) as specified by the analysis tool when it earlier called the Ais_add_signal function. The signal handler calls the program functions ck_process and print_fun_info. The program function ck_process checks to see if the target application process is still running. The program function print_fun_info prints the coverage information collected by the data callback, and schedules another SIGALRM signal to be delivered to the analysis tool process after another 15 seconds. Here's the signal handler followed by the functions that it calls.

int sighandler_ALRM(int signal){

   ck_process();

 

   print_fun_info();

   sleep(5);

   alarm(15);

   return(0);

}

 

void ck_process(void){

   //check to see if the process was destroyed

 

   ConnectState *state=new ConnectState;

    AisStatus sts = P.query_state(state);

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

      if (*state == PRC_destroyed){

          cout<<"Process terminated.  Exiting...\n";

          write_results();

          exit(0);

      }

    }

 

}

 

void print_fun_info(void)

{

  int num_tested=0;

  if (fun_count == 0){

      printf(" No function found!!\n");

  } else {

     printf("\n** Number of times each function was executed:\n");

     for (int i=0; i< num_installed; i++){

         printf("** %s\t\t%d\n",fun_arr[i].funname,fun_arr[i].pcount);

         if (fun_arr[i].pcount >0) num_tested++;

     }

     printf("\n** Test coverage = %d %%\n",(num_tested*100)/num_installed);

     fflush(stdout);

  }

}

The preceding signal handler and function will be called at 15 second intervals in order to print the coverage information collected by the data callback to standard output. If the end user presses <Control>-c, the DPCL system detects the SIGINT signal which, like the SIGALRM signal, has been added to the list of signals it monitors. In the case of the SIGINT signal, the DPCL system calls the analysis tool's signal handler sighandler. This signal handler breaks the analysis tool out of the DPCL main event loop (using the Ais_end_main_loop function), and writes the final coverage information to a file by calling the program function write_results.

int sighandler(int s){

  if (lflag == 1){

    Ais_end_main_loop();

    write_results(); 

  } else {

    printf("*\"<CTRL>-c\" was entered.  Exiting..\n");

    exit(0);

  } 

  return(0);

}

 

 

void write_results(void){

    FILE *fileout=fopen(FILEOUT,"w");

    int num_tested=0;

    fprintf(fileout,"Source file \"%s\":\n",filename);

    if (fun_count == 0){

      fprintf(fileout," No function found!!\n");

    } else {

      fprintf(fileout,"\nNumber of times each function was executed:\n");

      for (int i=0; i< num_installed; i++){

         fprintf(fileout," %s\t\t%d\n",fun_arr[i].funname,fun_arr[i].pcount);

         if (fun_arr[i].pcount >0) num_tested++;

      }

      fprintf(fileout," Test coverage = %d %%\n",(num_tested*100)/num_installed);

    }

    fclose(fileout);

    printf("*Please check \"%s\" for the final result.\n",FILEOUT);

}

Since the preceding signal handler breaks the analysis tool out of the DPCL main loop, execution of the main routine can now continue past its call to the Ais_main_loop function. The analysis tool will then, since the end user has finished collecting the coverage information, attach to and kill any processes that it created and started. Processes that it merely connected to are allowed to continue running. To kill the single process or multiple processes of the POE application that it started, the analysis tool uses either the Process::battach and Process::bdestroy functions or the Application::battach and Application::bdestroy functions.

    if(strcmp(argv[2],"path")==0){

      AisStatus sts=P.battach();

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

        printf("battach() was successful\n");

      } else {

        printf("battach() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

      sts=P.bdestroy();

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

        printf("bdestroy() was successful\n");

      } else {

        printf("bdestroy() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    }

 

    if (strcmp(argv[2],"poe_path")==0)

    {

      AisStatus sts=A.battach();

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

        printf("battach() was successful\n");

      } else {

        printf("battach() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

      sts=A.bdestroy();

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

        printf("bdestroy() was successful\n");

      } else {

        printf("bdestroy() FAILED.. %s\n",sts.status_name());

        exit(0);

      }

    }

 

 

}


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