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