Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
SystemPerformanceClient.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <iostream>
7
8#include "Stroika/Foundation/Characters/FloatConversion.h"
12#include "Stroika/Foundation/Execution/CommandLine.h"
13#include "Stroika/Foundation/Execution/Process.h"
15#if qStroika_Foundation_Common_Platform_POSIX
16#include "Stroika/Foundation/Execution/SignalHandlers.h"
17#endif
19#include "Stroika/Foundation/Execution/WaitableEvent.h"
20#include "Stroika/Foundation/Streams/MemoryStream.h"
21
22#include "Stroika/Frameworks/SystemPerformance/AllInstruments.h"
23#include "Stroika/Frameworks/SystemPerformance/Capturer.h"
24#include "Stroika/Frameworks/SystemPerformance/Instruments/CPU.h"
25#include "Stroika/Frameworks/SystemPerformance/Instruments/Memory.h"
26#include "Stroika/Frameworks/SystemPerformance/Instruments/Process.h"
27#include "Stroika/Frameworks/SystemPerformance/Measurement.h"
28
29using namespace std;
30using std::byte;
31
32using namespace Stroika::Foundation;
34using namespace Stroika::Frameworks;
35using namespace Stroika::Frameworks::SystemPerformance;
36
40using Containers::Set;
43using Time::DateTime;
45using Time::Duration;
47
48namespace {
49 string Serialize_ (VariantValue v, bool oneLineMode)
50 {
51 Streams::MemoryStream::Ptr<byte> out = Streams::MemoryStream::New<byte> ();
53 // strip CRLF - so shows up on one line
54 String result = String::FromUTF8 (out.As<string> ());
55 if (oneLineMode) {
56 result = result.StripAll ([] (Character c) -> bool { return c == '\n' or c == '\r'; });
57 }
58 return result.AsNarrowSDKString ();
59 }
60#if qCompilerAndStdLib_template_template_argument_as_different_template_paramters_Buggy
61 template <typename X>
62 using RANGE_TEMPLATE_BWA = Range<X>;
63#endif
65 {
66#if qCompilerAndStdLib_template_template_argument_as_different_template_paramters_Buggy
67 return Time::clock_cast<DisplayedRealtimeClock, RANGE_TEMPLATE_BWA> (tpRange);
68#elif qCompilerAndStdLib_template_template_auto_deduced_Buggy
69 return Time::clock_cast<DisplayedRealtimeClock, Range> (tpRange);
70#else
71 return Time::clock_cast<DisplayedRealtimeClock> (tpRange);
72#endif
73 }
74 void Demo_PrintInstruments_ ()
75 {
76 cout << "Instrument:" << endl;
77 for (const Instrument& i : SystemPerformance::GetAllInstruments ()) {
78 cout << " " << i.instrumentName ().GetPrintName ().AsNarrowSDKString () << endl;
79 // print measurements too?
80 }
81 }
82 void Demo_UsingCapturerWithCallbacks_ (Set<InstrumentNameType> run, bool oneLineMode, Duration captureInterval, Duration runFor)
83 {
84 Capturer capturer;
85 {
86 CaptureSet cs;
87 cs.runPeriod = captureInterval;
88 for (const Instrument& i : SystemPerformance::GetAllInstruments ()) {
89 if (not run.empty ()) {
90 if (not run.Contains (i.instrumentName)) {
91 continue;
92 }
93 }
94 cs.AddInstrument (i);
95 }
96 capturer.AddCaptureSet (cs);
97 }
98 capturer.AddMeasurementsCallback ([oneLineMode] (MeasurementSet ms) {
99 cout << " Measured-At: " << toDisp_ (ms.fMeasuredAt).ToString ().AsNarrowSDKString () << endl;
100 for (const Measurement& mi : ms.fMeasurements) {
101 cout << " " << mi.fType.GetPrintName ().AsNarrowSDKString () << ": " << Serialize_ (mi.fValue, oneLineMode) << endl;
102 }
103 });
104
105 // run til timeout and then fall out...
106 IgnoreExceptionsForCall (Execution::WaitableEvent{}.Wait (runFor));
107 }
108 void Demo_Using_Direct_Capture_On_Instrument_ (Set<InstrumentNameType> run, bool oneLineMode, Duration captureInterval)
109 {
110 cout << "Results for each instrument:" << endl;
111 for (Instrument i : SystemPerformance::GetAllInstruments ()) {
112 if (not run.empty ()) {
113 if (not run.Contains (i.instrumentName)) {
114 continue;
115 }
116 }
117 cout << " " << i.instrumentName ().GetPrintName ().AsNarrowSDKString () << endl;
118 Execution::Sleep (captureInterval);
119 MeasurementSet m = i.Capture ();
120 if (m.fMeasurements.empty ()) {
121 cout << " NO DATA" << endl;
122 }
123 else {
124 cout << " Measured-At: " << toDisp_ (m.fMeasuredAt).ToString ().AsNarrowSDKString () << endl;
125 for (const Measurement& mi : m.fMeasurements) {
126 cout << " " << mi.fType.GetPrintName ().AsNarrowSDKString () << ": " << Serialize_ (mi.fValue, oneLineMode) << endl;
127 }
128 }
129 }
130 }
131}
132
133namespace {
134 namespace Demo_Using_Capturer_GetMostRecentMeasurements__Private_ {
135 using namespace Stroika::Frameworks::SystemPerformance;
136
137 struct MyCapturer_ : Capturer {
138 public:
139 Instruments::CPU::Instrument fCPUInstrument;
140 Instruments::Process::Instrument fProcessInstrument;
141
142 MyCapturer_ ()
143 : fProcessInstrument{Instruments::Process::Options{.fRestrictToPIDs = Set<pid_t>{Execution::GetCurrentProcessID ()}}}
144 {
145 AddCaptureSet (CaptureSet{30s, {fCPUInstrument, fProcessInstrument}});
146 }
147 };
148 }
149 void Demo_Using_Capturer_GetMostRecentMeasurements_ (const Duration& runFor)
150 {
151 /*
152 * The idea here is that the capturer runs in the thread in the background capturing stuff (on a periodic schedule).
153 * and you can simply grab (ANYTIME) the most recently captured values.
154 *
155 * This also demos using 'MeasurementsAs' so you can see the measurements as a structured result, as opposed to as
156 * a variant value.
157 */
158 using namespace Demo_Using_Capturer_GetMostRecentMeasurements__Private_;
159
160 MyCapturer_ capturer; // initialized threadsafe, but internally syncrhonized class
161
162 Time::TimePointSeconds doneAt = Time::GetTickCount () + runFor;
163 unsigned int pass{};
164 cout << "Printing most recent measurements (in loop):" << endl;
165 while (Time::GetTickCount () < doneAt) {
166 auto measurements = capturer.pMostRecentMeasurements (); // capture results on a regular cadence with MyCapturer, and just report the latest stats
167 DateTime now = DateTime::Now ();
168
169 optional<double> runQLength;
170 optional<double> totalCPUUsage;
171 optional<double> totalCPURatio;
172 if (auto om = capturer.fCPUInstrument.MeasurementAs<Instruments::CPU::Info> (measurements)) {
173 runQLength = om->fRunQLength;
174 totalCPUUsage = om->fTotalCPUUsage;
175 totalCPURatio = om->GetTotalCPURatio ();
176 }
177 optional<Duration> thisProcUptime;
178 optional<Duration> thisProcAverageCPUTimeUsed;
179 optional<uint64_t> thisProcWorkingOrResidentSetSize;
180 optional<double> thisProcCombinedIORate;
181 if (auto om = capturer.fProcessInstrument.MeasurementAs<Instruments::Process::Info> (measurements)) {
182 // It might not be found for some instruments (not implemented?)
183 Assert (om->size () <= 1);
184 if (om->size () == 1) {
185 Instruments::Process::ProcessType thisProcess = (*om)[Execution::GetCurrentProcessID ()];
186 if (auto o = thisProcess.fProcessStartedAt) {
187 thisProcUptime = now - *o;
188 }
189 thisProcAverageCPUTimeUsed = thisProcess.fAverageCPUTimeUsed;
190 thisProcWorkingOrResidentSetSize = Memory::NullCoalesce (thisProcess.fWorkingSetSize, thisProcess.fResidentMemorySize);
191 thisProcCombinedIORate = thisProcess.fCombinedIOWriteRate;
192 }
193 }
194 using namespace Memory; // for optional operator overloads
195 cout << "\tPass: " << pass << endl;
196 cout << "\t\tSys: " << endl;
197 cout << "\t\t\tRun-Q Length: " << Characters::ToString (runQLength).AsNarrowSDKString () << endl;
198 cout << "\t\t\tTotal CPU Usage: " << Characters::ToString (totalCPUUsage).AsNarrowSDKString () << " ("
199 << Characters::ToString (totalCPURatio * 100.0).AsNarrowSDKString () << "% of computer)" << endl;
200 cout << "\t\tThis Process: " << endl;
201 cout << "\t\t\tUptime: " << Characters::ToString (thisProcUptime).AsNarrowSDKString () << endl;
202 cout << "\t\t\tAverage CPU Time Used: " << Characters::ToString (thisProcAverageCPUTimeUsed).AsNarrowSDKString () << endl;
203 cout << "\t\t\tWorking Or Resident-Set Size: " << Characters::ToString (thisProcWorkingOrResidentSetSize).AsNarrowSDKString () << endl;
204 cout << "\t\t\tCombined IO Rate: " << Characters::ToString (thisProcCombinedIORate).AsNarrowSDKString () << endl;
205 Execution::Sleep (30s);
206 ++pass;
207 }
208 }
209}
210
211int main (int argc, const char* argv[])
212{
213 Execution::CommandLine cmdLine{argc, argv};
214 Debug::TraceContextBumper ctx{"main", "argv={}"_f, cmdLine};
215#if qStroika_Foundation_Common_Platform_POSIX
217#endif
218 using namespace Execution::StandardCommandLineOptions;
219 const Execution::CommandLine::Option kPrintNamesO_{.fSingleCharName = 'l', .fHelpOptionText = "prints only the instrument names"sv};
220 const Execution::CommandLine::Option kMostRecentO_{.fSingleCharName = 'm', .fHelpOptionText = "runs in most-recent-capture-mode"sv};
221 const Execution::CommandLine::Option kOneLineModeO_{.fSingleCharName = 'o', .fHelpOptionText = "prints instrument results (with newlines stripped)"sv};
222 const Execution::CommandLine::Option kRunInstrumentArg_{.fSingleCharName = 'r',
223 .fSupportsArgument = true,
224 .fRepeatable = true,
225 .fHelpArgName = "RUN-INSTRUMENT"sv,
226 .fHelpOptionText = "runs the given instrument (it can be repeated)"sv};
227 const Execution::CommandLine::Option kRunForO_{
228 .fSingleCharName = 't', .fSupportsArgument = true, .fHelpOptionText = "time to run for (if zero run each matching instrument once)"sv};
229 const Execution::CommandLine::Option kTimeBetweenCapturesO_{
230 .fSingleCharName = 'c', .fSupportsArgument = true, .fHelpArgName = "NSEC"sv, .fHelpOptionText = "time interval between captures"sv};
231
232 const initializer_list<Execution::CommandLine::Option> kAllOptions_ = {
233 kHelp, kPrintNamesO_, kMostRecentO_, kOneLineModeO_, kRunInstrumentArg_, kRunForO_, kTimeBetweenCapturesO_};
234
235 bool printUsage = cmdLine.Has (kHelp);
236 bool mostRecentCaptureMode = cmdLine.Has (kMostRecentO_);
237 bool printNames = cmdLine.Has (kPrintNamesO_);
238 bool oneLineMode = cmdLine.Has (kOneLineModeO_);
239 Time::DurationSeconds runFor = 0s; // default to runfor 0, so we do each once.
240 if (auto o = cmdLine.GetArgument (kRunForO_)) {
241 runFor = Duration{Characters::FloatConversion::ToFloat<Duration::rep> (*o)};
242 }
243 Time::DurationSeconds captureInterval = 15s;
244 if (auto o = cmdLine.GetArgument (kTimeBetweenCapturesO_)) {
245 captureInterval = Duration{Characters::FloatConversion::ToFloat<Duration::rep> (*o)};
246 }
248 cmdLine.GetArguments (kRunInstrumentArg_).Map<Set<InstrumentNameType>> ([] (const String& s) { return InstrumentNameType{s}; });
249
250 if (printUsage) {
251 cerr << cmdLine.GenerateUsage (kAllOptions_).AsNarrowSDKString ();
252 return EXIT_SUCCESS;
253 }
254
255 try {
256 cmdLine.Validate (kAllOptions_);
257 if (printNames) {
258 Demo_PrintInstruments_ ();
259 }
260 else if (mostRecentCaptureMode) {
261 Demo_Using_Capturer_GetMostRecentMeasurements_ (runFor);
262 }
263 else if (runFor > 0s) {
264 Demo_UsingCapturerWithCallbacks_ (run, oneLineMode, captureInterval, runFor);
265 }
266 else {
267 Demo_Using_Direct_Capture_On_Instrument_ (run, oneLineMode, Duration{captureInterval});
268 }
269 }
271 cerr << "Error encountered: " << Characters::ToString (current_exception ()).AsNarrowSDKString () << endl;
272 cerr << cmdLine.GenerateUsage (kAllOptions_).AsNarrowSDKString () << endl;
273 return EXIT_SUCCESS;
274 }
275 catch (...) {
276 String exceptMsg = Characters::ToString (current_exception ());
277 cerr << "Exception - " << exceptMsg.AsNarrowSDKString () << " - terminating..." << endl;
278 return EXIT_FAILURE;
279 }
280
281 return EXIT_SUCCESS;
282}
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
Definition Realtime.h:82
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
nonvirtual string AsNarrowSDKString() const
Definition String.inl:834
nonvirtual String StripAll(bool(*removeCharIf)(Character)) const
Definition String.cpp:1664
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
nonvirtual RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
'override' Iterable<>::Map () function so RESULT_CONTAINER defaults to Set, and improve that case to ...
An Atom is like a String, except that its much cheaper to copy/store/compare, and the semantics of co...
Definition Atom.h:133
nonvirtual void Write(const VariantValue &v, const Streams::OutputStream::Ptr< byte > &out) const
Definition Writer.inl:30
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
nonvirtual void Wait(Time::DurationSeconds timeout=Time::kInfinity)
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
nonvirtual bool empty() const
Returns true iff size() == 0.
Definition Iterable.inl:308
Common::Property< Duration > runPeriod
All the Instruments have their data 'captured' automatically every 'run-period' time interval.
Definition CaptureSet.h:48
An Instrument is a stateful object from which you can Capture () a series of measurements about a sys...
Definition Instrument.h:69
Create a format-string (see std::wformat_string or Stroika FormatString, or python 'f' strings.
int pid_t
TODO - maybe move this to configuraiotn module???
Definition Module.h:34
STL namespace.