1
1
#include < psutil-cpp/cpu.hpp>
2
+ #include < iostream>
3
+ #include < cassert>
4
+
5
+ #define MUTE_PSUTIL_LOGS
2
6
3
7
std::optional<std::vector<scputimes>> cpu_times (bool percpu)
4
8
{
@@ -152,4 +156,184 @@ std::ostream &operator<<(std::ostream &output, const scpufreq &cpufreq)
152
156
<< " , min=" << cpufreq.min
153
157
<< " , max=" << cpufreq.max
154
158
<< " )" ;
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;
155
339
}
0 commit comments