Add generic "event of interest" enumerated histogram.

This change aims to simplify the addition of events we may
want to track in the field, more specifically hard-to-reproduce
bugs.  Before this change, this requires creating a new histogram
and registering it via histograms.xml, which is not in the Chrome OS
repositories.  With this change, a new event just requires claiming
an event name (such as ModemManagerCommandSendFailure) in
metrics_library.cc, and running "metrics_client -v <event name>"
or calling SendCrosEventToUMA(event_name).

I can make up a bug for this.  Or not.

BUG=none
TEST=compiled

Change-Id: I9c56b58310f0d22e77624edee7fe6149abd60a49
Reviewed-on: https://gerrit.chromium.org/gerrit/45322
Commit-Queue: Luigi Semenzato <semenzato@chromium.org>
Reviewed-by: Luigi Semenzato <semenzato@chromium.org>
Tested-by: Luigi Semenzato <semenzato@chromium.org>
diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc
index 86bec73..6ffc13b 100644
--- a/metrics/metrics_client.cc
+++ b/metrics/metrics_client.cc
@@ -11,6 +11,7 @@
   fprintf(stderr,
           "Usage:  metrics_client [-ab] [-t] name sample min max nbuckets\n"
           "        metrics_client [-ab] -e   name sample max\n"
+          "        metrics_client [-ab] -v   event\n"
           "        metrics_client -u action\n"
           "        metrics_client [-cg]\n"
           "\n"
@@ -22,7 +23,9 @@
           "  -e: send linear/enumeration histogram data\n"
           "  -g: return exit status 0 if machine in guest mode, 1 otherwise\n"
           "  -t: convert sample from double seconds to int milliseconds\n"
-          "  -u: send a user action to Chrome\n");
+          "  -u: send a user action to Chrome\n"
+          "  -v: send a Platform.CrOSEvent enum histogram sample\n"
+          );
   exit(1);
 }
 
@@ -69,6 +72,19 @@
   return 0;
 }
 
+static int SendCrosEvent(char* argv[], int action_index) {
+  const char* event = argv[action_index];
+  bool result;
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  result = metrics_lib.SendCrosEventToUMA(event);
+  if (!result) {
+    fprintf(stderr, "metrics_client: could not send event %s\n", event);
+    return 1;
+  }
+  return 0;
+}
+
 static int HasConsent() {
   MetricsLibrary metrics_lib;
   metrics_lib.Init();
@@ -85,6 +101,7 @@
   enum Mode {
     kModeSendStats,
     kModeSendUserAction,
+    kModeSendCrosEvent,
     kModeHasConsent,
     kModeIsGuestMode
   } mode = kModeSendStats;
@@ -95,7 +112,7 @@
 
   // Parse arguments
   int flag;
-  while ((flag = getopt(argc, argv, "abcegtu")) != -1) {
+  while ((flag = getopt(argc, argv, "abcegtuv")) != -1) {
     switch (flag) {
       case 'a':
         mode = kModeSendStats;
@@ -122,6 +139,9 @@
       case 'u':
         mode = kModeSendUserAction;
         break;
+      case 'v':
+        mode = kModeSendCrosEvent;
+        break;
       default:
         ShowUsage();
         break;
@@ -134,6 +154,8 @@
     expected_args = send_enum ? 3 : 5;
   else if (mode == kModeSendUserAction)
     expected_args = 1;
+  else if (mode == kModeSendCrosEvent)
+    expected_args = 1;
 
   if ((arg_index + expected_args) != argc) {
     ShowUsage();
@@ -152,6 +174,8 @@
                        send_to_chrome);
     case kModeSendUserAction:
       return SendUserAction(argv, arg_index);
+    case kModeSendCrosEvent:
+      return SendCrosEvent(argv, arg_index);
     case kModeHasConsent:
       return HasConsent();
     case kModeIsGuestMode:
diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc
index fe48122..a2227ac 100644
--- a/metrics/metrics_library.cc
+++ b/metrics/metrics_library.cc
@@ -24,6 +24,8 @@
 static const char kUMAEventsPath[] = "/var/log/metrics/uma-events";
 static const char kConsentFile[] = "/home/chronos/Consent To Send Stats";
 static const int32_t kBufferSize = 1024;
+static const char kCrosEventHistogramName[] = "Platform.CrOSEvent";
+static const int kCrosEventHistogramMax = 100;
 
 time_t MetricsLibrary::cached_enabled_time_ = 0;
 bool MetricsLibrary::cached_enabled_ = false;
@@ -306,3 +308,19 @@
 void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) {
   policy_provider_.reset(provider);
 }
+
+bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) {
+  int n;
+  /* Add new events here.
+   *
+   * Whoever adds the second event (if anybody) please be so kind to change
+   * this to a map lookup.  (Or at least change "second" above to "third",
+   * etc.)
+   */
+  if (event.compare("ModemManagerCommandSendFailure") == 0) {
+    n = 0;
+  } else {
+    return false;
+  }
+  return SendEnumToUMA(kCrosEventHistogramName, n, kCrosEventHistogramMax);
+}
diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h
index 2539f02..ce6985f 100644
--- a/metrics/metrics_library.h
+++ b/metrics/metrics_library.h
@@ -90,6 +90,14 @@
   // has occurred.  Used by UMA to generate stability statistics.
   bool SendCrashToUMA(const char *crash_kind);
 
+  // Sends a "generic Chrome OS event" to UMA.  This is an event name
+  // that is translated into an enumerated histogram entry.  Event names
+  // are added to metrics_library.cc.  Optionally, they can be added
+  // to histograms.xml---but part of the reason for this is to simplify
+  // the addition of events (at the cost of having to look them up by
+  // number in the histograms dashboard).
+  bool SendCrosEventToUMA(const std::string& event);
+
   // Sends to Autotest and returns true on success.
   static bool SendToAutotest(const std::string& name, int value);