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"
22#include "Stroika/Foundation/Streams/MemoryStream.h"
33using namespace Stroika::Foundation::Memory;
36using namespace Stroika::Frameworks;
37using namespace Stroika::Frameworks::SystemPerformance;
50#ifndef qUseWMICollectionSupport_
51#define qUseWMICollectionSupport_ qStroika_Foundation_Common_Platform_Windows
54#if qUseWMICollectionSupport_
60#if qUseWMICollectionSupport_
62 const String kInstanceName_{
"_Total"sv};
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};
69 const String kFreeMem_{
"Free & Zero Page List Bytes"sv};
72 const String kHardwareReserved1_{
"System Driver Resident Bytes"sv};
73 const String kHardwareReserved2_{
"System Driver Total Bytes"sv};
118 template <
typename CONTEXT>
122#if qStroika_Foundation_Common_Platform_Linux
125 uint64_t fSaved_MajorPageFaultsSinceBoot{};
126 uint64_t fSaved_MinorPageFaultsSinceBoot{};
127 uint64_t fSaved_PageOutsSinceBoot{};
131 struct InstrumentRep_Linux_ : InstrumentRepBase_<_Context> {
133 using InstrumentRepBase_<_Context>::InstrumentRepBase_;
138 Read_ProcMemInfo_ (&result);
140 Read_ProcVMStat_ (&result);
143 DbgTrace (
"Ignoring error in Read_ProcVMStat_: {}"_f, current_exception ());
145 _NoteCompletedCapture ();
152#if USE_NOISY_TRACE_IN_THIS_MODULE_
156 if (line.size () >= 3 and line[0] == n) {
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));
170 static const filesystem::path kProcMemInfoFileName_{
"/proc/meminfo"sv};
173 optional<uint64_t> memTotal;
174 optional<uint64_t> slabReclaimable;
175 optional<uint64_t> slab;
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 ());
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);
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);
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;
211 if (not updateResult->fPhysicalMemory.
fAvailable.has_value () and updateResult->fPhysicalMemory.
fFree and
212 updateResult->fPhysicalMemory.
fInactive) {
213 if (not slabReclaimable.has_value ()) {
223#if USE_NOISY_TRACE_IN_THIS_MODULE_
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));
237 static const filesystem::path kProcVMStatFileName_{
"/proc/vmstat"sv};
238 optional<uint64_t> pgfault;
239 optional<uint64_t> pgpgout;
241 unsigned int nFound{};
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]);
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);
255 nFound += readVMStatLine (&pgpgout, kpgpgoutLabel_, line);
268 optional<uint64_t> faultsSinceBoot, optional<double>* faultsPerSecond) {
269 if (faultsSinceBoot) {
270 if (now - savedVMPageStatsAt >= _fOptions.fMinimumAveragingInterval) {
271 *faultsPerSecond = (*faultsSinceBoot - *savedBaseline) / (now - savedVMPageStatsAt).count ();
273 *savedBaseline = *faultsSinceBoot;
276 auto ctxLock = scoped_lock{_fContext};
277 auto ctx = _fContext.rwget ().rwref ();
278 doAve_ (ctx->fSaved_VMPageStats_At, now, &ctx->fSaved_MinorPageFaultsSinceBoot,
280 doAve_ (ctx->fSaved_VMPageStats_At, now, &ctx->fSaved_MajorPageFaultsSinceBoot,
282 doAve_ (ctx->fSaved_VMPageStats_At, now, &ctx->fSaved_PageOutsSinceBoot, updateResult->fPaging.
fPageOutsSinceBoot,
284 ctx->fSaved_VMPageStats_At = now;
291#if qStroika_Foundation_Common_Platform_Windows
294#if qUseWMICollectionSupport_
295 WMICollector fMemoryWMICollector_{
"Memory"sv,
297 {kCommittedBytes_, kCommitLimit_, kHardPageFaultsPerSec_, kPagesOutPerSec_, kFreeMem_,
298 kHardwareReserved1_, kHardwareReserved2_}};
301 struct InstrumentRep_Windows_ : InstrumentRepBase_<_Context> {
303 InstrumentRep_Windows_ (
const Options& options,
const shared_ptr<_Context>& context)
304 : InstrumentRepBase_<_Context>{options, context}
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 ());
317 Read_GlobalMemoryStatusEx_ (&result, &totalRAM);
318#if qUseWMICollectionSupport_
319 Read_WMI_ (&result, totalRAM);
325 Memory::AccumulateIf (&result.fPhysicalMemory.
fInactive, result.fPhysicalMemory.
fOSReserved, std::minus{});
327 _NoteCompletedCapture ();
335 MEMORYSTATUSEX statex{};
336 statex.dwLength =
sizeof (statex);
337 Verify (::GlobalMemoryStatusEx (&statex) != 0);
338 updateResult->fPhysicalMemory.
fFree = statex.ullAvailPhys;
339 *totalRAM = statex.ullTotalPhys;
347 updateResult->fPhysicalMemory.
fActive = statex.ullTotalPhys * statex.dwMemoryLoad / 100;
349#if qUseWMICollectionSupport_
352 auto lock = scoped_lock{_fContext};
353 shared_ptr<_Context> rwContext = _fContext.rwget ().rwref ();
354 rwContext->fMemoryWMICollector_.Collect ();
356 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kCommittedBytes_));
357 Memory::CopyToIf (&updateResult->fVirtualMemory.
fCommitLimit,
358 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kCommitLimit_));
360 rwContext->fMemoryWMICollector_.PeekCurrentValue (kInstanceName_, kHardPageFaultsPerSec_));
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) {
367 updateResult->fPhysicalMemory.
fInactive = totalRAM - *updateResult->fPhysicalMemory.
fActive -
static_cast<uint64_t
> (*freeMem);
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_));
376 updateResult->fPhysicalMemory.
fAvailable = updateResult->fPhysicalMemory.
fFree + updateResult->fPhysicalMemory.
fInactive;
388 struct MemoryInstrumentRep_
389#if qStroika_Foundation_Common_Platform_Linux
390 : InstrumentRep_Linux_
391#elif qStroika_Foundation_Common_Platform_Windows
392 : InstrumentRep_Windows_
394 : InstrumentRepBase_<SystemPerformance::Support::Context>
397#if qStroika_Foundation_Common_Platform_Linux
398 using inherited = InstrumentRep_Linux_;
399#elif qStroika_Foundation_Common_Platform_Windows
400 using inherited = InstrumentRep_Windows_;
402 using inherited = InstrumentRepBase_<SystemPerformance::Support::Context>;
404 MemoryInstrumentRep_ (
const Options& options,
const shared_ptr<_Context>& context = make_shared<_Context> ())
405 : inherited{options, context}
407 Require (_fOptions.fMinimumAveragingInterval > 0s);
419 return Do_Capture_Raw<Info> ([
this] () {
return _InternalCapture (); }, outMeasuredAt);
421 virtual unique_ptr<IRep> Clone ()
const override
423 return make_unique<MemoryInstrumentRep_> (_fOptions, _fContext.load ());
425 nonvirtual
Info _InternalCapture ()
427 AssertExternallySynchronizedMutex::WriteContext declareContext{*
this};
428#if USE_NOISY_TRACE_IN_THIS_MODULE_
431#if qStroika_Foundation_Common_Platform_Linux or qStroika_Foundation_Common_Platform_Windows
432 Info result = inherited::_InternalCapture ();
438 Common::GetSystemConfiguration_Memory ().fTotalPhysicalRAM);
439 if (result.fPhysicalMemory.
fActive and result.fPhysicalMemory.
fInactive and result.fPhysicalMemory.
fFree and
443 Common::GetSystemConfiguration_Memory ().fTotalPhysicalRAM);
457 mapper.
AddClass<Info::PhysicalRAMDetailsType> ({
464 mapper.
AddClass<Info::VirtualMemoryDetailsType> ({
469 mapper.
AddClass<Info::PagingDetailsType> ({
478 {
"Physical-Memory"sv, &Info::fPhysicalMemory},
479 {
"Virtual-Memory"sv, &Info::fVirtualMemory},
480 {
"Paging"sv, &Info::fPaging},
485Instruments::Memory::Instrument::Instrument (
const Options& options)
487 make_unique<MemoryInstrumentRep_> (options),
488 {kMemoryUsageMeasurement_},
490 kObjectVariantMapper}
503 MemoryInstrumentRep_* myCap =
dynamic_cast<MemoryInstrumentRep_*
> (fCaptureRep_.get ());
505 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.