Add sparse histograms to the metrics library and metrics client.

Samples to these histograms are any 32-bit int value.

BUG=chromium:222189
TEST=manual

Change-Id: Ic8d5773d05d717a275c4a4b5616e0e4c307337b8
Reviewed-on: https://gerrit.chromium.org/gerrit/45897
Tested-by: Luigi Semenzato <semenzato@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Commit-Queue: Luigi Semenzato <semenzato@chromium.org>
Reviewed-by: Luigi Semenzato <semenzato@chromium.org>
diff --git a/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc
index 8946699..5c7553c 100644
--- a/metrics/c_metrics_library.cc
+++ b/metrics/c_metrics_library.cc
@@ -43,6 +43,14 @@
   return lib->SendEnumToUMA(std::string(name), sample, max);
 }
 
+extern "C" int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle,
+                                              const char* name, int sample) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendSparseToUMA(std::string(name), sample);
+}
+
 extern "C" int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle,
                                                   const char* action) {
   MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
diff --git a/metrics/c_metrics_library.h b/metrics/c_metrics_library.h
index 5c7003d..28ae916 100644
--- a/metrics/c_metrics_library.h
+++ b/metrics/c_metrics_library.h
@@ -28,6 +28,10 @@
 int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
                                  const char* name, int sample, int max);
 
+// C wrapper for MetricsLibrary::SendSparseToUMA.
+int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle,
+                                   const char* name, int sample);
+
 // C wrapper for MetricsLibrary::SendUserActionToUMA.
 int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle,
                                        const char* action);
diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc
index 6ffc13b..cdf09a2 100644
--- a/metrics/metrics_client.cc
+++ b/metrics/metrics_client.cc
@@ -7,10 +7,21 @@
 
 #include "metrics_library.h"
 
