Open|SpeedShop logo
Plugin Creation Guide

Version 2.0.2 Release
November 9, 2012
(NOTE: concepts are still valid but some diagrams need updating)

Table of Contents

Open|SpeedShop Overview and Information Resources

Open|SpeedShop is a project co-funded by the Department of Energy (DOE), managed by the Tri-laboratories of LLNL, LANL, and Sandia, and developed by SGI.  Open|SpeedShop utilizes dynamic instrumentation technology developed at the Universities of Wisconsin and Maryland.

Open|SpeedShop is an open source multi platform Linux performance tool which is initially targeted to support performance analysis of applications running on both single node and large scale IA64, IA32, EM64T, and AMD64 platforms. It is explicitly designed with usability in mind and targets both application and computer scientists.
 
Open|SpeedShop's base functionality includes exclusive and inclusive user time, MPI call tracing, and CPU hardware performance counter experiments.  In addition, Open|SpeedShop is designed to be modular and extensible.  It supports several levels of plugins which allow users to add their own performance experiments.

The infrastructure and base components of Open|SpeedShop are being released open source under GPL and LGPL.

Features:

Open|SpeedShop Component Diagram

Host/Client Component Diagram

This diagram illustrates the interactions of the Open|SpeedShop client components.   An item note in this diagram is that the Command Line Interface is at the center of the user interface options for Open|SpeedShop.   All user interface requests come through the Command Line Interface.   The Command Line Interface interprets the CLI commands and interacts with the Open|SpeedShop Framework component to execute the commands.

The Framework is at the heart of the Open|SpeedShop tool.   It interacts with the User Interface, Maintains the Performance Experiment Database, forces victim application performance gathering instrumentation, obtains performance data back from the victim application, and stores the performance data into the Performance Experiment Database file.


Open|SpeedShop Client Overview

Runtime/Server Component Diagram

This diagram illustrates the interactions of the Open|SpeedShop server components.  

The Dyninst component is at the heart of the server portion Open|SpeedShop tool.   It interacts with the DPCL component to control the victim application being analyzed for performance.  Dyninst inserts into the victim application performance gathering instrumentation, obtains performance data back from the victim application, and sends the performance data back to the client side of the Open|SpeedShop tool.


Open|SpeedShop Server/Runtime Diagram


Information Resources:

Additional information about Open|SpeedShop usage and design can be found in a number of Open|SpeedShop documents.  The currently available documents are:

General Plugin Definition

A plugin is a mechanism that allows new functionality to be added to a software program without having to change the infrastructure of the existing software program.   The new plugin creator can add additional features by following a set of rules and using a set of well defined function calls (API) to create a plugin.   In general most tool's plugin API and rules are different from another tools plugin API and plugin rules.  However, the basic concepts are the same.  The plugin needs to be recognized as a software entity that is associated with the base software tool.  Plugins have to be dynamically shared objects in order for the base software tool to dynamically link in the plugin.  There may also be entry points that the base software program looks for in the dynamic shared object to verify that the shared object is associated with it.

When the plugin is verified to be compatible with the base software program the base software program updates it's internal data structures and an applicable user interface items that would make the new functionality available to the user.

Open|SpeedShop uses this methodology.   This document describes how to create an Open|SpeedShop specific plugin.

Open|SpeedShop Plugin Overview

The Open|SpeedShop performance tool is oriented toward gathering performance data from user applications and relating that performance data back to the application source file, function name and line number(s).    The data gathering, graphical user interface displays, and the command line interface views are defined via the Open|SpeedShop plugin mechanism.   What this means is that the code and data required to accomplish the above mentioned data gathering, GUI control and display panels, and the CLI displays is compiled into a dynamically loaded object called a plugin.   The Open|SpeedShop tools search a specified directory path or paths to find the plugins and dynamically load them.   Open|SpeedShop uses the information inside the plugin to adjust the tools menus, internal data structures, etc. to integrate the functionality contained in the plugins into the tool.

A key point to be aware of while studying the plugin structure of Open|SpeedShop, is that although Open|SpeedShop currently uses Dyninst as it's dynamic instrumentation package and uses DPCL as the cluster support package, their APIs are not exposed to the plugin writer.   This is a design decision that allows those packages to be replaced or modified without impact to the plugin interface.  Open|SpeedShop wraps the functionality to hide the instrumentation details from the plugin writer.

There are currently three separate types of Plugins that Open|SpeedShop recognizes and dynamically integrates into the Open|SpeedShop tool set.  The three types of plugins are:
This document will outline each of the plugin types and give instruction and examples of how to create and integrate plugins into the Open|SpeedShop tools.

Plugin Creation Creation Notes

These plugin collector creation notes are from actual developers that have used this guide to build new Open|SpeedShop plugins.  They are included here as additional information.   In addition, notes on how to create an additional graphical user interface plugin can be found here.

Plugin Creation Outline

Before getting into all the details, it may be helpful to outline the steps involved in writing Open|SpeedShop plugins from a high level.  The following outline indicates the basic steps needed in creating Open|SpeedShop plugins.   If some steps aren't clear at this point, don't despair, they will be explained later.   The purpose of this section is to give a broad overview and context to the specific steps that are described in the rest of the document.

General Open|SpeedShop Plugin Outline

See each of the following sections for the plugin specific outline.   As stated above, there are currently three types of plugins that can be created in the Open|SpeedShop tools.  The following list links to each of the plugin outlines:

General Plugin Structure

Performance Data Collector Plugin Structure
As noted above, the experiment collector plugin is actually physically two dynamic shared objects.  If one was to view the directory where the Open|SpeedShop plugins are installed (<install_dir_prefix>/lib/openspeedshop) you would see that there are two dso's for each experiment collector.   For example, here is what is shown for the PC Sampling experiment collector:
pcsamp.la*  pcsamp-rt.la*  pcsamp-rt.so*  pcsamp.so*

The runtime portion of the experiment collector plugin is appended with "-rt" while the client portion is not.
GUI Plugin Structure
GUI plugins have on physical shared object for each panel.   For the pcsamp experiment,  a listing of the plugin directory would show these files:
pcSamplePanel.la*  pcSamplePanel.so* 

Additionally, one would see another dynamically shared object relating to the PC Sampling experiment.  However, support for creating wizard plugins is not available at this time.
pcSampleWizardPanel.la*  pcSampleWizardPanel.so*

Interactive CLI Plugin Structure
Support for the interactive command line interface plugins is tbd.

How do plugins get recognized?

The keys to Open|SpeedShop recognizing that a file is an Open|SpeedShop plugin are listed here.  The keys are described in more details in the sections describing the implementation of the respective plugins.  The high level list of factors that indicate a file is an Open|SpeedShop plugin are:
There are additional details that are described in the plugin specific sections below.

Where are the plugins located?

Open|SpeedShop recognizes the OPENSS_PLUGIN_PATH environment variable and will search the directory path supplied by this environment variable to find user created plugins.   This environment variable provides an additional plugin path and does not override the Open|SpeedShop default plugin location path.    The plugins can be in any accessible directory as long as the OPENSS_PLUGIN_PATH environment variable specifies that path path.

