Introduce debug network stats

Bug: webrtc:11959
Change-Id: I29e94cf1cdc9aee2bbe4396aa14a759c1a9ae560
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/184600
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32170}
diff --git a/api/numerics/samples_stats_counter.h b/api/numerics/samples_stats_counter.h
index 9387e6b..283c1e4 100644
--- a/api/numerics/samples_stats_counter.h
+++ b/api/numerics/samples_stats_counter.h
@@ -45,6 +45,8 @@
 
   // Returns if there are any values in O(1) time.
   bool IsEmpty() const { return samples_.empty(); }
+  // Returns the amount of samples added into counter in O(1) time.
+  int64_t NumSamples() const { return stats_.Size(); }
 
   // Returns min in O(1) time. This function may not be called if there are no
   // samples.
diff --git a/api/test/network_emulation/BUILD.gn b/api/test/network_emulation/BUILD.gn
index 4780da2..fb7bedc 100644
--- a/api/test/network_emulation/BUILD.gn
+++ b/api/test/network_emulation/BUILD.gn
@@ -21,6 +21,7 @@
     "../../../rtc_base",
     "../../../rtc_base:checks",
     "../../../rtc_base:rtc_base_approved",
+    "../../numerics",
     "../../units:data_rate",
     "../../units:data_size",
     "../../units:timestamp",
diff --git a/api/test/network_emulation/network_emulation_interfaces.h b/api/test/network_emulation/network_emulation_interfaces.h
index 7c1a61f..36fb996 100644
--- a/api/test/network_emulation/network_emulation_interfaces.h
+++ b/api/test/network_emulation/network_emulation_interfaces.h
@@ -16,6 +16,7 @@
 
 #include "absl/types/optional.h"
 #include "api/array_view.h"
+#include "api/numerics/samples_stats_counter.h"
 #include "api/units/data_rate.h"
 #include "api/units/data_size.h"
 #include "api/units/timestamp.h"
@@ -69,6 +70,12 @@
 
   virtual DataSize BytesSent() const = 0;
 
+  // Returns the timestamped sizes of all sent packets if
+  // EmulatedEndpointConfig::stats_gatherming_mode was set to
+  // StatsGatheringMode::kDebug; otherwise, the returned value will be empty.
+  // Returned reference is valid until the next call to a non-const method.
+  virtual const SamplesStatsCounter& SentPacketsSizeCounter() const = 0;
+
   virtual DataSize FirstSentPacketSize() const = 0;
 
   // Returns time of the first packet sent or infinite value if no packets were
@@ -91,10 +98,21 @@
   virtual int64_t PacketsReceived() const = 0;
   // Total amount of bytes in received packets.
   virtual DataSize BytesReceived() const = 0;
+  // Returns the timestamped sizes of all received packets if
+  // EmulatedEndpointConfig::stats_gatherming_mode was set to
+  // StatsGatheringMode::kDebug; otherwise, the returned value will be empty.
+  // Returned reference is valid until the next call to a non-const method.
+  virtual const SamplesStatsCounter& ReceivedPacketsSizeCounter() const = 0;
   // Total amount of packets that were received, but no destination was found.
   virtual int64_t PacketsDropped() const = 0;
   // Total amount of bytes in dropped packets.
   virtual DataSize BytesDropped() const = 0;
+  // Returns the timestamped sizes of all packets that were received,
+  // but no destination was found if
+  // EmulatedEndpointConfig::stats_gatherming_mode was set to
+  // StatsGatheringMode::kDebug; otherwise, the returned value will be empty.
+  // Returned reference is valid until the next call to a non-const method.
+  virtual const SamplesStatsCounter& DroppedPacketsSizeCounter() const = 0;
 
   virtual DataSize FirstReceivedPacketSize() const = 0;
 
@@ -120,6 +138,17 @@
   virtual int64_t PacketsSent() const = 0;
 
   virtual DataSize BytesSent() const = 0;
+  // Returns the timestamped sizes of all sent packets if
+  // EmulatedEndpointConfig::stats_gatherming_mode was set to
+  // StatsGatheringMode::kDebug; otherwise, the returned value will be empty.
+  // Returned reference is valid until the next call to a non-const method.
+  virtual const SamplesStatsCounter& SentPacketsSizeCounter() const = 0;
+  // Returns the timestamped duration between packet was received on
+  // network interface and was dispatched to the network in microseconds if
+  // EmulatedEndpointConfig::stats_gatherming_mode was set to
+  // StatsGatheringMode::kDebug; otherwise, the returned value will be empty.
+  // Returned reference is valid until the next call to a non-const method.
+  virtual const SamplesStatsCounter& SentPacketsQueueWaitTimeUs() const = 0;
 
   virtual DataSize FirstSentPacketSize() const = 0;
   // Returns time of the first packet sent or infinite value if no packets were
@@ -134,10 +163,21 @@
   virtual int64_t PacketsReceived() const = 0;
   // Total amount of bytes in received packets.
   virtual DataSize BytesReceived() const = 0;
+  // Returns the timestamped sizes of all received packets if
+  // EmulatedEndpointConfig::stats_gatherming_mode was set to
+  // StatsGatheringMode::kDebug; otherwise, the returned value will be empty.
+  // Returned reference is valid until the next call to a non-const method.
+  virtual const SamplesStatsCounter& ReceivedPacketsSizeCounter() const = 0;
   // Total amount of packets that were received, but no destination was found.
   virtual int64_t PacketsDropped() const = 0;
   // Total amount of bytes in dropped packets.
   virtual DataSize BytesDropped() const = 0;
+  // Returns counter with timestamped sizes of all packets that were received,
+  // but no destination was found if
+  // EmulatedEndpointConfig::stats_gatherming_mode was set to
+  // StatsGatheringMode::kDebug; otherwise, the returned value will be empty.
+  // Returned reference is valid until the next call to a non-const method.
+  virtual const SamplesStatsCounter& DroppedPacketsSizeCounter() const = 0;
 
   virtual DataSize FirstReceivedPacketSize() const = 0;
   // Returns time of the first packet received or infinite value if no packets
