Stroika Library 3.0d23x
 
Loading...
Searching...
No Matches
Memory.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include "Stroika/Foundation/Characters/FloatConversion.h"
7#include "Stroika/Foundation/Characters/String2Int.h"
9#include "Stroika/Foundation/Containers/Mapping.h"
10#include "Stroika/Foundation/Containers/Sequence.h"
11#include "Stroika/Foundation/Containers/Set.h"
17#include "Stroika/Foundation/Execution/Exceptions.h"
18#include "Stroika/Foundation/Execution/ProcessRunner.h"
23#include "Stroika/Foundation/Streams/MemoryStream.h"
24
26
27#include "Memory.h"
28
29using namespace Stroika::Foundation;
33using namespace Stroika::Foundation::Execution;
34using namespace Stroika::Foundation::Memory;
36
37using namespace Stroika::Frameworks;
38using namespace Stroika::Frameworks::SystemPerformance;
39
43using Containers::Set;
44
47
48// Comment this in to turn on aggressive noisy DbgTrace in this module
49//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
50
51#ifndef qUseWMICollectionSupport_
52#define qUseWMICollectionSupport_ qStroika_Foundation_Common_Platform_Windows
53#endif
54
55#if qUseWMICollectionSupport_
57
59#endif
60
61#if qUseWMICollectionSupport_
62namespace {
63 const String kInstanceName_{"_Total"sv};
64
65 const String kCommittedBytes_{"Committed Bytes"sv};
66 const String kCommitLimit_{"Commit Limit"sv};
67 const String kHardPageFaultsPerSec_{"Pages/sec"sv};
68 const String kPagesOutPerSec_{"Pages Output/sec"sv};
69
70 const String kFreeMem_{"Free & Zero Page List Bytes"sv};
71
72 // Something of an empirical WAG (kHardwareReserved*) but not super important to get right -- LGP 2015-09-24
73 const String kHardwareReserved1_{"System Driver Resident Bytes"sv};
74 const String kHardwareReserved2_{"System Driver Total Bytes"sv};
75}
76#endif
77
78/*
79 ********************************************************************************
80 ************ Instruments::Memory::Info::PhysicalRAMDetailsType *****************
81 ********************************************************************************
82 */
87
88/*
89 ********************************************************************************
90 ************ Instruments::Memory::Info::VirtualMemoryDetailsType ***************
91 ********************************************************************************
92 */
97
98/*
99 ********************************************************************************
100 **************** Instruments::Memory::Info::PagingDetailsType ******************
101 ********************************************************************************
102 */
107
108/*
109 ********************************************************************************
110 ********************** Instruments::Memory::Info *******************************
111 ********************************************************************************
112 */
117
118namespace {
119 template <typename CONTEXT>
121}
122
123#if qStroika_Foundation_Common_Platform_Linux
124namespace {
125 struct _Context : SystemPerformance::Support::Context {
126 uint64_t fSaved_MajorPageFaultsSinceBoot{};
127 uint64_t fSaved_MinorPageFaultsSinceBoot{};
128 uint64_t fSaved_PageOutsSinceBoot{};
129 Time::TimePointSeconds fSaved_VMPageStats_At{};
130 };
131
132 struct InstrumentRep_Linux_ : InstrumentRepBase_<_Context> {
133
134 using InstrumentRepBase_<_Context>::InstrumentRepBase_;
135
136 Instruments::Memory::Info _InternalCapture ()
137 {
139 Read_ProcMemInfo_ (&result);
140 try {
141 Read_ProcVMStat_ (&result);
142 }
143 catch (...) {
144 DbgTrace ("Ignoring error in Read_ProcVMStat_: {}"_f, current_exception ());
145 }
146 _NoteCompletedCapture ();
147 return result;
148 }
149
150 private:
151 static void Read_ProcMemInfo_ (Instruments::Memory::Info* updateResult)
152 {
153#if USE_NOISY_TRACE_IN_THIS_MODULE_
154 Debug::TraceContextBumper ctx{"Read_ProcMemInfo_"};
155#endif
156 auto readMemInfoLine = [] (optional<uint64_t>* result, const String& n, const Sequence<String>& line) {
157 if (line.size () >= 3 and line[0] == n) {
158 String unit = line[2];
159 double factor = (unit == L"kB") ? 1024 : 1;
160 *result = Math::Round<uint64_t> (Characters::FloatConversion::ToFloat<double> (line[1]) * factor);
161#if USE_NOISY_TRACE_IN_THIS_MODULE_
162 DbgTrace (L"Set %s = %ld", n.c_str (), static_cast<long> (**result));
163#endif
164 }
165 };
166 /*
167 * @todo minor performance note: we current do about 10 (tha many strings * 45 (about that many lines in file) compares.
168 * We couuld read data and form a map so lookups faster. Or at least keep a list of items alreayd found and not
169 * look for them more, and stop when none left to look for (wont work if some like sreclaimable not found).
170 */
171 static const filesystem::path kProcMemInfoFileName_{"/proc/meminfo"sv};
173 // Note - /procfs files always unseekable
174 optional<uint64_t> memTotal;
175 optional<uint64_t> slabReclaimable;
176 optional<uint64_t> slab; // older kernels don't have slabReclaimable
177 for (const Sequence<String>& line :
178 reader.ReadMatrix (IO::FileSystem::FileInputStream::New (kProcMemInfoFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
179#if USE_NOISY_TRACE_IN_THIS_MODULE_
180 DbgTrace (L"***in Instruments::Memory::Info capture_ linesize=%d, line[0]=%s", line.size (), line.empty () ? L"" : line[0].c_str ());
181#endif
182 static const String kMemTotalLabel_{"MemTotal"sv};
183 static const String kMemFreelLabel_{"MemFree"sv};
184 static const String kMemAvailableLabel_{"MemAvailable"sv};
185 static const String kActiveLabel_{"Active"sv};
186 static const String kInactiveLabel_{"Inactive"sv};
187 static const String kCommitLimitLabel_{"CommitLimit"sv};
188 static const String kCommitted_ASLabel_{"Committed_AS"sv};
189 static const String kSwapTotalLabel_{"SwapTotal"sv};
190 static const String kSReclaimableLabel_{"SReclaimable"sv};
191 static const String kSlabLabel_{"Slab"sv};
192 readMemInfoLine (&memTotal, kMemTotalLabel_, line);
193 readMemInfoLine (&updateResult->fPhysicalMemory.fFree, kMemFreelLabel_, line);
194 readMemInfoLine (&updateResult->fPhysicalMemory.fAvailable, kMemAvailableLabel_, line);
195 readMemInfoLine (&updateResult->fPhysicalMemory.fActive, kActiveLabel_, line);
196 readMemInfoLine (&updateResult->fPhysicalMemory.fInactive, kInactiveLabel_, line);
197 readMemInfoLine (&updateResult->fVirtualMemory.fCommitLimit, kCommitLimitLabel_, line);
198 /*
199 * From docs on https://github.com/torvalds/linux/blob/master/Documentation/filesystems/proc.txt about
200 * Commited_AS - its unclear if this is the best measure of commited bytes.
201 */
202 readMemInfoLine (&updateResult->fVirtualMemory.fCommittedBytes, kCommitted_ASLabel_, line);
203 readMemInfoLine (&updateResult->fVirtualMemory.fPagefileTotalSize, kSwapTotalLabel_, line);
204 readMemInfoLine (&slabReclaimable, kSReclaimableLabel_, line);
205 readMemInfoLine (&slab, kSlabLabel_, line);
206 }
207 if (memTotal and updateResult->fPhysicalMemory.fFree and updateResult->fPhysicalMemory.fInactive and
208 updateResult->fPhysicalMemory.fActive) {
209 updateResult->fPhysicalMemory.fOSReserved = *memTotal - *updateResult->fPhysicalMemory.fFree -
210 *updateResult->fPhysicalMemory.fInactive - *updateResult->fPhysicalMemory.fActive;
211 }
212 if (not updateResult->fPhysicalMemory.fAvailable.has_value () and updateResult->fPhysicalMemory.fFree and
213 updateResult->fPhysicalMemory.fInactive) {
214 if (not slabReclaimable.has_value ()) {
215 // wag
216 slabReclaimable = NullCoalesce (slab) / 2;
217 }
218 updateResult->fPhysicalMemory.fAvailable =
219 *updateResult->fPhysicalMemory.fFree + *updateResult->fPhysicalMemory.fInactive + NullCoalesce (slabReclaimable);
220 }
221 }
222 nonvirtual void Read_ProcVMStat_ (Instruments::Memory::Info* updateResult)
223 {
224#if USE_NOISY_TRACE_IN_THIS_MODULE_
225 Debug::TraceContextBumper ctx{"Read_ProcVMStat_"};
226#endif
227 auto readVMStatLine = [] (optional<uint64_t>* result, const String& n, const Sequence<String>& line) -> unsigned int {
228 if (line.size () >= 2 and line[0] == n) {
229 *result = Characters::String2Int<uint64_t> (line[1]);
230#if USE_NOISY_TRACE_IN_THIS_MODULE_
231 DbgTrace (L"Set %s = %ld", n.c_str (), static_cast<long> (**result));
232#endif
233 return 1;
234 }
235 return 0;
236 };
237 {
238 static const filesystem::path kProcVMStatFileName_{"/proc/vmstat"sv};
239 optional<uint64_t> pgfault;
240 optional<uint64_t> pgpgout;
241 {
242 unsigned int nFound{};
243 // Note - /procfs files always unseekable
245 for (const Sequence<String>& line : reader.ReadMatrix (
246 IO::FileSystem::FileInputStream::New (kProcVMStatFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
247#if USE_NOISY_TRACE_IN_THIS_MODULE_
248 DbgTrace ("in Instruments::Memory::Info capture_ linesize={}, line[0]={}", line.size (), line.empty () ? ""_k : line[0]);
249#endif
250 static const String kpgfaultLabel_{"pgfault"sv};
251 static const String kpgpgoutLabel_{"pgpgout"sv};
252 static const String kpgmajfaultLabel_{"pgmajfault"sv};
253 nFound += readVMStatLine (&pgfault, kpgfaultLabel_, line);
254 // Unsure if this should be pgpgout or pgpgout, or none of the above. On a system with no swap, I seem to get both happening,
255 // which makes no sense
256 nFound += readVMStatLine (&pgpgout, kpgpgoutLabel_, line); // tried pgpgout but I don't know what it is but doesn't appear to be pages out - noneof this well documented
257 nFound += readVMStatLine (&updateResult->fPaging.fMajorPageFaultsSinceBoot, kpgmajfaultLabel_, line);
258 if (nFound >= 3) {
259 break; // avoid reading rest if all found
260 }
261 }
262 }
263 Time::TimePointSeconds now = Time::GetTickCount ();
264 updateResult->fPaging.fPageOutsSinceBoot = pgpgout;
265 if (pgfault and updateResult->fPaging.fMajorPageFaultsSinceBoot) {
266 updateResult->fPaging.fMinorPageFaultsSinceBoot = *pgfault - *updateResult->fPaging.fMajorPageFaultsSinceBoot;
267 }
268 auto doAve_ = [this] (Time::TimePointSeconds savedVMPageStatsAt, Time::TimePointSeconds now, uint64_t* savedBaseline,
269 optional<uint64_t> faultsSinceBoot, optional<double>* faultsPerSecond) {
270 if (faultsSinceBoot) {
271 if (now - savedVMPageStatsAt >= _fOptions.fMinimumAveragingInterval) {
272 *faultsPerSecond = (*faultsSinceBoot - *savedBaseline) / (now - savedVMPageStatsAt).count ();
273 }
274 *savedBaseline = *faultsSinceBoot;
275 }
276 };
277 auto ctxLock = scoped_lock{_fContext};
278 auto ctx = _fContext.rwget ().rwref ();
279 doAve_ (ctx->fSaved_VMPageStats_At, now, &ctx->fSaved_MinorPageFaultsSinceBoot,
280 updateResult->fPaging.fMinorPageFaultsSinceBoot, &updateResult->fPaging.fMinorPageFaultsPerSecond);
281 doAve_ (ctx->fSaved_VMPageStats_At, now, &ctx->fSaved_MajorPageFaultsSinceBoot,
282 updateResult->fPaging.fMajorPageFaultsSinceBoot, &updateResult->fPaging.fMajorPageFaultsPerSecond);
283 doAve_ (ctx->fSaved_VMPageStats_At, now, &ctx->fSaved_PageOutsSinceBoot, updateResult->fPaging.fPageOutsSinceBoot,
284 &updateResult->fPaging.fPageOutsPerSecond);
285 ctx->fSaved_VMPageStats_At = now;
286 }
287 }
288 };
289}
290#endif
291
292#if qStroika_Foundation_Common_Platform_Windows
293namespace {
294 struct _Context : SystemPerformance::Support::Context {
295#if qUseWMICollectionSupport_
296 WMICollector fMemoryWMICollector_{"Memory"sv,
297 {kInstanceName_},
298 {kCommittedBytes_, kCommitLimit_, kHardPageFaultsPerSec_, kPagesOutPerSec_, kFreeMem_,
299 kHardwareReserved1_, kHardwareReserved2_}};
300#endif
301 };
302 struct InstrumentRep_Windows_ : InstrumentRepBase_<_Context> {
303
304 InstrumentRep_Windows_ (const Options& options, const shared_ptr<_Context>& context)
305 : InstrumentRepBase_<_Context>{options, context}
306 {
307#if USE_NOISY_TRACE_IN_THIS_MODULE_
308 for (const String& i : _fContext.cget ().cref ()->fMemoryWMICollector_.GetAvailableCounters ()) {
309 DbgTrace (L"Memory:Countername: %s", i.c_str ());
310 }
311#endif
312 }
313
314 nonvirtual Instruments::Memory::Info _InternalCapture ()
315 {
317 uint64_t totalRAM{};
318 Read_GlobalMemoryStatusEx_ (&result, &totalRAM);
319#if qUseWMICollectionSupport_
320 Read_WMI_ (&result, totalRAM);
321#endif
322 // I've found no docs to clearly state one way or another, but empirically from looking at the graph in
323 // Resource Monitor, the amount reported as 'hardware' - which I'm thinking is roughly 'osreserved' is
324 // subtracted from 'standby'.
325 if (result.fPhysicalMemory.fOSReserved) {
326 Memory::AccumulateIf (&result.fPhysicalMemory.fInactive, result.fPhysicalMemory.fOSReserved, std::minus{});
327 }
328 _NoteCompletedCapture ();
329 return result;
330 }
331
332 private:
333 static void Read_GlobalMemoryStatusEx_ (Instruments::Memory::Info* updateResult, uint64_t* totalRAM)
334 {
335 RequireNotNull (totalRAM);
336 MEMORYSTATUSEX statex{};
337 statex.dwLength = sizeof (statex);
338 Verify (::GlobalMemoryStatusEx (&statex) != 0);
339 updateResult->fPhysicalMemory.fFree = statex.ullAvailPhys; // overridden later, but a good first estimate if we don't use WMI
340 *totalRAM = statex.ullTotalPhys;
341 updateResult->fVirtualMemory.fPagefileTotalSize = statex.ullTotalPageFile;
342
343 /*
344 * dwMemoryLoad
345 * A number between 0 and 100 that specifies the approximate percentage of physical
346 * memory that is in use (0 indicates no memory use and 100 indicates full memory use)
347 */
348 updateResult->fPhysicalMemory.fActive = statex.ullTotalPhys * statex.dwMemoryLoad / 100;
349 }
350#if qUseWMICollectionSupport_
351 nonvirtual void Read_WMI_ (Instruments::Memory::Info* updateResult, uint64_t totalRAM)
352 {
353 auto lock = scoped_lock{_fContext};
354 shared_ptr<_Context> rwContext = _fContext.rwget ().rwref ();
355 rwContext->fMemoryWMICollector_.Collect ();
356 Memory::CopyToIf (&updateResult->fVirtualMemory.fCommittedBytes,
357 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kCommittedBytes_));
358 Memory::CopyToIf (&updateResult->fVirtualMemory.fCommitLimit,
359 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kCommitLimit_));
360 Memory::CopyToIf (&updateResult->fPaging.fMajorPageFaultsPerSecond,
361 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kHardPageFaultsPerSec_));
362 Memory::CopyToIf (&updateResult->fPaging.fPageOutsPerSecond,
363 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kPagesOutPerSec_));
364 Memory::CopyToIf (&updateResult->fPhysicalMemory.fFree, rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kFreeMem_));
365 if (optional<double> freeMem = rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kFreeMem_)) {
366 if (updateResult->fPhysicalMemory.fActive) {
367 // Active + Inactive + Free == TotalRAM
368 updateResult->fPhysicalMemory.fInactive = totalRAM - *updateResult->fPhysicalMemory.fActive - static_cast<uint64_t> (*freeMem);
369 }
370 }
371 updateResult->fPhysicalMemory.fOSReserved = nullopt;
372 Memory::AccumulateIf (&updateResult->fPhysicalMemory.fOSReserved,
373 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kHardwareReserved1_));
374 Memory::AccumulateIf (&updateResult->fPhysicalMemory.fOSReserved,
375 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kHardwareReserved2_));
376 // fPhysicalMemory.fAvailable WAG TMPHACK - probably should add "hardware in use" memory + private WS of each process + shared memory "WS" - but not easy to compute...
377 updateResult->fPhysicalMemory.fAvailable = updateResult->fPhysicalMemory.fFree + updateResult->fPhysicalMemory.fInactive;
378 }
379#endif
380 };
381}
382#endif
383
384namespace {
385 const MeasurementType kMemoryUsageMeasurement_ = MeasurementType{"Memory-Usage"sv};
386}
387
388namespace {
389 struct MemoryInstrumentRep_
390#if qStroika_Foundation_Common_Platform_Linux
391 : InstrumentRep_Linux_
392#elif qStroika_Foundation_Common_Platform_Windows
393 : InstrumentRep_Windows_
394#else
395 : InstrumentRepBase_<SystemPerformance::Support::Context>
396#endif
397 {
398#if qStroika_Foundation_Common_Platform_Linux
399 using inherited = InstrumentRep_Linux_;
400#elif qStroika_Foundation_Common_Platform_Windows
401 using inherited = InstrumentRep_Windows_;
402#else
403 using inherited = InstrumentRepBase_<SystemPerformance::Support::Context>;
404#endif
405 MemoryInstrumentRep_ (const Options& options, const shared_ptr<_Context>& context = Memory::MakeSharedPtr<_Context> ())
406 : inherited{options, context}
407 {
408 Require (_fOptions.fMinimumAveragingInterval > 0s);
409 }
410 virtual MeasurementSet Capture () override
411 {
412 Debug::TraceContextBumper ctx{"SystemPerformance::Instrument...Memory...MemoryInstrumentRep_::Capture ()"};
413 MeasurementSet results;
414 results.fMeasurements.Add (Measurement{
415 kMemoryUsageMeasurement_, Instruments::Memory::Instrument::kObjectVariantMapper.FromObject (Capture_Raw (&results.fMeasuredAt))});
416 return results;
417 }
418 nonvirtual Info Capture_Raw (Range<TimePointSeconds>* outMeasuredAt)
419 {
420 return Do_Capture_Raw<Info> ([this] () { return _InternalCapture (); }, outMeasuredAt);
421 }
422 virtual unique_ptr<IRep> Clone () const override
423 {
424 return make_unique<MemoryInstrumentRep_> (_fOptions, _fContext.load ());
425 }
426 nonvirtual Info _InternalCapture ()
427 {
428 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
429#if USE_NOISY_TRACE_IN_THIS_MODULE_
430 Debug::TraceContextBumper ctx{"Instruments::Memory::Info _InternalCapture"};
431#endif
432#if qStroika_Foundation_Common_Platform_Linux or qStroika_Foundation_Common_Platform_Windows
433 Info result = inherited::_InternalCapture ();
434#else
435 Info result;
436#endif
437 Ensure (NullCoalesce (result.fPhysicalMemory.fActive) + NullCoalesce (result.fPhysicalMemory.fInactive) +
438 NullCoalesce (result.fPhysicalMemory.fFree) + NullCoalesce (result.fPhysicalMemory.fOSReserved) <=
439 Common::GetSystemConfiguration_Memory ().fTotalPhysicalRAM);
440 if (result.fPhysicalMemory.fActive and result.fPhysicalMemory.fInactive and result.fPhysicalMemory.fFree and
441 result.fPhysicalMemory.fOSReserved) {
442 Ensure (NullCoalesce (result.fPhysicalMemory.fActive) + NullCoalesce (result.fPhysicalMemory.fInactive) +
443 NullCoalesce (result.fPhysicalMemory.fFree) + NullCoalesce (result.fPhysicalMemory.fOSReserved) ==
444 Common::GetSystemConfiguration_Memory ().fTotalPhysicalRAM);
445 }
446 return result;
447 }
448 };
449}
450
451/*
452 ********************************************************************************
453 ********************** Instruments::Memory::Instrument *************************
454 ********************************************************************************
455 */
457 ObjectVariantMapper mapper;
458 mapper.AddClass<Info::PhysicalRAMDetailsType> ({
464 });
465 mapper.AddClass<Info::VirtualMemoryDetailsType> ({
468 {"Pagefile-Total-Size"sv, &Info::VirtualMemoryDetailsType::fPagefileTotalSize},
469 });
470 mapper.AddClass<Info::PagingDetailsType> ({
471 {"Major-Faults-Since-Boot"sv, &Info::PagingDetailsType::fMajorPageFaultsSinceBoot},
472 {"Minor-Faults-Since-Boot"sv, &Info::PagingDetailsType::fMinorPageFaultsSinceBoot},
473 {"Page-Outs-Since-Boot"sv, &Info::PagingDetailsType::fPageOutsSinceBoot},
474 {"Major-Faults-Per-Second"sv, &Info::PagingDetailsType::fMajorPageFaultsPerSecond},
475 {"Minor-Faults-Per-Second"sv, &Info::PagingDetailsType::fMinorPageFaultsPerSecond},
476 {"Page-Outs-Per-Second"sv, &Info::PagingDetailsType::fPageOutsPerSecond},
477 });
478 mapper.AddClass<Info> ({
479 {"Physical-Memory"sv, &Info::fPhysicalMemory},
480 {"Virtual-Memory"sv, &Info::fVirtualMemory},
481 {"Paging"sv, &Info::fPaging},
482 });
483 return mapper;
484}();
485
486Instruments::Memory::Instrument::Instrument (const Options& options)
487 : SystemPerformance::Instrument{InstrumentNameType{"Memory"sv},
488 make_unique<MemoryInstrumentRep_> (options),
489 {kMemoryUsageMeasurement_},
490 {KeyValuePair<type_index, MeasurementType>{typeid (Info), kMemoryUsageMeasurement_}},
491 kObjectVariantMapper}
492{
493}
494
495/*
496 ********************************************************************************
497 ********* SystemPerformance::Instrument::CaptureOneMeasurement *****************
498 ********************************************************************************
499 */
500template <>
502{
503 Debug::TraceContextBumper ctx{"SystemPerformance::Instrument::CaptureOneMeasurement<Memory::Info>"};
504 MemoryInstrumentRep_* myCap = dynamic_cast<MemoryInstrumentRep_*> (fCaptureRep_.get ());
505 AssertNotNull (myCap);
506 return myCap->Capture_Raw (measurementTimeOut);
507}
#define AssertNotNull(p)
Definition Assertions.h:333
#define RequireNotNull(p)
Definition Assertions.h:347
#define Verify(c)
Definition Assertions.h:419
wstring Capture(const Options &options={})
Definition BackTrace.cpp:46
const OT & NullCoalesce(const OT &l, const OT &r)
return one of l, or r, with first preference for which is engaged, and second preference for left-to-...
Definition Optional.inl:134
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
#define DbgTrace
Definition Trace.h:309
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
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.
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...
nonvirtual void AddClass(const Traversal::Iterable< StructFieldInfo > &fieldDescriptions, const ClassMapperOptions< CLASS > &mapperOptions={})
nonvirtual VariantValue FromObject(const T &from) const
This COULD be easily used to read CSV files, or tab-delimited files, for example.
nonvirtual String WriteAsString(const VariantValue &v) const
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)
Create a format-string (see std::wformat_string or Stroika FormatString, or python 'f' strings.