The Open|SpeedShop default plugin search path is based on the "--prefix" specified for the Open|SpeedShop build and installation.   Open|SpeedShop follows the GNU convention which specifies the package name appended the <install_dir>/lib/<package_name> for the plugin path.    Based on the GNU convention,  the path to the default plugin directory for Open|SpeedShop is <install_dir>/lib/openspeedshop.

Performance Data Collector Plugin Information

Writing a performance data collector consists of several steps.   The first step is to think about the performance data that is to be collected.  This first step can be characterized as identifying and analyzing the performance data desired.   See the section below that is dedicated to the outlining the details associated with this action.  Once the performance data criteria are resolved the next step in the collector plugin creation is the actual implementation action.   See the sections below that are associated with implementation and also the section detailing a performance data collector plugin example.

Performance Data Collector Quick Overview

Performance Data Collector Plugin Outline

For building a performance data collector plugin these are the high level steps in outline form:
Return to General Plugin Section

Performance Data Collector Plugin Structure

As noted above, the experiment collector plugin is actually physically two dynamic shared objects.  If one was to view the directory where the Open|SpeedShop plugins are installed (<install_dir_prefix>/lib/openspeedshop) you would see that there are two dso's for each experiment collector.   For example, here is what is shown for the PC Sampling experiment collector:

pcsamp.la*  pcsamp-rt.la*  pcsamp-rt.so*  pcsamp.so*

The runtime portion of the experiment collector plugin is appended with "-rt" while the client portion is not.


Identifying and Analyzing The Performance Data Desired

The first step is the thought process which identifies the performance data desired to be collected.  In other words, "What performance information do we want to try and collect?".   Open|SpeedShop allows the user to collect the performance data and then map the gathered data back to the program source function name and/or source line numbers.   One question to be asked before starting to write a collector plugin for Open|SpeedShop is can the performance data desired  be associated with the program's source line and function addresses.  Does the performance data map back to the source?   If so, then it is a candidate to be gathered by an Open|SpeedShop plugin.   

For example, the PC Sampling experiment takes periodic samples of the program address counter.   A periodic timer is set and when it triggers, the address of where the program is executing at that point is saved.   The address can be mapped back to the source program because we have saved the address in the program, as the performance data gathered.   We can, by using debugging information, map the saved addresses back to the program's source.   Actually, a set of addresses and the count of the times each address is "hit" are saved.

Another part of this step is to identify and define the metric and parameters.  The metric is a function of the time intervals and the instruction address space.   It is also the basic item of performance data that is to be gathered and displayed by the collector plugin.  The parameter(s) can be thought of as the dial, selector, or knob that controls how the data is collected.   For example, the PC Sampling experiment gathers the application's address at time intervals across the applications running time.  The time interval at which each address sample is taken is the dial, selector, or knob controlling how (actually when)  the data is collected.   The metric, in this case, is time spent in the application's functions and source line numbers based on translating the addresses that were gathered, back to the source function names and line numbers.

Given that the "metric" is the performance data item desired to be displayed and that the "parameter(s)" is the dial, selector, or knob to control the collection of the "metric", lets examine these items in more detail.   

Examples of metrics already being collected in Open|SpeedShop are:
Examples of parameters already supported by Open|SpeedShop are:

Key point:
If the parameter is a type that is already supported by Open|SpeedShop, additional GUI and/or CLI plugins may not have to be written to support the new collector plugin.   Since, the existing types are recognized, a generic view can display the performance data.


Implementing The Collector Plugin

Overview

This section provides a brief implementation related overview of what is going on inside Open|SpeedShop with respect to the interactions of the collector plugin with the other components of the tool and with the application being analyzed.   A collector plugin will facilitate Open|SpeedShop to gather performance measurements that will be gathered and stored temporarily in the victim application.  This performance data is sent from the victim application to the Open|SpeedShop client when the performance data buffer fills up in the victim application.   When the Open|SpeedShop client receives the performance data it is stored in the experiment database file.

Tool Overview

Plugin Recognition

The Open|SpeedShop framework component, using libltdl, will search the plugin directory paths for files that can be dynamically opened, (dso) via dlopen.  It then opens the dso files and look for a special entry point, "<plugin_name>_LTX_CollectorFactory".   If this entry point is found, and it is the only visible entry point in the dynamically shared object file then the file is recognized as a plugin.   At this point further plugin verification and creation processing occurs.   The  CollectorFactory function is  called to create  a new instantiation of the "CollectorImpl" class which, in reality, creates the collector plugin.   The code that is responsible for the instantiation can be found in  the Open|SpeedShop source file "libopenss-framework/CollectorPluginTable.cxx" in CollectorImpl* CollectorPluginTable::instantiate function.  No changes are required by the plugin writer in this function.  This information is included here for completeness.  The unique identification code of the collector plugin is also checked in this routine to see if the plugin has already been encountered.  If it has not been encountered yet, it is added to the list of collector plugins.   The collector is actually added to the collector list in the function "Collector::instantiateImpl" which is found in the Open|SpeedShop source file "libopenss-framework/Collector.cxx".

So, to assure that the plugin you are creating is found, make sure you define the special entry point "<your_plugin_name>_LTX_CollectoryFactory".    The Example collector plugin shows this in the file "examples/libopenss-framework/collector/ExampleCollector.cxx"    Here is the definition of "example_LTX_CollectorFactory()" from the example collector.

/**
 * Collector's factory method.
 *
 * Factory method for instantiating a collector implementation. This is the
 * only function that is externally visible from outside the collector plugin.
 *
 * @return    New instance of this collector's implementation.
 */
extern "C" CollectorImpl* example_LTX_CollectorFactory()
{
    return new ExampleCollector();
}

Data blobs

The collector writer defines the format of the performance data being gathered and stored in the victim application.   Open|SpeedShop uses the term, blob (binary large object), to refer to the performance data objects being gathered and stored in the victim application. 

This is a very important phase of the collector creation!   Much time needs to be spent in the design and layout of the data blob data definitions to optimize the data layout.  Having an optimal data layout in size and content will help to create an experiment that executes efficiently.   Size considerations and making sure only the necessary performance data items are stored are the two main considerations.

 The design issues to consider when defining the structure and size of the blob data are:
Data blobs are specified in the XDR (External Data Representation) language.   XDR deals with endianess issues, data packing, and other related issues.   The XDR language looks like a C structure definition and is specified in a file call blob.x.   This is what the collector plugin expects.  The blob.x file is fed into rpcgen which generates a ".c" file and an ".h" file.  These files create a compilable C structure and functions to convert the data to a C structure and back.
blobs.x:
For the example collector the blobs.x XDR language defines the experiments parameter(s), performance data, and the structure to pass parameters to the runtime/victim start_sampling routine.

/** Structure of the blob containing our parameters. */
struct example_parameters {
    unsigned sampling_rate;  /**< Sampling rate in samples/second. */
};

/** Structure of the blob containing our performance data. */
struct example_data {
    uint64_t interval;    /**< Sampling interval in nanoseconds. */
    uint64_t pc<>;        /**< Program counter (PC) addresses. */
};

