root/opal/mca/pstat/linux/pstat_linux_module.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. linux_module_init
  2. linux_module_fini
  3. next_field
  4. convert_value
  5. query
  6. local_getline
  7. local_stripper
  8. local_getfields

   1 /*
   2  * Copyright (c) 2004-2008 The Trustees of Indiana University and Indiana
   3  *                         University Research and Technology
   4  *                         Corporation.  All rights reserved.
   5  * Copyright (c) 2004-2005 The University of Tennessee and The University
   6  *                         of Tennessee Research Foundation.  All rights
   7  *                         reserved.
   8  * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
   9  *                         University of Stuttgart.  All rights reserved.
  10  * Copyright (c) 2004-2005 The Regents of the University of California.
  11  *                         All rights reserved.
  12  * Copyright (c) 2006-2015 Cisco Systems, Inc.  All rights reserved.
  13  * Copyright (c) 2013      Los Alamos National Security, LLC.  All rights reserved.
  14  * Copyright (c) 2013      Intel, Inc.  All rights reserved.
  15  * Copyright (c) 2015      Research Organization for Information Science
  16  *                         and Technology (RIST). All rights reserved.
  17  *
  18  * $COPYRIGHT$
  19  *
  20  * Additional copyrights may follow
  21  *
  22  * $HEADER$
  23  */
  24 
  25 #include "opal_config.h"
  26 #include "opal/constants.h"
  27 
  28 /* This component will only be compiled on Linux, where we are
  29    guaranteed to have <unistd.h> and friends */
  30 #include <stdio.h>
  31 #include <fcntl.h>
  32 #include <unistd.h>
  33 #include <stdlib.h>
  34 #include <string.h>
  35 #include <errno.h>
  36 #include <ctype.h>
  37 #include <time.h>
  38 #ifdef HAVE_SYS_TIME_H
  39 #include <sys/time.h>
  40 #endif
  41 
  42 #include <sys/param.h>  /* for HZ to convert jiffies to actual time */
  43 
  44 #include "opal/dss/dss_types.h"
  45 #include "opal/util/argv.h"
  46 #include "opal/util/printf.h"
  47 
  48 #include "pstat_linux.h"
  49 
  50 /*
  51  * API functions
  52  */
  53 static int linux_module_init(void);
  54 static int query(pid_t pid,
  55                  opal_pstats_t *stats,
  56                  opal_node_stats_t *nstats);
  57 static int linux_module_fini(void);
  58 
  59 /*
  60  * Linux pstat module
  61  */
  62 const opal_pstat_base_module_t opal_pstat_linux_module = {
  63     /* Initialization function */
  64     linux_module_init,
  65     query,
  66     linux_module_fini
  67 };
  68 
  69 #define OPAL_STAT_MAX_LENGTH   1024
  70 
  71 /* Local functions */
  72 static char *local_getline(FILE *fp);
  73 static char *local_stripper(char *data);
  74 static void local_getfields(char *data, char ***fields);
  75 
  76 /* Local data */
  77 static char input[OPAL_STAT_MAX_LENGTH];
  78 
  79 static int linux_module_init(void)
  80 {
  81     return OPAL_SUCCESS;
  82 }
  83 
  84 static int linux_module_fini(void)
  85 {
  86     return OPAL_SUCCESS;
  87 }
  88 
  89 static char *next_field(char *ptr, int barrier)
  90 {
  91     int i=0;
  92 
  93     /* we are probably pointing to the last char
  94      * of the current field, so look for whitespace
  95      */
  96     while (!isspace(*ptr) && i < barrier) {
  97         ptr++;  /* step over the current char */
  98         i++;
  99     }
 100 
 101     /* now look for the next field */
 102     while (isspace(*ptr) && i < barrier) {
 103         ptr++;
 104         i++;
 105     }
 106 
 107     return ptr;
 108 }
 109 
 110 static float convert_value(char *value)
 111 {
 112     char *ptr;
 113     float fval;
 114 
 115     /* compute base value */
 116     fval = (float)strtoul(value, &ptr, 10);
 117     /* get the unit multiplier */
 118     if (NULL != ptr && NULL != strstr(ptr, "kB")) {
 119         fval /= 1024.0;
 120     }
 121     return fval;
 122 }
 123 
 124 static int query(pid_t pid,
 125                  opal_pstats_t *stats,
 126                  opal_node_stats_t *nstats)
 127 {
 128     char data[4096];
 129     int fd;
 130     size_t numchars;
 131     char *ptr, *eptr;
 132     int i;
 133     int len, itime;
 134     double dtime;
 135     FILE *fp;
 136     char *dptr, *value;
 137     char **fields;
 138     opal_diskstats_t *ds;
 139     opal_netstats_t *ns;
 140 
 141     if (NULL != stats) {
 142         /* record the time of this sample */
 143         gettimeofday(&stats->sample_time, NULL);
 144         /* check the nstats - don't do gettimeofday twice
 145          * as it is expensive
 146          */
 147         if (NULL != nstats) {
 148             nstats->sample_time.tv_sec = stats->sample_time.tv_sec;
 149             nstats->sample_time.tv_usec = stats->sample_time.tv_usec;
 150         }
 151     } else if (NULL != nstats) {
 152         /* record the time of this sample */
 153         gettimeofday(&nstats->sample_time, NULL);
 154     }
 155 
 156     if (NULL != stats) {
 157         /* create the stat filename for this proc */
 158         numchars = snprintf(data, sizeof(data), "/proc/%d/stat", pid);
 159         if (numchars >= sizeof(data)) {
 160             return OPAL_ERR_VALUE_OUT_OF_BOUNDS;
 161         }
 162 
 163         if (0 > (fd = open(data, O_RDONLY))) {
 164             /* can't access this file - most likely, this means we
 165              * aren't really on a supported system, or the proc no
 166              * longer exists. Just return an error
 167              */
 168             return OPAL_ERR_FILE_OPEN_FAILURE;
 169         }
 170 
 171         /* absorb all of the file's contents in one gulp - we'll process
 172          * it once it is in memory for speed
 173          */
 174         memset(data, 0, sizeof(data));
 175         len = read(fd, data, sizeof(data)-1);
 176         if (len < 0) {
 177             /* This shouldn't happen! */
 178             close(fd);
 179             return OPAL_ERR_FILE_OPEN_FAILURE;
 180         }
 181         close(fd);
 182 
 183         /* remove newline at end */
 184         data[len] = '\0';
 185 
 186         /* the stat file consists of a single line in a carefully formatted
 187          * form. Parse it field by field as per proc(3) to get the ones we want
 188          */
 189 
 190         /* we don't need to read the pid from the file - we already know it! */
 191         stats->pid = pid;
 192 
 193         /* the cmd is surrounded by parentheses - find the start */
 194         if (NULL == (ptr = strchr(data, '('))) {
 195             /* no cmd => something wrong with data, return error */
 196             return OPAL_ERR_BAD_PARAM;
 197         }
 198         /* step over the paren */
 199         ptr++;
 200 
 201         /* find the ending paren */
 202         if (NULL == (eptr = strchr(ptr, ')'))) {
 203             /* no end to cmd => something wrong with data, return error */
 204             return OPAL_ERR_BAD_PARAM;
 205         }
 206 
 207         /* save the cmd name, up to the limit of the array */
 208         i = 0;
 209         while (ptr < eptr && i < OPAL_PSTAT_MAX_STRING_LEN) {
 210             stats->cmd[i++] = *ptr++;
 211         }
 212 
 213         /* move to the next field in the data */
 214         ptr = next_field(eptr, len);
 215 
 216         /* next is the process state - a single character */
 217         stats->state[0] = *ptr;
 218         /* move to next field */
 219         ptr = next_field(ptr, len);
 220 
 221         /* skip fields until we get to the times */
 222         ptr = next_field(ptr, len); /* ppid */
 223         ptr = next_field(ptr, len); /* pgrp */
 224         ptr = next_field(ptr, len); /* session */
 225         ptr = next_field(ptr, len); /* tty_nr */
 226         ptr = next_field(ptr, len); /* tpgid */
 227         ptr = next_field(ptr, len); /* flags */
 228         ptr = next_field(ptr, len); /* minflt */
 229         ptr = next_field(ptr, len); /* cminflt */
 230         ptr = next_field(ptr, len); /* majflt */
 231         ptr = next_field(ptr, len); /* cmajflt */
 232 
 233         /* grab the process time usage fields */
 234         itime = strtoul(ptr, &ptr, 10);    /* utime */
 235         itime += strtoul(ptr, &ptr, 10);   /* add the stime */
 236         /* convert to time in seconds */
 237         dtime = (double)itime / (double)HZ;
 238         stats->time.tv_sec = (int)dtime;
 239         stats->time.tv_usec = (int)(1000000.0 * (dtime - stats->time.tv_sec));
 240         /* move to next field */
 241         ptr = next_field(ptr, len);
 242 
 243         /* skip fields until we get to priority */
 244         ptr = next_field(ptr, len); /* cutime */
 245         ptr = next_field(ptr, len); /* cstime */
 246 
 247         /* save the priority */
 248         stats->priority = strtol(ptr, &ptr, 10);
 249         /* move to next field */
 250         ptr = next_field(ptr, len);
 251 
 252         /* skip nice */
 253         ptr = next_field(ptr, len);
 254 
 255         /* get number of threads */
 256         stats->num_threads = strtoul(ptr, &ptr, 10);
 257         /* move to next field */
 258         ptr = next_field(ptr, len);
 259 
 260         /* skip fields until we get to processor id */
 261         ptr = next_field(ptr, len);  /* itrealvalue */
 262         ptr = next_field(ptr, len);  /* starttime */
 263         ptr = next_field(ptr, len);  /* vsize */
 264         ptr = next_field(ptr, len);  /* rss */
 265         ptr = next_field(ptr, len);  /* rss limit */
 266         ptr = next_field(ptr, len);  /* startcode */
 267         ptr = next_field(ptr, len);  /* endcode */
 268         ptr = next_field(ptr, len);  /* startstack */
 269         ptr = next_field(ptr, len);  /* kstkesp */
 270         ptr = next_field(ptr, len);  /* kstkeip */
 271         ptr = next_field(ptr, len);  /* signal */
 272         ptr = next_field(ptr, len);  /* blocked */
 273         ptr = next_field(ptr, len);  /* sigignore */
 274         ptr = next_field(ptr, len);  /* sigcatch */
 275         ptr = next_field(ptr, len);  /* wchan */
 276         ptr = next_field(ptr, len);  /* nswap */
 277         ptr = next_field(ptr, len);  /* cnswap */
 278         ptr = next_field(ptr, len);  /* exit_signal */
 279 
 280         /* finally - get the processor */
 281         stats->processor = strtol(ptr, NULL, 10);
 282 
 283         /* that's all we care about from this data - ignore the rest */
 284 
 285         /* now create the status filename for this proc */
 286         memset(data, 0, sizeof(data));
 287         numchars = snprintf(data, sizeof(data), "/proc/%d/status", pid);
 288         if (numchars >= sizeof(data)) {
 289             return OPAL_ERR_VALUE_OUT_OF_BOUNDS;
 290         }
 291 
 292         if (NULL == (fp = fopen(data, "r"))) {
 293             /* ignore this */
 294             return OPAL_SUCCESS;
 295         }
 296 
 297         /* parse it according to proc(3) */
 298         while (NULL != (dptr = local_getline(fp))) {
 299             if (NULL == (value = local_stripper(dptr))) {
 300                 /* cannot process */
 301                 continue;
 302             }
 303             /* look for VmPeak */
 304             if (0 == strncmp(dptr, "VmPeak", strlen("VmPeak"))) {
 305                 stats->peak_vsize = convert_value(value);
 306             } else if (0 == strncmp(dptr, "VmSize", strlen("VmSize"))) {
 307                 stats->vsize = convert_value(value);
 308             } else if (0 == strncmp(dptr, "VmRSS", strlen("VmRSS"))) {
 309                 stats->rss = convert_value(value);
 310             }
 311         }
 312         fclose(fp);
 313 
 314         /* now create the smaps filename for this proc */
 315         memset(data, 0, sizeof(data));
 316         numchars = snprintf(data, sizeof(data), "/proc/%d/smaps", pid);
 317         if (numchars >= sizeof(data)) {
 318             return OPAL_ERR_VALUE_OUT_OF_BOUNDS;
 319         }
 320 
 321         if (NULL == (fp = fopen(data, "r"))) {
 322             /* ignore this */
 323             return OPAL_SUCCESS;
 324         }
 325 
 326         /* parse it to find lines that start with "Pss" */
 327         while (NULL != (dptr = local_getline(fp))) {
 328             if (NULL == (value = local_stripper(dptr))) {
 329                 /* cannot process */
 330                 continue;
 331             }
 332             /* look for Pss */
 333             if (0 == strncmp(dptr, "Pss", strlen("Pss"))) {
 334                 stats->pss += convert_value(value);
 335             }
 336         }
 337         fclose(fp);
 338     }
 339 
 340     if (NULL != nstats) {
 341         /* get the loadavg data */
 342         if (0 > (fd = open("/proc/loadavg", O_RDONLY))) {
 343             /* not an error if we don't find this one as it
 344              * isn't critical
 345              */
 346             goto diskstats;
 347         }
 348 
 349         /* absorb all of the file's contents in one gulp - we'll process
 350          * it once it is in memory for speed
 351          */
 352         memset(data, 0, sizeof(data));
 353         len = read(fd, data, sizeof(data)-1);
 354         close(fd);
 355         if (len < 0) {
 356             goto diskstats;
 357         }
 358 
 359         /* remove newline at end */
 360         data[len] = '\0';
 361 
 362         /* we only care about the first three numbers */
 363         nstats->la = strtof(data, &ptr);
 364         nstats->la5 = strtof(ptr, &eptr);
 365         nstats->la15 = strtof(eptr, NULL);
 366 
 367         /* see if we can open the meminfo file */
 368         if (NULL == (fp = fopen("/proc/meminfo", "r"))) {
 369             /* ignore this */
 370             goto diskstats;
 371         }
 372 
 373         /* read the file one line at a time */
 374         while (NULL != (dptr = local_getline(fp))) {
 375             if (NULL == (value = local_stripper(dptr))) {
 376                 /* cannot process */
 377                 continue;
 378             }
 379             if (0 == strcmp(dptr, "MemTotal")) {
 380                 nstats->total_mem = convert_value(value);
 381             } else if (0 == strcmp(dptr, "MemFree")) {
 382                 nstats->free_mem = convert_value(value);
 383             } else if (0 == strcmp(dptr, "Buffers")) {
 384                 nstats->buffers = convert_value(value);
 385             } else if (0 == strcmp(dptr, "Cached")) {
 386                 nstats->cached = convert_value(value);
 387             } else if (0 == strcmp(dptr, "SwapCached")) {
 388                 nstats->swap_cached = convert_value(value);
 389             } else if (0 == strcmp(dptr, "SwapTotal")) {
 390                 nstats->swap_total = convert_value(value);
 391             } else if (0 == strcmp(dptr, "SwapFree")) {
 392                 nstats->swap_free = convert_value(value);
 393             } else if (0 == strcmp(dptr, "Mapped")) {
 394                 nstats->mapped = convert_value(value);
 395             }
 396         }
 397         fclose(fp);
 398 
 399     diskstats:
 400         /* look for the diskstats file */
 401         if (NULL == (fp = fopen("/proc/diskstats", "r"))) {
 402             /* not an error if we don't find this one as it
 403              * isn't critical
 404              */
 405             goto netstats;
 406         }
 407         /* read the file one line at a time */
 408         while (NULL != (dptr = local_getline(fp))) {
 409             /* look for the local disks */
 410             if (NULL == strstr(dptr, "sd")) {
 411                 continue;
 412             }
 413             /* parse to extract the fields */
 414             fields = NULL;
 415             local_getfields(dptr, &fields);
 416             if (NULL == fields) {
 417                 continue;
 418             }
 419             if (14 < opal_argv_count(fields)) {
 420                 opal_argv_free(fields);
 421                 continue;
 422             }
 423             /* pack the ones of interest into the struct */
 424             ds = OBJ_NEW(opal_diskstats_t);
 425             ds->disk = strdup(fields[2]);
 426             ds->num_reads_completed = strtoul(fields[3], NULL, 10);
 427             ds->num_reads_merged = strtoul(fields[4], NULL, 10);
 428             ds->num_sectors_read = strtoul(fields[5], NULL, 10);
 429             ds->milliseconds_reading = strtoul(fields[6], NULL, 10);
 430             ds->num_writes_completed = strtoul(fields[7], NULL, 10);
 431             ds->num_writes_merged = strtoul(fields[8], NULL, 10);
 432             ds->num_sectors_written = strtoul(fields[9], NULL, 10);
 433             ds->milliseconds_writing = strtoul(fields[10], NULL, 10);
 434             ds->num_ios_in_progress = strtoul(fields[11], NULL, 10);
 435             ds->milliseconds_io = strtoul(fields[12], NULL, 10);
 436             ds->weighted_milliseconds_io = strtoul(fields[13], NULL, 10);
 437             opal_list_append(&nstats->diskstats, &ds->super);
 438             opal_argv_free(fields);
 439         }
 440         fclose(fp);
 441 
 442     netstats:
 443         /* look for the netstats file */
 444         if (NULL == (fp = fopen("/proc/net/dev", "r"))) {
 445             /* not an error if we don't find this one as it
 446              * isn't critical
 447              */
 448             goto complete;
 449         }
 450         /* skip the first two lines as they are headers */
 451         local_getline(fp);
 452         local_getline(fp);
 453         /* read the file one line at a time */
 454         while (NULL != (dptr = local_getline(fp))) {
 455             /* the interface is at the start of the line */
 456             if (NULL == (ptr = strchr(dptr, ':'))) {
 457                 continue;
 458             }
 459             *ptr = '\0';
 460             ptr++;
 461             /* parse to extract the fields */
 462             fields = NULL;
 463             local_getfields(ptr, &fields);
 464             if (NULL == fields) {
 465                 continue;
 466             }
 467             /* pack the ones of interest into the struct */
 468             ns = OBJ_NEW(opal_netstats_t);
 469             ns->net_interface = strdup(dptr);
 470             ns->num_bytes_recvd = strtoul(fields[0], NULL, 10);
 471             ns->num_packets_recvd = strtoul(fields[1], NULL, 10);
 472             ns->num_recv_errs = strtoul(fields[2], NULL, 10);
 473             ns->num_bytes_sent = strtoul(fields[8], NULL, 10);
 474             ns->num_packets_sent = strtoul(fields[9], NULL, 10);
 475             ns->num_send_errs = strtoul(fields[10], NULL, 10);
 476             opal_list_append(&nstats->netstats, &ns->super);
 477             opal_argv_free(fields);
 478         }
 479         fclose(fp);
 480     }
 481 
 482  complete:
 483     return OPAL_SUCCESS;
 484 }
 485 
 486 static char *local_getline(FILE *fp)
 487 {
 488     char *ret, *ptr;
 489 
 490     ret = fgets(input, OPAL_STAT_MAX_LENGTH, fp);
 491     if (NULL != ret) {
 492         input[strlen(input)-1] = '\0';  /* remove newline */
 493         /* strip leading white space */
 494         ptr = input;
 495         while (!isalnum(*ptr)) {
 496             ptr++;
 497         }
 498         return ptr;
 499     }
 500 
 501     return NULL;
 502 }
 503 
 504 static char *local_stripper(char *data)
 505 {
 506     char *ptr, *end, *enddata;
 507     int len = strlen(data);
 508 
 509     /* find the colon */
 510     if (NULL == (end = strchr(data, ':'))) {
 511         return NULL;
 512     }
 513     ptr = end;
 514     --end;
 515     /* working backwards, look for first non-whitespace */
 516     while (end != data && !isalnum(*end)) {
 517         --end;
 518     }
 519     ++end;
 520     *end = '\0';
 521     /* now look for value */
 522     ptr++;
 523     enddata = &(data[len-1]);
 524     while (ptr != enddata && !isalnum(*ptr)) {
 525         ++ptr;
 526     }
 527     return ptr;
 528 }
 529 
 530 static void local_getfields(char *dptr, char ***fields)
 531 {
 532     char *ptr, *end;
 533 
 534     /* set default */
 535     *fields = NULL;
 536 
 537     /* find the beginning */
 538     ptr = dptr;
 539     while ('\0' != *ptr && !isalnum(*ptr)) {
 540         ptr++;
 541     }
 542     if ('\0' == *ptr) {
 543         return;
 544     }
 545 
 546     /* working from this point, find the end of each
 547      * alpha-numeric field and store it on the stack.
 548      * Then shift across the white space to the start
 549      * of the next one
 550      */
 551     end = ptr;  /* ptr points to an alnum */
 552     end++;  /* look at next character */
 553     while ('\0' != *end) {
 554         /* find the end of this alpha string */
 555         while ('\0' != *end && isalnum(*end)) {
 556             end++;
 557         }
 558         /* terminate it */
 559         *end = '\0';
 560         /* store it on the stack */
 561         opal_argv_append_nosize(fields, ptr);
 562         /* step across any white space */
 563         end++;
 564         while ('\0' != *end && !isalnum(*end)) {
 565             end++;
 566         }
 567         if ('\0' == *end) {
 568             ptr = NULL;
 569             break;
 570         }
 571         ptr = end;
 572         end++;
 573     }
 574     if (NULL != ptr) {
 575         /* have a hanging field */
 576         opal_argv_append_nosize(fields, ptr);
 577     }
 578 }

/* [<][>][^][v][top][bottom][index][help] */