diff --git a/api/test/network_emulation_manager.h b/api/test/network_emulation_manager.h
index d2e6417..58ee3bf 100644
--- a/api/test/network_emulation_manager.h
+++ b/api/test/network_emulation_manager.h
@@ -46,6 +46,13 @@
 
 struct EmulatedEndpointConfig {
   enum class IpAddressFamily { kIpv4, kIpv6 };
+  enum class StatsGatheringMode {
+    // Gather main network stats counters.
+    kDefault,
+    // kDefault + also gather per packet statistics. In this mode more memory
+    // will be used.
+    kDebug
+  };
 
   IpAddressFamily generated_ip_family = IpAddressFamily::kIpv4;
   // If specified will be used as IP address for endpoint node. Must be unique
@@ -56,6 +63,7 @@
   bool start_as_enabled = true;
   // Network type which will be used to represent endpoint to WebRTC.
   rtc::AdapterType type = rtc::AdapterType::ADAPTER_TYPE_UNKNOWN;
+  StatsGatheringMode stats_gathering_mode = StatsGatheringMode::kDefault;
 };
 
 
diff --git a/test/network/BUILD.gn b/test/network/BUILD.gn
index 71d0533..a632849 100644
--- a/test/network/BUILD.gn
+++ b/test/network/BUILD.gn
@@ -10,13 +10,13 @@
 
 rtc_library("emulated_network") {
   visibility = [
-    "../../api:create_network_emulation_manager",
     ":*",
+    "../../api:create_network_emulation_manager",
   ]
   if (rtc_include_tests) {
     visibility += [
-      "../scenario:*",
       "../peer_scenario:*",
+      "../scenario:*",
     ]
   }
   testonly = true
@@ -39,6 +39,7 @@
     "../../api:network_emulation_manager_api",
     "../../api:simulated_network_api",
     "../../api:time_controller",
+    "../../api/numerics",
     "../../api/units:data_rate",
     "../../api/units:data_size",
     "../../api/units:time_delta",
@@ -114,6 +115,7 @@
   deps = [
     ":emulated_network",
     "../:test_support",
+    "../../api:network_emulation_manager_api",
     "../../api:simulated_network_api",
     "../../call:simulated_network",
     "../../rtc_base",
diff --git a/test/network/cross_traffic_unittest.cc b/test/network/cross_traffic_unittest.cc
index a3c7b42..c8d848f 100644
--- a/test/network/cross_traffic_unittest.cc
+++ b/test/network/cross_traffic_unittest.cc
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include "absl/memory/memory.h"
+#include "api/test/network_emulation_manager.h"
 #include "api/test/simulated_network.h"
 #include "call/simulated_network.h"
 #include "rtc_base/event.h"
@@ -46,12 +47,14 @@
   SimulatedClock clock{0};
   CountingReceiver counter;
   TaskQueueForTest task_queue_;
-  EmulatedEndpointImpl endpoint{/*id=*/1,
-                                rtc::IPAddress(kTestIpAddress),
-                                /*is_enabled=*/true,
-                                /*type=*/rtc::AdapterType::ADAPTER_TYPE_UNKNOWN,
-                                &task_queue_,
-                                &clock};
+  EmulatedEndpointImpl endpoint{
+      /*id=*/1,
+      rtc::IPAddress(kTestIpAddress),
+      EmulatedEndpointConfig::StatsGatheringMode::kDefault,
+      /*is_enabled=*/true,
+      /*type=*/rtc::AdapterType::ADAPTER_TYPE_UNKNOWN,
+      &task_queue_,
+      &clock};
 };
 
 }  // namespace
diff --git a/test/network/network_emulation.cc b/test/network/network_emulation.cc
index 13d3d87..b969dfc 100644
--- a/test/network/network_emulation.cc
+++ b/test/network/network_emulation.cc
@@ -14,6 +14,7 @@
 #include <limits>
 #include <memory>
 
+#include "api/numerics/samples_stats_counter.h"
 #include "api/units/data_size.h"
 #include "rtc_base/bind.h"
 #include "rtc_base/logging.h"
@@ -78,8 +79,10 @@
   sequence_checker_.Detach();
 }
 