/** Structure of the blob containing example_start_sampling()'s arguments. */
struct example_start_sampling_args {
    unsigned sampling_rate;  /**< Sampling rate in samples/second. */
    int experiment;  /**< Identifier of experiment to contain the data. */
    int collector;   /**< Identifier of collector gathering data. */
    int thread;      /**< Identifier of gathered data's thread. */
};

Return to top of Data blobs section

blobs.h:
The blob data structures are used both by the Collector Runtime plugin (server/victim side) and also by the Collector plugin (client side). 
Excerpts from the blobs.h file are shown here.  Note the similarity between the C structures here, in blobs.h, with the XDR definitions in blobs.x.

struct example_parameters {
        u_int sampling_rate;
};
typedef struct example_parameters example_parameters;

struct example_data {
        uint64_t interval;
        struct {
                u_int pc_len;
                uint64_t *pc_val;
        } pc;
};
typedef struct example_data example_data;

struct example_start_sampling_args {
        u_int sampling_rate;
        int experiment;
        int collector;
        int thread;
};
typedef struct example_start_sampling_args example_start_sampling_args;

/* the xdr functions */

#if defined(__STDC__) || defined(__cplusplus)
extern  bool_t xdr_example_parameters (XDR *, example_parameters*);
extern  bool_t xdr_example_data (XDR *, example_data*);
extern  bool_t xdr_example_start_sampling_args (XDR *, example_start_sampling_args*);

#else /* K&R C */
extern bool_t xdr_example_parameters ();
extern bool_t xdr_example_data ();
extern bool_t xdr_example_start_sampling_args ();

#endif /* K&R C */

Return to top of Data blobs section
blobs.c:
In blobs.c, we see routines that are generated by rpcgen.  These routines contain code using XDR functionality  to return the experiments (example in this case) parameter and data.  The blob "C" files are compiled into each of the plugins (runtime and client) that represent the collector.  Excerpts from the blobs.c file are shown here.


#include "blobs.h"

bool_t
xdr_example_parameters (XDR *xdrs, example_parameters *objp)
{
        register int32_t *buf;

         if (!xdr_u_int (xdrs, &objp->sampling_rate))
                 return FALSE;
        return TRUE;
}

bool_t
xdr_example_data (XDR *xdrs, example_data *objp)
{
        register int32_t *buf;

         if (!xdr_uint64_t (xdrs, &objp->interval))
                 return FALSE;
         if (!xdr_array (xdrs, (char **)&objp->pc.pc_val, (u_int *) &objp->pc.pc_len, ~0,
                sizeof (uint64_t), (xdrproc_t) xdr_uint64_t))
                 return FALSE;
        return TRUE;
}
Return to top of Data blobs section

Collector Runtime - Server/Victim portion of Experiment Collector

The collector runtime gathers data dependent on the type of performance data the collector writer has decided to gather.    The collector runtime's purpose is to gather the data, package it, and send it from the victim application to the Open|SpeedShop experiment collector plugin client.   Functionality that is generally needed by collector runtime plugins includes the following.    Note that the code examples in this section reference a thread local storage structure which holds thread specific information (see Thread Specific Data for the definition).
There is also a set of commonly used shared functions that are bundled into the Open|SpeedShop libopenss-runtime dynamic object.   These are functions that are used by more than one collector.   For example, some of the libunwind functionality is contain in the libopenss-runtime dynamic object.  The implementation of this portion of the collector is contained in the file "runtime.c"   The runtime versions of start and stop sampling are contained in this file.

Thread Specific Data
/** Thread-local storage. */
static __thread struct {

    OpenSS_DataHeader header;  /**< Header for following data blob. */
    example_data data;         /**< Actual data blob. */

    /** Sample buffer. */
    struct {
        uint64_t pc[BufferSize];  /**< Program counter (PC) addresses. */
    } buffer;

} tls;



Timer event handler
/**
 * Timer event handler.
 *
 * Called by the timer handler each time a sample is to be taken. Extracts the
 * program counter (PC) address from the signal context and places it into the
 * sample buffer. When the sample buffer is full, it is sent to the framework
 * for storage in the experiment's database.
 *
 * @param context    Thread context at timer interrupt.
 */
static void exampleTimerHandler(const ucontext_t* context)
{
    uint64_t pc;

    /* Obtain the program counter (PC) address from the thread context */
    pc = OpenSS_GetPCFromContext(context);

    /* Add a new entry for this PC address to the sample buffer */
    tls.buffer.pc[tls.data.pc.pc_len++] = pc;

    /* Update the address interval in the data blob's header */
    if(pc < tls.header.addr_begin)
        tls.header.addr_begin = pc;
    if(pc > tls.header.addr_end)
        tls.header.addr_end = pc;

    /* Is the sample buffer full? */
    if(tls.data.pc.pc_len == BufferSize) {

        /* Send these samples */
        tls.header.time_end = OpenSS_GetTime();
        OpenSS_Send(&(tls.header), (xdrproc_t)xdr_example_data, &(tls.data));

        /* Re-initialize the data blob's header */
        tls.header.time_begin = tls.header.time_end;
        tls.header.time_end = 0;
        tls.header.addr_begin = ~0;
        tls.header.addr_end = 0;

        /* Re-initialize the actual data blob */
        tls.data.pc.pc_len = 0;

    }
}

Back To Collector Runtime - Server/Victim portion of Experiment Collector

Start Sampling
/**
 * Start sampling.
 *
 * Starts program counter (PC) sampling for the thread executing this function.
 * Initializes the appropriate thread-local data structures and then enables the
 * sampling timer.
 *
 * @param arguments    Encoded function arguments.
 */
void example_start_sampling(const char* arguments)
{
    example_start_sampling_args args;

    /* Decode the passed function arguments. */
    memset(&args, 0, sizeof(args));
    OpenSS_DecodeParameters(arguments,
                            (xdrproc_t)xdr_example_start_sampling_args,
                            &args);

    /* Initialize the data blob's header */
    tls.header.experiment = args.experiment;
    tls.header.collector = args.collector;
    tls.header.thread = args.thread;
    tls.header.time_begin = 0;
    tls.header.time_end = 0;
    tls.header.addr_begin = ~0;
    tls.header.addr_end = 0;

    /* Initialize the actual data blob */
    tls.data.interval = (uint64_t)(1000000000) / (uint64_t)(args.sampling_rate);
    tls.data.pc.pc_len = 0;
    tls.data.pc.pc_val = tls.buffer.pc;

    /* Begin sampling */
    tls.header.time_begin = OpenSS_GetTime();
    OpenSS_Timer(tls.data.interval, exampleTimerHandler);
}

Back To Collector Runtime - Server/Victim portion of Experiment Collector

Stop Sampling
/**
 * Stop sampling.
 *
 * Stops program counter (PC) sampling for the thread executing this function.
 * Disables the sampling timer and sends any samples remaining in the buffer.
 *
 * @param arguments    Encoded (unused) function arguments.
 */
