Once you are familiar with the PCP and PMDA frameworks, you can quickly implement a new PMDA with only a few data structures and functions. This is covered in far greater detail in the Performance Co-Pilot Programmer's Guide.
A PMDA is responsible for a set of performance metrics, in the sense that it must respond to requests from pmcd(1) for information about performance metrics, instance domains, and instantiated values. The pmcd(1) process generates requests on behalf of performance tools that make requests using PMAPI(3) routines.
This man page contains sections of the simple PMDA which is located at $PCP_PMDAS_DIR/simple.
The preferred approach is for a separate process (daemon) to communicate with pmcd(1) using the Performance Data Units (PDU) Inter-Process Communication (IPC) protocol.
All PMDAs are launched and controlled by the pmcd(1) process on the local host. The requests from the clients are received by pmcd(1) and forwarded to the appropriate PMDAs. Responses, when required, are returned through pmcd(1) to the clients. The requests (PDUs) that may be sent to a PMDA from pmcd(1) are PDU_FETCH, PDU_PROFILE, PDU_INSTANCE_REQ, PDU_DESC_REQ, PDU_TEXT_REQ and PDU_RESULT.
As most of the procedural interface is identical for all PMDAs, they are provided as part of this support library (pmdaProfile(3), pmdaFetch(3), pmdaInstance(3), pmdaDesc(3), pmdaText(3) and pmdaStore(3)). However, these routines require access to the pmdaInterface state information so it must be correctly initialized using pmdaConnect(3), pmdaDaemon(3), pmdaOpenLog(3), pmdaDSO(3), pmdaGetOpt(3) and pmdaInit(3).
Every instance requires a unique integer identifier and a unique name, as defined by the structure pmdaInstid:
/*
* Instance description: index and name
*/
typedef struct {
int i_inst; /* internal instance identifier */
char *i_name; /* external instance identifier */
} pmdaInstid;
An instance domain requires its own unique identification (pmInDom), the number of instances the domain represents, and a pointer to an array of instance descriptions. This is defined in the structure pmdaIndom:
/*
* Instance domain description: unique instance id,
* number of instances in this domain, and the list of
* instances (not null terminated).
*/
typedef struct {
pmInDom it_indom; /* indom, filled in */
int it_numinst; /* number of instances */
pmdaInstid *it_set; /* instance identifiers */
} pmdaIndom;
The simple PMDA has one instance domain for simple.color with three instances (red, green and blue), and a second instance domain for simple.now with instances which can be specified at run-time. These instance domains are defined as:
static pmdaInstid _color[] = {
{ 0, "red" }, { 1, "green" }, { 2, "blue" }
};
static pmdaInstid *_timenow = NULL;
static pmdaIndom indomtab[] = {
#define COLOR_INDOM 0
{ COLOR_INDOM, 3, _color },
#define NOW_INDOM 1
{ NOW_INDOM, 0, NULL },
};
The preprocessor macros COLOR_INDOM and NOW_INDOM are used in the metric description table to identify the instance domains of individual metrics. These correspond to the serial value in the instance domain pmInDom structure (the domain field is set by pmdaInit(3) at run-time). The serial value must be unique for each instance domain within the PMDA.
The indom table shown above which is usually passed to pmdaInit(3) does not need to be created if one wants to write one's own Fetch and Instance functions. See pmdaInit(3) for more details.
simple {
numfetch 253:0:0
color 253:0:1
time
now 253:2:4
}
simple.time {
user 253:1:2
sys 253:1:3
}
The domain number of 253 is obtained from $PCP_VAR_DIR/pmns/stdpmid. New PMDAs should specify a unique domain number in this file, and obtain the number during installation. This allows the domain number to change by modifying only the file $PCP_VAR_DIR/pmns/stdpmid.
The simple.time and simple.now metrics are defined in separate clusters to the other metrics which allows a PMDA to support more than 1024 metrics, as well as grouping similar metrics together. Therefore, the item numbers for a new cluster may be identical to the item numbers in other clusters. The simple PMDA continues to increment the item numbers to permit direct mapping (see pmdaInit(3)).
The namespace file should be installed and removed with the agent using pmnsadd(1) and pmnsdel(1). See the later sections on INSTALLATION and REMOVAL.
A simple ASCII namespace can be constructed by creating a file similar to $PCP_PMDAS_DIR/simple/root:
/*
* fake "root" for validating the local PMNS subtree
*/
#include "$PCP_VAR_DIR/pmns/stdpmid"
root { simple }
#include "pmns"
and can be referred to with the -n option in most PCP tools.
/*
* Metric description: handle for extending description,
* and the description.
*/
typedef struct {
void* m_user; /* for users external use */
pmDesc m_desc; /* metric description */
} pmdaMetric;
The simple PMDA defines the metrics as:
static pmdaMetric metrictab[] = {
/* numfetch */
{ (void *)0,
{ PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
{ 0,0,0,0,0,0} }, },
/* color */
{ (void *)0,
{ PMDA_PMID(0,1), PM_TYPE_32, COLOR_INDOM, PM_SEM_INSTANT,
{ 0,0,0,0,0,0} }, },
/* time.user */
{ (void*)0,
{ PMDA_PMID(1,2), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_COUNTER,
{ 0, 1, 0, 0, PM_TIME_SEC, 0 } }, },
/* time.sys */
{ (void*)0,
{ PMDA_PMID(1,3), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_COUNTER,
{ 0, 1, 0, 0, PM_TIME_SEC, 0 } }, },
/* now */
{ NULL,
{ PMDA_PMID(2,4), PM_TYPE_U32, NOW_INDOM, PM_SEM_INSTANT,
{ 0,0,0,0,0,0 } }, },
};
The macro PMDA_PMID (defined in /usr/include/pcp/pmda.h) is used to specify each metric's cluster and unit number in the __pmID_int structure defined in /usr/include/pcp/impl.h. As with instance domains, the domain field is set by pmdaInit(3) at run-time, however, the default domain is assumed to be defined by the PMDA in the macro MYDOMAIN.
The metric table shown above which is usually passed to pmdaInit(3) does not need to be created if one wants to write one's own Fetch and Descriptor functions. See pmdaInit(3) for more details.
The simple PMDA uses its own store and fetch callback. simple_fetch() calls pmdaFetch(3) which requires a callback to be set with pmdaSetFetchCallBack(3) as can be seen in $PCP_PMDAS_DIR/simple/simple.c.
The flag _isDSO is used to determine if the PMDA is a daemon or a DSO so that the correct initialization routine, pmdaDaemon(3) or pmdaDSO(3), is called.
The pmdaInterface structure must be completely defined by the daemon PMDA. The function pmdaDaemon(3) can be called at the start of main() to set most of these fields. Command line parsing can be simplified by using pmdaGetOpt(3), which is similar to getopt(2), but extracts a common set of options into the pmdaInterface structure. stderr can be mapped to a log file using pmdaOpenLog(3) to simplify debugging and error messages. The connection to pmcd can be made with pmdaConnect(3) and the loop which handles the incoming PDUs, pmdaMain(3), should be the last function called. This can be seen in $PCP_PMDAS_DIR/simple/simple.c.
The simple_init() routine is common to an agent that can be run as both a Daemon and DSO PMDA.
The status field of the pmdaInterface structure should be zero after pmdaDaemon, pmdaDSO, pmdaGetOpt, pmdaConnect and pmdaInit are called. A value less than zero indicates that initialization has failed.
Some error messages that are common to most functions in this library are:
Due to changes to the PMAPI(3) and PMDA(3) API in the PCP 2.0 release, as described in the product release notes, PMDAs built using PCP 2.0 must specify PMDA_INTERFACE_2 or later and link with libpcp_pmda.so.2 and libpcp.so.2. Pre-existing Daemon PMDAs specifying PMDA_PROTOCOL_1 will continue to function using the backwards compatible libpcp_pmda.so.1 and libpcp.so.1 libraries and may be recompiled using the headers installed in /usr/include/pcp1.x/ without any modification. These backwards compatible headers and libraries are contained in the pcp.sw.compat subsystem.
For a complete description of the pcp_pmda library and the PMDA development process, refer to the Insight book Performance Co-Pilot Programmer's Guide.