-void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent(Timestamp sent_time,
-                                                       DataSize packet_size) {
+void EmulatedNetworkOutgoingStatsBuilder::OnPacketSent(
+    Timestamp sent_time,
+    DataSize packet_size,
+    EmulatedEndpointConfig::StatsGatheringMode mode) {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   RTC_CHECK_GE(packet_size, DataSize::Zero());
   if (first_packet_sent_time_.IsInfinite()) {
@@ -89,6 +92,9 @@
   last_packet_sent_time_ = sent_time;
   packets_sent_++;
   bytes_sent_ += packet_size;
+  if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) {
+    sent_packets_size_counter_.AddSample(packet_size.bytes());
+  }
 }
 
 void EmulatedNetworkOutgoingStatsBuilder::AddOutgoingStats(
@@ -96,6 +102,7 @@
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   packets_sent_ += stats.PacketsSent();
   bytes_sent_ += stats.BytesSent();
+  sent_packets_size_counter_.AddSamples(stats.SentPacketsSizeCounter());
   if (first_packet_sent_time_ > stats.FirstPacketSentTime()) {
     first_packet_sent_time_ = stats.FirstPacketSentTime();
     first_sent_packet_size_ = stats.FirstSentPacketSize();
@@ -109,8 +116,8 @@
 EmulatedNetworkOutgoingStatsBuilder::Build() const {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   return std::make_unique<EmulatedNetworkOutgoingStatsImpl>(
-      packets_sent_, bytes_sent_, first_sent_packet_size_,
-      first_packet_sent_time_, last_packet_sent_time_);
+      packets_sent_, bytes_sent_, sent_packets_size_counter_,
+      first_sent_packet_size_, first_packet_sent_time_, last_packet_sent_time_);
 }
 
 EmulatedNetworkIncomingStatsBuilder::EmulatedNetworkIncomingStatsBuilder() {
@@ -118,15 +125,20 @@
 }
 
 void EmulatedNetworkIncomingStatsBuilder::OnPacketDropped(
-    DataSize packet_size) {
+    DataSize packet_size,
+    EmulatedEndpointConfig::StatsGatheringMode mode) {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   packets_dropped_++;
   bytes_dropped_ += packet_size;
+  if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) {
+    dropped_packets_size_counter_.AddSample(packet_size.bytes());
+  }
 }
 
 void EmulatedNetworkIncomingStatsBuilder::OnPacketReceived(
     Timestamp received_time,
-    DataSize packet_size) {
+    DataSize packet_size,
+    EmulatedEndpointConfig::StatsGatheringMode mode) {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   RTC_CHECK_GE(packet_size, DataSize::Zero());
   if (first_packet_received_time_.IsInfinite()) {
@@ -136,6 +148,9 @@
   last_packet_received_time_ = received_time;
   packets_received_++;
   bytes_received_ += packet_size;
+  if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) {
+    received_packets_size_counter_.AddSample(packet_size.bytes());
+  }
 }
 
 void EmulatedNetworkIncomingStatsBuilder::AddIncomingStats(
@@ -143,8 +158,10 @@
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   packets_received_ += stats.PacketsReceived();
   bytes_received_ += stats.BytesReceived();
+  received_packets_size_counter_.AddSamples(stats.ReceivedPacketsSizeCounter());
   packets_dropped_ += stats.PacketsDropped();
   bytes_dropped_ += stats.BytesDropped();
+  dropped_packets_size_counter_.AddSamples(stats.DroppedPacketsSizeCounter());
   if (first_packet_received_time_ > stats.FirstPacketReceivedTime()) {
     first_packet_received_time_ = stats.FirstPacketReceivedTime();
     first_received_packet_size_ = stats.FirstReceivedPacketSize();
@@ -158,7 +175,8 @@
 EmulatedNetworkIncomingStatsBuilder::Build() const {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   return std::make_unique<EmulatedNetworkIncomingStatsImpl>(
-      packets_received_, bytes_received_, packets_dropped_, bytes_dropped_,
+      packets_received_, bytes_received_, received_packets_size_counter_,
+      packets_dropped_, bytes_dropped_, dropped_packets_size_counter_,
       first_received_packet_size_, first_packet_received_time_,
       last_packet_received_time_);
 }
@@ -173,26 +191,36 @@
   sequence_checker_.Detach();
 }
 
-void EmulatedNetworkStatsBuilder::OnPacketSent(Timestamp sent_time,
-                                               rtc::IPAddress destination_ip,
-                                               DataSize packet_size) {
+void EmulatedNetworkStatsBuilder::OnPacketSent(
+    Timestamp queued_time,
+    Timestamp sent_time,
+    rtc::IPAddress destination_ip,
+    DataSize packet_size,
+    EmulatedEndpointConfig::StatsGatheringMode mode) {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
-  outgoing_stats_per_destination_[destination_ip].OnPacketSent(sent_time,
-                                                               packet_size);
+  if (mode == EmulatedEndpointConfig::StatsGatheringMode::kDebug) {
+    sent_packets_queue_wait_time_us_.AddSample((sent_time - queued_time).us());
+  }
+  outgoing_stats_per_destination_[destination_ip].OnPacketSent(
+      sent_time, packet_size, mode);
 }
 
-void EmulatedNetworkStatsBuilder::OnPacketDropped(rtc::IPAddress source_ip,
-                                                  DataSize packet_size) {
+void EmulatedNetworkStatsBuilder::OnPacketDropped(
+    rtc::IPAddress source_ip,
+    DataSize packet_size,
+    EmulatedEndpointConfig::StatsGatheringMode mode) {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
-  incoming_stats_per_source_[source_ip].OnPacketDropped(packet_size);
+  incoming_stats_per_source_[source_ip].OnPacketDropped(packet_size, mode);
 }
 
-void EmulatedNetworkStatsBuilder::OnPacketReceived(Timestamp received_time,
-                                                   rtc::IPAddress source_ip,
-                                                   DataSize packet_size) {
+void EmulatedNetworkStatsBuilder::OnPacketReceived(
+    Timestamp received_time,
+    rtc::IPAddress source_ip,
+    DataSize packet_size,
+    EmulatedEndpointConfig::StatsGatheringMode mode) {
   RTC_DCHECK_RUN_ON(&sequence_checker_);
   incoming_stats_per_source_[source_ip].OnPacketReceived(received_time,
-                                                         packet_size);
+                                                         packet_size, mode);
 }
 
 void EmulatedNetworkStatsBuilder::AddEmulatedNetworkStats(
@@ -204,6 +232,9 @@
     local_addresses_.push_back(addr);
   }
 
+  sent_packets_queue_wait_time_us_.AddSamples(
+      stats.SentPacketsQueueWaitTimeUs());
+
   // Add outgoing stats from other endpoints to the builder.
   const std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
       outgoing_stats_per_destination = stats.OutgoingStatsPerDestination();
@@ -234,7 +265,8 @@
     incoming_stats.emplace(entry.first, entry.second.Build());
   }
   return std::make_unique<EmulatedNetworkStatsImpl>(
-      local_addresses_, std::move(outgoing_stats), std::move(incoming_stats));
+      local_addresses_, sent_packets_queue_wait_time_us_,
+      std::move(outgoing_stats), std::move(incoming_stats));
 }
 
 void LinkEmulation::OnPacketReceived(EmulatedIpPacket packet) {
@@ -383,14 +415,17 @@
 
 EmulatedNetworkNode::~EmulatedNetworkNode() = default;
 
-EmulatedEndpointImpl::EmulatedEndpointImpl(uint64_t id,
-                                           const rtc::IPAddress& ip,
-                                           bool is_enabled,
-                                           rtc::AdapterType type,
-                                           rtc::TaskQueue* task_queue,
-                                           Clock* clock)
+EmulatedEndpointImpl::EmulatedEndpointImpl(
+    uint64_t id,
+    const rtc::IPAddress& ip,
+    EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode,
+    bool is_enabled,
+    rtc::AdapterType type,
+    rtc::TaskQueue* task_queue,
+    Clock* clock)
     : id_(id),
       peer_local_addr_(ip),
+      stats_gathering_mode_(stats_gathering_mode),
       is_enabled_(is_enabled),
       type_(type),
       clock_(clock),
@@ -430,8 +465,9 @@
                           clock_->CurrentTime(), application_overhead);
   task_queue_->PostTask([this, packet = std::move(packet)]() mutable {
     RTC_DCHECK_RUN_ON(task_queue_);
-    stats_builder_.OnPacketSent(clock_->CurrentTime(), packet.to.ipaddr(),
-                                DataSize::Bytes(packet.ip_packet_size()));
+    stats_builder_.OnPacketSent(
+        packet.arrival_time, clock_->CurrentTime(), packet.to.ipaddr(),
+        DataSize::Bytes(packet.ip_packet_size()), stats_gathering_mode_);
 
     router_.OnPacketReceived(std::move(packet));
   });
@@ -495,7 +531,8 @@
       << "; Receiver peer_local_addr_=" << peer_local_addr_.ToString();
   rtc::CritScope crit(&receiver_lock_);
   stats_builder_.OnPacketReceived(clock_->CurrentTime(), packet.from.ipaddr(),
-                                  DataSize::Bytes(packet.ip_packet_size()));
+                                  DataSize::Bytes(packet.ip_packet_size()),
+                                  stats_gathering_mode_);
   auto it = port_to_receiver_.find(packet.to.port());
   if (it == port_to_receiver_.end()) {
     // It can happen, that remote peer closed connection, but there still some
@@ -504,7 +541,8 @@
     RTC_LOG(INFO) << "Drop packet: no receiver registered in " << id_
                   << " on port " << packet.to.port();
     stats_builder_.OnPacketDropped(packet.from.ipaddr(),
-                                   DataSize::Bytes(packet.ip_packet_size()));
+                                   DataSize::Bytes(packet.ip_packet_size()),
+                                   stats_gathering_mode_);
     return;
   }
   // Endpoint assumes frequent calls to bind and unbind methods, so it holds
