PMDAMAIN(3)

πŸ“¦
version 7.2.2 Β· 4 versions
, in arch rolling, centos-stream 10, fedora 43, fedora 44, ubuntu resolute, ubuntu stonking

✨ NAME ✨

hi bestie 🫢 welcome to PMDAMAIN(3), the page about generic PDU processing for a PMDA. it covers:

  • pmdaMain
  • pmdaGetContext
  • pmdaSetResultCallBack
  • pmdaSetCheckCallBack
  • pmdaSetDoneCallBack
  • pmdaSetEndContextCallBack

✨ C SYNOPSIS ✨

#include <pcp/pmapi.h>
#include <pcp/pmda.h>

void pmdaMain(pmdaInterface *dispatch);
void pmdaSetCheckCallBack(pmdaInterface *dispatch,
                          pmdaCheckCallBack callback);
void pmdaSetDoneCallBack(pmdaInterface *dispatch,
                         pmdaDoneCallBack callback);
void pmdaSetResultCallBack(pmdaInterface *dispatch,
                           pmdaResultCallBack callback);
void pmdaSetEndContextCallBack(pmdaInterface *dispatch,
                               pmdaEndContextCallBack callback);
int pmdaGetContext(void);

cc ... -lpcp_pmda -lpcp

✨ DESCRIPTION ✨

so if you're building a Performance Metric Domain Agent β€” that's a PMDA(3) β€” that talks to pmcd(1) using the binary PDU protocols, then pmdaMain is literally your whole main loop, handed to you on a platter. it's a generic implementation of the PDU-driven main loop. iconic behavior, no notes πŸ’…

dispatch tells it how to process each incoming PDU. it's a vector of function pointers, one per request PDU type, exactly like the DSO interface for a PMDA uses. and it looks like this:

/*
 * Interface Definitions for PMDA Methods
 */
typedef struct {
    int domain;         /* set/return performance metrics domain id here */
    struct {
        unsigned int    pmda_interface: 8; /* PMDA DSO interface version */
        unsigned int    pmapi_version : 8; /* PMAPI version */
        unsigned int    flags : 16;        /* optional feature flags */
    } comm;             /* set/return communication and version info */
    int status;         /* return initialization status here */

    union {
        struct {                              /* PMDA_INTERFACE_2 or _3 */
            pmdaExt *ext;
            int (*profile)(pmProfile *, pmdaExt *);
            int (*fetch)(int, pmID *, pmResult **, pmdaExt *);
            int (*desc)(pmID, pmDesc *, pmdaExt *);
            int (*instance)(pmInDom, int, char *, pmInResult **, pmdaExt *);
            int (*text)(int, int, char **, pmdaExt *);
            int (*store)(pmResult *, pmdaExt *);
        } two, three;

        struct {                              /* PMDA_INTERFACE_4 or _5 */
            pmdaExt *ext;
            int     (*profile)(pmProfile *, pmdaExt *);
            int     (*fetch)(int, pmID *, pmResult **, pmdaExt *);
            int     (*desc)(pmID, pmDesc *, pmdaExt *);
            int     (*instance)(pmInDom, int, char *, pmInResult **, pmdaExt *);
            int     (*text)(int, int, char **, pmdaExt *);
            int     (*store)(pmResult *, pmdaExt *);
            int     (*pmid)(char *, pmID *, pmdaExt *);
            int     (*name)(pmID, char ***, pmdaExt *);
            int     (*children)(char *, int, char ***, int **, pmdaExt *);
        } four, five;

        struct {                              /* PMDA_INTERFACE_6 */
            pmdaExt *ext;
            int     (*profile)(pmProfile *, pmdaExt *);
            int     (*fetch)(int, pmID *, pmResult **, pmdaExt *);
            int     (*desc)(pmID, pmDesc *, pmdaExt *);
            int     (*instance)(pmInDom, int, char *, pmInResult **, pmdaExt *);
            int     (*text)(int, int, char **, pmdaExt *);
            int     (*store)(pmResult *, pmdaExt *);
            int     (*pmid)(char *, pmID *, pmdaExt *);
            int     (*name)(pmID, char ***, pmdaExt *);
            int     (*children)(char *, int, char ***, int **, pmdaExt *);
            int     (*attribute)(int, int, const char *, int, pmdaExt *);
        } six;
    } version;

} pmdaInterface;

this struct got extended over time to hold all the multiple interface versions that evolved, so yes it's a little chunky, we love her anyway 🌸

