
Plugin Creation Guide
Developer
Beta Release
November 13, 2005
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:
- Three modes of operation: batch, interactive command line
interface, and
graphical user interface.
- Multiple platform single system image (SSI), traditional cluster,
and SGI super cluster support.
- Support for large numbers of processes, threads, and ranks.
- Ability for users to create their own experiments via a well
defined plugin facility.
- Ability to attach to running processes, threads, and ranks to
gather performance data.
- Ability to view performance data for all of application's
lifetime or smaller time slices.
- Ability to view performance data on demand (while the experiment
is running).
- Ability to save and restore performance experiment data and
symbol information to do post experiment performance analysis.
- Ability to compare performance results for processes, threads,
ranks, or to compare current experiment results with previous
experiment results.
- GUI Wizard facility and context sensitive help to aid in tools
usability.
- CLI help facility which lists the CLI commands, syntax, and
typical usage.
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.

Runtime/Server
Component 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:
- Performance data collector plugins
- Collector Runtime - run time plugin - loaded into victim
application
- Collector Plugin - client side plugin - loaded into
Open|SpeedShop client tool
- Graphical User Interface plugins
- Command Line Interface plugins
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
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:
- The file is in the default plugin directory path or a directory
specified by the environment variable: OPENSS_PLUGIN_PATH.
- The file is a dynamically linked object (dso) - can dlopen the
file.
- A specific named entry point exists in the dso and is callable.
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:
- Identify and evaluate the performance data you
want to gather.
- Can it be mapped back to the source line numbers or function
definitions?
- What's the goal? Identify the metrics - the performance
data desired to be output by the Open|SpeedShop tool.
- Determine what will be in the performance data structure (data
blob) in the victim application.
- Analyze and define the data blob structure
- Too much information (large data blobs) might impact the
victim applications behavior (memory growth).
- Sending lots of small data blobs could cause network
performance issues.
- Create a bare bones directory using the Example Plugin directory
as a model or copy the Example Plugin directory and modify the files
accordingly
- Note: The Example Plugin is the simplest plugin code
found in the Open|SpeedShop source directory. However, if there
is an already implemented plugin that is closer in functionality to
what the plugin writer is implementing, then starting with that plugin
makes more sense than starting with the Example Plugin.
- Proceed on the actual implementation of the collector plugin
- Create base files using Example Plugin files as model/guide
- Implement the plugin recognition portion of the plugin
- Implement the performance data definitions (data blob
structure) for the data gathered by the collector runtime.
- Implement the runtime code that gathers the performance data
defined by the data blob (runtime.c file)
- Implement the six virtual functions that are required for the
client portion of the collector.
- Compile and build plugins using the Makefile.am file as a guide
or modify the Makefile.am file
- To build in place modify the <top_srcdir>/configure.ac
and <top_srcdir>/Makefile.am files to include the new collector
directory.
- Test and refine the new collector
- Point OPENSS_PLUGIN_PATH to the directory with the new
collector plugins if not installed in the default location.
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:
- Exclusive time - Time spent in this function only
- Inclusive time - Time spent in this function and all functions it
calls.
- Numbers of event occurrences, such as hardware counter events
Examples of parameters already supported by Open|SpeedShop are:
- Sampling Rate in milliseconds
- Trigger Count Number of Events (the number of event occurrences
before a sample is taken)
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.

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:
- Try not to use too much memory because the blob is contained in
the victim application.
- The blob should not be too small because having to send many
small blobs across the network could slow the network down.
- 16k bytes have been found to be a reasonable size. PC
Sampling currently uses 8k bytes as the blob size.
- 16k bytes is the high limit on the buffer size able to be sent in
a message from the Open|SpeedShop runtime to the client tool.
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).
- Setting up a timer to trigger the gathering of performance data
(see Timer event handler for
example code)
- Start Sampling - enable Open|SpeedShop to gather performance data
(see Start Sampling for example code)
- Stop Sampling - disable Open|SpeedShop's gathering of performance
data (see Stop Sampling for example code)
- Using the PAPI overflow mechanism
- Obtaining a callstack entry
- Getting a program counter (PC) address
- Putting the collected data into a data blob
- Compressing the data to increase the bandwidth when sending to
the tool host (collector plugin client)
- Sending the data to the tool host when the buffer (blob) fills up
and on program termination
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:
- Decoding the data blobs via XDR translation
- Open|SpeedShop timer functions
- Send the data blobs from the victim application to the
Open|SpeedShop client
- Get the program counter address from a given context
- PAPI support functions
- Access to libunwind 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.
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:
- Start Collecting Experiment Performance data (metrics)
- Stop Collecting
- Get the Collector's Metric Values
- Set the Collector Parameter
- Get the Collector's Default Parameters
- Get the Collector's Parameter(s)
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:
- collector - The collector that will be starting data collection
- thread - The thread for which to start data collection.
What's needed
in the
function body:
- Assemble and encode collector arguments to the collectors start
collecting routine.
- Make decision on where to insert your instrumentation code
(performance data gathering code). The options are:
- Execute this function now
- Execute at entry to a function
- Execute at exit of a function
- Execute another function in place of a specified function
- Insert instrumentation to:
- Execute collector's stop_sampling routine when we enter exit()
for the thread.
- Execute collector's start_sampling routine in the thread
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:
- collector - The collector that will be stopping data collection
- 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:
- Insert instrumentation to execute the collector's stop_sampling
routine in the thread
- Remove the instrumentation that was inserted by startCollecting:
- Execute collector's stop_sampling routine when we enter exit()
for the thread.
- Execute collector's start_sampling routine in the thread
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:
- metric - Unique identifier of the metric
- extent - Extent of the performance data blob
- blob - Blob containing the performance data
- subextents - The subextents for which to get the values
- 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:
- Process the collector's metric value(s) by iterating over the
blob containing the raw performance data.
- Check input parameters for validity
- Decode the data blob using XDR
- Using subextents to find out if there are metrics to return
by
iterating over the samples in the data blob
- Do collector specific calculations for time, event counts, or
other
- 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:
- parameter - Unique identifier of the input parameter to be set
- ptr - Untyped pointer to the parameter value
Return
values:
- 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:
- Decode the blob containing the parameter values
- Set the "collector specific" parameter based on the input
- 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(¶meters, 0, sizeof(parameters));
data.getXDRDecoding(reinterpret_cast<xdrproc_t>(xdr_example_parameters),
¶meters);
// 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),
¶meters);
}
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:
- 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:
- Setup an empty parameter structure
- Set the default parameters
- 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(¶meters, 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),
¶meters);
}
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:
- parameter - Unique identifier of the input parameter to be get.
- data - blob containing the parameter values for the collector
Return values:
- ptr - Untyped pointer to the parameter value
Decisions and
what's needed
in the function body:
- Decode the data
blob containing the parameter values using XDR (External Data
Representation)
- 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(¶meters, 0, sizeof(parameters));
data.getXDRDecoding(reinterpret_cast<xdrproc_t>(xdr_example_parameters),
¶meters);
// 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:
- runtime.c -
Contains code responsible for gathering
the performance information from the victim application
See the section describing the
Collector runtime for
how to implement the necessary functions for runtime.c.
- Makefile.am - Has
the makefile definitions to compile and
create the two dynamic shared objects representing the experiment
collector plugin
Use this file as guide to create the
new collector plugin.
- ExampleCollector.cxx
- Contains code that is used on
the client side to decode the data blobs, control collecting, and
manage
parameters. This file also contains the collector's factory
method that is the key to plugin recognition.
- ExampleCollector.hxx -
Header file for ExampleCollector.cxx
See the section describing the
Collector Plugin (client)
for how to implement the necessary functions for the client side of the
collector plugin.
- blobs.x - Binary Large
Object definition of the data blob which
is the gathered data structure definition. rpcgen is run on
this file creating "C" language data definition and data packaging code.
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_INSTALL_DIR/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.
- Think about the data that the plugin needs to display - Can this
be done with the existing plugins?
- If a GUI plugin is really needed, decide if there is an existing
GUI plugin or if the Example GUI plugin can be used as a model.
- Use the mknewpanel script to create a new directory and skeletal
panel
- Read the information in this document regarding example code and
apply it to the new panel if appropriate.
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.
- In "configure.ac" add the
directory path to the newly created GUI plugin anywhere after the list
of "plugins/panels/<panel name>/Makefile"
entries. In this example, add
"plugins/panels/TestPanel/Makefile".
- In "Makefile.am" add the name of the newly
created GUI plugin panel directory into the "SUBDIRS" list.
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:
- The new Panel Constructor
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.
- The next two most important member functions are related to
communication between panels via the "::listener" and the
"::broadcast" functions.
- The listener function is used by other panels to communicate with
this panel.
- The broadcast function is used to communicate with other panels.
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.
- Another interesting member function is the "::menu"
callback. This simply overloads the Qt contextSensitiveMenu
signal to ping your menu routine for localize menus that you might want
appended to the menu list. There are numerous
examples of this in the panels.
See pcSamplePanel::menu for a simple example.
- There are other more obscurely used functions in
Panel.[ch]xx. They are not detailed here.
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