shill: mac80211_monitor: add metrics

Add metrics reporting, so that we know how often the mac80211
TX queues are getting stuck.

BUG=chromium:392209
TEST=unit tests

Change-Id: I56874da399ec47a19bc1ba3a3796b0a922da2ce6
Reviewed-on: https://chromium-review.googlesource.com/211707
Reviewed-by: Paul Stewart <pstew@chromium.org>
Tested-by: mukesh agrawal <quiche@chromium.org>
diff --git a/mac80211_monitor.cc b/mac80211_monitor.cc
index 8ef4609..b430018 100644
--- a/mac80211_monitor.cc
+++ b/mac80211_monitor.cc
@@ -10,6 +10,7 @@
 #include <base/strings/string_split.h>
 
 #include "shill/logging.h"
+#include "shill/metrics.h"
 
 namespace shill {
 
@@ -22,9 +23,11 @@
 const size_t Mac80211Monitor::kMaxQueueStateSizeBytes = 2048;
 
 Mac80211Monitor::Mac80211Monitor(
-    const string &link_name, size_t queue_length_limit)
+    const string &link_name, size_t queue_length_limit, Metrics *metrics)
     : link_name_(link_name),
-      queue_length_limit_(queue_length_limit) {
+      queue_length_limit_(queue_length_limit),
+      metrics_(metrics) {
+  CHECK(metrics_);
 }
 
 Mac80211Monitor::~Mac80211Monitor() {
@@ -60,9 +63,17 @@
     for (unsigned int i=0; i < kStopReasonMax; ++i) {
       auto stop_reason = static_cast<QueueStopReason>(i);
       if (stuck_flags & GetFlagForReason(stop_reason)) {
-        // TODO(quiche): report stop reason to UMA
+        metrics_->SendEnumToUMA(Metrics::kMetricWifiStoppedTxQueueReason,
+                                stop_reason,
+                                kStopReasonMax);
       }
     }
+
+    metrics_->SendToUMA(Metrics::kMetricWifiStoppedTxQueueLength,
+                        max_stuck_queue_len,
+                        Metrics::kMetricWifiStoppedTxQueueLengthMin,
+                        Metrics::kMetricWifiStoppedTxQueueLengthMax,
+                        Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets);
   }
 
   return stuck_flags;
diff --git a/mac80211_monitor.h b/mac80211_monitor.h
index 5f5e053..0762f20 100644
--- a/mac80211_monitor.h
+++ b/mac80211_monitor.h
@@ -13,6 +13,8 @@
 
 namespace shill {
 
+class Metrics;
+
 class Mac80211Monitor {
  public:
   struct QueueState {
@@ -27,7 +29,8 @@
     size_t queue_length;
   };
 
-  Mac80211Monitor(const std::string &link_name, size_t queue_length_limit);
+  Mac80211Monitor(const std::string &link_name, size_t queue_length_limit,
+                  Metrics *metrics);
   virtual ~Mac80211Monitor();
 
  private:
@@ -36,6 +39,8 @@
   FRIEND_TEST(Mac80211MonitorTest, CheckAreQueuesStuckMultipleQueues);
   FRIEND_TEST(Mac80211MonitorTest, CheckAreQueuesStuckNotStuck);
   FRIEND_TEST(Mac80211MonitorTest, CheckAreQueuesStuckQueueLength);
+  FRIEND_TEST(Mac80211MonitorTest,
+              CheckAreQueuesStuckQueueLengthIgnoresUnstopped);
   FRIEND_TEST(Mac80211MonitorTest, CheckAreQueuesStuckSingleReason);
   FRIEND_TEST(Mac80211MonitorTest, ParseQueueStateBadInput);
   FRIEND_TEST(Mac80211MonitorTest, ParseQueueStateSimple);
@@ -76,6 +81,7 @@
 
   const std::string link_name_;
   size_t queue_length_limit_;
+  Metrics *metrics_;
 
   DISALLOW_COPY_AND_ASSIGN(Mac80211Monitor);
 };
diff --git a/mac80211_monitor_unittest.cc b/mac80211_monitor_unittest.cc
index 043692a..98dca4f 100644
--- a/mac80211_monitor_unittest.cc
+++ b/mac80211_monitor_unittest.cc
@@ -9,8 +9,11 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "shill/mock_metrics.h"
+
 using std::vector;
 using ::testing::ElementsAre;
+using ::testing::StrictMock;
 
 namespace shill {
 
@@ -23,17 +26,20 @@
 class Mac80211MonitorTest : public testing::Test {
  public:
   Mac80211MonitorTest()
-      : mac80211_monitor_(kTestDeviceName, kQueueLengthLimit) {}
+      : metrics_(nullptr),
+        mac80211_monitor_(kTestDeviceName, kQueueLengthLimit, &metrics_) {}
   virtual ~Mac80211MonitorTest() {}
 
  protected:
   static const size_t kQueueLengthLimit = 5;
 
+  MockMetrics &metrics() { return metrics_; }
   uint32_t CheckAreQueuesStuck(const vector<QState> &queue_states) {
     return mac80211_monitor_.CheckAreQueuesStuck(queue_states);
   }
 
  private:
+  StrictMock<MockMetrics> metrics_;
   Mac80211Monitor mac80211_monitor_;
 };
 
@@ -165,6 +171,20 @@
 }
 
 TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckSingleReason) {
+  EXPECT_CALL(metrics(), SendEnumToUMA(
+      Metrics::kMetricWifiStoppedTxQueueReason,
+      Mac80211Monitor::kStopReasonDriver,
+      Mac80211Monitor::kStopReasonMax));
+  EXPECT_CALL(metrics(), SendEnumToUMA(
+      Metrics::kMetricWifiStoppedTxQueueReason,
+      Mac80211Monitor::kStopReasonPowerSave,
+      Mac80211Monitor::kStopReasonMax));
+  EXPECT_CALL(metrics(), SendToUMA(
+      Metrics::kMetricWifiStoppedTxQueueLength,
+      kQueueLengthLimit,
+      Metrics::kMetricWifiStoppedTxQueueLengthMin,
+      Metrics::kMetricWifiStoppedTxQueueLengthMax,
+      Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets)).Times(2);
   EXPECT_EQ(Mac80211Monitor::kStopFlagDriver,
             CheckAreQueuesStuck({
                 QState(0, Mac80211Monitor::kStopFlagDriver, kQueueLengthLimit)}));
@@ -174,6 +194,24 @@
 }
 
 TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckMultipleReasons) {
+  EXPECT_CALL(metrics(), SendEnumToUMA(
+      Metrics::kMetricWifiStoppedTxQueueReason,
+      Mac80211Monitor::kStopReasonPowerSave,
+      Mac80211Monitor::kStopReasonMax)).Times(2);
+  EXPECT_CALL(metrics(), SendEnumToUMA(
+      Metrics::kMetricWifiStoppedTxQueueReason,
+      Mac80211Monitor::kStopReasonDriver,
+      Mac80211Monitor::kStopReasonMax)).Times(2);
+  EXPECT_CALL(metrics(), SendEnumToUMA(
+      Metrics::kMetricWifiStoppedTxQueueReason,
+      Mac80211Monitor::kStopReasonChannelSwitch,
+      Mac80211Monitor::kStopReasonMax)).Times(2);
+  EXPECT_CALL(metrics(), SendToUMA(
+      Metrics::kMetricWifiStoppedTxQueueLength,
+      kQueueLengthLimit,
+      Metrics::kMetricWifiStoppedTxQueueLengthMin,
+      Metrics::kMetricWifiStoppedTxQueueLengthMax,
+      Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets)).Times(3);
   EXPECT_EQ(Mac80211Monitor::kStopFlagDriver |
             Mac80211Monitor::kStopFlagPowerSave,
             CheckAreQueuesStuck({
@@ -198,6 +236,20 @@
 }
 
 TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckMultipleQueues) {
+  EXPECT_CALL(metrics(), SendEnumToUMA(
+      Metrics::kMetricWifiStoppedTxQueueReason,
+      Mac80211Monitor::kStopReasonPowerSave,
+      Mac80211Monitor::kStopReasonMax)).Times(5);
+  EXPECT_CALL(metrics(), SendEnumToUMA(
+      Metrics::kMetricWifiStoppedTxQueueReason,
+      Mac80211Monitor::kStopReasonDriver,
+      Mac80211Monitor::kStopReasonMax)).Times(2);
+  EXPECT_CALL(metrics(), SendToUMA(
+      Metrics::kMetricWifiStoppedTxQueueLength,
+      kQueueLengthLimit,
+      Metrics::kMetricWifiStoppedTxQueueLengthMin,
+      Metrics::kMetricWifiStoppedTxQueueLengthMax,
+      Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets)).Times(5);
   EXPECT_EQ(Mac80211Monitor::kStopFlagPowerSave,
             CheckAreQueuesStuck({
                 QState(0, 0, 0),
@@ -237,6 +289,16 @@
 }
 
 TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckQueueLength) {
+  EXPECT_CALL(metrics(), SendEnumToUMA(
+      Metrics::kMetricWifiStoppedTxQueueReason,
+      Mac80211Monitor::kStopReasonPowerSave,
+      Mac80211Monitor::kStopReasonMax)).Times(4);
+  EXPECT_CALL(metrics(), SendToUMA(
+      Metrics::kMetricWifiStoppedTxQueueLength,
+      kQueueLengthLimit,
+      Metrics::kMetricWifiStoppedTxQueueLengthMin,
+      Metrics::kMetricWifiStoppedTxQueueLengthMax,
+      Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets)).Times(4);
   EXPECT_TRUE(CheckAreQueuesStuck({
         QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit)}));
   EXPECT_TRUE(CheckAreQueuesStuck({
@@ -253,4 +315,20 @@
         QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit-2)}));
 }
 
+TEST_F(Mac80211MonitorTest, CheckAreQueuesStuckQueueLengthIgnoresUnstopped) {
+  EXPECT_CALL(metrics(), SendEnumToUMA(
+      Metrics::kMetricWifiStoppedTxQueueReason,
+      Mac80211Monitor::kStopReasonPowerSave,
+      Mac80211Monitor::kStopReasonMax));
+  EXPECT_CALL(metrics(), SendToUMA(
+      Metrics::kMetricWifiStoppedTxQueueLength,
+      kQueueLengthLimit,
+      Metrics::kMetricWifiStoppedTxQueueLengthMin,
+      Metrics::kMetricWifiStoppedTxQueueLengthMax,
+      Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets));
+  EXPECT_TRUE(CheckAreQueuesStuck({
+        QState(0, 0, kQueueLengthLimit * 10),
+        QState(0, Mac80211Monitor::kStopFlagPowerSave, kQueueLengthLimit)}));
+}
+
 }  // namespace shill
diff --git a/metrics.cc b/metrics.cc
index a46665d..5d0f07b 100644
--- a/metrics.cc
+++ b/metrics.cc
@@ -280,6 +280,18 @@
 const int Metrics::kMetricWifiAvailableBSSesMin = 1;
 const int Metrics::kMetricWifiAvailableBSSesNumBuckets = 10;
 
+// static
+const char Metrics::kMetricWifiStoppedTxQueueReason[] =
+    "Network.Shill.WiFi.StoppedTxQueueReason";
+// Values are defined in mac80211_monitor.h.
+
+// static
+const char Metrics::kMetricWifiStoppedTxQueueLength[] =
+    "Network.Shill.WiFi.StoppedTxQueueLength";
+const int Metrics::kMetricWifiStoppedTxQueueLengthMax = 10000;
+const int Metrics::kMetricWifiStoppedTxQueueLengthMin = 1;
+const int Metrics::kMetricWifiStoppedTxQueueLengthNumBuckets = 50;
+
 // Number of services associated with currently connected network.
 const char Metrics::kMetricServicesOnSameNetwork[] =
     "Network.Shill.ServicesOnSameNetwork";
diff --git a/metrics.h b/metrics.h
index 4eac003..2a84b98 100644
--- a/metrics.h
+++ b/metrics.h
@@ -514,6 +514,15 @@
   static const int kMetricWifiAvailableBSSesMin;
   static const int kMetricWifiAvailableBSSesNumBuckets;
 
+  // Reason that the mac80211 TX queue is stopped.
+  static const char kMetricWifiStoppedTxQueueReason[];
+
+  // Maximal queue length amongst all stopped mac80211 TX queues.
+  static const char kMetricWifiStoppedTxQueueLength[];
+  static const int kMetricWifiStoppedTxQueueLengthMax;
+  static const int kMetricWifiStoppedTxQueueLengthMin;
+  static const int kMetricWifiStoppedTxQueueLengthNumBuckets;
+
   // Number of services associated with currently connected network.
   static const char kMetricServicesOnSameNetwork[];
   static const int kMetricServicesOnSameNetworkMax;