diff --git a/test/network/network_emulation.h b/test/network/network_emulation.h
index d292533..13d4386 100644
--- a/test/network/network_emulation.h
+++ b/test/network/network_emulation.h
@@ -21,6 +21,7 @@
 
 #include "absl/types/optional.h"
 #include "api/array_view.h"
+#include "api/numerics/samples_stats_counter.h"
 #include "api/test/network_emulation_manager.h"
 #include "api/test/simulated_network.h"
 #include "api/units/timestamp.h"
@@ -31,6 +32,7 @@
 #include "rtc_base/synchronization/sequence_checker.h"
 #include "rtc_base/task_queue_for_test.h"
 #include "rtc_base/task_utils/repeating_task.h"
+#include "rtc_base/thread_annotations.h"
 #include "rtc_base/thread_checker.h"
 #include "system_wrappers/include/clock.h"
 
@@ -40,13 +42,16 @@
 class EmulatedNetworkOutgoingStatsImpl final
     : public EmulatedNetworkOutgoingStats {
  public:
-  EmulatedNetworkOutgoingStatsImpl(int64_t packets_sent,
-                                   DataSize bytes_sent,
-                                   DataSize first_sent_packet_size,
-                                   Timestamp first_packet_sent_time,
-                                   Timestamp last_packet_sent_time)
+  EmulatedNetworkOutgoingStatsImpl(
+      int64_t packets_sent,
+      DataSize bytes_sent,
+      SamplesStatsCounter sent_packets_size_counter,
+      DataSize first_sent_packet_size,
+      Timestamp first_packet_sent_time,
+      Timestamp last_packet_sent_time)
       : packets_sent_(packets_sent),
         bytes_sent_(bytes_sent),
+        sent_packets_size_counter_(std::move(sent_packets_size_counter)),
         first_sent_packet_size_(first_sent_packet_size),
         first_packet_sent_time_(first_packet_sent_time),
         last_packet_sent_time_(last_packet_sent_time) {}
@@ -54,6 +59,7 @@
       const EmulatedNetworkOutgoingStats& stats)
       : packets_sent_(stats.PacketsSent()),
         bytes_sent_(stats.BytesSent()),
+        sent_packets_size_counter_(stats.SentPacketsSizeCounter()),
         first_sent_packet_size_(stats.FirstSentPacketSize()),
         first_packet_sent_time_(stats.FirstPacketSentTime()),
         last_packet_sent_time_(stats.LastPacketSentTime()) {}
@@ -63,6 +69,10 @@
 
   DataSize BytesSent() const override { return bytes_sent_; }
 
+  const SamplesStatsCounter& SentPacketsSizeCounter() const override {
+    return sent_packets_size_counter_;
+  }
+
   DataSize FirstSentPacketSize() const override {
     return first_sent_packet_size_;
   }
@@ -80,6 +90,7 @@
  private:
   const int64_t packets_sent_;
   const DataSize bytes_sent_;
+  const SamplesStatsCounter sent_packets_size_counter_;
   const DataSize first_sent_packet_size_;
   const Timestamp first_packet_sent_time_;
   const Timestamp last_packet_sent_time_;
@@ -89,17 +100,22 @@
 class EmulatedNetworkIncomingStatsImpl final
     : public EmulatedNetworkIncomingStats {
  public:
-  EmulatedNetworkIncomingStatsImpl(int64_t packets_received,
-                                   DataSize bytes_received,
-                                   int64_t packets_dropped,
-                                   DataSize bytes_dropped,
-                                   DataSize first_received_packet_size,
-                                   Timestamp first_packet_received_time,
-                                   Timestamp last_packet_received_time)
+  EmulatedNetworkIncomingStatsImpl(
+      int64_t packets_received,
+      DataSize bytes_received,
+      SamplesStatsCounter received_packets_size_counter,
+      int64_t packets_dropped,
+      DataSize bytes_dropped,
+      SamplesStatsCounter dropped_packets_size_counter,
+      DataSize first_received_packet_size,
+      Timestamp first_packet_received_time,
+      Timestamp last_packet_received_time)
       : packets_received_(packets_received),
         bytes_received_(bytes_received),
+        received_packets_size_counter_(received_packets_size_counter),
         packets_dropped_(packets_dropped),
         bytes_dropped_(bytes_dropped),
+        dropped_packets_size_counter_(dropped_packets_size_counter),
         first_received_packet_size_(first_received_packet_size),
         first_packet_received_time_(first_packet_received_time),
         last_packet_received_time_(last_packet_received_time) {}
@@ -107,8 +123,10 @@
       const EmulatedNetworkIncomingStats& stats)
       : packets_received_(stats.PacketsReceived()),
         bytes_received_(stats.BytesReceived()),
+        received_packets_size_counter_(stats.ReceivedPacketsSizeCounter()),
         packets_dropped_(stats.PacketsDropped()),
         bytes_dropped_(stats.BytesDropped()),
+        dropped_packets_size_counter_(stats.DroppedPacketsSizeCounter()),
         first_received_packet_size_(stats.FirstReceivedPacketSize()),
         first_packet_received_time_(stats.FirstPacketReceivedTime()),
         last_packet_received_time_(stats.LastPacketReceivedTime()) {}
@@ -118,10 +136,18 @@
 
   DataSize BytesReceived() const override { return bytes_received_; }
 
+  const SamplesStatsCounter& ReceivedPacketsSizeCounter() const override {
+    return received_packets_size_counter_;
+  }
+
   int64_t PacketsDropped() const override { return packets_dropped_; }
 
   DataSize BytesDropped() const override { return bytes_dropped_; }
 
+  const SamplesStatsCounter& DroppedPacketsSizeCounter() const override {
+    return dropped_packets_size_counter_;
+  }
+
   DataSize FirstReceivedPacketSize() const override {
     return first_received_packet_size_;
   }
