diff --git a/proc/libprocps.sym b/proc/libprocps.sym index e1866935..77655d94 100644 --- a/proc/libprocps.sym +++ b/proc/libprocps.sym @@ -70,10 +70,15 @@ global: procps_meminfo_get_chain; procps_stat_new; procps_stat_read; + procps_stat_read_jiffs; procps_stat_ref; procps_stat_unref; procps_stat_get_cpu; procps_stat_get_cpu_chain; + procps_stat_get_jiffs; + procps_stat_get_jiffs_all; + procps_stat_get_jiffs_hist; + procps_stat_get_jiffs_hist_all; procps_stat_get_sys; procps_stat_get_sys_chain; procps_vmstat_new; diff --git a/proc/readstat.c b/proc/readstat.c index 16322ad3..48c323f4 100644 --- a/proc/readstat.c +++ b/proc/readstat.c @@ -13,16 +13,7 @@ #define STAT_FILE "/proc/stat" struct stat_data { - jiff cpu_user; - jiff cpu_nice; - jiff cpu_sys; - jiff cpu_idle; - jiff cpu_iowait; - jiff cpu_irq; - jiff cpu_sirq; - jiff cpu_stol; - jiff cpu_guest; - jiff cpu_gnice; + struct procps_jiffs cpu; unsigned int intr; unsigned int ctxt; unsigned int btime; @@ -31,10 +22,19 @@ struct stat_data { unsigned int procs_running; }; +struct procps_jiffs_private { + struct procps_jiffs_hist cpu; + // future additions go here, please +}; + struct procps_statinfo { int refcount; int stat_fd; struct stat_data data; + int jiff_hists_alloc; + int jiff_hists_inuse; + struct procps_jiffs_private *jiff_hists; + struct procps_jiffs_private cpu_summary; }; @@ -58,6 +58,8 @@ PROCPS_EXPORT int procps_stat_new ( v->refcount = 1; v->stat_fd = -1; +/* v->jiff_hists_alloc = 0; unecessary with calloc */ +/* v->jiff_hists_inuse = 0; but serves as reminder */ *info = v; return 0; } @@ -79,7 +81,7 @@ PROCPS_EXPORT int procps_stat_read ( int size; if (info == NULL) - return -1; + return -EINVAL; memset(&(info->data), 0, sizeof(struct stat_data)); /* read in the data */ @@ -111,16 +113,16 @@ PROCPS_EXPORT int procps_stat_read ( *tail = '\0'; if (0 == strcmp(head, "cpu")) { if (sscanf(tail+1, "%Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu", - &(info->data.cpu_user), - &(info->data.cpu_nice), - &(info->data.cpu_sys), - &(info->data.cpu_idle), - &(info->data.cpu_iowait), - &(info->data.cpu_irq), - &(info->data.cpu_sirq), - &(info->data.cpu_stol), - &(info->data.cpu_guest), - &(info->data.cpu_nice) + &(info->data.cpu.user), + &(info->data.cpu.nice), + &(info->data.cpu.system), + &(info->data.cpu.idle), + &(info->data.cpu.iowait), + &(info->data.cpu.irq), + &(info->data.cpu.sirq), + &(info->data.cpu.stolen), + &(info->data.cpu.guest), + &(info->data.cpu.nice) ) != 10) return -1; @@ -167,6 +169,8 @@ PROCPS_EXPORT struct procps_statinfo *procps_stat_unref ( info->refcount--; if (info->refcount > 0) return info; + if (info->jiff_hists) + free(info->jiff_hists); free(info); return NULL; } @@ -177,25 +181,25 @@ PROCPS_EXPORT jiff procps_stat_get_cpu ( { switch (item) { case PROCPS_CPU_USER: - return info->data.cpu_user; + return info->data.cpu.user; case PROCPS_CPU_NICE: - return info->data.cpu_nice; + return info->data.cpu.nice; case PROCPS_CPU_SYSTEM: - return info->data.cpu_sys; + return info->data.cpu.system; case PROCPS_CPU_IDLE: - return info->data.cpu_idle; + return info->data.cpu.idle; case PROCPS_CPU_IOWAIT: - return info->data.cpu_iowait; + return info->data.cpu.iowait; case PROCPS_CPU_IRQ: - return info->data.cpu_irq; + return info->data.cpu.irq; case PROCPS_CPU_SIRQ: - return info->data.cpu_sirq; + return info->data.cpu.sirq; case PROCPS_CPU_STOLEN: - return info->data.cpu_stol; + return info->data.cpu.stolen; case PROCPS_CPU_GUEST: - return info->data.cpu_guest; + return info->data.cpu.guest; case PROCPS_CPU_GNICE: - return info->data.cpu_gnice; + return info->data.cpu.gnice; } return 0; } @@ -210,34 +214,34 @@ PROCPS_EXPORT int procps_get_cpu_chain ( do { switch (item->item) { case PROCPS_CPU_USER: - item->result = info->data.cpu_user; + item->result = info->data.cpu.user; break; case PROCPS_CPU_NICE: - item->result = info->data.cpu_nice; + item->result = info->data.cpu.nice; break; case PROCPS_CPU_SYSTEM: - item->result = info->data.cpu_sys; + item->result = info->data.cpu.system; break; case PROCPS_CPU_IDLE: - item->result = info->data.cpu_idle; + item->result = info->data.cpu.idle; break; case PROCPS_CPU_IOWAIT: - item->result = info->data.cpu_iowait; + item->result = info->data.cpu.iowait; break; case PROCPS_CPU_IRQ: - item->result = info->data.cpu_irq; + item->result = info->data.cpu.irq; break; case PROCPS_CPU_SIRQ: - item->result = info->data.cpu_sirq; + item->result = info->data.cpu.sirq; break; case PROCPS_CPU_STOLEN: - item->result = info->data.cpu_stol; + item->result = info->data.cpu.stolen; break; case PROCPS_CPU_GUEST: - item->result = info->data.cpu_guest; + item->result = info->data.cpu.guest; break; case PROCPS_CPU_GNICE: - item->result = info->data.cpu_gnice; + item->result = info->data.cpu.gnice; break; default: return -EINVAL; @@ -304,3 +308,221 @@ PROCPS_EXPORT int procps_stat_get_sys_chain ( return 0; } + +/* + * procps_stat_read_jiffs: + * + * Read the cpu data out of /proc/stat putting the information + * into the dynamically acquired 'jiff_hists' hanging off the + * info structure. Along the way we gather historical stats and + * and embed the cpu summary line in the info structure as well + * ( since we had to read that first /proc/stat line anyway ). + * + * This is all private information but can be copied to caller + * supplied structures upon request. + */ +PROCPS_EXPORT int procps_stat_read_jiffs ( + struct procps_statinfo *info) +{ + #define ALLOCincr 32 + struct procps_jiffs_private *sum_ptr, *cpu_ptr; + char buf[8192], *bp; + int i, rc, size; + + if (info == NULL) + return -EINVAL; + + if (!info->jiff_hists_alloc) { + info->jiff_hists = calloc(ALLOCincr, sizeof(struct procps_jiffs_private)); + if (!(info->jiff_hists)) + return -ENOMEM; + info->jiff_hists_alloc = ALLOCincr; + info->jiff_hists_inuse = 0; + } + + if (-1 == info->stat_fd && (info->stat_fd = open(STAT_FILE, O_RDONLY)) == -1) + return -errno; + if (lseek(info->stat_fd, 0L, SEEK_SET) == -1) + return -errno; + for (;;) { + if ((size = read(info->stat_fd, buf, sizeof(buf)-1)) < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -errno; + } + break; + } + if (size == 0) + return 0; + buf[size] = '\0'; + bp = buf; + + sum_ptr = &info->cpu_summary; + // remember from last time around + memcpy(&sum_ptr->cpu.old, &sum_ptr->cpu.new, sizeof(struct procps_jiffs)); + // then value the summary line + if (8 > sscanf(bp, "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu" + , &sum_ptr->cpu.new.user, &sum_ptr->cpu.new.nice, &sum_ptr->cpu.new.system + , &sum_ptr->cpu.new.idle, &sum_ptr->cpu.new.iowait, &sum_ptr->cpu.new.irq + , &sum_ptr->cpu.new.sirq, &sum_ptr->cpu.new.stolen + , &sum_ptr->cpu.new.guest, &sum_ptr->cpu.new.gnice)) + return -1; + sum_ptr->cpu.id = -1; // mark as summary + + i = 0; +reap_em_again: + cpu_ptr = info->jiff_hists + i; // adapt to relocated if reap_em_again + do { + bp = 1 + strchr(bp, '\n'); + // remember from last time around + memcpy(&cpu_ptr->cpu.old, &cpu_ptr->cpu.new, sizeof(struct procps_jiffs)); + if (8 > (rc = sscanf(bp, "cpu%d %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu" + , &cpu_ptr->cpu.id + , &cpu_ptr->cpu.new.user, &cpu_ptr->cpu.new.nice, &cpu_ptr->cpu.new.system + , &cpu_ptr->cpu.new.idle, &cpu_ptr->cpu.new.iowait, &cpu_ptr->cpu.new.irq + , &cpu_ptr->cpu.new.sirq, &cpu_ptr->cpu.new.stolen + , &cpu_ptr->cpu.new.guest, &cpu_ptr->cpu.new.gnice))) { + memmove(cpu_ptr, sum_ptr, sizeof(struct procps_jiffs_hist)); + break; // we must tolerate cpus taken offline + } + ++i; + ++cpu_ptr; + } while (i < info->jiff_hists_alloc); + + if (i == info->jiff_hists_alloc && rc >= 8) { + info->jiff_hists_alloc += ALLOCincr; + info->jiff_hists = realloc(info->jiff_hists, info->jiff_hists_alloc * sizeof(struct procps_jiffs_private)); + if (!(info->jiff_hists)) + return -ENOMEM; + goto reap_em_again; + } + + info->jiff_hists_inuse = i; + return i; + #undef ALLOCincr +} + +/* + * procps_stat_get_jiffs: + * + * Return the designated cpu data in the caller supplied structure. + * A negative 'which' denotes the cpu_summary, not a real cpu. + * + * This function deals only with the 'current' jiffs counts. + */ +PROCPS_EXPORT int procps_stat_get_jiffs ( + struct procps_statinfo *info, + struct procps_jiffs *item, + int which) +{ + struct procps_jiffs_private *p; + int i; + + if (info == NULL || item == NULL) + return -EINVAL; + if (which < 0) { + // note, we're just copying the 'new' portion of our procps_jiffs_private + memcpy(item, &info->cpu_summary, sizeof(struct procps_jiffs)); + return 0; + } + p = info->jiff_hists; + for (i = 0; i < info->jiff_hists_inuse; i++) { + if (p->cpu.id == which) { + // note, we're just copying the 'new' portion of our procps_jiffs_private + memcpy(item, p, sizeof(struct procps_jiffs)); + return 0; + } + ++p; + } + return -1; +} + +/* + * procps_stat_get_jiffs_all: + * + * Return all available cpu data in the caller supplied structures, + * up to the lesser of numitems or total available. + * + * We tolerate a numitems greater than the total available, and + * the caller had better tolerate fewer returned than requested. + * + * This function deals only with the 'current' jiffs counts. + */ +PROCPS_EXPORT int procps_stat_get_jiffs_all ( + struct procps_statinfo *info, + struct procps_jiffs *item, + int numitems) +{ + int i; + + if (info == NULL || item == NULL) + return -EINVAL; + if (!info->jiff_hists_inuse) + return -1; + for (i = 0; i < info->jiff_hists_inuse && i < numitems; i++) { + // note, we're just copying the 'new' portion of our procps_jiffs_private + memcpy(item + i, info->jiff_hists + i, sizeof(struct procps_jiffs)); + } + return i; +} + +/* + * procps_stat_get_jiffs_hist: + * + * Return the designated cpu data in the caller supplied structure. + * A negative 'which' denotes the cpu_summary, not a real cpu. + * + * This function provides both 'new' and 'old' jiffs counts. + */ +PROCPS_EXPORT int procps_stat_get_jiffs_hist ( + struct procps_statinfo *info, + struct procps_jiffs_hist *item, + int which) +{ + struct procps_jiffs_private *p; + int i; + + if (info == NULL || item == NULL) + return -EINVAL; + if (which < 0) { + memcpy(item, &info->cpu_summary, sizeof(struct procps_jiffs_hist)); + return 0; + } + p = info->jiff_hists; + for (i = 0; i < info->jiff_hists_inuse; i++) { + if (p->cpu.id == which) { + memcpy(item, p, sizeof(struct procps_jiffs_hist)); + return 0; + } + ++p; + } + return -1; +} + +/* + * procps_stat_get_jiffs_hist_all: + * + * Return all available cpu data in the caller supplied structures, + * up to the lesser of numitems or total available. + * + * We tolerate a numitems greater than the total available, and + * the caller had better tolerate fewer returned than requested. + * + * This function provides both 'new' and 'old' jiffs counts. + */ +PROCPS_EXPORT int procps_stat_get_jiffs_hist_all ( + struct procps_statinfo *info, + struct procps_jiffs_hist *item, + int numitems) +{ + int i; + + if (info == NULL || item == NULL) + return -EINVAL; + if (!info->jiff_hists_inuse) + return -1; + for (i = 0; i < info->jiff_hists_inuse && i < numitems; i++) { + memcpy(item + i, info->jiff_hists + i, sizeof(struct procps_jiffs_hist)); + } + return i; +} diff --git a/proc/readstat.h b/proc/readstat.h index 77cffc98..d155524a 100644 --- a/proc/readstat.h +++ b/proc/readstat.h @@ -52,6 +52,16 @@ struct procps_cpu_result { struct procps_cpu_result *next; }; +struct procps_jiffs { + jiff user, nice, system, idle, iowait, irq, sirq, stolen, guest, gnice; +}; + +struct procps_jiffs_hist { + struct procps_jiffs new; + struct procps_jiffs old; + int id; +}; + struct procps_sys_result { enum procps_stat_item item; int result; @@ -62,12 +72,17 @@ struct procps_statinfo; int procps_stat_new (struct procps_statinfo **info); int procps_stat_read (struct procps_statinfo *info, const int cpu_only); +int procps_stat_read_jiffs (struct procps_statinfo *info); struct procps_statinfo *procps_stat_ref (struct procps_statinfo *info); struct procps_statinfo *procps_stat_unref (struct procps_statinfo *info); jiff procps_stat_get_cpu (struct procps_statinfo *info, enum procps_cpu_item item); int procps_stat_get_cpu_chain (struct procps_statinfo *info, struct procps_cpu_result *item); +int procps_stat_get_jiffs (struct procps_statinfo *info, struct procps_jiffs *item, int which); +int procps_stat_get_jiffs_all (struct procps_statinfo *info, struct procps_jiffs *item, int numitems); +int procps_stat_get_jiffs_hist (struct procps_statinfo *info, struct procps_jiffs_hist *item, int which); +int procps_stat_get_jiffs_hist_all (struct procps_statinfo *info, struct procps_jiffs_hist *item, int numitems); unsigned int procps_stat_get_sys (struct procps_statinfo *info, enum procps_stat_item item); int procps_stat_get_sys_chain (struct procps_statinfo *info, struct procps_sys_result *item);