1 // Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
2 // This Source Code Form is subject to the terms of the Mozilla Public
3 // License, v. 2.0. If a copy of the MPL was not distributed with this
4 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
6 #include "cpu_load_measurer.hpp"
7
8 #include <fstream>
9 #include <string>
10 #include <iostream>
11 #include <sstream>
12 #include <vector>
13 #include <stdexcept>
14 #include <cstdio>
15
16 #include <sys/types.h>
17 #include <unistd.h>
18
~cpu_load_measurer()19 cpu_load_measurer::~cpu_load_measurer() {
20 }
21
cpu_load_measurer(std::uint32_t _pid)22 cpu_load_measurer::cpu_load_measurer(std::uint32_t _pid) :
23 pid_(_pid),
24 jiffies_complete_start_(0),
25 jiffies_idle_start_(0),
26 jiffies_complete_stop_(0),
27 jiffies_idle_stop_(0),
28 clock_ticks_(0),
29 jiffies_passed_pid_start_(0),
30 jiffies_passed_pid_stop_(0),
31 cpu_load_pid_(0.0),
32 cpu_load_overall_(0.0),
33 cpu_load_pid_wo_idle_(0.0) {
34 }
35
start()36 void cpu_load_measurer::start() {
37 // reset everything
38 jiffies_complete_start_ = 0;
39 jiffies_idle_start_ = 0;
40 jiffies_complete_stop_ = 0;
41 jiffies_idle_stop_ = 0;
42 clock_ticks_ = 0;
43 jiffies_passed_pid_start_ = 0;
44 jiffies_passed_pid_stop_ = 0;
45 cpu_load_pid_= 0.0;
46 cpu_load_overall_ = 0.0;
47 cpu_load_pid_wo_idle_ = 0.0;
48 //start
49 jiffies_complete_start_ = read_proc_stat(&jiffies_idle_start_);
50 jiffies_passed_pid_start_ = read_proc_pid_stat();
51 }
52
stop()53 void cpu_load_measurer::stop() {
54 jiffies_complete_stop_ = read_proc_stat(&jiffies_idle_stop_);
55 jiffies_passed_pid_stop_ = read_proc_pid_stat();
56 if(jiffies_complete_stop_ < jiffies_complete_start_ || jiffies_passed_pid_stop_ < jiffies_passed_pid_start_) {
57 std::cerr << "Overflow of values in procfs occured, can't calculate load" << std::endl;
58 exit(0);
59 }
60 cpu_load_pid_ = 100.0
61 * static_cast<double>(jiffies_passed_pid_stop_
62 - jiffies_passed_pid_start_)
63 / static_cast<double>(jiffies_complete_stop_
64 - jiffies_complete_start_);
65 cpu_load_overall_ = 100.0
66 * static_cast<double>((jiffies_complete_stop_ - jiffies_idle_stop_)
67 - (jiffies_complete_start_ - jiffies_idle_start_))
68 / static_cast<double>(jiffies_complete_stop_
69 - jiffies_complete_start_);
70 cpu_load_pid_wo_idle_ = 100.0
71 * static_cast<double>(jiffies_passed_pid_stop_
72 - jiffies_passed_pid_start_)
73 / static_cast<double>((jiffies_complete_stop_ - jiffies_idle_stop_)
74 - (jiffies_complete_start_ - jiffies_idle_start_));
75
76 }
77
print_cpu_load() const78 void cpu_load_measurer::print_cpu_load() const {
79 std::cout << "Used Jiffies complete: "
80 << jiffies_complete_stop_ - jiffies_complete_start_ << " (worked: "
81 << (jiffies_complete_stop_ - jiffies_idle_stop_)
82 - (jiffies_complete_start_ - jiffies_idle_start_)
83 << " idled: " << jiffies_idle_stop_ - jiffies_idle_start_
84 << ")" << std::endl;
85 std::cout << "Used Jiffies of pid " << pid_ << ": " << jiffies_passed_pid_stop_ - jiffies_passed_pid_start_ << std::endl;
86 std::cout << "Cpu load pid " << pid_ << " [%]: " << cpu_load_pid_ << std::endl;
87 std::cout << "Overall cpu load[%]: " << cpu_load_overall_ << std::endl;
88 std::cout << "Load caused by pid " << pid_ << " of overall cpu load [%]:" << cpu_load_pid_wo_idle_ << std::endl;
89 }
90
get_cpu_load() const91 double cpu_load_measurer::get_cpu_load() const {
92 return cpu_load_pid_;
93 }
94
read_proc_pid_stat()95 std::uint64_t cpu_load_measurer::read_proc_pid_stat() {
96 std::string path("/proc/" + std::to_string(pid_) + "/stat");
97 FILE* f = std::fopen(path.c_str(), "r");
98 if(!f) {
99 std::perror(std::string("Failed to open " + path).c_str());
100 exit(1);
101 }
102 // see Table 1-4 Contents of the stat files (as of 2.6.30-rc7)
103 // at https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/Documentation/filesystems/proc.txt?id=refs/tags/v3.10.98
104 // and man proc (for conversion specifier)
105 std::uint64_t utime(0);
106 std::uint64_t stime(0);
107 std::int64_t cutime(0);
108 std::int64_t cstime(0);
109 if (std::fscanf(f, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u "
110 "%lu %lu %ld %ld", // utime, stime, cutime, cstime
111 &utime, &stime, &cutime, &cstime) == EOF) {
112 std::cerr << "Failed to read " + path << std::endl;
113 exit(1);
114 }
115 std::fclose(f);
116 return utime + stime + static_cast<std::uint64_t>(cutime) +
117 static_cast<std::uint64_t>(cstime);
118 }
119
read_proc_stat(std::uint64_t * _idle)120 std::uint64_t cpu_load_measurer::read_proc_stat(std::uint64_t* _idle) {
121 FILE* f = std::fopen("/proc/stat", "r");
122 if(!f) {
123 std::perror("Failed to open /proc/stat");
124 exit(1);
125 }
126
127 // see 1.8 Miscellaneous kernel statistics in /proc/stat
128 // at https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/Documentation/filesystems/proc.txt?id=refs/tags/v3.10.98
129 std::uint64_t user(0);
130 std::uint64_t nice(0);
131 std::uint64_t system(0);
132 std::uint64_t idle(0);
133 std::uint64_t iowait(0);
134 std::uint64_t irq(0);
135 std::uint64_t softirq(0);
136 std::uint64_t steal(0);
137 std::uint64_t guest(0);
138 std::uint64_t guest_nice(0);
139 if (std::fscanf(f, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", &user,
140 &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest,
141 &guest_nice) == EOF) {
142 std::cerr << "Failed to read /proc/stat" << std::endl;
143 exit(1);
144 }
145 std::fclose(f);
146 *_idle = idle;
147 return user + nice + system + idle + iowait + irq + softirq + steal + guest
148 + guest_nice;
149 }
150
read_clock_ticks()151 bool cpu_load_measurer::read_clock_ticks() {
152 long val(::sysconf(_SC_CLK_TCK));
153 if(val < 0 && errno == EINVAL) {
154 std::perror(__func__);
155 return false;
156 }
157 clock_ticks_ = static_cast<std::uint64_t>(val);
158 return true;
159 }
160