void example_stop_sampling(const char* arguments)
{
    /* Stop sampling */
    OpenSS_Timer(0, NULL);
    tls.header.time_end = OpenSS_GetTime();

    /* Send any samples remaining in the sample buffer */
    if(tls.data.pc.pc_len > 0)
        OpenSS_Send(&(tls.header), (xdrproc_t)xdr_example_data, &(tls.data));
}

Back To Collector Runtime - Server/Victim portion of Experiment Collector


Framework runtime
The framework runtime contains a number of routines that the experiment collector can use.   The libopenss-runtime Open|SpeedShop source directory contains the source for these supporting routines.   Routines available in this directory provide this functionality:

Collector Plugin - Client/Tool portion of Experiment Collector

The client collector plugin activates the collector runtime by directly and indirectly making calls to the collector runtime functions.    An example user action illustrates the path between the client and runtime collector plugin functionality.   Suppose the user does a user interface gesture that tells Open|SpeedShop to start collecting.    This triggers the user interface to call the Open|SpeedShop framework which then accesses the start_collecting function in the client collector plugin.   The client collector plugin calls an Open|SpeedShop routine which ultimately triggers the start collecting function in the runtime collector plugin.

Start Collection
        Illustration

Collector Plugin API- Public Member Functions

There are six functions which form the basis for the collector and must be defined in every collector plugin.   The six functions make up the public member functions for collector plugin API and perform the following tasks:
These public functions make use of the collector plugin private member functions.   The following sub-sections describe the public member functions.   See Collector Plugin API - Private Member Functions for details.
The Start Collecting function
What it does:
Start data collection function implements starting data collection for a particular thread.
Definition:
This is an example definition from the Open|SpeedShop Usertime Collector Plugin:
void ExampleCollector::startCollecting (const Collector & collector,
                                                                  const Thread & thread ) const

Parameters:
The meaning or definitions for the arguments above are summarized here:
  1. collector - The collector that will be starting data collection
  2. thread - The thread for which to start data collection.
What's needed in the function body:
  1. Assemble and encode collector arguments to the collectors start collecting routine.
  2. Make decision on where to insert your instrumentation code (performance data gathering code).  The options are:
  3. Insert instrumentation to:
Example Collector Code Example:
/**
 * Start data collection.
 *
 * Implement starting data collection for a particular thread.
 *
 * @param collector    Collector starting data collection.
 * @param thread       Thread for which to start collecting data.
 */
void ExampleCollector::startCollecting(const Collector& collector,
                                       const Thread& thread) const
{
    // Assemble and encode arguments to example_start_sampling()
    example_start_sampling_args args;
    memset(&args, 0, sizeof(args));
    collector.getParameterValue("sampling_rate", args.sampling_rate);
    getECT(collector, thread, args.experiment, args.collector, args.thread);
    Blob arguments(reinterpret_cast<xdrproc_t>(xdr_example_start_sampling_args),
                   &args);

    // Execute example_stop_sampling() when we enter exit() for the thread
    executeAtEntry(collector, thread,
                   "exit", "example-rt: example_stop_sampling", Blob());

    // Execute example_start_sampling() in the thread
    executeNow(collector, thread,
               "example-rt: example_start_sampling", arguments);
}

The Stop Collecting function
What it does:
Stop data collection function implements stopping data collection for a particular thread.
Definition:
This is an example definition from the Open|SpeedShop Example Collector Plugin:
void ExampleCollector::stopCollecting (const Collector & collector,
                                                                  const Thread & thread ) const

Parameters:
The meaning or definitions for the arguments above are summarized here:
  1. collector - The collector that will be stopping data collection
  2. thread - The thread for which to stop data collection.
Decisions and what's needed in the function body:
The primary tasks of this function that need to be done are:
  1. Insert instrumentation to execute the collector's stop_sampling routine in the thread
  2. Remove the instrumentation that was inserted by startCollecting:

Example collector code example:
/**
 * Stops data collection.
 *
 * Implement stopping data collection for a particular thread.
 *
 * @param collector    Collector stopping data collection.
 * @param thread       Thread for which to stop collecting data.
 */
void ExampleCollector::stopCollecting(const Collector& collector,
                                      const Thread& thread) const
{
    // Execute example_stop_sampling() in the thread
    executeNow(collector, thread,
               "example-rt: example_stop_sampling", Blob());

    // Remove all instrumentation associated with this collector/thread pairing
    uninstrument(collector, thread);
}

The Get Metric Values function
What it does:
This routine is used by the Open|SpeedShop framework to get the metrics for each data blob the framework processes.  The framework will first query for address range extents and then calls the collector plugin (getMetricValues) once for each data blob.   Extents are defined as a set of address ranges and a time interval.   For example, an address range could be the addresses in the function "xyz" and the time interval could be the entire running time of the program.
Definition:
This is an example definition from the Open|SpeedShop Example Collector Plugin:
void ExampleCollector::getMetricValues(const std::string& metric,
                                        const Extent& extent,
                                        const Blob& blob,
                                        const ExtentGroup& subextents,
                                        void* ptr) const
Parameters:
The meaning or definitions for the arguments above are summarized here:
  1. metric - Unique identifier of the metric
  2. extent - Extent of the performance data blob
  3. blob - Blob containing the performance data
  4. subextents - The subextents for which to get the values
  5. ptr  - Untyped pointer to the return values.
Decisions and what's needed in the function body:
The primary tasks of this function that need to be done are:
  1. Process the collector's metric value(s) by iterating over the blob containing the raw performance data.
  2. Free the decoded performance data blob.
Example collector code example:
/**
 * Get metric values.
 *
 * Implements getting one of this collector's metric values over all subextents
 * of the specified extent for a particular thread, for one of the collected
 * performance data blobs.
 *
 * @param metric        Unique identifier of the metric.
 * @param collector     Collector for which to get values.
 * @param thread        Thread for which to get values.
 * @param extent        Extent of the performance data blob.
 * @param blob          Blob containing the performance data.
 * @param subextents    Subextents for which to get values.
 * @retval ptr          Untyped pointer to the values of the metric.
 */
void ExampleCollector::getMetricValues(const std::string& metric,
                                       const Collector& collector,
                                       const Thread& thread,
                                       const Extent& extent,
                                       const Blob& blob,
                                       const ExtentGroup& subextents,
                                       void* ptr) const
{
    // Only the "time" metric returns anything
    if(metric != "time")
        return;

    // Cast the untyped pointer into a vector of doubles
    std::vector<double>* values = reinterpret_cast<std::vector<double>*>(ptr);

    // Check assertions
    Assert(values->size() >= subextents.size());

    // Decode this data blob
    example_data data;
    memset(&data, 0, sizeof(data));
    blob.getXDRDecoding(reinterpret_cast<xdrproc_t>(xdr_example_data), &data);

    // Calculate the time (in seconds) attributable to each sample
    double t_sample =
        static_cast<double>(data.interval) / 1000000000.0;

    // Calculate time (in nS) of data blob's extent
    double t_blob =
        static_cast<double>(extent.getTimeInterval().getWidth());

    // Iterate over each of the samples
    for(unsigned i = 0; i < data.pc.pc_len; ++i) {

        // Find the subextents that contain this sample
        std::set<ExtentGroup::size_type> intersection =
            subextents.getIntersectionWith(
                Extent(extent.getTimeInterval(),
                       AddressRange(data.pc.pc_val[i]))
                );

        // Iterate over each subextent in the intersection
        for(std::set<ExtentGroup::size_type>::const_iterator
                j = intersection.begin(); j != intersection.end(); ++j) {

            // Calculate intersection time (in nS) of subextent and data blob
            double t_intersection = static_cast<double>
                ((extent.getTimeInterval() &
                  subextents[*j].getTimeInterval()).getWidth());

            // Add (to the subextent's metric value) the appropriate fraction
            // of the total time attributable to this sample
            (*values)[*j] += t_sample * (t_intersection / t_blob);
        }
    }

    // Free the decoded data blob
    xdr_free(reinterpret_cast<xdrproc_t>(xdr_example_data),
             reinterpret_cast<char*>(&data));
}

