From c5b60594b64089d780426ea27961bfe0c8376227 Mon Sep 17 00:00:00 2001 From: Clement Date: Fri, 7 Dec 2018 12:07:35 -0800 Subject: [PATCH 1/4] Record more detailed stats on Retransmit/duplicate --- assignment-client/src/assets/AssetServer.cpp | 4 +- libraries/networking/src/LimitedNodeList.h | 2 - libraries/networking/src/udt/Connection.cpp | 7 ++-- libraries/networking/src/udt/Connection.h | 2 +- .../networking/src/udt/ConnectionStats.cpp | 26 ++++++++++++- .../networking/src/udt/ConnectionStats.h | 38 ++++++++++++------- libraries/networking/src/udt/SendQueue.cpp | 4 +- libraries/networking/src/udt/SendQueue.h | 2 +- tools/udt-test/src/UDTTest.cpp | 2 +- 9 files changed, 60 insertions(+), 27 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 41aeaba468..e0772c7aea 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -945,14 +945,14 @@ void AssetServer::sendStatsPacket() { upstreamStats["2. Sent Packets"] = stat.second.sentPackets; upstreamStats["3. Recvd ACK"] = events[Events::ReceivedACK]; upstreamStats["4. Procd ACK"] = events[Events::ProcessedACK]; - upstreamStats["5. Retransmitted"] = events[Events::Retransmission]; + upstreamStats["5. Retransmitted"] = stat.second.retransmittedPackets; nodeStats["Upstream Stats"] = upstreamStats; QJsonObject downstreamStats; downstreamStats["1. Recvd (P/s)"] = stat.second.receiveRate; downstreamStats["2. Recvd Packets"] = stat.second.receivedPackets; downstreamStats["3. Sent ACK"] = events[Events::SentACK]; - downstreamStats["4. Duplicates"] = events[Events::Duplicate]; + downstreamStats["4. Duplicates"] = stat.second.duplicatePackets; nodeStats["Downstream Stats"] = downstreamStats; QString uuid; diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 33a4a7e0b4..5be9ff477c 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -372,8 +372,6 @@ protected: qint64 sendPacket(std::unique_ptr packet, const Node& destinationNode, const HifiSockAddr& overridenSockAddr); - qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr, - const QUuid& connectionSecret = QUuid()); void collectPacketStats(const NLPacket& packet); void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr); diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 4798288a18..d87f6fd35d 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -192,8 +192,9 @@ void Connection::recordSentPackets(int wireSize, int payloadSize, _congestionControl->onPacketSent(wireSize, seqNum, timePoint); } -void Connection::recordRetransmission(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) { - _stats.record(ConnectionStats::Stats::Retransmission); +void Connection::recordRetransmission(int wireSize, int payloadSize, + SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint) { + _stats.recordRetransmittedPackets(payloadSize, wireSize); _congestionControl->onPacketReSent(wireSize, seqNum, timePoint); } @@ -270,7 +271,7 @@ bool Connection::processReceivedSequenceNumber(SequenceNumber sequenceNumber, in sendACK(); if (wasDuplicate) { - _stats.record(ConnectionStats::Stats::Duplicate); + _stats.recordDuplicatePackets(payloadSize, packetSize); } else { _stats.recordReceivedPackets(payloadSize, packetSize); } diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index 17e8a9b1f9..5a482dd200 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -80,7 +80,7 @@ signals: private slots: void recordSentPackets(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint); - void recordRetransmission(int wireSize, SequenceNumber sequenceNumber, p_high_resolution_clock::time_point timePoint); + void recordRetransmission(int wireSize, int payloadSize, SequenceNumber sequenceNumber, p_high_resolution_clock::time_point timePoint); void queueInactive(); void queueTimeout(); diff --git a/libraries/networking/src/udt/ConnectionStats.cpp b/libraries/networking/src/udt/ConnectionStats.cpp index e30c588dba..f0bfbde067 100644 --- a/libraries/networking/src/udt/ConnectionStats.cpp +++ b/libraries/networking/src/udt/ConnectionStats.cpp @@ -60,6 +60,28 @@ void ConnectionStats::recordReceivedPackets(int payload, int total) { _total.receivedBytes += total; } +void ConnectionStats::recordRetransmittedPackets(int payload, int total) { + ++_currentSample.retransmittedPackets; + ++_total.retransmittedPackets; + + _currentSample.retransmittedUtilBytes += payload; + _total.retransmittedUtilBytes += payload; + + _currentSample.retransmittedBytes += total; + _total.retransmittedBytes += total; +} + +void ConnectionStats::recordDuplicatePackets(int payload, int total) { + ++_currentSample.duplicatePackets; + ++_total.duplicatePackets; + + _currentSample.duplicateUtilBytes += payload; + _total.duplicateUtilBytes += payload; + + _currentSample.duplicateBytes += total; + _total.duplicateBytes += total; +} + void ConnectionStats::recordUnreliableSentPackets(int payload, int total) { ++_currentSample.sentUnreliablePackets; ++_total.sentUnreliablePackets; @@ -117,13 +139,13 @@ QDebug& operator<<(QDebug&& debug, const udt::ConnectionStats::Stats& stats) { HIFI_LOG_EVENT(SentACK) HIFI_LOG_EVENT(ReceivedACK) HIFI_LOG_EVENT(ProcessedACK) - HIFI_LOG_EVENT(Retransmission) - HIFI_LOG_EVENT(Duplicate) ; #undef HIFI_LOG_EVENT debug << " Sent packets: " << stats.sentPackets; + debug << "\n Retransmitted packets: " << stats.retransmittedPackets; debug << "\n Received packets: " << stats.receivedPackets; + debug << "\n Duplicate packets: " << stats.duplicatePackets; debug << "\n Sent util bytes: " << stats.sentUtilBytes; debug << "\n Sent bytes: " << stats.sentBytes; debug << "\n Received bytes: " << stats.receivedBytes << "\n"; diff --git a/libraries/networking/src/udt/ConnectionStats.h b/libraries/networking/src/udt/ConnectionStats.h index 0fdd1636b3..0be4778323 100644 --- a/libraries/networking/src/udt/ConnectionStats.h +++ b/libraries/networking/src/udt/ConnectionStats.h @@ -14,6 +14,7 @@ #include #include +#include namespace udt { @@ -24,8 +25,6 @@ public: SentACK, ReceivedACK, ProcessedACK, - Retransmission, - Duplicate, NumEvents }; @@ -40,19 +39,27 @@ public: Events events; // packet counts and sizes - int sentPackets { 0 }; - int receivedPackets { 0 }; - int sentUtilBytes { 0 }; - int receivedUtilBytes { 0 }; - int sentBytes { 0 }; - int receivedBytes { 0 }; + uint32_t sentPackets { 0 }; + uint32_t receivedPackets { 0 }; + uint32_t retransmittedPackets { 0 }; + uint32_t duplicatePackets { 0 }; + + uint64_t sentUtilBytes { 0 }; + uint64_t receivedUtilBytes { 0 }; + uint64_t retransmittedUtilBytes { 0 }; + uint64_t duplicateUtilBytes { 0 }; + + uint64_t sentBytes { 0 }; + uint64_t receivedBytes { 0 }; + uint64_t retransmittedBytes { 0 }; + uint64_t duplicateBytes { 0 }; - int sentUnreliablePackets { 0 }; - int receivedUnreliablePackets { 0 }; - int sentUnreliableUtilBytes { 0 }; - int receivedUnreliableUtilBytes { 0 }; - int sentUnreliableBytes { 0 }; - int receivedUnreliableBytes { 0 }; + uint32_t sentUnreliablePackets { 0 }; + uint32_t receivedUnreliablePackets { 0 }; + uint64_t sentUnreliableUtilBytes { 0 }; + uint64_t receivedUnreliableUtilBytes { 0 }; + uint64_t sentUnreliableBytes { 0 }; + uint64_t receivedUnreliableBytes { 0 }; // the following stats are trailing averages in the result, not totals int sendRate { 0 }; @@ -75,6 +82,9 @@ public: void recordSentPackets(int payload, int total); void recordReceivedPackets(int payload, int total); + + void recordRetransmittedPackets(int payload, int total); + void recordDuplicatePackets(int payload, int total); void recordUnreliableSentPackets(int payload, int total); void recordUnreliableReceivedPackets(int payload, int total); diff --git a/libraries/networking/src/udt/SendQueue.cpp b/libraries/networking/src/udt/SendQueue.cpp index 3178217a36..b507f0921d 100644 --- a/libraries/networking/src/udt/SendQueue.cpp +++ b/libraries/networking/src/udt/SendQueue.cpp @@ -404,6 +404,7 @@ bool SendQueue::maybeResendPacket() { Packet::ObfuscationLevel level = (Packet::ObfuscationLevel)(entry.first < 2 ? 0 : (entry.first - 2) % 4); auto wireSize = resendPacket.getWireSize(); + auto payloadSize = resendPacket.getPayloadSize(); auto sequenceNumber = it->first; if (level != Packet::NoObfuscation) { @@ -439,7 +440,8 @@ bool SendQueue::maybeResendPacket() { sentLocker.unlock(); } - emit packetRetransmitted(wireSize, sequenceNumber, p_high_resolution_clock::now()); + emit packetRetransmitted(wireSize, payloadSize, sequenceNumber, + p_high_resolution_clock::now()); // Signal that we did resend a packet return true; diff --git a/libraries/networking/src/udt/SendQueue.h b/libraries/networking/src/udt/SendQueue.h index c1faac3b22..148d813fc1 100644 --- a/libraries/networking/src/udt/SendQueue.h +++ b/libraries/networking/src/udt/SendQueue.h @@ -78,7 +78,7 @@ public slots: signals: void packetSent(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint); - void packetRetransmitted(int wireSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint); + void packetRetransmitted(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint); void queueInactive(); diff --git a/tools/udt-test/src/UDTTest.cpp b/tools/udt-test/src/UDTTest.cpp index 46e7ed0be0..65bf2c7ebd 100644 --- a/tools/udt-test/src/UDTTest.cpp +++ b/tools/udt-test/src/UDTTest.cpp @@ -386,7 +386,7 @@ void UDTTest::sampleStats() { QString::number(stats.events[udt::ConnectionStats::Stats::ReceivedACK]).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()), QString::number(stats.events[udt::ConnectionStats::Stats::ProcessedACK]).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()), QString::number(stats.sentPackets).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()), - QString::number(stats.events[udt::ConnectionStats::Stats::Retransmission]).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()) + QString::number(stats.retransmittedPackets).rightJustified(CLIENT_STATS_TABLE_HEADERS[++headerIndex].size()) }; // output this line of values From f163bbc0d5c792576ca47d52280c20fcd728aaf1 Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 12 Dec 2018 12:15:44 -0800 Subject: [PATCH 2/4] Sample connections stats every seconds --- assignment-client/src/assets/AssetServer.cpp | 49 ++--- assignment-client/src/audio/AudioMixer.cpp | 2 +- assignment-client/src/avatars/AvatarMixer.cpp | 4 +- .../src/messages/MessagesMixer.cpp | 4 +- interface/resources/qml/+android/Stats.qml | 8 +- interface/resources/qml/Stats.qml | 8 +- interface/src/Application.cpp | 22 +- interface/src/ui/Stats.cpp | 52 ++--- .../networking/src/BandwidthRecorder.cpp | 190 ------------------ libraries/networking/src/BandwidthRecorder.h | 75 ------- libraries/networking/src/LimitedNodeList.cpp | 68 +++++-- libraries/networking/src/LimitedNodeList.h | 16 +- libraries/networking/src/NetworkPeer.cpp | 16 -- libraries/networking/src/NetworkPeer.h | 9 - libraries/networking/src/Node.cpp | 34 ++++ libraries/networking/src/Node.h | 13 ++ libraries/networking/src/PacketReceiver.cpp | 4 - libraries/networking/src/udt/Connection.cpp | 8 + libraries/networking/src/udt/Connection.h | 4 + .../networking/src/udt/ConnectionStats.cpp | 54 +---- .../networking/src/udt/ConnectionStats.h | 7 +- libraries/networking/src/udt/Socket.cpp | 12 +- 22 files changed, 204 insertions(+), 455 deletions(-) delete mode 100644 libraries/networking/src/BandwidthRecorder.cpp delete mode 100644 libraries/networking/src/BandwidthRecorder.h diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index e0772c7aea..cad6a852cb 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -915,59 +915,52 @@ void AssetServer::handleAssetUpload(QSharedPointer message, Sha void AssetServer::sendStatsPacket() { QJsonObject serverStats; - auto stats = DependencyManager::get()->sampleStatsForAllConnections(); + auto nodeList = DependencyManager::get(); + nodeList->eachNode([&](auto& node) { + auto& stats = node->getConnectionStats(); - for (const auto& stat : stats) { QJsonObject nodeStats; - auto endTimeMs = std::chrono::duration_cast(stat.second.endTime); + auto endTimeMs = std::chrono::duration_cast(stats.endTime); QDateTime date = QDateTime::fromMSecsSinceEpoch(endTimeMs.count()); static const float USEC_PER_SEC = 1000000.0f; static const float MEGABITS_PER_BYTE = 8.0f / 1000000.0f; // Bytes => Mbits - float elapsed = (float)(stat.second.endTime - stat.second.startTime).count() / USEC_PER_SEC; // sec + float elapsed = (float)(stats.endTime - stats.startTime).count() / USEC_PER_SEC; // sec float megabitsPerSecPerByte = MEGABITS_PER_BYTE / elapsed; // Bytes => Mb/s QJsonObject connectionStats; connectionStats["1. Last Heard"] = date.toString(); - connectionStats["2. Est. Max (P/s)"] = stat.second.estimatedBandwith; - connectionStats["3. RTT (ms)"] = stat.second.rtt; - connectionStats["4. CW (P)"] = stat.second.congestionWindowSize; - connectionStats["5. Period (us)"] = stat.second.packetSendPeriod; - connectionStats["6. Up (Mb/s)"] = stat.second.sentBytes * megabitsPerSecPerByte; - connectionStats["7. Down (Mb/s)"] = stat.second.receivedBytes * megabitsPerSecPerByte; + connectionStats["2. Est. Max (P/s)"] = stats.estimatedBandwith; + connectionStats["3. RTT (ms)"] = stats.rtt; + connectionStats["4. CW (P)"] = stats.congestionWindowSize; + connectionStats["5. Period (us)"] = stats.packetSendPeriod; + connectionStats["6. Up (Mb/s)"] = stats.sentBytes * megabitsPerSecPerByte; + connectionStats["7. Down (Mb/s)"] = stats.receivedBytes * megabitsPerSecPerByte; nodeStats["Connection Stats"] = connectionStats; using Events = udt::ConnectionStats::Stats::Event; - const auto& events = stat.second.events; + const auto& events = stats.events; QJsonObject upstreamStats; - upstreamStats["1. Sent (P/s)"] = stat.second.sendRate; - upstreamStats["2. Sent Packets"] = stat.second.sentPackets; + upstreamStats["1. Sent (P/s)"] = stats.sendRate; + upstreamStats["2. Sent Packets"] = (int)stats.sentPackets; upstreamStats["3. Recvd ACK"] = events[Events::ReceivedACK]; upstreamStats["4. Procd ACK"] = events[Events::ProcessedACK]; - upstreamStats["5. Retransmitted"] = stat.second.retransmittedPackets; + upstreamStats["5. Retransmitted"] = (int)stats.retransmittedPackets; nodeStats["Upstream Stats"] = upstreamStats; QJsonObject downstreamStats; - downstreamStats["1. Recvd (P/s)"] = stat.second.receiveRate; - downstreamStats["2. Recvd Packets"] = stat.second.receivedPackets; + downstreamStats["1. Recvd (P/s)"] = stats.receiveRate; + downstreamStats["2. Recvd Packets"] = (int)stats.receivedPackets; downstreamStats["3. Sent ACK"] = events[Events::SentACK]; - downstreamStats["4. Duplicates"] = stat.second.duplicatePackets; + downstreamStats["4. Duplicates"] = (int)stats.duplicatePackets; nodeStats["Downstream Stats"] = downstreamStats; - QString uuid; - auto nodelist = DependencyManager::get(); - if (stat.first == nodelist->getDomainHandler().getSockAddr()) { - uuid = uuidStringWithoutCurlyBraces(nodelist->getDomainHandler().getUUID()); - nodeStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = "DomainServer"; - } else { - auto node = nodelist->findNodeWithAddr(stat.first); - uuid = uuidStringWithoutCurlyBraces(node ? node->getUUID() : QUuid()); - nodeStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuid; - } + QString uuid = uuidStringWithoutCurlyBraces(node->getUUID()); + nodeStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuid; serverStats[uuid] = nodeStats; - } + }); // send off the stats packets ThreadedAssignment::addPacketStatsAndSendStatsPacket(serverStats); diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 77f416f31e..004e4ad2ea 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -338,7 +338,7 @@ void AudioMixer::sendStatsPacket() { QJsonObject nodeStats; QString uuidString = uuidStringWithoutCurlyBraces(node->getUUID()); - nodeStats["outbound_kbps"] = node->getOutboundBandwidth(); + nodeStats["outbound_kbps"] = node->getOutboundKbps(); nodeStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidString; nodeStats["jitter"] = clientData->getAudioStreamStats(); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 53fc13e5cf..5b72616e5f 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -839,8 +839,8 @@ void AvatarMixer::sendStatsPacket() { // add the key to ask the domain-server for a username replacement, if it has it avatarStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); - avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundBandwidth(); - avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundBandwidth(); + avatarStats[NODE_OUTBOUND_KBPS_STAT_KEY] = node->getOutboundKbps(); + avatarStats[NODE_INBOUND_KBPS_STAT_KEY] = node->getInboundKbps(); AvatarMixerClientData* clientData = static_cast(node->getLinkedData()); if (clientData) { diff --git a/assignment-client/src/messages/MessagesMixer.cpp b/assignment-client/src/messages/MessagesMixer.cpp index c11c8f40a0..d2127835f9 100644 --- a/assignment-client/src/messages/MessagesMixer.cpp +++ b/assignment-client/src/messages/MessagesMixer.cpp @@ -75,8 +75,8 @@ void MessagesMixer::sendStatsPacket() { DependencyManager::get()->eachNode([&](const SharedNodePointer& node) { QJsonObject clientStats; clientStats[USERNAME_UUID_REPLACEMENT_STATS_KEY] = uuidStringWithoutCurlyBraces(node->getUUID()); - clientStats["outbound_kbps"] = node->getOutboundBandwidth(); - clientStats["inbound_kbps"] = node->getInboundBandwidth(); + clientStats["outbound_kbps"] = node->getOutboundKbps(); + clientStats["inbound_kbps"] = node->getInboundKbps(); messagesMixerObject[uuidStringWithoutCurlyBraces(node->getUUID())] = clientStats; }); diff --git a/interface/resources/qml/+android/Stats.qml b/interface/resources/qml/+android/Stats.qml index e9a2aa47eb..fe56f3797b 100644 --- a/interface/resources/qml/+android/Stats.qml +++ b/interface/resources/qml/+android/Stats.qml @@ -192,13 +192,13 @@ Item { } StatText { visible: root.expanded; - text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " + - "Silent: " + root.audioSilentInboundPPS + " pps"; + text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " + + root.audioMixerOutPps + "pps"; } StatText { visible: root.expanded; - text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " + - root.audioMixerOutPps + "pps"; + text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " + + "Silent: " + root.audioSilentInboundPPS + " pps"; } StatText { visible: root.expanded; diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 1a29ce87df..a65170ee3b 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -210,13 +210,13 @@ Item { } StatText { visible: root.expanded; - text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " + - "Silent: " + root.audioSilentInboundPPS + " pps"; + text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " + + root.audioMixerOutPps + "pps"; } StatText { visible: root.expanded; - text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " + - root.audioMixerOutPps + "pps"; + text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " + + "Silent: " + root.audioSilentInboundPPS + " pps"; } StatText { visible: root.expanded; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 182ff77098..e5739fd91e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -859,7 +859,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(true); @@ -1574,13 +1573,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(this, SIGNAL(aboutToQuit()), this, SLOT(onAboutToQuit())); - // hook up bandwidth estimator - QSharedPointer bandwidthRecorder = DependencyManager::get(); - connect(nodeList.data(), &LimitedNodeList::dataSent, - bandwidthRecorder.data(), &BandwidthRecorder::updateOutboundData); - connect(nodeList.data(), &LimitedNodeList::dataReceived, - bandwidthRecorder.data(), &BandwidthRecorder::updateInboundData); - // FIXME -- I'm a little concerned about this. connect(myAvatar->getSkeletonModel().get(), &SkeletonModel::skeletonLoaded, this, &Application::checkSkeleton, Qt::QueuedConnection); @@ -2046,15 +2038,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo properties["deadlock_watchdog_maxElapsed"] = (int)DeadlockWatchdogThread::_maxElapsed; properties["deadlock_watchdog_maxElapsedAverage"] = (int)DeadlockWatchdogThread::_maxElapsedAverage; - auto bandwidthRecorder = DependencyManager::get(); - properties["packet_rate_in"] = bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond(); - properties["packet_rate_out"] = bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond(); - properties["kbps_in"] = bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond(); - properties["kbps_out"] = bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond(); - - properties["atp_in_kbps"] = bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AssetServer); - auto nodeList = DependencyManager::get(); + properties["packet_rate_in"] = nodeList->getInboundPPS(); + properties["packet_rate_out"] = nodeList->getOutboundPPS(); + properties["kbps_in"] = nodeList->getInboundKbps(); + properties["kbps_out"] = nodeList->getOutboundKbps(); + SharedNodePointer entityServerNode = nodeList->soloNodeOfType(NodeType::EntityServer); SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); @@ -2065,6 +2054,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo properties["avatar_ping"] = avatarMixerNode ? avatarMixerNode->getPingMs() : -1; properties["asset_ping"] = assetServerNode ? assetServerNode->getPingMs() : -1; properties["messages_ping"] = messagesMixerNode ? messagesMixerNode->getPingMs() : -1; + properties["atp_in_kbps"] = messagesMixerNode ? assetServerNode->getInboundKbps() : 0.0f; auto loadingRequests = ResourceCache::getLoadingRequests(); diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 495e29f986..cb204c9772 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -30,7 +30,6 @@ #include -#include "BandwidthRecorder.h" #include "Menu.h" #include "Util.h" #include "SequenceNumberStats.h" @@ -166,20 +165,25 @@ void Stats::updateStats(bool force) { STAT_UPDATE(collisionPicksUpdated, updatedPicks[PickQuery::Collision]); } - auto bandwidthRecorder = DependencyManager::get(); - STAT_UPDATE(packetInCount, (int)bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); - STAT_UPDATE(packetOutCount, (int)bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); - STAT_UPDATE_FLOAT(mbpsIn, (float)bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond() / 1000.0f, 0.01f); - STAT_UPDATE_FLOAT(mbpsOut, (float)bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond() / 1000.0f, 0.01f); + STAT_UPDATE(packetInCount, nodeList->getInboundPPS()); + STAT_UPDATE(packetOutCount, nodeList->getOutboundPPS()); + STAT_UPDATE_FLOAT(mbpsIn, nodeList->getInboundKbps() / 1000.0f, 0.01f); + STAT_UPDATE_FLOAT(mbpsOut, nodeList->getOutboundKbps() / 1000.0f, 0.01f); - STAT_UPDATE_FLOAT(assetMbpsIn, (float)bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AssetServer) / 1000.0f, 0.01f); - STAT_UPDATE_FLOAT(assetMbpsOut, (float)bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AssetServer) / 1000.0f, 0.01f); - - // Second column: ping SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); SharedNodePointer avatarMixerNode = nodeList->soloNodeOfType(NodeType::AvatarMixer); SharedNodePointer assetServerNode = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer messageMixerNode = nodeList->soloNodeOfType(NodeType::MessagesMixer); + + if (assetServerNode) { + STAT_UPDATE_FLOAT(assetMbpsIn, assetServerNode->getInboundKbps() / 1000.0f, 0.01f); + STAT_UPDATE_FLOAT(assetMbpsOut, assetServerNode->getOutboundKbps() / 1000.0f, 0.01f); + } else { + STAT_UPDATE_FLOAT(assetMbpsIn, 0.0f, 0.01f); + STAT_UPDATE_FLOAT(assetMbpsOut, 0.0f, 0.01f); + } + + // Second column: ping STAT_UPDATE(audioPing, audioMixerNode ? audioMixerNode->getPingMs() : -1); const int mixerLossRate = (int)roundf(_audioStats->data()->getMixerStream()->lossRateWindow() * 100.0f); const int clientLossRate = (int)roundf(_audioStats->data()->getClientStream()->lossRateWindow() * 100.0f); @@ -198,7 +202,7 @@ void Stats::updateStats(bool force) { // TODO: this should also support entities if (node->getType() == NodeType::EntityServer) { totalPingOctree += node->getPingMs(); - totalEntityKbps += node->getInboundBandwidth(); + totalEntityKbps += node->getInboundKbps(); octreeServerCount++; if (pingOctreeMax < node->getPingMs()) { pingOctreeMax = node->getPingMs(); @@ -218,10 +222,10 @@ void Stats::updateStats(bool force) { if (_expanded || force) { SharedNodePointer avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); if (avatarMixer) { - STAT_UPDATE(avatarMixerInKbps, (int)roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AvatarMixer))); - STAT_UPDATE(avatarMixerInPps, (int)roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer))); - STAT_UPDATE(avatarMixerOutKbps, (int)roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer))); - STAT_UPDATE(avatarMixerOutPps, (int)roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer))); + STAT_UPDATE(avatarMixerInKbps, (int)roundf(avatarMixer->getInboundKbps())); + STAT_UPDATE(avatarMixerInPps, avatarMixer->getInboundPPS()); + STAT_UPDATE(avatarMixerOutKbps, (int)roundf(avatarMixer->getOutboundKbps())); + STAT_UPDATE(avatarMixerOutPps, avatarMixer->getOutboundPPS()); } else { STAT_UPDATE(avatarMixerInKbps, -1); STAT_UPDATE(avatarMixerInPps, -1); @@ -233,17 +237,15 @@ void Stats::updateStats(bool force) { SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer); auto audioClient = DependencyManager::get().data(); if (audioMixerNode || force) { - STAT_UPDATE(audioMixerKbps, (int)roundf( - bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) + - bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); - STAT_UPDATE(audioMixerPps, (int)roundf( - bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) + - bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); + STAT_UPDATE(audioMixerKbps, (int)roundf(audioMixerNode->getInboundKbps() + + audioMixerNode->getOutboundKbps())); + STAT_UPDATE(audioMixerPps, audioMixerNode->getInboundPPS() + + audioMixerNode->getOutboundPPS()); - STAT_UPDATE(audioMixerInKbps, (int)roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer))); - STAT_UPDATE(audioMixerInPps, (int)roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer))); - STAT_UPDATE(audioMixerOutKbps, (int)roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer))); - STAT_UPDATE(audioMixerOutPps, (int)roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer))); + STAT_UPDATE(audioMixerInKbps, (int)roundf(audioMixerNode->getInboundKbps())); + STAT_UPDATE(audioMixerInPps, audioMixerNode->getInboundPPS()); + STAT_UPDATE(audioMixerOutKbps, (int)roundf(audioMixerNode->getOutboundKbps())); + STAT_UPDATE(audioMixerOutPps, audioMixerNode->getOutboundPPS()); STAT_UPDATE(audioAudioInboundPPS, (int)audioClient->getAudioInboundPPS()); STAT_UPDATE(audioSilentInboundPPS, (int)audioClient->getSilentInboundPPS()); STAT_UPDATE(audioOutboundPPS, (int)audioClient->getAudioOutboundPPS()); diff --git a/libraries/networking/src/BandwidthRecorder.cpp b/libraries/networking/src/BandwidthRecorder.cpp deleted file mode 100644 index 80276dba5a..0000000000 --- a/libraries/networking/src/BandwidthRecorder.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// -// BandwidthMeter.cpp -// interface/src/ui -// -// Created by Seth Alves on 2015-1-30 -// Copyright 2015 High Fidelity, Inc. -// -// Based on code by Tobias Schwinger -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "BandwidthRecorder.h" - -#include - -BandwidthRecorder::Channel::Channel() { -} - -float BandwidthRecorder::Channel::getAverageInputPacketsPerSecond() const { - float averageTimeBetweenPackets = _input.getEventDeltaAverage(); - if (averageTimeBetweenPackets > 0.0f) { - return (1.0f / averageTimeBetweenPackets); - } - return 0.0f; -} - -float BandwidthRecorder::Channel::getAverageOutputPacketsPerSecond() const { - float averageTimeBetweenPackets = _output.getEventDeltaAverage(); - if (averageTimeBetweenPackets > 0.0f) { - return (1.0f / averageTimeBetweenPackets); - } - return 0.0f; -} - -float BandwidthRecorder::Channel::getAverageInputKilobitsPerSecond() const { - return (_input.getAverageSampleValuePerSecond() * (8.0f / 1000)); -} - -float BandwidthRecorder::Channel::getAverageOutputKilobitsPerSecond() const { - return (_output.getAverageSampleValuePerSecond() * (8.0f / 1000)); -} - - -void BandwidthRecorder::Channel::updateInputAverage(const float sample) { - _input.updateAverage(sample); -} - -void BandwidthRecorder::Channel::updateOutputAverage(const float sample) { - _output.updateAverage(sample); -} - -BandwidthRecorder::BandwidthRecorder() { - for (uint i=0; iupdateInputAverage(sample); -} - -void BandwidthRecorder::updateOutboundData(const quint8 channelType, const int sample) { - if (! _channels[channelType]) { - _channels[channelType] = new Channel(); - } - _channels[channelType]->updateOutputAverage(sample); -} - -float BandwidthRecorder::getAverageInputPacketsPerSecond(const quint8 channelType) const { - if (! _channels[channelType]) { - return 0.0f; - } - return _channels[channelType]->getAverageInputPacketsPerSecond(); -} - -float BandwidthRecorder::getAverageOutputPacketsPerSecond(const quint8 channelType) const { - if (! _channels[channelType]) { - return 0.0f; - } - return _channels[channelType]->getAverageOutputPacketsPerSecond(); -} - -float BandwidthRecorder::getAverageInputKilobitsPerSecond(const quint8 channelType) const { - if (! _channels[channelType]) { - return 0.0f; - } - return _channels[channelType]->getAverageInputKilobitsPerSecond(); -} - -float BandwidthRecorder::getAverageOutputKilobitsPerSecond(const quint8 channelType) const { - if (! _channels[channelType]) { - return 0.0f; - } - return _channels[channelType]->getAverageOutputKilobitsPerSecond(); -} - -float BandwidthRecorder::getTotalAverageInputPacketsPerSecond() const { - float result = 0.0f; - for (uint i=0; igetAverageInputPacketsPerSecond(); - } - } - return result; -} - -float BandwidthRecorder::getTotalAverageOutputPacketsPerSecond() const { - float result = 0.0f; - for (uint i=0; igetAverageOutputPacketsPerSecond(); - } - } - return result; -} - -float BandwidthRecorder::getTotalAverageInputKilobitsPerSecond() const { - float result = 0.0f; - for (uint i=0; igetAverageInputKilobitsPerSecond(); - } - } - return result; -} - -float BandwidthRecorder::getTotalAverageOutputKilobitsPerSecond() const { - float result = 0.0f; - for (uint i=0; igetAverageOutputKilobitsPerSecond(); - } - } - return result; -} - -float BandwidthRecorder::getCachedTotalAverageInputPacketsPerSecond() const { - static qint64 lastCalculated = 0; - static float cachedValue = 0.0f; - qint64 now = QDateTime::currentMSecsSinceEpoch(); - if (now - lastCalculated > 1000.0f) { - lastCalculated = now; - cachedValue = getTotalAverageInputPacketsPerSecond(); - } - return cachedValue; -} - -float BandwidthRecorder::getCachedTotalAverageOutputPacketsPerSecond() const { - static qint64 lastCalculated = 0; - static float cachedValue = 0.0f; - qint64 now = QDateTime::currentMSecsSinceEpoch(); - if (now - lastCalculated > 1000.0f) { - lastCalculated = now; - cachedValue = getTotalAverageOutputPacketsPerSecond(); - } - return cachedValue; -} - -float BandwidthRecorder::getCachedTotalAverageInputKilobitsPerSecond() const { - static qint64 lastCalculated = 0; - static float cachedValue = 0.0f; - qint64 now = QDateTime::currentMSecsSinceEpoch(); - if (now - lastCalculated > 1000.0f) { - lastCalculated = now; - cachedValue = getTotalAverageInputKilobitsPerSecond(); - } - return cachedValue; -} - -float BandwidthRecorder::getCachedTotalAverageOutputKilobitsPerSecond() const { - static qint64 lastCalculated = 0; - static float cachedValue = 0.0f; - qint64 now = QDateTime::currentMSecsSinceEpoch(); - if (now - lastCalculated > 1000.0f) { - lastCalculated = now; - cachedValue = getTotalAverageOutputKilobitsPerSecond(); - } - return cachedValue; -} diff --git a/libraries/networking/src/BandwidthRecorder.h b/libraries/networking/src/BandwidthRecorder.h deleted file mode 100644 index b1cee570f2..0000000000 --- a/libraries/networking/src/BandwidthRecorder.h +++ /dev/null @@ -1,75 +0,0 @@ -// -// BandwidthRecorder.h -// -// Created by Seth Alves on 2015-1-30 -// Copyright 2015 High Fidelity, Inc. -// -// Based on code by Tobias Schwinger -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - - -#ifndef hifi_BandwidthRecorder_h -#define hifi_BandwidthRecorder_h - -#include -#include -#include "DependencyManager.h" -#include "SimpleMovingAverage.h" - - -class BandwidthRecorder : public QObject, public Dependency { - Q_OBJECT - SINGLETON_DEPENDENCY - -public: - BandwidthRecorder(); - ~BandwidthRecorder(); - - // keep track of data rate in two directions as well as units and style to use during display - class Channel { - public: - Channel(); - float getAverageInputPacketsPerSecond() const; - float getAverageOutputPacketsPerSecond() const; - float getAverageInputKilobitsPerSecond() const; - float getAverageOutputKilobitsPerSecond() const; - - void updateInputAverage(const float sample); - void updateOutputAverage(const float sample); - - private: - SimpleMovingAverage _input; - SimpleMovingAverage _output; - }; - - float getAverageInputPacketsPerSecond(const quint8 channelType) const; - float getAverageOutputPacketsPerSecond(const quint8 channelType) const; - float getAverageInputKilobitsPerSecond(const quint8 channelType) const; - float getAverageOutputKilobitsPerSecond(const quint8 channelType) const; - - float getTotalAverageInputPacketsPerSecond() const; - float getTotalAverageOutputPacketsPerSecond() const; - float getTotalAverageInputKilobitsPerSecond() const; - float getTotalAverageOutputKilobitsPerSecond() const; - - float getCachedTotalAverageInputPacketsPerSecond() const; - float getCachedTotalAverageOutputPacketsPerSecond() const; - float getCachedTotalAverageInputKilobitsPerSecond() const; - float getCachedTotalAverageOutputKilobitsPerSecond() const; - - -private: - // one for each possible Node type - static const unsigned int CHANNEL_COUNT = 256; - Channel* _channels[CHANNEL_COUNT]; - - -public slots: - void updateInboundData(const quint8 channelType, const int bytes); - void updateOutboundData(const quint8 channelType, const int bytes); -}; - -#endif diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index a1137b785a..063885e782 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -83,6 +83,11 @@ LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) : connect(silentNodeTimer, &QTimer::timeout, this, &LimitedNodeList::removeSilentNodes); silentNodeTimer->start(NODE_SILENCE_THRESHOLD_MSECS); + const int CONNECTION_STATS_SAMPLE_INTERVAL_MSECS = 1000; + QTimer* statsSampleTimer = new QTimer(this); + connect(statsSampleTimer, &QTimer::timeout, this, &LimitedNodeList::sampleConnectionStats); + statsSampleTimer->start(CONNECTION_STATS_SAMPLE_INTERVAL_MSECS); + // check the local socket right now updateLocalSocket(); @@ -295,7 +300,6 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe }); if (sendingNodeType != NodeType::Unassigned) { - emit dataReceived(sendingNodeType, packet.getPayloadSize()); return true; } else { HIFI_FCDEBUG(networking(), "Replicated packet of type" << headerType @@ -303,9 +307,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe return false; } - } else { - emit dataReceived(NodeType::Unassigned, packet.getPayloadSize()); return true; } } else { @@ -328,8 +330,6 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe packet.getSenderSockAddr() == getDomainSockAddr() && PacketTypeEnum::getDomainSourcedPackets().contains(headerType)) { // This is a packet sourced by the domain server - - emit dataReceived(NodeType::Unassigned, packet.getPayloadSize()); return true; } @@ -367,8 +367,6 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe // from this sending node sourceNode->setLastHeardMicrostamp(usecTimestampNow()); - emit dataReceived(sourceNode->getType(), packet.getPayloadSize()); - return true; } else { @@ -407,9 +405,6 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node& return 0; } - emit dataSent(destinationNode.getType(), packet.getDataSize()); - destinationNode.recordBytesSent(packet.getDataSize()); - return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getAuthenticateHash()); } @@ -430,9 +425,6 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node& auto activeSocket = destinationNode.getActiveSocket(); if (activeSocket) { - emit dataSent(destinationNode.getType(), packet->getDataSize()); - destinationNode.recordBytesSent(packet->getDataSize()); - return sendPacket(std::move(packet), *activeSocket, destinationNode.getAuthenticateHash()); } else { qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending"; @@ -470,8 +462,6 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi bytesSent += sendPacket(packetList.takeFront(), *activeSocket, connectionHash); } - - emit dataSent(destinationNode.getType(), bytesSent); return bytesSent; } else { qCDebug(networking) << "LimitedNodeList::sendPacketList called without active socket for node" << destinationNode @@ -887,10 +877,56 @@ void LimitedNodeList::removeSilentNodes() { } } +void LimitedNodeList::sampleConnectionStats() { + uint32_t packetsIn { 0 }; + uint32_t packetsOut { 0 }; + uint64_t bytesIn { 0 }; + uint64_t bytesOut { 0 }; + int elapsedSum { 0 }; + int elapsedCount { 0 }; + + auto allStats = _nodeSocket.sampleStatsForAllConnections(); + for (const auto& stats : allStats) { + auto node = findNodeWithAddr(stats.first); + if (node && node->getActiveSocket() && + *node->getActiveSocket() == stats.first) { + node->updateStats(stats.second); + } + + packetsIn += stats.second.receivedPackets; + packetsIn += stats.second.receivedUnreliablePackets; + packetsOut += stats.second.sentPackets; + packetsOut += stats.second.sentUnreliablePackets; + bytesIn += stats.second.receivedBytes; + bytesIn += stats.second.receivedUnreliableBytes; + bytesOut += stats.second.sentBytes; + bytesOut += stats.second.sentUnreliableBytes; + elapsedSum += (stats.second.endTime - stats.second.startTime).count(); + elapsedCount++; + } + + if (elapsedCount > 0) { + float elapsedAvg = (float)elapsedSum / elapsedCount; + float factor = USECS_PER_SECOND / elapsedAvg; + + float kilobitsReceived = (float)bytesIn * BITS_IN_BYTE / BYTES_PER_KILOBYTE; + float kilobitsSent = (float)bytesOut * BITS_IN_BYTE / BYTES_PER_KILOBYTE; + + _inboundPPS = packetsIn * factor; + _outboundPPS = packetsOut * factor; + _inboundKbps = kilobitsReceived * factor; + _outboundKbps = kilobitsSent * factor; + } else { + _inboundPPS = 0; + _outboundPPS = 0; + _inboundKbps = 0.0f; + _outboundKbps = 0.0f; + } +} + const uint32_t RFC_5389_MAGIC_COOKIE = 0x2112A442; const int NUM_BYTES_STUN_HEADER = 20; - void LimitedNodeList::makeSTUNRequestPacket(char* stunRequestPacket) { int packetIndex = 0; diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 5be9ff477c..78d4d5810f 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -319,6 +319,11 @@ public: void sendFakedHandshakeRequestToNode(SharedNodePointer node); #endif + int getInboundPPS() const { return _inboundPPS; } + int getOutboundPPS() const { return _outboundPPS; } + float getInboundKbps() const { return _inboundKbps; } + float getOutboundKbps() const { return _outboundKbps; } + public slots: void reset(); void eraseAllNodes(); @@ -332,10 +337,10 @@ public slots: bool killNodeWithUUID(const QUuid& nodeUUID, ConnectionID newConnectionID = NULL_CONNECTION_ID); -signals: - void dataSent(quint8 channelType, int bytes); - void dataReceived(quint8 channelType, int bytes); +private slots: + void sampleConnectionStats(); +signals: // QUuid might be zero for non-sourced packet types. void packetVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID); @@ -442,6 +447,11 @@ private: LocalIDMapping _localIDMap; Node::LocalID _sessionLocalID { 0 }; bool _flagTimeForConnectionStep { false }; // only keep track in interface + + int _inboundPPS { 0 }; + int _outboundPPS { 0 }; + float _inboundKbps { 0.0f }; + float _outboundKbps { 0.0f }; }; #endif // hifi_LimitedNodeList_h diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index 35956c4789..4e0a82ba0e 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -228,19 +228,3 @@ QDebug operator<<(QDebug debug, const NetworkPeer &peer) { << "- local:" << peer.getLocalSocket(); return debug; } - -void NetworkPeer::recordBytesSent(int count) const { - _bandwidthRecorder.updateOutboundData(0, count); -} - -void NetworkPeer::recordBytesReceived(int count) const { - _bandwidthRecorder.updateInboundData(0, count); -} - -float NetworkPeer::getOutboundBandwidth() const { - return _bandwidthRecorder.getAverageOutputKilobitsPerSecond(0); -} - -float NetworkPeer::getInboundBandwidth() const { - return _bandwidthRecorder.getAverageInputKilobitsPerSecond(0); -} diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index 4688498a96..b75d2f8b86 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -18,7 +18,6 @@ #include #include -#include "BandwidthRecorder.h" #include "HifiSockAddr.h" #include "UUID.h" @@ -78,12 +77,6 @@ public: void incrementConnectionAttempts() { ++_connectionAttempts; } void resetConnectionAttempts() { _connectionAttempts = 0; } - void recordBytesSent(int count) const; - void recordBytesReceived(int count) const; - - float getOutboundBandwidth() const; // in kbps - float getInboundBandwidth() const; // in kbps - // Typically the LimitedNodeList removes nodes after they are "silent" // meaning that we have not received any packets (including simple keepalive pings) from them for a set interval. // The _isForcedNeverSilent flag tells the LimitedNodeList that a Node should never be killed by removeSilentNodes() @@ -114,8 +107,6 @@ protected: HifiSockAddr _symmetricSocket; HifiSockAddr* _activeSocket; - mutable BandwidthRecorder _bandwidthRecorder; - quint64 _wakeTimestamp; std::atomic_ullong _lastHeardMicrostamp; diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 9421e1da44..a2bd60914a 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -219,3 +219,37 @@ void Node::setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; _authenticateHash->setKey(_connectionSecret); } + +void Node::updateStats(Stats stats) { + _stats = stats; +} + +const Node::Stats& Node::getConnectionStats() const { + return _stats; +} + +float Node::getInboundKbps() const { + float bitsReceived = (_stats.receivedBytes + _stats.receivedUnreliableBytes) * BITS_IN_BYTE; + auto elapsed = _stats.endTime - _stats.startTime; + auto bps = (bitsReceived * USECS_PER_SECOND) / elapsed.count(); + return bps / BYTES_PER_KILOBYTE; +} + +float Node::getOutboundKbps() const { + float bitsSent = (_stats.sentBytes + _stats.sentUnreliableBytes) * BITS_IN_BYTE; + auto elapsed = _stats.endTime - _stats.startTime; + auto bps = (bitsSent * USECS_PER_SECOND) / elapsed.count(); + return bps / BYTES_PER_KILOBYTE; +} + +int Node::getInboundPPS() const { + float packetsReceived = _stats.receivedPackets + _stats.receivedUnreliablePackets; + auto elapsed = _stats.endTime - _stats.startTime; + return (packetsReceived * USECS_PER_SECOND) / elapsed.count(); +} + +int Node::getOutboundPPS() const { + float packetsSent = _stats.sentPackets + _stats.sentUnreliablePackets; + auto elapsed = _stats.endTime - _stats.startTime; + return (packetsSent * USECS_PER_SECOND) / elapsed.count(); +} diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 6c5a56c94e..fe3177d785 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -35,10 +35,13 @@ #include "MovingPercentile.h" #include "NodePermissions.h" #include "HMACAuth.h" +#include "udt/ConnectionStats.h" +#include "NumericalConstants.h" class Node : public NetworkPeer { Q_OBJECT public: + using Stats = udt::ConnectionStats::Stats; Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, @@ -94,6 +97,14 @@ public: friend QDataStream& operator<<(QDataStream& out, const Node& node); friend QDataStream& operator>>(QDataStream& in, Node& node); + void updateStats(Stats stats); + const Stats& getConnectionStats() const; + + int getInboundPPS() const; + int getOutboundPPS() const; + float getInboundKbps() const; + float getOutboundKbps() const; + private: // privatize copy and assignment operator to disallow Node copying Node(const Node &otherNode); @@ -115,6 +126,8 @@ private: IgnoredNodeIDs _ignoredNodeIDs; mutable QReadWriteLock _ignoredNodeIDSetLock; std::vector _replicatedUsernames { }; + + Stats _stats; }; Q_DECLARE_METATYPE(Node*) diff --git a/libraries/networking/src/PacketReceiver.cpp b/libraries/networking/src/PacketReceiver.cpp index fe2a273d61..83be481914 100644 --- a/libraries/networking/src/PacketReceiver.cpp +++ b/libraries/networking/src/PacketReceiver.cpp @@ -284,10 +284,6 @@ void PacketReceiver::handleVerifiedMessage(QSharedPointer recei connectionType = _directlyConnectedObjects.contains(listener.object) ? Qt::DirectConnection : Qt::AutoConnection; } - if (matchingNode) { - matchingNode->recordBytesReceived(receivedMessage->getSize()); - } - QMetaMethod metaMethod = listener.method; static const QByteArray QSHAREDPOINTER_NODE_NORMALIZED = QMetaObject::normalizedType("QSharedPointer"); diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index d87f6fd35d..317c5e6255 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -199,6 +199,14 @@ void Connection::recordRetransmission(int wireSize, int payloadSize, _congestionControl->onPacketReSent(wireSize, seqNum, timePoint); } +void Connection::recordSentUnreliablePackets(int wireSize, int payloadSize) { + _stats.recordUnreliableSentPackets(payloadSize, wireSize); +} + +void Connection::recordRecievedUnreliablePackets(int wireSize, int payloadSize) { + _stats.recordUnreliableReceivedPackets(payloadSize, wireSize); +} + void Connection::sendACK() { SequenceNumber nextACKNumber = nextACK(); diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index 5a482dd200..34b01c3394 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -73,6 +73,9 @@ public: void setMaxBandwidth(int maxBandwidth); void sendHandshakeRequest(); + + void recordSentUnreliablePackets(int wireSize, int payloadSize); + void recordRecievedUnreliablePackets(int wireSize, int payloadSize); signals: void packetSent(); @@ -81,6 +84,7 @@ signals: private slots: void recordSentPackets(int wireSize, int payloadSize, SequenceNumber seqNum, p_high_resolution_clock::time_point timePoint); void recordRetransmission(int wireSize, int payloadSize, SequenceNumber sequenceNumber, p_high_resolution_clock::time_point timePoint); + void queueInactive(); void queueTimeout(); diff --git a/libraries/networking/src/udt/ConnectionStats.cpp b/libraries/networking/src/udt/ConnectionStats.cpp index f0bfbde067..4f0722edd3 100644 --- a/libraries/networking/src/udt/ConnectionStats.cpp +++ b/libraries/networking/src/udt/ConnectionStats.cpp @@ -19,7 +19,6 @@ using namespace std::chrono; ConnectionStats::ConnectionStats() { auto now = duration_cast(system_clock::now().time_since_epoch()); _currentSample.startTime = now; - _total.startTime = now; } ConnectionStats::Stats ConnectionStats::sample() { @@ -35,101 +34,50 @@ ConnectionStats::Stats ConnectionStats::sample() { void ConnectionStats::record(Stats::Event event) { ++_currentSample.events[(int) event]; - ++_total.events[(int) event]; } void ConnectionStats::recordSentPackets(int payload, int total) { ++_currentSample.sentPackets; - ++_total.sentPackets; - _currentSample.sentUtilBytes += payload; - _total.sentUtilBytes += payload; - _currentSample.sentBytes += total; - _total.sentBytes += total; } void ConnectionStats::recordReceivedPackets(int payload, int total) { ++_currentSample.receivedPackets; - ++_total.receivedPackets; - _currentSample.receivedUtilBytes += payload; - _total.receivedUtilBytes += payload; - _currentSample.receivedBytes += total; - _total.receivedBytes += total; } void ConnectionStats::recordRetransmittedPackets(int payload, int total) { ++_currentSample.retransmittedPackets; - ++_total.retransmittedPackets; - _currentSample.retransmittedUtilBytes += payload; - _total.retransmittedUtilBytes += payload; - _currentSample.retransmittedBytes += total; - _total.retransmittedBytes += total; } void ConnectionStats::recordDuplicatePackets(int payload, int total) { ++_currentSample.duplicatePackets; - ++_total.duplicatePackets; - _currentSample.duplicateUtilBytes += payload; - _total.duplicateUtilBytes += payload; - _currentSample.duplicateBytes += total; - _total.duplicateBytes += total; } void ConnectionStats::recordUnreliableSentPackets(int payload, int total) { ++_currentSample.sentUnreliablePackets; - ++_total.sentUnreliablePackets; - _currentSample.sentUnreliableUtilBytes += payload; - _total.sentUnreliableUtilBytes += payload; - _currentSample.sentUnreliableBytes += total; - _total.sentUnreliableBytes += total; } void ConnectionStats::recordUnreliableReceivedPackets(int payload, int total) { ++_currentSample.receivedUnreliablePackets; - ++_total.receivedUnreliablePackets; - _currentSample.receivedUnreliableUtilBytes += payload; - _total.receivedUnreliableUtilBytes += payload; - - _currentSample.sentUnreliableBytes += total; - _total.receivedUnreliableBytes += total; -} - -static const double EWMA_CURRENT_SAMPLE_WEIGHT = 0.125; -static const double EWMA_PREVIOUS_SAMPLES_WEIGHT = 1.0 - EWMA_CURRENT_SAMPLE_WEIGHT; - -void ConnectionStats::recordSendRate(int sample) { - _currentSample.sendRate = sample; - _total.sendRate = (int)((_total.sendRate * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT)); -} - -void ConnectionStats::recordReceiveRate(int sample) { - _currentSample.receiveRate = sample; - _total.receiveRate = (int)((_total.receiveRate * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT)); -} - -void ConnectionStats::recordRTT(int sample) { - _currentSample.rtt = sample; - _total.rtt = (int)((_total.rtt * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT)); + _currentSample.receivedUnreliableBytes += total; } void ConnectionStats::recordCongestionWindowSize(int sample) { _currentSample.congestionWindowSize = sample; - _total.congestionWindowSize = (int)((_total.congestionWindowSize * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT)); } void ConnectionStats::recordPacketSendPeriod(int sample) { _currentSample.packetSendPeriod = sample; - _total.packetSendPeriod = (int)((_total.packetSendPeriod * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT)); } QDebug& operator<<(QDebug&& debug, const udt::ConnectionStats::Stats& stats) { diff --git a/libraries/networking/src/udt/ConnectionStats.h b/libraries/networking/src/udt/ConnectionStats.h index 0be4778323..b7350d4341 100644 --- a/libraries/networking/src/udt/ConnectionStats.h +++ b/libraries/networking/src/udt/ConnectionStats.h @@ -76,7 +76,6 @@ public: ConnectionStats(); Stats sample(); - Stats getTotalStats(); void record(Stats::Event event); @@ -88,16 +87,12 @@ public: void recordUnreliableSentPackets(int payload, int total); void recordUnreliableReceivedPackets(int payload, int total); - - void recordSendRate(int sample); - void recordReceiveRate(int sample); - void recordRTT(int sample); + void recordCongestionWindowSize(int sample); void recordPacketSendPeriod(int sample); private: Stats _currentSample; - Stats _total; }; } diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 25e6fae023..d01fe71486 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -129,6 +129,12 @@ qint64 Socket::writePacket(const Packet& packet, const HifiSockAddr& sockAddr) { sequenceNumber = ++_unreliableSequenceNumbers[sockAddr]; } + auto connection = findOrCreateConnection(sockAddr, true); + if (connection) { + connection->recordSentUnreliablePackets(packet.getWireSize(), + packet.getPayloadSize()); + } + // write the correct sequence number to the Packet here packet.writeSequenceNumber(sequenceNumber); @@ -392,9 +398,10 @@ void Socket::readPendingDatagrams() { // call our verification operator to see if this packet is verified if (!_packetFilterOperator || _packetFilterOperator(*packet)) { + auto connection = findOrCreateConnection(senderSockAddr, true); + if (packet->isReliable()) { // if this was a reliable packet then signal the matching connection with the sequence number - auto connection = findOrCreateConnection(senderSockAddr, true); if (!connection || !connection->processReceivedSequenceNumber(packet->getSequenceNumber(), packet->getDataSize(), @@ -406,6 +413,9 @@ void Socket::readPendingDatagrams() { #endif continue; } + } else if (connection) { + connection->recordRecievedUnreliablePackets(packet->getWireSize(), + packet->getPayloadSize()); } if (packet->isPartOfMessage()) { From 9475a31f1bf2e8fd3a3f0b00bdb125d57092640c Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 13 Dec 2018 14:17:38 -0800 Subject: [PATCH 3/4] Record ACK data in stats --- libraries/networking/src/udt/Connection.cpp | 4 ++-- libraries/networking/src/udt/ConnectionStats.cpp | 10 ++++++++++ libraries/networking/src/udt/ConnectionStats.h | 5 ++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 317c5e6255..2d5ac0218f 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -221,7 +221,7 @@ void Connection::sendACK() { // have the socket send off our packet _parentSocket->writeBasePacket(*_ackPacket, _destination); - _stats.record(ConnectionStats::Stats::SentACK); + _stats.recordSentACK(_ackPacket->getWireSize()); } SequenceNumber Connection::nextACK() const { @@ -327,7 +327,7 @@ void Connection::processACK(ControlPacketPointer controlPacket) { controlPacket->readPrimitive(&ack); // update the total count of received ACKs - _stats.record(ConnectionStats::Stats::ReceivedACK); + _stats.recordReceivedACK(controlPacket->getWireSize()); // validate that this isn't a BS ACK if (ack > getSendQueue().getCurrentSequenceNumber()) { diff --git a/libraries/networking/src/udt/ConnectionStats.cpp b/libraries/networking/src/udt/ConnectionStats.cpp index 4f0722edd3..188cc3114d 100644 --- a/libraries/networking/src/udt/ConnectionStats.cpp +++ b/libraries/networking/src/udt/ConnectionStats.cpp @@ -36,6 +36,16 @@ void ConnectionStats::record(Stats::Event event) { ++_currentSample.events[(int) event]; } +void ConnectionStats::recordSentACK(int size) { + record(Stats::SentACK); + recordSentPackets(0, size); +} + +void ConnectionStats::recordReceivedACK(int size) { + record(Stats::ReceivedACK); + recordReceivedPackets(0, size); +} + void ConnectionStats::recordSentPackets(int payload, int total) { ++_currentSample.sentPackets; _currentSample.sentUtilBytes += payload; diff --git a/libraries/networking/src/udt/ConnectionStats.h b/libraries/networking/src/udt/ConnectionStats.h index b7350d4341..8ff0ec90fd 100644 --- a/libraries/networking/src/udt/ConnectionStats.h +++ b/libraries/networking/src/udt/ConnectionStats.h @@ -78,7 +78,10 @@ public: Stats sample(); void record(Stats::Event event); - + + void recordSentACK(int size); + void recordReceivedACK(int size); + void recordSentPackets(int payload, int total); void recordReceivedPackets(int payload, int total); From a0ede77e4f7ee9a5d91bb2f48e51d4f202b0a8eb Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 3 Jan 2019 10:04:27 -0800 Subject: [PATCH 4/4] Fix typo --- libraries/networking/src/udt/Connection.cpp | 2 +- libraries/networking/src/udt/Connection.h | 2 +- libraries/networking/src/udt/Socket.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/networking/src/udt/Connection.cpp b/libraries/networking/src/udt/Connection.cpp index 2d5ac0218f..418dc8f417 100644 --- a/libraries/networking/src/udt/Connection.cpp +++ b/libraries/networking/src/udt/Connection.cpp @@ -203,7 +203,7 @@ void Connection::recordSentUnreliablePackets(int wireSize, int payloadSize) { _stats.recordUnreliableSentPackets(payloadSize, wireSize); } -void Connection::recordRecievedUnreliablePackets(int wireSize, int payloadSize) { +void Connection::recordReceivedUnreliablePackets(int wireSize, int payloadSize) { _stats.recordUnreliableReceivedPackets(payloadSize, wireSize); } diff --git a/libraries/networking/src/udt/Connection.h b/libraries/networking/src/udt/Connection.h index 34b01c3394..938ec36860 100644 --- a/libraries/networking/src/udt/Connection.h +++ b/libraries/networking/src/udt/Connection.h @@ -75,7 +75,7 @@ public: void sendHandshakeRequest(); void recordSentUnreliablePackets(int wireSize, int payloadSize); - void recordRecievedUnreliablePackets(int wireSize, int payloadSize); + void recordReceivedUnreliablePackets(int wireSize, int payloadSize); signals: void packetSent(); diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index d01fe71486..358acce694 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -414,7 +414,7 @@ void Socket::readPendingDatagrams() { continue; } } else if (connection) { - connection->recordRecievedUnreliablePackets(packet->getWireSize(), + connection->recordReceivedUnreliablePackets(packet->getWireSize(), packet->getPayloadSize()); }