4#include "Stroika/Frameworks/StroikaPreComp.h"
6#if qStroika_Foundation_Common_Platform_Windows
12#include "Stroika/Foundation/Characters/FloatConversion.h"
13#include "Stroika/Foundation/Characters/String2Int.h"
14#include "Stroika/Foundation/Containers/Mapping.h"
15#include "Stroika/Foundation/Containers/Sequence.h"
16#include "Stroika/Foundation/Containers/Set.h"
22#include "Stroika/Foundation/Execution/ProcessRunner.h"
27#include "Stroika/Foundation/Streams/MemoryStream.h"
30#if qStroika_Foundation_Common_Platform_Windows
31#include "Stroika/Foundation/Execution/Platform/Windows/Exception.h"
42using namespace Stroika::Foundation::Memory;
44using namespace Stroika::Frameworks;
45using namespace Stroika::Frameworks::SystemPerformance;
61#ifndef qUseWMICollectionSupport_
62#define qUseWMICollectionSupport_ qStroika_Foundation_Common_Platform_Windows
65#ifndef qSupportProcNet_
66#define qSupportProcNet_ qStroika_Foundation_Common_Platform_Linux
69#if qUseWMICollectionSupport_
75#if qUseWMICollectionSupport_
77 const String kBytesReceivedPerSecond_{
"Bytes Received/sec"sv};
78 const String kBytesSentPerSecond_{
"Bytes Sent/sec"sv};
79 const String kPacketsReceivedPerSecond_{
"Packets Received/sec"sv};
80 const String kPacketsSentPerSecond_{
"Packets Sent/sec"sv};
81 const String kTCPSegmentsPerSecond_{
"Segments/sec"sv};
82 const String kSegmentsRetransmittedPerSecond_{
"Segments Retransmitted/sec"sv};
86#if qStroika_Foundation_Common_Platform_Windows
87#pragma comment(lib, "iphlpapi.lib")
98 Memory::AccumulateIf (&fTotalBytesReceived, rhs.fTotalBytesReceived);
100 Memory::AccumulateIf (&fBytesPerSecondReceived, rhs.fBytesPerSecondReceived);
102 Memory::AccumulateIf (&fTCPSegmentsPerSecond, rhs.fTCPSegmentsPerSecond);
104 Memory::AccumulateIf (&fTCPRetransmittedSegmentsPerSecond, rhs.fTCPRetransmittedSegmentsPerSecond);
106 Memory::AccumulateIf (&fTotalPacketsReceived, rhs.fTotalPacketsReceived);
108 Memory::AccumulateIf (&fPacketsPerSecondReceived, rhs.fPacketsPerSecondReceived);
139String Instruments::Network::InterfaceInfo::ToString ()
const
145 template <
typename CONTEXT>
149#if qStroika_Foundation_Common_Platform_POSIX
152 uint64_t fTotalBytesReceived;
153 uint64_t fTotalBytesSent;
154 uint64_t fTotalPacketsReceived;
155 uint64_t fTotalPacketsSent;
156 TimePointSeconds fAt;
159 uint64_t fTotalTCPSegments;
160 uint64_t fTotalTCPRetransmittedSegments;
161 TimePointSeconds fAt;
166 optional<LastSum> fLastSum;
169 struct InstrumentRep_POSIX_ : InstrumentRepBase_<_Context> {
171 using InstrumentRepBase_<_Context>::InstrumentRepBase_;
176 using Instruments::Network::InterfaceInfo;
182 IgnoreExceptionsExceptThreadAbortForCall (Read_proc_net_dev_ (&interfaceResults, &accumSummary));
183 IgnoreExceptionsExceptThreadAbortForCall (Read_proc_net_snmp_ (&accumSummary));
187 auto contextLock = scoped_lock{_fContext};
188 shared_ptr<_Context> context = _fContext.rwget ().rwref ();
191 if (timespan >= _fOptions.fMinimumAveragingInterval) {
192 accumSummary.fTCPSegmentsPerSecond =
198 if (timespan >= _fOptions.fMinimumAveragingInterval) {
199 accumSummary.fTCPRetransmittedSegmentsPerSecond =
207 _NoteCompletedCapture ();
208 return Info{interfaceResults, accumSummary};
214 using Instruments::Network::InterfaceInfo;
218 static const filesystem::path kProcFileName_{
"/proc/net/dev"};
221#if qStroika_Foundation_Debug_DefaultTracingOn
222 unsigned int nLine = 0;
224 unsigned int n2Skip = 2;
226 reader.ReadMatrix (IO::FileSystem::FileInputStream::New (kProcFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
227#if USE_NOISY_TRACE_IN_THIS_MODULE_
228 DbgTrace (
"in Instruments::Network::Info capture_ linesize={}, line[0]={}"_f, line.size (), line.empty () ?
""_k : line[0]);
230#if qStroika_Foundation_Debug_DefaultTracingOn
237 if (line.size () >= 17) {
238 constexpr int kOffset2XMit_ = 8;
240 if (
auto info = systemInterfacesMgr.
GetById (line[0])) {
241 ii.fInterface = *info;
244 ii.fInterface.fInternalInterfaceID = line[0];
245 ii.fInterface.fFriendlyName = line[0];
247 ii.fIOStatistics.fTotalBytesReceived = Characters::String2Int<uint64_t> (line[1]);
248 ii.fIOStatistics.fTotalBytesSent = Characters::String2Int<uint64_t> (line[kOffset2XMit_ + 1]);
249 ii.fIOStatistics.fTotalPacketsReceived = Characters::String2Int<uint64_t> (line[kOffset2XMit_ + 2]);
250 ii.fIOStatistics.fTotalPacketsSent = Characters::String2Int<uint64_t> (line[2]);
251 ii.fIOStatistics.fTotalErrors =
252 Characters::String2Int<uint64_t> (line[3]) + Characters::String2Int<uint64_t> (line[kOffset2XMit_ + 3]);
253 ii.fIOStatistics.fTotalPacketsDropped =
254 Characters::String2Int<uint64_t> (line[4]) + Characters::String2Int<uint64_t> (line[kOffset2XMit_ + 4]);
257 if (
auto o = _fContext.cget ().cref ()->fLast.Lookup (ii.fInterface.fInternalInterfaceID)) {
259 if (scanTime > _fOptions.fMinimumAveragingInterval) {
260 ii.fIOStatistics.fBytesPerSecondReceived =
261 (*ii.fIOStatistics.fTotalBytesReceived - o->fTotalBytesReceived) / scanTime.count ();
262 ii.fIOStatistics.fBytesPerSecondSent = (*ii.fIOStatistics.fTotalBytesSent - o->fTotalBytesSent) / scanTime.count ();
263 ii.fIOStatistics.fPacketsPerSecondReceived =
264 (*ii.fIOStatistics.fTotalPacketsReceived - o->fTotalPacketsReceived) / scanTime.count ();
265 ii.fIOStatistics.fPacketsPerSecondSent =
266 (*ii.fIOStatistics.fTotalPacketsReceived - o->fTotalPacketsReceived) / scanTime.count ();
269 (*accumSummary) += ii.fIOStatistics;
270 interfaceResults->
Add (ii);
271 _fContext.rwget ().rwref ()->fLast.Add (ii.fInterface.fInternalInterfaceID,
272 Last{*ii.fIOStatistics.fTotalBytesReceived, *ii.fIOStatistics.fTotalBytesSent,
273 *ii.fIOStatistics.fTotalPacketsReceived, *ii.fIOStatistics.fTotalPacketsSent, now});
276 DbgTrace (
"Line {} bad in file {}"_f, nLine, kProcFileName_);
280 void Read_proc_net_netstat_ (
IOStatistics* accumSummary)
285 static const filesystem::path kProcFileName_{
"/proc/net/netstat"};
287 bool firstTime =
true;
290 reader.ReadMatrix (IO::FileSystem::FileInputStream::New (kProcFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
291#if USE_NOISY_TRACE_IN_THIS_MODULE_
292 DbgTrace (
"in Instruments::Network::Info Read_proc_net_netstat_ linesize={}, line[0]={}"_f, line.size (),
293 line.empty () ?
""_k : line[0]);
295 if (line.size () >= 2 and line[0].Trim () ==
"TcpExt:"sv) {
298 for (
const String& i : line) {
299 labelMap.
Add (i.Trim (), idx++);
305 if (
auto oTCPSynRetransIdx = labelMap.
Lookup (
"TCPSynRetrans"sv)) {
306 if (*oTCPSynRetransIdx < line.length ()) {
307 uint64_t TCPSynRetrans = Characters::String2Int<uint64_t> (line[*oTCPSynRetransIdx]);
319 static const filesystem::path kProcFileName_{
"/proc/net/snmp"sv};
321 bool firstTime =
true;
323 optional<uint64_t> totalTCPSegments;
325 reader.ReadMatrix (IO::FileSystem::FileInputStream::New (kProcFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
326#if USE_NOISY_TRACE_IN_THIS_MODULE_
327 DbgTrace (
"in Instruments::Network::Info Read_proc_net_snmp_ linesize={}, line[0]={}"_f, line.size (), line.empty () ?
""_k : line[0]);
329 if (line.size () >= 2 and line[0].Trim () ==
"Tcp:"sv) {
332 for (
const String& i : line) {
333 labelMap.
Add (i.Trim (), idx++);
338 static const String kInSegs_{
"InSegs"sv};
339 static const String kOutSegs_{
"OutSegs"sv};
340 static const String kRetransSegs_{
"RetransSegs"sv};
341 if (
auto idx = labelMap.
Lookup (kInSegs_)) {
342 if (*idx < line.length ()) {
343 Memory::AccumulateIf (&totalTCPSegments, Characters::String2Int<uint64_t> (line[*idx]));
346 if (
auto idx = labelMap.
Lookup (kOutSegs_)) {
347 if (*idx < line.length ()) {
348 Memory::AccumulateIf (&totalTCPSegments, Characters::String2Int<uint64_t> (line[*idx]));
351 if (
auto idx = labelMap.
Lookup (kRetransSegs_)) {
352 if (*idx < line.length ()) {
366#if qStroika_Foundation_Common_Platform_Windows
369#if qUseWMICollectionSupport_
370 WMICollector fNetworkWMICollector_{
371 "Network Interface"sv, {}, {kBytesReceivedPerSecond_, kBytesSentPerSecond_, kPacketsReceivedPerSecond_, kPacketsSentPerSecond_}};
372 WMICollector fTCPv4WMICollector_{
"TCPv4"sv, {}, {kTCPSegmentsPerSecond_, kSegmentsRetransmittedPerSecond_}};
373 WMICollector fTCPv6WMICollector_{
"TCPv6"sv, {}, {kTCPSegmentsPerSecond_, kSegmentsRetransmittedPerSecond_}};
374 Set<String> fAvailableInstances_{fNetworkWMICollector_.GetAvailableInstances ()};
378 struct InstrumentRep_Windows_ : InstrumentRepBase_<_Context> {
380 InstrumentRep_Windows_ (
const Options& options,
const shared_ptr<_Context>& context)
381 : InstrumentRepBase_<_Context>{options, context}
383#if qUseWMICollectionSupport_
384#if USE_NOISY_TRACE_IN_THIS_MODULE_
387 for (
const String& i : fAvailableInstances_) {
388 DbgTrace (L
"wmiInstanceName='{}'", i);
398 using Instruments::Network::InterfaceInfo;
406#if qUseWMICollectionSupport_
407 _fContext.rwget ().rwref ()->fNetworkWMICollector_.Collect ();
408 _fContext.rwget ().rwref ()->fTCPv4WMICollector_.Collect ();
409 _fContext.rwget ().rwref ()->fTCPv6WMICollector_.Collect ();
415 ii.fInternalInterfaceID = networkInterface.fInternalInterfaceID;
416 ii.fDisplayName = networkInterface.fFriendlyName;
417 ii.fInterfaceType = networkInterface.fType;
418 ii.fInterfaceStatus = networkInterface.fStatus;
420 ii.fInterface = networkInterface;
421#if qUseWMICollectionSupport_
422 Read_WMI_ (networkInterface, &ii);
424 accumSummary += ii.fIOStatistics;
425 interfaceResults.
Add (move (ii));
428 result.fInterfaces = interfaceResults;
432 accumSummary.fTotalPacketsReceived = stats.dwInReceives;
436 _NoteCompletedCapture ();
439#if qUseWMICollectionSupport_
442#if USE_NOISY_TRACE_IN_THIS_MODULE_
452#if USE_NOISY_TRACE_IN_THIS_MODULE_
454 DbgTrace (
"wmiInstanceName='{}'"_f, wmiInstanceName);
464 if (_fContext.cget ().cref ()->fAvailableInstances_.Contains (wmiInstanceName)) {
465 auto lock = scoped_lock{_fContext};
466 auto context = _fContext.cget ().cref ();
467 context->fNetworkWMICollector_.AddInstancesIf (wmiInstanceName);
468 context->fTCPv4WMICollector_.AddInstancesIf (wmiInstanceName);
469 context->fTCPv6WMICollector_.AddInstancesIf (wmiInstanceName);
472 if (_fContext.cget ().cref ()->fAvailableInstances_.Contains (wmiInstanceName)) {
473 auto lock = scoped_lock{_fContext};
474 auto context = _fContext.rwget ().rwref ();
479 Memory::CopyToIf (&updateResult->fIOStatistics.fBytesPerSecondReceived,
480 context->fNetworkWMICollector_.PeekCurrentValue (wmiInstanceName, kBytesReceivedPerSecond_));
481 Memory::CopyToIf (&updateResult->fIOStatistics.fBytesPerSecondSent,
482 context->fNetworkWMICollector_.PeekCurrentValue (wmiInstanceName, kBytesSentPerSecond_));
483 Memory::CopyToIf (&updateResult->fIOStatistics.fPacketsPerSecondReceived,
484 context->fNetworkWMICollector_.PeekCurrentValue (wmiInstanceName, kPacketsReceivedPerSecond_));
485 Memory::CopyToIf (&updateResult->fIOStatistics.fPacketsPerSecondSent,
486 context->fNetworkWMICollector_.PeekCurrentValue (wmiInstanceName, kPacketsSentPerSecond_));
488 Memory::AccumulateIf (&updateResult->fIOStatistics.fTCPSegmentsPerSecond,
489 context->fTCPv4WMICollector_.PeekCurrentValue (wmiInstanceName, kTCPSegmentsPerSecond_));
490 Memory::AccumulateIf (&updateResult->fIOStatistics.fTCPSegmentsPerSecond,
491 context->fTCPv6WMICollector_.PeekCurrentValue (wmiInstanceName, kTCPSegmentsPerSecond_));
493 Memory::AccumulateIf (&updateResult->fIOStatistics.fTCPRetransmittedSegmentsPerSecond,
494 context->fTCPv4WMICollector_.PeekCurrentValue (wmiInstanceName, kSegmentsRetransmittedPerSecond_));
495 Memory::AccumulateIf (&updateResult->fIOStatistics.fTCPRetransmittedSegmentsPerSecond,
496 context->fTCPv6WMICollector_.PeekCurrentValue (wmiInstanceName, kSegmentsRetransmittedPerSecond_));
509 struct NetworkInstrumentRep_
510#if qStroika_Foundation_Common_Platform_POSIX
511 : InstrumentRep_POSIX_
512#elif qStroika_Foundation_Common_Platform_Windows
513 : InstrumentRep_Windows_
515 : InstrumentRepBase_<SystemPerformance::Support::Context>
518#if qStroika_Foundation_Common_Platform_POSIX
519 using inherited = InstrumentRep_POSIX_;
520#elif qStroika_Foundation_Common_Platform_Windows
521 using inherited = InstrumentRep_Windows_;
523 using inherited = InstrumentRepBase_<SystemPerformance::Support::Context>;
525 NetworkInstrumentRep_ (
const Options& options,
const shared_ptr<_Context>& context = make_shared<_Context> ())
526 : inherited{options, context}
528 Require (_fOptions.fMinimumAveragingInterval > 0s);
536 results.fMeasurements.Add (m);
541 auto before = _GetCaptureContextTime ();
542 Info rawMeasurement = _InternalCapture ();
543 if (outMeasuredAt !=
nullptr) {
545 *outMeasuredAt =
Range<TimePointSeconds> (before, _GetCaptureContextTime (), Openness::eClosed, Openness::eClosed);
547 return rawMeasurement;
549 virtual unique_ptr<IRep> Clone ()
const override
551 return make_unique<NetworkInstrumentRep_> (_fOptions, _fContext.load ());
555 AssertExternallySynchronizedMutex::WriteContext declareContext{*
this};
556#if USE_NOISY_TRACE_IN_THIS_MODULE_
559 return inherited::_InternalCapture ();
576 mapper.
AddClass<InterfaceInfo::Interface> ({
580 {
"Interface-Type"sv, &InterfaceInfo::Interface::fType},
590 {
"Total-Bytes-Received"sv, &IOStatistics::fTotalBytesReceived},
592 {
"Bytes-Per-Second-Received"sv, &IOStatistics::fBytesPerSecondReceived},
594 {
"TCP-Segments-Per-Second"sv, &IOStatistics::fTCPSegmentsPerSecond},
596 {
"TCP-Retransmitted-Segments-Per-Second"sv, &IOStatistics::fTCPRetransmittedSegmentsPerSecond},
598 {
"Total-Packets-Received"sv, &IOStatistics::fTotalPacketsReceived},
600 {
"Packets-Per-Second-Received"sv, &IOStatistics::fPacketsPerSecondReceived},
605 {
"Interface"sv, &InterfaceInfo::fInterface},
606 {
"IO-Statistics"sv, &InterfaceInfo::fIOStatistics},
609 mapper.
AddCommonType<optional<Collection<InterfaceInfo>>> ();
612 {
"Interfaces"sv, &Info::fInterfaces},
618Instruments::Network::Instrument::Instrument (
const Options& options)
620 make_unique<NetworkInstrumentRep_> (options),
621 {kNetworkInterfacesMeasurement_},
623 kObjectVariantMapper}
636 NetworkInstrumentRep_* myCap =
dynamic_cast<NetworkInstrumentRep_*
> (fCaptureRep_.get ());
638 return myCap->Capture_Raw (measurementTimeOut);
#define RequireNotNull(p)
#define AssertNotReached()
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...
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
String is like std::u32string, except it is much easier to use, often much more space efficient,...
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
nonvirtual void Add(ArgByValueType< value_type > item)
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
nonvirtual optional< mapped_type > Lookup(ArgByValueType< key_type > key) const
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 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
nonvirtual Traversal::Iterable< Interface > GetAll()
nonvirtual optional< Interface > GetById(const Interface::SystemIDType &internalInterfaceID)
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Create a format-string (see std::wformat_string or Stroika FormatString, or python 'f' strings.
optional< Containers::Set< Status > > fStatus
optional< uint64_t > fTransmitSpeedBaud
SystemIDType fInternalInterfaceID
optional< String > fDescription
optional< uint64_t > fReceiveLinkSpeedBaud
optional< String > fHardwareAddress