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