Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
Frameworks/SystemPerformance/Instruments/Filesystem.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <filesystem>
7#include <functional>
8
9#if qStroika_Foundation_Common_Platform_POSIX
10#include <sys/stat.h>
11#include <sys/statvfs.h>
12#include <sys/types.h>
13#include <unistd.h>
14#elif qStroika_Foundation_Common_Platform_Windows
15#include <Windows.h>
16#include <winioctl.h>
17#endif
18
19#if qStroika_Foundation_Common_Platform_Linux
20#include <sys/sysmacros.h>
21#endif
22
24#include "Stroika/Foundation/Characters/FloatConversion.h"
25#include "Stroika/Foundation/Characters/String2Int.h"
27#include "Stroika/Foundation/Containers/Mapping.h"
28#include "Stroika/Foundation/Containers/Sequence.h"
29#include "Stroika/Foundation/Containers/Set.h"
34#include "Stroika/Foundation/Execution/Exceptions.h"
35#include "Stroika/Foundation/Execution/ProcessRunner.h"
42#include "Stroika/Foundation/Streams/MemoryStream.h"
43
45
46#include "Filesystem.h"
47
48using namespace Stroika::Foundation;
52using namespace Stroika::Foundation::Execution;
53using namespace Stroika::Foundation::Memory;
54using namespace Stroika::Foundation::Streams;
55
56using namespace Stroika::Frameworks;
57using namespace Stroika::Frameworks::SystemPerformance;
58
59using Characters::String2Int;
60
61using Instruments::Filesystem::BlockDeviceKind;
63using Instruments::Filesystem::DynamicDiskIDType;
67using Instruments::Filesystem::MountedFilesystemNameType;
69
70// Comment this in to turn on aggressive noisy DbgTrace in this module
71//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
72
73/*
74 ********************************************************************************
75 ********* Instruments::Filesystem::IOStatsType::EstimatedPercentInUse **********
76 ********************************************************************************
77 */
78optional<double> IOStatsType::EstimatedPercentInUse () const
79{
80 if (fInUsePercent) {
81 return fInUsePercent;
82 }
83 // %InUse = QL / (1 + QL)
84 if (fQLength) {
85 double QL = *fQLength;
86 Require (0 <= QL);
87 double pct{100.0 * (QL / (1 + QL))};
88 Require (0 <= pct and pct <= 100.0);
89 return pct;
90 }
91 return nullopt;
92}
93
94// for io stats
95#ifndef qUseWMICollectionSupport_
96#define qUseWMICollectionSupport_ qStroika_Foundation_Common_Platform_Windows
97#endif
98
99#if qUseWMICollectionSupport_
101
103#endif
104
105/*
106 ********************************************************************************
107 ***************** Instruments::Filesystem::MountedFilesystemInfoType ***********
108 ********************************************************************************
109 */
114
115/*
116 ********************************************************************************
117 ********************* Instruments::Filesystem::IOStatsType *********************
118 ********************************************************************************
119 */
124
125/*
126 ********************************************************************************
127 ******************** Instruments::Filesystem::DiskInfoType *********************
128 ********************************************************************************
129 */
134
135/*
136 ********************************************************************************
137 ********************** Instruments::Filesystem::Info ***************************
138 ********************************************************************************
139 */
144
145namespace {
146#if qUseWMICollectionSupport_
147 const String kDiskReadBytesPerSec_{"Disk Read Bytes/sec"sv};
148 const String kDiskWriteBytesPerSec_{"Disk Write Bytes/sec"sv};
149 const String kDiskReadsPerSec_{"Disk Reads/sec"sv};
150 const String kDiskWritesPerSec_{"Disk Writes/sec"sv};
151 const String kPctDiskReadTime_{"% Disk Read Time"sv};
152 const String kPctDiskWriteTime_{"% Disk Write Time"sv};
153 const String kAveDiskReadQLen_{"Avg. Disk Read Queue Length"sv};
154 const String kAveDiskWriteQLen_{"Avg. Disk Write Queue Length"sv};
155 const String kPctIdleTime_{"% Idle Time"sv};
156
157 constexpr bool kUseDiskPercentReadTime_ElseAveQLen_ToComputeQLen_{false};
158 /*
159 * No logical reason todo this. Its probably masking a real bug. But empirically it produces
160 * values closer to those reported by Windows Task Mgr.
161 * -- LGP 2015-07-20
162 */
163 constexpr bool kUsePctIdleIimeForAveQLen_{true};
164#endif
165}
166
167namespace {
168 template <typename CONTEXT>
170}
171
172#if qStroika_Foundation_Common_Platform_POSIX
173namespace {
175 {
176 RequireNotNull (volumes);
177 static const Set<String> kRealDiskFS{
178 "ext2"sv, "ext3"sv, "ext4"sv, "xfs"sv, "jfs2"sv,
179 };
180 static const Set<String> kSysFSList_{
181 "autofs"sv, "binfmt_misc"sv, "cgroup"sv, "configfs"sv, "debugfs"sv, "devpts"sv,
182 "devtmpfs"sv, "fusectl"sv, "fuse.gvfsd-fuse"sv, "hugetlbfs"sv, "mqueue"sv,
183 "nfsd"sv, // not nfs filesystem, but special config fs - http://linux.die.net/man/7/nfsd
184 "pstore"sv, "proc"sv, "rpc_pipefs"sv, "securityfs"sv, "selinuxfs"sv, "sunrpc"sv,
185 "sysfs"sv, "usbfs"sv,
186 };
187 static const Set<String> kNetworkFS_{
188 "nfs"sv,
189 "nfs3"sv,
190 "vboxsf"sv,
191 };
192 for (auto vi = volumes->begin (); vi != volumes->end (); ++vi) {
193 // @todo - NOTE - this is NOT a reliable way to tell, but hopefully good enough for starters
194 MountedFilesystemInfoType val2Update = vi->fValue;
195 if (val2Update.fFileSystemType) {
196 String fstype = *val2Update.fFileSystemType;
197 bool changed{false};
198 if (kRealDiskFS.Contains (fstype)) {
199 val2Update.fDeviceKind = BlockDeviceKind::eLocalDisk;
200 changed = true;
201 }
202 else if (kNetworkFS_.Contains (fstype)) {
203 val2Update.fDeviceKind = BlockDeviceKind::eNetworkDrive;
204 changed = true;
205 }
206 else if (fstype == "tmpfs"_k) {
207 val2Update.fDeviceKind = BlockDeviceKind::eTemporaryFiles;
208 changed = true;
209 }
210 else if (fstype == "iso9660"_k) {
211 val2Update.fDeviceKind = BlockDeviceKind::eReadOnlyEjectable;
212 changed = true;
213 }
214 else if (kSysFSList_.Contains (fstype)) {
215 val2Update.fDeviceKind = BlockDeviceKind::eSystemInformation;
216 changed = true;
217 }
218 if (changed) {
219 volumes->Update (vi, val2Update, &vi);
220 }
221 }
222 }
223 }
224}
225#endif
226#if qStroika_Foundation_Common_Platform_Linux
227namespace {
228 struct PerfStats_ {
229 double fSectorsRead;
230 double fTimeSpentReading;
231 double fReadsCompleted;
232 double fSectorsWritten;
233 double fTimeSpentWriting;
234 double fWritesCompleted;
235 double fWeightedTimeInQSeconds; // see https://www.kernel.org/doc/Documentation/block/stat.txt time_in_queue (product of the number of milliseconds times the number of requests waiting)
236 };
237 struct _Context : SystemPerformance::Support::Context {
238 Mapping<String, uint32_t> fDeviceName2SectorSizeMap_;
239 optional<Mapping<dev_t, PerfStats_>> fDiskPerfStats_;
240 };
241
242 struct InstrumentRep_Linux_ : InstrumentRepBase_<_Context> {
243 public:
244 using InstrumentRepBase_<_Context>::InstrumentRepBase_;
245
246 nonvirtual Info _InternalCapture ()
247 {
248#if USE_NOISY_TRACE_IN_THIS_MODULE_
249 Debug::TraceContextBumper ctx{"Instruments::Filesystem...InstrumentRep_Linux_::capture"};
250#endif
251 Info results;
252
253 constexpr bool kUseProcFSForMounts_{true};
254 if (kUseProcFSForMounts_) {
255 results.fMountedFilesystems = ReadVolumesAndUsageFromProcMountsAndstatvfs_ ();
256 }
257 else {
258 results.fMountedFilesystems = RunDF_ ();
259 }
260 ApplyDiskTypes_ (&results.fMountedFilesystems);
261 if (not _fOptions.fIncludeTemporaryDevices or not _fOptions.fIncludeSystemDevices) {
262 for (const KeyValuePair<MountedFilesystemNameType, MountedFilesystemInfoType>& i : results.fMountedFilesystems) {
263 if (not _fOptions.fIncludeTemporaryDevices and i.fValue.fDeviceKind == BlockDeviceKind::eTemporaryFiles) {
264 results.fMountedFilesystems.Remove (i.fKey);
265 }
266 else if (not not _fOptions.fIncludeSystemDevices and i.fValue.fDeviceKind == BlockDeviceKind::eSystemInformation) {
267 results.fMountedFilesystems.Remove (i.fKey);
268 }
269 }
270 }
271 if (_fOptions.fIOStatistics) {
272 ReadAndApplyProcFS_diskstats_ (&results.fMountedFilesystems);
273 }
274 _NoteCompletedCapture ();
275 return results;
276 }
277
278 private:
279 static Mapping<MountedFilesystemNameType, MountedFilesystemInfoType> ReadVolumesAndUsageFromProcMountsAndstatvfs_ ()
280 {
282 for (const IO::FileSystem::MountedFilesystemType& mi : IO::FileSystem::GetMountedFilesystems ()) {
284 String deviceName = (not mi.fDevicePaths.has_value () or mi.fDevicePaths->empty ()) ? String{} : String{mi.fDevicePaths->Nth (0)};
285 if (not deviceName.empty ()) {
286 vi.fDeviceOrVolumeName = deviceName;
287 }
288 vi.fFileSystemType = mi.fFileSystemType;
289 UpdateVolumeInfo_statvfs_ (mi.fMountedOn, &vi);
290 result.Add (mi.fMountedOn, vi);
291 }
292 return result;
293 }
294
295 private:
296 static void UpdateVolumeInfo_statvfs_ (const filesystem::path& mountedOnName, MountedFilesystemInfoType* v)
297 {
298 RequireNotNull (v);
299 struct statvfs sbuf{};
300 if (::statvfs (mountedOnName.c_str (), &sbuf) == 0) {
301 uint64_t diskSize = sbuf.f_bsize * sbuf.f_blocks;
302 v->fSizeInBytes = diskSize;
303 v->fAvailableSizeInBytes = sbuf.f_bsize * sbuf.f_bavail;
304 v->fUsedSizeInBytes = diskSize - sbuf.f_bsize * sbuf.f_bfree;
305 }
306 else {
307#if USE_NOISY_TRACE_IN_THIS_MODULE_
308 DbgTrace ("statvfs ({}) return error: errno={}"_f, mountedOnName, errno);
309#endif
310 }
311 }
312
313 private:
314 nonvirtual void ReadAndApplyProcFS_diskstats_ (Mapping<MountedFilesystemNameType, MountedFilesystemInfoType>* volumes)
315 {
316 try {
317 Mapping<dev_t, PerfStats_> diskStats = ReadProcFS_diskstats_ ();
318 Time::DurationSeconds timeSinceLastMeasure = Time::GetTickCount () - _GetCaptureContextTime ();
319 for (Iterator<KeyValuePair<MountedFilesystemNameType, MountedFilesystemInfoType>> i = volumes->begin (); i != volumes->end (); ++i) {
320 MountedFilesystemInfoType vi = i->fValue;
321 if (vi.fDeviceOrVolumeName.has_value ()) {
322 if (_fContext.load ()->fDiskPerfStats_) {
323 String devNameLessSlashes = *vi.fDeviceOrVolumeName;
324 size_t i = devNameLessSlashes.rfind ('/');
325 if (i != string::npos) {
326 devNameLessSlashes = devNameLessSlashes.SubString (i + 1);
327 }
328 dev_t useDevT;
329 {
330 struct stat sbuf{};
331 if (::stat (vi.fDeviceOrVolumeName->AsNarrowSDKString ().c_str (), &sbuf) == 0) {
332 useDevT = sbuf.st_rdev;
333 }
334 else {
335 continue;
336 }
337 }
338 optional<PerfStats_> oOld = _fContext.load ()->fDiskPerfStats_->Lookup (useDevT);
339 optional<PerfStats_> oNew = diskStats.Lookup (useDevT);
340 if (oOld.has_value () and oNew.has_value ()) {
341 unsigned int sectorSizeTmpHack = GetSectorSize_ (devNameLessSlashes);
342 IOStatsType readStats;
343 readStats.fBytesTransferred = (oNew->fSectorsRead - oOld->fSectorsRead) * sectorSizeTmpHack;
344 readStats.fTotalTransfers = oNew->fReadsCompleted - oOld->fReadsCompleted;
345 if (timeSinceLastMeasure > _fOptions.fMinimumAveragingInterval) {
346 readStats.fQLength = (oNew->fTimeSpentReading - oOld->fTimeSpentReading) / timeSinceLastMeasure.count ();
347 }
348
349 IOStatsType writeStats;
350 writeStats.fBytesTransferred = (oNew->fSectorsWritten - oOld->fSectorsWritten) * sectorSizeTmpHack;
351 writeStats.fTotalTransfers = oNew->fWritesCompleted - oOld->fWritesCompleted;
352 if (timeSinceLastMeasure > _fOptions.fMinimumAveragingInterval) {
353 writeStats.fQLength = (oNew->fTimeSpentWriting - oOld->fTimeSpentWriting) / timeSinceLastMeasure.count ();
354 }
355
356 IOStatsType combinedStats;
357 combinedStats.fBytesTransferred = *readStats.fBytesTransferred + *writeStats.fBytesTransferred;
358 combinedStats.fTotalTransfers = *readStats.fTotalTransfers + *writeStats.fTotalTransfers;
359 combinedStats.fQLength = *readStats.fQLength + *writeStats.fQLength;
360
361 vi.fReadIOStats = readStats;
362 vi.fWriteIOStats = writeStats;
363 // @todo DESCRIBE divide by time between 2 and * 1000 - NYI
364 if (timeSinceLastMeasure > _fOptions.fMinimumAveragingInterval) {
365 combinedStats.fQLength =
366 ((oNew->fWeightedTimeInQSeconds - oOld->fWeightedTimeInQSeconds) / timeSinceLastMeasure.count ());
367 }
368 vi.fCombinedIOStats = combinedStats;
369 }
370 }
371 }
372 volumes->Update (i, vi, &i);
373 }
374 _fContext.rwget ().rwref ()->fDiskPerfStats_ = diskStats;
375 }
376 catch (...) {
377 DbgTrace ("Exception gathering procfs disk io stats: {}"_f, current_exception ());
378 }
379 }
380
381 private:
382 static optional<filesystem::path> GetSysBlockDirPathForDevice_ (const String& deviceName)
383 {
384 Require (not deviceName.empty ());
385 Require (not deviceName.Contains ("/"sv));
386 // Sometimes the /sys/block directory appears to have data for the each major/minor pair, and sometimes it appears
387 // to only have it for the top level (minor=0) one without the digit after in the name.
388 //
389 // I don't understand this well yet, but this appears to temporarily allow us to limp along --LGP 2015-07-10
390 //tmphack
391 static const filesystem::path kSysBlock_{"/sys/block"sv};
392 filesystem::path tmp{kSysBlock_ / deviceName.As<filesystem::path> ()};
393 if (IO::FileSystem::Default ().Access (tmp)) {
394 return tmp;
395 }
396 //tmphack - try using one char less
397 tmp = kSysBlock_ / deviceName.SubString (0, -1).As<filesystem::path> ();
398 if (IO::FileSystem::Default ().Access (tmp)) {
399 return tmp;
400 }
401 return nullopt;
402 }
403
404 private:
405 nonvirtual uint32_t GetSectorSize_ (const String& deviceName)
406 {
407 auto o = _fContext.load ()->fDeviceName2SectorSizeMap_.Lookup (deviceName);
408 if (not o.has_value ()) {
409 if (optional<filesystem::path> blockDeviceInfoPath = GetSysBlockDirPathForDevice_ (deviceName)) {
410 filesystem::path fn = *blockDeviceInfoPath / "queue/hw_sector_size";
411 try {
412 o = String2Int<uint32_t> (
413 BinaryToText::Reader::New (IO::FileSystem::FileInputStream::New (fn, IO::FileSystem::FileInputStream::eNotSeekable))
414 .ReadAll ()
415 .Trim ());
416 _fContext.rwget ().rwref ()->fDeviceName2SectorSizeMap_.Add (deviceName, *o);
417 }
418 catch (...) {
419 DbgTrace ("Unknown error reading {}"_f, fn);
420 // ignore
421 }
422 }
423 }
424 return o.value_or (512); // seems the typical answer on UNIX
425 }
426
427 private:
428 // CURRENTLY UNUSED, but COULD be used on MacOS to gather some stats... -- LGP 2021-04-21
430 {
432 ProcessRunner pr{"/bin/df -k -P"sv};
433 Streams::MemoryStream::Ptr<byte> useStdOut = Streams::MemoryStream::New<byte> ();
434 std::exception_ptr runException;
435 try {
436 pr.Run (nullptr, useStdOut);
437 }
438 catch (...) {
439 runException = current_exception ();
440 }
441 String out;
443 bool skippedHeader = false;
444 for (String i = stdOut.ReadLine (); not i.empty (); i = stdOut.ReadLine ()) {
445 if (not skippedHeader) {
446 skippedHeader = true;
447 continue;
448 }
449 Sequence<String> l = i.Tokenize (Set<Characters::Character>{' '});
450 if (l.size () < 6) {
451 DbgTrace ("skipping line cuz len={}"_f, l.size ());
452 continue;
453 }
455 {
456 String d = l[0].Trim ();
457 if (not d.empty () and d != "none"sv) {
459 }
460 }
461 {
462 double szInBytes = Characters::FloatConversion::ToFloat<double> (l[1]) * 1024;
463 if (not std::isnan (szInBytes) and not std::isinf (szInBytes)) {
464 v.fSizeInBytes = szInBytes;
465 }
466 }
467 {
468 double usedSizeInBytes = FloatConversion::ToFloat<double> (l[2]) * 1024;
469 if (not std::isnan (usedSizeInBytes) and not std::isinf (usedSizeInBytes)) {
470 v.fUsedSizeInBytes = usedSizeInBytes;
471 }
472 }
473 if (v.fSizeInBytes and v.fUsedSizeInBytes) {
474 v.fAvailableSizeInBytes = *v.fSizeInBytes - *v.fUsedSizeInBytes;
475 }
476 result.Add (l[5].Trim ().As<filesystem::path> (), v);
477 }
478 // Sometimes (with busy box df especially) we get bogus error return. So only rethrow if we found no good data
479 if (runException and result.empty ()) {
480 Execution::ReThrow (runException);
481 }
482 return result;
483 }
484 static Mapping<MountedFilesystemNameType, MountedFilesystemInfoType> RunDF_ (bool includeFSTypes)
485 {
487 //
488 // I looked through the /proc filesystem stuff and didnt see anything obvious to retrive this info...
489 // run def with ProcessRunner
490 //
491 // NEW NOTE - I THINK ITS IN THERE.... RE-EXAMINE proc/filesystems proc/partitions, and http://en.wikipedia.org/wiki/Procfs
492 // -- LGP 2014-08-01
493 ProcessRunner pr{includeFSTypes ? "/bin/df -k -T"sv : "/bin/df -k"sv};
494 Streams::MemoryStream::Ptr<byte> useStdOut = Streams::MemoryStream::New<byte> ();
495 std::exception_ptr runException;
496 try {
497 pr.Run (nullptr, useStdOut);
498 }
499 catch (...) {
500 runException = current_exception ();
501 }
502 String out;
504 bool skippedHeader = false;
505 for (String i = stdOut.ReadLine (); not i.empty (); i = stdOut.ReadLine ()) {
506 if (not skippedHeader) {
507 skippedHeader = true;
508 continue;
509 }
510 Sequence<String> l = i.Tokenize (Set<Characters::Character>{' '});
511 if (l.size () < (includeFSTypes ? 7 : 6)) {
512 DbgTrace ("skipping line cuz len={}"_f, l.size ());
513 continue;
514 }
516 if (includeFSTypes) {
517 v.fFileSystemType = l[1].Trim ();
518 }
519 {
520 String d = l[0].Trim ();
521 if (not d.empty () and d != "none"_k) {
523 }
524 }
525 v.fSizeInBytes = FloatConversion::ToFloat<double> (l[includeFSTypes ? 2 : 1]) * 1024;
526 v.fUsedSizeInBytes = FloatConversion::ToFloat<double> (l[includeFSTypes ? 3 : 2]) * 1024;
527 v.fAvailableSizeInBytes = *v.fSizeInBytes - *v.fUsedSizeInBytes;
528 result.Add (l[includeFSTypes ? 6 : 5].Trim ().As<filesystem::path> (), v);
529 }
530 // Sometimes (with busy box df especially) we get bogus error return. So only rethrow if we found no good data
531 if (runException and result.empty ()) {
532 Execution::ReThrow (runException);
533 }
534 return result;
535 }
537 {
538 try {
539 return RunDF_ (true);
540 }
541 catch (...) {
542 return RunDF_ (false);
543 }
544 }
545
546 private:
547 static Mapping<dev_t, PerfStats_> ReadProcFS_diskstats_ ()
548 {
551 static const filesystem::path kProcMemInfoFileName_{"/proc/diskstats"sv};
552 // Note - /procfs files always unseekable
553 for (const Sequence<String>& line :
554 reader.ReadMatrix (IO::FileSystem::FileInputStream::New (kProcMemInfoFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
555#if USE_NOISY_TRACE_IN_THIS_MODULE_
556 DbgTrace (L"***in Instruments::Filesystem::ReadProcFS_diskstats_ linesize=%d, line[0]=%s", line.size (),
557 line.empty () ? L"" : line[0].c_str ());
558#endif
559 //
560 // https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
561 //
562 // 1 - major number
563 // 2 - minor mumber
564 // 3 - device name
565 // 4 - reads completed successfully
566 // 6 - sectors read
567 // 7 - time spent reading (ms)
568 // 8 - writes completed
569 // 10 - sectors written
570 // 11 - time spent writing (ms)
571 //
572 if (line.size () >= 13) {
573 String majorDevNumber = line[1 - 1];
574 String minorDevNumber = line[2 - 1];
575 String devName = line[3 - 1];
576 String readsCompleted = line[4 - 1];
577 String sectorsRead = line[6 - 1];
578 String timeSpentReadingMS = line[7 - 1];
579 String writesCompleted = line[8 - 1];
580 String sectorsWritten = line[10 - 1];
581 String timeSpentWritingMS = line[11 - 1];
582 constexpr bool kAlsoReadQLen_{true};
583 optional<double> weightedTimeInQSeconds;
584 if (kAlsoReadQLen_) {
585 optional<filesystem::path> sysBlockInfoPath = GetSysBlockDirPathForDevice_ (devName);
586 if (sysBlockInfoPath) {
587 for (const Sequence<String>& ll : reader.ReadMatrix (IO::FileSystem::FileInputStream::New (
588 *sysBlockInfoPath / "stat", IO::FileSystem::FileInputStream::eNotSeekable))) {
589 if (ll.size () >= 11) {
590 weightedTimeInQSeconds = FloatConversion::ToFloat (ll[11 - 1]) / 1000.0; // we record in seconds, but the value in file in milliseconds
591 break;
592 }
593 }
594 }
595 }
596 result.Add (makedev (String2Int<unsigned int> (majorDevNumber), String2Int<unsigned int> (minorDevNumber)),
597 PerfStats_{FloatConversion::ToFloat (sectorsRead), FloatConversion::ToFloat (timeSpentReadingMS) / 1000,
598 FloatConversion::ToFloat (readsCompleted), FloatConversion::ToFloat (sectorsWritten),
599 FloatConversion::ToFloat (timeSpentWritingMS) / 1000, FloatConversion::ToFloat (writesCompleted),
600 NullCoalesce (weightedTimeInQSeconds)});
601 }
602 }
603 return result;
604 }
605 };
606}
607#endif
608
609#if qStroika_Foundation_Common_Platform_Windows
610namespace {
611
612 struct _Context : SystemPerformance::Support::Context {
613#if qUseWMICollectionSupport_
614 // for collecting IO statistics
615 WMICollector fLogicalDiskWMICollector_{"LogicalDisk"sv,
616 {},
617 { kDiskReadBytesPerSec_,
618 kDiskWriteBytesPerSec_,
619 kDiskReadsPerSec_,
620 kDiskWritesPerSec_,
621 (kUseDiskPercentReadTime_ElseAveQLen_ToComputeQLen_ ? kPctDiskReadTime_ : kAveDiskReadQLen_),
622 (kUseDiskPercentReadTime_ElseAveQLen_ToComputeQLen_ ? kPctDiskWriteTime_ : kAveDiskWriteQLen_),
623 kPctIdleTime_ }};
624#endif
625 };
626
627 struct InstrumentRep_Windows_ : InstrumentRepBase_<_Context> {
628 using InstrumentRepBase_<_Context>::InstrumentRepBase_;
629
630 nonvirtual Info _InternalCapture ()
631 {
632#if USE_NOISY_TRACE_IN_THIS_MODULE_
633 Debug::TraceContextBumper ctx{"Instruments::Filesystem...InstrumentRep_Windows_::_InternalCapture"};
634#endif
635 Info result;
636
637 // Could probably usefully optimize to not capture if no drives because we can only get this when running as
638 // Admin, and for now, we capture little useful information at the drive level. But - we may eventually capture more...
640 for (const IO::FileSystem::DiskInfoType& pd : physDrives) {
641 DiskInfoType di{};
642 di.fSizeInBytes = pd.fSizeInBytes;
643 result.fDisks.Add (pd.fDeviceName, di);
644 }
645
646#if qUseWMICollectionSupport_
647 auto contextLock = scoped_lock{_fContext};
648 auto contextPtr = _fContext.rwget ().rwref ();
649 optional<Time::DurationSeconds> timeCollecting;
650 {
651 optional<Time::TimePointSeconds> timeOfPrevCollection = contextPtr->fLogicalDiskWMICollector_.GetTimeOfLastCollection ();
652 if (_fOptions.fIOStatistics) {
653 contextPtr->fLogicalDiskWMICollector_.Collect ();
654 }
655 if (timeOfPrevCollection) {
656 // time of lastCollection - once set, cannot become unset
657 timeCollecting = *contextPtr->fLogicalDiskWMICollector_.GetTimeOfLastCollection () - *timeOfPrevCollection;
658 }
659 }
660#endif
661
662 for (const IO::FileSystem::MountedFilesystemType& mfinfo : IO::FileSystem::GetMountedFilesystems ()) {
664 v.fFileSystemType = mfinfo.fFileSystemType;
665 v.fVolumeID = mfinfo.fVolumeID;
666 if (not physDrives.empty ()) {
667 v.fOnPhysicalDrive = mfinfo.fDevicePaths;
668 }
669
670 /*
671 * For now, we only capture the volume ID for a disk
672 */
673 if (v.fVolumeID) {
674 switch (::GetDriveType (v.fVolumeID->AsSDKString ().c_str ())) {
675 case DRIVE_REMOVABLE:
676 v.fDeviceKind = BlockDeviceKind::eRemovableDisk;
677 break;
678 case DRIVE_FIXED:
679 v.fDeviceKind = BlockDeviceKind::eLocalDisk;
680 break;
681 case DRIVE_REMOTE:
682 v.fDeviceKind = BlockDeviceKind::eNetworkDrive;
683 break;
684 case DRIVE_RAMDISK:
685 v.fDeviceKind = BlockDeviceKind::eTemporaryFiles;
686 break;
687 case DRIVE_CDROM:
688 v.fDeviceKind = BlockDeviceKind::eReadOnlyEjectable;
689 break;
690 default:; /*ignored - if it doesn't map or error - nevermind */
691 }
692 }
693
694 {
695 ULARGE_INTEGER freeBytesAvailable{};
696 ULARGE_INTEGER totalNumberOfBytes{};
697 ULARGE_INTEGER totalNumberOfFreeBytes{};
698 [[maybe_unused]] DWORD ignored =
699 ::GetDiskFreeSpaceEx (mfinfo.fMountedOn.c_str (), &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes);
700 v.fSizeInBytes = totalNumberOfBytes.QuadPart;
701 v.fUsedSizeInBytes = *v.fSizeInBytes - freeBytesAvailable.QuadPart;
702 v.fAvailableSizeInBytes = *v.fSizeInBytes - *v.fUsedSizeInBytes;
703#if qUseWMICollectionSupport_
704 auto safePctInUse2QL_ = [] (double pctInUse) {
705 // %InUse = QL / (1 + QL)
706 pctInUse /= 100;
707 pctInUse = clamp<double> (pctInUse, 0, 1);
708 return pctInUse / (1 - pctInUse);
709 };
710 if (_fOptions.fIOStatistics) {
711 String wmiInstanceName = String{mfinfo.fMountedOn}.RTrim ([] (Characters::Character c) { return c == '\\'; });
712 contextPtr->fLogicalDiskWMICollector_.AddInstancesIf (wmiInstanceName);
713
714 IOStatsType readStats;
715 if (timeCollecting) {
716 if (auto o = contextPtr->fLogicalDiskWMICollector_.PeekCurrentValue (wmiInstanceName, kDiskReadBytesPerSec_)) {
717 readStats.fBytesTransferred = *o * timeCollecting->count ();
718 }
719 if (auto o = contextPtr->fLogicalDiskWMICollector_.PeekCurrentValue (wmiInstanceName, kDiskReadsPerSec_)) {
720 readStats.fTotalTransfers = *o * timeCollecting->count ();
721 }
722 }
723 if (kUseDiskPercentReadTime_ElseAveQLen_ToComputeQLen_) {
724 if (auto o = contextPtr->fLogicalDiskWMICollector_.PeekCurrentValue (wmiInstanceName, kPctDiskReadTime_)) {
725 readStats.fInUsePercent = *o;
726 readStats.fQLength = safePctInUse2QL_ (*o);
727 }
728 }
729 else {
730 if (auto o = contextPtr->fLogicalDiskWMICollector_.PeekCurrentValue (wmiInstanceName, kAveDiskReadQLen_)) {
731 readStats.fInUsePercent = *o;
732 readStats.fQLength = *o;
733 }
734 }
735
736 IOStatsType writeStats;
737 if (timeCollecting) {
738 if (auto o = contextPtr->fLogicalDiskWMICollector_.PeekCurrentValue (wmiInstanceName, kDiskWriteBytesPerSec_)) {
739 writeStats.fBytesTransferred = *o * timeCollecting->count ();
740 }
741 if (auto o = contextPtr->fLogicalDiskWMICollector_.PeekCurrentValue (wmiInstanceName, kDiskWritesPerSec_)) {
742 writeStats.fTotalTransfers = *o * timeCollecting->count ();
743 }
744 }
745 if (kUseDiskPercentReadTime_ElseAveQLen_ToComputeQLen_) {
746 if (auto o = contextPtr->fLogicalDiskWMICollector_.PeekCurrentValue (wmiInstanceName, kPctDiskWriteTime_)) {
747 writeStats.fInUsePercent = *o;
748 writeStats.fQLength = safePctInUse2QL_ (*o);
749 }
750 }
751 else {
752 if (auto o = contextPtr->fLogicalDiskWMICollector_.PeekCurrentValue (wmiInstanceName, kAveDiskWriteQLen_)) {
753 writeStats.fInUsePercent = *o;
754 writeStats.fQLength = *o;
755 }
756 }
757
758 IOStatsType combinedStats = readStats;
759 Memory::AccumulateIf (&combinedStats.fBytesTransferred, writeStats.fBytesTransferred);
760 Memory::AccumulateIf (&combinedStats.fTotalTransfers, writeStats.fTotalTransfers);
761 Memory::AccumulateIf (&combinedStats.fQLength, writeStats.fQLength);
762 Memory::AccumulateIf (&combinedStats.fInUsePercent, writeStats.fInUsePercent);
763 if (readStats.fInUsePercent and writeStats.fInUsePercent) {
764 combinedStats.fInUsePercent = *combinedStats.fInUsePercent / 2; // must be safe cuz above would have set combined stats
765 }
766
767 if (kUsePctIdleIimeForAveQLen_) {
768 if (auto o = contextPtr->fLogicalDiskWMICollector_.PeekCurrentValue (wmiInstanceName, kPctIdleTime_)) {
769 double aveCombinedQLen = safePctInUse2QL_ (100.0 - *o);
770 if (readStats.fQLength and writeStats.fQLength and *combinedStats.fQLength > 0) {
771 // for some reason, the pct-idle-time #s combined are OK, but #s for aveQLen and disk read PCT/Write PCT wrong.
772 // asusme ratio rate, and scale
773 double correction = aveCombinedQLen / *combinedStats.fQLength;
774 Memory::AccumulateIf (&readStats.fQLength, correction, std::multiplies{});
775 Memory::AccumulateIf (&writeStats.fQLength, correction, std::multiplies{});
776 }
777 combinedStats.fQLength = aveCombinedQLen;
778 }
779 }
780
781 if (readStats.fBytesTransferred or readStats.fTotalTransfers or readStats.fQLength) {
782 v.fReadIOStats = readStats;
783 }
784 if (writeStats.fBytesTransferred or writeStats.fTotalTransfers or writeStats.fQLength) {
785 v.fWriteIOStats = writeStats;
786 }
787 if (combinedStats.fBytesTransferred or combinedStats.fTotalTransfers or combinedStats.fQLength) {
788 v.fCombinedIOStats = combinedStats;
789 }
790 }
791#endif
792 }
793 result.fMountedFilesystems.Add (mfinfo.fMountedOn, v);
794 }
795#if qUseWMICollectionSupport_
796 _NoteCompletedCapture (contextPtr->fLogicalDiskWMICollector_.GetTimeOfLastCollection ().value_or (Time::GetTickCount ()));
797#else
798 _NoteCompletedCapture ();
799#endif
800 return result;
801 }
802
803#if 0
804 private:
805// As of 2021-04-21, this is UNUSED. Not sure what it was for. Leave around for a bit longer, and then delete...
806 static optional<String> GetDeviceNameForVolumneName_ (const String& volumeName)
807 {
808 // use
809 // http://msdn.microsoft.com/en-us/library/windows/desktop/cc542456(v=vs.85).aspx
810 // CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName));
811 // to get DEVICENAME
812 //
813 String tmp = volumeName;
814 // Skip the \\?\ prefix and remove the trailing backslash.
815 // QueryDosDeviceW does not allow a trailing backslash,
816 // so temporarily remove it.
817 if (tmp.length () < 5 or
818 tmp[0] != L'\\' ||
819 tmp[1] != L'\\' ||
820 tmp[2] != L'?' ||
821 tmp[3] != L'\\' ||
822 tmp[tmp.length () - 1] != L'\\') {
823 //Error = ERROR_BAD_PATHNAME;
824 //wprintf(L"FindFirstVolumeW/FindNextVolumeW returned a bad path: %s\n", VolumeName);
825 return nullopt;
826 }
827 tmp = tmp.SubString (4, -1);
828
829 WCHAR deviceName[MAX_PATH] = L"";
830 if (::QueryDosDeviceW (tmp.c_str (), deviceName, ARRAYSIZE (deviceName)) != 0) {
831 return String{deviceName};
832 }
833 return nullopt;
834 }
835#endif
836 };
837}
838#endif
839
840namespace {
841 static const MeasurementType kMountedVolumeUsage_ = MeasurementType{"Mounted-Filesystem-Usage"sv};
842}
843
844namespace {
845 struct FilesystemInstrumentRep_
846#if qStroika_Foundation_Common_Platform_Linux
847 : InstrumentRep_Linux_
848#elif qStroika_Foundation_Common_Platform_Windows
849 : InstrumentRep_Windows_
850#else
851 : InstrumentRepBase_<SystemPerformance::Support::Context>
852#endif
853 {
854#if qStroika_Foundation_Common_Platform_Linux
855 using inherited = InstrumentRep_Linux_;
856#elif qStroika_Foundation_Common_Platform_Windows
857 using inherited = InstrumentRep_Windows_;
858#else
859 using inherited = InstrumentRepBase_<SystemPerformance::Support::Context>;
860#endif
861 FilesystemInstrumentRep_ (const Options& options, const shared_ptr<_Context>& context = Memory::MakeSharedPtr<_Context> ())
862 : inherited{options, context}
863 {
864 Require (_fOptions.fMinimumAveragingInterval > 0s);
865 }
866 virtual MeasurementSet Capture () override
867 {
868 MeasurementSet results;
869 Measurement m{kMountedVolumeUsage_,
870 Instruments::Filesystem::Instrument::kObjectVariantMapper.FromObject (Capture_Raw (&results.fMeasuredAt))};
871 results.fMeasurements.Add (m);
872 return results;
873 }
874 nonvirtual Info Capture_Raw (Range<TimePointSeconds>* outMeasuredAt)
875 {
876 return Do_Capture_Raw<Info> ([this] () { return _InternalCapture (); }, outMeasuredAt);
877 }
878 virtual unique_ptr<IRep> Clone () const override
879 {
880 return make_unique<FilesystemInstrumentRep_> (_fOptions, _fContext.load ());
881 }
882 nonvirtual Info _InternalCapture ()
883 {
884 AssertExternallySynchronizedMutex::WriteContext declareContext{*this};
885 Debug::TraceContextBumper ctx{"Instruments::Filesystem _InternalCapture"};
886#if qStroika_Foundation_Common_Platform_Linux or qStroika_Foundation_Common_Platform_Windows
887 Info result = inherited::_InternalCapture ();
888#else
889 Info result;
890#endif
891 if (_fOptions.fEstimateFilesystemStatsFromDiskStatsIfHelpful) {
892 result.fMountedFilesystems = ApplyDiskStatsToMissingFileSystemStats_ (result.fDisks, result.fMountedFilesystems);
893 }
894 return result;
895 }
896 // rarely used - see fEstimateFilesystemStatsFromDiskStatsIfHelpful in filesystem options
898 ApplyDiskStatsToMissingFileSystemStats_ (const Mapping<DynamicDiskIDType, DiskInfoType>& disks,
900 {
901#if USE_NOISY_TRACE_IN_THIS_MODULE_
902 Debug::TraceContextBumper ctx{"Instruments::Filesystem ... ApplyDiskStatsToMissingFileSystemStats_"};
903#endif
904 // Each FS will have some stats about disk usage, and we want to use those to relatively weight the stats from the disk usage when
905 // applied back to other FS stats.
906 //
907 // So first compute the total stat per disk
908 using WeightingStat2UseType = double;
911 Set<DynamicDiskIDType> disksForFS = NullCoalesce (i.fValue.fOnPhysicalDrive);
912 if (disksForFS.size () > 0) {
913 WeightingStat2UseType weightForFS = NullCoalesce (NullCoalesce (i.fValue.fCombinedIOStats).fBytesTransferred);
914 weightForFS /= disksForFS.size ();
915 for (const DynamicDiskIDType& di : disksForFS) {
916 totalWeights.Add (di, totalWeights.LookupValue (di) + weightForFS); // accumulate relative application to each disk
917 }
918 }
919 }
920#if USE_NOISY_TRACE_IN_THIS_MODULE_
921 {
922 Debug::TraceContextBumper ctx1{"Weighted disk stats"};
923 for (const auto& i : totalWeights) {
924 DbgTrace ("Disk '{}' weight {}"_f, i.fKey.c_str (), i.fValue);
925 }
926 }
927#endif
928
929 // At this point, for all disks with stats we can attribute back to a filesystem - we have the relative total of bytes xfered per disk
930
931 /*
932 * Now walk the filesystem objects, and their stats, and replace the fInUsePCT and fQLength stats with weighted values from
933 * the disks, based on their relative number of byte IO.
934 *
935 * This will not work well, but will work better than anything else I can think of, and have the NICE effect of at least not counting
936 * filesystems that are not doing any IO.
937 */
939 if (totalWeights.size () >= 1) {
941 MountedFilesystemInfoType mfi = i.fValue;
942 Set<DynamicDiskIDType> disksForFS = NullCoalesce (mfi.fOnPhysicalDrive);
943 if (disksForFS.size () > 0) {
944 WeightingStat2UseType weightForFS = NullCoalesce (NullCoalesce (i.fValue.fCombinedIOStats).fBytesTransferred);
945 weightForFS /= disksForFS.size ();
946 IOStatsType cumStats = NullCoalesce (mfi.fCombinedIOStats);
947 bool computeInuse = not cumStats.fInUsePercent.has_value ();
948 bool computeQLen = not cumStats.fQLength.has_value ();
949 bool computeTotalXFers = not cumStats.fTotalTransfers.has_value ();
950
951 for (const DynamicDiskIDType& di : disksForFS) {
952 IOStatsType diskIOStats = NullCoalesce (disks.LookupValue (di).fCombinedIOStats);
953 if (weightForFS > 0) {
954 double scaleFactor = weightForFS / totalWeights.LookupValue (di);
955 //Assert (0.0 <= scaleFactor and scaleFactor <= 1.0);
956 scaleFactor = clamp (scaleFactor, 0.0, 1.0);
957 if (computeInuse and diskIOStats.fInUsePercent) {
958 Memory::AccumulateIf<double> (&cumStats.fInUsePercent, *diskIOStats.fInUsePercent * scaleFactor);
959 }
960 if (computeQLen and diskIOStats.fInUsePercent) {
961 Memory::AccumulateIf<double> (&cumStats.fQLength, *diskIOStats.fQLength * scaleFactor);
962 }
963 if (computeTotalXFers and diskIOStats.fTotalTransfers) {
964 Memory::AccumulateIf<double> (&cumStats.fTotalTransfers, *diskIOStats.fTotalTransfers * scaleFactor);
965 }
966 }
967 }
968#if USE_NOISY_TRACE_IN_THIS_MODULE_
969 if (computeInuse) {
970 DbgTrace ("Adjusted fInUsePCT for filesystem '{}' is {}"_f, i.fKey.c_str (), NullCoalesce (cumStats.fInUsePercent));
971 }
972 if (computeQLen) {
973 DbgTrace ("Adjusted fQLength for filesystem '{}' is {}"_f, i.fKey.c_str (), NullCoalesce (cumStats.fQLength));
974 }
975#endif
976 mfi.fCombinedIOStats = cumStats;
977 }
978 newFilessytems.Add (i.fKey, mfi);
979 }
980 return newFilessytems;
981 }
982 else {
983 return fileSystems;
984 }
985 }
986 };
987}
988
989/*
990 ********************************************************************************
991 ******************** Instruments::Filesystem::Instrument ***********************
992 ********************************************************************************
993 */
995 ObjectVariantMapper mapper;
996 mapper.Add (mapper.MakeCommonSerializer_NamedEnumerations<BlockDeviceKind> ());
997 mapper.AddCommonType<optional<BlockDeviceKind>> ();
998 mapper.AddClass<IOStatsType> ({
999 {"Bytes"sv, &IOStatsType::fBytesTransferred},
1000 {"Q-Length"sv, &IOStatsType::fQLength},
1001 {"In-Use-%"sv, &IOStatsType::fInUsePercent},
1002 {"Total-Transfers"sv, &IOStatsType::fTotalTransfers},
1003 });
1004 mapper.AddCommonType<optional<IOStatsType>> ();
1005 mapper.AddClass<DiskInfoType> ({
1006 {"Persistence-Volume-ID"sv, &DiskInfoType::fPersistenceVolumeID},
1007 {"Device-Kind"sv, &DiskInfoType::fDeviceKind},
1008 {"Size"sv, &DiskInfoType::fSizeInBytes},
1009 {"Read-IO-Stats"sv, &DiskInfoType::fReadIOStats},
1010 {"Write-IO-Stats"sv, &DiskInfoType::fWriteIOStats},
1011 {"Combined-IO-Stats"sv, &DiskInfoType::fCombinedIOStats},
1012 });
1013 mapper.AddCommonType<Set<String>> ();
1014 mapper.AddCommonType<optional<Set<String>>> ();
1016 mapper.AddCommonType<optional<Set<filesystem::path>>> ();
1018 {"Device-Kind"_k, &MountedFilesystemInfoType::fDeviceKind},
1019 {"Filesystem-Type"_k, &MountedFilesystemInfoType::fFileSystemType},
1020 {"Device-Name"_k, &MountedFilesystemInfoType::fDeviceOrVolumeName},
1021 {"On-Physical-Drives"_k, &MountedFilesystemInfoType::fOnPhysicalDrive},
1022 {"Volume-ID"_k, &MountedFilesystemInfoType::fVolumeID},
1023 {"Total-Size"_k, &MountedFilesystemInfoType::fSizeInBytes},
1024 {"Available-Size"_k, &MountedFilesystemInfoType::fAvailableSizeInBytes},
1025 {"Used-Size"_k, &MountedFilesystemInfoType::fUsedSizeInBytes},
1026 {"Read-IO-Stats"_k, &MountedFilesystemInfoType::fReadIOStats},
1027 {"Write-IO-Stats"_k, &MountedFilesystemInfoType::fWriteIOStats},
1028 {"Combined-IO-Stats"_k, &MountedFilesystemInfoType::fCombinedIOStats},
1029 });
1032 mapper.AddClass<Info> ({
1033 {"Disks"_k, &Info::fDisks},
1034 {"Mounted-Filesystems"_k, &Info::fMountedFilesystems},
1035 });
1036 return mapper;
1037}();
1038
1039Instruments::Filesystem::Instrument::Instrument (const Options& options)
1040 : SystemPerformance::Instrument{InstrumentNameType{"Filesystem"sv},
1041 make_unique<FilesystemInstrumentRep_> (options),
1042 {kMountedVolumeUsage_},
1043 {KeyValuePair<type_index, MeasurementType>{typeid (Info), kMountedVolumeUsage_}},
1045{
1046}
1047
1048/*
1049 ********************************************************************************
1050 ********* SystemPerformance::Instrument::CaptureOneMeasurement *****************
1051 ********************************************************************************
1052 */
1053template <>
1055{
1056 Debug::TraceContextBumper ctx{"SystemPerformance::Instrument::CaptureOneMeasurement"};
1057 FilesystemInstrumentRep_* myCap = dynamic_cast<FilesystemInstrumentRep_*> (fCaptureRep_.get ());
1058 AssertNotNull (myCap);
1059 return myCap->Capture_Raw (measurementTimeOut);
1060}
#define AssertNotNull(p)
Definition Assertions.h:334
#define RequireNotNull(p)
Definition Assertions.h:348
wstring Capture(const Options &options={})
Definition BackTrace.cpp:47
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
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
#define DbgTrace
Definition Trace.h:317
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual bool Contains(Character c, CompareOptions co=eWithCase) const
Definition String.inl:697
nonvirtual size_t length() const noexcept
Definition String.inl:1051
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
Definition String.inl:1055
nonvirtual size_t rfind(Character c) const
Definition String.inl:1075
nonvirtual String SubString(SZ from) const
nonvirtual String RTrim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
Definition String.cpp:1509
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
Definition Mapping.inl:188
nonvirtual optional< mapped_type > Lookup(ArgByValueType< key_type > key) const
Definition Mapping.inl:142
nonvirtual void Update(const Iterator< value_type > &i, ArgByValueType< mapped_type > newValue, Iterator< value_type > *nextI=nullptr)
Definition Mapping.inl:312
nonvirtual mapped_type LookupValue(ArgByValueType< key_type > key, ArgByValueType< mapped_type > defaultValue=mapped_type{}) const
Definition Mapping.inl:166
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 void AddCommonType(ARGS &&... args)
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
Run the given command, and optionally support stdin/stdout/stderr as streams (either sync with Run,...
nonvirtual bool Access(const filesystem::path &fileFullPath, AccessMode accessMode=AccessMode::eRead) const noexcept
InputStream<>::Ptr is Smart pointer (with abstract Rep) class defining the interface to reading from ...
nonvirtual size_t size() const
Returns the number of items contained.
Definition Iterable.inl:303
nonvirtual T Nth(ptrdiff_t n) const
Find the Nth element of the Iterable<>
nonvirtual Iterator< T > begin() const
Support for ranged for, and STL syntax in general.
nonvirtual bool empty() const
Returns true iff size() == 0.
Definition Iterable.inl:309
static constexpr default_sentinel_t end() noexcept
Support for ranged for, and STL syntax in general.
An Iterator<T> is a copyable object which allows traversing the contents of some container....
Definition Iterator.h:225
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)
Containers::KeyedCollection< MountedFilesystemType, filesystem::path > GetMountedFilesystems()
Containers::KeyedCollection< DiskInfoType, filesystem::path > GetAvailableDisks()
Definition Disk.cpp:222
Ptr New(const InputStream::Ptr< byte > &src, optional< AutomaticCodeCvtFlags > codeCvtFlags={}, optional< SeekableFlag > seekable={}, ReadAhead readAhead=eReadAheadAllowed)
Create an InputStream::Ptr<Character> from the arguments (usually binary source) - which can be used ...