Thrill  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
linux_proc_stats.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  * thrill/common/linux_proc_stats.cpp
3  *
4  * Profiling Task which reads CPU, network, I/O loads, and more from Linux's
5  * /proc filesystem.
6  *
7  * Part of Project Thrill - http://project-thrill.org
8  *
9  * Copyright (C) 2016 Timo Bingmann <[email protected]>
10  *
11  * All rights reserved. Published under the BSD-2 license in the LICENSE file.
12  ******************************************************************************/
13 
15 
17 #include <thrill/common/logger.hpp>
20 #include <thrill/common/string.hpp>
22 
23 #include <tlx/die.hpp>
25 #include <tlx/string/trim.hpp>
26 
27 #include <cstring>
28 #include <fstream>
29 #include <limits>
30 #include <string>
31 #include <vector>
32 
33 #if __linux__
34 
35 #include <dirent.h>
36 #include <unistd.h>
37 
38 #endif
39 
40 namespace thrill {
41 namespace common {
42 
43 #if __linux__
44 
45 using steady_clock = std::chrono::steady_clock;
46 
47 class LinuxProcStats final : public ProfileTask
48 {
49  static constexpr bool debug = false;
50 
51 public:
52  explicit LinuxProcStats(JsonLogger& logger) : logger_(logger) {
53 
54  sc_pagesize_ = sysconf(_SC_PAGESIZE);
55 
56  file_stat_.open("/proc/stat");
57  file_net_dev_.open("/proc/net/dev");
58  file_diskstats_.open("/proc/diskstats");
59  file_meminfo_.open("/proc/meminfo");
60 
61  pid_t mypid = getpid();
62  file_pid_stat_.open("/proc/" + std::to_string(mypid) + "/stat");
63  file_pid_io_.open("/proc/" + std::to_string(mypid) + "/io");
64 
65  read_sys_block_devices();
66  }
67 
68  //! read /sys/block to find block devices
69  void read_sys_block_devices();
70 
71  //! calculate percentage of change relative to base.
72  static double perc(unsigned long long prev, unsigned long long curr,
73  unsigned long long base) {
74  if (curr < prev)
75  return 0.0;
76  else
77  return static_cast<double>(curr - prev)
78  / static_cast<double>(base) * 100.0;
79  }
80 
81  //! method to prepare JsonLine
82  JsonLine& prepare_out(JsonLine& out);
83 
84  //! read /proc/stat
85  void read_stat(JsonLine& out);
86 
87  //! read /proc/<pid>/stat
88  void read_pid_stat(JsonLine& out);
89 
90  //! read /proc/net/dev
91  void read_net_dev(const steady_clock::time_point& tp, JsonLine& out);
92 
93  //! read /proc/<pid>/io
94  void read_pid_io(const steady_clock::time_point& tp, JsonLine& out);
95 
96  //! read /proc/diskstats
97  void read_diskstats(JsonLine& out);
98 
99  //! read /proc/meminfo
100  void read_meminfo(JsonLine& out);
101 
102  void RunTask(const steady_clock::time_point& tp) final {
103 
104  // JsonLine to construct
105  JsonLine out = logger_.line();
106 
107  read_stat(out);
108  read_pid_stat(out);
109  read_net_dev(tp, out);
110  read_pid_io(tp, out);
111  read_diskstats(out);
112  read_meminfo(out);
113 
114  tp_last_ = tp;
115  }
116 
117 private:
118  //! reference to JsonLogger for output
119  JsonLogger& logger_;
120 
121  //! open file handle to /proc/stat
122  std::ifstream file_stat_;
123  //! open file handle to /proc/net/dev
124  std::ifstream file_net_dev_;
125  //! open file handle to /proc/<our-pid>/stat
126  std::ifstream file_pid_stat_;
127  //! open file handle to /proc/<our-pid>/io
128  std::ifstream file_pid_io_;
129  //! open file handle to /proc/diskstats
130  std::ifstream file_diskstats_;
131  //! open file handle to /proc/meminfo
132  std::ifstream file_meminfo_;
133 
134  //! last time point called
135  steady_clock::time_point tp_last_;
136 
137  //! sysconf(_SC_PAGESIZE)
138  size_t sc_pagesize_;
139 
140  struct CpuStat {
141  unsigned long long user = 0;
142  unsigned long long nice = 0;
143  unsigned long long sys = 0;
144  unsigned long long idle = 0;
145  unsigned long long iowait = 0;
146  unsigned long long steal = 0;
147  unsigned long long hardirq = 0;
148  unsigned long long softirq = 0;
149  unsigned long long guest = 0;
150  unsigned long long guest_nice = 0;
151 
152  //! total uptime across all modes
153  unsigned long long uptime() const {
154  return user + nice + sys + idle
155  + iowait + hardirq + steal + softirq;
156  }
157  //! return pure user mode time excluding virtual guests
158  unsigned long long user_plain() const { return user - guest; }
159  //! return pure nice mode time excluding virtual guests
160  unsigned long long nice_plain() const { return nice - guest_nice; }
161  };
162 
163  struct PidStat {
164  unsigned long long check_pid = 0;
165  unsigned long long utime = 0;
166  unsigned long long stime = 0;
167  unsigned long long cutime = 0;
168  unsigned long long cstime = 0;
169  unsigned long long num_threads = 0;
170  unsigned long long vsize = 0;
171  unsigned long long rss = 0;
172  };
173 
174  struct NetDevStat {
175  std::string if_name;
176  unsigned long long rx_pkts = 0;
177  unsigned long long tx_pkts = 0;
178  unsigned long long rx_bytes = 0;
179  unsigned long long tx_bytes = 0;
180  };
181 
182  struct PidIoStat {
183  unsigned long long read_bytes = 0;
184  unsigned long long write_bytes = 0;
185  };
186 
187  struct DiskStats {
188  std::string dev_name;
189  //! number of read operations issued to the device
190  unsigned long long rd_ios = 0;
191  //! number of read requests merged
192  unsigned long long rd_merged = 0;
193  //! number of sectors read (512b sectors)
194  unsigned long long rd_sectors = 0;
195  //! time of read requests in queue (ms)
196  unsigned long long rd_time = 0;
197 
198  //! number of write operations issued to the device
199  unsigned long long wr_ios = 0;
200  //! number of write requests merged
201  unsigned long long wr_merged = 0;
202  //! number of sectors written (512b sectors)
203  unsigned long long wr_sectors = 0;
204  //! Time of write requests in queue (ms)
205  unsigned long long wr_time = 0;
206 
207  //! number of I/Os in progress
208  unsigned long long ios_progr = 0;
209  //! number of time total (for this device) for I/O (ms)
210  unsigned long long total_time = 0;
211  //! number of time requests spent in queue (ms)
212  unsigned long long rq_time = 0;
213  };
214 
215  //! delta jiffies since the last iteration (read from uptime() of the cpu
216  //! summary)
217  unsigned long long jiffies_delta_ = 0;
218 
219  //! previous summary cpu reading
220  CpuStat cpu_prev_;
221 
222  //! previous cpu core reading
223  std::vector<CpuStat> cpu_core_prev_;
224 
225  //! previous reading from pid's stat file
226  PidStat pid_stat_prev_;
227 
228  //! previous reading from network stats
229  std::vector<NetDevStat> net_dev_prev_;
230 
231  //! find or create entry for net_dev
232  NetDevStat& find_net_dev(const std::string& if_name);
233 
234  //! previous reading of pid's io file
235  PidIoStat pid_io_prev_;
236 
237  //! find or create entry for net_dev
238  DiskStats * find_diskstats(const char* dev_name);
239 
240  //! previous reading from diskstats
241  std::vector<DiskStats> diskstats_prev_;
242 
243  //! helper method to parse size lines from /proc/meminfo
244  static bool parse_meminfo(const char* str, size_t& size);
245 };
246 
247 JsonLine& LinuxProcStats::prepare_out(JsonLine& out) {
248  if (out.items() == 2) {
249  out << "class" << "LinuxProcStats"
250  << "event" << "profile";
251  }
252  return out;
253 }
254 
255 void LinuxProcStats::read_stat(JsonLine& out) {
256  if (!file_stat_.is_open()) return;
257 
258  file_stat_.clear();
259  file_stat_.seekg(0);
260  if (!file_stat_.good()) return;
261 
262  // read the number of jiffies spent in the various modes since the
263  // last tick.
264 
265  const double kNaN = 0; // we use zero since NaN is not compatible with JSON
266 
267  std::vector<double> cores_user, cores_nice, cores_sys, cores_idle,
268  cores_iowait, cores_hardirq, cores_softirq,
269  cores_steal, cores_guest, cores_guest_nice;
270 
271  std::string line;
272  while (std::getline(file_stat_, line)) {
273  if (tlx::starts_with(line, "cpu ")) {
274 
275  CpuStat curr;
276  int ret = sscanf(
277  line.data() + 5,
278  "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
279  &curr.user,
280  &curr.nice,
281  &curr.sys,
282  &curr.idle,
283  &curr.iowait,
284  &curr.hardirq,
285  &curr.softirq,
286  &curr.steal,
287  &curr.guest,
288  &curr.guest_nice);
289 
290  if (ret < 4) die("/proc/stat returned too few values");
291 
292  CpuStat& prev = cpu_prev_;
293 
294  if (!prev.user) {
295  // just store the first reading
296  prev = curr;
297  continue;
298  }
299 
300  jiffies_delta_ = curr.uptime() - prev.uptime();
301  unsigned long long base = jiffies_delta_;
302 
303  sLOG << "cpu"
304  << "delta" << jiffies_delta_
305  << "user" << perc(prev.user, curr.user, base)
306  << "nice" << perc(prev.nice, curr.nice, base)
307  << "sys" << perc(prev.sys, curr.sys, base)
308  << "iowait" << (ret >= 5 ? perc(prev.iowait, curr.iowait, base) : kNaN)
309  << "hardirq" << (ret >= 6 ? perc(prev.hardirq, curr.hardirq, base) : kNaN)
310  << "softirq" << (ret >= 7 ? perc(prev.softirq, curr.softirq, base) : kNaN)
311  << "steal" << (ret >= 8 ? perc(prev.steal, curr.steal, base) : kNaN)
312  << "guest" << (ret >= 9 ? perc(prev.guest, curr.guest, base) : kNaN)
313  << "guest_nice" << (ret >= 10 ? perc(prev.guest_nice, curr.guest_nice, base) : kNaN)
314  << "idle" << perc(prev.idle, curr.idle, base);
315 
316  prepare_out(out)
317  << "cpu_user" << perc(prev.user, curr.user, base)
318  << "cpu_nice" << perc(prev.nice, curr.nice, base)
319  << "cpu_sys" << perc(prev.sys, curr.sys, base)
320  << "cpu_idle" << perc(prev.idle, curr.idle, base)
321  << "cpu_iowait" << (ret >= 5 ? perc(prev.iowait, curr.iowait, base) : kNaN)
322  << "cpu_hardirq" << (ret >= 6 ? perc(prev.hardirq, curr.hardirq, base) : kNaN)
323  << "cpu_softirq" << (ret >= 7 ? perc(prev.softirq, curr.softirq, base) : kNaN)
324  << "cpu_steal" << (ret >= 8 ? perc(prev.steal, curr.steal, base) : kNaN)
325  << "cpu_guest" << (ret >= 9 ? perc(prev.guest, curr.guest, base) : kNaN)
326  << "cpu_guest_nice" << (ret >= 10 ? perc(prev.guest_nice, curr.guest_nice, base) : kNaN);
327 
328  prev = curr;
329  }
330  else if (tlx::starts_with(line, "cpu")) {
331 
332  unsigned core_id;
333  CpuStat curr;
334  int ret = sscanf(
335  line.data() + 3,
336  "%u %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
337  &core_id,
338  &curr.user,
339  &curr.nice,
340  &curr.sys,
341  &curr.idle,
342  &curr.iowait,
343  &curr.hardirq,
344  &curr.softirq,
345  &curr.steal,
346  &curr.guest,
347  &curr.guest_nice);
348 
349  if (ret < 5) die("/proc/stat returned too few values");
350 
351  if (cpu_core_prev_.size() < core_id + 1)
352  cpu_core_prev_.resize(core_id + 1);
353 
354  CpuStat& prev = cpu_core_prev_[core_id];
355 
356  if (!prev.user) {
357  // just store the first reading
358  prev = curr;
359  continue;
360  }
361 
362  jiffies_delta_ = curr.uptime() - prev.uptime();
363  unsigned long long base = jiffies_delta_;
364 
365  sLOG << "core" << core_id
366  << "delta" << jiffies_delta_
367  << "user" << perc(prev.user, curr.user, base)
368  << "nice" << perc(prev.nice, curr.nice, base)
369  << "sys" << perc(prev.sys, curr.sys, base)
370  << "iowait" << (ret >= 6 ? perc(prev.iowait, curr.iowait, base) : kNaN)
371  << "hardirq" << (ret >= 7 ? perc(prev.hardirq, curr.hardirq, base) : kNaN)
372  << "softirq" << (ret >= 8 ? perc(prev.softirq, curr.softirq, base) : kNaN)
373  << "steal" << (ret >= 9 ? perc(prev.steal, curr.steal, base) : kNaN)
374  << "guest" << (ret >= 10 ? perc(prev.guest, curr.guest, base) : kNaN)
375  << "guest_nice" << (ret >= 11 ? perc(prev.guest_nice, curr.guest_nice, base) : kNaN)
376  << "idle" << perc(prev.idle, curr.idle, base);
377 
378  cores_user.emplace_back(perc(prev.user, curr.user, base));
379  cores_nice.emplace_back(perc(prev.nice, curr.nice, base));
380  cores_sys.emplace_back(perc(prev.sys, curr.sys, base));
381  cores_idle.emplace_back(perc(prev.idle, curr.idle, base));
382  cores_iowait.push_back(ret >= 6 ? perc(prev.iowait, curr.iowait, base) : kNaN);
383  cores_hardirq.push_back(ret >= 7 ? perc(prev.hardirq, curr.hardirq, base) : kNaN);
384  cores_softirq.push_back(ret >= 8 ? perc(prev.softirq, curr.softirq, base) : kNaN);
385  cores_steal.push_back(ret >= 9 ? perc(prev.steal, curr.steal, base) : kNaN);
386  cores_guest.push_back(ret >= 10 ? perc(prev.guest, curr.guest, base) : kNaN);
387  cores_guest_nice.push_back(ret >= 11 ? perc(prev.guest_nice, curr.guest_nice, base) : kNaN);
388 
389  prev = curr;
390  }
391  }
392 
393  if (!cores_user.empty()) {
394  prepare_out(out)
395  << "cores_user" << cores_user
396  << "cores_nice" << cores_nice
397  << "cores_sys" << cores_sys
398  << "cores_idle" << cores_idle
399  << "cores_iowait" << cores_iowait
400  << "cores_hardirq" << cores_hardirq
401  << "cores_softirq" << cores_softirq
402  << "cores_steal" << cores_steal
403  << "cores_guest" << cores_guest
404  << "cores_guest_nice" << cores_guest_nice;
405  }
406 }
407 
408 void LinuxProcStats::read_pid_stat(JsonLine& out) {
409  if (!file_pid_stat_.is_open()) return;
410 
411  file_pid_stat_.clear();
412  file_pid_stat_.seekg(0);
413  if (!file_pid_stat_.good()) return;
414 
415  std::string line;
416  std::getline(file_pid_stat_, line);
417 
418  PidStat curr;
419 
420  /* Field Content */
421  /* pid process id */
422  /* tcomm filename of the executable */
423  /* state state (R is running, S is sleeping, D is sleeping in an */
424  /* uninterruptible wait, Z is zombie, T is traced or stopped) */
425  /* ppid process id of the parent process */
426  /* pgrp pgrp of the process */
427  /* sid session id */
428  /* tty_nr tty the process uses */
429  /* tty_pgrp pgrp of the tty */
430  /* flags task flags */
431  /* min_flt number of minor faults */
432  /* cmin_flt number of minor faults with child's */
433  /* maj_flt number of major faults */
434  /* cmaj_flt number of major faults with child's */
435  /* utime user mode jiffies */
436  /* stime kernel mode jiffies */
437  /* cutime user mode jiffies with child's */
438  /* cstime kernel mode jiffies with child's */
439  /* priority priority level */
440  /* nice nice level */
441  /* num_threads number of threads */
442  /* it_real_value (obsolete, always 0) */
443  /* start_time time the process started after system boot */
444  /* vsize virtual memory size */
445  /* rss resident set memory size in SC_PAGESIZE units */
446  /* rsslim current limit in bytes on the rss */
447  int ret = sscanf(
448  line.data(),
449  /* pid tcomm state ppid pgrp sid tty_nr tty_pgrp flags */
450  /* 19162 (firefox) R 1 19162 19162 0 -1 4218880 */
451  "%llu %*s %*s %*u %*u %*u %*u %*u %*u "
452  /* min_flt cmin_flt maj_flt cmaj_flt utime stime cutime cstime priority nice */
453  /* 340405 6560 3 0 7855 526 3 2 20 0 */
454  "%*u %*u %*u %*u %llu %llu %llu %llu %*u %*u "
455  /* num_threads it_real_value start_time vsize rss rsslim */
456  /* 44 0 130881921 1347448832 99481 18446744073709551615 */
457  "%llu %*u %*u %llu %llu",
458  /* (firefox) more: 4194304 4515388 140732862948048 140732862941536 246430093205 0 0 4096 33572015 18446744073709551615 0 0 17 0 0 0 0 0 0 8721489 8726954 14176256 140732862948868 140732862948876 140732862948876 140732862951399 0 */
459  &curr.check_pid,
460  &curr.utime, &curr.stime, &curr.cutime, &curr.cstime,
461  &curr.num_threads, &curr.vsize, &curr.rss);
462 
463  die_unequal(8, ret);
464 
465  if (!pid_stat_prev_.check_pid) {
466  pid_stat_prev_ = curr;
467  return;
468  }
469  unsigned long long base = jiffies_delta_;
470 
471  sLOG << "pid_stat"
472  << "utime" << perc(pid_stat_prev_.utime, curr.utime, base)
473  << "stime" << perc(pid_stat_prev_.stime, curr.stime, base)
474  << "cutime" << perc(pid_stat_prev_.cutime, curr.cutime, base)
475  << "cstime" << perc(pid_stat_prev_.cstime, curr.cstime, base)
476  << "num_threads" << curr.num_threads
477  << "vsize" << curr.vsize
478  << "rss" << curr.rss * sc_pagesize_;
479 
480  prepare_out(out)
481  << "pr_user" << perc(pid_stat_prev_.utime, curr.utime, base)
482  << "pr_sys" << perc(pid_stat_prev_.stime, curr.stime, base)
483  << "pr_nthreads" << curr.num_threads
484  << "pr_vsize" << curr.vsize
485  << "pr_rss" << curr.rss * sc_pagesize_;
486 
487  pid_stat_prev_ = curr;
488 }
489 
490 LinuxProcStats::NetDevStat&
491 LinuxProcStats::find_net_dev(const std::string& if_name) {
492  for (NetDevStat& i : net_dev_prev_) {
493  if (i.if_name == if_name) return i;
494  }
495  net_dev_prev_.emplace_back();
496  net_dev_prev_.back().if_name = if_name;
497  return net_dev_prev_.back();
498 }
499 
500 void LinuxProcStats::read_net_dev(
501  const steady_clock::time_point& tp, JsonLine& out) {
502  if (!file_net_dev_.is_open()) return;
503 
504  file_net_dev_.clear();
505  file_net_dev_.seekg(0);
506  if (!file_net_dev_.good()) return;
507 
508  double elapsed = static_cast<double>(
509  std::chrono::duration_cast<std::chrono::microseconds>(
510  tp - tp_last_).count()) / 1e6;
511 
512  NetDevStat sum;
513  bool sum_output = false;
514 
515  std::string line;
516  while (std::getline(file_net_dev_, line)) {
517  std::string::size_type colonpos = line.find(':');
518  if (colonpos == std::string::npos) continue;
519 
520  std::string if_name = line.substr(0, colonpos);
521  tlx::trim(&if_name);
522 
523  NetDevStat curr;
524  int ret = sscanf(line.data() + colonpos + 1,
525  "%llu %llu %*u %*u %*u %*u %*u %*u %llu %llu",
526  &curr.rx_bytes, &curr.rx_pkts,
527  &curr.tx_bytes, &curr.tx_pkts);
528  die_unequal(4, ret);
529 
530  curr.if_name = if_name;
531  NetDevStat& prev = find_net_dev(if_name);
532 
533  if (prev.rx_bytes == 0) {
534  // just store the first reading
535  prev = curr;
536  continue;
537  }
538 
539  sLOG << "net" << if_name
540  << "rx_bytes" << curr.rx_bytes - prev.rx_bytes
541  << "tx_bytes" << curr.tx_bytes - prev.tx_bytes
542  << "rx_pkts" << curr.rx_pkts - prev.rx_pkts
543  << "tx_pkts" << curr.tx_pkts - prev.tx_pkts
544  << "rx_speed"
545  << static_cast<double>(curr.rx_bytes - prev.rx_bytes) / elapsed
546  << "tx_speed"
547  << static_cast<double>(curr.tx_bytes - prev.tx_bytes) / elapsed;
548 
549  sum.rx_bytes += curr.rx_bytes - prev.rx_bytes;
550  sum.tx_bytes += curr.tx_bytes - prev.tx_bytes;
551  sum.rx_pkts += curr.rx_pkts - prev.rx_pkts;
552  sum.tx_pkts += curr.tx_pkts - prev.tx_pkts;
553  sum_output = true;
554 
555  prev = curr;
556  }
557 
558  // summarizes interfaces
559  if (sum_output)
560  {
561  sLOG << "net" << "(all)"
562  << "rx_bytes" << sum.rx_bytes
563  << "tx_bytes" << sum.tx_bytes
564  << "rx_pkts" << sum.rx_pkts
565  << "tx_pkts" << sum.tx_pkts
566  << "rx_speed" << static_cast<double>(sum.rx_bytes) / elapsed
567  << "tx_speed" << static_cast<double>(sum.tx_bytes) / elapsed;
568 
569  prepare_out(out)
570  << "net_rx_bytes" << sum.rx_bytes
571  << "net_tx_bytes" << sum.tx_bytes
572  << "net_rx_pkts" << sum.rx_pkts
573  << "net_tx_pkts" << sum.tx_pkts
574  << "net_rx_speed" << static_cast<double>(sum.rx_bytes) / elapsed
575  << "net_tx_speed" << static_cast<double>(sum.tx_bytes) / elapsed;
576  }
577 }
578 
579 void LinuxProcStats::read_pid_io(const steady_clock::time_point& tp, JsonLine& out) {
580  if (!file_pid_io_.is_open()) return;
581 
582  file_pid_io_.clear();
583  file_pid_io_.seekg(0);
584  if (!file_pid_io_.good()) return;
585 
586  PidIoStat curr;
587 
588  std::string line;
589  while (std::getline(file_stat_, line)) {
590  if (tlx::starts_with(line, "read_bytes: ")) {
591  int ret = sscanf(line.data() + 12, "%llu", &curr.read_bytes);
592  die_unequal(1, ret);
593  }
594  else if (tlx::starts_with(line, "write_bytes: ")) {
595  int ret = sscanf(line.data() + 13, "%llu", &curr.write_bytes);
596  die_unequal(1, ret);
597  }
598  }
599 
600  if (!pid_io_prev_.read_bytes) {
601  // just store the first reading
602  pid_io_prev_ = curr;
603  return;
604  }
605 
606  double elapsed = static_cast<double>(
607  std::chrono::duration_cast<std::chrono::microseconds>(
608  tp - tp_last_).count()) / 1e6;
609 
610  PidIoStat& prev = pid_io_prev_;
611 
612  sLOG << "pid_io"
613  << "read_bytes" << curr.read_bytes - prev.read_bytes
614  << "write_bytes" << curr.write_bytes - prev.write_bytes
615  << "read_speed"
616  << static_cast<double>(curr.read_bytes - prev.read_bytes) / elapsed
617  << "write_speed"
618  << static_cast<double>(curr.write_bytes - prev.write_bytes) / elapsed;
619 
620  prepare_out(out)
621  << "pr_io_read_bytes" << curr.read_bytes - prev.read_bytes
622  << "pr_io_write_bytes" << curr.read_bytes - prev.read_bytes
623  << "pr_io_read_speed"
624  << static_cast<double>(curr.read_bytes - prev.read_bytes) / elapsed
625  << "pr_io_write_speed"
626  << static_cast<double>(curr.write_bytes - prev.write_bytes) / elapsed;
627 
628  prev = curr;
629 }
630 
631 LinuxProcStats::DiskStats*
632 LinuxProcStats::find_diskstats(const char* dev_name) {
633  for (DiskStats& i : diskstats_prev_) {
634  if (strcmp(i.dev_name.c_str(), dev_name) == 0) return &i;
635  }
636  return nullptr;
637 }
638 
639 void LinuxProcStats::read_sys_block_devices() {
640  DIR* dirp = opendir("/sys/block");
641  if (!dirp) return;
642 
643  struct dirent de_prev, * de;
644  while (readdir_r(dirp, &de_prev, &de) == 0 && de != nullptr) {
645  if (de->d_name[0] == '.') continue;
646  // push into diskstats vector
647  diskstats_prev_.emplace_back();
648  diskstats_prev_.back().dev_name = de->d_name;
649  }
650  closedir(dirp);
651 }
652 
653 void LinuxProcStats::read_diskstats(JsonLine& out) {
654  if (!file_diskstats_.is_open()) return;
655 
656  file_diskstats_.clear();
657  file_diskstats_.seekg(0);
658  if (!file_diskstats_.good()) return;
659 
660  DiskStats sum;
661  bool sum_valid = false;
662  JsonLine disks = prepare_out(out).sub("disks");
663 
664  std::string line;
665  while (std::getline(file_diskstats_, line)) {
666 
667  char dev_name[32];
668  DiskStats curr;
669  int ret = sscanf(
670  line.data(),
671  "%*u %*u %31s %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
672  dev_name,
673  &curr.rd_ios, &curr.rd_merged, &curr.rd_sectors, &curr.rd_time,
674  &curr.wr_ios, &curr.wr_merged, &curr.wr_sectors, &curr.wr_time,
675  &curr.ios_progr, &curr.total_time, &curr.rq_time);
676  die_unequal(12, ret);
677 
678  DiskStats* ptr_prev = find_diskstats(dev_name);
679  if (!ptr_prev) continue;
680 
681  DiskStats& prev = *ptr_prev;
682  curr.dev_name = dev_name;
683 
684  if (!prev.rd_ios && !prev.wr_ios && !prev.ios_progr) {
685  // just store the first reading, also: skipped entries that remain
686  // zero.
687  prev = curr;
688  continue;
689  }
690 
691  sLOG << "diskstats"
692  << "dev" << dev_name
693  << "rd_ios" << curr.rd_ios - prev.rd_ios
694  << "rd_merged" << curr.rd_merged - prev.rd_merged
695  << "rd_bytes" << (curr.rd_sectors - prev.rd_sectors) * 512
696  << "rd_time" << double(curr.rd_time - prev.rd_time) / 1e3
697  << "wr_ios" << curr.wr_ios - prev.wr_ios
698  << "wr_merged" << curr.wr_merged - prev.wr_merged
699  << "wr_bytes" << (curr.wr_sectors - prev.wr_sectors) * 512
700  << "wr_time" << double(curr.wr_time - prev.wr_time) / 1e3
701  << "ios_progr" << curr.ios_progr
702  << "total_time" << double(curr.total_time - prev.total_time) / 1e3
703  << "rq_time" << double(curr.rq_time - prev.rq_time) / 1e3;
704 
705  disks.sub(dev_name)
706  << "rd_ios" << curr.rd_ios - prev.rd_ios
707  << "rd_merged" << curr.rd_merged - prev.rd_merged
708  << "rd_bytes" << (curr.rd_sectors - prev.rd_sectors) * 512
709  << "rd_time" << double(curr.rd_time - prev.rd_time) / 1e3
710  << "wr_ios" << curr.wr_ios - prev.wr_ios
711  << "wr_merged" << curr.wr_merged - prev.wr_merged
712  << "wr_bytes" << (curr.wr_sectors - prev.wr_sectors) * 512
713  << "wr_time" << double(curr.wr_time - prev.wr_time) / 1e3
714  << "ios_progr" << curr.ios_progr
715  << "total_time" << double(curr.total_time - prev.total_time) / 1e3
716  << "rq_time" << double(curr.rq_time - prev.rq_time) / 1e3;
717 
718  sum.rd_ios += curr.rd_ios - prev.rd_ios;
719  sum.rd_merged += curr.rd_merged - prev.rd_merged;
720  sum.rd_sectors += curr.rd_sectors - prev.rd_sectors;
721  sum.rd_time += curr.rd_time - prev.rd_time;
722  sum.wr_ios += curr.wr_ios - prev.wr_ios;
723  sum.wr_merged += curr.wr_merged - prev.wr_merged;
724  sum.wr_sectors += curr.wr_sectors - prev.wr_sectors;
725  sum.wr_time += curr.wr_time - prev.wr_time;
726  sum.ios_progr += curr.ios_progr;
727  sum.total_time += curr.total_time - prev.total_time;
728  sum.rq_time += curr.rq_time - prev.rq_time;
729  sum_valid = true;
730 
731  prev = curr;
732  }
733 
734  disks.Close();
735 
736  if (sum_valid) {
737  prepare_out(out).sub("diskstats")
738  << "rd_ios" << sum.rd_ios
739  << "rd_merged" << sum.rd_merged
740  << "rd_bytes" << sum.rd_sectors * 512
741  << "rd_time" << double(sum.rd_time) / 1e3
742  << "wr_ios" << sum.wr_ios
743  << "wr_merged" << sum.wr_merged
744  << "wr_bytes" << sum.wr_sectors * 512
745  << "wr_time" << double(sum.wr_time) / 1e3
746  << "ios_progr" << sum.ios_progr
747  << "total_time" << double(sum.total_time) / 1e3
748  << "rq_time" << double(sum.rq_time) / 1e3;
749  }
750 }
751 
752 //! helper method to parse size lines from /proc/meminfo
753 bool LinuxProcStats::parse_meminfo(const char* str, size_t& size) {
754  char* endptr;
755  size = strtoul(str, &endptr, 10);
756  // parse failed, no number
757  if (!endptr) return false;
758 
759  // skip over spaces
760  while (*endptr == ' ') ++endptr;
761 
762  // multiply with 2^power
763  if (*endptr == 'k' || *endptr == 'K')
764  size *= 1024, ++endptr;
765  else if (*endptr == 'm' || *endptr == 'M')
766  size *= 1024 * 1024, ++endptr;
767  else if (*endptr == 'g' || *endptr == 'G')
768  size *= 1024 * 1024 * 1024llu, ++endptr;
769 
770  // byte indicator
771  if (*endptr == 'b' || *endptr == 'B') {
772  ++endptr;
773  }
774 
775  // skip over spaces
776  while (*endptr == ' ') ++endptr;
777 
778  return (*endptr == 0);
779 }
780 
781 void LinuxProcStats::read_meminfo(JsonLine& out) {
782  if (!file_meminfo_.is_open()) return;
783 
784  file_meminfo_.clear();
785  file_meminfo_.seekg(0);
786  if (!file_meminfo_.good()) return;
787 
788  JsonLine mem = prepare_out(out).sub("meminfo");
789 
790  size_t swap_total = 0, swap_free = 0;
791 
792  std::string line;
793  while (std::getline(file_meminfo_, line)) {
794  std::string::size_type colonpos = line.find(':');
795  if (colonpos == std::string::npos) continue;
796 
797  common::StringView key(line.begin(), line.begin() + colonpos);
798 
799  size_t size;
800 
801  if (key == "MemTotal") {
802  if (parse_meminfo(line.data() + colonpos + 1, size))
803  mem << "total" << size;
804  }
805  else if (key == "MemFree") {
806  if (parse_meminfo(line.data() + colonpos + 1, size))
807  mem << "free" << size;
808  }
809  else if (key == "MemAvailable") {
810  if (parse_meminfo(line.data() + colonpos + 1, size))
811  mem << "available" << size;
812  }
813  else if (key == "Buffers") {
814  if (parse_meminfo(line.data() + colonpos + 1, size))
815  mem << "buffers" << size;
816  }
817  else if (key == "Cached") {
818  if (parse_meminfo(line.data() + colonpos + 1, size))
819  mem << "cached" << size;
820  }
821  else if (key == "Mapped") {
822  if (parse_meminfo(line.data() + colonpos + 1, size))
823  mem << "mapped" << size;
824  }
825  else if (key == "Shmem") {
826  if (parse_meminfo(line.data() + colonpos + 1, size))
827  mem << "shmem" << size;
828  }
829  else if (key == "SwapTotal") {
830  if (parse_meminfo(line.data() + colonpos + 1, size)) {
831  mem << "swap_total" << size;
832  swap_total = size;
833  if (swap_total && swap_free) {
834  mem << "swap_used" << swap_total - swap_free;
835  swap_total = swap_free = 0;
836  }
837  }
838  }
839  else if (key == "SwapFree") {
840  if (parse_meminfo(line.data() + colonpos + 1, size)) {
841  mem << "swap_free" << size;
842  swap_free = size;
843  if (swap_total && swap_free) {
844  mem << "swap_used" << swap_total - swap_free;
845  swap_total = swap_free = 0;
846  }
847  }
848  }
849  }
850 }
851 
852 void StartLinuxProcStatsProfiler(ProfileThread& sched, JsonLogger& logger) {
853  sched.Add(std::chrono::seconds(1),
854  new LinuxProcStats(logger), /* own_task */ true);
855 }
856 
857 #else
858 
860 { }
861 
862 #endif // __linux__
863 
864 } // namespace common
865 } // namespace thrill
866 
867 /******************************************************************************/
#define sLOG
Default logging method: output if the local debug variable is true.
Definition: logger.hpp:34
std::string & trim(std::string *str)
Trims the given string in-place on the left and right.
Definition: trim.cpp:20
#define die(msg)
Instead of std::terminate(), throw the output the message via an exception.
Definition: die.hpp:22
static by_string to_string(int val)
convert to string
#define die_unequal(X, Y)
Definition: die.hpp:40
void StartLinuxProcStatsProfiler(ProfileThread &, JsonLogger &)
launch profiler task
std::basic_string< char, std::char_traits< char >, Allocator< char > > string
string with Manager tracking
Definition: allocator.hpp:220
static constexpr bool debug
JsonLogger is a receiver of JSON output objects for logging.
Definition: json_logger.hpp:69
bool starts_with(const std::string &str, const std::string &match)
Checks if the given match string is located at the start of this string.
Definition: starts_with.cpp:19