/* * Copyright (c) 2004 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2010 Max Matveev. 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 "common.h" #include #include typedef struct { int fetched; int err; kstat_t *ksp; cpu_stat_t cpustat; kstat_t *info; int info_is_good; } ctl_t; static int ncpu; static int hz; static long pagesize; static ctl_t *ctl; static char uname_full[SYS_NMLN * 5]; static int nloadavgs; static double loadavgs[3]; void sysinfo_init(int first) { kstat_t *ksp; int i; char buf[10]; /* cpuXXXXX */ kstat_ctl_t *kc; if (!first) /* TODO ... not sure if/when we'll use this re-init hook */ return; if ((kc = kstat_ctl_update()) == NULL) return; for (ncpu = 0; ; ncpu++) { ksp = kstat_lookup(kc, "cpu_stat", ncpu, NULL); if (ksp == NULL) break; if ((ctl = (ctl_t *)realloc(ctl, (ncpu+1) * sizeof(ctl_t))) == NULL) { fprintf(stderr, "sysinfo_init: ctl realloc[%d] @ cpu=%d failed: %s\n", (int)((ncpu+1) * sizeof(ctl_t)), ncpu, osstrerror()); exit(1); } ctl[ncpu].info = kstat_lookup(kc, "cpu_info", ncpu, NULL); ctl[ncpu].ksp = ksp; ctl[ncpu].err = 0; } indomtab[CPU_INDOM].it_numinst = ncpu; indomtab[CPU_INDOM].it_set = (pmdaInstid *)malloc(ncpu * sizeof(pmdaInstid)); /* TODO check? */ for (i = 0; i < ncpu; i++) { indomtab[CPU_INDOM].it_set[i].i_inst = i; snprintf(buf, sizeof(buf), "cpu%d", i); indomtab[CPU_INDOM].it_set[i].i_name = strdup(buf); /* TODO check? */ } hz = (int)sysconf(_SC_CLK_TCK); pagesize = sysconf(_SC_PAGESIZE); #ifdef PCP_DEBUG if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { /* desperate */ fprintf(stderr, "sysinfo: ncpu=%d hz=%d\n", ncpu, hz); } #endif } static __uint32_t sysinfo_derived(pmdaMetric *mdesc, int inst) { pmID pmid = mdesc->m_desc.pmid; __pmID_int *ip = (__pmID_int *)&pmid; __uint32_t val; ip->domain = 0; switch (pmid) { case PMDA_PMID(SCLR_SYSINFO,56): /* hinv.ncpu */ if (inst == 0) val = ncpu; else val = 0; break; default: fprintf(stderr, "cpu_derived: Botch: no method for pmid %s\n", pmIDStr(mdesc->m_desc.pmid)); val = 0; break; } #ifdef PCP_DEBUG if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { /* desperate */ fprintf(stderr, "cpu_derived: pmid %s inst %d val %d\n", pmIDStr(mdesc->m_desc.pmid), inst, val); } #endif return val; } void sysinfo_prefetch(void) { int i; nloadavgs = -1; for (i = 0; i < ncpu; i++) { ctl[i].fetched = 0; ctl[i].info_is_good = 0; } } int kstat_named_to_pmAtom(const kstat_named_t *kn, pmAtomValue *atom) { static char chardat[sizeof(kn->value.c) + 1]; switch (kn->data_type) { case KSTAT_DATA_UINT64: atom->ull = kn->value.ui64; return 1; case KSTAT_DATA_INT64: atom->ull = kn->value.i64; return 1; case KSTAT_DATA_UINT32: atom->ull = kn->value.ui32; return 1; case KSTAT_DATA_INT32: atom->ull = kn->value.i32; return 1; case KSTAT_DATA_STRING: atom->cp = kn->value.str.addr.ptr; return 1; case KSTAT_DATA_CHAR: memcpy(chardat, kn->value.c, sizeof(kn->value.c)); chardat[sizeof(chardat)-1] = '\0'; atom->cp = chardat; return 1; default: return 0; } } static int kstat_fetch_named(kstat_ctl_t *kc, pmAtomValue *atom, char *metric, int shift_bits) { kstat_t *ks; if ((ks = kstat_lookup(kc, "unix", -1, "system_pages")) != NULL) { kstat_named_t *kn; if (kstat_read(kc, ks, NULL) == -1) return 0; if (((kn = kstat_data_lookup(ks, metric)) != NULL) && kstat_named_to_pmAtom(kn, atom)) { atom->ull = (atom->ull * pagesize) >> shift_bits; return 1; } } return 0; } int kstat_named_to_typed_atom(const kstat_named_t *kn, int pmtype, pmAtomValue *atom) { static char chardat[sizeof(kn->value.c) + 1]; switch (pmtype) { case PM_TYPE_32: if (kn->data_type == KSTAT_DATA_INT32) { atom->l = kn->value.i32; return 1; } break; case PM_TYPE_U32: if (kn->data_type == KSTAT_DATA_UINT32) { atom->ul = kn->value.ui32; return 1; } break; case PM_TYPE_64: if (kn->data_type == KSTAT_DATA_INT64) { atom->ll = kn->value.i64; return 1; } break; case PM_TYPE_U64: if (kn->data_type == KSTAT_DATA_UINT64) { atom->ull = kn->value.ui64; return 1; } break; case PM_TYPE_STRING: switch(kn->data_type) { case KSTAT_DATA_STRING: atom->cp = kn->value.str.addr.ptr; return 1; case KSTAT_DATA_CHAR: memcpy(chardat, kn->value.c, sizeof(kn->value.c)); chardat[sizeof(chardat)-1] = '\0'; atom->cp = chardat; return 1; } break; } return 0; } int sysinfo_fetch(pmdaMetric *mdesc, int inst, pmAtomValue *atom) { __uint64_t ull; int i; int ok; ptrdiff_t offset; struct utsname u; kstat_ctl_t *kc; if ((kc = kstat_ctl_update()) == NULL) return 0; /* Special processing of metrics which notionally belong * to sysinfo category */ switch (pmid_item(mdesc->m_desc.pmid)) { case 109: /* hinv.physmem */ return kstat_fetch_named(kc, atom, "physmem", 20); case 136: /* mem.physmem */ return kstat_fetch_named(kc, atom, "physmem", 10); case 137: /* mem.freemem */ return kstat_fetch_named(kc, atom, "freemem", 10); case 138: /* mem.lotsfree */ return kstat_fetch_named(kc, atom, "lotsfree", 10); case 139: /* mem.availrmem */ return kstat_fetch_named(kc, atom, "availrmem", 10); case 108: /* hinv.pagesize */ atom->ul = pagesize; return 1; case 107: /* pmda.uname */ if (uname(&u) < 0) return 0; snprintf(uname_full, sizeof(uname_full), "%s %s %s %s %s", u.sysname, u.nodename, u.release, u.version, u.machine); atom->cp = uname_full; return 1; case 135: /* kernel.all.load */ if (nloadavgs < 0) { if ((nloadavgs = getloadavg(loadavgs, 3)) < 0) return 0; } switch (inst) { case 1: atom->f = (float)loadavgs[LOADAVG_1MIN]; return nloadavgs > LOADAVG_1MIN; case 5: atom->f = (float)loadavgs[LOADAVG_5MIN]; return nloadavgs > LOADAVG_5MIN; case 15: atom->f = (float)loadavgs[LOADAVG_15MIN]; return nloadavgs > LOADAVG_15MIN; } return PM_ERR_INST; } ok = 1; for (i = 0; i < ncpu; i++) { if (inst == PM_IN_NULL || inst == i) { if (!ctl[i].info_is_good) { ctl[i].info_is_good = (ctl[i].info && (kstat_read(kc, ctl[i].info, NULL) != -1)); } if (ctl[i].fetched == 1) continue; if (kstat_read(kc, ctl[i].ksp, &ctl[i].cpustat) == -1) { if (ctl[i].err == 0) { fprintf(stderr, "Error: sysinfo_fetch(pmid=%s cpu=%d ...)\n", pmIDStr(mdesc->m_desc.pmid), i); fprintf(stderr, "kstat_read(kc=%p, ksp=%p, ...) failed: %s\n", kc, ctl[i].ksp, osstrerror()); } ctl[i].err++; ctl[i].fetched = -1; ok = 0; } else { ctl[i].fetched = 1; if (ctl[i].err != 0) { fprintf(stderr, "Success: sysinfo_fetch(pmid=%s cpu=%d ...) after %d errors as previously reported\n", pmIDStr(mdesc->m_desc.pmid), i, ctl[i].err); ctl[i].err = 0; } } } } if (!ok) return 0; ull = 0; for (i = 0; i < ncpu; i++) { if (inst == PM_IN_NULL || inst == i) { offset = ((metricdesc_t *)mdesc->m_user)->md_offset; if (offset == -1) { ull += sysinfo_derived(mdesc, i); } else if (offset > sizeof(ctl[i].cpustat)) { char *stat = (char *)offset; kstat_named_t *kn; if (!ctl[i].info_is_good) return 0; if ((kn = kstat_data_lookup(ctl[i].info, stat)) == NULL) { fprintf(stderr, "No kstat called %s for CPU %d\n", stat, i); return 0; } return kstat_named_to_typed_atom(kn, mdesc->m_desc.type, atom); } else { /* all the kstat fields are 32-bit unsigned */ __uint32_t *ulp; ulp = (__uint32_t *)&((char *)&ctl[i].cpustat)[offset]; ull += *ulp; #ifdef PCP_DEBUG if ((pmDebug & (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) == (DBG_TRACE_APPL0|DBG_TRACE_APPL2)) { /* desperate */ fprintf(stderr, "sysinfo_fetch: pmid %s inst %d val %u\n", pmIDStr(mdesc->m_desc.pmid), i, *ulp); } #endif } } } if (mdesc->m_desc.units.dimTime == 1) { /* sysinfo times are in ticks, and we export as 64-bit msec */ atom->ull = ull * 1000 / hz; } else if (mdesc->m_desc.type == PM_TYPE_U64) { /* export as 64-bit value */ atom->ull = ull; } else { /* else export as a 32-bit */ atom->ul = (__uint32_t)ull; } return 1; }