The Set Parameter function
What it does:

The set a parameter function is responsible for setting one of the collector parameter values.

Definition:

This is an example definition from the Open|SpeedShop Example Collector Plugin:
void ExampleCollector::setParameterValue(const std::string& parameter,
                                                                        const void* ptr,
                                                                        Blob& data) const

Parameters:
  1. parameter - Unique identifier of the input parameter to be set
  2. ptr - Untyped pointer to the parameter value
Return values:
  1. data - blob containing the parameter values for the collector
Decisions and what's needed in the function body:
The primary tasks of this function that need to be done are:
  1. Decode the blob containing the parameter values
  2. Set the "collector specific" parameter based on the input
  3. Re-encode the blob containing the parameter values
Example collector code example:
/**
 * Set a parameter value.
 *
 * Implements setting one of our parameter values.
 *
 * @param parameter    Unique identifier of the parameter.
 * @param ptr          Untyped pointer to the parameter value.
 * @retval data        Blob containing the parameter values.
 */
void ExampleCollector::setParameterValue(const std::string& parameter,
                                         const void* ptr, Blob& data) const
{
    // Decode the blob containing the parameter values
    example_parameters parameters;
    memset(&parameters, 0, sizeof(parameters));
    data.getXDRDecoding(reinterpret_cast<xdrproc_t>(xdr_example_parameters),
                        &parameters);

    // Handle the "sampling_rate" parameter
    if(parameter == "sampling_rate") {
        const unsigned* value = reinterpret_cast<const unsigned*>(ptr);
        parameters.sampling_rate = *value;
    }

    // Re-encode the blob containing the parameter values
    data = Blob(reinterpret_cast<xdrproc_t>(xdr_example_parameters),
                &parameters);
}

The Get Default Parameters function
What it does:
The get default parameters function implements getting the default parameter set for the collector.
Definition:
This is an example definition from the Open|SpeedShop Example Collector Plugin:
Blob ExampleCollector::getDefaultParameterValues() const
Return values:
  1. data - Blob containing the default parameter values.
Decisions and what's needed in the function body:
The primary tasks of this function that need to be done are:
  1. Setup an empty parameter structure
  2. Set the default parameters
  3. Return the encoded blob to the caller
Example collector code example:
/**
 * Get the default parameter values.
 *
 * Implement getting our default parameter values.
 *
 * @return    Blob containing the default parameter values.
 */
Blob ExampleCollector::getDefaultParameterValues() const
{
    // Setup an empty parameter structure
    example_parameters parameters;
    memset(&parameters, 0, sizeof(parameters));

    // Set the default parameters
    parameters.sampling_rate = 100;

    // Return the encoded blob to the caller
    return Blob(reinterpret_cast<xdrproc_t>(xdr_example_parameters),
                &parameters);
}

The Get Parameters function
What it does:
The get a parameter function implements getting one of the collector parameter values.
Definition:
This is an example definition from the Open|SpeedShop Example Collector Plugin:
void ExampleCollector::getParameterValue(const std::string& parameter,
                                                                       const Blob& data, void* ptr) const
Parameters:
  1. parameter - Unique identifier of the input parameter to be get.
  2. data - blob containing the parameter values for the collector
Return values:
  1. ptr - Untyped pointer to the parameter value
Decisions and what's needed in the function body:
  1. Decode the data blob containing the parameter values using XDR (External Data Representation)
  2. Extract the parameter value from the decoded parameter value data blob
Example collector code example:
/**
 * Get a parameter value.
 *
 * Implement getting one of our parameter values.
 *
 * @param parameter    Unique identifier of the parameter.
 * @param data         Blob containing the parameter values.
 * @retval ptr         Untyped pointer to the parameter value.
 */
void ExampleCollector::getParameterValue(const std::string& parameter,
                                                                                 const Blob& data, void* ptr) const
{
    // Decode the blob containing the parameter values
    example_parameters parameters;
    memset(&parameters, 0, sizeof(parameters));
    data.getXDRDecoding(reinterpret_cast<xdrproc_t>(xdr_example_parameters),
                        &parameters);

    // Handle the "sampling_rate" parameter
    if(parameter == "sampling_rate") {
        unsigned* value = reinterpret_cast<unsigned*>(ptr);
        *value = parameters.sampling_rate;
    }
}

Collector Plugin API - Protected Member Functions

These are the functions that the collector writer uses to implement the runtime/victim portion of the experiment data collector plugin.  They are implemented in the Open|SpeedShop framework library.   The source can be found in "<top_srcdir>/libopenss-framework/CollectorImpl.cxx".

Collector Implementation Constructor
Definition
     CollectorImpl (const std::string &, const std::string &, const std::string &)
What it does, when it can be used, and example usage code
Constructs a collector implementation with the given metadata. 

/**
 * Default constructor.
 *
 * Constructs a new example collector with the proper metadata.
 */
ExampleCollector::ExampleCollector() :
    CollectorImpl("example",
                  "Example",
                  "Simple example performance data collector illustrating the "
                  "basics of the Collector API. Periodically interrupts the "
                  "running thread, obtains the current program counter (PC) "
                  "value, stores it, and allows the thread to continue "
                  "execution.")
{
    // Declare our parameters
    declareParameter(Metadata("sampling_rate", "Sampling Rate",
                              "Sampling rate in samples/seconds.",
                              typeid(unsigned)));

    // Declare our metrics
    declareMetric(Metadata("time", "CPU Time",
                           "Exclusive CPU time in seconds.",
                           typeid(double)));
}

Declare a Parameter
Definition
void     declareParameter (const Metadata &)
What it does, when it can be used, and example usage code
Declares a parameter by adding it to this collector implementation and is called by a collector when declaring the parameter for the particular collector.  The call to declareParameter is usually made from collector's constructor.    Here is the a code snippet from the example collector found in "<top_srcdir>/examples/libopenss-framework/collector/ExampleCollector.cxx".

/**
 * Default constructor.
 *
 * Constructs a new example collector with the proper metadata.
 */