+enum Mode {
+    kModeSendSample,
+    kModeSendEnumSample,
+    kModeSendSparseSample,
+    kModeSendUserAction,
+    kModeSendCrosEvent,
+    kModeHasConsent,
+    kModeIsGuestMode,
+};
+
 void ShowUsage() {
   fprintf(stderr,
           "Usage:  metrics_client [-ab] [-t] name sample min max nbuckets\n"
           "        metrics_client [-ab] -e   name sample max\n"
+          "        metrics_client [-ab] -s   name sample\n"
           "        metrics_client [-ab] -v   event\n"
           "        metrics_client -u action\n"
           "        metrics_client [-cg]\n"
@@ -22,6 +33,7 @@
           "  -c: return exit status 0 if user consents to stats, 1 otherwise\n"
           "  -e: send linear/enumeration histogram data\n"
           "  -g: return exit status 0 if machine in guest mode, 1 otherwise\n"
+          "  -s: send a sparse histogram sample\n"
           "  -t: convert sample from double seconds to int milliseconds\n"
           "  -u: send a user action to Chrome\n"
           "  -v: send a Platform.CrOSEvent enum histogram sample\n"
@@ -31,7 +43,7 @@
 
 static int SendStats(char* argv[],
                      int name_index,
-                     bool send_enum,
+                     enum Mode mode,
                      bool secs_to_msecs,
                      bool send_to_autotest,
                      bool send_to_chrome) {
@@ -51,7 +63,9 @@
   if (send_to_chrome) {
     MetricsLibrary metrics_lib;
     metrics_lib.Init();
-    if (send_enum) {
+    if (mode == kModeSendSparseSample) {
+      metrics_lib.SendSparseToUMA(name, sample);
+    } else if (mode == kModeSendEnumSample) {
       int max = atoi(argv[name_index + 2]);
       metrics_lib.SendEnumToUMA(name, sample, max);
     } else {
@@ -98,29 +112,20 @@
 }
 
 int main(int argc, char** argv) {
-  enum Mode {
-    kModeSendStats,
-    kModeSendUserAction,
-    kModeSendCrosEvent,
-    kModeHasConsent,
-    kModeIsGuestMode
-  } mode = kModeSendStats;
+  enum Mode mode = kModeSendSample;
   bool send_to_autotest = false;
   bool send_to_chrome = true;
-  bool send_enum = false;
   bool secs_to_msecs = false;
 
   // Parse arguments
   int flag;
-  while ((flag = getopt(argc, argv, "abcegtuv")) != -1) {
+  while ((flag = getopt(argc, argv, "abcegstuv")) != -1) {
     switch (flag) {
       case 'a':
-        mode = kModeSendStats;
         send_to_autotest = true;
         send_to_chrome = false;
         break;
       case 'b':
-        mode = kModeSendStats;
         send_to_chrome = true;
         send_to_autotest = true;
         break;
@@ -128,11 +133,14 @@
         mode = kModeHasConsent;
         break;
       case 'e':
-        send_enum = true;
+        mode = kModeSendEnumSample;
         break;
       case 'g':
         mode = kModeIsGuestMode;
         break;
+      case 's':
+        mode = kModeSendSparseSample;
+        break;
       case 't':
         secs_to_msecs = true;
         break;
@@ -150,8 +158,12 @@
   int arg_index = optind;
 
   int expected_args = 0;
-  if (mode == kModeSendStats)
-    expected_args = send_enum ? 3 : 5;
+  if (mode == kModeSendSample)
+    expected_args = 5;
+  else if (mode == kModeSendEnumSample)
+    expected_args = 3;
+  else if (mode == kModeSendSparseSample)
+    expected_args = 2;
   else if (mode == kModeSendUserAction)
     expected_args = 1;
   else if (mode == kModeSendCrosEvent)
@@ -162,13 +174,15 @@
   }
 
   switch(mode) {
-    case kModeSendStats:
-      if (send_enum && secs_to_msecs) {
+    case kModeSendSample:
+    case kModeSendEnumSample:
+    case kModeSendSparseSample:
+      if ((mode != kModeSendSample) && secs_to_msecs) {
         ShowUsage();
       }
       return SendStats(argv,
                        arg_index,
-                       send_enum,
+                       mode,
                        secs_to_msecs,
                        send_to_autotest,
                        send_to_chrome);
diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc
index 1c7c28d..e5aaae6 100644
--- a/metrics/metrics_library.cc
+++ b/metrics/metrics_library.cc
@@ -290,6 +290,19 @@
   return SendMessageToChrome(message_length, message);
 }
 
+bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
+  // Format the message.
+  char message[kBufferSize];
+  int32_t message_length =
+      FormatChromeMessage(kBufferSize, message, "sparsehistogram%c%s %d",
+                          '\0', name.c_str(), sample);
+  if (message_length < 0)
+    return false;
+
+  // Send the message.
+  return SendMessageToChrome(message_length, message);
+}
+
 bool MetricsLibrary::SendUserActionToUMA(const std::string& action) {
   // Format the message.
   char message[kBufferSize];
diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h
index ce6985f..9a4c59b 100644
--- a/metrics/metrics_library.h
+++ b/metrics/metrics_library.h
@@ -20,6 +20,7 @@
   virtual bool SendToUMA(const std::string& name, int sample,
                          int min, int max, int nbuckets) = 0;
   virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0;
+  virtual bool SendSparseToUMA(const std::string& name, int sample) = 0;
   virtual bool SendUserActionToUMA(const std::string& action) = 0;
   virtual ~MetricsLibraryInterface() {}
 };
@@ -74,6 +75,12 @@
   // normal, while 100 is high).
   bool SendEnumToUMA(const std::string& name, int sample, int max);
 
+  // Sends sparse histogram sample to Chrome for transport to UMA.  Returns
+  // true on success.
+  //
+  // |sample| is the 32-bit integer value to be recorded.
+  bool SendSparseToUMA(const std::string& name, int sample);
+
   // Sends a user action to Chrome for transport to UMA and returns true on
   // success. This method results in the equivalent of an asynchronous
   // non-blocking RPC to UserMetrics::RecordAction.  The new metric must be
diff --git a/metrics/metrics_library_mock.h b/metrics/metrics_library_mock.h
index 9a5d59e..537f4e4 100644
--- a/metrics/metrics_library_mock.h
+++ b/metrics/metrics_library_mock.h
@@ -18,6 +18,7 @@
                                int min, int max, int nbuckets));
   MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample,
                                    int max));
+  MOCK_METHOD2(SendSparseToUMA, bool(const std::string& name, int sample));
   MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action));
 };
 
diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc
index 3a5edcd..03ccd4b 100644
--- a/metrics/metrics_library_test.cc
+++ b/metrics/metrics_library_test.cc
@@ -230,6 +230,17 @@
   EXPECT_EQ(0, memcmp(exp, buf, kLen));
 }
 
+TEST_F(MetricsLibraryTest, SendSparseToUMA) {
+  char buf[100];
+  const int kLen = 4 + sizeof("sparsehistogram") + sizeof("Test.Sparse 1234");
+  EXPECT_TRUE(lib_.SendSparseToUMA("Test.Sparse", 1234));
+  EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100));
+
+  char exp[kLen];
+  sprintf(exp, "%c%c%c%csparsehistogram%cTest.Sparse 1234", kLen, 0, 0, 0, 0);
+  EXPECT_EQ(0, memcmp(exp, buf, kLen));
+}
+
 TEST_F(MetricsLibraryTest, SendCrashToUMA) {
   EXPECT_TRUE(lib_.SendCrashToUMA("kernel"));
   char exp[100];