@@ -139,8 +165,10 @@
  private:
   const int64_t packets_received_;
   const DataSize bytes_received_;
+  const SamplesStatsCounter received_packets_size_counter_;
   const int64_t packets_dropped_;
   const DataSize bytes_dropped_;
+  const SamplesStatsCounter dropped_packets_size_counter_;
   const DataSize first_received_packet_size_;
   const Timestamp first_packet_received_time_;
   const Timestamp last_packet_received_time_;
@@ -151,14 +179,18 @@
  public:
   EmulatedNetworkStatsImpl(
       std::vector<rtc::IPAddress> local_addresses,
+      SamplesStatsCounter sent_packets_queue_wait_time_us,
       std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
           outgoing_stats_per_destination,
       std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
           incoming_stats_per_source)
       : local_addresses_(std::move(local_addresses)),
+        sent_packets_queue_wait_time_us_(sent_packets_queue_wait_time_us),
         outgoing_stats_per_destination_(
             std::move(outgoing_stats_per_destination)),
-        incoming_stats_per_source_(std::move(incoming_stats_per_source)) {}
+        incoming_stats_per_source_(std::move(incoming_stats_per_source)),
+        overall_outgoing_stats_(GetOverallOutgoingStats()),
+        overall_incoming_stats_(GetOverallIncomingStats()) {}
   ~EmulatedNetworkStatsImpl() override = default;
 
   std::vector<rtc::IPAddress> LocalAddresses() const override {
@@ -166,59 +198,75 @@
   }
 
   int64_t PacketsSent() const override {
-    return GetOverallOutgoingStats()->PacketsSent();
+    return overall_outgoing_stats_->PacketsSent();
   }
 
   DataSize BytesSent() const override {
-    return GetOverallOutgoingStats()->BytesSent();
+    return overall_outgoing_stats_->BytesSent();
+  }
+
+  const SamplesStatsCounter& SentPacketsSizeCounter() const override {
+    return overall_outgoing_stats_->SentPacketsSizeCounter();
+  }
+
+  const SamplesStatsCounter& SentPacketsQueueWaitTimeUs() const override {
+    return sent_packets_queue_wait_time_us_;
   }
 
   DataSize FirstSentPacketSize() const override {
-    return GetOverallOutgoingStats()->FirstSentPacketSize();
+    return overall_outgoing_stats_->FirstSentPacketSize();
   }
 
   Timestamp FirstPacketSentTime() const override {
-    return GetOverallOutgoingStats()->FirstPacketSentTime();
+    return overall_outgoing_stats_->FirstPacketSentTime();
   }
 
   Timestamp LastPacketSentTime() const override {
-    return GetOverallOutgoingStats()->LastPacketSentTime();
+    return overall_outgoing_stats_->LastPacketSentTime();
   }
 
   DataRate AverageSendRate() const override {
-    return GetOverallOutgoingStats()->AverageSendRate();
+    return overall_outgoing_stats_->AverageSendRate();
   }
 
   int64_t PacketsReceived() const override {
-    return GetOverallIncomingStats()->PacketsReceived();
+    return overall_incoming_stats_->PacketsReceived();
   }
 
   DataSize BytesReceived() const override {
-    return GetOverallIncomingStats()->BytesReceived();
+    return overall_incoming_stats_->BytesReceived();
+  }
+
+  const SamplesStatsCounter& ReceivedPacketsSizeCounter() const override {
+    return overall_incoming_stats_->ReceivedPacketsSizeCounter();
   }
 
   int64_t PacketsDropped() const override {
-    return GetOverallIncomingStats()->PacketsDropped();
+    return overall_incoming_stats_->PacketsDropped();
   }
 
   DataSize BytesDropped() const override {
-    return GetOverallIncomingStats()->BytesDropped();
+    return overall_incoming_stats_->BytesDropped();
+  }
+
+  const SamplesStatsCounter& DroppedPacketsSizeCounter() const override {
+    return overall_incoming_stats_->DroppedPacketsSizeCounter();
   }
 
   DataSize FirstReceivedPacketSize() const override {
-    return GetOverallIncomingStats()->FirstReceivedPacketSize();
+    return overall_incoming_stats_->FirstReceivedPacketSize();
   }
 
   Timestamp FirstPacketReceivedTime() const override {
-    return GetOverallIncomingStats()->FirstPacketReceivedTime();
+    return overall_incoming_stats_->FirstPacketReceivedTime();
   }
 
   Timestamp LastPacketReceivedTime() const override {
-    return GetOverallIncomingStats()->LastPacketReceivedTime();
+    return overall_incoming_stats_->LastPacketReceivedTime();
   }
 
   DataRate AverageReceiveRate() const override {
-    return GetOverallIncomingStats()->AverageReceiveRate();
+    return overall_incoming_stats_->AverageReceiveRate();
   }
 
   std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
@@ -232,17 +280,22 @@
   std::unique_ptr<EmulatedNetworkIncomingStats> GetOverallIncomingStats() const;
 
   const std::vector<rtc::IPAddress> local_addresses_;
+  const SamplesStatsCounter sent_packets_queue_wait_time_us_;
   const std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
       outgoing_stats_per_destination_;
   const std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
       incoming_stats_per_source_;