ExampleCollector::ExampleCollector() :
    CollectorImpl("example",
                  "Example",
                  "Simple example performance data collector illustrating the "
                  "basics of the Collector API. Periodically interrupts the "
                  "running thread, obtains the current program counter (PC) "
                  "value, stores it, and allows the thread to continue "
                  "execution.")
{
    // Declare our parameters
    declareParameter(Metadata("sampling_rate", "Sampling Rate",
                              "Sampling rate in samples/seconds.",
                              typeid(unsigned)));

    // Declare our metrics
    declareMetric(Metadata("time", "CPU Time",
                           "Exclusive CPU time in seconds.",
                           typeid(double)));
}

Parameters
Metadata  -   Declares the properties of the parameter to be used by the collector.

Metadata general description:
Container storing object metadata consisting of a unique identifier, short name, verbose description, and underlying type. These properties are assigned when the Metadata object is created. Member functions for querying these properties are defined.   Metadata objects are defined as being equivalent when their unique identifiers are identical.   Used for describing collectors, their parameters, and their metrics.

Return Values
None.
Declare a Metric
Definition
void     declareMetric (const Metadata &)
What it does, when it can be used, and example usage code
Declares a metric by adding it to this collector implementation and is called by a collector when declaring the metric for the particular collector.  The call to declareMetric is usually made from collector's constructor.    Here is the a code snippet from the example collector found in "<top_srcdir>/examples/libopenss-framework/collector/ExampleCollector.cxx".

/**
 * Default constructor.
 *
 * Constructs a new example collector with the proper metadata.
 */
ExampleCollector::ExampleCollector() :
    CollectorImpl("example",
                  "Example",
                  "Simple example performance data collector illustrating the "
                  "basics of the Collector API. Periodically interrupts the "
                  "running thread, obtains the current program counter (PC) "
                  "value, stores it, and allows the thread to continue "
                  "execution.")
{
    // Declare our parameters
    declareParameter(Metadata("sampling_rate", "Sampling Rate",
                              "Sampling rate in samples/seconds.",
                              typeid(unsigned)));

    // Declare our metrics
    declareMetric(Metadata("time", "CPU Time",
                           "Exclusive CPU time in seconds.",
                           typeid(double)));
}

Parameters
Metadata  -   Declares the properties of the metric to be gathered by the collector.

Metadata general description:
Container storing object metadata consisting of a unique identifier, short name, verbose description, and underlying type. These properties are assigned when the Metadata object is created. Member functions for querying these properties are defined.   Metadata objects are defined as being equivalent when their unique identifiers are identical.   Used for describing collectors, their parameters, and their metrics.

Return Values
None.

Get experiment, collector, and thread (ECT) identifiers
void     getECT (const Collector &, const Thread &, int &, int &, int &) const
     Get experiment, collector, and thread (ECT) identifiers.
Execute library function now.
void     executeNow (const Collector &, const Thread &, const std::string &, const Blob &) const
     Execute library function now.

Execute library function at another function's entry.
void     executeAtEntry (const Collector &, const Thread &, const std::string &, const std::string &, const Blob &) const
     Execute library function at another function's entry.

Execute library function at another function's exit.
void     executeAtExit (const Collector &, const Thread &, const std::string &, const std::string &, const Blob &) const
     Execute library function at another function's exit.

Execute a library function in place of another function.
void     executeInPlaceOf (const Collector &, const Thread &, const std::string &, const std::string &) const
     Execute a library function in place of another function.

Remove instrumentation from a thread
void     uninstrument (const Collector &, const Thread &) const
     Remove instrumentation from a thread.

Example Collector Plugin

The code for the Example Collector Plugin can be found in the <top_srcdir>/examples/libopenss-framework/collector directory.  The files contained in that directory are outlined here:
See the section describing the Collector runtime for how to implement the necessary functions for runtime.c.
Use this file as guide to create the new collector plugin.
See the section describing the Collector Plugin (client) for how to implement the necessary functions for the client side of the collector plugin.
See the section describing the data blob for how to design and implement the performance data definitions for the data that the runtime collector plugin will send to the Open|SpeedShop client.


Executing the "make install" make file command in the "<top_srcdir>/examples/libopenss-framework/collector" directory will install the example plugin to the default plugin installation path, which is <top_srcdir>../lib/openspeedshop or to "$OPENSS_PREFIX/lib/openspeedshop".   After the example collector plugin is installed, the Open|SpeedShop tool recognizes it's presence and it can be used as a collector.  

The example tool does pcsampling, but all the hashing and any extra code that might make it more complicated has been taken out so that only the basic plugin functionality is present.   For this run, the example plugin was installed into the default plugin directory and used in the same manner as any other plugin.   This is the example plugin experiment output:

orbit20-169>pwd
/home/jeg/openss/current/test/executables/fred
orbit20-170>openss -cli
Welcome to Open|SpeedShop, version 0.1.
Type 'help' for more information.
# Create an interactive command line interface experiment on the executable "fred" using the "example" collector
openss>>expcreate -f fred example
   Found symtab
The new focused experiment identifier is:  -x 1
# Run the focused experiment asynchroniously
openss>>expgo
Start asynchronous execution of experiment:  -x 1
openss>>
# Give me status of the focused experiment
openss>>expstatus
Experiment definition
{ # ExpId is 1, Status is Running, Temporary database is /tmp/ssdb1DthPUP.openss
  Currently Specified Components:
    -h orbit20 -p 32414 example
  Metrics:
    example::time
  Parameter Values:
    example::sampling_rate =  100
  Available Views:
    pcsamp
}
openss>>
# Execute a ps in the shell, outside of Open|SpeedShop
openss>>!ps -u jeg
  PID TTY          TIME CMD
27766 ?        00:00:02 sshd
27767 pts/0    00:00:00 csh
31008 ?        00:00:00 sshd
31009 pts/1    00:00:00 csh
32403 pts/1    00:00:04 openss
32413 ?        00:00:03 dpcld64
32414 ?        00:03:06 fred
32416 pts/1    00:00:00 openss
32417 pts/1    00:00:00 ps

openss>>Experiment 1 has terminated.
# View the results of the focused experiment, using the default view
openss>>expview

            CPU Time  Function Name
            141.2500  f3(fred: f3.c,2)
             95.0400  f2(fred: f2.c,2)
             47.5100  f1(fred: f1.c,2)
openss>>

Collector Plugin FAQ:

What is blob.x for? What is the significance of the .x suffix?

"blob.x" contains XDR descriptions of the blobs (Binary Large OBjectS) used by the collector. The term "blob" is a standard one in relational databases:
http://www.hyperdictionary.com/dictionary/binary+large+object

And ".x" is the standard suffix used for XDR files. You can do (this is what I
did) a search for "XDR file suffix" and you'll see that everyone uses ".x".

Collector parameters can be of any arbitrary type or structure. And the raw data gathered by the collector can be of any arbitrary type or structure. But the relational database doesn't know anything about these types... So whenever
parameters or raw performance data is stored in the database, it must first be converted into a "chunk of bytes" (a blob). XDR is added to the mix in order to easily handle things like endianess and structure alignment issues.

What is specifically needed to be known by the framework? Do these class structures need to follow a particular convention?

