#include "pmix_config.h"
#include "pmix_common.h"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <sys/param.h>
#include "pstat_linux.h"
#include "src/include/pmix_globals.h"
#include "src/util/pmix_argv.h"
#include "src/util/pmix_printf.h"
static int linux_module_init(void);
static int query(pid_t pid, pmix_proc_stats_t *stats, pmix_node_stats_t *nstats);
static int linux_module_fini(void);
const pmix_pstat_base_module_t pmix_pstat_linux_module = {
linux_module_init, query, linux_module_fini};
#define PMIX_STAT_MAX_LENGTH 1024
typedef struct {
pmix_list_item_t super;
pmix_disk_stats_t dstat;
} dstats_t;
static PMIX_CLASS_INSTANCE(dstats_t, pmix_list_item_t, NULL, NULL);
typedef struct {
pmix_list_item_t super;
pmix_net_stats_t nstat;
} ndstats_t;
static PMIX_CLASS_INSTANCE(ndstats_t, pmix_list_item_t, NULL, NULL);
static char *local_getline(FILE *fp);
static char *local_stripper(char *data);
static void local_getfields(char *data, char ***fields);
static char input[PMIX_STAT_MAX_LENGTH];
static int linux_module_init(void)
{
return PMIX_SUCCESS;
}
static int linux_module_fini(void)
{
return PMIX_SUCCESS;
}
static char *next_field(char *ptr, int barrier)
{
int i = 0;
while (!isspace(*ptr) && i < barrier) {
ptr++;
i++;
}
while (isspace(*ptr) && i < barrier) {
ptr++;
i++;
}
return ptr;
}
static float convert_value(char *value)
{
char *ptr;
float fval;
fval = (float) strtoul(value, &ptr, 10);
if (NULL != ptr && NULL != strstr(ptr, "kB")) {
fval /= 1024.0;
}
return fval;
}
static int query(pid_t pid, pmix_proc_stats_t *stats, pmix_node_stats_t *nstats)
{
char data[4096];
int fd;
size_t numchars;
char *ptr, *eptr;
int i;
int len, itime;
double dtime;
FILE *fp;
char *dptr, *value;
char **fields;
pmix_list_t cache;
dstats_t *ds;
ndstats_t *ns;
if (NULL != stats) {
gettimeofday(&stats->sample_time, NULL);
if (NULL != nstats) {
nstats->sample_time.tv_sec = stats->sample_time.tv_sec;
nstats->sample_time.tv_usec = stats->sample_time.tv_usec;
}
} else if (NULL != nstats) {
gettimeofday(&nstats->sample_time, NULL);
}
if (NULL != stats) {
stats->node = strdup(pmix_globals.hostname);
numchars = pmix_snprintf(data, sizeof(data), "/proc/%d/stat", pid);
if (numchars >= sizeof(data)) {
return PMIX_ERROR;
}
if (0 > (fd = open(data, O_RDONLY))) {
return PMIX_ERROR;
}
memset(data, 0, sizeof(data));
len = read(fd, data, sizeof(data) - 1);
if (len < 0) {
close(fd);
return PMIX_ERROR;
}
close(fd);
data[len] = '\0';
stats->pid = pid;
if (NULL == (ptr = strchr(data, '('))) {
return PMIX_ERR_BAD_PARAM;
}
ptr++;
if (NULL == (eptr = strchr(ptr, ')'))) {
return PMIX_ERR_BAD_PARAM;
}
*eptr = '\0';
stats->cmd = strdup(ptr);
*eptr = ')';
ptr = next_field(eptr, len);
stats->state = *ptr;
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
itime = strtoul(ptr, &ptr, 10);
itime += strtoul(ptr, &ptr, 10);
dtime = (double) itime / (double) HZ;
stats->time.tv_sec = (int) dtime;
stats->time.tv_usec = (int) (1000000.0 * (dtime - stats->time.tv_sec));
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
stats->priority = strtol(ptr, &ptr, 10);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
stats->num_threads = strtoul(ptr, &ptr, 10);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
ptr = next_field(ptr, len);
stats->processor = strtol(ptr, NULL, 10);
memset(data, 0, sizeof(data));
numchars = pmix_snprintf(data, sizeof(data), "/proc/%d/status", pid);
if (numchars >= sizeof(data)) {
return PMIX_ERROR;
}
if (NULL == (fp = fopen(data, "r"))) {
return PMIX_SUCCESS;
}
while (NULL != (dptr = local_getline(fp))) {
if (NULL == (value = local_stripper(dptr))) {
continue;
}
if (0 == strncmp(dptr, "VmPeak", strlen("VmPeak"))) {
stats->peak_vsize = convert_value(value);
} else if (0 == strncmp(dptr, "VmSize", strlen("VmSize"))) {
stats->vsize = convert_value(value);
} else if (0 == strncmp(dptr, "VmRSS", strlen("VmRSS"))) {
stats->rss = convert_value(value);
}
}
fclose(fp);
memset(data, 0, sizeof(data));
numchars = pmix_snprintf(data, sizeof(data), "/proc/%d/smaps", pid);
if (numchars >= sizeof(data)) {
return PMIX_ERROR;
}
if (NULL == (fp = fopen(data, "r"))) {
return PMIX_SUCCESS;
}
while (NULL != (dptr = local_getline(fp))) {
if (NULL == (value = local_stripper(dptr))) {
continue;
}
if (0 == strncmp(dptr, "Pss", strlen("Pss"))) {
stats->pss += convert_value(value);
}
}
fclose(fp);
}
if (NULL != nstats) {
nstats->node = strdup(pmix_globals.hostname);
if (0 > (fd = open("/proc/loadavg", O_RDONLY))) {
goto diskstats;
}
memset(data, 0, sizeof(data));
len = read(fd, data, sizeof(data) - 1);
close(fd);
if (len < 0) {
goto diskstats;
}
data[len] = '\0';
nstats->la = strtof(data, &ptr);
nstats->la5 = strtof(ptr, &eptr);
nstats->la15 = strtof(eptr, NULL);
if (NULL == (fp = fopen("/proc/meminfo", "r"))) {
goto diskstats;
}
while (NULL != (dptr = local_getline(fp))) {
if (NULL == (value = local_stripper(dptr))) {
continue;
}
if (0 == strcmp(dptr, "MemTotal")) {
nstats->total_mem = convert_value(value);
} else if (0 == strcmp(dptr, "MemFree")) {
nstats->free_mem = convert_value(value);
} else if (0 == strcmp(dptr, "Buffers")) {
nstats->buffers = convert_value(value);
} else if (0 == strcmp(dptr, "Cached")) {
nstats->cached = convert_value(value);
} else if (0 == strcmp(dptr, "SwapCached")) {
nstats->swap_cached = convert_value(value);
} else if (0 == strcmp(dptr, "SwapTotal")) {
nstats->swap_total = convert_value(value);
} else if (0 == strcmp(dptr, "SwapFree")) {
nstats->swap_free = convert_value(value);
} else if (0 == strcmp(dptr, "Mapped")) {
nstats->mapped = convert_value(value);
}
}
fclose(fp);
diskstats:
if (NULL == (fp = fopen("/proc/diskstats", "r"))) {
goto netstats;
}
PMIX_CONSTRUCT(&cache, pmix_list_t);
while (NULL != (dptr = local_getline(fp))) {
if (NULL == strstr(dptr, "sd")) {
continue;
}
fields = NULL;
local_getfields(dptr, &fields);
if (NULL == fields) {
continue;
}
if (14 < PMIx_Argv_count(fields)) {
PMIx_Argv_free(fields);
continue;
}
ds = PMIX_NEW(dstats_t);
ds->dstat.disk = strdup(fields[2]);
ds->dstat.num_reads_completed = strtoul(fields[3], NULL, 10);
ds->dstat.num_reads_merged = strtoul(fields[4], NULL, 10);
ds->dstat.num_sectors_read = strtoul(fields[5], NULL, 10);
ds->dstat.milliseconds_reading = strtoul(fields[6], NULL, 10);
ds->dstat.num_writes_completed = strtoul(fields[7], NULL, 10);
ds->dstat.num_writes_merged = strtoul(fields[8], NULL, 10);
ds->dstat.num_sectors_written = strtoul(fields[9], NULL, 10);
ds->dstat.milliseconds_writing = strtoul(fields[10], NULL, 10);
ds->dstat.num_ios_in_progress = strtoul(fields[11], NULL, 10);
ds->dstat.milliseconds_io = strtoul(fields[12], NULL, 10);
ds->dstat.weighted_milliseconds_io = strtoul(fields[13], NULL, 10);
pmix_list_append(&cache, &ds->super);
PMIx_Argv_free(fields);
}
fclose(fp);
if (0 < (len = pmix_list_get_size(&cache))) {
PMIX_DISK_STATS_CREATE(nstats->diskstats, len);
i = 0;
PMIX_LIST_FOREACH (ds, &cache, dstats_t) {
memcpy(&nstats->diskstats[i], &ds->dstat, sizeof(pmix_disk_stats_t));
++i;
}
}
PMIX_LIST_DESTRUCT(&cache);
netstats:
if (NULL == (fp = fopen("/proc/net/dev", "r"))) {
goto complete;
}
local_getline(fp);
local_getline(fp);
PMIX_CONSTRUCT(&cache, pmix_list_t);
while (NULL != (dptr = local_getline(fp))) {
if (NULL == (ptr = strchr(dptr, ':'))) {
continue;
}
*ptr = '\0';
ptr++;
fields = NULL;
local_getfields(ptr, &fields);
if (NULL == fields) {
continue;
}
ns = PMIX_NEW(ndstats_t);
ns->nstat.net_interface = strdup(dptr);
ns->nstat.num_bytes_recvd = strtoul(fields[0], NULL, 10);
ns->nstat.num_packets_recvd = strtoul(fields[1], NULL, 10);
ns->nstat.num_recv_errs = strtoul(fields[2], NULL, 10);
ns->nstat.num_bytes_sent = strtoul(fields[8], NULL, 10);
ns->nstat.num_packets_sent = strtoul(fields[9], NULL, 10);
ns->nstat.num_send_errs = strtoul(fields[10], NULL, 10);
pmix_list_append(&cache, &ns->super);
PMIx_Argv_free(fields);
}
fclose(fp);
if (0 < (len = pmix_list_get_size(&cache))) {
PMIX_NET_STATS_CREATE(nstats->netstats, len);
i = 0;
PMIX_LIST_FOREACH (ns, &cache, ndstats_t) {
memcpy(&nstats->netstats[i], &ns->nstat, sizeof(pmix_net_stats_t));
++i;
}
}
PMIX_LIST_DESTRUCT(&cache);
}
complete:
return PMIX_SUCCESS;
}
static char *local_getline(FILE *fp)
{
char *ret, *ptr;
ret = fgets(input, PMIX_STAT_MAX_LENGTH, fp);
if (NULL != ret) {
input[strlen(input) - 1] = '\0';
ptr = input;
while (!isalnum(*ptr)) {
ptr++;
}
return ptr;
}
return NULL;
}
static char *local_stripper(char *data)
{
char *ptr, *end, *enddata;
int len = strlen(data);
if (NULL == (end = strchr(data, ':'))) {
return NULL;
}
ptr = end;
--end;
while (end != data && !isalnum(*end)) {
--end;
}
++end;
*end = '\0';
ptr++;
enddata = &(data[len - 1]);
while (ptr != enddata && !isalnum(*ptr)) {
++ptr;
}
return ptr;
}
static void local_getfields(char *dptr, char ***fields)
{
char *ptr, *end;
*fields = NULL;
ptr = dptr;
while ('\0' != *ptr && !isalnum(*ptr)) {
ptr++;
}
if ('\0' == *ptr) {
return;
}
end = ptr;
end++;
while ('\0' != *end) {
while ('\0' != *end && isalnum(*end)) {
end++;
}
*end = '\0';
PMIx_Argv_append_nosize(fields, ptr);
end++;
while ('\0' != *end && !isalnum(*end)) {
end++;
}
if ('\0' == *end) {
ptr = NULL;
break;
}
ptr = end;
end++;
}
if (NULL != ptr) {
PMIx_Argv_append_nosize(fields, ptr);
}
}