static void EndLocalContext(void) { int i; __pmDSO *dp; int ctx = pmWhichContext(); if (PM_MULTIPLE_THREADS(PM_SCOPE_DSO_PMDA)) /* * Local context requires single-threaded applications * ... should not really get here, so do nothing! */ return; for (i = 0; i < numdso; i++) { dp = &dsotab[i]; if (dp->domain != -1 && dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5 && dp->dispatch.version.four.ext->e_endCallBack != NULL) { #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_CONTEXT) { fprintf(stderr, "NotifyEndLocalContext: DSO PMDA %s (%d) notified of context %d close\n", dp->name, dp->domain, ctx); } #endif (*(dp->dispatch.version.four.ext->e_endCallBack))(ctx); } } }
void __pmAFblock(void) { if (PM_MULTIPLE_THREADS(PM_SCOPE_AF)) return; block = 1; AFhold(); }
void __pmAFunblock(void) { if (PM_MULTIPLE_THREADS(PM_SCOPE_AF)) return; block = 0; AFrearm(); AFrelse(); }
int __pmAFunregister(int afid) { qelt *qp; qelt *priorp; struct timeval now; struct timeval interval; if (PM_MULTIPLE_THREADS(PM_SCOPE_AF)) return PM_ERR_THREAD; if (!block) AFhold(); for (qp = root, priorp = NULL; qp != NULL && qp->q_afid != afid; qp = qp->q_next) priorp = qp; if (qp == NULL) { if (!block) AFrelse(); return -1; } if (priorp == NULL) { root = qp->q_next; if (root != NULL) { /* * we removed the head of the queue, set itimer for the * new head of queue */ interval = root->q_when; __pmtimevalNow(&now); tsub(&interval, &now); if (interval.tv_sec == 0 && interval.tv_usec < MIN_ITIMER_USEC) /* use minimal delay (platform dependent) */ interval.tv_usec = MIN_ITIMER_USEC; #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_AF) { __pmPrintStamp(stderr, &now); fprintf(stderr, " AFsetitimer for delta "); printdelta(stderr, &interval); fputc('\n', stderr); } #endif AFsetitimer(&interval); } } else priorp->q_next = qp->q_next; free(qp); if (!block) AFrelse(); return 0; }
int __pmAFregister(const struct timeval *delta, void *data, void (*func)(int, void *)) { qelt *qp; struct timeval now; struct timeval interval; if (PM_MULTIPLE_THREADS(PM_SCOPE_AF)) return PM_ERR_THREAD; if (!block) AFhold(); if (afid == 0x8000 && !block) /* first time */ AFrearm(); if ((qp = (qelt *)malloc(sizeof(qelt))) == NULL) { return -oserror(); } qp->q_afid = ++afid; qp->q_data = data; qp->q_delta = *delta; qp->q_func = func; __pmtimevalNow(&qp->q_when); tadd(&qp->q_when, &qp->q_delta); enqueue(qp); if (root == qp) { /* we ended up at the head of the list, set itimer */ interval = qp->q_when; __pmtimevalNow(&now); tsub(&interval, &now); if (interval.tv_sec == 0 && interval.tv_usec < MIN_ITIMER_USEC) /* use minimal delay (platform dependent) */ interval.tv_usec = MIN_ITIMER_USEC; #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_AF) { __pmPrintStamp(stderr, &now); fprintf(stderr, " AFsetitimer for delta "); printdelta(stderr, &interval); fputc('\n', stderr); } #endif AFsetitimer(&interval); } if (!block) AFrelse(); return qp->q_afid; }
int pmFetch(int numpmid, pmID pmidlist[], pmResult **result) { int n; if (numpmid < 1) { n = PM_ERR_TOOSMALL; goto done; } if ((n = pmWhichContext()) >= 0) { __pmContext *ctxp = __pmHandleToPtr(n); int newcnt; pmID *newlist = NULL; int have_dm; if (ctxp == NULL) { n = PM_ERR_NOCONTEXT; goto done; } if (ctxp->c_type == PM_CONTEXT_LOCAL && PM_MULTIPLE_THREADS(PM_SCOPE_DSO_PMDA)) { /* Local context requires single-threaded applications */ n = PM_ERR_THREAD; PM_UNLOCK(ctxp->c_lock); goto done; } /* for derived metrics, may need to rewrite the pmidlist */ have_dm = newcnt = __pmPrepareFetch(ctxp, numpmid, pmidlist, &newlist); if (newcnt > numpmid) { /* replace args passed into pmFetch */ numpmid = newcnt; pmidlist = newlist; } if (ctxp->c_type == PM_CONTEXT_HOST) { /* * Thread-safe note * * Need to be careful here, because the PMCD changed protocol * may mean several PDUs are returned, but __pmDecodeResult() * may request more info from PMCD if pmDebug is set. * * So unlock ctxp->c_pmcd->pc_lock as soon as possible. */ PM_LOCK(ctxp->c_pmcd->pc_lock); if ((n = request_fetch(n, ctxp, numpmid, pmidlist)) >= 0) { int changed = 0; do { __pmPDU *pb; int pinpdu; pinpdu = n = __pmGetPDU(ctxp->c_pmcd->pc_fd, ANY_SIZE, ctxp->c_pmcd->pc_tout_sec, &pb); if (n == PDU_RESULT) { PM_UNLOCK(ctxp->c_pmcd->pc_lock); n = __pmDecodeResult(pb, result); } else if (n == PDU_ERROR) { __pmDecodeError(pb, &n); if (n > 0) /* PMCD state change protocol */ changed = n; else PM_UNLOCK(ctxp->c_pmcd->pc_lock); } else { PM_UNLOCK(ctxp->c_pmcd->pc_lock); if (n != PM_ERR_TIMEOUT) n = PM_ERR_IPC; } if (pinpdu > 0) __pmUnpinPDUBuf(pb); } while (n > 0); if (n == 0) n |= changed; } else PM_UNLOCK(ctxp->c_pmcd->pc_lock); } else if (ctxp->c_type == PM_CONTEXT_LOCAL) { n = __pmFetchLocal(ctxp, numpmid, pmidlist, result); } else { /* assume PM_CONTEXT_ARCHIVE */ n = __pmLogFetch(ctxp, numpmid, pmidlist, result); if (n >= 0 && (ctxp->c_mode & __PM_MODE_MASK) != PM_MODE_INTERP) { ctxp->c_origin.tv_sec = (__int32_t)(*result)->timestamp.tv_sec; ctxp->c_origin.tv_usec = (__int32_t)(*result)->timestamp.tv_usec; } } /* process derived metrics, if any */ if (have_dm) { __pmFinishResult(ctxp, n, result); if (newlist != NULL) free(newlist); } PM_UNLOCK(ctxp->c_lock); } done: #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_FETCH) { fprintf(stderr, "pmFetch returns ...\n"); if (n > 0) { fprintf(stderr, "PMCD state changes: agent(s)"); if (n & PMCD_ADD_AGENT) fprintf(stderr, " added"); if (n & PMCD_RESTART_AGENT) fprintf(stderr, " restarted"); if (n & PMCD_DROP_AGENT) fprintf(stderr, " dropped"); fputc('\n', stderr); } if (n >= 0) __pmDumpResult(stderr, *result); else { char errmsg[PM_MAXERRMSGLEN]; fprintf(stderr, "Error: %s\n", pmErrStr_r(n, errmsg, sizeof(errmsg))); } } #endif return n; }
/* * Called with valid context locked ... */ int __pmFetchLocal(__pmContext *ctxp, int numpmid, pmID pmidlist[], pmResult **result) { int sts; int ctx; int j; int k; int n; pmResult *ans; pmResult *tmp_ans; __pmDSO *dp; int need; static pmID * splitlist=NULL; static int splitmax=0; if (PM_MULTIPLE_THREADS(PM_SCOPE_DSO_PMDA)) /* Local context requires single-threaded applications */ return PM_ERR_THREAD; if (numpmid < 1) return PM_ERR_TOOSMALL; ctx = __pmPtrToHandle(ctxp); /* * this is very ugly ... the DSOs have a high-water mark * allocation algorithm for the result skeleton, but the * code that calls us assumes it has freedom to retain * this result structure for as long as it wishes, and * then to call pmFreeResult * * we make another skeleton, selectively copy and return that * * (numpmid - 1) because there's room for one valueSet * in a pmResult */ need = (int)sizeof(pmResult) + (numpmid - 1) * (int)sizeof(pmValueSet *); if ((ans = (pmResult *)malloc(need)) == NULL) return -oserror(); /* * Check if we have enough space to accomodate "best" case scenario - * all pmids are from the same domain */ if (splitmax < numpmid) { splitmax = numpmid; pmID *tmp_list = (pmID *)realloc(splitlist, sizeof(pmID)*splitmax); if (tmp_list == NULL) { free(splitlist); splitmax = 0; free(ans); return -oserror(); } splitlist = tmp_list; } ans->numpmid = numpmid; __pmtimevalNow(&ans->timestamp); for (j = 0; j < numpmid; j++) ans->vset[j] = NULL; for (j = 0; j < numpmid; j++) { int cnt; if (ans->vset[j] != NULL) /* picked up in a previous fetch */ continue; sts = 0; if ((dp = __pmLookupDSO(((__pmID_int *)&pmidlist[j])->domain)) == NULL) /* based on domain, unknown PMDA */ sts = PM_ERR_NOAGENT; else { if (ctxp->c_sent != dp->domain) { /* * current profile is _not_ already cached at other end of * IPC, so send get current profile ... * Note: trickier than the non-local case, as no per-PMDA * caching at the PMCD end, so need to remember the * last domain to receive a profile */ #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_FETCH) fprintf(stderr, "__pmFetchLocal: calling ???_profile(domain: %d), " "context: %d\n", dp->domain, ctx); #endif if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5) dp->dispatch.version.four.ext->e_context = ctx; sts = dp->dispatch.version.any.profile(ctxp->c_instprof, dp->dispatch.version.any.ext); if (sts >= 0) ctxp->c_sent = dp->domain; } } /* Copy all pmID for the current domain into the temp. list */ for (cnt=0, k=j; k < numpmid; k++ ) { if (((__pmID_int*)(pmidlist+k))->domain == ((__pmID_int*)(pmidlist+j))->domain) splitlist[cnt++] = pmidlist[k]; } if (sts >= 0) { if (dp->dispatch.comm.pmda_interface >= PMDA_INTERFACE_5) dp->dispatch.version.four.ext->e_context = ctx; sts = dp->dispatch.version.any.fetch(cnt, splitlist, &tmp_ans, dp->dispatch.version.any.ext); } /* Copy results back * * Note: We DO NOT have to free tmp_ans since DSO PMDA would * ALWAYS return a pointer to the static area. */ for (n = 0, k = j; k < numpmid && n < cnt; k++) { if (pmidlist[k] == splitlist[n]) { if (sts < 0) { ans->vset[k] = (pmValueSet *)malloc(sizeof(pmValueSet)); if (ans->vset[k] == NULL) { /* cleanup all partial allocations for ans->vset[] */ for (k--; k >=0; k--) free(ans->vset[k]); free(ans); return -oserror(); } ans->vset[k]->numval = sts; ans->vset[k]->pmid = pmidlist[k]; } else { ans->vset[k] = tmp_ans->vset[n]; } #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_FETCH) { char strbuf[20]; char errmsg[PM_MAXERRMSGLEN]; fprintf(stderr, "__pmFetchLocal: [%d] PMID=%s nval=", k, pmIDStr_r(pmidlist[k], strbuf, sizeof(strbuf))); if (ans->vset[k]->numval < 0) fprintf(stderr, "%s\n", pmErrStr_r(ans->vset[k]->numval, errmsg, sizeof(errmsg))); else fprintf(stderr, "%d\n", ans->vset[k]->numval); } #endif n++; } } } *result = ans; return 0; }
/* Return (in result) a list of active pmlogger ports on the local machine. * The return value of the function is the number of elements in the array. * The caller must NOT free any part of the result stucture, it's storage is * managed here. Subsequent calls will overwrite the data so the caller should * copy it if persistence is required. */ int __pmLogFindLocalPorts(int pid, __pmLogPort **result) { char dir[MAXPATHLEN]; int lendir; int i, j, n; int nf; /* number of port files found */ struct dirent **files = NULL; /* array of port file dirents */ char *p; int len; char namebuf[MAXPATHLEN]; int (*scanfn)(const_dirent *dep); FILE *pfile; char buf[MAXPATHLEN]; if (PM_MULTIPLE_THREADS(PM_SCOPE_LOGPORT)) return PM_ERR_THREAD; if (result == NULL) return -EINVAL; if ((p = pmGetOptionalConfig("PCP_TMP_DIR")) == NULL) return PM_ERR_GENERIC; lendir = snprintf(dir, sizeof(dir), "%s%cpmlogger", p, __pmPathSeparator()); /* Set up the appropriate function to select files from the control port * directory. Anticipate that this will usually be an exact match for * the primary logger control port. */ scanfn = is_match; switch (pid) { case PM_LOG_PRIMARY_PID: /* primary logger control (single) */ strcpy(match, "primary"); break; case PM_LOG_ALL_PIDS: /* find all ports */ scanfn = is_portfile; break; default: /* a specific pid (single) */ if (!__pmProcessExists((pid_t)pid)) { #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_LOG) { fprintf(stderr, "__pmLogFindLocalPorts() -> 0, " "pid(%d) doesn't exist\n", pid); } #endif *result = NULL; return 0; } snprintf(match, sizeof(match), "%d", pid); break; } nf = scandir(dir, &files, scanfn, alphasort); #ifdef PCP_DEBUG if (nf < 1 && (pmDebug & DBG_TRACE_LOG)) { fprintf(stderr, "__pmLogFindLocalPorts: scandir() -> %d %s\n", nf, pmErrStr(oserror())); } #endif if (nf == -1 && oserror() == ENOENT) nf = 0; else if (nf == -1) { char errmsg[PM_MAXERRMSGLEN]; pmprintf("__pmLogFindLocalPorts: scandir: %s\n", osstrerror_r(errmsg, sizeof(errmsg))); pmflush(); return -oserror(); } if (resize_logports(nf) < 0) { for (i=0; i < nf; i++) free(files[i]); free(files); return -oserror(); } if (nf == 0) { #ifdef PCP_DEBUG if (pmDebug & DBG_TRACE_LOG) { fprintf(stderr, "__pmLogFindLocalPorts() -> 0, " "num files = 0\n"); } #endif *result = NULL; free(files); return 0; } /* make a buffer for the longest complete pathname found */ len = (int)strlen(files[0]->d_name); for (i = 1; i < nf; i++) if ((j = (int)strlen(files[i]->d_name)) > len) len = j; /* +1 for trailing path separator, +1 for null termination */ len += lendir + 2; /* namebuf is the complete pathname, p points to the trailing filename * within namebuf. */ strcpy(namebuf, dir); p = namebuf + lendir; *p++ = __pmPathSeparator(); /* open the file, try to read the port number and add the port to the * logport array if successful. */ for (i = 0; i < nf; i++) { char *fname = files[i]->d_name; int err = 0; __pmLogPort *lpp = &logport[nlogports]; strcpy(p, fname); if ((pfile = fopen(namebuf, "r")) == NULL) { char errmsg[PM_MAXERRMSGLEN]; pmprintf("__pmLogFindLocalPorts: pmlogger port file %s: %s\n", namebuf, osstrerror_r(errmsg, sizeof(errmsg))); free(files[i]); pmflush(); continue; } if (!err && fgets(buf, MAXPATHLEN, pfile) == NULL) { if (feof(pfile)) { clearerr(pfile); pmprintf("__pmLogFindLocalPorts: pmlogger port file %s empty!\n", namebuf); } else { char errmsg[PM_MAXERRMSGLEN]; pmprintf("__pmLogFindLocalPorts: pmlogger port file %s: %s\n", namebuf, osstrerror_r(errmsg, sizeof(errmsg))); } err = 1; } else { char *endp; lpp->port = (int)strtol(buf, &endp, 10); if (*endp != '\n') { pmprintf("__pmLogFindLocalPorts: pmlogger port file %s: no port number\n", namebuf); err = 1; } else { lpp->pid = (int)strtol(fname, &endp, 10); if (*endp != '\0') { if (strcmp(fname, "primary") == 0) lpp->pid = PM_LOG_PRIMARY_PORT; else { pmprintf("__pmLogFindLocalPorts: unrecognised pmlogger port file %s\n", namebuf); err = 1; } } } } if (err) { pmflush(); fclose(pfile); } else { if (fgets(buf, MAXPATHLEN, pfile) == NULL) { pmprintf("__pmLogFindLocalPorts: pmlogger port file %s: no PMCD host name\n", namebuf); pmflush(); } else { char *q = strchr(buf, '\n'); if (q != NULL) *q = '\0'; lpp->pmcd_host = strdup(buf); if (fgets(buf, MAXPATHLEN, pfile) == NULL) { pmprintf("__pmLogFindLocalPorts: pmlogger port file %s: no archive base pathname\n", namebuf); pmflush(); } else { char *q = strchr(buf, '\n'); if (q != NULL) *q = '\0'; lpp->archive = strdup(buf); } } fclose(pfile); if ((lpp->name = strdup(fname)) != NULL) nlogports++; else { if (lpp->pmcd_host != NULL) { free(lpp->pmcd_host); lpp->pmcd_host = NULL; } if (lpp->archive != NULL) { free(lpp->archive); lpp->archive = NULL; } break; } } free(files[i]); } if (i == nf) { /* all went well */ n = nlogports; *result = logport; } else { /* strdup error on fname, clean up */ *result = NULL; for (j = i; j < nf; j++) free(files[j]); n = -oserror(); } free(files); return n; }
/* Return (in result) a list of active pmlogger ports on the specified machine. * The return value of the function is the number of elements in the array. * The caller must NOT free any part of the result stucture, it's storage is * managed here. Subsequent calls will overwrite the data so the caller should * copy it if persistence is required. */ int __pmLogFindPort(const char *host, int pid, __pmLogPort **lpp) { int ctx, oldctx; char *ctxhost; int sts, numval; int i, j; int findone = pid != PM_LOG_ALL_PIDS; int localcon = 0; /* > 0 for local connection */ pmDesc desc; pmResult *res; char *namelist[] = {"pmcd.pmlogger.port"}; pmID pmid; if (PM_MULTIPLE_THREADS(PM_SCOPE_LOGPORT)) return PM_ERR_THREAD; *lpp = NULL; /* pass null back in event of error */ localcon = __pmIsLocalhost(host); if (localcon > 0) /* do the work here instead of making PMCD do it */ return __pmLogFindLocalPorts(pid, lpp); else if (localcon < 0) return localcon; /* note: there may not be a current context */ ctx = 0; oldctx = pmWhichContext(); /* * Enclose ctxhost in [] in case it is an ipv6 address. This prevents * the first colon from being taken as a port separator by pmNewContext * and does no harm otherwise. */ ctxhost = malloc(strlen(host) + 2 + 1); if (ctxhost == NULL) { sts = -ENOMEM; goto ctxErr; } sprintf(ctxhost, "[%s]", host); ctx = pmNewContext(PM_CONTEXT_HOST, ctxhost); free(ctxhost); if (ctx < 0) return ctx; if ((sts = pmLookupName(1, namelist, &pmid)) < 0) goto ctxErr; if ((sts = pmLookupDesc(pmid, &desc)) < 0) goto ctxErr; if ((sts = pmFetch(1, &pmid, &res) < 0)) goto ctxErr; if ((sts = numval = res->vset[0]->numval) < 0) goto resErr; j = 0; if (numval) { if (resize_logports(findone ? 1 : numval) < 0) { sts = -oserror(); goto resErr; } /* scan the pmResult, copying matching pid(s) to logport */ for (i = j = 0; i < numval; i++) { __pmLogPort *p = &logport[j]; pmValue *vp = &res->vset[0]->vlist[i]; if (vp->inst == 1) /* old vcr instance (pseudo-init) */ continue; if (findone && vp->inst != pid) continue; p->pid = vp->inst; p->port = vp->value.lval; sts = pmNameInDom(desc.indom, p->pid, &p->name); if (sts < 0) { p->name = NULL; goto resErr; } j++; if (findone) /* found one, stop searching */ break; } *lpp = logport; } sts = j; /* the number actually added */ resErr: pmFreeResult(res); ctxErr: if (oldctx >= 0) pmUseContext(oldctx); if (ctx >= 0) pmDestroyContext(ctx); return sts; }