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