+  const std::unique_ptr<EmulatedNetworkOutgoingStats> overall_outgoing_stats_;
+  const std::unique_ptr<EmulatedNetworkIncomingStats> overall_incoming_stats_;
 };
 
 class EmulatedNetworkOutgoingStatsBuilder {
  public:
   EmulatedNetworkOutgoingStatsBuilder();
 
-  void OnPacketSent(Timestamp sent_time, DataSize packet_size);
+  void OnPacketSent(Timestamp sent_time,
+                    DataSize packet_size,
+                    EmulatedEndpointConfig::StatsGatheringMode mode);
 
   void AddOutgoingStats(const EmulatedNetworkOutgoingStats& stats);
 
@@ -253,6 +306,8 @@
 
   int64_t packets_sent_ RTC_GUARDED_BY(sequence_checker_) = 0;
   DataSize bytes_sent_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero();
+  SamplesStatsCounter sent_packets_size_counter_
+      RTC_GUARDED_BY(sequence_checker_);
   DataSize first_sent_packet_size_ RTC_GUARDED_BY(sequence_checker_) =
       DataSize::Zero();
   Timestamp first_packet_sent_time_ RTC_GUARDED_BY(sequence_checker_) =
@@ -265,9 +320,12 @@
  public:
   EmulatedNetworkIncomingStatsBuilder();
 
-  void OnPacketDropped(DataSize packet_size);
+  void OnPacketDropped(DataSize packet_size,
+                       EmulatedEndpointConfig::StatsGatheringMode mode);
 
-  void OnPacketReceived(Timestamp received_time, DataSize packet_size);
+  void OnPacketReceived(Timestamp received_time,
+                        DataSize packet_size,
+                        EmulatedEndpointConfig::StatsGatheringMode mode);
 
   // Adds stats collected from another endpoints to the builder.
   void AddIncomingStats(const EmulatedNetworkIncomingStats& stats);
@@ -279,8 +337,12 @@
 
   int64_t packets_received_ RTC_GUARDED_BY(sequence_checker_) = 0;
   DataSize bytes_received_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero();
+  SamplesStatsCounter received_packets_size_counter_
+      RTC_GUARDED_BY(sequence_checker_);
   int64_t packets_dropped_ RTC_GUARDED_BY(sequence_checker_) = 0;
   DataSize bytes_dropped_ RTC_GUARDED_BY(sequence_checker_) = DataSize::Zero();
+  SamplesStatsCounter dropped_packets_size_counter_
+      RTC_GUARDED_BY(sequence_checker_);
   DataSize first_received_packet_size_ RTC_GUARDED_BY(sequence_checker_) =
       DataSize::Zero();
   Timestamp first_packet_received_time_ RTC_GUARDED_BY(sequence_checker_) =
@@ -296,15 +358,20 @@
   EmulatedNetworkStatsBuilder();
   explicit EmulatedNetworkStatsBuilder(rtc::IPAddress local_ip);
 
-  void OnPacketSent(Timestamp sent_time,
+  void OnPacketSent(Timestamp queued_time,
+                    Timestamp sent_time,
                     rtc::IPAddress destination_ip,
-                    DataSize packet_size);
+                    DataSize packet_size,
+                    EmulatedEndpointConfig::StatsGatheringMode mode);
 
-  void OnPacketDropped(rtc::IPAddress source_ip, DataSize packet_size);
+  void OnPacketDropped(rtc::IPAddress source_ip,
+                       DataSize packet_size,
+                       EmulatedEndpointConfig::StatsGatheringMode mode);
 
   void OnPacketReceived(Timestamp received_time,
                         rtc::IPAddress source_ip,
-                        DataSize packet_size);
+                        DataSize packet_size,
+                        EmulatedEndpointConfig::StatsGatheringMode mode);
 
   void AddEmulatedNetworkStats(const EmulatedNetworkStats& stats);
 
@@ -315,6 +382,7 @@
 
   std::vector<rtc::IPAddress> local_addresses_
       RTC_GUARDED_BY(sequence_checker_);
+  SamplesStatsCounter sent_packets_queue_wait_time_us_;
   std::map<rtc::IPAddress, EmulatedNetworkOutgoingStatsBuilder>
       outgoing_stats_per_destination_ RTC_GUARDED_BY(sequence_checker_);
   std::map<rtc::IPAddress, EmulatedNetworkIncomingStatsBuilder>
@@ -414,12 +482,14 @@
 // from other EmulatedNetworkNodes.
 class EmulatedEndpointImpl : public EmulatedEndpoint {
  public:
-  EmulatedEndpointImpl(uint64_t id,
-                       const rtc::IPAddress& ip,
-                       bool is_enabled,
-                       rtc::AdapterType type,
-                       rtc::TaskQueue* task_queue,
-                       Clock* clock);
+  EmulatedEndpointImpl(
+      uint64_t id,
+      const rtc::IPAddress& ip,
+      EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode,
+      bool is_enabled,
+      rtc::AdapterType type,
+      rtc::TaskQueue* task_queue,
+      Clock* clock);
   ~EmulatedEndpointImpl() override;
 
   uint64_t GetId() const;
@@ -456,9 +526,10 @@
   rtc::RecursiveCriticalSection receiver_lock_;
   rtc::ThreadChecker enabled_state_checker_;
 
-  uint64_t id_;
+  const uint64_t id_;
   // Peer's local IP address for this endpoint network interface.
   const rtc::IPAddress peer_local_addr_;
+  const EmulatedEndpointConfig::StatsGatheringMode stats_gathering_mode_;
   bool is_enabled_ RTC_GUARDED_BY(enabled_state_checker_);
   const rtc::AdapterType type_;
   Clock* const clock_;
diff --git a/test/network/network_emulation_manager.cc b/test/network/network_emulation_manager.cc
index 1e8e5ef..4a2e31e 100644
--- a/test/network/network_emulation_manager.cc
+++ b/test/network/network_emulation_manager.cc
@@ -98,8 +98,8 @@
   bool res = used_ip_addresses_.insert(*ip).second;
   RTC_CHECK(res) << "IP=" << ip->ToString() << " already in use";
   auto node = std::make_unique<EmulatedEndpointImpl>(
-      next_node_id_++, *ip, config.start_as_enabled, config.type, &task_queue_,
-      clock_);
+      next_node_id_++, *ip, config.stats_gathering_mode,
+      config.start_as_enabled, config.type, &task_queue_, clock_);
   EmulatedEndpoint* out = node.get();
   endpoints_.push_back(std::move(node));
   return out;
diff --git a/test/network/network_emulation_unittest.cc b/test/network/network_emulation_unittest.cc
index f9f088a..c042f65 100644
--- a/test/network/network_emulation_unittest.cc
+++ b/test/network/network_emulation_unittest.cc
@@ -273,6 +273,16 @@
     EXPECT_EQ(dest_st.at(bob_ip)->PacketsSent(), 2000l);
     EXPECT_EQ(dest_st.at(bob_ip)->BytesSent().bytes(),
               single_packet_size * 2000l);
