Unify metrics_collection and metrics_daemon into metrics.

Tested new binaries on the target.
Tested incremental build.
Tested arm-generic build.

Review URL: http://codereview.chromium.org/1650006
diff --git a/metrics/Makefile b/metrics/Makefile
new file mode 100644
index 0000000..0b04736
--- /dev/null
+++ b/metrics/Makefile
@@ -0,0 +1,91 @@
+# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# Makefile for metrics utilities -- library, client and daemon
+#
+
+CCONFIG = $(shell $(PKG_CONFIG) --cflags dbus-1 glib-2.0 dbus-glib-1)
+LDCONFIG = $(shell $(PKG_CONFIG) --libs dbus-1 glib-2.0 gthread-2.0 dbus-glib-1)
+
+CFLAGS = -Wall -Werror -I/usr/include -fpic -O2 $(CCONFIG)
+CXXFLAGS = $(CFLAGS) -fno-exceptions
+
+CLIENT = metrics_client
+DAEMON = metrics_daemon
+TESTDAEMON = test_daemon
+LIB = libmetrics.a
+SHAREDLIB = libmetrics.so
+
+CLIENT_OBJS = \
+	metrics_client.o
+LIB_OBJS = \
+	metrics_library.o
+DAEMON_OBJS = \
+	marshal_void__string_boxed.o \
+	metrics_daemon.o \
+	metrics_daemon_main.o
+TESTDAEMON_OBJS = \
+	marshal_void__string_boxed.o \
+	metrics_daemon.o \
+	metrics_daemon_unittest.o
+
+DAEMON_LDFLAGS = $(LDCONFIG) -lrt -lbase -lpthread -lgflags
+TESTDAEMON_LIBS = -lgtest
+
+all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) $(TESTDAEMON)
+
+$(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB)
+	$(CXX) $(LDFLAGS) $^ -o $@
+
+$(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB)
+	$(CXX) -o $@ $^ $(DAEMON_LDFLAGS)
+
+$(TESTDAEMON): $(TESTDAEMON_OBJS) $(SHAREDLIB)
+	$(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(TESTDAEMON_LIBS)
+
+$(LIB): $(LIB_OBJS)
+	ar rcs $@ $^
+
+$(SHAREDLIB): $(LIB_OBJS)
+	$(CXX) $(LDFLAGS) -shared $^ -o $@
+
+%.o: %.cc
+	$(CXX) $(CXXFLAGS) -c $< -o $@
+
+%.o: %.c
+	$(CC) $(CFLAGS) -c $< -o $@
+
+%.c: %.list
+	glib-genmarshal --body --prefix=marshal $< > $@
+
+%.h: %.list
+	glib-genmarshal --header --prefix=marshal $< > $@
+
+# dependencies in addition to those defined by the rules
+
+metrics_daemon.o: \
+	marshal_void__string_boxed.h \
+	metrics_daemon.h \
+	network_states.h
+metrics_daemon_unittest.o: \
+	marshal_void__string_boxed.h \
+	metrics_daemon.h \
+	network_states.h
+marshal_void__string_boxed.o: \
+	marshal_void__string_boxed.h
+
+.PRECIOUS: marshal_void__string_boxed.c  # keep around for debugging
+
+install:
+	install $(CLIENT) $(DESTDIR)/usr/bin
+	install $(DAEMON) $(DESTDIR)/usr/bin
+	install $(LIB) $(DESTDIR)/usr/lib
+	install $(SHAREDLIB) $(DESTDIR)/usr/lib
+	install metrics_library.h $(DESTDIR)/usr/include
+	install syslog_parser.sh $(DESTDIR)/usr/bin
+	install omaha_tracker.sh $(DESTDIR)/usr/sbin
+
+clean:
+	rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) $(TESTDAEMON)
+	rm -f *.o marshal_void__string_boxed.[ch]
diff --git a/metrics/README b/metrics/README
new file mode 100644
index 0000000..d88992d
--- /dev/null
+++ b/metrics/README
@@ -0,0 +1,8 @@
+This packages contains all scripts and programs assoicated with metrics
+collection for both Chrome's User Metrics Server and automated performance
+metrics collection via Autotest. 
+
+The package includes the metrics daemon for Chrome OS.  This program
+runs as a daemon and collects events by polling and listening for
+d-bus signals. It then adds timing (if needed) and sends the events
+to Chrome for transport to the UMA server at Google.
diff --git a/metrics/marshal_void__string_boxed.list b/metrics/marshal_void__string_boxed.list
new file mode 100644
index 0000000..e72aa4b
--- /dev/null
+++ b/metrics/marshal_void__string_boxed.list
@@ -0,0 +1 @@
+VOID:STRING,BOXED
diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc
new file mode 100644
index 0000000..cdea012
--- /dev/null
+++ b/metrics/metrics_client.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <sys/file.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <cstdlib>
+#include <iostream>
+
+#include "metrics_library.h"
+
+using namespace std;
+
+// Usage:  metrics_client [-ab] metric_name metric_value
+int main(int argc, char** argv) {
+  bool send_to_autotest = false;
+  bool send_to_chrome = true;
+  int metric_name_index = 1;
+  int metric_value_index = 2;
+  bool print_usage = false;
+
+  if (argc >= 3) {
+    // Parse arguments
+    int flag;
+    while ((flag = getopt(argc, argv, "ab")) != -1) {
+      switch (flag) {
+        case 'a':
+          send_to_autotest = true;
+          send_to_chrome = false;
+          break;
+        case 'b':
+          send_to_chrome = true;
+          send_to_autotest = true;
+          break;
+        default:
+          print_usage = true;
+          break;
+      }
+    }
+    metric_name_index = optind;
+    metric_value_index = optind + 1;
+  } else {
+    print_usage = true;
+  }
+
+  // Metrics value should be the last argument passed
+  if ((metric_value_index + 1) != argc) {
+    print_usage = true;
+  }
+
+  if (print_usage) {
+    cerr << "Usage:  metrics_client [-ab] name value" << endl;
+    cerr << endl;
+    cerr << "  default: send metric to chrome only" << endl;
+    cerr << "  -a: send metric to autotest only" << endl;
+    cerr << "  -b: send metric to both chrome and autotest" << endl;
+    return 1;
+  }
+
+  // Send metrics
+  if (send_to_autotest) {
+    MetricsLibrary::SendToAutotest(argv[metric_name_index],
+                                   argv[metric_value_index]);
+  }
+  if (send_to_chrome) {
+    MetricsLibrary::SendToChrome(argv[metric_name_index],
+                                 argv[metric_value_index]);
+  }
+  return 0;
+}
diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc
new file mode 100644
index 0000000..a924b8a
--- /dev/null
+++ b/metrics/metrics_daemon.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "metrics_daemon.h"
+#include "metrics_library.h"
+
+#include <glib-object.h>
+
+extern "C" {
+#include "marshal_void__string_boxed.h"
+}
+
+#include <base/logging.h>
+
+#define SAFE_MESSAGE(e) ((e && e->message) ? e->message : "unknown error")
+
+MetricsDaemon::NetworkState
+MetricsDaemon::network_states_[MetricsDaemon::kNumberNetworkStates] = {
+#define STATE(name, capname) { #name, "Connman" # capname },
+#include "network_states.h"
+};
+
+void MetricsDaemon::Run(bool run_as_daemon, bool testing) {
+  Init(testing);
+  if (!run_as_daemon || daemon(0, 0) == 0) {
+    Loop();
+  }
+}
+
+void MetricsDaemon::Init(bool testing) {
+  testing_ = testing;
+  network_state_id_ = kUnknownNetworkStateId;
+
+  ::g_thread_init(NULL);
+  ::g_type_init();
+  ::dbus_g_thread_init();
+
+  ::GError* error = NULL;
+  ::DBusGConnection* dbc = ::dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
+  // Note that LOG(FATAL) terminates the process; otherwise we'd have to worry
+  // about leaking |error|.
+  LOG_IF(FATAL, dbc == NULL) <<
+    "cannot connect to dbus: " << SAFE_MESSAGE(error);
+
+  ::DBusGProxy* net_proxy = ::dbus_g_proxy_new_for_name(
+      dbc, "org.moblin.connman", "/", "org.moblin.connman.Metrics");
+  LOG_IF(FATAL, net_proxy == NULL) << "no dbus proxy for network";
+
+#if 0
+  // Unclear how soon one can call dbus_g_type_get_map().  Doing it before the
+  // call to dbus_g_bus_get() results in a (non-fatal) assertion failure.
+  // GetProperties returns a hash table.
+  hashtable_gtype = ::dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
+                                          G_TYPE_VALUE);
+#endif
+
+  dbus_g_object_register_marshaller(marshal_VOID__STRING_BOXED,
+                                    G_TYPE_NONE,
+                                    G_TYPE_STRING,
+                                    G_TYPE_VALUE,
+                                    G_TYPE_INVALID);
+  ::dbus_g_proxy_add_signal(net_proxy, "ConnectionStateChanged",
+                            G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
+  ::dbus_g_proxy_connect_signal(net_proxy, "ConnectionStateChanged",
+                                G_CALLBACK(&StaticNetSignalHandler),
+                                this, NULL);
+}
+
+void MetricsDaemon::Loop() {
+  ::GMainLoop* loop = ::g_main_loop_new(NULL, false);
+  ::g_main_loop_run(loop);
+}
+
+void MetricsDaemon::StaticNetSignalHandler(::DBusGProxy* proxy,
+                                           const char* property,
+                                           const ::GValue* value,
+                                           void *data) {
+  (static_cast<MetricsDaemon*>(data))->NetSignalHandler(proxy, property, value);
+}
+
+void MetricsDaemon::NetSignalHandler(::DBusGProxy* proxy,
+                                     const char* property,
+                                     const ::GValue* value) {
+  if (strcmp("ConnectionState", property) != 0) {
+    return;
+  }
+
+  const char* newstate = static_cast<const char*>(g_value_get_string(value));
+  LogNetworkStateChange(newstate);
+}
+
+void MetricsDaemon::LogNetworkStateChange(const char* newstate) {
+  NetworkStateId new_id = GetNetworkStateId(newstate);
+  if (new_id == kUnknownNetworkStateId) {
+    LOG(WARNING) << "unknown network connection state " << newstate;
+    return;
+  }
+  NetworkStateId old_id = network_state_id_;
+  if (new_id == old_id) {  // valid new state and no change
+    return;
+  }
+  struct timeval now;
+  if (gettimeofday(&now, NULL) != 0) {
+    PLOG(WARNING) << "gettimeofday";
+  }
+  if (old_id != kUnknownNetworkStateId) {
+    struct timeval diff;
+    timersub(&now, &network_state_start_, &diff);
+    int diff_ms = diff.tv_usec / 1000 + diff.tv_sec * 1000;
+    // Saturates rather than overflowing.  We expect this to be statistically
+    // insignificant, since INT_MAX milliseconds is 24.8 days.
+    if (diff.tv_sec >= INT_MAX / 1000) {
+      diff_ms = INT_MAX;
+    }
+    char buffer[100];
+    snprintf(buffer, sizeof(buffer), "%d", diff_ms);
+    if (testing_) {
+      TestPublishMetric(network_states_[old_id].stat_name, buffer);
+    } else {
+      ChromePublishMetric(network_states_[old_id].stat_name, buffer);
+    }
+  }
+  network_state_id_ = new_id;
+  network_state_start_ = now;
+}
+
+MetricsDaemon::NetworkStateId
+MetricsDaemon::GetNetworkStateId(const char* state_name) {
+  for (int i = 0; i < kNumberNetworkStates; i++) {
+    if (strcmp(state_name, network_states_[i].name) == 0) {
+      return static_cast<NetworkStateId>(i);
+    }
+  }
+  return static_cast<NetworkStateId>(-1);
+}
+
+void MetricsDaemon::ChromePublishMetric(const char* name, const char* value) {
+  MetricsLibrary::SendToChrome(name, value);
+}
+
+void MetricsDaemon::TestPublishMetric(const char* name, const char* value) {
+  LOG(INFO) << "received metric: " << name << " " << value;
+}
diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h
new file mode 100644
index 0000000..2ac1ea0
--- /dev/null
+++ b/metrics/metrics_daemon.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef METRICS_DAEMON_H_
+#define METRICS_DAEMON_H_
+
+#include <dbus/dbus-glib.h>
+#include <sys/time.h>
+#include <time.h>
+
+class MetricsDaemon {
+
+ public:
+  MetricsDaemon()
+      : network_state_id_(kUnknownNetworkStateId) {
+  }
+  ~MetricsDaemon() {}
+
+  // Does all the work.  If |run_as_daemon| is true, daemonize by forking.  If
+  // |testing| is true, log the stats instead of sending them to Chrome.
+  void Run(bool run_as_daemon, bool testing);
+
+ private:
+  // Shared with Chrome for transport.
+  static const char* kMetricsFilePath;
+  static const int kMetricsMessageMaxLength = 4096;
+
+  // The network states.  See network_states.h.
+  typedef enum {
+    // Initial/unknown network state id.
+    kUnknownNetworkStateId = -1,
+#define STATE(name, capname) kNetworkState ## capname,
+#include "network_states.h"
+    kNumberNetworkStates
+  } NetworkStateId;
+
+  typedef struct {
+    const char* name;
+    const char* stat_name;
+  } NetworkState;
+
+  // Initializes.
+  void Init(bool testing);
+
+  // Creates the event loop and enters it.
+  void Loop();
+
+  // Static callback for network events on DBus.
+  static void StaticNetSignalHandler(::DBusGProxy* proxy, const char* property,
+                                     const ::GValue* value, void* data);
+
+  // Callback for network events on DBus.
+  void NetSignalHandler(::DBusGProxy* proxy, const char* property,
+                        const ::GValue* value);
+
+  // This is called at each network state change.  The new state is identified
+  // by the string @newstate.  As a side effect, this method ships to Chrome
+  // (or prints to stdout when testing) the name and duration of the state
+  // that has ended.
+  void LogNetworkStateChange(const char* newstate);
+
+  // Given a string with the name of a state, returns the id for the state.
+  NetworkStateId GetNetworkStateId(const char* state_name);
+
+  // Sends a stat to Chrome for transport to UMA.
+  void ChromePublishMetric(const char* name, const char* value);
+
+  // Prints a stat for testing.
+  void TestPublishMetric(const char* name, const char* value);
+
+#if 0
+  // Fetches a name-value hash table from DBus.
+  bool GetProperties(::DBusGProxy* proxy, ::GHashTable** table);
+
+  // The type descriptor for a glib hash table.
+  GType hashtable_gtype;
+#endif
+
+  // Array of network states of interest.
+  static NetworkState network_states_[kNumberNetworkStates];
+
+  bool testing_;                           // just testing
+  NetworkStateId network_state_id_;        // id of current state
+  struct timeval network_state_start_;     // when current state was entered
+};
+
+#endif  // METRICS_DAEMON_H_
diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc
new file mode 100644
index 0000000..302bdcb
--- /dev/null
+++ b/metrics/metrics_daemon_main.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include <gflags/gflags.h>
+
+#include "metrics_daemon.h"
+
+DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)");
+
+int main(int argc, char** argv) {
+  MetricsDaemon::MetricsDaemon d;
+  google::ParseCommandLineFlags(&argc, &argv, true);
+  d.Run(FLAGS_daemon, false);
+}
diff --git a/metrics/metrics_daemon_unittest.cc b/metrics/metrics_daemon_unittest.cc
new file mode 100644
index 0000000..222cefa
--- /dev/null
+++ b/metrics/metrics_daemon_unittest.cc
@@ -0,0 +1,10 @@
+// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "metrics_daemon.h"
+
+int main(int argc, char** argv) {
+  MetricsDaemon d;
+  d.Run(false, true);
+}
diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc
new file mode 100644
index 0000000..99ad616
--- /dev/null
+++ b/metrics/metrics_library.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ * metrics_library.cc
+ *
+ *  Created on: Dec 1, 2009
+ *      Author: sosa
+ */
+
+#include "metrics_library.h"
+
+#include <errno.h>
+#include <sys/file.h>
+#include <string.h>
+#include <stdio.h>
+
+#define READ_WRITE_ALL_FILE_FLAGS \
+  (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
+
+static const char kAutotestPath[] = "/tmp/.chromeos-metrics-autotest";
+static const char kChromePath[] = "/tmp/.chromeos-metrics";
+static const int kBufferSize = 4096;
+
+using namespace std;
+
+// TODO(sosa@chromium.org) - use Chromium logger instead of stderr
+void MetricsLibrary::PrintError(const char *message, const char *file,
+                               int code) {
+  const char *kProgramName = "metrics_library";
+  if (code == 0) {
+    fprintf(stderr, "%s: %s\n", kProgramName, message);
+  } else if (file == NULL) {
+    fprintf(stderr, "%s: ", kProgramName);
+    perror(message);
+  } else {
+    fprintf(stderr, "%s: %s: ", kProgramName, file);
+    perror(message);
+  }
+}
+
+void MetricsLibrary::SendToAutotest(string name, string value) {
+  FILE *autotest_file = fopen(kAutotestPath, "a+");
+  if (autotest_file == NULL) {
+    PrintError("fopen", kAutotestPath, errno);
+    return;
+  }
+
+  fprintf(autotest_file, "%s=%s\n", name.c_str(), value.c_str());
+  fclose(autotest_file);
+}
+
+void MetricsLibrary::SendToChrome(string name, string value) {
+  int chrome_fd = open(kChromePath,
+                       O_WRONLY | O_APPEND | O_CREAT,
+                       READ_WRITE_ALL_FILE_FLAGS);
+  // If we failed to open it, return
+  if (chrome_fd < 0) {
+    PrintError("open", kChromePath, errno);
+    return;
+  }
+
+  // Need to chmod because open flags are anded with umask.
+  if (fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS) < 0) {
+    PrintError("fchmod", kChromePath, errno);
+    close(chrome_fd);
+    return;
+  }
+
+  // Grab an exclusive lock to protect Chrome from truncating underneath us
+  if (flock(chrome_fd, LOCK_EX) < 0) {
+    PrintError("flock", kChromePath, errno);
+    close(chrome_fd);
+    return;
+  }
+
+  // Message format is: LENGTH (binary), NAME, VALUE
+  char message[kBufferSize];
+  char *curr_ptr = message;
+  int32_t message_length =
+      name.length() + value.length() + 2 + sizeof(message_length);
+  if (message_length > static_cast<int32_t>(sizeof(message)))
+    PrintError("name/value too long", NULL, 0);
+
+  // Make sure buffer is blanked
+  memset(message, 0, sizeof(message));
+  memcpy(curr_ptr, &message_length, sizeof(message_length));
+  curr_ptr += sizeof(message_length);
+  strncpy(curr_ptr, name.c_str(), name.length());
+  curr_ptr += name.length() + 1;
+  strncpy(curr_ptr, value.c_str(), value.length());
+  if (write(chrome_fd, message, message_length) != message_length)
+    PrintError("write", kChromePath, errno);
+
+  // Release the file lock and close file
+  if (flock(chrome_fd, LOCK_UN) < 0)
+    PrintError("unlock", kChromePath, errno);
+  close(chrome_fd);
+}
diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h
new file mode 100644
index 0000000..f1268c9
--- /dev/null
+++ b/metrics/metrics_library.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ * metrics_library.h
+ *
+ *  Created on: Dec 1, 2009
+ *      Author: sosa
+ */
+
+#ifndef METRICS_LIBRARY_H_
+#define METRICS_LIBRARY_H_
+
+#include <stdio.h>
+#include <string>
+
+// TODO(sosa@chromium.org): Add testing for send methods
+
+// Library used to send metrics both Autotest and Chrome
+class MetricsLibrary {
+ public:
+  // Sends histogram data to Chrome.
+  static void SendToChrome(std::string name, std::string value);
+  // Sends to Autotest.
+  static void SendToAutotest(std::string name, std::string value);
+
+ private:
+  // Prints message to stderr
+  static void PrintError(const char *message, const char *file, int code);
+};
+
+#endif /* METRICS_LIBRARY_H_ */
diff --git a/metrics/network_states.h b/metrics/network_states.h
new file mode 100644
index 0000000..69205ed
--- /dev/null
+++ b/metrics/network_states.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A table of network states, to be included when building tabular things.
+//
+// This file is used to construct two things: an enumerated type in
+// metrics_daemon.h, and a table of structures with state names in
+// metrics_daemon.cc.  Including this file ensures that the two tables are
+// always in sync (and saves typing).  I don't know of other ways of achieving
+// the same result in C/C++, but it doesn't mean there isn't one.
+
+// Before you include this file, define STATE to do something useful, or else
+// if will be a no-op.  STATE will be undefined on exit.  Don't worry about
+// collisions for the STATE macro (as long as it's a macro) because the
+// compiler will flag them---in that case, just change the name.  If someone is
+// misguided enough to use STATE for something other than a macro, the error
+// messages will be slightly more complicated.
+
+
+#ifndef STATE
+#define STATE(name, capname)
+#endif
+
+STATE(association, Association)
+STATE(configuration, Configuration)
+STATE(disconnect, Disconnect)
+STATE(failure, Failure)
+STATE(idle, Idle)
+STATE(offline, Offline)
+STATE(online, Online)
+STATE(ready, Ready)
+
+#undef STATE
diff --git a/metrics/omaha_tracker.sh b/metrics/omaha_tracker.sh
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/metrics/omaha_tracker.sh
diff --git a/metrics/syslog_parser.sh b/metrics/syslog_parser.sh
new file mode 100755
index 0000000..7f3003f
--- /dev/null
+++ b/metrics/syslog_parser.sh
@@ -0,0 +1,69 @@
+#! /bin/sh
+
+# This script parses /var/log/syslog for messages from programs that log
+# uptime and disk stats (number of sectors read).  It then outputs
+# these stats in a format usable by the metrics collector, which forwards
+# them to autotest and UMA.
+
+# To add a new metric add a line below, as PROGRAM_NAME  METRIC_NAME.
+# PROGRAM_NAME is the name of the job whose start time we
+# are interested in.  METRIC_NAME is the prefix we want to use for
+# reporting to UMA and autotest.  The script prepends "Time" and
+# "Sectors" to METRIC_NAME for the two available measurements, uptime
+# and number of sectors read thus far.
+
+# You will need to emit messages similar to the following in order to add a 
+# a metric using this process.  You will need to emit both a start and stop
+# time and the metric reported will be the difference in values
+
+# Nov 15 08:05 localhost PROGRAM_NAME[822]: start METRIC_NAME time 12 sectors 56
+# Nov 15 08:05 localhost PROGRAM_NAME[822]: stop METRIC_NAME time 24 sectors 68
+
+# If you add metrics without a start, it is assumed you are requesting the
+# time differece from system start
+
+# Metrics we are interested in measuring
+METRICS="
+upstart start_x
+"
+
+first=1
+program=""
+
+# Get the metrics for all things
+for m in $METRICS
+do
+  if [ $first -eq 1 ]
+  then
+    first=0
+    program_name=$m 
+  else
+    first=1
+    metrics_name=$m       
+         
+    # Example of line from /var/log/messages:
+    # Nov 15 08:05:42 localhost connmand[822]: start metric time 12 sectors 56
+    # "upstart:" is $5, 1234 is $9, etc.
+    program="${program}/$program_name([[0-9]+]:|:) start $metrics_name/\
+    {
+      metrics_start[\"${metrics_name}Time\"] = \$9;
+      metrics_start[\"${metrics_name}Sectors\"] = \$11;            
+    }"
+    program="${program}/$program_name([[0-9]+]:|:) stop $metrics_name/\
+    { 
+        metrics_stop[\"${metrics_name}Time\"] = \$9;
+        metrics_stop[\"${metrics_name}Sectors\"] = \$11;
+    }"
+  fi      
+done
+
+# Do all the differencing here
+program="${program}\
+END{
+  for (i in metrics_stop) {
+    value_time = metrics_stop[i] - metrics_start[i];
+    print i \"=\" value_time;
+  }
+}"
+
+exec awk "$program" /var/log/syslog
\ No newline at end of file