Product SiteDocumentation Site

2.5. PMDA Interface

This section describes an interface for the request handling callbacks in a PMDA. This interface is used by PMCD for communicating with DSO PMDAs and is also used by daemon PMDAs with pmdaMain.

2.5.1. Overview

Both daemon and DSO PMDAs must handle multiple request types from PMCD. A daemon PMDA communicates with PMCD using the PDU protocol, while a DSO PMDA defines callbacks for each request type. To avoid duplicating this PDU processing (in the case of a PMDA that can be installed either as a daemon or as a DSO), and to allow a consistent framework, pmdaMain can be used by a daemon PMDA as a wrapper to handle the communication protocol using the same callbacks as a DSO PMDA. This allows a PMDA to be built as both a daemon and a DSO, and then to be installed as either.
To further simplify matters, default callbacks are declared in <pcp/pmda.h>:
  • pmdaFetch
  • pmdaProfile
  • pmdaInstance
  • pmdaDesc
  • pmdaText
  • pmdaStore
  • pmdaPMID
  • pmdaName
  • pmdaChildren
  • pmdaAttribute
  • pmdaLabel
Each callback takes a pmdaExt structure as its last argument. This structure contains all the information that is required by the default callbacks in most cases. The one exception is pmdaFetch, which needs an additional callback to instantiate the current value for each supported combination of a performance metric and an instance.
Therefore, for most PMDAs all the communication with PMCD is automatically handled by functions in libpcp.so and libpcp_pmda.so.

2.5.1.1. Trivial PMDA

The trivial PMDA uses all of the default callbacks as shown in Example 2.20, “Request Handling Callbacks in the Trivial PMDA”. The additional callback for pmdaFetch is defined as trivial_fetchCallBack:

Example 2.20. Request Handling Callbacks in the Trivial PMDA

static int
trivial_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
{
   __pmID_int      *idp = (__pmID_int *)&(mdesc->m_desc.pmid);

   if (idp->cluster != 0 || idp->item != 0)
       return PM_ERR_PMID;
   if (inst != PM_IN_NULL)
       return PM_ERR_INST;
   atom->l = time(NULL);
   return 0;
}
This function checks that the PMID and instance are valid, and then places the metric value for the current time into the pmAtomValue structure.
The callback is set up by a call to pmdaSetFetchCallBack in trivial_init. As a rule of thumb, the API routines with named ending with CallBack are helpers for the higher PDU handling routines like pmdaFetch. The latter are set directly using the PMDA Interface Structures, as described in Section 2.5.2, “PMDA Structures”.

2.5.1.2. Simple PMDA

The simple PMDA callback for pmdaFetch is more complicated because it supports more metrics, some metrics are instantiated with each fetch, and one instance domain is dynamic. The default pmdaFetch callback, shown in Example 2.21, “Request Handling Callbacks in the Simple PMDA”, is replaced by simple_fetch in simple_init, which increments the number of fetches and updates the instance domain for INDOM_NOW before calling pmdaFetch:

Example 2.21. Request Handling Callbacks in the Simple PMDA

static int
simple_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda)
{
    numfetch++;
    simple_timenow_check();
    simple_timenow_refresh();
    return pmdaFetch(numpmid, pmidlist, resp, pmda);
}
The callback for pmdaFetch is defined as simple_fetchCallBack. The PMID is extracted from the pmdaMetric structure, and if valid, the appropriate field in the pmAtomValue structure is set. The available types and associated fields are described further in Section 3.4, “Performance Metric Descriptions” and Example 3.18, “ pmAtomValue Structure”.

Note

Note that PMID validity checking need only check the cluster and item numbers, the domain number is guaranteed to be valid and the PMDA should make no assumptions about the actual domain number being used at this point.
The simple.numfetch metric has no instance domain and is easily handled first as shown in Example 2.22, “ simple.numfetch Metric”:

Example 2.22.  simple.numfetch Metric