+
+    // No debug stats are collected by default.
+    EXPECT_TRUE(st->SentPacketsSizeCounter().IsEmpty());
+    EXPECT_TRUE(st->SentPacketsQueueWaitTimeUs().IsEmpty());
+    EXPECT_TRUE(st->ReceivedPacketsSizeCounter().IsEmpty());
+    EXPECT_TRUE(st->DroppedPacketsSizeCounter().IsEmpty());
+    EXPECT_TRUE(dest_st.at(bob_ip)->SentPacketsSizeCounter().IsEmpty());
+    EXPECT_TRUE(source_st.at(bob_ip)->ReceivedPacketsSizeCounter().IsEmpty());
+    EXPECT_TRUE(source_st.at(bob_ip)->DroppedPacketsSizeCounter().IsEmpty());
+
     received_stats_count++;
   });
   nt2->GetStats([&](std::unique_ptr<EmulatedNetworkStats> st) {
@@ -304,6 +314,16 @@
     EXPECT_EQ(dest_st.at(alice_ip)->PacketsSent(), 2000l);
     EXPECT_EQ(dest_st.at(alice_ip)->BytesSent().bytes(),
               single_packet_size * 2000l);
+
+    // No debug stats are collected by default.
+    EXPECT_TRUE(st->SentPacketsSizeCounter().IsEmpty());
+    EXPECT_TRUE(st->SentPacketsQueueWaitTimeUs().IsEmpty());
+    EXPECT_TRUE(st->ReceivedPacketsSizeCounter().IsEmpty());
+    EXPECT_TRUE(st->DroppedPacketsSizeCounter().IsEmpty());
+    EXPECT_TRUE(dest_st.at(alice_ip)->SentPacketsSizeCounter().IsEmpty());
+    EXPECT_TRUE(source_st.at(alice_ip)->ReceivedPacketsSizeCounter().IsEmpty());
+    EXPECT_TRUE(source_st.at(alice_ip)->DroppedPacketsSizeCounter().IsEmpty());
+
     received_stats_count++;
   });
   ASSERT_EQ_SIMULATED_WAIT(received_stats_count.load(), 2,
@@ -311,6 +331,105 @@
                            *network_manager.time_controller());
 }
 
+TEST(NetworkEmulationManagerTest, DebugStatsCollectedInDebugMode) {
+  NetworkEmulationManagerImpl network_manager(TimeMode::kSimulated);
+
+  EmulatedNetworkNode* alice_node = network_manager.CreateEmulatedNode(
+      std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+  EmulatedNetworkNode* bob_node = network_manager.CreateEmulatedNode(
+      std::make_unique<SimulatedNetwork>(BuiltInNetworkBehaviorConfig()));
+  EmulatedEndpointConfig debug_config;
+  debug_config.stats_gathering_mode =
+      EmulatedEndpointConfig::StatsGatheringMode::kDebug;
+  EmulatedEndpoint* alice_endpoint =
+      network_manager.CreateEndpoint(debug_config);
+  EmulatedEndpoint* bob_endpoint =
+      network_manager.CreateEndpoint(EmulatedEndpointConfig());
+  network_manager.CreateRoute(alice_endpoint, {alice_node}, bob_endpoint);
+  network_manager.CreateRoute(bob_endpoint, {bob_node}, alice_endpoint);
+
+  EmulatedNetworkManagerInterface* nt1 =
+      network_manager.CreateEmulatedNetworkManagerInterface({alice_endpoint});
+  EmulatedNetworkManagerInterface* nt2 =
+      network_manager.CreateEmulatedNetworkManagerInterface({bob_endpoint});
+
+  rtc::Thread* t1 = nt1->network_thread();
+  rtc::Thread* t2 = nt2->network_thread();
+
+  rtc::CopyOnWriteBuffer data("Hello");
+  for (uint64_t j = 0; j < 2; j++) {
+    auto* s1 = t1->socketserver()->CreateAsyncSocket(AF_INET, SOCK_DGRAM);
+    auto* s2 = t2->socketserver()->CreateAsyncSocket(AF_INET, SOCK_DGRAM);
+
+    SocketReader r1(s1, t1);
+    SocketReader r2(s2, t2);
+
+    rtc::SocketAddress a1(alice_endpoint->GetPeerLocalAddress(), 0);
+    rtc::SocketAddress a2(bob_endpoint->GetPeerLocalAddress(), 0);
+
+    t1->Invoke<void>(RTC_FROM_HERE, [&] {
+      s1->Bind(a1);
+      a1 = s1->GetLocalAddress();
+    });
+    t2->Invoke<void>(RTC_FROM_HERE, [&] {
+      s2->Bind(a2);
+      a2 = s2->GetLocalAddress();
+    });
+
+    t1->Invoke<void>(RTC_FROM_HERE, [&] { s1->Connect(a2); });
+    t2->Invoke<void>(RTC_FROM_HERE, [&] { s2->Connect(a1); });
+
+    for (uint64_t i = 0; i < 1000; i++) {
+      t1->PostTask(RTC_FROM_HERE,
+                   [&]() { s1->Send(data.data(), data.size()); });
+      t2->PostTask(RTC_FROM_HERE,
+                   [&]() { s2->Send(data.data(), data.size()); });
+    }
+
+    network_manager.time_controller()->AdvanceTime(TimeDelta::Seconds(1));
+
+    EXPECT_EQ(r1.ReceivedCount(), 1000);
+    EXPECT_EQ(r2.ReceivedCount(), 1000);
+
+    t1->Invoke<void>(RTC_FROM_HERE, [&] { delete s1; });
+    t2->Invoke<void>(RTC_FROM_HERE, [&] { delete s2; });
+  }
+
+  const int64_t single_packet_size = data.size() + kOverheadIpv4Udp;
+  std::atomic<int> received_stats_count{0};
+  nt1->GetStats([&](std::unique_ptr<EmulatedNetworkStats> st) {
+    rtc::IPAddress bob_ip = bob_endpoint->GetPeerLocalAddress();
+    std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkIncomingStats>>
+        source_st = st->IncomingStatsPerSource();
+    ASSERT_EQ(source_st.size(), 1lu);
+
+    std::map<rtc::IPAddress, std::unique_ptr<EmulatedNetworkOutgoingStats>>
+        dest_st = st->OutgoingStatsPerDestination();
+    ASSERT_EQ(dest_st.size(), 1lu);
+
+    // No debug stats are collected by default.
+    EXPECT_EQ(st->SentPacketsSizeCounter().NumSamples(), 2000l);
+    EXPECT_EQ(st->ReceivedPacketsSizeCounter().GetAverage(),
+              single_packet_size);
+    EXPECT_EQ(st->SentPacketsQueueWaitTimeUs().NumSamples(), 2000l);
+    EXPECT_LT(st->SentPacketsQueueWaitTimeUs().GetMax(), 1);
+    EXPECT_TRUE(st->DroppedPacketsSizeCounter().IsEmpty());
+    EXPECT_EQ(dest_st.at(bob_ip)->SentPacketsSizeCounter().NumSamples(), 2000l);
+    EXPECT_EQ(dest_st.at(bob_ip)->SentPacketsSizeCounter().GetAverage(),
+              single_packet_size);
+    EXPECT_EQ(source_st.at(bob_ip)->ReceivedPacketsSizeCounter().NumSamples(),
+              2000l);
+    EXPECT_EQ(source_st.at(bob_ip)->ReceivedPacketsSizeCounter().GetAverage(),
+              single_packet_size);
+    EXPECT_TRUE(source_st.at(bob_ip)->DroppedPacketsSizeCounter().IsEmpty());
+
+    received_stats_count++;
+  });
+  ASSERT_EQ_SIMULATED_WAIT(received_stats_count.load(), 1,
+                           kStatsWaitTimeout.ms(),
+                           *network_manager.time_controller());
+}
+
 TEST(NetworkEmulationManagerTest, ThroughputStats) {
   NetworkEmulationManagerImpl network_manager(TimeMode::kRealTime);
 
diff --git a/test/pc/e2e/BUILD.gn b/test/pc/e2e/BUILD.gn
index e00ab36..f9c0c9b 100644
--- a/test/pc/e2e/BUILD.gn
+++ b/test/pc/e2e/BUILD.gn
@@ -696,6 +696,7 @@
       "../../../api:peer_connection_quality_test_fixture_api",
       "../../../api:rtc_stats_api",
       "../../../api:scoped_refptr",
+      "../../../api/numerics",
       "../../../api/test/network_emulation",
       "../../../api/units:data_rate",
       "../../../api/units:data_size",
diff --git a/test/pc/e2e/stats_based_network_quality_metrics_reporter.cc b/test/pc/e2e/stats_based_network_quality_metrics_reporter.cc
index 9f4283d..eea8397 100644
--- a/test/pc/e2e/stats_based_network_quality_metrics_reporter.cc
+++ b/test/pc/e2e/stats_based_network_quality_metrics_reporter.cc
@@ -250,6 +250,16 @@
                     /*important=*/false);
 }
 
