4#include "Stroika/Frameworks/StroikaPreComp.h"
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"
34using namespace Stroika::Foundation::Memory;
37using namespace Stroika::Frameworks;
38using namespace Stroika::Frameworks::SystemPerformance;
51#ifndef qUseWMICollectionSupport_
52#define qUseWMICollectionSupport_ qStroika_Foundation_Common_Platform_Windows
55#if qUseWMICollectionSupport_
61#if qUseWMICollectionSupport_
63 const String kInstanceName_{
"_Total"sv};
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};
70 const String kFreeMem_{
"Free & Zero Page List Bytes"sv};
73 const String kHardwareReserved1_{
"System Driver Resident Bytes"sv};
74 const String kHardwareReserved2_{
"System Driver Total Bytes"sv};
119 template <
typename CONTEXT>
123#if qStroika_Foundation_Common_Platform_Linux
126 uint64_t fSaved_MajorPageFaultsSinceBoot{};
127 uint64_t fSaved_MinorPageFaultsSinceBoot{};
128 uint64_t fSaved_PageOutsSinceBoot{};
132 struct InstrumentRep_Linux_ : InstrumentRepBase_<_Context> {
134 using InstrumentRepBase_<_Context>::InstrumentRepBase_;
139 Read_ProcMemInfo_ (&result);
141 Read_ProcVMStat_ (&result);
144 DbgTrace (
"Ignoring error in Read_ProcVMStat_: {}"_f, current_exception ());
146 _NoteCompletedCapture ();
153#if USE_NOISY_TRACE_IN_THIS_MODULE_
157 if (line.size () >= 3 and line[0] == n) {
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));
171 static const filesystem::path kProcMemInfoFileName_{
"/proc/meminfo"sv};
174 optional<uint64_t> memTotal;
175 optional<uint64_t> slabReclaimable;
176 optional<uint64_t> slab;
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 ());
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);
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);
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;
212 if (not updateResult->fPhysicalMemory.
fAvailable.has_value () and updateResult->fPhysicalMemory.
fFree and
213 updateResult->fPhysicalMemory.
fInactive) {
214 if (not slabReclaimable.has_value ()) {
224#if USE_NOISY_TRACE_IN_THIS_MODULE_
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));
238 static const filesystem::path kProcVMStatFileName_{
"/proc/vmstat"sv};
239 optional<uint64_t> pgfault;
240 optional<uint64_t> pgpgout;
242 unsigned int nFound{};
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]);
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);
256 nFound += readVMStatLine (&pgpgout, kpgpgoutLabel_, line);
269 optional<uint64_t> faultsSinceBoot, optional<double>* faultsPerSecond) {
270 if (faultsSinceBoot) {
271 if (now - savedVMPageStatsAt >= _fOptions.fMinimumAveragingInterval) {
272 *faultsPerSecond = (*faultsSinceBoot - *savedBaseline) / (now - savedVMPageStatsAt).count ();
274 *savedBaseline = *faultsSinceBoot;
277 auto ctxLock = scoped_lock{_fContext};
278 auto ctx = _fContext.rwget ().rwref ();
279 doAve_ (ctx->fSaved_VMPageStats_At, now, &ctx->fSaved_MinorPageFaultsSinceBoot,
281 doAve_ (ctx->fSaved_VMPageStats_At, now, &ctx->fSaved_MajorPageFaultsSinceBoot,
283 doAve_ (ctx->fSaved_VMPageStats_At, now, &ctx->fSaved_PageOutsSinceBoot, updateResult->fPaging.
fPageOutsSinceBoot,
285 ctx->fSaved_VMPageStats_At = now;
292#if qStroika_Foundation_Common_Platform_Windows
295#if qUseWMICollectionSupport_
296 WMICollector fMemoryWMICollector_{
"Memory"sv,
298 {kCommittedBytes_, kCommitLimit_, kHardPageFaultsPerSec_, kPagesOutPerSec_, kFreeMem_,
299 kHardwareReserved1_, kHardwareReserved2_}};
302 struct InstrumentRep_Windows_ : InstrumentRepBase_<_Context> {
304 InstrumentRep_Windows_ (
const Options& options,
const shared_ptr<_Context>& context)
305 : InstrumentRepBase_<_Context>{options, context}
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 ());
318 Read_GlobalMemoryStatusEx_ (&result, &totalRAM);
319#if qUseWMICollectionSupport_
320 Read_WMI_ (&result, totalRAM);
326 Memory::AccumulateIf (&result.fPhysicalMemory.
fInactive, result.fPhysicalMemory.
fOSReserved, std::minus{});
328 _NoteCompletedCapture ();
336 MEMORYSTATUSEX statex{};
337 statex.dwLength =
sizeof (statex);
338 Verify (::GlobalMemoryStatusEx (&statex) != 0);
339 updateResult->fPhysicalMemory.
fFree = statex.ullAvailPhys;
340 *totalRAM = statex.ullTotalPhys;
348 updateResult->fPhysicalMemory.
fActive = statex.ullTotalPhys * statex.dwMemoryLoad / 100;
350#if qUseWMICollectionSupport_
353 auto lock = scoped_lock{_fContext};
354 shared_ptr<_Context> rwContext = _fContext.rwget ().rwref ();
355 rwContext->fMemoryWMICollector_.Collect ();
357 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kCommittedBytes_));
358 Memory::CopyToIf (&updateResult->fVirtualMemory.
fCommitLimit,
359 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kCommitLimit_));
361 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kHardPageFaultsPerSec_));
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) {
368 updateResult->fPhysicalMemory.
fInactive = totalRAM - *updateResult->fPhysicalMemory.
fActive -
static_cast<uint64_t
> (*freeMem);
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_));
377 updateResult->fPhysicalMemory.
fAvailable = updateResult->fPhysicalMemory.
fFree + updateResult->fPhysicalMemory.
fInactive;
389 struct MemoryInstrumentRep_
390#if qStroika_Foundation_Common_Platform_Linux
391 : InstrumentRep_Linux_
392#elif qStroika_Foundation_Common_Platform_Windows
393 : InstrumentRep_Windows_
395 : InstrumentRepBase_<SystemPerformance::Support::Context>
398#if qStroika_Foundation_Common_Platform_Linux
399 using inherited = InstrumentRep_Linux_;
400#elif qStroika_Foundation_Common_Platform_Windows
401 using inherited = InstrumentRep_Windows_;
403 using inherited = InstrumentRepBase_<SystemPerformance::Support::Context>;
405 MemoryInstrumentRep_ (
const Options& options,
const shared_ptr<_Context>& context = Memory::MakeSharedPtr<_Context> ())
406 : inherited{options, context}
408 Require (_fOptions.fMinimumAveragingInterval > 0s);
420 return Do_Capture_Raw<Info> ([
this] () {
return _InternalCapture (); }, outMeasuredAt);
422 virtual unique_ptr<IRep> Clone ()
const override
424 return make_unique<MemoryInstrumentRep_> (_fOptions, _fContext.load ());
426 nonvirtual
Info _InternalCapture ()
428 AssertExternallySynchronizedMutex::WriteContext declareContext{*
this};
429#if USE_NOISY_TRACE_IN_THIS_MODULE_
432#if qStroika_Foundation_Common_Platform_Linux or qStroika_Foundation_Common_Platform_Windows
433 Info result = inherited::_InternalCapture ();
439 Common::GetSystemConfiguration_Memory ().fTotalPhysicalRAM);
440 if (result.fPhysicalMemory.
fActive and result.fPhysicalMemory.
fInactive and result.fPhysicalMemory.
fFree and
444 Common::GetSystemConfiguration_Memory ().fTotalPhysicalRAM);
458 mapper.
AddClass<Info::PhysicalRAMDetailsType> ({
465 mapper.
AddClass<Info::VirtualMemoryDetailsType> ({
470 mapper.
AddClass<Info::PagingDetailsType> ({
479 {
"Physical-Memory"sv, &Info::fPhysicalMemory},
480 {
"Virtual-Memory"sv, &Info::fVirtualMemory},
481 {
"Paging"sv, &Info::fPaging},
486Instruments::Memory::Instrument::Instrument (
const Options& options)
488 make_unique<MemoryInstrumentRep_> (options),
489 {kMemoryUsageMeasurement_},
491 kObjectVariantMapper}
504 MemoryInstrumentRep_* myCap =
dynamic_cast<MemoryInstrumentRep_*
> (fCaptureRep_.get ());
506 return myCap->Capture_Raw (measurementTimeOut);
#define RequireNotNull(p)
wstring Capture(const Options &options={})
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-...
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
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...
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
Create a format-string (see std::wformat_string or Stroika FormatString, or python 'f' strings.