Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Capturer.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. 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 "Stroika/Frameworks/SystemPerformance/CaptureSet.h"
18#include "Stroika/Frameworks/SystemPerformance/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;
42 using Time::Duration;
43
44 /**
45 * A Capturer is a utility you MAY wish to use the the SystemPerformance framework. It provides some
46 * fairly handy default mechanisms to manage (possibly multiple) CaptureSets, and either store the last result
47 * for each, or to run callbacks on those results.
48 *
49 * This also runs its capturing on a (single) background thread (so that captures / averages are over a consistent time interval).
50 * This has implications for how much its able to keep up with and maintain all the measurements in question.
51 *
52 * Note - there is no reason you cannot use the rest of the SystemPerformance framework without this class,
53 * if its pattern doesn't meet your needs.
54 *
55 * \pre Debug::AppearsDuringMainLifetime() at all points during the Capturer lifetime since it owns threads.
56 *
57 * \par Example Usage
58 * This example shows using capturer with callbacks, and raw mode - treating the measurements as 'untyped'
59 * VariantValue objects.
60 *
61 * \code
62 * Capturer capturer;
63 * {
64 * CaptureSet cs;
65 * cs.runPeriod = 15s;
66 * for (Instrument i : SystemPerformance::GetAllInstruments ()) {
67 * cs.AddInstrument (i);
68 * }
69 * capturer.AddCaptureSet (cs);
70 * }
71 * capturer.AddMeasurementsCallback ([oneLineMode] (MeasurementSet ms) {
72 * cout << " Measured-At: " << ms.fMeasuredAt.ToString () << endl;
73 * for (Measurement mi : ms.fMeasurements) {
74 * cout << " " << mi.fType.GetPrintName () << ": " << Serialize_ (mi.fValue, oneLineMode) << endl;
75 * }
76 * });
77 * // run til timeout and then fall out...
78 * IgnoreExceptionsForCall (Execution::WaitableEvent{}.Wait (runFor));
79 * \endcode
80 *
81 * \par Example Usage
82 * This example shows using capturer, running in the background at regular time intervals, and allows callers to capture bits
83 * of performance status (like to report in web service) - just reporting the latest captured data as of when called.
84 *
85 * \code
86 * struct MyCapturer_ : Capturer {
87 * public:
88 * Instruments::CPU::Instrument fCPUInstrument;
89 * Instruments::Process::Instrument fProcessInstrument;
90 * MyCapturer_ ()
91 * : fProcessInstrument{Instruments::Process::Options{
92 * .fRestrictToPIDs = Set<pid_t>{Execution::GetCurrentProcessID ()},
93 * }}
94 * {
95 * AddCaptureSet (CaptureSet{30s, {fCPUInstrument, fProcessInstrument}});
96 * }
97 * };
98 *
99 * MyCapturer_ capturer; // capturer internally synrchonized so no need for extra sync...; must must respect Require (Debug::AppearsDuringMainLifetime ());
100 *
101 * // Now do from any thread as often as desired, reporting latest data:
102 * auto measurements = capturer.GetMostRecentMeasurements (); // capture results on a regular cadence with MyCapturer, and just report the latest stats
103 * DateTime now = DateTime::Now ();
104 * optional<double> runQLength;
105 * optional<double> totalCPUUsage;
106 * if (auto om = capturer.fCPUInstrument.MeasurementAs<Instruments::CPU::Info> (measurements)) {
107 * runQLength = om->fRunQLength;
108 * totalCPUUsage = om->fTotalCPUUsage;
109 * }
110 * optional<Duration> processUptime;
111 * optional<double> averageCPUTimeUsed;
112 * optional<uint64_t> workingOrResidentSetSize;
113 * optional<double> combinedIORate;
114 * if (auto om = capturer.fProcessInstrument.MeasurementAs<Instruments::Process::Info> (measurements)) {
115 * Assert (om->size () == 1);
116 * Instruments::Process::ProcessType thisProcess = (*om)[Execution::GetCurrentProcessID ()];
117 * if (auto o = thisProcess.fProcessStartedAt) {
118 * processUptime = now - *o;
119 * }
120 * averageCPUTimeUsed = thisProcess.fAverageCPUTimeUsed;
121 * workingOrResidentSetSize = Memory::NullCoalesce (thisProcess.fWorkingSetSize, thisProcess.fResidentMemorySize);
122 * combinedIORate = thisProcess.fCombinedIOWriteRate;
123 * }
124 * \endcode
125 *
126 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
127 */
128 class Capturer {
129 public:
130 /**
131 * Note - if the constructor is called with explicit capture-sets, then these are run once before the constructor returns
132 * (so the caller can read current value)
133 */
134 Capturer ();
135 Capturer (const CaptureSet& cs);
136 Capturer (const Capturer&) = delete;
137
138 public:
139 nonvirtual Capturer& operator= (const Capturer&) = delete;
140
141 public:
142 /**
143 * Read this anytime (for example if you don't want to bother with callbacks or if
144 * some other process needs to query the latest values from the instrument measurers.
145 */
147
148 public:
149 /**
150 */
151 using NewMeasurementsCallbackType = function<void (const MeasurementSet&)>;
152
153 public:
154 /**
155 * \brief Names a callback AddMeasurementsCallback added, for RemoveMeasurementsCallback.
156 */
158
159 public:
160 /**
161 * \brief Call cb with each MeasurementSet captured from now on - on the capturing thread (and, for the first capture of
162 * a set AddCaptureSet adds, on its caller's), so cb must be thread-safe. An exception from it is logged (DbgTrace)
163 * and ignored: the other callbacks still get the measurements, and the capturing goes on. Never waits for a
164 * running callback.
165 */
166 nonvirtual MeasurementsCallbackID AddMeasurementsCallback (const NewMeasurementsCallbackType& cb);
167
168 public:
169 /**
170 * \brief Once this returns, that callback is not running, and is never called again - except that, called from within it,
171 * the call already under way finishes. So it waits for that callback, running on another thread: do not call it
172 * holding a lock that callback takes. Removing one not added (or already removed) does nothing.
173 */
175
176 public:
177 /**
178 */
180
181 public:
182 /**
183 */
184 nonvirtual void AddCaptureSet (const CaptureSet& cs);
185
186 private:
187 nonvirtual void ManageRunner_ (bool on);
188
189 private:
190 nonvirtual void Runner_ ();
191
192 private:
193 nonvirtual void RunnerOnce_ (const CaptureSet& cs);
194
195 private:
196 // FOR NOW - just assign/overwrite the latest measurement set, and call
197 // callbacks as needed
198 nonvirtual void UpdateMeasurementSet_ (const MeasurementSet& ms);
199
200 private:
202 uint64_t fCaptureSetChangeCount_{0}; // doesn't need to be atomic because only updated/checked holding capturesets lock
203 Execution::CallbackRegistry<void (const MeasurementSet&)> fCallbacks_;
204 Execution::Synchronized<MeasurementSet> fCurrentMeasurementSet_;
205 Execution::ThreadPool fThreadPool_{Execution::ThreadPool::Options{.fThreadCount = 0, .fThreadPoolName = "SystemPerformanceCapturer"}}; // Subtle - construct last so auto-destructed first (shuts down threads)
206 };
207
208}
209
210/*
211 ********************************************************************************
212 ***************************** Implementation Details ***************************
213 ********************************************************************************
214 */
215#include "Capturer.inl"
216
217#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 ...
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
The callbacks an object calls when something happens - each added, and removed by the ID Add gave it ...
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
nonvirtual MeasurementsCallbackID AddMeasurementsCallback(const NewMeasurementsCallbackType &cb)
Call cb with each MeasurementSet captured from now on - on the capturing thread (and,...
Definition Capturer.cpp:47
nonvirtual void RemoveMeasurementsCallback(MeasurementsCallbackID cb)
Once this returns, that callback is not running, and is never called again - except that,...
Definition Capturer.cpp:52
Execution::CallbackRegistry< void(const MeasurementSet &)>::ID MeasurementsCallbackID
Names a callback AddMeasurementsCallback added, for RemoveMeasurementsCallback.
Definition Capturer.h:157
Common::ReadOnlyProperty< MeasurementSet > pMostRecentMeasurements
Definition Capturer.h:146