/* * 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 #include #include #include #include #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); }