Chapter 8, Creating probes describes how to define a probe that can execute as part of a target application process. The manor in which the probe is installed and executed within one or more target application processes distinguishes the probe as a particular probe type -- either a point probe, a phase probe, or a one-shot probe.
Point probes are probes installed at particular locations in the target application code that, when in an activated state, are triggered whenever execution reaches that location in the code. To install and activate a point probe in one or more target application processes, the analysis tool must:
The following steps describe these tasks in greater detail. For sample code, see Example: Installing and activating a point probe.
A probe is a probe expression that may optionally call functions. The first step in installing and activating a point probe is to build the actual probe expression that will serve as the point probe. See Chapter 8, Creating probes for detailed instructions on how to do this.
Instrumentation points (InstPoint objects) are locations within a target application process where an analysis tool can install point probes. Before the analysis tool can install a point probe in a target application process, it must get a reference to the InstPoint object where it wishes to place the probe. To get such a reference, the analysis tool must navigate the target application's source structure by means of source objects (SourceObj objects). Each SourceObj object represents part of the source code structure associated with the target application process, and a group of such objects provide the hierarchical representation of the source code structure which the analysis tool can navigate. For an overview of instrumentation points, see What are instrumentation points?. For an overview of source objects, see What are source objects?.
To navigate an application's source structure to get an instrumentation point, the analysis tool must:
Note: | For reusable, general-purpose, analysis tools, it is helpful to provide the end user with some way of searching the target application for functions that match a user-supplied search string. The analysis tool can provide this functionality using the SourceObj::bget_function_list function, the SourceObj::get_function_list function, and member functions of the FunctionList and FunctionId classes. After learning the information that follows, refer to Chapter 15, Searching for, and listing, functions in the target application for information on providing a function-searching capability to the analysis tool's end user. |
If the analysis tool is instrumenting a serial application, it doesn't need to perform this step, and you can skip ahead to Step 2b: Get program object. If the analysis tool is instrumenting a parallel application, however, it must first get a reference to a single process (Process object) that is managed by the Application class object. This is because a source hierarchy is associated with a particular process only. If multiple processes in the parallel application were compiled from the same source code (such as a SPMD program), the analysis tool can later use member functions of the Application class to install, activate, and remove the point probe for all the Process objects managed by the Application. To navigate the source hierarchy, however, the analysis tool must identify a single representative Process in the Application. To do this, the analysis tool uses the Application::get_process function. The following line of code, for example, returns the first Process object in the Application object app1.
Process p = app1.get_process(0);
For more information on the Application::get_process function, refer to its UNIX man page, or its entry in the DPCL Class Reference.
To navigate the source code structure associated with a particular process, the analysis tool first needs to get a reference to the source object (SourceObj object) that represents the top of the process source hierarchy. This top level SourceObj object is called the "program object" and is returned by the Process::get_program_object function. The following line of code stores the program object in a new SourceObj object named myprog.
SourceObj myprog = P.get_program_object();
For more information on the Application::get_program_object function, refer to its UNIX man page, or its entry in the DPCL Class Reference.
Once the analysis tool has a reference to the program object, it needs to navigate one level down into the source hierarchy to get a reference to the SourceObj object that represents the module where the analysis tool will install the point probe. To do this, the analysis tool can use the SourceObj::child_count, SourceObj::child, and SourceObj::module_name functions
The SourceObj::child_count function returns the number of child SourceObj objects associated with the SourceObj object; in the case of a program object, these child SourceObj objects represent the program modules. The SourceObj::child function returns a specific child SourceObj. Once it has a reference to the module level SourceObj object, the analysis tool can use the SourceObj::module_name function to identify the name of the module.
This code example 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. This sample code assumes a specialized analysis tool designed for a particular program, and so the name of the target module is already known. For a general-purpose analysis tool, where the target application is not known at design time, these same functions could be used, for example, to populate a scrolled list to show the module names. Here's the specialized analysis tool example that identifies hello.c as the target module.
for (int c = 0; c < myprog.child_count(); c++) { mymod = myprog.child(c); if (strcmp(mymod.module_name(bufmname, bufSize), "hello.c") ==0) { // CODE HERE } }
For more information on the SourceObj::child_count, SourceObj::child, and SourceObj::module_name functions, refer to their UNIX man pages, or their entries in the DPCL Class Reference.
Once the analysis tool identifies the target module source object, it must
expand it in order to navigate further down into the source hierarchy.
To do this, the analysis tool can use either the blocking function
SourceObj::bexpand, or the asynchronous function
SourceObj::expand.
Table 47. Expanding a module-level source object
To use: | For example: |
The blocking function SourceObj::bexpand |
for (int c = 0; c < myprog.child_count(); c++) { mymod = myprog.child(c); if (strcmp(mymod.module_name(bufmname, bufSize), "hello.c") ==0) { printf ("found module hello.c. expanding....\n"); sts = mymod.bexpand(P); printf ("expand status module # %d = %d\n", c, (int)sts); if ( (int)sts != 0) printf("%s\n", sts.status_name()); break; } } |
The asynchronous function SourceObj::expand |
void expand_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg); // ... for (c = 0; c < myprog.child_count(); c++) { mymod = myprog.child(c); name = mymod.module_name(buffer, bufsize); if (strcmp(name, "integral_c.c") == 0) { // found the target module, expand it async_done = 0; sts = mymod.expand(P, (GCBFuncType)expand_cb, (GCBTagType)2); printf("expand is submitted, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); break; } } // ... void expand_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg) { AisStatus *sts = (AisStatus *)msg; printf("expand is completed, status = %d\n", (int)*sts); async_done = 1; Ais_end_main_loop(); } |
For more information on the SourceObj::bexpand and SourceObj::expand functions, refer to their UNIX man pages, or their entries in the DPCL Class Reference
Once the analysis tool has expanded the target module, it can navigate one more level down into the source hierarchy to get a reference to the SourceObj that represents the function where the analysis tool will install the point probe. Once again, the analysis tool can navigate down into the hierarchy using the SourceObj::child_count and SourceObj::child functions. The SourceObj::child_count function returns the number of child SourceObj objects associated with the SourceObj object; in the case of a module source object, these child SourceObj objects represent the module's functions. The SourceObj::child function returns a specific child SourceObj object. Once it has a reference to the function level SourceObj object, the analysis tool can use the SourceObj::get_demangled_name function (to identify the demangled name of the function) or the SourceObj::get_mangled_name function (to identify the mangled name of the function).
This code example uses the SourceObj::child_count function to initialize a for loop, and then, within the loop, uses SourceObj::child and SourceObj::get_demangled_name functions to identify the target function. This sample code assumes a specialized analysis tool designed for a particular program, and so the name of the target function is already known. For a general purpose analysis tool, where the target application is not known at design time, these same functions could be used, for example, to populate a scrolled list to show the function names. Here's the specialized example that identifies the module hello as the target function.
SourceObj myfun; char bufdname[bufSize]; // buffer for get_demangled_name(..) printf("function count = %d\n", mymod.child_count()); for ( c = 0; c < mymod.child_count(); c++) { myfun = mymod.child(c); char *name = myfun.get_demangled_name(bufdname, bufSize); if (strcmp(name, "hello") ==0) { printf("function hello found.\n"); break; } }
For more information on the SourceObj::child_count, SourceObj::child, SourceObj::get_demangled_name, and SourceObj::get_mangled_name functions, refer to their UNIX man pages or their entries in the DPCL Class Reference.
Once the analysis tool identifies the target function source object, it can identify the actual instrumentation point (InstPoint object) where it will install the point probe. To do this, the analysis tool can use the SourceObj::exclusive_point_count, SourceObj::exclusive_point, and InstPoint::get_type functions. The SourceObj::exclusive_point_count function returns the number of instrumentation points in the function, and the SourceObj::exclusive_point function returns a specific InstPoint object. The InstPoint::get_type function enables the analysis tool to determine whether the instrumentation point represents a function entry, function exit, or function call site.
This code example uses the SourceObj::exclusive_point_count function to initialize a for loop, and then, within the for loop, uses the SourceObj::exclusive_point and InstPoint::get_type functions to identify the function entry site.
InstPoint mypoint; printf("point count = %d\n", myfun.exclusive_point_count()); for ( c = 0; c < myfun.exclusive_point_count(); c++) { mypoint = myfun.exclusive_point(c); if ( mypoint.get_type() == IPT_function_entry) { printf("function entry point found.\n"); break; } }
For more information on the SourceObj::exclusive_point_count, SourceObj::exclusive_point, and InstPoint::get_type functions, refer to their UNIX man pages or their entries in the DPCL Class Reference.
Once the analysis tool has defined its probes (as described in Chapter 8, Creating probes) and identified an instrumentation point (as described in Step 2: Navigate application source structure to get instrumentation point), it can install the probe at the
instrumentation point. To do this on a single process basis, the
analysis tool can use the asynchronous function
Process::install_probe or its blocking equivalent
Process::binstall_probe. To do this on an
application-wide basis, the analysis tool can use the functions
Application::install_probe or
Application::binstall_probe. Note that all of
these functions accept an array of probes. This means that the analysis
tool can install multiple point probes using a single call.
Table 48. Installing a point probe in one or more target application processes
To install a probe: | In a single process (Process object) | In multiple processes (all of the Process objects managed by an Application object) |
---|---|---|
Using the asynchronous function install_probe |
void install_probe_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg); // ... // install the probe in function entry point points[0] = mypoint; cbs[0] = data_cb; tags[0] = (GCBTagType)1; async_done = 0; sts = P.install_probe(1, &fullexp, points, cbs, tags, (GCBFuncType)install_probe_cb, (GCBTagType)3, phds); printf("install_probe is submitted, \ status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // ... void install_probe_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg) { Process *p = (Process *)obj; AisStatus *sts = (AisStatus *)msg; printf("task %d install_probe is \ completed, status = %d\n", p->get_task(), (int)*sts); async_done = 1; Ais_end_main_loop(); } |
void install_probe_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg); // ... // install the probe in function entry point points[0] = mypoint; cbs[0] = data_cb; tags[0] = (GCBTagType)1; async_done = 0; sts = A.install_probe(1, &fullexp, points, cbs, tags, (GCBFuncType)install_probe_cb, (GCBTagType)3, phds); printf("install_probe is submitted, \ status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // ... void install_probe_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg) { static int count = 0; Process *p = (Process *)obj; AisStatus *sts = (AisStatus *)msg; printf("task %d install_probe is \ completed, status = %d\n", p->get_task(), (int)*sts); count++; if (count >= num_procs) { async_done = 1; Ais_end_main_loop(); } } |
Using the blocking function binstall_probe |
// install the probe in function entry point points[0] = mypoint; cbs[0] = data_cb; tags[0] = (GCBTagType)1; sts = P.binstall_probe(1, &fullexp, points, cbs, tags, phds); printf("install_probe is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); |
// install the probe in function entry point points[0] = mypoint; cbs[0] = data_cb; tags[0] = (GCBTagType)1; sts = A.binstall_probe(1, &fullexp, points, cbs, tags, phds); printf("install_probe is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); |
For more information on the Process::install_probe, Process::binstall_probe, Application::install_probe, or Application::binstall_probe, refer to their UNIX man pages or their entries in the DPCL Class Reference
Once you have installed a point probe in one or more target application
processes, you must activate it so that it will execute as part of the target
application process whenever execution reaches its installed location in the
target application code. To do this on a single process basis, the
analysis tool can use the asynchronous function
Process::activate_probe or its blocking equivalent
Process::bactivate_probe. To do this on an
application-wide basis (for all Process objects managed by an
Application object), the analysis tool can use the function
Application::activate_probe or
Application::bactivate_probe. Note that all of
these functions accept an array of probe handles. This means that the
analysis tool can activate more than one point probe using a single
call.
Table 49. Activating a point probe in one or more target application processes
To activate a probe: | In a single process (Process object) | In multiple processes (all of the Process objects managed by an Application object) |
---|---|---|
Using the asynchronous function activate_probe |
void activate_probe_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg); // ... // activate the probe async_done = 0; sts = P.activate_probe(1, phds, (GCBFuncType)activate_probe_cb, (GCBTagType)4); printf("activate_probe is submitted, \ status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // ... void activate_probe_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg) { Process *p = (Process *)obj; AisStatus *sts = (AisStatus *)msg; async_done = 1; printf("task %d activate_probe is \ completed, status = %d\n", p->get_task(), (int)*sts); Ais_end_main_loop(); } |
void activate_probe_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg); // ... // activate the probe async_done = 0; sts = A.activate_probe(1, phds, (GCBFuncType)activate_probe_cb, (GCBTagType)4); printf("activate_probe is submitted, \ status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // ... void activate_probe_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg) { static int count = 0; Process *p = (Process *)obj; AisStatus *sts = (AisStatus *)msg; printf("task %d activate_probe is \ completed, status = %d\n", p->get_task(), (int)*sts); count++; if (count >= num_procs) { async_done = 1; Ais_end_main_loop(); } } |
Using the blocking function bactivate_probe |
// activate the probe sts = P.bactivate_probe(1, phds); printf("activate_probe is done, \ status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // enter main loop Ais_main_loop(); printf("----- The End -----\n"); |
// activate the probe sts = A.bactivate_probe(1, phds); printf("activate_probe is done, \ status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // enter main loop Ais_main_loop(); printf("----- The End -----\n"); |
For more information on the Process::activate_probe, Process::bactivate_probe, Application::activate_probe, or Application::bactivate_probe functions, refer to their UNIX man pages or their entries in the DPCL Class Reference.
The following example code:
#include <dpcl.h> void data_cb (GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg); // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% main(int argc, char *argv[]) { PoeAppl A; // A represents a POE application Ais_initialize(); AisStatus sts = A.binit_procs(argv[1], atoi(argv[2])); printf ("read config status = %d\n", (int)sts); sts = A.bconnect(); printf ("connect status = %d\n", (int)sts); Process P = A.get_process(0); SourceObj myprog = P.get_program_object(); SourceObj mymod; const int bufSize = 128; char bufmname[bufSize]; // buffer for module_name(..) for (int c = 0; c < myprog.child_count(); c++) { mymod = myprog.child(c); char * modname = mymod.module_name(bufmname,bufSize); if (strcmp(modname, "chaotic.f") ==0) { printf("Module chaotic.f found... expanding\n"); sts = mymod.bexpand(P); printf ("expand status = %d\n", (int)sts); break; } } SourceObj myfun; char bufdname[bufSize]; // buffer for get_demangled_name(..) for ( c = 0; c < mymod.child_count(); c++) { myfun = mymod.child(c); char * funname = myfun.get_demangled_name(bufdname,bufSize); if (strcmp(funname, "exchange") ==0) { printf("function exchange found... \n"); break; } } InstPoint mypoint; for ( c = 0; c < myfun.exclusive_point_count(); c++) { mypoint = myfun.exclusive_point(c); if ( mypoint.get_type() == IPT_function_entry) { printf("Found function entry point\n"); break; } } // malloc pcount; int val = 0; ProbeExp pcount = A.balloc_mem( int32_type(), &val, sts); printf ("malloc pcount status = %d\n", (int)sts); ProbeExp addexp = pcount.assign(pcount + ProbeExp(1)); // Create the expression parameters ProbeExp parms[3]; parms[0] = Ais_msg_handle; parms[1] = pcount.address(); parms[2] = ProbeExp (4); ProbeExp sendexp = Ais_send.call(3,parms); ProbeExp fullexp = addexp.sequence(sendexp); ProbeHandle myph; GCBFuncType cbs[] = {data_cb}; GCBTagType tags[] = {(GCBTagType)0}; sts = A.binstall_probe(1, &fullexp, &mypoint, cbs, tags, &myph); printf ("install status = %d\n",(int)sts); sts = A.bactivate_probe(1, &myph); printf ("activate status = %d\n", (int)sts); Ais_main_loop(); } // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void data_cb(GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg) { Process *p = (Process *)obj; int *i = (int *)msg; printf("Task %d sent the count %d\n", p->get_task(),*i); // terminate the program after 10 messages are received if (*i == 10) { Ais_end_main_loop(); } } // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Phase probes are probes that are executed periodically upon execution of a timer, regardless of what part of the target application's code is executing. The interval at which the probes are executed is measured in CPU time (as opposed to wall-clock time), and is called the phase period. The control mechanism for invoking phase probes at these set CPU-time intervals is called a "phase". Represented by instances of the Phase class, phases enable your analysis tool code to specify the particular phase probe(s) to be invoked and the interval at which their execution is triggered. Note that the DPCL system uses a SIGPROF signal to activate a phase; target applications that themselves use the SIGPROF signal cannot be instrumented with phases. For more information on phase probes and phases, refer to What is a phase probe?.
To add a phase to one or more target application processes in order to have it call its associated phase probes (probe module functions) at set CPU-time intervals, the analysis tool:
The first step in creating a phase structure to execute phase probes is to create one or more probe modules that contain the functions to be triggered by the phase. A phase can, each time its interval expires, call up to three probe module functions -- a begin function, a data function, and an end function. While the phase must, in order to be useful, call at least one of these functions, any one of them is optional. At the very least, an analysis tool will usually supply a data function.
Once the phase is activated, it will call the phase probe module functions that have been associated with it. The first phase probe it calls is the one identifying the begin function (provided one has been specified). Typically, the begin function will perform any setup tasks that may be required. When the begin function completes, the phase calls the phase probe that identifies the data function (provided one has been specified). The data function executes once per datum that the analysis tool will have previously allocated and associated with this phase. When the data function finishes executing for the last datum, the phase calls the phase probe that identifies the end function (provided one has been specified). Typically, the end function performs any clean up chores that may be required.
In addition to these probes module functions that are executed each time the phase is activated, the analysis tool can also create:
When the phase exit functions are triggered, the first phase probe it calls is the one identifying the phase exit begin function (provided one has been specified). Like the phase begin function, the phase exit begin function will perform any setup tasks that may be required for the data and end functions to follow. When the phase exit begin function completes, the DPCL system executes the phase probe that identifies the phase exit data function (provided one has been specified). The phase exit data function, like the phase data function, executes once per datum that the analysis tool will have previously allocated and associated with this phase. When the phase exit data function finishes executing for the last datum, the DPCL system calls the phase probe that identifies the phase exit end function (provided one has been specified). Typically, the end function performs any final clean up chores that may be required. Like the phase functions, however, any of these phase exit functions is optional.
The following example code shows a probe module that defines the phase begin function, the phase data function, and the phase end function. It also defines an initialization function to be executed when the phase is first added to a target application process. In order to execute this module's functions in one or more target application process, the module will need to be compiled and then loaded into the target application process. For more information on how to do this, refer to Creating and calling probe module functions.
#include <stdio.h> #include <sys/time.h> #include <sys/param.h> #include <dpclExt.h> static int visit = 0; static char msg[MAXPATHLEN]; static char msg_loc[MAXPATHLEN]; static char msg_time[MAXPATHLEN]; static int msg_size; void init_func(void *handle) { sprintf(msg, "init_func() started\n"); msg_size = strlen(msg) + 1; Ais_send(handle,msg,msg_size) ; } void begin_func(void *handle) { int rc; sprintf(msg, "begin_func() invoked"); msg_size = strlen(msg) + 1; if ((rc = Ais_send(handle, msg, msg_size)) != 0) { printf("begin_func(): ERROR, Ais_send()=%d\n", rc); } printf("begin_func() called\n"); } void data_func(void *handle, void *_data) { int rc; int *data = (int *)_data; *data = *data + 1; sprintf(msg, "data_func(): data = %d", *data); msg_size = strlen(msg) + 1; if ((rc = Ais_send(handle, msg, msg_size)) != 0) { printf("data_func(): ERROR, Ais_send()=%d\n", rc); } printf("data_func() called\n"); } void end_func(void *handle) { int rc; sprintf(msg, "end_func() invoked\n"); msg_size = strlen(msg) + 1; if ((rc = Ais_send(handle, msg, msg_size)) != 0) { printf("end_func(): ERROR, Ais_send()=%d\n", rc); } printf("end_func() called\n"); }
When creating an instance of the Phase class (as described next in Step 3: Create phase), the analysis tool must, for each phase probe (module function) that the Phase will trigger, create a probe expression that represents a reference to the function. In order to have loaded the probe module into the target application process(es), the analysis tool will have already created a ProbeModule class object to represent the probe module. (For more information, see Creating and calling probe module functions.) To create a probe expression that represents a reference to one of the probe module's functions, the analysis tool can call the Probe_Module::get_reference function. The analysis tool code provides this function with the index of the function within the probe module. To determine the number of functions in the module, the analysis tool can call the ProbeModule::get_count function. To determine the name of a particular function, the analysis tool can call the ProbeModule::get_name function.
This following code example uses the ProbeModule::get_count, ProbeModule::get_name, and ProbeModule::get_reference functions to create probe expressions that represent references to all the probe module functions shown in the example code in Step 1: Create probe module(s). The phase begin function (begin_func), the phase data function (data_func) and the phase end function (end_func) will all be used when instantiating the Phase class in the next step. The initialization function (init_func) is the one to be executed only when the phase is first added to a target application process; the probe expression that represents a reference to this function will be used as a function parameter to the Process::add_phase, Process::badd_phase, Application::add_phase, or Application::badd_phase. These functions are described in Step 4: Add phase to the target application process(es).
// look for functions in the loaded module // that are to be used in phase for (int j = 0; j < load_mod.get_count(); j++) { if (strcmp("init_func", load_mod.get_name(j, buffer, sizeof(buffer))) == 0) { init_func = load_mod.get_reference(j); } else if (strcmp("begin_func", load_mod.get_name(j, buffer, sizeof(buffer))) == 0) { begin_func = load_mod.get_reference(j); } else if (strcmp("data_func", load_mod.get_name(j, buffer, sizeof(buffer))) == 0) { data_func = load_mod.get_reference(j); } else if (strcmp("end_func", load_mod.get_name(j, buffer, sizeof(buffer))) == 0) { end_func = load_mod.get_reference(j); } }
For more information on the ProbeModule::get_count, ProbeModule::get_name, and ProbeModule::get_reference functions, refer to their UNIX man pages or their entries in the DPCL Class Reference.
A phase structure (Phase class object) defines the phase probe(s) (probe module function(s)) to be executed and the interval between successive invocations of these probes. The Phase class is defined in the header file Phase.h. This following example code creates a phase object that specifies the phase should be activated every second of CPU time to execute the phase begin function (begin_func), the phase data function (data_func) and the phase end function (end_func).
// create a phase with one second period float period = 1.0; myphase = Phase(period, begin_func, (GCBFuncType)data_cb, (GCBTagType)1, data_func, (GCBFuncType)data_cb, (GCBTagType)2, end_func, (GCBFuncType)data_cb, (GCBTagType)3);
Although the phase period (the CPU-time interval at which the phase is activated to execute its probes) is initially set when the analysis tool defines the phase, note that the analysis tool can later lengthen or shorted this interval as desired. Refer to Step 7: Modify phase period for more information.
For more information on the Phase class and its constructors, refer to the DPCL Class Reference.
Once the analysis tool has created the Phase object (as described in Step 3: Create phase), it can add it to one or more target application processes. To add a phase on a single process basis, the analysis tool can use the asynchronous function Process::add_phase or its blocking equivalent Process::badd_phase. To add a phase on an application-wide basis (for all Process class objects managed by an Application object), the analysis tool can use the functions Application::add_phase or Application::badd_phase. Optional parameters of these four functions enable you to specify an initialization function to be executed when the phase is added to a target application process, as well as a data callback routine and callback tag for handling message data generated by the initialization function. The following code example uses the Process::badd_phase function to add the phase we created in Step 3: Create phase to a single target application process. The initialization function shown in the probe module in Step 1: Create probe module(s) will execute when the Phase is added to the process. The probe expression init_func represents a reference to the probe module function; we created this probe expression in Step 5: Create probe expression(s) to allocate and associate data with the phase.
// add the phase to application sts = P.badd_phase(myphase, init_func, (GCBFuncType)data_cb, (GCBTagType)4); if (sts.status()==ASC_success){ printf("badd_phase() was successful\n"); } else { printf("badd_phase() FAILED.. %s\n",sts.status_name()); exit(0); }
For more information on the Process::add_phase, Process::badd_phase, Application::add_phase, or Application::badd_phase functions, refer to their UNIX man pages or their entries in the DPCL Class Reference.
If using a phase data function, the analysis tool code will need to create a probe expression to allocate and associate data with the phase. Each time the phase is triggered, the data function executes once per datum that the analysis tool has previously allocated and associated with the phase. Executing once per datum enables the data function to perform the same actions on the different data. Each datum, for example, could be a separate counter -- each incremented by the same data function. If the analysis tool does not associate any data with the phase, then the data function will not execute.
Creating probe expressions to represent persistent data describes how you can use the Process::alloc_mem, Process::balloc_mem, Application::alloc_mem, and Application::balloc_mem to allocated memory in target application processes. What this earlier section did not state, however, is the an optional parameter of these functions enables the analysis tool to associate the data allocated in the process(es) with a particular phase. In this following example of the Process::balloc_mem function, the probe expression phase_da is created to represent a persistent integer variable with the initial value of 1000. This probe expression also associates the allocated data with the Phase object myphase created in Step 3: Create phase. Each time this phase is activated, this data value will be passed to the phase data function data_func. Since there is only this one datum associated with the phase, the phase data function will execute only once each time the phase is activated.
// create variable to be used in the phase int init_value = 1000; ProbeExp phase_da = P.balloc_mem(int32_type(), (void *)&init_value, myphase, sts); if (sts.status()==ASC_success){ printf("balloc_mem() was successful\n"); } else { printf("balloc_mem() FAILED.. %s\n",sts.status_name()); exit(0); }
For more information on the Process::alloc_mem, Process::balloc_mem, Application::alloc_mem, and Application::balloc_mem functions, refer to their UNIX man pages or their entries in the DPCL Class Reference.
If the analysis tool code created one or more phase exit functions in Step 1: Create probe module(s), it needs to associate them with the Phase on one or more target application processes. To do this, the analysis tool code calls the Process::set_phase_exit, Process::bset_phase_exit, Application::set_phase_exit, or Application::bset_phase_exit function. The analysis tool code must supply these functions with probe expressions that represent references to the actual probe module functions. The analysis tool code must, therefore, have already created probe expressions for the exit functions as described in Step 5: Create probe expression(s) to allocate and associate data with the phase. The set_phase_exit and bset_phase_exit functions also allow the analysis tool to specify a callback routine and callback tag for each of the phase exit functions. The following code uses the Process::bset_phase_exit function call to associate a phase exit begin function, a phase exit data function, and a phase exit end function with the Phase object phase1 on one particular target application process. The three phase exit functions are represented by the probe expressions exit_begin_func, exit_data_func, and exit_end_func previously created by calling the ProbeModule::get_reference. The procedure for creating a probe expression using the ProbeModule::get_reference function is described in Step 5: Create probe expression(s) to allocate and associate data with the phase.
sts = P.bset_phase_exit(myphase, exit_begin_func, (GCBFuncType)msg_cb, (GCBTagType)6, exit_data_func, (GCBFuncType)msg_cb, (GCBTagType)7, exit_end_func, (GCBFuncType)msg_cb, (GCBTagType)8);
For more information on the Process::set_phase_exit, Process::bset_phase_exit, Application::set_phase_exit, or Application::bset_phase_exit functions, refer to their UNIX man pages or their entries in the DPCL Class Reference.
A phase period specifies the interval of CPU time at which a phase is activated. When the phase is activated, it then executes its phase probes (phase begin, phase data, and phase end functions). The analysis tool initially sets the phase period when defining the Phase class object as described in Step 3: Create phase. The analysis tool can also, once the Phase class object has been added to one or more target application processes, modify the phase period so that the phase is activated at longer or shorter intervals of CPU time. The analysis tool can reset a phase period within a single target application process by calling the Process::set_phase_period function or its blocking equivalent -- Process::bset_phase_period. The analysis tool can also reset a phase period on an application-wide basis (for each Process object managed by an Application object) by calling the Application::set_phase_period or Application::bset_phase_period function.
One use of the Process::set_phase_period, Process::bset_phase_period, Application::set_phase_period, and Application::bset_phase_period functions is to control when a phase will first execute. For example, say you do not want a phase to be activated until after the analysis tool has allocated and associated data with the phase. Unfortunately, you have to add the phase to one or more processes (as described in Step 4: Add phase to the target application process(es)) before you can allocate data for it (as described in Step 5: Create probe expression(s) to allocate and associate data with the phase). In the example code below, the analysis tool adds a phase whose phase period is 999.0 to a particular process. The long phase period effectively places the phase in a suspended state while the analysis tool allocates and associates data with the phase. Once the data allocation step is complete, the analysis tool calls the Process::set_phase_period function to set the phase period to the intended CPU-time interval of 0.1 seconds
// create a phase object with a large period // to effectively suspend its execution period = 999.0; try { phase1 = Phase(period, begin_func, (GCBFuncType)msg_cb, (GCBTagType)1, data_func, (GCBFuncType)msg_cb, (GCBTagType)2, end_func, (GCBFuncType)msg_cb, (GCBTagType)3); } catch (AisStatus excp) { printf("new Phase failed with sts=%s\n", excp.status_name()); exit(1); } // add the phase to target application sts = P.badd_phase(phase1, init_func, (GCBFuncType)msg_cb, (GCBTagType)4); printf("add_phase is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // set the phase's exit functions sts = P.bset_phase_exit(phase1, exit_begin_func, (GCBFuncType)msg_cb, (GCBTagType)6, exit_data_func, (GCBFuncType)msg_cb, (GCBTagType)7, exit_end_func, (GCBFuncType)msg_cb, (GCBTagType)8); printf("set_phase_exit is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // allocate data variables for the phase value = 0; pcount = P.balloc_mem(int32_type(), &value, phase1, sts); printf("alloc_mem is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); value = 100; pcount2 = P.balloc_mem(int32_type(), &value, phase1, sts); printf("alloc_mem is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // set the phase to its true execution interval // to resume its normal operation period = 0.1; sts = P.bset_phase_period(phase1, period); printf("set_phase_period is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // query the phase's execution interval interval = P.get_phase_period(phase1, sts); printf("phase period = %f\n", interval);
In addition to being able to set a phase period, the analysis tool can also ascertain the value of a phase period by calling the Process::get_phase_period function. For more information on the Process::set_phase_period, Process::bset_phase_period, Application::set_phase_period, Application::bset_phase_period, or Process::get_phase_period functions, refer to their UNIX man pages, or their entries in the DPCL Class Reference.
The following example code:
#include <stdio.h> #include <stdlib.h> #include <dpcl.h> void msg_cb(GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg); void main(int argc, char *argv[]) { Process P; AisStatus sts; int c; int value; ProbeExp pcount; ProbeExp pcount2; ProbeExp init_func; ProbeExp begin_func; ProbeExp data_func; ProbeExp end_func; ProbeExp exit_begin_func; ProbeExp exit_data_func; ProbeExp exit_end_func; ProbeModule load_1; float period; float interval; Phase phase1; const int bufsize = 256; char buffer[bufsize]; char *name; // initialize DPCL environment Ais_initialize(); // construct a valid Process object P = Process(argv[1], atoi(argv[2])); // connect to the target application sts = P.bconnect(); printf("connect is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // load probe module load_1 = ProbeModule("./load_1"); sts = P.bload_module(&load_1); printf("load_module is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // look for all the phase related functions for (c = 0;c < load_1.get_count(); c++) { name = load_1.get_name(c, buffer, bufsize); if (strcmp("init_func", name) == 0) { init_func = load_1.get_reference(c); printf("found init_func\n"); } else if (strcmp("begin_func", name) == 0) { begin_func = load_1.get_reference(c); printf("found begin_func\n"); } else if (strcmp("data_func", name) == 0) { data_func = load_1.get_reference(c); printf("found data_func\n"); } else if (strcmp("end_func", name) == 0) { end_func = load_1.get_reference(c); printf("found end_func\n"); } else if (strcmp("exit_begin_func", name) == 0) { exit_begin_func = load_1.get_reference(c); printf("found exit_begin_func\n"); } else if (strcmp("exit_data_func", name) == 0) { exit_data_func = load_1.get_reference(c); printf("found exit_data_func\n"); } else if (strcmp("exit_end_func", name) == 0) { exit_end_func = load_1.get_reference(c); printf("found exit_end_func\n"); } } // create a phase object with a large period // to effectively suspend its execution period = 999.0; try { phase1 = Phase(period, begin_func, (GCBFuncType)msg_cb, (GCBTagType)1, data_func, (GCBFuncType)msg_cb, (GCBTagType)2, end_func, (GCBFuncType)msg_cb, (GCBTagType)3); } catch (AisStatus excp) { printf("new Phase failed with sts=%s\n", excp.status_name()); exit(1); } // add the phase to target application sts = P.badd_phase(phase1, init_func, (GCBFuncType)msg_cb, (GCBTagType)4); printf("add_phase is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // set the phase's exit functions sts = P.bset_phase_exit(phase1, exit_begin_func, (GCBFuncType)msg_cb, (GCBTagType)6, exit_data_func, (GCBFuncType)msg_cb, (GCBTagType)7, exit_end_func, (GCBFuncType)msg_cb, (GCBTagType)8); printf("set_phase_exit is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // allocate data variables for the phase value = 0; pcount = P.balloc_mem(int32_type(), &value, phase1, sts); printf("alloc_mem is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); value = 100; pcount2 = P.balloc_mem(int32_type(), &value, phase1, sts); printf("alloc_mem is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // set the phase to its true execution interval // to resume its normal operation period = 0.1; sts = P.bset_phase_period(phase1, period); printf("set_phase_period is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // query the phase's execution interval interval = P.get_phase_period(phase1, sts); printf("phase period = %f\n", interval); // remove the phase from target application sts = P.bremove_phase(phase1); printf("remove_phase is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); //Ais_main_loop(); printf("----- The End -----\n"); } void msg_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg) { static int count = 0; count++; char *chp = (char *)msg; printf("msg_cb received the msg(%d)=\"", count); for (int i = 0; i < sys.msg_size; ++i) { printf("%c", chp[i]); } printf("\"\n"); }
A one-shot probe is a type of probe that is executed by the DPCL system immediately upon request, regardless of what the application happens to be doing. To execute a one-shot probe in one or more target application processes, the analysis tool must:
A probe is a probe expression that may optionally call functions. The first step in executing a one-shot probe is to build the actual probe expression that will serve as the one-shot probe. Since a one-shot probe is executed immediately upon request, regardless of what the target application happens to be doing, your probe expression should be "signal safe".
For detailed instructions on creating probe expressions, refer to Chapter 8, Creating probes.
Once the analysis tool has defined the probe expression that will serve as
the one-shot probe, it can execute it in one or more target application
processes. To execute a one-shot probe in a single process, the
analysis tool can use the asynchronous function
Process::execute or its blocking equivalent
Process::bexecute. To execute a one-shot probe
on an application-wide basis (for all Process objects managed by an
Application object), the analysis tool can use the function
Application::execute or
Application::bexecute.
Table 50. Executing a one-shot probe in one or more target application processes
To execute a one-shot probe: | In a single process (Process object) | In multiple processes (all of the Process objects managed by an Application object) |
---|---|---|
Using the asynchronous function execute_probe |
void execute_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg); // ... addexp = pcount.assign(pcount + ProbeExp(1)); // create a probe expression to send the result // back to DPCL program parms[0] = Ais_msg_handle; parms[1] = pcount.address(); parms[2] = ProbeExp(4); sendexp = Ais_send.call(3, parms); fullexp = addexp.sequence(sendexp); // issue an one-shot probe sts = P.execute(fullexp, (GCBFuncType)data_cb, (GCBTagType)1, (GCBFuncType)execute_cb, (GCBTagType)2); printf("execute is submitted, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // enter main loop Ais_main_loop(); printf("----- The End -----\n"); // ... void execute_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg) { Process *p = (Process *)obj; AisStatus *sts = (AisStatus *)msg; printf("task %d execute is completed, \ status = %d\n", p->get_task(), (int)*sts); Ais_end_main_loop(); } |
void execute_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg); // ... addexp = pcount.assign(pcount + ProbeExp(1)); // create a probe expression to send the result // back to DPCL program parms[0] = Ais_msg_handle; parms[1] = pcount.address(); parms[2] = ProbeExp(4); sendexp = Ais_send.call(3, parms); fullexp = addexp.sequence(sendexp); // issue an one-shot probe sts = A.execute(fullexp, (GCBFuncType)data_cb, (GCBTagType)1, (GCBFuncType)execute_cb, (GCBTagType)2); printf("execute is submitted, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // enter main loop Ais_main_loop(); printf("----- The End -----\n"); // ... void execute_cb(GCBSysType sys, GCBTagType tag, GCBTagType obj, GCBMsgType msg) { static int count = 0; Process *p = (Process *)obj; AisStatus *sts = (AisStatus *)msg; count++; printf("task %d execute is completed, \ status = %d\n", p->get_task(), (int)*sts); if (count >= num_procs) { Ais_end_main_loop(); } } |
Using the blocking function bexecute_probe |
// create an assignment statement addexp = pcount.assign(pcount + ProbeExp(1)); // create a probe expression to send the result // back to DPCL program parms[0] = Ais_msg_handle; parms[1] = pcount.address(); parms[2] = ProbeExp(4); sendexp = Ais_send.call(3, parms); fullexp = addexp.sequence(sendexp); // issue an one-shot probe sts = P.bexecute(fullexp, (GCBFuncType)data_cb, (GCBTagType)1); printf("execute is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); printf("----- The End -----\n"); |
// create an assignment statement addexp = pcount.assign(pcount + ProbeExp(1)); // create a probe expression to send the result // back to DPCL program parms[0] = Ais_msg_handle; parms[1] = pcount.address(); parms[2] = ProbeExp(4); sendexp = Ais_send.call(3, parms); fullexp = addexp.sequence(sendexp); // issue an one-shot probe sts = A.bexecute(fullexp, (GCBFuncType)data_cb, (GCBTagType)1); printf("execute is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); printf("----- The End -----\n"); |
For more information on the Process::execute, Process::bexecute, Application::execute, and Application::bexecute functions, refer to their UNIX man pages, or their entries in the DPCL Class Reference.
The following example code builds a probe expression and executes it as a one-shot probe in a single application process.
#include <stdio.h> #include <stdlib.h> #include <dpcl.h> void data_cb(GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg); void main(int argc, char *argv[]) { Process P; AisStatus sts; int c; int value; ProbeExp pcount; ProbeExp addexp; ProbeExp sendexp; ProbeExp fullexp; ProbeExp parms[5]; // initialize DPCL environment Ais_initialize(); // construct a valid Process object P = Process(argv[1], atoi(argv[2])); // connect to the target application sts = P.bconnect(); printf("connect is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // allocate a data variable with init value = 0 value = 0; pcount = P.balloc_mem(int32_type(), &value, sts); printf("alloc_mem is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); // create an assignment statement addexp = pcount.assign(pcount + ProbeExp(1)); // create a probe expression to send the result // back to DPCL program parms[0] = Ais_msg_handle; parms[1] = pcount.address(); parms[2] = ProbeExp(4); sendexp = Ais_send.call(3, parms); fullexp = addexp.sequence(sendexp); // issue an one-shot probe sts = P.bexecute(fullexp, (GCBFuncType)data_cb, (GCBTagType)1); printf("execute is done, status = %d\n", (int)sts); if (sts.status() != ASC_success) exit(1); printf("----- The End -----\n"); } void data_cb(GCBSysType sys, GCBTagType tag, GCBObjType obj, GCBMsgType msg) { Process *p = (Process *)obj; int *i = (int *)msg; printf("task %d sent the count %d\n", p->get_task(), *i); }