Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Frameworks/SystemPerformance/Instruments/Process.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Framework_SystemPerformance_Instruments_Process_h_
5#define _Stroika_Framework_SystemPerformance_Instruments_Process_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include <filesystem>
10
12#include "Stroika/Foundation/Containers/Mapping.h"
13#include "Stroika/Foundation/Containers/Sequence.h"
14#include "Stroika/Foundation/DataExchange/ObjectVariantMapper.h"
15#include "Stroika/Foundation/Execution/Process.h"
16#include "Stroika/Foundation/IO/FileSystem/Common.h"
17#include "Stroika/Frameworks/SystemPerformance/Instrument.h"
18
19/*
20 *
21 * TODO:
22 * @todo In POSIX/fAllowUse_PS mode add or fix support for Command-Line (done badly), EXE-Path, Process-Started-At,
23 * Percent-CPUTime-Used.
24 *
25 * @todo Consider replacing fIncluidePIDs and fOMitPIDs with FilterFunctionType...
26 */
27
28namespace Stroika::Frameworks::SystemPerformance::Instruments::Process {
29
30 using Foundation::Containers::Mapping;
31 using Foundation::DataExchange::ObjectVariantMapper;
33 using Foundation::Time::DurationSeconds;
34 using Foundation::Time::TimePointSeconds;
35
36 using MemorySizeType = uint64_t;
37
38 /**
39 * Based closely on http://en.wikipedia.org/wiki/Procfs
40 */
41 struct ProcessType {
42 /**
43 * If true (unix only) - this process is really a thread builtin to the kernel. If missing, unknown.
44 */
45 optional<bool> fKernelProcess;
46
47 /**
48 * This will be omitted if unknown, but can be the special value zero, whose meaning varies from OS to OS, but
49 * typically means created by the system sponaneously (like the init process on UNIX has parent process id 0).
50 *
51 * Kernel processes on some operating systems (e.g. AIX) also tend to have parent PID 0)
52 */
53 optional<pid_t> fParentProcessID;
54
55 /*
56 * On some systems, in some cases, we cannot find the EXE name, but still have some sort of process
57 * name we can retrieve.
58 */
59 optional<String> fProcessName;
60
61 /*
62 * on windows, this is in the form of username@domain if a domain is present
63 */
64 optional<String> fUserName;
65
66 optional<String> fCommandLine;
67 optional<filesystem::path> fCurrentWorkingDirectory;
68 optional<Mapping<String, String>> fEnvironmentVariables;
69 optional<filesystem::path> fEXEPath;
70
71 /**
72 * can be differnt than / if chroot()
73 */
74 optional<filesystem::path> fRoot;
75
76 optional<Time::DateTime> fProcessStartedAt;
77
78 /**
79 * Based on
80 * http://linux.die.net/man/5/proc (search for /proc/[pid]/stat)
81 * One character from the string "RSDZTW" where R is running, S is sleeping in
82 * an interruptible wait, D is waiting in uninterruptible disk sleep, Z is zombie,
83 * T is traced or stopped (on a signal), and W is paging.
84 *
85 * \note Common::DefaultNames<> supported
86 */
87 enum class RunStatus {
88 eRunning,
89 eSleeping,
90 eWaitingOnDisk,
91 eWaitingOnPaging,
92 eZombie,
93 eSuspended, // T is traced or stopped (on a signal)
94
95 Stroika_Define_Enum_Bounds (eRunning, eSuspended)
96 };
97
98 /**
99 */
100 optional<RunStatus> fRunStatus;
101
102 /**
103 * This is the total VM allocated solely for the purpose of this process.
104 * This includes data space, SHOULD (but may not include)stack space for threads etc, and
105 * heap.
106 *
107 * On UNIX, this corresponds to VSZ in ps, and top.
108 * For now, this is not supported in Windows.
109 *
110 * @see fPrivateBytes
111 */
112 optional<MemorySizeType> fPrivateVirtualMemorySize;
113
114 /**
115 * This is the total VM size for the process, including all mapped shared data. This value will
116 * often grossly over-state the amount of 'virtual memory' in use for a process, because
117 * it can include things like memory mapped files, etc.
118 */
119 optional<MemorySizeType> fTotalVirtualMemorySize;
120
121 /**
122 * Resident Set Size (RSS): number of [BYTES] the process has in real memory. This is just the
123 * pages which count toward text, data, or stack space. This does not include pages which have not
124 * been demand-loaded in, or which are swapped out.
125 *
126 * @todo DECIDE IF
127 * This does NOT include 'shared' memory (e.g. for mapped .so files)
128 * SEEN CONTRADICTORY INFO.
129 * RATIONALIZE AND PROIVIDE BOTH (optionally)
130 *
131 * This value is commonly used/available in UNIX.
132 *
133 * \note AIX lipperf: proc_real_mem_data + proc_real_mem_text
134 */
135 optional<MemorySizeType> fResidentMemorySize;
136
137 /*
138 * From http://superuser.com/questions/618686/private-bytes-vs-working-set-in-process-explorer:
139 * Private Bytes refers to the amount of Page file space that is allocated to the process
140 * (not necessarily used) in the event that the process's private memory footprint is completely
141 * paged out to swap. most of the time, the process is not entirely (or at all) page-file resident,
142 * so that's why private bytes appears to have "room" for further allocation. It is not however the case.
143 *
144 * Private bytes however only refers to the processes private memory, so this value may not reflect
145 * shared resources (even if the shared resource is only used by this process at present).
146 *
147 * This value is commonly used/available in Windows
148 *
149 * For Linux:
150 * This is all the private area from /proc/<PID>/smaps with the label Private_Clean: or Private_Dirty
151 *
152 * For AIX:
153 * This is lipperf psinfo proc_size minus proc_virt_mem_text (so all VM that is process specific, less text space VM).
154 *
155 * @see fPrivateVirtualMemorySize
156 */
157 optional<MemorySizeType> fPrivateBytes;
158
159 /**
160 * Total number of page major (causing a block/disk read)
161 */
162 optional<unsigned int> fMajorPageFaultCount;
163
164 /**
165 * Total number of page faults (read or write) ever for this process.
166 */
167 optional<unsigned int> fPageFaultCount;
168
169 /**
170 * @todo better document, and only implemented for windows - but need todo for unix
171 * Linux doesn't track WSS, but does track RSS - 'fResidentMemorySize' - which is nearly the same thing.
172 */
173 optional<MemorySizeType> fWorkingSetSize;
174
175 /**
176 * @todo support in Windows AND UNIX - WIndows must use WMI - I think - and
177 * @todo DOCUMENT
178 */
179 optional<MemorySizeType> fPrivateWorkingSetSize;
180
181 /**
182 * Average CPU time used / second over this collection interval. This when available - is logically
183 * (fTotalCPUTimeEverUsed-PREV.fTotalCPUTimeEverUsed)/measurement_time;
184 *
185 * So - if you have two cores running constantly, this returns 2.0;
186 */
187 optional<DurationSeconds> fAverageCPUTimeUsed;
188
189 /**
190 * In seconds - combines system and user time, and is NOT a time over the interval, but rather is
191 * the total (user + system) usage of the process since it started.
192 *
193 * This is in units of a single CPU, so if you have a 2 CPU system running flat out for 3 seconds,
194 * this number would be 6 (2 * 3).
195 */
196 optional<DurationSeconds> fTotalCPUTimeEverUsed;
197
198 /**
199 */
200 optional<unsigned int> fThreadCount;
201
202 /**
203 * Rate in bytes per second.
204 * This is summed accross all IO devices, including disk and network.
205 */
206 optional<double> fCombinedIOReadRate;
207
208 /*
209 * Rate in bytes per second
210 * This is summed accross all IO devices, including disk and network.
211 */
212 optional<double> fCombinedIOWriteRate;
213
214 /**
215 * See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
216 * search for 'read_bytes'
217 *
218 * This reflects the total number of bytes read over the process lifetime.
219 *
220 * This (I believe) - includes paging reads/writes.
221 *
222 * @todo make sure This is summed accross all IO devices, including disk and network
223 */
224 optional<double> fCombinedIOReadBytes;
225
226 /**
227 * See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
228 * search for 'write_bytes'
229 *
230 * This reflects the total number of bytes written over the process lifetime.
231 *
232 * This (I believe) - includes paging reads/writes.
233 *
234 * @todo make sure This is summed accross all IO devices, including disk and network
235 */
236 optional<double> fCombinedIOWriteBytes;
237
238 /**
239 * Where available, check the number of TCP streams associated with this process - and
240 * separately count established, listening, and other (usually shutting down)
241 * TCP streams.
242 *
243 * \note @todo http://stroika-bugs.sophists.com/browse/STK-478 - fTCPStats for whole computer not process
244 */
245 struct TCPStats {
246 unsigned int fEstablished{};
247 unsigned int fListening{};
248 unsigned int fOther{};
249 };
250 optional<TCPStats> fTCPStats;
251
252 /**
253 * @see Characters::ToString ();
254 */
255 nonvirtual String ToString () const;
256 };
257
258 /**
259 */
261
262 /**
263 */
264 using Info = ProcessMapType;
265
266 /**
267 * This instrument produce a measurement of type kProcessMapMeasurement, whcih can be converted to ProcessMapType.
268 *
269 * \par Example Usage
270 * \code
271 * for (Measurement m : ms.fMeasurements) {
272 * if (m.fType == SystemPerformance::Instruments::Process::kProcessMapMeasurement) {
273 * AccumulateMeasurement_Process_ (m);
274 * }
275 * ...
276 * \endcode
277 */
278 const inline MeasurementType kProcessMapMeasurement{"Process-Details"sv};
279
280 /**
281 */
282 enum class CachePolicy {
283 eOmitUnchangedValues,
284 eIncludeAllRequestedValues,
285 };
286
287 /**
288 */
289 struct Options {
290 /**
291 * If FilterFunctionType is nullptr, then treat this as false.
292 */
293 using FilterFunctionType = function<bool (pid_t pid, const filesystem::path& processPath)>;
294
295 /**
296 * To compute averages, the instrument may keep around some earlier snapshots of data. This time interval is regulated by how often
297 * the capture is called (typically the Captureset::'run interval'. However, this value can be used to override that partly, and provide
298 * a minimum time for averaging.
299 *
300 * If you call capture more frequently than this interval, some (averaged) items maybe missing from the result.
301 *
302 * \pre fMinimumAveragingInterval > 0
303 */
304 Time::DurationSeconds fMinimumAveragingInterval{1.0};
305
306 /*
307 * Assign nullptr to disable commandline capture.
308 */
309 FilterFunctionType fCaptureCommandLine{[] (pid_t /*pid*/, const filesystem::path& /*processPath*/) -> bool { return true; }};
310
311 bool fCaptureEnvironmentVariables{true};
312 bool fCaptureCurrentWorkingDirectory{true};
313 bool fCaptureRoot{true};
314 bool fCaptureTCPStatistics{false};
315 optional<Set<pid_t>> fRestrictToPIDs;
316 optional<Set<pid_t>> fOmitPIDs;
317 CachePolicy fCachePolicy{CachePolicy::eIncludeAllRequestedValues};
318
319 enum ProcessNameReadPolicy {
320 eNever,
321 eOnlyIfEXENotRead,
322 eAlways
323 };
324 ProcessNameReadPolicy fProcessNameReadPolicy{eOnlyIfEXENotRead};
325#if qStroika_Foundation_Common_Platform_POSIX
326 bool fAllowUse_ProcFS{true};
327 bool fAllowUse_PS{true};
328#elif qStroika_Foundation_Common_Platform_Windows
329//PERHAPS SUPPORT IN FUTURE?
330//bool fAllowUse_WMI { true };
331#endif
332 };
333
334 /**
335 * This class is designed to be object-sliced into just the SystemPerformance::Instrument
336 *
337 * \note Constructing the instrument does no capturing (so sb quick/cheap) - capturing starts when you
338 * first call i.Capture()
339 */
341 public:
342 Instrument (const Options& options = Options{});
343
344 public:
345 /**
346 * For Instruments::Process::Info, ProcessType and ProcessMapType, etc types.
347 */
349 };
350
351}
352
353namespace Stroika::Frameworks::SystemPerformance {
354
355 /*
356 * Specialization to improve performance
357 */
358 template <>
360
361}
362
363/*
364 ********************************************************************************
365 ***************************** Implementation Details ***************************
366 ********************************************************************************
367 */
369 template <>
370 constexpr EnumNames<Frameworks::SystemPerformance::Instruments::Process::ProcessType::RunStatus>
371 DefaultNames<Frameworks::SystemPerformance::Instruments::Process::ProcessType::RunStatus>::k{{{
372 {Frameworks::SystemPerformance::Instruments::Process::ProcessType::RunStatus::eRunning, L"Running"},
373 {Frameworks::SystemPerformance::Instruments::Process::ProcessType::RunStatus::eSleeping, L"Sleeping"},
374 {Frameworks::SystemPerformance::Instruments::Process::ProcessType::RunStatus::eWaitingOnDisk, L"WaitingOnDisk"},
375 {Frameworks::SystemPerformance::Instruments::Process::ProcessType::RunStatus::eWaitingOnPaging, L"WaitingOnPaging"},
376 {Frameworks::SystemPerformance::Instruments::Process::ProcessType::RunStatus::eZombie, L"Zombie"},
377 {Frameworks::SystemPerformance::Instruments::Process::ProcessType::RunStatus::eSuspended, L"Suspended"},
378 }}};
379}
380#endif /*_Stroika_Framework_SystemPerformance_Instruments_Process_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
An Atom is like a String, except that its much cheaper to copy/store/compare, and the semantics of co...
Definition Atom.h:133
ObjectVariantMapper can be used to map C++ types to and from variant-union types, which can be transp...
An Instrument is a stateful object from which you can Capture () a series of measurements about a sys...
Definition Instrument.h:69
nonvirtual T CaptureOneMeasurement(Range< TimePointSeconds > *measurementTimeOut=nullptr)
int pid_t
TODO - maybe move this to configuraiotn module???
Definition Module.h:34