+void StatsBasedNetworkQualityMetricsReporter::ReportResult(
+    const std::string& metric_name,
+    const std::string& network_label,
+    const SamplesStatsCounter& value,
+    const std::string& unit) const {
+  test::PrintResult(metric_name, /*modifier=*/"",
+                    GetTestCaseName(network_label), value, unit,
+                    /*important=*/false);
+}
+
 std::string StatsBasedNetworkQualityMetricsReporter::GetTestCaseName(
     absl::string_view network_label) const {
   rtc::StringBuilder builder;
@@ -258,7 +268,7 @@
 }
 
 void StatsBasedNetworkQualityMetricsReporter::LogNetworkLayerStats(
-    absl::string_view peer_name,
+    const std::string& peer_name,
     const NetworkLayerStats& stats) const {
   DataRate average_send_rate = stats.stats->PacketsSent() >= 2
                                    ? stats.stats->AverageSendRate()
@@ -273,6 +283,19 @@
   for (size_t i = 0; i < local_ips.size(); ++i) {
     log << "  " << local_ips[i].ToString() << "\n";
   }
+  if (!stats.stats->SentPacketsSizeCounter().IsEmpty()) {
+    ReportResult("sent_packets_size", peer_name,
+                 stats.stats->SentPacketsSizeCounter(), "sizeInBytes");
+  }
+  if (!stats.stats->ReceivedPacketsSizeCounter().IsEmpty()) {
+    ReportResult("received_packets_size", peer_name,
+                 stats.stats->ReceivedPacketsSizeCounter(), "sizeInBytes");
+  }
+  if (!stats.stats->DroppedPacketsSizeCounter().IsEmpty()) {
+    ReportResult("dropped_packets_size", peer_name,
+                 stats.stats->DroppedPacketsSizeCounter(), "sizeInBytes");
+  }
+
   log << "Send statistic:\n"
       << "  packets: " << stats.stats->PacketsSent()
       << " bytes: " << stats.stats->BytesSent().bytes()
@@ -289,6 +312,11 @@
         << " bytes: " << entry.second->BytesSent().bytes()
         << " avg_rate (bytes/sec): " << source_average_send_rate.bytes_per_sec()
         << " avg_rate (bps): " << source_average_send_rate.bps() << "\n";
+    if (!entry.second->SentPacketsSizeCounter().IsEmpty()) {
+      ReportResult("sent_packets_size",
+                   peer_name + "/" + entry.first.ToString(),
+                   stats.stats->SentPacketsSizeCounter(), "sizeInBytes");
+    }
   }
 
   log << "Receive statistic:\n"
@@ -309,6 +337,16 @@
         << " avg_rate (bytes/sec): "
         << source_average_receive_rate.bytes_per_sec()
         << " avg_rate (bps): " << source_average_receive_rate.bps() << "\n";
+    if (!entry.second->ReceivedPacketsSizeCounter().IsEmpty()) {
+      ReportResult("received_packets_size",
+                   peer_name + "/" + entry.first.ToString(),
+                   stats.stats->ReceivedPacketsSizeCounter(), "sizeInBytes");
+    }
+    if (!entry.second->DroppedPacketsSizeCounter().IsEmpty()) {
+      ReportResult("dropped_packets_size",
+                   peer_name + "/" + entry.first.ToString(),
+                   stats.stats->DroppedPacketsSizeCounter(), "sizeInBytes");
+    }
   }
 
   RTC_LOG(INFO) << log.str();
diff --git a/test/pc/e2e/stats_based_network_quality_metrics_reporter.h b/test/pc/e2e/stats_based_network_quality_metrics_reporter.h
index f01a49d..d14bb43 100644
--- a/test/pc/e2e/stats_based_network_quality_metrics_reporter.h
+++ b/test/pc/e2e/stats_based_network_quality_metrics_reporter.h
@@ -20,6 +20,7 @@
 #include <vector>
 
 #include "absl/strings/string_view.h"
+#include "api/numerics/samples_stats_counter.h"
 #include "api/test/network_emulation/network_emulation_interfaces.h"
 #include "api/test/network_emulation_manager.h"
 #include "api/test/peerconnection_quality_test_fixture.h"
@@ -95,8 +96,12 @@
                     const std::string& network_label,
                     const double value,
                     const std::string& unit) const;
+  void ReportResult(const std::string& metric_name,
+                    const std::string& network_label,
+                    const SamplesStatsCounter& value,
+                    const std::string& unit) const;
   std::string GetTestCaseName(absl::string_view network_label) const;
-  void LogNetworkLayerStats(absl::string_view peer_name,
+  void LogNetworkLayerStats(const std::string& peer_name,
                             const NetworkLayerStats& stats) const;
 
   NetworkLayerStatsCollector collector_;