Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Capturer.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Framework_SystemPerformance_Capturer_h_
5#define _Stroika_Framework_SystemPerformance_Capturer_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include "Stroika/Foundation/Common/Property.h"
10#include "Stroika/Foundation/Containers/Collection.h"
11#include "Stroika/Foundation/Containers/Set.h"
16
17#include "CaptureSet.h"
18#include "Measurement.h"
19
20/*
21 * TODO:
22 * @todo Somehow make captrueset comparable, so that we can use it as a KEY with mreasumrnes
23 * and then store latest for each captureset.
24 *
25 * Then combine into one massive measuremnets list fMeasurmentsMapping.Values() (once
26 * we have impelemtend values() on mapping).
27 *
28 * @todo make it optional storing the most recent measurement set.
29 *
30 * @todo Use threadpool CORRECTLY!. Maybe push ALL measurements into
31 * threadpool at just the right time, and then paralell process as much as the threadpool size allows.
32 * That seems the best appraoch! Default to theadpool size of one! One recurring task in threadpool
33 * would be to re-schedule rest of measurements...
34 */
35
36namespace Stroika::Frameworks::SystemPerformance {
37
38 using namespace Stroika::Foundation;
41 using Containers::Set;
43 using Time::Duration;
44
45 /**
46 * A Capturer is a utility you MAY wish to use the the SystemPerformance framework. It provides some
47 * fairly handy default mechanisms to manage (possibly multiple) CaptureSets, and either store the last result
48 * for each, or to run callbacks on those results.
49 *
50 * This also runs its capturing on a (single) background thread (so that captures / averages are over a consistent time interval).
51 * This has implications for how much its able to keep up with and maintain all the measurements in question.
52 *
53 * Note - there is no reason you cannot use the rest of the SystemPerformance framework without this class,
54 * if its pattern doesn't meet your needs.
55 *
56 * \pre Debug::AppearsDuringMainLifetime() at all points during the Capturer lifetime since it owns threads.
57 *
58 * \par Example Usage
59 * This example shows using capturer with callbacks, and raw mode - treating the measurements as 'untyped'
60 * VariantValue objects.
61 *
62 * \code
63 * Capturer capturer;
64 * {
65 * CaptureSet cs;
66 * cs.runPeriod = 15s;
67 * for (Instrument i : SystemPerformance::GetAllInstruments ()) {
68 * cs.AddInstrument (i);
69 * }
70 * capturer.AddCaptureSet (cs);
71 * }
72 * capturer.AddMeasurementsCallback ([oneLineMode] (MeasurementSet ms) {
73 * cout << " Measured-At: " << ms.fMeasuredAt.ToString () << endl;
74 * for (Measurement mi : ms.fMeasurements) {
75 * cout << " " << mi.fType.GetPrintName () << ": " << Serialize_ (mi.fValue, oneLineMode) << endl;
76 * }
77 * });
78 * // run til timeout and then fall out...
79 * IgnoreExceptionsForCall (Execution::WaitableEvent{}.Wait (runFor));
80 * \endcode
81 *
82 * \par Example Usage
83 * This example shows using capturer, running in the background at regular time intervals, and allows callers to capture bits
84 * of performance status (like to report in web service) - just reporting the latest captured data as of when called.
85 *
86 * \code
87 * struct MyCapturer_ : Capturer {
88 * public:
89 * Instruments::CPU::Instrument fCPUInstrument;
90 * Instruments::Process::Instrument fProcessInstrument;
91 * MyCapturer_ ()
92 * : fProcessInstrument{Instruments::Process::Options{
93 * .fRestrictToPIDs = Set<pid_t>{Execution::GetCurrentProcessID ()},
94 * }}
95 * {
96 * AddCaptureSet (CaptureSet{30s, {fCPUInstrument, fProcessInstrument}});
97 * }
98 * };
99 *
100 * MyCapturer_ capturer; // capturer internally syncrhonized so no need for extra sync...; must must respect Require (Debug::AppearsDuringMainLifetime ());
101 *
102 * // Now do from any thread as often as desired, reporting latest data:
103 * auto measurements = capturer.GetMostRecentMeasurements (); // capture results on a regular cadence with MyCapturer, and just report the latest stats
104 * DateTime now = DateTime::Now ();
105 * optional<double> runQLength;
106 * optional<double> totalCPUUsage;
107 * if (auto om = capturer.fCPUInstrument.MeasurementAs<Instruments::CPU::Info> (measurements)) {
108 * runQLength = om->fRunQLength;
109 * totalCPUUsage = om->fTotalCPUUsage;
110 * }
111 * optional<Duration> processUptime;
112 * optional<double> averageCPUTimeUsed;
113 * optional<uint64_t> workingOrResidentSetSize;
114 * optional<double> combinedIORate;
115 * if (auto om = capturer.fProcessInstrument.MeasurementAs<Instruments::Process::Info> (measurements)) {
116 * Assert (om->size () == 1);
117 * Instruments::Process::ProcessType thisProcess = (*om)[Execution::GetCurrentProcessID ()];
118 * if (auto o = thisProcess.fProcessStartedAt) {
119 * processUptime = now - *o;
120 * }
121 * averageCPUTimeUsed = thisProcess.fAverageCPUTimeUsed;
122 * workingOrResidentSetSize = Memory::NullCoalesce (thisProcess.fWorkingSetSize, thisProcess.fResidentMemorySize);
123 * combinedIORate = thisProcess.fCombinedIOWriteRate;
124 * }
125 * \endcode
126 *
127 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
128 */
129 class Capturer {
130 public:
131 /**
132 * Note - if the constructor is called with explicit capture-sets, then these are run once before the constructor returns
133 * (so the caller can read current value)
134 */
135 Capturer ();
136 Capturer (const CaptureSet& cs);
137 Capturer (const Capturer&) = delete;
138
139 public:
140 nonvirtual Capturer& operator= (const Capturer&) = delete;
141
142 public:
143 /**
144 * Read this anytime (for example if you don't want to bother with callbacks or if
145 * some other process needs to query the latest values from the instrument measurers.
146 */
148
149 public:
150 /**
151 */
153
154 public:
155 /**
156 */
158
159 public:
160 /**
161 */
162 nonvirtual void AddMeasurementsCallback (const NewMeasurementsCallbackType& cb);
163
164 public:
165 /**
166 */
167 nonvirtual void RemoveMeasurementsCallback (const NewMeasurementsCallbackType& cb);
168
169 public:
170 /**
171 */
173
174 public:
175 /**
176 */
177 nonvirtual void AddCaptureSet (const CaptureSet& cs);
178
179 private:
180 nonvirtual void ManageRunner_ (bool on);
181
182 private:
183 nonvirtual void Runner_ ();
184
185 private:
186 nonvirtual void RunnerOnce_ (const CaptureSet& cs);
187
188 private:
189 // FOR NOW - just assign/overwrite the latest measurement set, and call
190 // callbacks as needed
191 nonvirtual void UpdateMeasurementSet_ (const MeasurementSet& ms);
192
193 private:
195 uint64_t fCaptureSetChangeCount_{0}; // doesn't need to be atomic because only updated/checked holding capturesets lock
197 Execution::Synchronized<MeasurementSet> fCurrentMeasurementSet_;
198 Execution::ThreadPool fThreadPool_{Execution::ThreadPool::Options{.fThreadCount = 0, .fThreadPoolName = "SystemPerformanceCapturer"}}; // Subtle - construct last so auto-destructed first (shuts down threads)
199 };
200
201}
202
203/*
204 ********************************************************************************
205 ***************************** Implementation Details ***************************
206 ********************************************************************************
207 */
208#include "Capturer.inl"
209
210#endif /*_Stroika_Framework_SystemPerformance_Capturer_h_*/
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
Definition Collection.h:102
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
Definition Set.h:105
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
Common::ReadOnlyProperty< MeasurementSet > pMostRecentMeasurements
Definition Capturer.h:147