libutil++  1.9.3
 All Classes Functions Variables
Process.cpp
1 /*
2 ** libutil++
3 ** $Id: Process.cpp 1653 2016-02-28 19:54:59Z sella $
4 ** Copyright (c) 2011-2016 Digital Genesis, LLC. All Rights Reserved.
5 ** Released under the LGPL Version 2.1 License.
6 ** http://www.digitalgenesis.com
7 */
8 
9 static const char rcsid[] __attribute__((used)) = "$Id: Process.cpp 1653 2016-02-28 19:54:59Z sella $";
10 
11 #include "Process.h"
12 #include "CommonMacro.h"
13 #include "String.h"
14 #include "Time.h"
15 
16 #include <fcntl.h>
17 #include <limits.h>
18 #include <libgen.h>
19 #include <signal.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <dirent.h>
25 #include <execinfo.h>
26 #include <sys/time.h>
27 #include <sys/times.h>
28 #include <sys/stat.h>
29 #include <sys/prctl.h>
30 #include <sys/types.h>
31 #include <sys/syscall.h>
32 #include <sys/sysinfo.h>
33 #include <sys/resource.h>
34 
35 #include <thread>
36 
37 #ifdef __GNUG__
38  #include <cxxabi.h>
39 #endif
40 
41 #define CPU_COUNT_CXX11
42 
43 using namespace sella::util;
44 
45 Process::Process() throw (ProcessException) {
46  struct tms timeSample;
47 
48  stats.resize(16);
49  iostats.resize(8);
50 
51  ncpu = getProcessorCount();
52  jiffies = ::sysconf(_SC_CLK_TCK);
53 
54  prev = last = Time::clock_monotonic_coarse();
55 
56  lastCPU = times(&timeSample);
57  lastSysCPU = timeSample.tms_stime;
58  lastUserCPU = timeSample.tms_utime;
59 
60  update();
61 }
62 
63 Process::~Process() {
64 }
65 
66 void Process::update(void) throw (ProcessException) {
67  if (sysinfo(&info) == -1) {
68  memset(&info, 0, sizeof(info));
69 
70  THROW2(ProcessException, "sysinfo");
71  }
72 
73  try {
74  updateCPU();
75  } catch (...) { };
76 
77  try {
78  updateFDs();
79  } catch (...) { };
80 
81  try {
82  updateStats();
83  } catch (...) { };
84 
85  try {
86  updateIOStats();
87  } catch (...) { };
88 }
89 
90 uint64_t Process::getUpdateIntervalUsec(void) const throw () {
91  struct timespec dst = last;
92 
93  return Time::tstousec(Time::subtractts(dst, prev));
94 }
95 
96 uint64_t Process::getUpdateIntervalSec(void) const throw () {
97  struct timespec dst = last;
98 
99  return Time::tstosec(Time::subtractts(dst, prev));
100 }
101 
102 uint64_t Process::getStartTime(void) const throw () {
103  return getStartTime(getPID());
104 }
105 
106 uint64_t Process::getUptime(void) const throw () {
107  return time(NULL) - getStartTime(getPID());
108 }
109 
110 int Process::getCPU(bool scale) const throw () {
111  if (scale) {
112  return (cpu / ncpu);
113  }
114 
115  return cpu;
116 }
117 
118 int Process::getUserCPU(bool scale) const throw () {
119  if (scale) {
120  return (userCPU / ncpu);
121  }
122 
123  return userCPU;
124 }
125 
126 int Process::getSystemCPU(bool scale) const throw () {
127  if (scale) {
128  return (sysCPU / ncpu);
129  }
130 
131  return sysCPU;
132 }
133 
134 int Process::getMemory(void) const throw () {
135  return (getRSS() / getSystemMemory());
136 }
137 
138 char Process::getState(void) const throw () {
139  return stats[0];
140 }
141 
142 uint64_t Process::getFDs(void) const throw () {
143  return fds;
144 }
145 
146 uint64_t Process::getFDSize(void) const throw () {
147  return stats[1];
148 }
149 
150 uint64_t Process::getVMPeak(void) const throw () {
151  return stats[2];
152 }
153 
154 uint64_t Process::getVMSize(void) const throw () {
155  return stats[3];
156 }
157 
158 uint64_t Process::getLocked(void) const throw () {
159  return stats[4];
160 }
161 
162 uint64_t Process::getRSSHighWaterMark(void) const throw () {
163  return stats[5];
164 }
165 
166 uint64_t Process::getRSS(void) const throw () {
167  return stats[6];
168 }
169 
170 uint64_t Process::getData(void) const throw () {
171  return stats[7];
172 }
173 
174 uint64_t Process::getStack(void) const throw () {
175  return stats[8];
176 }
177 
178 uint64_t Process::getEXE(void) const throw () {
179  return stats[9];
180 }
181 
182 uint64_t Process::getLib(void) const throw () {
183  return stats[10];
184 }
185 
186 uint64_t Process::getPTE(void) const throw () {
187  return stats[11];
188 }
189 
190 uint64_t Process::getSwap(void) const throw () {
191  return stats[12];
192 }
193 
194 uint64_t Process::getThreads(void) const throw () {
195  return stats[13];
196 }
197 
198 uint64_t Process::getVoluntaryContextSwitch(void) const throw () {
199  return stats[14];
200 }
201 
202 uint64_t Process::getNonVoluntaryContextSwitch(void) const throw () {
203  return stats[15];
204 }
205 
206 uint64_t Process::getBytesRead(void) const throw () {
207  return iostats[0];
208 }
209 
210 uint64_t Process::getBytesWrite(void) const throw () {
211  return iostats[1];
212 }
213 
214 uint64_t Process::getReadSyscall(void) const throw () {
215  return iostats[2];
216 }
217 
218 uint64_t Process::getWriteSyscall(void) const throw () {
219  return iostats[3];
220 }
221 
222 uint64_t Process::getBytesReadStorage(void) const throw () {
223  return iostats[4];
224 }
225 
226 uint64_t Process::getBytesWriteStorage(void) const throw () {
227  return iostats[5];
228 }
229 
230 int64_t Process::getCancelledBytesWrite(void) const throw () {
231  return iostats[6];
232 }
233 
234 uint64_t Process::getBlockIOWait(void) const throw () {
235  return getBlockIOWait(getPID());
236 }
237 
238 int Process::getClockTicks(void) const throw () {
239  return jiffies;
240 }
241 
242 int Process::getProcessorCount(void) const throw () {
243 #ifdef CPU_COUNT_CXX11
244  int ncpu;
245 
246  if ((ncpu = std::thread::hardware_concurrency()) < 0) {
247  ncpu = sysconf(_SC_NPROCESSORS_ONLN);
248  }
249 
250  return ncpu;
251 #else
252  FILE* file;
253  int count = 0;
254  char line[128];
255 
256  if ((file = fopen("/proc/cpuinfo", "r")) != NULL) {
257  count = 0;
258 
259  while (fgets(line, sizeof(line), file) != NULL) {
260  if (strncmp(line, "processor", 9) == 0) {
261  count++;
262  }
263  }
264 
265  fclose(file);
266 
267  return count;
268  }
269 
270  THROW(ProcessException, "failed to open /proc/self/status");
271 #endif
272 }
273 
274 uint64_t Process::getSystemUptime(void) const throw () {
275  return info.uptime;
276 }
277 
278 uint64_t Process::getSystemLoadAverage(int min) const throw () {
279  if (min == 1) {
280  return info.loads[0];
281  } else if (min == 5) {
282  return info.loads[1];
283  } else if (min == 15) {
284  return info.loads[2];
285  }
286 
287  return 0;
288 }
289 
290 uint64_t Process::getSystemMemory(void) const throw () {
291  return info.totalram;
292 }
293 
294 uint64_t Process::getSystemFree(void) const throw () {
295  return info.freeram;
296 }
297 
298 uint64_t Process::getSystemShared(void) const throw () {
299  return info.sharedram;
300 }
301 
302 uint64_t Process::getSystemBuffer(void) const throw () {
303  return info.bufferram;
304 }
305 
306 uint64_t Process::getSystemSwap(void) const throw () {
307  return info.totalswap;
308 }
309 
310 uint64_t Process::getSystemProcesses(void) const throw () {
311  return info.procs;
312 }
313 
314 uint64_t Process::getSystemTotalHigh(void) const throw () {
315  return info.totalhigh;
316 }
317 
318 uint64_t Process::getSystemFreeHigh(void) const throw () {
319  return info.freehigh;
320 }
321 
322 uint64_t Process::getSystemMemUnitSize(void) const throw () {
323  return info.mem_unit;
324 }
325 
326 std::string Process::getBaseName(void) throw () {
327  std::string path = getPath();
328 
329  if (path.empty()) {
330  return path;
331  }
332 
333  return path.substr(path.rfind('/') + 1);
334 }
335 
336 std::string Process::getDirName(void) throw () {
337  std::string path = getPath();
338 
339  if (path.empty()) {
340  return path;
341  }
342 
343  return path.substr(0, path.rfind('/'));
344 }
345 
346 std::string Process::getPath(void) throw () {
347  std::string path;
348  char *buf = (char*) malloc(PATH_MAX);
349 
350  if (buf != NULL) {
351  memset(buf, 0, PATH_MAX);
352 
353  if (readlink("/proc/self/exe", buf, PATH_MAX - 1) != -1) {
354  path = buf;
355  }
356 
357  SAFE_FREE(buf);
358  }
359 
360  return path;
361 }
362 
363 void Process::setName(const std::string &name) throw (ProcessException) {
364 #ifdef PR_SET_NAME
365  char buf[16] = { 0 }; /* Max length of 16 */
366 
367  memset(buf, 0, sizeof(buf));
368  strncpy(buf, name.c_str(), sizeof(buf) - 1);
369 
370  if (prctl(PR_SET_NAME, (unsigned long) buf, 0, 0, 0) != 0) {
371  THROW2(ProcessException, "prctl");
372  }
373 #endif
374 }
375 
376 std::string Process::getName(void) throw (ProcessException) {
377  char buf[16] = { 0 }; /* Max length of 16 */
378 
379 #ifdef PR_GET_NAME
380  memset(buf, 0, sizeof(buf));
381 
382  if (prctl(PR_GET_NAME, (unsigned long) buf, 0, 0, 0) != 0) {
383  THROW2(ProcessException, "prctl");
384  }
385 #endif
386 
387  return std::string(buf);
388 }
389 
390 void Process::setThreadName(pthread_t tid, const std::string &name) throw (ProcessException) {
391  char buf[16] = { 0 }; /* Max length of 16 */
392 
393  memset(buf, 0, sizeof(buf));
394  strncpy(buf, name.c_str(), sizeof(buf) - 1);
395 
396  if (pthread_setname_np(tid, buf) != 0) {
397  THROW2(ProcessException, "pthread_setname_np");
398  }
399 }
400 
401 std::string Process::getThreadName(pthread_t tid) throw (ProcessException) {
402  char buf[16] = { 0 }; /* Max length of 16 */
403 
404  memset(buf, 0, sizeof(buf));
405 
406  if (pthread_getname_np(tid, buf, sizeof(buf)) != 0) {
407  THROW2(ProcessException, "pthread_getname_np");
408  }
409 
410  return std::string(buf);
411 }
412 
413 void Process::setTitle(const std::string &title) throw (ProcessException) {
414  Process::setName(title);
415 }
416 
417 pid_t Process::getPID(void) throw () {
418  return getpid();
419 }
420 
421 pid_t Process::getParentPID(void) throw () {
422  return getppid();
423 }
424 
425 bool Process::isRunning(pid_t pid) throw () {
426  return (::kill(pid, 0) == 0);
427 }
428 
429 uint64_t Process::getStartTime(pid_t pid) throw (ProcessException) {
430  int n = 0;
431 
432  char buf[8192] = { 0 };
433  int fd;
434 
435  struct timeval tv;
436 
437  struct sysinfo info;
438  ssize_t len;
439 
440  ::snprintf(buf, sizeof(buf), "/proc/%lu/stat", static_cast<uint64_t>(pid)); // Record 22 is jiffies since start.
441 
442  if ((fd = ::open(buf, O_RDONLY)) > 0) {
443  if ((len = ::read(fd, buf, sizeof(buf))) > 0) {
444  ::close(fd);
445 
446  for (char *c = buf; c < &(buf[len]); c++) {
447  if ((*c) == ' ') {
448  n += 1;
449 
450  if ((n == 21) && (::gettimeofday(&(tv), NULL) == 0) && (::sysinfo(&(info)) == 0)) {
451  return (tv.tv_sec - info.uptime + (::strtol(c + 1, NULL, 10) / sysconf(_SC_CLK_TCK)));
452  }
453  }
454  }
455  } else {
456  ::close(fd);
457 
458  THROW(ProcessException, "failed to read /proc/%lu/stat", pid);
459  }
460  }
461 
462  THROW(ProcessException, "failed to open /proc/%lu/stat", pid);
463 }
464 
465 uint64_t Process::getBlockIOWait(pid_t pid) throw (ProcessException) {
466  int n = 0;
467 
468  char buf[8192] = { 0 };
469  int fd;
470 
471  ssize_t len;
472 
473  ::snprintf(buf, sizeof(buf), "/proc/%lu/stat", static_cast<uint64_t>(pid)); // Record 42 is blkio_ticks
474 
475  if ((fd = ::open(buf, O_RDONLY)) > 0) {
476  if ((len = ::read(fd, buf, sizeof(buf))) > 0) {
477  ::close(fd);
478 
479  for (char *c = buf; c < &(buf[len]); c++) {
480  if ((*c) == ' ') {
481  n += 1;
482 
483  if (n == 41) {
484  return ((std::stoull(c + 1, NULL, 10) * 1000L) / sysconf(_SC_CLK_TCK));
485  }
486  }
487  }
488  } else {
489  ::close(fd);
490 
491  THROW(ProcessException, "failed to read /proc/%lu/stat", pid);
492  }
493  }
494 
495  THROW(ProcessException, "failed to open /proc/%lu/stat", pid);
496 }
497 
498 std::string Process::runCommand(const std::string &command) throw (ProcessException) {
499  std::string output;
500  FILE *fp;
501 
502  if ((fp = popen(command.c_str(), "r"))) {
503  char buf[4096] = { 0 };
504 
505  while (fgets(buf, sizeof(buf) - 1, fp)) {
506  output += buf;
507  }
508 
509  pclose(fp);
510  } else {
511  THROW2(ProcessException, "popen");
512  }
513 
514  return output;
515 }
516 
517 std::string Process::stackTrace(const std::string &executableFilename) throw () {
518  size_t maxFrames = 63;
519  void *frames[maxFrames + 1];
520  int size = backtrace(frames, sizeof(frames) / sizeof(frames[0]));
521  char **symbols = backtrace_symbols(frames, size);
522 
523  std::string stack;
524  size_t funcnamesize = 256;
525  char* funcname = (char*) malloc(funcnamesize);
526 
527  for (int i = 1; i < size; i++) {
528  char *begin_name = 0, *begin_offset = 0, *end_offset = 0, *begin_address = 0, *end_address = 0;
529 
530  for (char *p = symbols[i]; *p; ++p) {
531  if (*p == '(') {
532  begin_name = p;
533  } else if (*p == '+') {
534  begin_offset = p;
535  } else if (*p == ')') {
536  end_offset = p;
537  } else if (*p == '[' && end_offset) {
538  begin_address = p;
539  } else if (*p == '[') {
540  begin_address = p;
541  begin_offset = p - 3;
542  } else if (*p == ']' && begin_address) {
543  end_address = p;
544 
545  break;
546  }
547  }
548 
549  if (begin_name && begin_offset && end_offset && begin_address && end_address && begin_name < begin_offset) {
550  *begin_name++ = '\0';
551  *begin_offset++ = '\0';
552  *end_offset = '\0';
553  *begin_address++ = '\0';
554  *end_address = '\0';
555 
556  void *ptr = (void*) strtol(begin_address + 2, NULL, 16);
557  FILE *fp;
558  std::string command = String::sprintf("/usr/bin/addr2line -i -s -e %s %s 2> /dev/null", executableFilename.c_str(), begin_address);
559  std::vector<std::string> matches;
560 
561  if ((fp = popen(command.c_str(), "r"))) { /* Use adddr2line to find file and line number */
562  char buf[1024] = { 0 };
563 
564  while (fgets(buf, sizeof(buf) - 1, fp)) { } /* We only want the last line. */
565  String::regex_match(buf, "^([^:]+):([1-9][0-9]*).*?$", matches);
566 
567  pclose(fp);
568  }
569 
570  int status = -2;
571 #ifdef __GNUG__
572  char* ret = abi::__cxa_demangle(begin_name, funcname, &funcnamesize, &status);
573 #else
574  char* ret = NULL;
575 #endif
576 
577  if (status == 0) {
578  funcname = ret; /* see man - use possibly realloc'd string */
579 
580  if (matches.empty()) {
581  stack += String::sprintf("#%-2d %012p in %s from %s\n", i, ptr, funcname, symbols[i]);
582  } else {
583  stack += String::sprintf("#%-2d %012p in %s at %s:%s\n", i, ptr, funcname, matches[1].c_str(), matches[2].c_str());
584  }
585  } else { /* No demangle available */
586  if (matches.empty()) {
587  stack += String::sprintf("#%-2d %012p in %s() from %s\n", i, ptr, begin_name, symbols[i]);
588  } else {
589  stack += String::sprintf("#%-2d %012p in %s() at %s:%s\n", i, ptr, begin_name, matches[1].c_str(), matches[2].c_str());
590  }
591  }
592  } else if (begin_name && begin_address && end_address) {
593  *begin_name++ = '\0';
594  *begin_address++ = '\0';
595  *end_address = '\0';
596 
597  void *ptr = (void*) strtol(begin_address + 2, NULL, 16);
598 
599  stack += String::sprintf("#%-2d %012p in \?\?() from %s\n", i, ptr, symbols[i]);
600  } else { // couldn't parse the line? print the whole line.
601  stack += String::sprintf("#%-2d %012p in %s\n", i, 0, symbols[i]);
602  }
603  }
604 
605  stack.erase(stack.size() - 1);
606 
607  free(funcname);
608  free(symbols);
609 
610  return stack;
611 }
612 
613 void Process::setNice(int niceness, int which, int who) throw (ProcessException) {
614  if (::setpriority(which, who, niceness) != 0) {
615  THROW2(ProcessException, "setpriority");
616  }
617 }
618 
619 int Process::getNice(int which, int who) throw (ProcessException) {
620  errno = 0;
621  int r = ::getpriority(which, who);
622 
623  if (r == -1 && errno != 0) {
624  THROW2(ProcessException, "getpriority");
625  }
626 
627  return r;
628 }
629 
630 void Process::setIONice(int ioclass, int level, int which, int who) throw (ProcessException) {
631  if (::syscall(SYS_ioprio_set, which, who, IOPRIO_PRIO_VALUE(ioclass, level)) == -1) {
632  THROW2(ProcessException, "ioprio_set");
633  }
634 }
635 
636 int Process::getIONice(int &ioclass, int &level, int which, int who) throw (ProcessException) {
637  int ioprio;
638 
639  if ((ioprio = ::syscall(SYS_ioprio_get, which, who)) == -1) {
640  THROW2(ProcessException, "ioprio_get");
641  }
642 
643  ioclass = IOPRIO_PRIO_CLASS(ioprio);
644  level = IOPRIO_PRIO_DATA(ioprio);
645 
646  return ioprio;
647 }
648 
649 void Process::updateCPU(void) throw () {
650  struct tms timeSample;
651  clock_t now;
652  double percent;
653 
654  prev = last;
655  last = Time::clock_monotonic_coarse();
656  now = times(&timeSample);
657 
658  if (now <= lastCPU || timeSample.tms_stime < lastSysCPU || timeSample.tms_utime < lastUserCPU) {
659  cpu = 0; // Overflow - update called too quickly
660  userCPU = 0;
661  sysCPU = 0;
662  } else {
663  percent = (timeSample.tms_stime - lastSysCPU) + (timeSample.tms_utime - lastUserCPU);
664  percent /= (now - lastCPU);
665  percent *= jiffies;
666 
667  cpu = percent;
668 
669  percent = (timeSample.tms_utime - lastUserCPU);
670  percent /= (now - lastCPU);
671  percent *= jiffies;
672 
673  userCPU = percent;
674 
675  percent = (timeSample.tms_stime - lastSysCPU);
676  percent /= (now - lastCPU);
677  percent *= jiffies;
678 
679  sysCPU = percent;
680  }
681 
682  lastCPU = now;
683  lastSysCPU = timeSample.tms_stime;
684  lastUserCPU = timeSample.tms_utime;
685 }
686 
687 void Process::updateFDs(void) throw (ProcessException) {
688  int fds = 0;
689  struct dirent *dp;
690  DIR *dir;
691 
692  if ((dir = opendir("/proc/self/fd")) == NULL) {
693  THROW(ProcessException, "failed to open /proc/self/fd");
694  }
695 
696  while ((dp = readdir(dir)) != NULL) {
697  fds++;
698  }
699 
700  closedir(dir);
701 
702  this->fds = fds;
703 }
704 
705 void Process::updateStats(void) throw (ProcessException) {
706  int fd;
707  ssize_t len;
708  char buf[8192] = { 0 };
709 
710  if ((fd = ::open("/proc/self/status", O_RDONLY)) > 0) {
711  if ((len = ::read(fd, buf, sizeof(buf))) > 0) {
712  ::close(fd);
713 
714  std::vector<std::string> v = String::regex_split(buf, "(?::\\s+|\n)");
715 
716  for (size_t i = 0, len = v.size(); i < len; i += 2) {
717  if (v[i] == std::string("State")) {
718  stats[0] = v[i + 1].substr(0, 1)[0];
719  } else if (v[i] == std::string("FDSize")) {
720  stats[1] = std::stoull(v[i + 1]);
721  } else if (v[i] == std::string("VmPeak")) {
722  stats[2] = String::dataSize(v[i + 1]);
723  } else if (v[i] == std::string("VmSize")) {
724  stats[3] = String::dataSize(v[i + 1]);
725  } else if (v[i] == std::string("VmLck")) {
726  stats[4] = String::dataSize(v[i + 1]);
727  } else if (v[i] == std::string("VmHWM")) {
728  stats[5] = String::dataSize(v[i + 1]);
729  } else if (v[i] == std::string("VmRSS")) {
730  stats[6] = String::dataSize(v[i + 1]);
731  } else if (v[i] == std::string("VmData")) {
732  stats[7] = String::dataSize(v[i + 1]);
733  } else if (v[i] == std::string("VmStk")) {
734  stats[8] = String::dataSize(v[i + 1]);
735  } else if (v[i] == std::string("VmExe")) {
736  stats[9] = String::dataSize(v[i + 1]);
737  } else if (v[i] == std::string("VmLib")) {
738  stats[10] = String::dataSize(v[i + 1]);
739  } else if (v[i] == std::string("VmPTE")) {
740  stats[11] = String::dataSize(v[i + 1]);
741  } else if (v[i] == std::string("VmSwap")) {
742  stats[12] = String::dataSize(v[i + 1]);
743  } else if (v[i] == std::string("Threads")) {
744  stats[13] = std::stoull(v[i + 1]);
745  } else if (v[i] == std::string("voluntary_ctxt_switches")) {
746  stats[14] = std::stoull(v[i + 1]);
747  } else if (v[i] == std::string("nonvoluntary_ctxt_switches")) {
748  stats[15] = std::stoull(v[i + 1]);
749  }
750  }
751  } else {
752  ::close(fd);
753 
754  THROW(ProcessException, "failed to read /proc/self/status");
755  }
756 
757  return;
758  }
759 
760  THROW(ProcessException, "failed to open /proc/self/status");
761 }
762 
763 void Process::updateIOStats(void) throw (ProcessException) {
764  int fd;
765  ssize_t len;
766  char buf[8192] = { 0 };
767 
768  if ((fd = ::open("/proc/self/io", O_RDONLY)) > 0) {
769  if ((len = ::read(fd, buf, sizeof(buf))) > 0) {
770  ::close(fd);
771 
772  std::vector<std::string> v = String::regex_split(buf, "(?::\\s+|\n)");
773 
774  for (size_t i = 0, len = v.size(); i < len; i += 2) {
775  if (v[i] == std::string("rchar")) {
776  iostats[0] = std::stoull(v[i + 1]);
777  } else if (v[i] == std::string("wchar")) {
778  iostats[1] = std::stoull(v[i + 1]);
779  } else if (v[i] == std::string("syscr")) {
780  iostats[2] = std::stoull(v[i + 1]);
781  } else if (v[i] == std::string("syscw")) {
782  iostats[3] = std::stoull(v[i + 1]);
783  } else if (v[i] == std::string("read_bytes")) {
784  iostats[4] = std::stoull(v[i + 1]);
785  } else if (v[i] == std::string("write_bytes")) {
786  iostats[5] = std::stoull(v[i + 1]);
787  } else if (v[i] == std::string("cancelled_write_bytes")) {
788  iostats[6] = std::stoull(v[i + 1]);
789  }
790  }
791  } else {
792  ::close(fd);
793 
794  THROW(ProcessException, "failed to read /proc/self/io");
795  }
796 
797  return;
798  }
799 
800  THROW(ProcessException, "failed to open /proc/self/io");
801 }
802 
803 /*
804 ** vim: noet ts=3 sw=3
805 */