summaryrefslogtreecommitdiff
path: root/src/pmdas/txmon/txmon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdas/txmon/txmon.c')
-rw-r--r--src/pmdas/txmon/txmon.c383
1 files changed, 383 insertions, 0 deletions
diff --git a/src/pmdas/txmon/txmon.c b/src/pmdas/txmon/txmon.c
new file mode 100644
index 0000000..bdfbf7a
--- /dev/null
+++ b/src/pmdas/txmon/txmon.c
@@ -0,0 +1,383 @@
+/*
+ * txmon PMDA
+ *
+ * Copyright (c) 2012 Red Hat.
+ * Copyright (c) 1995-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <pcp/pmapi.h>
+#include <pcp/impl.h>
+#include <pcp/pmda.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include "domain.h"
+
+#define CACHELINE 32
+#define RND_TO_CACHE_LINE(x) (((x + CACHELINE - 1) / CACHELINE) * CACHELINE)
+
+/*
+ * txmon PMDA
+ *
+ * This PMDA is a sample that illustrates how a PMDA might be
+ * constructed with libpcp_pmda to use a System V shared memory (shm)
+ * segment to transfer performance data between the collectors on the
+ * application side (see ./txrecord and ./genload for a simple example)
+ * and this PCP PMDA
+ */
+
+/*
+ * list of instances ... created dynamically, after parsing cmd line options
+ */
+static pmdaInstid *tx_indom;
+
+/*
+ * list of instance domains ... initialized after parsing cmd line options
+ */
+static pmdaIndom indomtab[] = {
+#define TX_INDOM 0
+ { TX_INDOM, 0, NULL },
+};
+
+/*
+ * all metrics supported in this PMDA - one table entry for each
+ */
+static pmdaMetric metrictab[] = {
+/* count */
+ { NULL,
+ { PMDA_PMID(0,0), PM_TYPE_U32, TX_INDOM, PM_SEM_COUNTER,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+/* ave_time */
+ { NULL,
+ { PMDA_PMID(0,1), PM_TYPE_FLOAT, TX_INDOM, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0,1,-1,0,PM_TIME_SEC,PM_COUNT_ONE) } },
+/* max_time */
+ { NULL,
+ { PMDA_PMID(0,2), PM_TYPE_FLOAT, TX_INDOM, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0, 1, 0, 0, PM_TIME_SEC, 0 ) } },
+/* reset_count */
+ { NULL,
+ { PMDA_PMID(0,3), PM_TYPE_U32, TX_INDOM, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } },
+/* control.level */
+ { NULL,
+ { PMDA_PMID(0,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+/* control.reset */
+ { NULL,
+ { PMDA_PMID(0,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+ PMDA_PMUNITS(0,0,0,0,0,0) } },
+};
+
+#include "./txmon.h"
+
+static int shmid = -1;
+
+static char mypath[MAXPATHLEN];
+static char *username;
+
+/*
+ * callback provided to pmdaFetch
+ */
+static int
+txmon_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
+{
+ stat_t *sp;
+ __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid);
+ unsigned int real_count;
+
+ if (inst != PM_IN_NULL && mdesc->m_desc.indom == PM_INDOM_NULL)
+ return PM_ERR_INST;
+
+ if (idp->cluster != 0)
+ return PM_ERR_PMID;
+
+ if (idp->item <= 3) {
+ if (inst >= control->n_tx)
+ return PM_ERR_INST;
+
+ sp = (stat_t *)((__psint_t)control + control->index[inst]);
+
+ switch (idp->item) {
+ case 0: /* txmon.count */
+ if (control->level < 1)
+ return PM_ERR_AGAIN;
+ atom->ul = sp->count;
+ break;
+ case 1: /* txmon.ave_time */
+ if (control->level < 2)
+ return PM_ERR_AGAIN;
+ real_count = sp->count - sp->reset_count;
+ atom->f = real_count > 0 ? sp->sum_time / real_count : -1;
+ break;
+ case 2: /* txmon.max_time */
+ if (control->level < 2)
+ return PM_ERR_AGAIN;
+ atom->f = sp->max_time;
+ break;
+ case 3: /* txmon.reset_count */
+ if (control->level < 1)
+ return PM_ERR_AGAIN;
+ atom->ul = sp->count - sp->reset_count;
+ break;
+ }
+ }
+ else {
+ switch (idp->item) {
+
+ case 4: /* txmon.control.level */
+ atom->ul = control->level;
+ break;
+ case 5: /* txmon.control.reset */
+ atom->ul = 1;
+ break;
+ default:
+ return PM_ERR_PMID;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * support the storage of a value into the control metrics
+ */
+static int
+txmon_store(pmResult *result, pmdaExt *pmda)
+{
+ int i;
+ int n;
+ int val;
+ int sts = 0;
+ pmValueSet *vsp = NULL;
+ __pmID_int *pmidp = NULL;
+ stat_t *sp;
+
+ for (i = 0; i < result->numpmid; i++) {
+ vsp = result->vset[i];
+ pmidp = (__pmID_int *)&vsp->pmid;
+
+ if (pmidp->cluster == 0) { /* all storable metrics are cluster 0 */
+
+ switch (pmidp->item) {
+ case 0: /* no store for these ones */
+ case 1:
+ case 2:
+ case 3:
+ sts = PM_ERR_PERMISSION;
+ break;
+
+ case 4: /* txmon.control.level */
+ val = vsp->vlist[0].value.lval;
+ if (val < 0) {
+ sts = PM_ERR_SIGN;
+ val = 0;
+ }
+ control->level = val;
+ break;
+
+ case 5: /* txmon.control.reset */
+ for (n = 0; n < control->n_tx; n++) {
+ sp = (stat_t *)((__psint_t)control + control->index[n]);
+ sp->reset_count = sp->count;
+ sp->sum_time = 0;
+ sp->max_time = -1;
+ }
+ break;
+
+ default:
+ sts = PM_ERR_PMID;
+ break;
+ }
+ }
+ else
+ sts = PM_ERR_PMID;
+ }
+ return sts;
+}
+
+
+/*
+ * Initialise the agent
+ */
+void
+txmon_init(pmdaInterface *dp)
+{
+ if (dp->status != 0)
+ return;
+
+ __pmSetProcessIdentity(username);
+
+ dp->version.two.store = txmon_store;
+
+ pmdaSetFetchCallBack(dp, txmon_fetchCallBack);
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ fprintf(stderr, "after pmdaSetFetchCallBack() control @ %p\n", control);
+ }
+#endif
+
+ pmdaInit(dp, indomtab, 1, metrictab,
+ sizeof(metrictab)/sizeof(metrictab[0]));
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ fprintf(stderr, "after pmdaInit() control @ %p\n", control);
+ }
+#endif
+}
+
+/*
+ * come here on exit()
+ */
+static void
+done(void)
+{
+ if (shmid != -1)
+ /* remove the shm segment */
+ shmctl(shmid, IPC_RMID, NULL);
+}
+
+pmLongOptions longopts[] = {
+ PMDA_OPTIONS_HEADER("Options"),
+ PMOPT_DEBUG,
+ PMDAOPT_DOMAIN,
+ PMDAOPT_LOGFILE,
+ PMDAOPT_USERNAME,
+ PMOPT_HELP,
+ PMDA_OPTIONS_END
+};
+
+pmdaOptions opts = {
+ .short_options = "D:d:l:U:?",
+ .long_options = longopts,
+ .short_usage = "[options] tx_type [...]",
+};
+
+/*
+ * Set up the agent.
+ */
+int
+main(int argc, char **argv)
+{
+ int n, sep = __pmPathSeparator();
+ char *p;
+ pmdaInterface dispatch;
+ size_t index_size;
+ size_t shm_size;
+ stat_t *sp;
+
+ __pmSetProgname(argv[0]);
+ __pmGetUsername(&username);
+
+ snprintf(mypath, sizeof(mypath), "%s%c" "txmon" "%c" "help",
+ pmGetConfig("PCP_PMDAS_DIR"), sep, sep);
+ pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, TXMON,
+ "txmon.log", mypath);
+
+ pmdaGetOptions(argc, argv, &opts, &dispatch);
+ if (opts.errors) {
+ pmdaUsageMessage(&opts);
+ exit(1);
+ }
+ n = argc - opts.optind;
+ if (n < 1) {
+ pmprintf("No transaction types specified\n\n");
+ pmdaUsageMessage(&opts);
+ exit(1);
+ }
+ if (opts.username)
+ username = opts.username;
+
+ pmdaOpenLog(&dispatch);
+
+ /*
+ * create the instance domain table ... one entry per transaction type
+ */
+ if ((tx_indom = (pmdaInstid *)malloc(n * sizeof(pmdaInstid))) == NULL) {
+ fprintf(stderr, "malloc(%d): %s\n",
+ n * (int)sizeof(pmdaInstid), osstrerror());
+ exit(1);
+ }
+ indomtab[0].it_numinst = n;
+ indomtab[0].it_set = tx_indom;
+
+ /*
+ * size shm segment so each stat_t starts on a cache line boundary
+ */
+ index_size =
+ RND_TO_CACHE_LINE(sizeof(control->level) +
+ sizeof(control->n_tx) +
+ n * sizeof(control->index[0]));
+ shm_size = index_size + n * RND_TO_CACHE_LINE(sizeof(stat_t));
+
+ /*
+ * create the shm segment, and install exit() handler to remove it
+ */
+ if ((shmid = shmget(KEY, shm_size, IPC_CREAT|0666)) < 0) {
+ fprintf(stderr, "shmid: %s\n", osstrerror());
+ exit(1);
+ }
+ atexit(done);
+ if ((control = (control_t *)shmat(shmid, NULL, 0)) == (control_t *)-1) {
+ fprintf(stderr, "shmat: %s\n", osstrerror());
+ exit(1);
+ }
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ fprintf(stderr, "shmat -> control @ %p\n", control);
+ }
+#endif
+
+ /*
+ * set up the shm control info and directory
+ */
+ control->n_tx = n;
+ control->level = 2; /* arbitrary default stats level */
+ p = (char *)control;
+ p = &p[index_size];
+ for (n = 0; n < control->n_tx; n++) {
+ /*
+ * Note: it is important that the index[] entries are byte
+ * offsets from the start of the shm segment ... using
+ * pointers may cause problems for 32-bit and 64-bit apps
+ * attaching to the shm segment
+ */
+ control->index[n] = p - (char *)control;
+ sp = (stat_t *)p;
+ strncpy(sp->type, argv[opts.optind++], MAXNAMESIZE);
+ sp->type[MAXNAMESIZE-1] = '\0'; /* ensure null terminated */
+ sp->reset_count = 0;
+ sp->count = 0;
+ sp->max_time = -1.0;
+ sp->sum_time = 0.0;
+#ifdef PCP_DEBUG
+ if (pmDebug & DBG_TRACE_APPL0) {
+ fprintf(stderr, "index[%d]=%d @ %p name=\"%s\"\n", n, control->index[n], p, sp->type);
+ }
+#endif
+ p += RND_TO_CACHE_LINE(sizeof(stat_t));
+
+ /*
+ * and set up the corresponding indom table entries
+ */
+ tx_indom[n].i_inst = n;
+ tx_indom[n].i_name = sp->type;
+ }
+
+ /*
+ * the real work is done below here ...
+ */
+ txmon_init(&dispatch);
+ pmdaConnect(&dispatch);
+ pmdaMain(&dispatch);
+ exit(0);
+}