Skip to content

Commit 43cf7c1

Browse files
authored
Merge pull request #6 from mcmindcoder/master
Add process struct
2 parents c40096c + 9793282 commit 43cf7c1

File tree

4 files changed

+231
-5
lines changed

4 files changed

+231
-5
lines changed

include/psutil-cpp/cpu.hpp

+42
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,55 @@
22
#include <vector>
33
#include <ostream>
44
#include <optional>
5+
#include <chrono>
56

67
#include "psutil-cpp/utils.hpp"
78

89
#ifdef linux
910
#include <unistd.h>
1011
#endif
1112

13+
using PID = uint32_t;
14+
using PTime = std::chrono::system_clock::time_point;
15+
16+
/**
17+
* Represents single system process and CPU related data for this process
18+
*/
19+
struct process {
20+
process() = default;
21+
22+
explicit process(PID pid);
23+
24+
PID pid{0}; // field[0]
25+
std::string name; // field[1]
26+
std::string path;
27+
std::string cmdline_cmd;
28+
bool exists{false};
29+
PTime time;
30+
char status{0}; // field[2]
31+
PID ppid{0}; // field[3]
32+
uint32_t ttynr{0}; // field[6]
33+
float utime{0}; // field[13]
34+
float stime{0}; // field[14]
35+
float children_utime{0}; // field[15]
36+
float children_stime{0}; // field[16]
37+
float create_time{0}; // field[21]
38+
uint8_t cpu_num{0}; // field[38]
39+
uint32_t blkio_ticks{0}; // field[41] # aka 'delayacct_blkio_ticks'
40+
float iowait{0}; // calculated
41+
float cpu_percent{0}; // calculated
42+
// stores previous values
43+
float last_stime{-1};
44+
float last_utime{-1};
45+
PTime last_time;
46+
47+
void reset();
48+
49+
void update(PID pid);
50+
51+
float get_cpu_percent(uint32_t interval_mlsec = 0);
52+
};
53+
1254
struct scputimes
1355
{
1456
float user;

include/psutil-cpp/utils.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ std::vector<std::string> cat(std::string path);
5050
// Returns valid path for cpu_freq
5151
std::string get_path(int num);
5252

53-
void sleep_mlsec(uint32_t millisec);
53+
void sleep_mlsec(uint32_t mlsec);
5454

55-
bool is_only_numbers(const std::string &str);
55+
bool has_only_numbers(const std::string &str);
5656

5757
std::vector<std::string> get_dirs_list(const std::string &path);
5858

src/cpu.cpp

+184
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
#include <psutil-cpp/cpu.hpp>
2+
#include <iostream>
3+
#include <cassert>
4+
5+
#define MUTE_PSUTIL_LOGS
26

37
std::optional<std::vector<scputimes>> cpu_times(bool percpu)
48
{
@@ -152,4 +156,184 @@ std::ostream &operator<<(std::ostream &output, const scpufreq &cpufreq)
152156
<< ", min=" << cpufreq.min
153157
<< ", max=" << cpufreq.max
154158
<< ")";
159+
}
160+
161+
const std::string& get_proc_path() {
162+
static const std::string proc_path = std::getenv("PROC_PATH") ? std::getenv("PROC_PATH") : "/proc";
163+
return proc_path;
164+
}
165+
166+
process::process(PID pid) {
167+
update(pid);
168+
}
169+
170+
void process::reset() {
171+
pid = 0;
172+
name.clear();
173+
path.clear();
174+
cmdline_cmd.clear();
175+
exists = false;
176+
status = 0;
177+
ppid = 0;
178+
ttynr = 0;
179+
utime = 0;
180+
stime = 0;
181+
children_utime = 0;
182+
children_stime = 0;
183+
create_time = 0;
184+
cpu_num = 0;
185+
blkio_ticks = 0;
186+
iowait = 0;
187+
}
188+
189+
void process::update(PID pid) {
190+
const auto &proc_path = get_proc_path();
191+
path = proc_path + "/" + std::to_string(pid) + "/stat";
192+
this->pid = pid;
193+
194+
auto str = file_to_string(path);
195+
if (str.empty()) {
196+
std::cerr << "psutil: can not find process stat file with path " << path << std::endl;
197+
reset();
198+
this->pid = pid;
199+
return;
200+
}
201+
// TODO: optimize
202+
str = replace_all(str, "((", "(");
203+
str = replace_all(str, "))", ")");
204+
auto stat = split_by_delim(str, ")");
205+
assert(stat.size() == 2);
206+
auto stat2 = split_by_delim(stat[1], " ");
207+
208+
PID proc_pid = std::stoi(stat[0]);
209+
if (pid != proc_pid) {
210+
std::cerr << "psutil: stat pid[" << proc_pid << "] != requested pid[" << pid << "]" << std::endl;
211+
reset();
212+
this->pid = pid;
213+
return;
214+
}
215+
216+
cmdline_cmd = file_to_string(proc_path + "/" + std::to_string(pid) + "/cmdline");
217+
time = std::chrono::system_clock::now();
218+
name = split_by_delim(stat[0], "(")[1];
219+
status = stat2[1][0];
220+
ppid = std::stoi(stat2[2]);
221+
ttynr = std::stoi(stat2[5]);
222+
utime = std::stof(stat2[12]);
223+
stime = std::stof(stat2[13]);
224+
children_utime = std::stof(stat2[14]);
225+
children_stime = std::stof(stat2[15]);
226+
create_time = std::stof(stat2[20]);
227+
cpu_num = std::stoi(stat2[37]);
228+
blkio_ticks = std::stoi(stat2[40]);
229+
exists = true;
230+
231+
// calc cpu times
232+
static const long kClockTicks = sysconf(_SC_CLK_TCK);
233+
utime = float(utime) / kClockTicks;
234+
stime = float(stime) / kClockTicks;
235+
children_utime = float(children_utime) / kClockTicks;
236+
children_stime = float(children_stime) / kClockTicks;
237+
iowait = float(blkio_ticks) / kClockTicks;
238+
}
239+
240+
/**
241+
Return a float representing the current process CPU utilization as a percentage.
242+
243+
When *interval* is 0.0 or None (default) compares process times
244+
to system CPU times elapsed since last call, returning
245+
immediately (non-blocking). That means that the first time
246+
this is called it will return a meaningful 0.0 value.
247+
248+
When *interval* is > 0.0 compares process times to system CPU
249+
times elapsed before and after the interval (blocking).
250+
251+
In this case is recommended for accuracy that this function
252+
be called with at least 0.1 seconds between calls.
253+
254+
A value > 100.0 can be returned in case of processes running
255+
multiple threads on different CPU cores.
256+
257+
The returned value is explicitly NOT split evenly between
258+
all available logical CPUs. This means that a busy loop process
259+
running on a system with 2 logical CPUs will be reported as
260+
having 100% CPU utilization instead of 50%.
261+
*/
262+
float process::get_cpu_percent(uint32_t interval_mlsec) {
263+
if (pid == 0) {
264+
std::cerr << "psutil: get_cpu_percent() can not be run. Process PID is [0]" << std::endl;
265+
return 0;
266+
}
267+
268+
float stime1 = 0l, utime1 = 0, stime2 = 0, utime2 = 0;
269+
PTime time1, time2;
270+
if (interval_mlsec > 0) {
271+
update(pid);
272+
if (!exists) {
273+
return 0;
274+
}
275+
stime1 = stime;
276+
utime1 = utime;
277+
time1 = time;
278+
sleep_mlsec(interval_mlsec);
279+
update(pid);
280+
if (!exists) {
281+
return 0;
282+
}
283+
stime2 = stime;
284+
utime2 = utime;
285+
time2 = time;
286+
} else {
287+
if (last_utime == -1 || last_stime == -1) {
288+
last_stime = stime;
289+
last_utime = utime;
290+
last_time = time;
291+
return 0;
292+
}
293+
utime1 = last_utime;
294+
stime1 = last_stime;
295+
time1 = last_time;
296+
update(pid);
297+
if (!exists) {
298+
return 0;
299+
}
300+
utime2 = utime;
301+
stime2 = stime;
302+
time2 = time;
303+
last_stime = stime;
304+
last_utime = utime;
305+
last_time = time;
306+
}
307+
308+
auto delta_proc = (utime2 - utime1) + (stime2 - stime1);
309+
delta_proc = delta_proc < 0 ? 0 : delta_proc;
310+
auto delta_mksec = std::chrono::duration_cast<std::chrono::microseconds>(time2 - time1).count();
311+
auto delta_time = static_cast<float>(delta_mksec / 1000000.0 * cpu_count()); // NOLINT(cppcoreguidelines-narrowing-conversions)
312+
if (delta_time < 1) {
313+
#ifndef MUTE_PSUTIL_LOGS
314+
std::cerr << std::fixed << "pid={" << pid << "} delta_time={" << delta_time << "} is too small\n";
315+
#endif
316+
cpu_percent = 0;
317+
return cpu_percent;
318+
}
319+
auto overall_cpus_percent = delta_time == 0 ? 0 : ((delta_proc / delta_time) * 100);
320+
auto single_cpu_percent = overall_cpus_percent * (float) cpu_count();
321+
// TODO: round value
322+
cpu_percent = ceilf(single_cpu_percent * 100) / 100;
323+
324+
#ifndef MUTE_PSUTIL_LOGS
325+
if(single_cpu_percent > 0) {
326+
std::cout << "--------------------------------------------\n";
327+
std::cout << std::fixed << "pid={" << pid << "} pt1.system={" << stime1 << "} pt1.user={" << utime1 << "}\n";
328+
std::cout << std::fixed << "pid={" << pid << "} pt2.system={" << stime2 << "} pt2.user={" << utime2 << "}\n";
329+
auto tt1 = std::chrono::duration_cast<std::chrono::microseconds>(time1.time_since_epoch()).count();
330+
auto tt2 = std::chrono::duration_cast<std::chrono::microseconds>(time2.time_since_epoch()).count();
331+
std::cout << std::fixed << "st1={" << tt1 << "} st2={" << tt2 << "}\n";
332+
std::cout << std::fixed << "delta_time={" << delta_time << "}\n";
333+
std::cout << std::fixed << "delta_proc={" << delta_proc << "}\n";
334+
std::cout << std::fixed << "single_cpu_percent={" << cpu_percent << "}\n";
335+
}
336+
#endif
337+
338+
return cpu_percent;
155339
}

src/utils.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,11 @@ std::string get_path(int num)
147147
return "none";
148148
}
149149

150-
void sleep_mlsec(uint32_t millisec) {
151-
std::this_thread::sleep_for(std::chrono::milliseconds(millisec));
150+
void sleep_mlsec(uint32_t mlsec) {
151+
std::this_thread::sleep_for(std::chrono::milliseconds(mlsec));
152152
}
153153

154-
bool is_only_numbers(const std::string &str) {
154+
bool has_only_numbers(const std::string &str) {
155155
if (str.empty()) {
156156
return false;
157157
}

0 commit comments

Comments
 (0)