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"
28#include "Stroika/Foundation/Streams/MemoryStream.h"
31#if qStroika_Foundation_Common_Platform_Windows
32#include "Stroika/Foundation/Execution/Platform/Windows/Exception.h"
43using namespace Stroika::Foundation::Memory;
45using namespace Stroika::Frameworks;
46using namespace Stroika::Frameworks::SystemPerformance;
62#ifndef qUseWMICollectionSupport_
63#define qUseWMICollectionSupport_ qStroika_Foundation_Common_Platform_Windows
66#ifndef qSupportProcNet_
67#define qSupportProcNet_ qStroika_Foundation_Common_Platform_Linux
70#if qUseWMICollectionSupport_
76#if qUseWMICollectionSupport_
78 const String kBytesReceivedPerSecond_{
"Bytes Received/sec"sv};
79 const String kBytesSentPerSecond_{
"Bytes Sent/sec"sv};
80 const String kPacketsReceivedPerSecond_{
"Packets Received/sec"sv};
81 const String kPacketsSentPerSecond_{
"Packets Sent/sec"sv};
82 const String kTCPSegmentsPerSecond_{
"Segments/sec"sv};
83 const String kSegmentsRetransmittedPerSecond_{
"Segments Retransmitted/sec"sv};
87#if qStroika_Foundation_Common_Platform_Windows
88#pragma comment(lib, "iphlpapi.lib")
99 Memory::AccumulateIf (&fTotalBytesReceived, rhs.fTotalBytesReceived);
101 Memory::AccumulateIf (&fBytesPerSecondReceived, rhs.fBytesPerSecondReceived);
103 Memory::AccumulateIf (&fTCPSegmentsPerSecond, rhs.fTCPSegmentsPerSecond);
105 Memory::AccumulateIf (&fTCPRetransmittedSegmentsPerSecond, rhs.fTCPRetransmittedSegmentsPerSecond);
107 Memory::AccumulateIf (&fTotalPacketsReceived, rhs.fTotalPacketsReceived);
109 Memory::AccumulateIf (&fPacketsPerSecondReceived, rhs.fPacketsPerSecondReceived);
140String Instruments::Network::InterfaceInfo::ToString ()
const
146 template <
typename CONTEXT>
150#if qStroika_Foundation_Common_Platform_POSIX
153 uint64_t fTotalBytesReceived;
154 uint64_t fTotalBytesSent;
155 uint64_t fTotalPacketsReceived;
156 uint64_t fTotalPacketsSent;
157 TimePointSeconds fAt;
160 uint64_t fTotalTCPSegments;
161 uint64_t fTotalTCPRetransmittedSegments;
162 TimePointSeconds fAt;
167 optional<LastSum> fLastSum;
170 struct InstrumentRep_POSIX_ : InstrumentRepBase_<_Context> {
172 using InstrumentRepBase_<_Context>::InstrumentRepBase_;
177 using Instruments::Network::InterfaceInfo;
183 IgnoreExceptionsExceptThreadAbortForCall (Read_proc_net_dev_ (&interfaceResults, &accumSummary));
184 IgnoreExceptionsExceptThreadAbortForCall (Read_proc_net_snmp_ (&accumSummary));
188 auto contextLock = scoped_lock{_fContext};
189 shared_ptr<_Context> context = _fContext.rwget ().rwref ();
192 if (timespan >= _fOptions.fMinimumAveragingInterval) {
193 accumSummary.fTCPSegmentsPerSecond =
199 if (timespan >= _fOptions.fMinimumAveragingInterval) {
200 accumSummary.fTCPRetransmittedSegmentsPerSecond =
208 _NoteCompletedCapture ();
209 return Info{interfaceResults, accumSummary};
215 using Instruments::Network::InterfaceInfo;
219 static const filesystem::path kProcFileName_{
"/proc/net/dev"};
222#if qStroika_Foundation_Debug_DefaultTracingOn
223 unsigned int nLine = 0;
225 unsigned int n2Skip = 2;
227 reader.ReadMatrix (IO::FileSystem::FileInputStream::New (kProcFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
228#if USE_NOISY_TRACE_IN_THIS_MODULE_
229 DbgTrace (
"in Instruments::Network::Info capture_ linesize={}, line[0]={}"_f, line.size (), line.empty () ?
""_k : line[0]);
231#if qStroika_Foundation_Debug_DefaultTracingOn
238 if (line.size () >= 17) {
239 constexpr int kOffset2XMit_ = 8;
241 if (
auto info = systemInterfacesMgr.
GetById (line[0])) {
242 ii.fInterface = *info;
245 ii.fInterface.fInternalInterfaceID = line[0];
246 ii.fInterface.fFriendlyName = line[0];
248 ii.fIOStatistics.fTotalBytesReceived = Characters::String2Int<uint64_t> (line[1]);
249 ii.fIOStatistics.fTotalBytesSent = Characters::String2Int<uint64_t> (line[kOffset2XMit_ + 1]);
250 ii.fIOStatistics.fTotalPacketsReceived = Characters::String2Int<uint64_t> (line[kOffset2XMit_ + 2]);
251 ii.fIOStatistics.fTotalPacketsSent = Characters::String2Int<uint64_t> (line[2]);
252 ii.fIOStatistics.fTotalErrors =
253 Characters::String2Int<uint64_t> (line[3]) + Characters::String2Int<uint64_t> (line[kOffset2XMit_ + 3]);
254 ii.fIOStatistics.fTotalPacketsDropped =
255 Characters::String2Int<uint64_t> (line[4]) + Characters::String2Int<uint64_t> (line[kOffset2XMit_ + 4]);
258 if (
auto o = _fContext.cget ().cref ()->fLast.Lookup (ii.fInterface.fInternalInterfaceID)) {
260 if (scanTime > _fOptions.fMinimumAveragingInterval) {
261 ii.fIOStatistics.fBytesPerSecondReceived =
262 (*ii.fIOStatistics.fTotalBytesReceived - o->fTotalBytesReceived) / scanTime.count ();
263 ii.fIOStatistics.fBytesPerSecondSent = (*ii.fIOStatistics.fTotalBytesSent - o->fTotalBytesSent) / scanTime.count ();
264 ii.fIOStatistics.fPacketsPerSecondReceived =
265 (*ii.fIOStatistics.fTotalPacketsReceived - o->fTotalPacketsReceived) / scanTime.count ();
266 ii.fIOStatistics.fPacketsPerSecondSent =
267 (*ii.fIOStatistics.fTotalPacketsReceived - o->fTotalPacketsReceived) / scanTime.count ();
270 (*accumSummary) += ii.fIOStatistics;
271 interfaceResults->
Add (ii);
272 _fContext.rwget ().rwref ()->fLast.Add (ii.fInterface.fInternalInterfaceID,
273 Last{*ii.fIOStatistics.fTotalBytesReceived, *ii.fIOStatistics.fTotalBytesSent,
274 *ii.fIOStatistics.fTotalPacketsReceived, *ii.fIOStatistics.fTotalPacketsSent, now});
277 DbgTrace (
"Line {} bad in file {}"_f, nLine, kProcFileName_);
281 void Read_proc_net_netstat_ (
IOStatistics* accumSummary)
286 static const filesystem::path kProcFileName_{
"/proc/net/netstat"};
288 bool firstTime =
true;
291 reader.ReadMatrix (IO::FileSystem::FileInputStream::New (kProcFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
292#if USE_NOISY_TRACE_IN_THIS_MODULE_
293 DbgTrace (
"in Instruments::Network::Info Read_proc_net_netstat_ linesize={}, line[0]={}"_f, line.size (),
294 line.empty () ?
""_k : line[0]);
296 if (line.size () >= 2 and line[0].Trim () ==
"TcpExt:"sv) {
299 for (
const String& i : line) {
300 labelMap.
Add (i.Trim (), idx++);
306 if (
auto oTCPSynRetransIdx = labelMap.
Lookup (
"TCPSynRetrans"sv)) {
307 if (*oTCPSynRetransIdx < line.length ()) {
308 uint64_t TCPSynRetrans = Characters::String2Int<uint64_t> (line[*oTCPSynRetransIdx]);
320 static const filesystem::path kProcFileName_{
"/proc/net/snmp"sv};
322 bool firstTime =
true;
324 optional<uint64_t> totalTCPSegments;
326 reader.ReadMatrix (IO::FileSystem::FileInputStream::New (kProcFileName_, IO::FileSystem::FileInputStream::eNotSeekable))) {
327#if USE_NOISY_TRACE_IN_THIS_MODULE_
328 DbgTrace (
"in Instruments::Network::Info Read_proc_net_snmp_ linesize={}, line[0]={}"_f, line.size (), line.empty () ?
""_k : line[0]);
330 if (line.size () >= 2 and line[0].Trim () ==
"Tcp:"sv) {
333 for (
const String& i : line) {
334 labelMap.
Add (i.Trim (), idx++);
339 static const String kInSegs_{
"InSegs"sv};
340 static const String kOutSegs_{
"OutSegs"sv};
341 static const String kRetransSegs_{
"RetransSegs"sv};
342 if (
auto idx = labelMap.
Lookup (kInSegs_)) {
343 if (*idx < line.length ()) {
344 Memory::AccumulateIf (&totalTCPSegments, Characters::String2Int<uint64_t> (line[*idx]));
347 if (
auto idx = labelMap.
Lookup (kOutSegs_)) {
348 if (*idx < line.length ()) {
349 Memory::AccumulateIf (&totalTCPSegments, Characters::String2Int<uint64_t> (line[*idx]));
352 if (
auto idx = labelMap.
Lookup (kRetransSegs_)) {
353 if (*idx < line.length ()) {
367#if qStroika_Foundation_Common_Platform_Windows
370#if qUseWMICollectionSupport_
371 WMICollector fNetworkWMICollector_{
372 "Network Interface"sv, {}, {kBytesReceivedPerSecond_, kBytesSentPerSecond_, kPacketsReceivedPerSecond_, kPacketsSentPerSecond_}};
373 WMICollector fTCPv4WMICollector_{
"TCPv4"sv, {}, {kTCPSegmentsPerSecond_, kSegmentsRetransmittedPerSecond_}};
374 WMICollector fTCPv6WMICollector_{
"TCPv6"sv, {}, {kTCPSegmentsPerSecond_, kSegmentsRetransmittedPerSecond_}};
375 Set<String> fAvailableInstances_{fNetworkWMICollector_.GetAvailableInstances ()};
379 struct InstrumentRep_Windows_ : InstrumentRepBase_<_Context> {
381 InstrumentRep_Windows_ (
const Options& options,
const shared_ptr<_Context>& context)
382 : InstrumentRepBase_<_Context>{options, context}
384#if qUseWMICollectionSupport_
385#if USE_NOISY_TRACE_IN_THIS_MODULE_
388 for (
const String& i : fAvailableInstances_) {
389 DbgTrace (L
"wmiInstanceName='{}'", i);
399 using Instruments::Network::InterfaceInfo;
407#if qUseWMICollectionSupport_
408 _fContext.rwget ().rwref ()->fNetworkWMICollector_.Collect ();
409 _fContext.rwget ().rwref ()->fTCPv4WMICollector_.Collect ();
410 _fContext.rwget ().rwref ()->fTCPv6WMICollector_.Collect ();
416 ii.fInternalInterfaceID = networkInterface.fInternalInterfaceID;
417 ii.fDisplayName = networkInterface.fFriendlyName;
418 ii.fInterfaceType = networkInterface.fType;
419 ii.fInterfaceStatus = networkInterface.fStatus;
421 ii.fInterface = networkInterface;
422#if qUseWMICollectionSupport_
423 Read_WMI_ (networkInterface, &ii);
425 accumSummary += ii.fIOStatistics;
426 interfaceResults.
Add (move (ii));
429 result.fInterfaces = interfaceResults;
433 accumSummary.fTotalPacketsReceived = stats.dwInReceives;
437 _NoteCompletedCapture ();
440#if qUseWMICollectionSupport_
443#if USE_NOISY_TRACE_IN_THIS_MODULE_
453#if USE_NOISY_TRACE_IN_THIS_MODULE_
455 DbgTrace (
"wmiInstanceName='{}'"_f, wmiInstanceName);
465 if (_fContext.cget ().cref ()->fAvailableInstances_.Contains (wmiInstanceName)) {
466 auto lock = scoped_lock{_fContext};
467 auto context = _fContext.cget ().cref ();
468 context->fNetworkWMICollector_.AddInstancesIf (wmiInstanceName);
469 context->fTCPv4WMICollector_.AddInstancesIf (wmiInstanceName);
470 context->fTCPv6WMICollector_.AddInstancesIf (wmiInstanceName);
473 if (_fContext.cget ().cref ()->fAvailableInstances_.Contains (wmiInstanceName)) {
474 auto lock = scoped_lock{_fContext};
475 auto context = _fContext.rwget ().rwref ();
480 Memory::CopyToIf (&updateResult->fIOStatistics.fBytesPerSecondReceived,
481 context->fNetworkWMICollector_.PeekCurrentValue (wmiInstanceName, kBytesReceivedPerSecond_));
482 Memory::CopyToIf (&updateResult->fIOStatistics.fBytesPerSecondSent,
483 context->fNetworkWMICollector_.PeekCurrentValue (wmiInstanceName, kBytesSentPerSecond_));
484 Memory::CopyToIf (&updateResult->fIOStatistics.fPacketsPerSecondReceived,
485 context->fNetworkWMICollector_.PeekCurrentValue (wmiInstanceName, kPacketsReceivedPerSecond_));
486 Memory::CopyToIf (&updateResult->fIOStatistics.fPacketsPerSecondSent,
487 context->fNetworkWMICollector_.PeekCurrentValue (wmiInstanceName, kPacketsSentPerSecond_));
489 Memory::AccumulateIf (&updateResult->fIOStatistics.fTCPSegmentsPerSecond,
490 context->fTCPv4WMICollector_.PeekCurrentValue (wmiInstanceName, kTCPSegmentsPerSecond_));
491 Memory::AccumulateIf (&updateResult->fIOStatistics.fTCPSegmentsPerSecond,
492 context->fTCPv6WMICollector_.PeekCurrentValue (wmiInstanceName, kTCPSegmentsPerSecond_));
494 Memory::AccumulateIf (&updateResult->fIOStatistics.fTCPRetransmittedSegmentsPerSecond,
495 context->fTCPv4WMICollector_.PeekCurrentValue (wmiInstanceName, kSegmentsRetransmittedPerSecond_));
496 Memory::AccumulateIf (&updateResult->fIOStatistics.fTCPRetransmittedSegmentsPerSecond,
497 context->fTCPv6WMICollector_.PeekCurrentValue (wmiInstanceName, kSegmentsRetransmittedPerSecond_));
510 struct NetworkInstrumentRep_
511#if qStroika_Foundation_Common_Platform_POSIX
512 : InstrumentRep_POSIX_
513#elif qStroika_Foundation_Common_Platform_Windows
514 : InstrumentRep_Windows_
516 : InstrumentRepBase_<SystemPerformance::Support::Context>
519#if qStroika_Foundation_Common_Platform_POSIX
520 using inherited = InstrumentRep_POSIX_;
521#elif qStroika_Foundation_Common_Platform_Windows
522 using inherited = InstrumentRep_Windows_;
524 using inherited = InstrumentRepBase_<SystemPerformance::Support::Context>;
526 NetworkInstrumentRep_ (
const Options& options,
const shared_ptr<_Context>& context = MakeSharedPtr<_Context> ())
527 : inherited{options, context}
529 Require (_fOptions.fMinimumAveragingInterval > 0s);
537 results.fMeasurements.Add (m);
542 auto before = _GetCaptureContextTime ();
543 Info rawMeasurement = _InternalCapture ();
544 if (outMeasuredAt !=
nullptr) {
546 *outMeasuredAt =
Range<TimePointSeconds> (before, _GetCaptureContextTime (), Openness::eClosed, Openness::eClosed);
548 return rawMeasurement;
550 virtual unique_ptr<IRep> Clone ()
const override
552 return make_unique<NetworkInstrumentRep_> (_fOptions, _fContext.load ());
556 AssertExternallySynchronizedMutex::WriteContext declareContext{*
this};
557#if USE_NOISY_TRACE_IN_THIS_MODULE_
560 return inherited::_InternalCapture ();
577 mapper.
AddClass<InterfaceInfo::Interface> ({
581 {
"Interface-Type"sv, &InterfaceInfo::Interface::fType},
591 {
"Total-Bytes-Received"sv, &IOStatistics::fTotalBytesReceived},
593 {
"Bytes-Per-Second-Received"sv, &IOStatistics::fBytesPerSecondReceived},
595 {
"TCP-Segments-Per-Second"sv, &IOStatistics::fTCPSegmentsPerSecond},
597 {
"TCP-Retransmitted-Segments-Per-Second"sv, &IOStatistics::fTCPRetransmittedSegmentsPerSecond},
599 {
"Total-Packets-Received"sv, &IOStatistics::fTotalPacketsReceived},
601 {
"Packets-Per-Second-Received"sv, &IOStatistics::fPacketsPerSecondReceived},
606 {
"Interface"sv, &InterfaceInfo::fInterface},
607 {
"IO-Statistics"sv, &InterfaceInfo::fIOStatistics},
610 mapper.
AddCommonType<optional<Collection<InterfaceInfo>>> ();
613 {
"Interfaces"sv, &Info::fInterfaces},
619Instruments::Network::Instrument::Instrument (
const Options& options)
621 make_unique<NetworkInstrumentRep_> (options),
622 {kNetworkInterfacesMeasurement_},
624 kObjectVariantMapper}
637 NetworkInstrumentRep_* myCap =
dynamic_cast<NetworkInstrumentRep_*
> (fCaptureRep_.get ());
639 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