library: rename those readstat.c & h sources to 'stat'
In an attempt to normalize the 'stat' interface, we'll first shed any reminders of the old readproc interface by changing file names to be more descriptive & brief. Signed-off-by: Jim Warner <james.warner@comcast.net>
This commit is contained in:
563
proc/stat.c
Normal file
563
proc/stat.c
Normal file
@@ -0,0 +1,563 @@
|
||||
/*
|
||||
* libprocps - Library to read proc filesystem
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <proc/stat.h>
|
||||
#include "procps-private.h"
|
||||
|
||||
#define STAT_FILE "/proc/stat"
|
||||
|
||||
struct stat_data {
|
||||
struct procps_jiffs cpu;
|
||||
unsigned int intr;
|
||||
unsigned int ctxt;
|
||||
unsigned int btime;
|
||||
unsigned int procs;
|
||||
unsigned int procs_blocked;
|
||||
unsigned int procs_running;
|
||||
};
|
||||
|
||||
struct procps_jiffs_private {
|
||||
struct procps_jiffs_hist cpu;
|
||||
// future additions go here, please
|
||||
};
|
||||
|
||||
struct procps_stat {
|
||||
int refcount;
|
||||
int stat_fd;
|
||||
struct stat_data data;
|
||||
int jiff_hists_alloc;
|
||||
int jiff_hists_inuse;
|
||||
struct procps_jiffs_private cpu_summary;
|
||||
struct procps_jiffs_private *jiff_hists;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* procps_stat_new:
|
||||
*
|
||||
* Create a new container to hold the stat information
|
||||
*
|
||||
* The initial refcount is 1, and needs to be decremented
|
||||
* to release the resources of the structure.
|
||||
*
|
||||
* Returns: a new stat info container
|
||||
*/
|
||||
PROCPS_EXPORT int procps_stat_new (
|
||||
struct procps_stat **info)
|
||||
{
|
||||
struct procps_stat *v;
|
||||
v = calloc(1, sizeof(struct procps_stat));
|
||||
if (!v)
|
||||
return -ENOMEM;
|
||||
|
||||
v->refcount = 1;
|
||||
v->stat_fd = -1;
|
||||
/* v->jiff_hists_alloc = 0; unnecessary with calloc, */
|
||||
/* v->jiff_hists_inuse = 0; but serves as a reminder */
|
||||
*info = v;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* procps_stat_read:
|
||||
*
|
||||
* Read the data out of /proc/stat putting the information
|
||||
* into the supplied info structure.
|
||||
*
|
||||
* If CPU stats only needed, set cpu_only to non-zero
|
||||
*/
|
||||
PROCPS_EXPORT int procps_stat_read (
|
||||
struct procps_stat *info,
|
||||
const int cpu_only)
|
||||
{
|
||||
char buf[8192];
|
||||
char *head, *tail;
|
||||
int size;
|
||||
|
||||
if (info == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&(info->data), 0, sizeof(struct stat_data));
|
||||
/* read in the data */
|
||||
|
||||
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';
|
||||
|
||||
/* Scan the file */
|
||||
head = buf;
|
||||
do {
|
||||
tail = strchr(head, ' ');
|
||||
if (!tail)
|
||||
break;
|
||||
*tail = '\0';
|
||||
if (0 == strcmp(head, "cpu")) {
|
||||
if (sscanf(tail+1, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
|
||||
&(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;
|
||||
|
||||
if (cpu_only)
|
||||
return 0; // we got what we need
|
||||
} else if (0 == strcmp(head, "intr")) {
|
||||
info->data.intr = strtoul(tail+1, &tail, 10);
|
||||
} else if (0 == strcmp(head, "ctxt")) {
|
||||
info->data.ctxt = strtoul(tail+1, &tail, 10);
|
||||
} else if (0 == strcmp(head, "btime")) {
|
||||
info->data.btime = strtoul(tail+1, &tail, 10);
|
||||
} else if (0 == strcmp(head, "processes")) {
|
||||
info->data.procs = strtoul(tail+1, &tail, 10);
|
||||
} else if (0 == strcmp(head, "procs_blocked")) {
|
||||
info->data.procs_blocked = strtoul(tail+1, &tail, 10);
|
||||
} else if (0 == strcmp(head, "procs_running")) {
|
||||
info->data.procs_running = strtoul(tail+1, &tail, 10);
|
||||
}
|
||||
if (tail[0] != '\n')
|
||||
tail = strchr(tail+1, '\n');
|
||||
if (!tail)
|
||||
break;
|
||||
head = tail + 1;
|
||||
} while (tail);
|
||||
if (info->data.procs)
|
||||
info->data.procs--; // exclude this process
|
||||
return 0;
|
||||
}
|
||||
|
||||
PROCPS_EXPORT int procps_stat_ref (
|
||||
struct procps_stat *info)
|
||||
{
|
||||
if (info == NULL)
|
||||
return -EINVAL;
|
||||
info->refcount++;
|
||||
return info->refcount;
|
||||
}
|
||||
|
||||
PROCPS_EXPORT int procps_stat_unref (
|
||||
struct procps_stat **info)
|
||||
{
|
||||
if (info == NULL || *info == NULL)
|
||||
return -EINVAL;
|
||||
(*info)->refcount--;
|
||||
if ((*info)->refcount == 0) {
|
||||
if ((*info)->jiff_hists != NULL)
|
||||
free((*info)->jiff_hists);
|
||||
free(*info);
|
||||
*info = NULL;
|
||||
return 0;
|
||||
}
|
||||
return (*info)->refcount;
|
||||
}
|
||||
|
||||
PROCPS_EXPORT jiff procps_stat_cpu_get (
|
||||
struct procps_stat *info,
|
||||
enum procps_cpu_item item)
|
||||
{
|
||||
switch (item) {
|
||||
case PROCPS_CPU_USER:
|
||||
return info->data.cpu.user;
|
||||
case PROCPS_CPU_NICE:
|
||||
return info->data.cpu.nice;
|
||||
case PROCPS_CPU_SYSTEM:
|
||||
return info->data.cpu.system;
|
||||
case PROCPS_CPU_IDLE:
|
||||
return info->data.cpu.idle;
|
||||
case PROCPS_CPU_IOWAIT:
|
||||
return info->data.cpu.iowait;
|
||||
case PROCPS_CPU_IRQ:
|
||||
return info->data.cpu.irq;
|
||||
case PROCPS_CPU_SIRQ:
|
||||
return info->data.cpu.sirq;
|
||||
case PROCPS_CPU_STOLEN:
|
||||
return info->data.cpu.stolen;
|
||||
case PROCPS_CPU_GUEST:
|
||||
return info->data.cpu.guest;
|
||||
case PROCPS_CPU_GNICE:
|
||||
return info->data.cpu.gnice;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PROCPS_EXPORT int procps_stat_cpu_getstack (
|
||||
struct procps_stat *info,
|
||||
struct stat_result *these)
|
||||
{
|
||||
if (these == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
for (;;) {
|
||||
switch (these->item) {
|
||||
case PROCPS_CPU_USER:
|
||||
these->result.jiff = info->data.cpu.user;
|
||||
break;
|
||||
case PROCPS_CPU_NICE:
|
||||
these->result.jiff = info->data.cpu.nice;
|
||||
break;
|
||||
case PROCPS_CPU_SYSTEM:
|
||||
these->result.jiff = info->data.cpu.system;
|
||||
break;
|
||||
case PROCPS_CPU_IDLE:
|
||||
these->result.jiff = info->data.cpu.idle;
|
||||
break;
|
||||
case PROCPS_CPU_IOWAIT:
|
||||
these->result.jiff = info->data.cpu.iowait;
|
||||
break;
|
||||
case PROCPS_CPU_IRQ:
|
||||
these->result.jiff = info->data.cpu.irq;
|
||||
break;
|
||||
case PROCPS_CPU_SIRQ:
|
||||
these->result.jiff = info->data.cpu.sirq;
|
||||
break;
|
||||
case PROCPS_CPU_STOLEN:
|
||||
these->result.jiff = info->data.cpu.stolen;
|
||||
break;
|
||||
case PROCPS_CPU_GUEST:
|
||||
these->result.jiff = info->data.cpu.guest;
|
||||
break;
|
||||
case PROCPS_CPU_GNICE:
|
||||
these->result.jiff = info->data.cpu.gnice;
|
||||
break;
|
||||
case PROCPS_CPU_noop:
|
||||
// don't disturb potential user data in the result struct
|
||||
break;
|
||||
case PROCPS_CPU_stack_end:
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
++these;
|
||||
}
|
||||
}
|
||||
|
||||
PROCPS_EXPORT unsigned int procps_stat_sys_get (
|
||||
struct procps_stat *info,
|
||||
enum procps_stat_item item)
|
||||
{
|
||||
switch (item) {
|
||||
case PROCPS_STAT_INTR:
|
||||
return info->data.intr;
|
||||
case PROCPS_STAT_CTXT:
|
||||
return info->data.ctxt;
|
||||
case PROCPS_STAT_BTIME:
|
||||
return info->data.btime;
|
||||
case PROCPS_STAT_PROCS:
|
||||
return info->data.procs;
|
||||
case PROCPS_STAT_PROCS_BLK:
|
||||
return info->data.procs_blocked;
|
||||
case PROCPS_STAT_PROCS_RUN:
|
||||
return info->data.procs_running;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PROCPS_EXPORT int procps_stat_sys_getstack (
|
||||
struct procps_stat *info,
|
||||
struct stat_result *these)
|
||||
{
|
||||
if (these == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
for (;;) {
|
||||
switch (these->item) {
|
||||
case PROCPS_STAT_INTR:
|
||||
these->result.u_int = info->data.intr;
|
||||
break;
|
||||
case PROCPS_STAT_CTXT:
|
||||
these->result.u_int = info->data.ctxt;
|
||||
break;
|
||||
case PROCPS_STAT_BTIME:
|
||||
these->result.u_int = info->data.btime;
|
||||
break;
|
||||
case PROCPS_STAT_PROCS:
|
||||
these->result.u_int = info->data.procs;
|
||||
break;
|
||||
case PROCPS_STAT_PROCS_BLK:
|
||||
these->result.u_int = info->data.procs_blocked;
|
||||
break;
|
||||
case PROCPS_STAT_PROCS_RUN:
|
||||
these->result.u_int = info->data.procs_running;
|
||||
break;
|
||||
case PROCPS_STAT_noop:
|
||||
// don't disturb potential user data in the result struct
|
||||
break;
|
||||
case PROCPS_STAT_stack_end:
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
++these;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_stat *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 %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu"
|
||||
, &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 %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu"
|
||||
, &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_jiffs_get:
|
||||
*
|
||||
* 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_jiffs_get (
|
||||
struct procps_stat *info,
|
||||
struct procps_jiffs *dest,
|
||||
int which)
|
||||
{
|
||||
struct procps_jiffs_private *p;
|
||||
int i;
|
||||
|
||||
if (info == NULL || dest == NULL)
|
||||
return -EINVAL;
|
||||
if (which < 0) {
|
||||
// note, we're just copying the 'new' portion of our procps_jiffs_private
|
||||
memcpy(dest, &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(dest, p, sizeof(struct procps_jiffs));
|
||||
return 0;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* procps_stat_jiffs_fill:
|
||||
*
|
||||
* Refresh available cpu data, then return all cpu data in the caller
|
||||
* supplied structures, up to the lesser of maxdests or total available.
|
||||
*
|
||||
* We tolerate a maxdests 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_jiffs_fill (
|
||||
struct procps_stat *info,
|
||||
struct procps_jiffs *dests,
|
||||
int maxdests)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
if (info == NULL || dests == NULL)
|
||||
return -EINVAL;
|
||||
if ((rc = procps_stat_read_jiffs(info)) < 0)
|
||||
return rc;
|
||||
if (!info->jiff_hists_inuse)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < info->jiff_hists_inuse && i < maxdests; i++) {
|
||||
// note, we're just copying the 'new' portion of our procps_jiffs_private
|
||||
memcpy(dests + i, info->jiff_hists + i, sizeof(struct procps_jiffs));
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* procps_stat_jiffs_hist_get:
|
||||
*
|
||||
* 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_jiffs_hist_get (
|
||||
struct procps_stat *info,
|
||||
struct procps_jiffs_hist *dest,
|
||||
int which)
|
||||
{
|
||||
struct procps_jiffs_private *p;
|
||||
int i;
|
||||
|
||||
if (info == NULL || dest == NULL)
|
||||
return -EINVAL;
|
||||
if (which < 0) {
|
||||
memcpy(dest, &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(dest, p, sizeof(struct procps_jiffs_hist));
|
||||
return 0;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* procps_stat_jiffs_hist_fill:
|
||||
*
|
||||
* Refresh available cpu data, then return all cpu data in the caller
|
||||
* supplied structures, up to the lesser of maxdests or total available.
|
||||
*
|
||||
* We tolerate a maxdests 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_jiffs_hist_fill (
|
||||
struct procps_stat *info,
|
||||
struct procps_jiffs_hist *dests,
|
||||
int maxdests)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
if (info == NULL || dests == NULL)
|
||||
return -EINVAL;
|
||||
if ((rc = procps_stat_read_jiffs(info)) < 0)
|
||||
return rc;
|
||||
if (!info->jiff_hists_inuse)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < info->jiff_hists_inuse && i < maxdests; i++) {
|
||||
memcpy(dests + i, info->jiff_hists + i, sizeof(struct procps_jiffs_hist));
|
||||
}
|
||||
return i;
|
||||
}
|
Reference in New Issue
Block a user