for pmdaMain, both dispatch->domain and dispatch->status are just... ignored. not their moment. the field that actually matters is comm.pmda_interface β€” that's what decides which interface the PMDA is using:

  • set it to PMDA_INTERFACE_2 or PMDA_INTERFACE_3 and pmdaMain is forced to use the callbacks in the version.two or version.three struct.
  • set it to PMDA_INTERFACE_4 or PMDA_INTERFACE_5 and pmdaMain uses the callbacks in version.four or version.five.
  • set it to PMDA_INTERFACE_6 and pmdaMain uses the callbacks in version.six.
bestie, any other value = error and pmdaMain just terminates. no vibe check survived, it's over πŸ’€

ngl the tea here is that using dispatch as the interface between pmcd(1) and the methods of the PMDA means each PMDA can be implemented like it was a DSO all along, with pmdaMain being the convenient little wrapper that converts from the DSO interface to the binary PDU (daemon PMDA) interface. it's giving translator, it's giving bridge πŸ¦‹

pmdaMain runs as a continuous loop, and it only returns when an end of file shows up on the PDU input file descriptor. that's it. that's the exit condition. periodt.


✨ CALLBACKS ✨

besides the individual PDU processing callbacks β€” pmdaProfile(3), pmdaFetch(3), pmdaDesc(3), pmdaInstance(3), pmdaText(3), pmdaStore(3), pmdaPMID(3), pmdaName(3), pmdaChildren(3), and pmdaAttribute(3) β€” there are three extra ones that can affect or inform all PDU processing inside a PMDA: check, done and end. set them with pmdaSetCheckCallBack, pmdaSetDoneCallBack and pmdaSetEndContextCallBack.

the check callback πŸŽ€

if it's not null, check gets called after each PDU is received β€” but before it was processed.

if check returns a value less than zero (typically PM_ERR_AGAIN), then the PDU processing gets skipped, and in most cases that function value is sent back to pmcd(1) as an error PDU. this is the move for PMDAs that need some kind of deferred connection or reconnect protocols for their underlying sources of performance metrics β€” a DBMS, for example.

but heads up: the error indication from check is not passed back to pmcd(1) in the cases where no acknowledgment is expected, e.g. for a PDU_PROFILE. πŸ™ˆ

the done callback πŸŽ€

if it's not null, done gets called after each PDU is sent.

the end callback πŸŽ€

end lets a PMDA keep track of state for individual clients that are asking it to perform actions (aka PDU processing).

using pmdaGetContext, a PMDA can figure out, at any point, an integer identifier that uniquely identifies the client tools at the remote end of PMCD β€” for local context modes, that identifier is always zero. this is so important for handling event metrics, where each event must be propagated once only to each interested client. it also underlies the mechanism that passes connection information to the PMDA, like the credentials (user and group identifiers) for the client tool. main character energy for real πŸ‘‘

the result callback πŸŽ€

one final callback mechanism exists, for handling the pmResult built for a PDU_RESULT in response to a PDU_FETCH request.

by default, pmdaMain will free the pmResult once the result has been sent to pmcd(1). for some PMDAs that's just not the vibe β€” maybe the pmResult is statically allocated, or it's a hybrid of pinned PDU buffer information and dynamically allocated information. in those cases, use pmdaSetResultCallBack to define an alternative callback from pmdaMain. slay ✨


✨ DIAGNOSTICS ✨

these messages may be appended to the PMDA's log file:

  • PMDA interface version interface not supported β€” the interface version is not supported by pmdaMain.
  • Unrecognized pdu type β€” the PMDA received a PDU from pmcd that it does not recognize. this may indicate that the pmcd process is using a more advanced interface than pmdaMain.

and lowkey a fun one: if the PMAPI(3) debugging control options have the libpmda option set, then every single PDU that is received gets reported in the PMDA's log file. logging girlies stay winning πŸ’œ


✨ SEE ALSO ✨

pmcd(1), PMAPI(3), PMDA(3), pmdaProfile(3), pmdaFetch(3), pmdaDesc(3), pmdaInstance(3), pmdaText(3), pmdaStore(3), pmdaPMID(3), pmdaName(3), pmdaChildren(3), and pmdaAttribute(3).


okay that's the whole page bestie, go forth and write your PMDA πŸ’– this was the Performance Co-Pilot PCP PMDAMAIN(3) era, thanks for coming to my ted talk, mwah πŸŽ€πŸ¦‹βœ¨

πŸ“¦ where this page comes from