The naming of these files (e.g. "blob.x" vs. "my_funny_file_name.x") makes no difference to the framework.  The only things
that really matter to the framework are:

1) That a libtool-constructed module is placed into one of the plugin search locations (see previous emails regarding how we decide where to look for plugins). The name of the module itself is irrelevant (although conventions are in place).

2) That the module defines an externally-visible function:

CollectorImpl* CollectorFactor();

which creates a CollectorImpl instance for the collector.

Each collector defines a single class (e.g. PCSampCollector or UserTimeCollector) that inherits from CollectorImpl (which is in the framework source). CollectorImpl declares a bunch of pure virtual functions which all
inheriting classes must implement.   So you can think of CollectorImpl.hxx as the "API" between the framework and the collectors.

Graphical User Interface Plugin Information

All of the Open|SpeedShop GUI functionality is implemented using the GUI plugin mechanisms.   To create an Open|SpeedShop GUI plugin requires some knowledge about how to use the Qt GUI development toolkit.   A script is provided to help the GUI plugin developer create a basic skeletal Open|SpeedShop Qt Panel.   The GUI plugin developer then must provide the widgets and Qt GUI management routines to display what the plugin developer desires.    The basic skeletal Open|SpeedShop Qt Panel code does provide some code options which can be uncommented and modified in hopes of jump starting the process of developing additional panels.

Tips on interfacing with the rest of the Open|SpeedShop tool set are also provided along with the example code.


Graphical User Interface (GUI) Plugin Overview

GUI Plugin Outline

What are the high level steps that are required for creating a Graphical User Interface Plugin for Open|SpeedShop?   This outline provides a form of checklist to give the plugin writer an overview before delving into the details of developing the plugin.

Return to General Plugin Section

GUI Plugin Recognition

In order for a file to be recognized as a GUI plugin, it must first be in the plugin search path.   It must be either in the default plugin search path or in the path defined by the OPENSS_PLUGIN_PATH environment variable.   GUI plugins must also have the entry point function, panel_init, defined.  The Open|SpeedShop function "register_plugin" found in "libopenss-guiplugin/plugin_handler.cxx" is responsible for checking for this entry point.  If it is found the GUI plugin is registered in the master plugin list.   The function "initialize_plugins" from the same file as above calls "register_plugin".

Creating A Graphical User Interface Panel

Initial Skeletal Panel Creation - mknewpanel script

The "mknewpanel" script creates a basic Open|SpeedShop GUI Panel which can then be modified to integrate with the core functionality of Open|SpeedShop.   This script can be found in the subdirectory (from top of tree) "plugins/panels/TemplatePanel".   A "README" file in that same directory gives "mknewpanel" usage information.   The same information can be obtained by running the "mknewpanel" script without any arguments.  The script modifies the TemplatePanel sources in the TemplatePanel directory and creates a new subdirectory under "plugins/panels" using the information given to it by the plugin developer running the script.  

Here is the example output from the "mknewpanel" script creating a Panel named "Test Panel" that will appear under the Performance menu in Open|SpeedShop.  Note that the initial execution of the Linux command "ls" on the "plugins/panels" directory shows no directory named "TestPanel".
$ ls ..
CmdPanel   FPE_TracingPanel  Makefile     ManageProcessesPanel  mysed   StatsPanel
CustomExperimentPanel  HW_CounterPanel   Makefile.am  MPIPanel    pcSamplePanel  TemplatePanel
DebugPanel             IOPanel           Makefile.in  mvover                SourcePanel    UserTimePanel

Now we run the "./mknewpanel" script which creates the TestPanel subdirectory in "plugins/panels".

$  ./mknewpanel ../TestPanel TestPanel TestPanel "Test Panel" "Cntrl B" 1 Performance
PANEL_DIR=../TestPanel
PANEL_NAME=TestPanel
MENU_HEADING=TestPanel
MENU_LABEL=Test Panel
MENU_ACCEL=Cntrl B
SHOW_IMMEDIATE=Performance
PLUGIN_GROUPING=Performance
Preparing to create your new TestPanel files.
Succeeded: Finished creating files in ../TestPanel.
You'll now need to edit the panels/Makefile.am to add this new directory to the build list.
You'll also need to modify the top level configure.ac file to add the new directory to the list of directories to have makefiles created.

When we do an "ls" Linux command for the "plugins/panels" directory we now see a ""TestPanel subdirectory has been created.
$ ls ..
CmdPanel               HW_CounterPanel  Makefile.in           mysed          TemplatePanel
CustomExperimentPanel  IOPanel          ManageProcessesPanel  pcSamplePanel  TestPanel
DebugPanel             Makefile         MPIPanel              SourcePanel    UserTimePanel
FPE_TracingPanel       Makefile.am      mvover                StatsPanel

Doing a list of the files in the "TestPanel" subdirectory, we see that the "mknewpanel" script has modified the "TemplatePanel" template code and created similar files but renamed "TestPanel".

Once the GUI plugin developer modifies Open|SpeedShop's "configure.ac" (both in the top source directory) and "Makefile.am" (in plugins/panels directory) files to include the newly created panels and rebuilds Open|SpeedShop, the menu item representing the panel will be displayed under the menu that the GUI plugin developer provided to the mknewpanel script.  

The Panel Class

The new GUI panel is built off the base class Panel.cxx.  The Panel base class has several key functions.   Understanding this base functionality is critical for the GUI plugin writer to be successful in creating a GUI plugin that does what the writer wants it to.

The key classes to create a panel are:
This is typically where the new widgets for the panel are created.    See the Qt documentation for how to create Qt widgets.  When using the 'mknewpanel' script there are several examples of creating Qt widgets.  See the section Example Sections of Code.


Messages are very simple minded.   They are message object type with some data being passed around.   Many examples can be found looking in the existing panels.   Additionally messages can be broadcast to everyone in the tool, a group of panels, or to a specific panel.

An example of creating a message and broadcasting it to the group is shown below. It's the message send by hitting the "Update" button in any of the baseline experiment types. (i.e. pcSamplePanel.cxx
Here's the snippet of code:

     UpdateObject *update_object = new UpdateObject(NULL, expID, QString::null, FALSE);
     broadcast((char *)update_object, GROUP_T);

