-
Notifications
You must be signed in to change notification settings - Fork 21
/
cpumon.cpp
135 lines (112 loc) · 4.11 KB
/
cpumon.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (C) 2018-2020 CERN
// License Apache2 - see LICENCE file
#include "cpumon.h"
#include <string.h>
#include <unistd.h>
#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>
#include "utils.h"
#define MONITOR_NAME "cpumon"
// Constructor; uses RAII pattern to be valid
// after construction
cpumon::cpumon() : cpu_params{}, cpu_stats{} {
log_init(MONITOR_NAME);
#undef MONITOR_NAME
cpu_params.reserve(params.size());
for (const auto& param : params) {
cpu_params.push_back(param.get_name());
cpu_stats[param.get_name()] = 0;
}
}
void cpumon::update_stats(const std::vector<pid_t>& pids) {
for (auto stat : cpu_stats) stat.second = 0;
std::vector<std::string> stat_entries{};
stat_entries.reserve(prmon::stat_cpu_read_limit + 1);
std::string tmp_str{};
for (const auto pid : pids) {
std::stringstream stat_fname{};
stat_fname << "/proc/" << pid << "/stat" << std::ends;
std::ifstream proc_stat{stat_fname.str()};
while (proc_stat && stat_entries.size() < prmon::stat_cpu_read_limit + 1) {
proc_stat >> tmp_str;
if (proc_stat) stat_entries.push_back(tmp_str);
}
if (stat_entries.size() > prmon::stat_cpu_read_limit) {
cpu_stats["utime"] += std::stol(stat_entries[prmon::utime_pos]) +
std::stol(stat_entries[prmon::cutime_pos]);
cpu_stats["stime"] += std::stol(stat_entries[prmon::stime_pos]) +
std::stol(stat_entries[prmon::cstime_pos]);
}
stat_entries.clear();
}
for (auto& stat : cpu_stats) stat.second /= sysconf(_SC_CLK_TCK);
}
// Return the summed counters
std::map<std::string, unsigned long long> const cpumon::get_text_stats() {
return cpu_stats;
}
// Same for JSON
std::map<std::string, unsigned long long> const cpumon::get_json_total_stats() {
return cpu_stats;
}
// For CPU time there's nothing to return for an average
std::map<std::string, double> const cpumon::get_json_average_stats(
unsigned long long elapsed_clock_ticks) {
std::map<std::string, double> empty_average_stats{};
return empty_average_stats;
}
// Collect related hardware information
void const cpumon::get_hardware_info(nlohmann::json& hw_json) {
// Define the command and run it
const std::vector<std::string> cmd = {"lscpu"};
auto cmd_result = prmon::cmd_pipe_output(cmd);
// If the command failed print an error and move on
if (cmd_result.first) {
error("Failed to execute 'lscpu' to get CPU information (code " +
std::to_string(cmd_result.first) + ")");
return;
}
// Map lscpu names to the desired ones in the JSON
const std::unordered_map<std::string, std::string> metricToName{
{"Model name", "ModelName"},
{"CPU(s)", "CPUs"},
{"Socket(s)", "Sockets"},
{"Core(s) per socket", "CoresPerSocket"},
{"Thread(s) per core", "ThreadsPerCore"}};
// Useful function to determine if a string is purely a number
auto isNumber = [](const std::string& s) {
return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) {
return !std::isdigit(c);
}) == s.end();
};
// Loop over the output, parse the line, check the key and store the value if
// requested
std::string key{}, value{};
for (const auto& line : cmd_result.second) {
// Continue on empty line
if (line.empty()) continue;
// Tokenize by ":"
size_t splitIdx = line.find(":");
if (splitIdx == std::string::npos) continue;
// Read "key":"value" pairs
key = line.substr(0, splitIdx);
value = line.substr(splitIdx + 1);
if (key.empty() || value.empty()) continue;
key = std::regex_replace(key, std::regex("^\\s+|\\s+$"), "");
value = std::regex_replace(value, std::regex("^\\s+|\\s+$"), "");
// Fill the JSON with the information
if (metricToName.count(key) == 1) {
if (isNumber(value))
hw_json["HW"]["cpu"][metricToName.at(key)] = std::stoi(value);
else
hw_json["HW"]["cpu"][metricToName.at(key)] = value;
}
}
return;
}
void const cpumon::get_unit_info(nlohmann::json& unit_json) {
prmon::fill_units(unit_json, params);
return;
}