Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
SystemPerformanceClient.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. 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::Foundation::Execution;
35using namespace Stroika::Frameworks;
36using namespace Stroika::Frameworks::SystemPerformance;
37
41using Containers::Set;
42using Time::DateTime;
44using Time::Duration;
46
47namespace {
48 string Serialize_ (VariantValue v, bool oneLineMode)
49 {
50 Streams::MemoryStream::Ptr<byte> out = Streams::MemoryStream::New<byte> ();
52 // strip CRLF - so shows up on one line
53 String result = String::FromUTF8 (out.As<string> ());
54 if (oneLineMode) {
55 result = result.StripAll ([] (Character c) -> bool { return c == '\n' or c == '\r'; });
56 }
57 return result.AsNarrowSDKString ();
58 }
59#if qCompilerAndStdLib_template_template_argument_as_different_template_paramters_Buggy or qCompilerAndStdLib_template_template_auto_deduced_Buggy
60 template <typename X>
61 using RANGE_TEMPLATE_BWA = Range<X>;
62#endif
64 {
65#if qCompilerAndStdLib_template_template_argument_as_different_template_paramters_Buggy or qCompilerAndStdLib_template_template_auto_deduced_Buggy
66 return Time::clock_cast<DisplayedRealtimeClock, RANGE_TEMPLATE_BWA> (tpRange);
67#else
68 return Time::clock_cast<DisplayedRealtimeClock> (tpRange);
69#endif
70 }
71 void Demo_PrintInstruments_ ()
72 {
73 cout << "Instrument:" << endl;
74 for (const Instrument& i : SystemPerformance::GetAllInstruments ()) {
75 cout << " " << i.instrumentName ().GetPrintName () << endl;
76 // print measurements too?
77 }
78 }
79 void Demo_UsingCapturerWithCallbacks_ (Set<InstrumentNameType> run, bool oneLineMode, Duration captureInterval, Duration runFor)
80 {
81 Capturer capturer;
82 {
83 CaptureSet cs;
84 cs.runPeriod = captureInterval;
85 for (const Instrument& i : SystemPerformance::GetAllInstruments ()) {
86 if (not run.empty ()) {
87 if (not run.Contains (i.instrumentName)) {
88 continue;
89 }
90 }
91 cs.AddInstrument (i);
92 }
93 capturer.AddCaptureSet (cs);
94 }
95 capturer.AddMeasurementsCallback ([oneLineMode] (MeasurementSet ms) {
96 cout << " Measured-At: " << toDisp_ (ms.fMeasuredAt).ToString () << endl;
97 for (const Measurement& mi : ms.fMeasurements) {
98 cout << " " << mi.fType.GetPrintName () << ": " << Serialize_ (mi.fValue, oneLineMode) << endl;
99 }
100 });
101
102 // run til timeout and then fall out...
103 IgnoreExceptionsForCall (WaitableEvent{}.Wait (runFor));
104 }
105 void Demo_Using_Direct_Capture_On_Instrument_ (Set<InstrumentNameType> run, bool oneLineMode, Duration captureInterval)
106 {
107 cout << "Results for each instrument:" << endl;
108 for (Instrument i : SystemPerformance::GetAllInstruments ()) {
109 if (not run.empty ()) {
110 if (not run.Contains (i.instrumentName)) {
111 continue;
112 }
113 }
114 cout << " " << i.instrumentName ().GetPrintName () << endl;
115 Execution::Sleep (captureInterval);
116 MeasurementSet m = i.Capture ();
117 if (m.fMeasurements.empty ()) {
118 cout << " NO DATA" << endl;
119 }
120 else {
121 cout << " Measured-At: " << toDisp_ (m.fMeasuredAt).ToString () << endl;
122 for (const Measurement& mi : m.fMeasurements) {
123 cout << " " << mi.fType.GetPrintName () << ": " << Serialize_ (mi.fValue, oneLineMode) << endl;
124 }
125 }
126 }
127 }
128}
129
130namespace {
131 namespace Demo_Using_Capturer_GetMostRecentMeasurements__Private_ {
132 using namespace Stroika::Frameworks::SystemPerformance;
133
134 struct MyCapturer_ : Capturer {
135 public:
136 Instruments::CPU::Instrument fCPUInstrument;
137 Instruments::Process::Instrument fProcessInstrument;
138
139 MyCapturer_ ()
140 : fProcessInstrument{Instruments::Process::Options{.fRestrictToPIDs = Set<pid_t>{GetCurrentProcessID ()}}}
141 {
142 AddCaptureSet (CaptureSet{30s, {fCPUInstrument, fProcessInstrument}});
143 }
144 };
145 }
146 void Demo_Using_Capturer_GetMostRecentMeasurements_ (const Duration& runFor)
147 {
148 /*
149 * The idea here is that the capturer runs in the thread in the background capturing stuff (on a periodic schedule).
150 * and you can simply grab (ANYTIME) the most recently captured values.
151 *
152 * This also demos using 'MeasurementsAs' so you can see the measurements as a structured result, as opposed to as
153 * a variant value.
154 */
155 using namespace Demo_Using_Capturer_GetMostRecentMeasurements__Private_;
156
157 MyCapturer_ capturer; // initialized threadsafe, but internally synrchonized class
158
159 Time::TimePointSeconds doneAt = Time::GetTickCount () + runFor;
160 unsigned int pass{};
161 cout << "Printing most recent measurements (in loop):" << endl;
162 while (Time::GetTickCount () < doneAt) {
163 auto measurements = capturer.pMostRecentMeasurements (); // capture results on a regular cadence with MyCapturer, and just report the latest stats
164 DateTime now = DateTime::Now ();
165
166 optional<double> runQLength;
167 optional<double> totalCPUUsage;
168 optional<double> totalCPURatio;
169 if (auto om = capturer.fCPUInstrument.MeasurementAs<Instruments::CPU::Info> (measurements)) {
170 runQLength = om->fRunQLength;
171 totalCPUUsage = om->fTotalCPUUsage;
172 totalCPURatio = om->GetTotalCPURatio ();
173 }
174 optional<Duration> thisProcUptime;
175 optional<Duration> thisProcAverageCPUTimeUsed;
176 optional<uint64_t> thisProcWorkingOrResidentSetSize;
177 optional<double> thisProcCombinedIORate;
178 if (auto om = capturer.fProcessInstrument.MeasurementAs<Instruments::Process::Info> (measurements)) {
179 // It might not be found for some instruments (not implemented?)
180 Assert (om->size () <= 1);
181 if (om->size () == 1) {
182 Instruments::Process::ProcessType thisProcess = (*om)[GetCurrentProcessID ()];
183 if (auto o = thisProcess.fProcessStartedAt) {
184 thisProcUptime = now - *o;
185 }
186 thisProcAverageCPUTimeUsed = thisProcess.fAverageCPUTimeUsed;
187 thisProcWorkingOrResidentSetSize = Memory::NullCoalesce (thisProcess.fWorkingSetSize, thisProcess.fResidentMemorySize);
188 thisProcCombinedIORate = thisProcess.fCombinedIOWriteRate;
189 }
190 }
191 using namespace Memory; // for optional operator overloads
192 cout << "\tPass: " << pass << endl;
193 cout << "\t\tSys: " << endl;
194 cout << "\t\t\tRun-Q Length: " << Characters::ToString (runQLength) << endl;
195 cout << "\t\t\tTotal CPU Usage: " << Characters::ToString (totalCPUUsage) << " ("
196 << Characters::ToString (totalCPURatio * 100.0) << "% of computer)" << endl;
197 cout << "\t\tThis Process: " << endl;
198 cout << "\t\t\tUptime: " << Characters::ToString (thisProcUptime) << endl;
199 cout << "\t\t\tAverage CPU Time Used: " << Characters::ToString (thisProcAverageCPUTimeUsed) << endl;
200 cout << "\t\t\tWorking Or Resident-Set Size: " << Characters::ToString (thisProcWorkingOrResidentSetSize) << endl;
201 cout << "\t\t\tCombined IO Rate: " << Characters::ToString (thisProcCombinedIORate) << endl;
202 Execution::Sleep (30s);
203 ++pass;
204 }
205 }
206}
207
208int main (int argc, const char* argv[])
209{
210 CommandLine cmdLine{argc, argv};
211 Debug::TraceContextBumper ctx{"main", "argv={}"_f, cmdLine};
212#if qStroika_Foundation_Common_Platform_POSIX
214#endif
215 using namespace StandardCommandLineOptions;
216 const CommandLine::Option kPrintNamesO_{.fSingleCharName = 'l', .fHelpOptionText = "prints only the instrument names"sv};
217 const CommandLine::Option kMostRecentO_{.fSingleCharName = 'm', .fHelpOptionText = "runs in most-recent-capture-mode"sv};
218 const CommandLine::Option kOneLineModeO_{.fSingleCharName = 'o', .fHelpOptionText = "prints instrument results (with newlines stripped)"sv};
219 const CommandLine::Option kRunInstrumentArg_{.fSingleCharName = 'r',
220 .fSupportsArgument = true,
221 .fRepeatable = true,
222 .fHelpArgName = "RUN-INSTRUMENT"sv,
223 .fHelpOptionText = "runs the given instrument (it can be repeated)"sv};
224 const CommandLine::Option kRunForO_{
225 .fSingleCharName = 't', .fSupportsArgument = true, .fHelpOptionText = "time to run for (if zero run each matching instrument once)"sv};
226 const CommandLine::Option kTimeBetweenCapturesO_{
227 .fSingleCharName = 'c', .fSupportsArgument = true, .fHelpArgName = "NSEC"sv, .fHelpOptionText = "time interval between captures"sv};
228
229 const initializer_list<CommandLine::Option> kAllOptions_ = {
230 kHelp, kPrintNamesO_, kMostRecentO_, kOneLineModeO_, kRunInstrumentArg_, kRunForO_, kTimeBetweenCapturesO_};
231
232 bool printUsage = cmdLine.Has (kHelp);
233 bool mostRecentCaptureMode = cmdLine.Has (kMostRecentO_);
234 bool printNames = cmdLine.Has (kPrintNamesO_);
235 bool oneLineMode = cmdLine.Has (kOneLineModeO_);
236 Time::DurationSeconds runFor = 0s; // default to runfor 0, so we do each once.
237 if (auto o = cmdLine.GetArgument (kRunForO_)) {
238 runFor = Duration{Characters::FloatConversion::ToFloat<Duration::rep> (*o)};
239 }
240 Time::DurationSeconds captureInterval = 15s;
241 if (auto o = cmdLine.GetArgument (kTimeBetweenCapturesO_)) {
242 captureInterval = Duration{Characters::FloatConversion::ToFloat<Duration::rep> (*o)};
243 }
245 cmdLine.GetArguments (kRunInstrumentArg_).Map<Set<InstrumentNameType>> ([] (const String& s) { return InstrumentNameType{s}; });
246
247 if (printUsage) {
248 cerr << cmdLine.GenerateUsage (kAllOptions_);
249 return EXIT_SUCCESS;
250 }
251
252 try {
253 cmdLine.Validate (kAllOptions_);
254 if (printNames) {
255 Demo_PrintInstruments_ ();
256 }
257 else if (mostRecentCaptureMode) {
258 Demo_Using_Capturer_GetMostRecentMeasurements_ (runFor);
259 }
260 else if (runFor > 0s) {
261 Demo_UsingCapturerWithCallbacks_ (run, oneLineMode, captureInterval, runFor);
262 }
263 else {
264 Demo_Using_Direct_Capture_On_Instrument_ (run, oneLineMode, Duration{captureInterval});
265 }
266 }
267 catch (const InvalidCommandLineArgument&) {
268 cerr << "Error encountered: " << Characters::ToString (current_exception ()) << endl;
269 cerr << cmdLine.GenerateUsage (kAllOptions_) << endl;
270 return EXIT_SUCCESS;
271 }
272 catch (...) {
273 String exceptMsg = Characters::ToString (current_exception ());
274 cerr << "Exception - " << exceptMsg.AsNarrowSDKString () << " - terminating..." << endl;
275 return EXIT_FAILURE;
276 }
277
278 return EXIT_SUCCESS;
279}
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:1665
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
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
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:309
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.