static int
simple_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
{
   int             i;
   static int      oldfetch;
   static double   usr, sys;
   __pmID_int      *idp = (__pmID_int *)&(mdesc->m_desc.pmid);

   if (inst != PM_IN_NULL &&
       !(idp->cluster == 0 && idp->item == 1) &&
       !(idp->cluster == 2 && idp->item == 4))
       return PM_ERR_INST;
   if (idp->cluster == 0) {
       if (idp->item == 0) {                   /* simple.numfetch */
           atom->l = numfetch;
       }
In Example 2.23, “ simple.color Metric”, the inst parameter is used to specify which instance is required for the simple.color metric:

Example 2.23.  simple.color Metric

       else if (idp->item == 1) {              /* simple.color */
            switch (inst) {
            case 0:                             /* red */
                red = (red + 1) % 256;
                atom->l = red;
                break;
            case 1:                             /* green */
                green = (green + 1) % 256;
                atom->l = green;
                break;
            case 2:                             /* blue */
                blue = (blue + 1) % 256;
                atom->l = blue;
                break;
            default:
                return PM_ERR_INST;
            }
       }
       else
           return PM_ERR_PMID;
In Example 2.24, “ simple.time Metric”, the simple.time metric is in a second cluster and has a simple optimization to reduce the overhead of calling times twice on the same fetch and return consistent values from a single call to times when both metrics simple.time.user and simple.time.sys are requested in a single pmFetch. The previous fetch count is used to determine if the usr and sys values should be updated:

Example 2.24.  simple.time Metric

   else if (idp->cluster == 1) {               /* simple.time */
       if (oldfetch < numfetch) {
           __pmProcessRunTimes(&usr, &sys);
           oldfetch = numfetch;
       }
       if (idp->item == 2)                     /* simple.time.user */
           atom->d = usr;
       else if (idp->item == 3)                /* simple.time.sys */
           atom->d = sys;
       else
           return PM_ERR_PMID;
    }
In Example 2.25, “ simple.now Metric”, the simple.now metric is in a third cluster and uses inst again to select a specific instance from the INDOM_NOW instance domain. The values associated with instances in this instance domain are managed using the pmdaCache(3) helper routines, which provide efficient interfaces for managing more complex instance domains:

Example 2.25.  simple.now Metric

    else if (idp->cluster == 2) {
        if (idp->item == 4) {                 /* simple.now */
            struct timeslice *tsp;
            sts = pmdaCacheLookup(*now_indom, inst, NULL, (void *)&tsp);
            if (sts != PMDA_CACHE_ACTIVE) {
                if (sts < 0)
                    pmNotifyErr(LOG_ERR, "pmdaCacheLookup failed: inst=%d: %s",
                                  inst, pmErrStr(sts));
                return PM_ERR_INST;
            }
            atom->l = tsp->tm_field;
        }
        else 
            return PM_ERR_PMID;
    }

2.5.1.3.  simple_store in the Simple PMDA

The simple PMDA permits some of the metrics it supports to be modified by pmStore as shown in Example 2.26, “ simple_store in the Simple PMDA”. For additional information, see the pmstore(1) and pmStore(3) man pages.

Example 2.26.  simple_store in the Simple PMDA

The pmdaStore callback (which returns PM_ERR_PERMISSION to indicate no metrics can be altered) is replaced by simple_store in simple_init. This replacement function must take the same arguments so that it can be assigned to the function pointer in the pmdaInterface structure.
The function traverses the pmResult and checks the cluster and unit of each PMID to ensure that it corresponds to a metric that can be changed. Checks are made on the values to ensure they are within range before being assigned to variables in the PMDA that hold the current values for exported metrics:
static int
simple_store(pmResult *result, pmdaExt *pmda)
{
    int         i, j, val, sts = 0;
    pmAtomValue av;
    pmValueSet  *vsp = NULL;
    __pmID_int  *pmidp = NULL;

    /* a store request may affect multiple metrics at once */
    for (i = 0; i < result->numpmid; i++) {
        vsp = result->vset[i];
        pmidp = (__pmID_int *)&vsp->pmid;
        if (pmidp->cluster == 0) {  /* storable metrics are cluster 0 */
            switch (pmidp->item) {
            case 0:                           /* simple.numfetch */
                val = vsp->vlist[0].value.lval;
                if (val < 0) {
                    sts = PM_ERR_SIGN;
                    val = 0;
                }
                numfetch = val;
                break;
            case 1:                             /* simple.color */
                /* a store request may affect multiple instances at once */
                for (j = 0; j < vsp->numval && sts == 0; j++) {
                    val = vsp->vlist[j].value.lval;
                    if (val < 0) {
                        sts = PM_ERR_SIGN;
                        val = 0;
                    } if (val > 255) {
                        sts = PM_ERR_CONV;
                        val = 255;
                    }
The simple.color metric has an instance domain that must be searched because any or all instances may be specified. Any instances that are not supported in this instance domain should cause an error value of PM_ERR_INST to be returned as shown in Example 2.27, “ simple.color and PM_ERR_INST Errors”:

Example 2.27.  simple.color and PM_ERR_INST Errors

                       switch (vsp->vlist[j].inst) {
                       case 0:                         /* red */
                           red = val;
                           break;
                       case 1:                         /* green */
                           green = val;
                           break;
                       case 2:                         /* blue */
                           blue = val;
                           break;
                       default:
                           sts = PM_ERR_INST;
                       }
Any other PMIDs in cluster 0 that are not supported by the simple PMDA should result in an error value of PM_ERR_PMID as shown in Example 2.28, “ PM_ERR_PMID Errors”:

Example 2.28.  PM_ERR_PMID Errors

                default:
                    sts = PM_ERR_PMID;
                    break;
            }
        }
Any metrics that cannot be altered should generate an error value of PM_ERR_PERMISSION, and metrics not supported by the PMDA should result in an error value of PM_ERR_PMID as shown in Example 2.29, “ PM_ERR_PERMISSION and PM_ERR_PMID Errors”:

Example 2.29.  PM_ERR_PERMISSION and PM_ERR_PMID Errors

        else if ((pmidp->cluster == 1 &&
                 (pmidp->item == 2 || pmidp->item == 3)) ||
                 (pmidp->cluster == 2 && pmidp->item == 4)) {
            sts = PM_ERR_PERMISSION;
            break;
        }
        else {
            sts = PM_ERR_PMID;
            break;
        }
    }
    return sts;
}
The structure pmdaExt pmda argument is not used by the simple_store function above.

Note

When using storable metrics, it is important to consider the implications. It is possible pmlogger is actively sampling the metric being modified, for example, which may cause unexpected results to be persisted in an archive. Consider also the use of client credentials, available via the attribute callback of the pmdaInterface structure, to appropriately limit access to any modifications that might be made via your storable metrics.

2.5.1.4. Return Codes for pmdaFetch Callbacks

In PMDA_INTERFACE_1 and PMDA_INTERFACE_2, the return codes for the pmdaFetch callback function are defined:
Value
Meaning
< 0
Error code (for example, PM_ERR_PMID, PM_ERR_INST or PM_ERR_AGAIN)
0
Success
In PMDA_INTERFACE_3 and all later versions, the return codes for the pmdaFetch callback function are defined:
Value
Meaning
< 0
Error code (for example, PM_ERR_PMID, PM_ERR_INST)
0
Metric value not currently available
> 0
Success