The example above creates an UpdateObject() (which is a canned message to tell everyone that receives it that they should refresh their view(s).   It then broadcasts it to everyone with the same group id.  Similarly this could be sent to every panel by changing the scope with:

     broadcast((char *)update_object, ALL_T);

If there's only one other panel your new panel is interested in communicating with you can communicate directly to its listener routine.   The StatsPanel does this to a particular source panel.

If panel writer wants to communicate with a specific panel, and your panel doesn't yet have a handle on the new panel, it can be queried.   (StatsPanel.cxx)

    // First construct the name of the panel you're looking for...
    QString name = QString("Source Panel [%1]").arg(expID);
    // Now query all panels for that particular named panel.
    Panel *sourcePanel = getPanelContainer()->findNamedPanel(getPanelContainer()-.getMasterPC(), (char *)name.ascii() );
    // Now you can send information directly to that source panel.
    UpdateObject *update_object = new UpdateObject(NULL, expID, QString::null, FALSE);
    sourcePanel->listener( (void *)updateObject );

The listener on the receiving end looks at the message coming in and if it's interested it acts upon it and returns TRUE notifying the broadcast routine that someone fielded the message.   The listener routine should return FALSE if it's not interested in the message of for whatever reason failed to be able to handle it properly.

Example Sections of Code

The newly created GUI panel code has examples of GUI code that might be interesting to an Open|SpeedShop GUI panel writer.
The following sections describe the different examples and how to enable them.

QtClass
This code snippet from the newly created "TestPanel.cxx" can be enabled and modified to use the GUI panel writer's QtDesigner created class.   When modified and enabled the GUI panel writer will see what they created in QtDesigner in the "TestPanel" display.    Use "#define QTCLASS" or remove the "#ifdef QTCLASS" and "#endif // QTCLASS" to enable this section of code.

#include <qlistview.h>  // For QListView
#include <qvaluelist.h>  // For QTextEdit in example below...
#include <qsplitter.h>  // For QSplitter in example below...
#include <chartform.hxx>  // For chart in example below...
TestPanel::TestPanel(PanelContainer *pc, const char *n, void *argument) : Panel(pc, n)
{
  setCaption("TestPanel");
  frameLayout = new QVBoxLayout( getBaseWidgetFrame(), 1, 2, getName() );

#ifdef QTCLASS
// Here's an example of  adding a class you created with QtDesigner...
// Defining QtClass (above) will enable this block.
// This block will include a class that you designed with QtDesigner.
  YourClass *ppc = new YourClass(getBaseWidgetFrame(), getName() );
  frameLayout->addWidget( ppc );
  ppc->show();
#endif // QTCLASS


TopLevel
This code snippet from the newly created "TestPanel.cxx" can be enabled and modified to create a GUI panel similar to the PCSampling and UserTime experiment panels.   When modified and enabled the GUI panel writer will Run, Pause, Update, and Terminate process control buttons created in the "TestPanel" display.    Use "#define TOPLEVEL" or remove the "#ifdef TOPLEVEL" and "#endif // TOPLEVEL" to enable this section of code.  As the comments in the code say, the GUI panel writer can look at existing panels such as the PC Sampling Experiment panel in the PCSamplePanel directory.


#ifdef TOPLEVEL
// Here's an example of a putting a name toplevel panel container in the
// panel.
// Defining TOPLEVEL (above) will enable this block.
// This block will create an outline much like the pcSamplePanel and
// UserTimePanel experiments.
// A process control panel (run, pause, update, terminate) will be created
// and a panel container to place child panels will be created.
// See pcSamplePanel for a working example.
  ProcessControlObject *pco = new ProcessControlObject(frameLayout, getBaseWidgetFrame(), (Panel *)this );

  QHBoxLayout *statusLayout = new QHBoxLayout( 0, 10, 0, "statusLayout" );

  QLabel *statusLabel = new QLabel( getBaseWidgetFrame(), "statusLabel");
  statusLayout->addWidget( statusLabel );

  QLineEdit *statusLabelText = new QLineEdit( getBaseWidgetFrame(), "statusLabelText");
  statusLabelText->setReadOnly(TRUE);
  statusLayout->addWidget( statusLabelText );

  frameLayout->addLayout( statusLayout );

  QWidget *namedPanelContainerWidget = new QWidget( getBaseWidgetFrame(),
                                        "namedPanelContainerWidget" );
  PanelContainer *topPPL = createPanelContainer( namedPanelContainerWidget, "", NULL, pc->getMasterPCList() );
  frameLayout->addWidget( namedPanelContainerWidget );

//  namedPanelContainerWidget->show();
  topPPL->show();
  topPPL->topLevel = TRUE;
#endif // TOPLEVEL



Interfacing With Open|SpeedShop - Tips and Example Information

Executing a command through Command Line Interface (CLI)

The GUI panels use the same commands that an interactive user would.  The Open|SpeedShop tool was designed in this manner to prevent the interactive command line interface tool, the batch tool, and the GUI from diverging and doing basic functions differently.   This makes coding a GUI panel slightly more intuitive.  If the GUI panel writer knows what function they would like the GUI panel to execute, then they can look at the interactive command line interface documentation and see if that function is covered by an existing command.   The Command Line Interface document can be accessed by clicking here.

This case block taken from the pcSamplePanel.cxx file illustrates how CLI commands are passed to the CLI from the GUI.

      case  PAUSE_T:
        {
        nprintf( DEBUG_MESSAGES ) ("Pause\n");
        command = QString("expPause -x %1\n").arg(expID);
        int wid = getPanelContainer()->getMainWindow()->widStr.toInt();
        cli->runSynchronousCLI(command.ascii());
        statusLabelText->setText( tr("Process Paused...") );
        }
        ret_val = 1;
        break;


Communicating with other GUI panels

See The Panel Class section for examples.

Managing process control

Create a new process control object code example from "pcSamplePanel:pcSamplePanel" in "...../plugins/panels/pcSamplePanel"

  frameLayout = new QVBoxLayout( getBaseWidgetFrame(), 1, 2, getName() );
  pco = new ProcessControlObject(frameLayout, getBaseWidgetFrame(), (Panel *)this );
  runnableFLAG = FALSE;
  pco->runButton->setEnabled(FALSE);
  pco->runButton->enabledFLAG = FALSE;


This code snippet from "pcSamplePanel::listener" in pcSamplePanel.cxx gives an example of how Open|SpeedShop manages the process control of the experiment/victim application

      case  RUN_T:
        // Put out some notification that we're about to run this.
        // Otherwise there's a deafning silence between when the user
        // clicks the run button and when the run actually begins.
        // This gives the user immediate feedback of what's taking place.
        aboutToRunFLAG = TRUE;
        pco->runButton->setEnabled(FALSE);
        pco->runButton->enabledFLAG = FALSE;
        pco->pauseButton->setEnabled(FALSE);
        pco->pauseButton->enabledFLAG = FALSE;
        pco->updateButton->setEnabled(FALSE);
        pco->updateButton->enabledFLAG = FALSE;

        command = QString("expGo -x %1\n").arg(expID);
        {
        int status = -1;
        nprintf( DEBUG_MESSAGES ) ("Run\n");
        statusLabelText->setText( tr("Process running...") );

        int wid = getPanelContainer()->getMainWindow()->widStr.toInt();
        cli->runSynchronousCLI(command.ascii());
        ret_val = 1;
        }
        break;
      case  PAUSE_T:
        {



Example Graphical User Interface Plugin

The example graphical user interface plugin is the plugin that is used as the focus of the "mknewpanel" script, the TemplatePanel is found in the "<top_srcdir>/plugins/panels/TemplatePanel" directory in the Open|SpeedShop source tree.

Interactive Command Line Interface Plugin Information

tbd

Interactive Command Line Interface (CLI) Plugin Overview

tbd

Interactive Command Line Interface (CLI) Plugin Outline

tbd

Return to General Plugin Section

Top