http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/d6abb29d/be/src/kudu/util/metrics-test.cc
----------------------------------------------------------------------
diff --git a/be/src/kudu/util/metrics-test.cc b/be/src/kudu/util/metrics-test.cc
new file mode 100644
index 0000000..7493056
--- /dev/null
+++ b/be/src/kudu/util/metrics-test.cc
@@ -0,0 +1,309 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <gtest/gtest.h>
+#include <rapidjson/document.h>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include "kudu/gutil/bind.h"
+#include "kudu/gutil/map-util.h"
+#include "kudu/util/hdr_histogram.h"
+#include "kudu/util/jsonreader.h"
+#include "kudu/util/jsonwriter.h"
+#include "kudu/util/metrics.h"
+#include "kudu/util/test_util.h"
+
+using std::string;
+using std::unordered_set;
+using std::vector;
+
+DECLARE_int32(metrics_retirement_age_ms);
+
+namespace kudu {
+
+METRIC_DEFINE_entity(test_entity);
+
+class MetricsTest : public KuduTest {
+ public:
+  void SetUp() override {
+    KuduTest::SetUp();
+
+    entity_ = METRIC_ENTITY_test_entity.Instantiate(&registry_, "my-test");
+  }
+
+ protected:
+  MetricRegistry registry_;
+  scoped_refptr<MetricEntity> entity_;
+};
+
+METRIC_DEFINE_counter(test_entity, reqs_pending, "Requests Pending", 
MetricUnit::kRequests,
+                      "Number of requests pending");
+
+TEST_F(MetricsTest, SimpleCounterTest) {
+  scoped_refptr<Counter> requests =
+    new Counter(&METRIC_reqs_pending);
+  ASSERT_EQ("Number of requests pending", 
requests->prototype()->description());
+  ASSERT_EQ(0, requests->value());
+  requests->Increment();
+  ASSERT_EQ(1, requests->value());
+  requests->IncrementBy(2);
+  ASSERT_EQ(3, requests->value());
+}
+
+METRIC_DEFINE_gauge_uint64(test_entity, fake_memory_usage, "Memory Usage",
+                           MetricUnit::kBytes, "Test Gauge 1");
+
+TEST_F(MetricsTest, SimpleAtomicGaugeTest) {
+  scoped_refptr<AtomicGauge<uint64_t> > mem_usage =
+    METRIC_fake_memory_usage.Instantiate(entity_, 0);
+  ASSERT_EQ(METRIC_fake_memory_usage.description(), 
mem_usage->prototype()->description());
+  ASSERT_EQ(0, mem_usage->value());
+  mem_usage->IncrementBy(7);
+  ASSERT_EQ(7, mem_usage->value());
+  mem_usage->set_value(5);
+  ASSERT_EQ(5, mem_usage->value());
+}
+
+METRIC_DEFINE_gauge_int64(test_entity, test_func_gauge, "Test Gauge", 
MetricUnit::kBytes,
+                          "Test Gauge 2");
+
+static int64_t MyFunction(int* metric_val) {
+  return (*metric_val)++;
+}
+
+TEST_F(MetricsTest, SimpleFunctionGaugeTest) {
+  int metric_val = 1000;
+  scoped_refptr<FunctionGauge<int64_t> > gauge =
+    METRIC_test_func_gauge.InstantiateFunctionGauge(
+      entity_, Bind(&MyFunction, Unretained(&metric_val)));
+
+  ASSERT_EQ(1000, gauge->value());
+  ASSERT_EQ(1001, gauge->value());
+
+  gauge->DetachToCurrentValue();
+  // After detaching, it should continue to return the same constant value.
+  ASSERT_EQ(1002, gauge->value());
+  ASSERT_EQ(1002, gauge->value());
+
+  // Test resetting to a constant.
+  gauge->DetachToConstant(2);
+  ASSERT_EQ(2, gauge->value());
+}
+
+TEST_F(MetricsTest, AutoDetachToLastValue) {
+  int metric_val = 1000;
+  scoped_refptr<FunctionGauge<int64_t> > gauge =
+    METRIC_test_func_gauge.InstantiateFunctionGauge(
+        entity_, Bind(&MyFunction, Unretained(&metric_val)));
+
+  ASSERT_EQ(1000, gauge->value());
+  ASSERT_EQ(1001, gauge->value());
+  {
+    FunctionGaugeDetacher detacher;
+    gauge->AutoDetachToLastValue(&detacher);
+    ASSERT_EQ(1002, gauge->value());
+    ASSERT_EQ(1003, gauge->value());
+  }
+
+  ASSERT_EQ(1004, gauge->value());
+  ASSERT_EQ(1004, gauge->value());
+}
+
+TEST_F(MetricsTest, AutoDetachToConstant) {
+  int metric_val = 1000;
+  scoped_refptr<FunctionGauge<int64_t> > gauge =
+    METRIC_test_func_gauge.InstantiateFunctionGauge(
+        entity_, Bind(&MyFunction, Unretained(&metric_val)));
+
+  ASSERT_EQ(1000, gauge->value());
+  ASSERT_EQ(1001, gauge->value());
+  {
+    FunctionGaugeDetacher detacher;
+    gauge->AutoDetach(&detacher, 12345);
+    ASSERT_EQ(1002, gauge->value());
+    ASSERT_EQ(1003, gauge->value());
+  }
+
+  ASSERT_EQ(12345, gauge->value());
+}
+
+METRIC_DEFINE_gauge_uint64(test_entity, counter_as_gauge, "Gauge exposed as 
Counter",
+                           MetricUnit::kBytes, "Gauge exposed as Counter",
+                           EXPOSE_AS_COUNTER);
+TEST_F(MetricsTest, TEstExposeGaugeAsCounter) {
+  ASSERT_EQ(MetricType::kCounter, METRIC_counter_as_gauge.type());
+}
+
+METRIC_DEFINE_histogram(test_entity, test_hist, "Test Histogram",
+                        MetricUnit::kMilliseconds, "foo", 1000000, 3);
+
+TEST_F(MetricsTest, SimpleHistogramTest) {
+  scoped_refptr<Histogram> hist = METRIC_test_hist.Instantiate(entity_);
+  hist->Increment(2);
+  hist->IncrementBy(4, 1);
+  ASSERT_EQ(2, hist->histogram_->MinValue());
+  ASSERT_EQ(3, hist->histogram_->MeanValue());
+  ASSERT_EQ(4, hist->histogram_->MaxValue());
+  ASSERT_EQ(2, hist->histogram_->TotalCount());
+  ASSERT_EQ(6, hist->histogram_->TotalSum());
+  // TODO: Test coverage needs to be improved a lot.
+}
+
+TEST_F(MetricsTest, JsonPrintTest) {
+  scoped_refptr<Counter> bytes_seen = METRIC_reqs_pending.Instantiate(entity_);
+  bytes_seen->Increment();
+  entity_->SetAttribute("test_attr", "attr_val");
+
+  // Generate the JSON.
+  std::ostringstream out;
+  JsonWriter writer(&out, JsonWriter::PRETTY);
+  ASSERT_OK(entity_->WriteAsJson(&writer, { "*" }, MetricJsonOptions()));
+
+  // Now parse it back out.
+  JsonReader reader(out.str());
+  ASSERT_OK(reader.Init());
+
+  vector<const rapidjson::Value*> metrics;
+  ASSERT_OK(reader.ExtractObjectArray(reader.root(), "metrics", &metrics));
+  ASSERT_EQ(1, metrics.size());
+  string metric_name;
+  ASSERT_OK(reader.ExtractString(metrics[0], "name", &metric_name));
+  ASSERT_EQ("reqs_pending", metric_name);
+  int64_t metric_value;
+  ASSERT_OK(reader.ExtractInt64(metrics[0], "value", &metric_value));
+  ASSERT_EQ(1L, metric_value);
+
+  const rapidjson::Value* attributes;
+  ASSERT_OK(reader.ExtractObject(reader.root(), "attributes", &attributes));
+  string attr_value;
+  ASSERT_OK(reader.ExtractString(attributes, "test_attr", &attr_value));
+  ASSERT_EQ("attr_val", attr_value);
+
+  // Verify that, if we filter for a metric that isn't in this entity, we get 
no result.
+  out.str("");
+  ASSERT_OK(entity_->WriteAsJson(&writer, { "not_a_matching_metric" }, 
MetricJsonOptions()));
+  ASSERT_EQ("", out.str());
+}
+
+// Test that metrics are retired when they are no longer referenced.
+TEST_F(MetricsTest, RetirementTest) {
+  FLAGS_metrics_retirement_age_ms = 100;
+
+  const string kMetricName = "foo";
+  scoped_refptr<Counter> counter = METRIC_reqs_pending.Instantiate(entity_);
+  ASSERT_EQ(1, entity_->UnsafeMetricsMapForTests().size());
+
+  // Since we hold a reference to the counter, it should not get retired.
+  entity_->RetireOldMetrics();
+  ASSERT_EQ(1, entity_->UnsafeMetricsMapForTests().size());
+
+  // When we de-ref it, it should not get immediately retired, either, because
+  // we keep retirable metrics around for some amount of time. We try retiring
+  // a number of times to hit all the cases.
+  counter = nullptr;
+  for (int i = 0; i < 3; i++) {
+    entity_->RetireOldMetrics();
+    ASSERT_EQ(1, entity_->UnsafeMetricsMapForTests().size());
+  }
+
+  // If we wait for longer than the retirement time, and call retire again, 
we'll
+  // actually retire it.
+  SleepFor(MonoDelta::FromMilliseconds(FLAGS_metrics_retirement_age_ms * 1.5));
+  entity_->RetireOldMetrics();
+  ASSERT_EQ(0, entity_->UnsafeMetricsMapForTests().size());
+}
+
+TEST_F(MetricsTest, TestRetiringEntities) {
+  ASSERT_EQ(1, registry_.num_entities());
+
+  // Drop the reference to our entity.
+  entity_.reset();
+
+  // Retire metrics. Since there is nothing inside our entity, it should
+  // retire immediately (no need to loop).
+  registry_.RetireOldMetrics();
+
+  ASSERT_EQ(0, registry_.num_entities());
+}
+
+// Test that we can mark a metric to never be retired.
+TEST_F(MetricsTest, NeverRetireTest) {
+  entity_->NeverRetire(METRIC_test_hist.Instantiate(entity_));
+  FLAGS_metrics_retirement_age_ms = 0;
+
+  for (int i = 0; i < 3; i++) {
+    entity_->RetireOldMetrics();
+    ASSERT_EQ(1, entity_->UnsafeMetricsMapForTests().size());
+  }
+}
+
+TEST_F(MetricsTest, TestInstantiatingTwice) {
+  // Test that re-instantiating the same entity ID returns the same object.
+  scoped_refptr<MetricEntity> new_entity = 
METRIC_ENTITY_test_entity.Instantiate(
+      &registry_, entity_->id());
+  ASSERT_EQ(new_entity.get(), entity_.get());
+}
+
+TEST_F(MetricsTest, TestInstantiatingDifferentEntities) {
+  scoped_refptr<MetricEntity> new_entity = 
METRIC_ENTITY_test_entity.Instantiate(
+      &registry_, "some other ID");
+  ASSERT_NE(new_entity.get(), entity_.get());
+}
+
+TEST_F(MetricsTest, TestDumpJsonPrototypes) {
+  // Dump the prototype info.
+  std::ostringstream out;
+  JsonWriter w(&out, JsonWriter::PRETTY);
+  MetricPrototypeRegistry::get()->WriteAsJson(&w);
+  string json = out.str();
+
+  // Quick sanity check for one of our metrics defined in this file.
+  const char* expected =
+    "        {\n"
+    "            \"name\": \"test_func_gauge\",\n"
+    "            \"label\": \"Test Gauge\",\n"
+    "            \"type\": \"gauge\",\n"
+    "            \"unit\": \"bytes\",\n"
+    "            \"description\": \"Test Gauge 2\",\n"
+    "            \"entity_type\": \"test_entity\"\n"
+    "        }";
+  ASSERT_STR_CONTAINS(json, expected);
+
+  // Parse it.
+  rapidjson::Document d;
+  d.Parse<0>(json.c_str());
+
+  // Ensure that we got a reasonable number of metrics.
+  int num_metrics = d["metrics"].Size();
+  int num_entities = d["entities"].Size();
+  LOG(INFO) << "Parsed " << num_metrics << " metrics and " << num_entities << 
" entities";
+  ASSERT_GT(num_metrics, 5);
+  ASSERT_EQ(num_entities, 2);
+
+  // Spot-check that some metrics were properly registered and that the JSON 
was properly
+  // formed.
+  unordered_set<string> seen_metrics;
+  for (int i = 0; i < d["metrics"].Size(); i++) {
+    InsertOrDie(&seen_metrics, d["metrics"][i]["name"].GetString());
+  }
+  ASSERT_TRUE(ContainsKey(seen_metrics, "threads_started"));
+  ASSERT_TRUE(ContainsKey(seen_metrics, "test_hist"));
+}
+
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/d6abb29d/be/src/kudu/util/metrics.cc
----------------------------------------------------------------------
diff --git a/be/src/kudu/util/metrics.cc b/be/src/kudu/util/metrics.cc
new file mode 100644
index 0000000..079bf89
--- /dev/null
+++ b/be/src/kudu/util/metrics.cc
@@ -0,0 +1,686 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+#include "kudu/util/metrics.h"
+
+#include <iostream>
+#include <sstream>
+#include <map>
+#include <set>
+
+#include <gflags/gflags.h>
+
+#include "kudu/gutil/atomicops.h"
+#include "kudu/gutil/casts.h"
+#include "kudu/gutil/map-util.h"
+#include "kudu/gutil/singleton.h"
+#include "kudu/gutil/stl_util.h"
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/util/flag_tags.h"
+#include "kudu/util/hdr_histogram.h"
+#include "kudu/util/histogram.pb.h"
+#include "kudu/util/jsonwriter.h"
+#include "kudu/util/locks.h"
+#include "kudu/util/status.h"
+
+DEFINE_int32(metrics_retirement_age_ms, 120 * 1000,
+             "The minimum number of milliseconds a metric will be kept for 
after it is "
+             "no longer active. (Advanced option)");
+TAG_FLAG(metrics_retirement_age_ms, runtime);
+TAG_FLAG(metrics_retirement_age_ms, advanced);
+
+// Process/server-wide metrics should go into the 'server' entity.
+// More complex applications will define other entities.
+METRIC_DEFINE_entity(server);
+
+namespace kudu {
+
+using std::string;
+using std::vector;
+using strings::Substitute;
+
+//
+// MetricUnit
+//
+
+const char* MetricUnit::Name(Type unit) {
+  switch (unit) {
+    case kCacheHits:
+      return "hits";
+    case kCacheQueries:
+      return "queries";
+    case kBytes:
+      return "bytes";
+    case kRequests:
+      return "requests";
+    case kEntries:
+      return "entries";
+    case kRows:
+      return "rows";
+    case kCells:
+      return "cells";
+    case kConnections:
+      return "connections";
+    case kOperations:
+      return "operations";
+    case kProbes:
+      return "probes";
+    case kNanoseconds:
+      return "nanoseconds";
+    case kMicroseconds:
+      return "microseconds";
+    case kMilliseconds:
+      return "milliseconds";
+    case kSeconds:
+      return "seconds";
+    case kThreads:
+      return "threads";
+    case kTransactions:
+      return "transactions";
+    case kUnits:
+      return "units";
+    case kScanners:
+      return "scanners";
+    case kMaintenanceOperations:
+      return "operations";
+    case kBlocks:
+      return "blocks";
+    case kLogBlockContainers:
+      return "log block containers";
+    case kTasks:
+      return "tasks";
+    case kMessages:
+      return "messages";
+    case kContextSwitches:
+      return "context switches";
+    case kDataDirectories:
+      return "data directories";
+    default:
+      DCHECK(false) << "Unknown unit with type = " << unit;
+      return "UNKNOWN UNIT";
+  }
+}
+
+//
+// MetricType
+//
+
+const char* const MetricType::kGaugeType = "gauge";
+const char* const MetricType::kCounterType = "counter";
+const char* const MetricType::kHistogramType = "histogram";
+const char* MetricType::Name(MetricType::Type type) {
+  switch (type) {
+    case kGauge:
+      return kGaugeType;
+    case kCounter:
+      return kCounterType;
+    case kHistogram:
+      return kHistogramType;
+    default:
+      return "UNKNOWN TYPE";
+  }
+}
+
+//
+// MetricEntityPrototype
+//
+
+MetricEntityPrototype::MetricEntityPrototype(const char* name)
+  : name_(name) {
+  MetricPrototypeRegistry::get()->AddEntity(this);
+}
+
+MetricEntityPrototype::~MetricEntityPrototype() {
+}
+
+scoped_refptr<MetricEntity> MetricEntityPrototype::Instantiate(
+    MetricRegistry* registry,
+    const std::string& id,
+    const MetricEntity::AttributeMap& initial_attrs) const {
+  return registry->FindOrCreateEntity(this, id, initial_attrs);
+}
+
+
+//
+// MetricEntity
+//
+
+MetricEntity::MetricEntity(const MetricEntityPrototype* prototype,
+                           std::string id, AttributeMap attributes)
+    : prototype_(prototype),
+      id_(std::move(id)),
+      attributes_(std::move(attributes)) {}
+
+MetricEntity::~MetricEntity() {
+}
+
+void MetricEntity::CheckInstantiation(const MetricPrototype* proto) const {
+  CHECK_STREQ(prototype_->name(), proto->entity_type())
+    << "Metric " << proto->name() << " may not be instantiated entity of type "
+    << prototype_->name() << " (expected: " << proto->entity_type() << ")";
+}
+
+scoped_refptr<Metric> MetricEntity::FindOrNull(const MetricPrototype& 
prototype) const {
+  std::lock_guard<simple_spinlock> l(lock_);
+  return FindPtrOrNull(metric_map_, &prototype);
+}
+
+namespace {
+
+bool MatchMetricInList(const string& metric_name,
+                       const vector<string>& match_params) {
+  for (const string& param : match_params) {
+    // Handle wildcard.
+    if (param == "*") return true;
+    // The parameter is a substring match of the metric name.
+    if (metric_name.find(param) != std::string::npos) {
+      return true;
+    }
+  }
+  return false;
+}
+
+} // anonymous namespace
+
+
+Status MetricEntity::WriteAsJson(JsonWriter* writer,
+                                 const vector<string>& requested_metrics,
+                                 const MetricJsonOptions& opts) const {
+  bool select_all = MatchMetricInList(id(), requested_metrics);
+
+  // We want the keys to be in alphabetical order when printing, so we use an 
ordered map here.
+  typedef std::map<const char*, scoped_refptr<Metric> > OrderedMetricMap;
+  OrderedMetricMap metrics;
+  AttributeMap attrs;
+  {
+    // Snapshot the metrics in this registry (not guaranteed to be a 
consistent snapshot)
+    std::lock_guard<simple_spinlock> l(lock_);
+    attrs = attributes_;
+    for (const MetricMap::value_type& val : metric_map_) {
+      const MetricPrototype* prototype = val.first;
+      const scoped_refptr<Metric>& metric = val.second;
+
+      if (select_all || MatchMetricInList(prototype->name(), 
requested_metrics)) {
+        InsertOrDie(&metrics, prototype->name(), metric);
+      }
+    }
+  }
+
+  // If we had a filter, and we didn't either match this entity or any metrics 
inside
+  // it, don't print the entity at all.
+  if (!requested_metrics.empty() && !select_all && metrics.empty()) {
+    return Status::OK();
+  }
+
+  writer->StartObject();
+
+  writer->String("type");
+  writer->String(prototype_->name());
+
+  writer->String("id");
+  writer->String(id_);
+
+  writer->String("attributes");
+  writer->StartObject();
+  for (const AttributeMap::value_type& val : attrs) {
+    writer->String(val.first);
+    writer->String(val.second);
+  }
+  writer->EndObject();
+
+  writer->String("metrics");
+  writer->StartArray();
+  for (OrderedMetricMap::value_type& val : metrics) {
+    WARN_NOT_OK(val.second->WriteAsJson(writer, opts),
+                strings::Substitute("Failed to write $0 as JSON", val.first));
+
+  }
+  writer->EndArray();
+
+  writer->EndObject();
+
+  return Status::OK();
+}
+
+void MetricEntity::RetireOldMetrics() {
+  MonoTime now(MonoTime::Now());
+
+  std::lock_guard<simple_spinlock> l(lock_);
+  for (auto it = metric_map_.begin(); it != metric_map_.end();) {
+    const scoped_refptr<Metric>& metric = it->second;
+
+    if (PREDICT_TRUE(!metric->HasOneRef())) {
+      // The metric is still in use. Note that, in the case of 
"NeverRetire()", the metric
+      // will have a ref-count of 2 because it is reffed by the 
'never_retire_metrics_'
+      // collection.
+
+      // Ensure that it is not marked for later retirement (this could happen 
in the case
+      // that a metric is un-reffed and then re-reffed later by looking it up 
from the
+      // registry).
+      metric->retire_time_ = MonoTime();
+      ++it;
+      continue;
+    }
+
+    if (!metric->retire_time_.Initialized()) {
+      VLOG(3) << "Metric " << it->first << " has become un-referenced. Will 
retire after "
+              << "the retention interval";
+      // This is the first time we've seen this metric as retirable.
+      metric->retire_time_ =
+          now + MonoDelta::FromMilliseconds(FLAGS_metrics_retirement_age_ms);
+      ++it;
+      continue;
+    }
+
+    // If we've already seen this metric in a previous scan, check if it's
+    // time to retire it yet.
+    if (now < metric->retire_time_) {
+      VLOG(3) << "Metric " << it->first << " is un-referenced, but still 
within "
+              << "the retention interval";
+      ++it;
+      continue;
+    }
+
+
+    VLOG(2) << "Retiring metric " << it->first;
+    metric_map_.erase(it++);
+  }
+}
+
+void MetricEntity::NeverRetire(const scoped_refptr<Metric>& metric) {
+  std::lock_guard<simple_spinlock> l(lock_);
+  never_retire_metrics_.push_back(metric);
+}
+
+void MetricEntity::SetAttributes(const AttributeMap& attrs) {
+  std::lock_guard<simple_spinlock> l(lock_);
+  attributes_ = attrs;
+}
+
+void MetricEntity::SetAttribute(const string& key, const string& val) {
+  std::lock_guard<simple_spinlock> l(lock_);
+  attributes_[key] = val;
+}
+
+//
+// MetricRegistry
+//
+
+MetricRegistry::MetricRegistry() {
+}
+
+MetricRegistry::~MetricRegistry() {
+}
+
+Status MetricRegistry::WriteAsJson(JsonWriter* writer,
+                                   const vector<string>& requested_metrics,
+                                   const MetricJsonOptions& opts) const {
+  EntityMap entities;
+  {
+    std::lock_guard<simple_spinlock> l(lock_);
+    entities = entities_;
+  }
+
+  writer->StartArray();
+  for (const EntityMap::value_type e : entities) {
+    WARN_NOT_OK(e.second->WriteAsJson(writer, requested_metrics, opts),
+                Substitute("Failed to write entity $0 as JSON", 
e.second->id()));
+  }
+  writer->EndArray();
+
+  // Rather than having a thread poll metrics periodically to retire old ones,
+  // we'll just retire them here. The only downside is that, if no one is 
polling
+  // metrics, we may end up leaving them around indefinitely; however, metrics 
are
+  // small, and one might consider it a feature: if monitoring stops polling 
for
+  // metrics, we should keep them around until the next poll.
+  entities.clear(); // necessary to deref metrics we just dumped before doing 
retirement scan.
+  const_cast<MetricRegistry*>(this)->RetireOldMetrics();
+  return Status::OK();
+}
+
+void MetricRegistry::RetireOldMetrics() {
+  std::lock_guard<simple_spinlock> l(lock_);
+  for (auto it = entities_.begin(); it != entities_.end();) {
+    it->second->RetireOldMetrics();
+
+    if (it->second->num_metrics() == 0 && it->second->HasOneRef()) {
+      // No metrics and no external references to this entity, so we can 
retire it.
+      // Unlike retiring the metrics themselves, we don't wait for any timeout
+      // to retire them -- we assume that that timed retention has been 
satisfied
+      // by holding onto the metrics inside the entity.
+      entities_.erase(it++);
+    } else {
+      ++it;
+    }
+  }
+}
+
+//
+// MetricPrototypeRegistry
+//
+MetricPrototypeRegistry* MetricPrototypeRegistry::get() {
+  return Singleton<MetricPrototypeRegistry>::get();
+}
+
+void MetricPrototypeRegistry::AddMetric(const MetricPrototype* prototype) {
+  std::lock_guard<simple_spinlock> l(lock_);
+  metrics_.push_back(prototype);
+}
+
+void MetricPrototypeRegistry::AddEntity(const MetricEntityPrototype* 
prototype) {
+  std::lock_guard<simple_spinlock> l(lock_);
+  entities_.push_back(prototype);
+}
+
+void MetricPrototypeRegistry::WriteAsJson(JsonWriter* writer) const {
+  std::lock_guard<simple_spinlock> l(lock_);
+  MetricJsonOptions opts;
+  opts.include_schema_info = true;
+  writer->StartObject();
+
+  // Dump metric prototypes.
+  writer->String("metrics");
+  writer->StartArray();
+  for (const MetricPrototype* p : metrics_) {
+    writer->StartObject();
+    p->WriteFields(writer, opts);
+    writer->String("entity_type");
+    writer->String(p->entity_type());
+    writer->EndObject();
+  }
+  writer->EndArray();
+
+  // Dump entity prototypes.
+  writer->String("entities");
+  writer->StartArray();
+  for (const MetricEntityPrototype* p : entities_) {
+    writer->StartObject();
+    writer->String("name");
+    writer->String(p->name());
+    writer->EndObject();
+  }
+  writer->EndArray();
+
+  writer->EndObject();
+}
+
+void MetricPrototypeRegistry::WriteAsJsonAndExit() const {
+  std::ostringstream s;
+  JsonWriter w(&s, JsonWriter::PRETTY);
+  WriteAsJson(&w);
+  std::cout << s.str() << std::endl;
+  exit(0);
+}
+
+//
+// MetricPrototype
+//
+MetricPrototype::MetricPrototype(CtorArgs args) : args_(std::move(args)) {
+  MetricPrototypeRegistry::get()->AddMetric(this);
+}
+
+void MetricPrototype::WriteFields(JsonWriter* writer,
+                                  const MetricJsonOptions& opts) const {
+  writer->String("name");
+  writer->String(name());
+
+  if (opts.include_schema_info) {
+    writer->String("label");
+    writer->String(label());
+
+    writer->String("type");
+    writer->String(MetricType::Name(type()));
+
+    writer->String("unit");
+    writer->String(MetricUnit::Name(unit()));
+
+    writer->String("description");
+    writer->String(description());
+  }
+}
+
+//
+// FunctionGaugeDetacher
+//
+
+FunctionGaugeDetacher::FunctionGaugeDetacher() {
+}
+
+FunctionGaugeDetacher::~FunctionGaugeDetacher() {
+  for (const Closure& c : callbacks_) {
+    c.Run();
+  }
+}
+
+scoped_refptr<MetricEntity> MetricRegistry::FindOrCreateEntity(
+    const MetricEntityPrototype* prototype,
+    const std::string& id,
+    const MetricEntity::AttributeMap& initial_attributes) {
+  std::lock_guard<simple_spinlock> l(lock_);
+  scoped_refptr<MetricEntity> e = FindPtrOrNull(entities_, id);
+  if (!e) {
+    e = new MetricEntity(prototype, id, initial_attributes);
+    InsertOrDie(&entities_, id, e);
+  } else {
+    e->SetAttributes(initial_attributes);
+  }
+  return e;
+}
+
+//
+// Metric
+//
+Metric::Metric(const MetricPrototype* prototype)
+  : prototype_(prototype) {
+}
+
+Metric::~Metric() {
+}
+
+//
+// Gauge
+//
+
+Status Gauge::WriteAsJson(JsonWriter* writer,
+                          const MetricJsonOptions& opts) const {
+  writer->StartObject();
+
+  prototype_->WriteFields(writer, opts);
+
+  writer->String("value");
+  WriteValue(writer);
+
+  writer->EndObject();
+  return Status::OK();
+}
+
+//
+// StringGauge
+//
+
+StringGauge::StringGauge(const GaugePrototype<string>* proto,
+                         string initial_value)
+    : Gauge(proto), value_(std::move(initial_value)) {}
+
+std::string StringGauge::value() const {
+  std::lock_guard<simple_spinlock> l(lock_);
+  return value_;
+}
+
+void StringGauge::set_value(const std::string& value) {
+  std::lock_guard<simple_spinlock> l(lock_);
+  value_ = value;
+}
+
+void StringGauge::WriteValue(JsonWriter* writer) const {
+  writer->String(value());
+}
+
+//
+// Counter
+//
+// This implementation is optimized by using a striped counter. See LongAdder 
for details.
+
+scoped_refptr<Counter> CounterPrototype::Instantiate(const 
scoped_refptr<MetricEntity>& entity) {
+  return entity->FindOrCreateCounter(this);
+}
+
+Counter::Counter(const CounterPrototype* proto) : Metric(proto) {
+}
+
+int64_t Counter::value() const {
+  return value_.Value();
+}
+
+void Counter::Increment() {
+  IncrementBy(1);
+}
+
+void Counter::IncrementBy(int64_t amount) {
+  value_.IncrementBy(amount);
+}
+
+Status Counter::WriteAsJson(JsonWriter* writer,
+                            const MetricJsonOptions& opts) const {
+  writer->StartObject();
+
+  prototype_->WriteFields(writer, opts);
+
+  writer->String("value");
+  writer->Int64(value());
+
+  writer->EndObject();
+  return Status::OK();
+}
+
+/////////////////////////////////////////////////
+// HistogramPrototype
+/////////////////////////////////////////////////
+
+HistogramPrototype::HistogramPrototype(const MetricPrototype::CtorArgs& args,
+                                       uint64_t max_trackable_value, int 
num_sig_digits)
+  : MetricPrototype(args),
+    max_trackable_value_(max_trackable_value),
+    num_sig_digits_(num_sig_digits) {
+  // Better to crash at definition time that at instantiation time.
+  CHECK(HdrHistogram::IsValidHighestTrackableValue(max_trackable_value))
+      << Substitute("Invalid max trackable value on histogram $0: $1",
+                    args.name_, max_trackable_value);
+  CHECK(HdrHistogram::IsValidNumSignificantDigits(num_sig_digits))
+      << Substitute("Invalid number of significant digits on histogram $0: $1",
+                    args.name_, num_sig_digits);
+}
+
+scoped_refptr<Histogram> HistogramPrototype::Instantiate(
+    const scoped_refptr<MetricEntity>& entity) {
+  return entity->FindOrCreateHistogram(this);
+}
+
+/////////////////////////////////////////////////
+// Histogram
+/////////////////////////////////////////////////
+
+Histogram::Histogram(const HistogramPrototype* proto)
+  : Metric(proto),
+    histogram_(new HdrHistogram(proto->max_trackable_value(), 
proto->num_sig_digits())) {
+}
+
+void Histogram::Increment(int64_t value) {
+  histogram_->Increment(value);
+}
+
+void Histogram::IncrementBy(int64_t value, int64_t amount) {
+  histogram_->IncrementBy(value, amount);
+}
+
+Status Histogram::WriteAsJson(JsonWriter* writer,
+                              const MetricJsonOptions& opts) const {
+
+  HistogramSnapshotPB snapshot;
+  RETURN_NOT_OK(GetHistogramSnapshotPB(&snapshot, opts));
+  writer->Protobuf(snapshot);
+  return Status::OK();
+}
+
+Status Histogram::GetHistogramSnapshotPB(HistogramSnapshotPB* snapshot_pb,
+                                         const MetricJsonOptions& opts) const {
+  HdrHistogram snapshot(*histogram_);
+  snapshot_pb->set_name(prototype_->name());
+  if (opts.include_schema_info) {
+    snapshot_pb->set_type(MetricType::Name(prototype_->type()));
+    snapshot_pb->set_label(prototype_->label());
+    snapshot_pb->set_unit(MetricUnit::Name(prototype_->unit()));
+    snapshot_pb->set_description(prototype_->description());
+    snapshot_pb->set_max_trackable_value(snapshot.highest_trackable_value());
+    snapshot_pb->set_num_significant_digits(snapshot.num_significant_digits());
+  }
+  snapshot_pb->set_total_count(snapshot.TotalCount());
+  snapshot_pb->set_total_sum(snapshot.TotalSum());
+  snapshot_pb->set_min(snapshot.MinValue());
+  snapshot_pb->set_mean(snapshot.MeanValue());
+  snapshot_pb->set_percentile_75(snapshot.ValueAtPercentile(75));
+  snapshot_pb->set_percentile_95(snapshot.ValueAtPercentile(95));
+  snapshot_pb->set_percentile_99(snapshot.ValueAtPercentile(99));
+  snapshot_pb->set_percentile_99_9(snapshot.ValueAtPercentile(99.9));
+  snapshot_pb->set_percentile_99_99(snapshot.ValueAtPercentile(99.99));
+  snapshot_pb->set_max(snapshot.MaxValue());
+
+  if (opts.include_raw_histograms) {
+    RecordedValuesIterator iter(&snapshot);
+    while (iter.HasNext()) {
+      HistogramIterationValue value;
+      RETURN_NOT_OK(iter.Next(&value));
+      snapshot_pb->add_values(value.value_iterated_to);
+      snapshot_pb->add_counts(value.count_at_value_iterated_to);
+    }
+  }
+  return Status::OK();
+}
+
+uint64_t Histogram::CountInBucketForValueForTests(uint64_t value) const {
+  return histogram_->CountInBucketForValue(value);
+}
+
+uint64_t Histogram::TotalCount() const {
+  return histogram_->TotalCount();
+}
+
+uint64_t Histogram::MinValueForTests() const {
+  return histogram_->MinValue();
+}
+
+uint64_t Histogram::MaxValueForTests() const {
+  return histogram_->MaxValue();
+}
+double Histogram::MeanValueForTests() const {
+  return histogram_->MeanValue();
+}
+
+ScopedLatencyMetric::ScopedLatencyMetric(Histogram* latency_hist)
+  : latency_hist_(latency_hist) {
+  if (latency_hist_) {
+    time_started_ = MonoTime::Now();
+  }
+}
+
+ScopedLatencyMetric::~ScopedLatencyMetric() {
+  if (latency_hist_ != nullptr) {
+    MonoTime time_now = MonoTime::Now();
+    latency_hist_->Increment((time_now - time_started_).ToMicroseconds());
+  }
+}
+
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/d6abb29d/be/src/kudu/util/metrics.h
----------------------------------------------------------------------
diff --git a/be/src/kudu/util/metrics.h b/be/src/kudu/util/metrics.h
new file mode 100644
index 0000000..8aeaf93
--- /dev/null
+++ b/be/src/kudu/util/metrics.h
@@ -0,0 +1,1081 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+#ifndef KUDU_UTIL_METRICS_H
+#define KUDU_UTIL_METRICS_H
+
+/////////////////////////////////////////////////////
+// Kudu Metrics
+/////////////////////////////////////////////////////
+//
+// Summary
+// ------------------------------------------------------------
+//
+// This API provides a basic set of metrics primitives along the lines of the 
Coda Hale's
+// metrics library along with JSON formatted output of running metrics.
+//
+// The metrics system has a few main concepts in its data model:
+//
+// Metric Prototypes
+// -----------------
+// Every metric that may be emitted is constructed from a prototype. The 
prototype defines
+// the name of the metric, the entity it is attached to, its type, its units, 
and a description.
+//
+// Metric prototypes are defined statically using the METRIC_DEFINE_*(...) 
macros. This
+// allows us to easily enumerate a full list of every metric that might be 
emitted from a
+// server, thus allowing auto-generation of metric metadata for integration 
with
+// monitoring systems such as Cloudera Manager.
+//
+// Metric Entity Prototypes
+// ------------------------
+// The other main type in the data model is the Metric Entity. The most basic 
entity is the
+// "server" entity -- metrics such as memory usage, RPC rates, etc, are 
typically associated
+// with the server as a whole.
+//
+// Users of the metrics framework can define more entity types using the
+// METRIC_DEFINE_entity(...) macro.
+//
+// MetricEntity instances
+// -----------------------
+// Each defined Metric Entity Type serves as a prototype allowing 
instantiation of a
+// MetricEntity object. Each instance then has its own unique set of metrics. 
For
+// example, in the case of Kudu, we define a Metric Entity Type called 
'tablet', and the
+// Tablet Server instantiates one MetricEntity instance per tablet that it 
hosts.
+//
+// MetricEntity instances are instantiated within a MetricRegistry, and each 
instance is
+// expected to have a unique string identifier within that registry. To 
continue the
+// example above, a tablet entity uses its tablet ID as its unique identifier. 
These
+// identifiers are exposed to the operator and surfaced in monitoring tools.
+//
+// MetricEntity instances may also carry a key-value map of string attributes. 
These
+// attributes are directly exposed to monitoring systems via the JSON output. 
Monitoring
+// systems may use this information to allow hierarchical aggregation beteween 
entities,
+// display them to the user, etc.
+//
+// Metric instances
+// ----------------
+// Given a MetricEntity instance and a Metric Prototype, one can instantiate a 
Metric
+// instance. For example, the Kudu Tablet Server instantiates one MetricEntity 
instance
+// for each tablet, and then instantiates the 'tablet_rows_inserted' prototype 
within that
+// entity. Thus, each tablet then has a separate instance of the metric, 
allowing the end
+// operator to track the metric on a per-tablet basis.
+//
+//
+// Types of metrics
+// ------------------------------------------------------------
+// Gauge: Set or get a point-in-time value.
+//  - string: Gauge for a string value.
+//  - Primitive types (bool, int64_t/uint64_t, double): Lock-free gauges.
+// Counter: Get, reset, increment or decrement an int64_t value.
+// Histogram: Increment buckets of values segmented by configurable max and 
precision.
+//
+// Gauge vs. Counter
+// ------------------------------------------------------------
+//
+// A Counter is a metric we expect to only monotonically increase. A
+// Gauge is a metric that can decrease and increase. Use a Gauge to
+// reflect a sample, e.g., the number of transaction in-flight at a
+// given time; use a Counter when considering a metric over time,
+// e.g., exposing the number of transactions processed since start to
+// produce a metric for the number of transactions processed over some
+// time period.
+//
+// The one exception to this rule is that occasionally it may be more 
convenient to
+// implement a metric as a Gauge, even when it is logically a counter, due to 
Gauge's
+// support for fetching metric values via a bound function. In that case, you 
can
+// use the 'EXPOSE_AS_COUNTER' flag when defining the gauge prototype. For 
example:
+//
+// METRIC_DEFINE_gauge_uint64(server, threads_started,
+//                            "Threads Started",
+//                            kudu::MetricUnit::kThreads,
+//                            "Total number of threads started on this server",
+//                            kudu::EXPOSE_AS_COUNTER);
+//
+//
+// Metrics ownership
+// ------------------------------------------------------------
+//
+// Metrics are reference-counted, and one of the references is always held by 
a metrics
+// entity itself. Users of metrics should typically hold a scoped_refptr to 
their metrics
+// within class instances, so that they also hold a reference. The one 
exception to this
+// is FunctionGauges: see the class documentation below for a typical Gauge 
ownership pattern.
+//
+// Because the metrics entity holds a reference to the metric, this means that 
metrics will
+// not be immediately destructed when your class instance publishing them is 
destructed.
+// This is on purpose: metrics are retained for a configurable time interval 
even after they
+// are no longer being published. The purpose of this is to allow monitoring 
systems, which
+// only poll metrics infrequently (eg once a minute) to see the last value of 
a metric whose
+// owner was destructed in between two polls.
+//
+//
+// Example usage for server-level metrics
+// ------------------------------------------------------------
+//
+// 1) In your server class, define the top-level registry and the server 
entity:
+//
+//   MetricRegistry metric_registry_;
+//   scoped_refptr<MetricEntity> metric_entity_;
+//
+// 2) In your server constructor/initialization, construct metric_entity_. 
This instance
+//    will be plumbed through into other subsystems that want to register 
server-level
+//    metrics.
+//
+//   metric_entity_ = METRIC_ENTITY_server.Instantiate(&registry_, "some 
server identifier)");
+//
+// 3) At the top of your .cc file where you want to emit a metric, define the 
metric prototype:
+//
+//   METRIC_DEFINE_counter(server, ping_requests, "Ping Requests", 
kudu::MetricUnit::kRequests,
+//       "Number of Ping() RPC requests this server has handled since start");
+//
+// 4) In your class where you want to emit metrics, define the metric instance 
itself:
+//   scoped_refptr<Counter> ping_counter_;
+//
+// 5) In your class constructor, instantiate the metric based on the 
MetricEntity plumbed in:
+//
+//   MyClass(..., const scoped_refptr<MetricEntity>& metric_entity) :
+//     ping_counter_(METRIC_ping_requests.Instantiate(metric_entity)) {
+//   }
+//
+// 6) Where you want to change the metric value, just use the instance 
variable:
+//
+//   ping_counter_->IncrementBy(100);
+//
+//
+// Example usage for custom entity metrics
+// ------------------------------------------------------------
+// Follow the same pattern as above, but also define a metric entity 
somewhere. For example:
+//
+// At the top of your CC file:
+//
+//   METRIC_DEFINE_entity(my_entity);
+//   METRIC_DEFINE_counter(my_entity, ping_requests, "Ping Requests", 
kudu::MetricUnit::kRequests,
+//       "Number of Ping() RPC requests this particular entity has handled 
since start");
+//
+// In whatever class represents the entity:
+//
+//   entity_ = METRIC_ENTITY_my_entity.Instantiate(&registry_, my_entity_id);
+//
+// In whatever classes emit metrics:
+//
+//   scoped_refptr<Counter> ping_requests_ = 
METRIC_ping_requests.Instantiate(entity);
+//   ping_requests_->Increment();
+//
+// NOTE: at runtime, the metrics system prevents you from instantiating a 
metric in the
+// wrong entity type. This ensures that the metadata can fully describe the 
set of metric-entity
+// relationships.
+//
+// Plumbing of MetricEntity and MetricRegistry objects
+// ------------------------------------------------------------
+// Generally, the rule of thumb to follow when plumbing through entities and 
registries is
+// this: if you're creating new entities or you need to dump the registry 
contents
+// (e.g. path handlers), pass in the registry. Otherwise, pass in the entity.
+//
+// ===========
+// JSON output
+// ===========
+//
+// The first-class output format for metrics is pretty-printed JSON.
+// Such a format is relatively easy for humans and machines to read.
+//
+// The top level JSON object is an array, which contains one element per
+// entity. Each entity is an object which has its type, id, and an array
+// of metrics. Each metric contains its type, name, unit, description, value,
+// etc.
+// TODO: Output to HTML.
+//
+// Example JSON output:
+//
+// [
+//     {
+//         "type": "tablet",
+//         "id": "e95e57ba8d4d48458e7c7d35020d4a46",
+//         "attributes": {
+//           "table_id": "12345",
+//           "table_name": "my_table"
+//         },
+//         "metrics": [
+//             {
+//                 "type": "counter",
+//                 "name": "log_reader_bytes_read",
+//                 "label": "Log Reader Bytes Read",
+//                 "unit": "bytes",
+//                 "description": "Number of bytes read since tablet start",
+//                 "value": 0
+//             },
+//             ...
+//           ]
+//      },
+//      ...
+// ]
+//
+/////////////////////////////////////////////////////
+
+#include <algorithm>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <gtest/gtest_prod.h>
+
+#include "kudu/gutil/bind.h"
+#include "kudu/gutil/callback.h"
+#include "kudu/gutil/casts.h"
+#include "kudu/gutil/map-util.h"
+#include "kudu/gutil/ref_counted.h"
+#include "kudu/gutil/singleton.h"
+#include "kudu/util/atomic.h"
+#include "kudu/util/jsonwriter.h"
+#include "kudu/util/locks.h"
+#include "kudu/util/monotime.h"
+#include "kudu/util/status.h"
+#include "kudu/util/striped64.h"
+
+// Define a new entity type.
+//
+// The metrics subsystem itself defines the entity type 'server', but other
+// entity types can be registered using this macro.
+#define METRIC_DEFINE_entity(name)                               \
+  ::kudu::MetricEntityPrototype METRIC_ENTITY_##name(#name)
+
+// Convenience macros to define metric prototypes.
+// See the documentation at the top of this file for example usage.
+#define METRIC_DEFINE_counter(entity, name, label, unit, desc)   \
+  ::kudu::CounterPrototype METRIC_##name(                        \
+      ::kudu::MetricPrototype::CtorArgs(#entity, #name, label, unit, desc))
+
+#define METRIC_DEFINE_gauge_string(entity, name, label, unit, desc, ...) \
+  ::kudu::GaugePrototype<std::string> METRIC_##name(                 \
+      ::kudu::MetricPrototype::CtorArgs(#entity, #name, label, unit, desc, ## 
__VA_ARGS__))
+#define METRIC_DEFINE_gauge_bool(entity, name, label, unit, desc, ...) \
+  ::kudu::GaugePrototype<bool> METRIC_##  name(                    \
+      ::kudu::MetricPrototype::CtorArgs(#entity, #name, label, unit, desc, ## 
__VA_ARGS__))
+#define METRIC_DEFINE_gauge_int32(entity, name, label, unit, desc, ...) \
+  ::kudu::GaugePrototype<int32_t> METRIC_##name(                   \
+      ::kudu::MetricPrototype::CtorArgs(#entity, #name, label, unit, desc, ## 
__VA_ARGS__))
+#define METRIC_DEFINE_gauge_uint32(entity, name, label, unit, desc, ...) \
+  ::kudu::GaugePrototype<uint32_t> METRIC_##name(                    \
+      ::kudu::MetricPrototype::CtorArgs(#entity, #name, label, unit, desc, ## 
__VA_ARGS__))
+#define METRIC_DEFINE_gauge_int64(entity, name, label, unit, desc, ...) \
+  ::kudu::GaugePrototype<int64_t> METRIC_##name(                   \
+      ::kudu::MetricPrototype::CtorArgs(#entity, #name, label, unit, desc, ## 
__VA_ARGS__))
+#define METRIC_DEFINE_gauge_uint64(entity, name, label, unit, desc, ...) \
+  ::kudu::GaugePrototype<uint64_t> METRIC_##name(                    \
+      ::kudu::MetricPrototype::CtorArgs(#entity, #name, label, unit, desc, ## 
__VA_ARGS__))
+#define METRIC_DEFINE_gauge_double(entity, name, label, unit, desc, ...) \
+  ::kudu::GaugePrototype<double> METRIC_##name(                      \
+      ::kudu::MetricPrototype::CtorArgs(#entity, #name, label, unit, desc, ## 
__VA_ARGS__))
+
+#define METRIC_DEFINE_histogram(entity, name, label, unit, desc, max_val, 
num_sig_digits) \
+  ::kudu::HistogramPrototype METRIC_##name(                                    
   \
+      ::kudu::MetricPrototype::CtorArgs(#entity, #name, label, unit, desc), \
+    max_val, num_sig_digits)
+
+// The following macros act as forward declarations for entity types and 
metric prototypes.
+#define METRIC_DECLARE_entity(name) \
+  extern ::kudu::MetricEntityPrototype METRIC_ENTITY_##name
+#define METRIC_DECLARE_counter(name)                             \
+  extern ::kudu::CounterPrototype METRIC_##name
+#define METRIC_DECLARE_gauge_string(name) \
+  extern ::kudu::GaugePrototype<std::string> METRIC_##name
+#define METRIC_DECLARE_gauge_bool(name) \
+  extern ::kudu::GaugePrototype<bool> METRIC_##name
+#define METRIC_DECLARE_gauge_int32(name) \
+  extern ::kudu::GaugePrototype<int32_t> METRIC_##name
+#define METRIC_DECLARE_gauge_uint32(name) \
+  extern ::kudu::GaugePrototype<uint32_t> METRIC_##name
+#define METRIC_DECLARE_gauge_int64(name) \
+  extern ::kudu::GaugePrototype<int64_t> METRIC_##name
+#define METRIC_DECLARE_gauge_uint64(name) \
+  extern ::kudu::GaugePrototype<uint64_t> METRIC_##name
+#define METRIC_DECLARE_gauge_double(name) \
+  extern ::kudu::GaugePrototype<double> METRIC_##name
+#define METRIC_DECLARE_histogram(name) \
+  extern ::kudu::HistogramPrototype METRIC_##name
+
+#if defined(__APPLE__)
+#define METRIC_DEFINE_gauge_size(entity, name, label, unit, desc, ...) \
+  ::kudu::GaugePrototype<size_t> METRIC_##name(                    \
+      ::kudu::MetricPrototype::CtorArgs(#entity, #name, label, unit, desc, ## 
__VA_ARGS__))
+#define METRIC_DECLARE_gauge_size(name) \
+  extern ::kudu::GaugePrototype<size_t> METRIC_##name
+#else
+#define METRIC_DEFINE_gauge_size METRIC_DEFINE_gauge_uint64
+#define METRIC_DECLARE_gauge_size METRIC_DECLARE_gauge_uint64
+#endif
+
+namespace kudu {
+
+class Counter;
+class CounterPrototype;
+
+template<typename T>
+class AtomicGauge;
+template<typename T>
+class FunctionGauge;
+class Gauge;
+template<typename T>
+class GaugePrototype;
+
+class Metric;
+class MetricEntityPrototype;
+class MetricPrototype;
+class MetricRegistry;
+
+class HdrHistogram;
+class Histogram;
+class HistogramPrototype;
+class HistogramSnapshotPB;
+
+class MetricEntity;
+
+} // namespace kudu
+
+// Forward-declare the generic 'server' entity type.
+// We have to do this here below the forward declarations, but not
+// in the kudu namespace.
+METRIC_DECLARE_entity(server);
+
+namespace kudu {
+
+// Unit types to be used with metrics.
+// As additional units are required, add them to this enum and also to Name().
+struct MetricUnit {
+  enum Type {
+    kCacheHits,
+    kCacheQueries,
+    kBytes,
+    kRequests,
+    kEntries,
+    kRows,
+    kCells,
+    kConnections,
+    kOperations,
+    kProbes,
+    kNanoseconds,
+    kMicroseconds,
+    kMilliseconds,
+    kSeconds,
+    kThreads,
+    kTransactions,
+    kUnits,
+    kScanners,
+    kMaintenanceOperations,
+    kBlocks,
+    kLogBlockContainers,
+    kTasks,
+    kMessages,
+    kContextSwitches,
+    kDataDirectories,
+  };
+  static const char* Name(Type unit);
+};
+
+class MetricType {
+ public:
+  enum Type { kGauge, kCounter, kHistogram };
+  static const char* Name(Type t);
+ private:
+  static const char* const kGaugeType;
+  static const char* const kCounterType;
+  static const char* const kHistogramType;
+};
+
+struct MetricJsonOptions {
+  MetricJsonOptions() :
+    include_raw_histograms(false),
+    include_schema_info(false) {
+  }
+
+  // Include the raw histogram values and counts in the JSON output.
+  // This allows consumers to do cross-server aggregation or window
+  // data over time.
+  // Default: false
+  bool include_raw_histograms;
+
+  // Include the metrics "schema" information (i.e description, label,
+  // unit, etc).
+  // Default: false
+  bool include_schema_info;
+};
+
+class MetricEntityPrototype {
+ public:
+  explicit MetricEntityPrototype(const char* name);
+  ~MetricEntityPrototype();
+
+  const char* name() const { return name_; }
+
+  // Find or create an entity with the given ID within the provided 'registry'.
+  scoped_refptr<MetricEntity> Instantiate(
+      MetricRegistry* registry,
+      const std::string& id) const {
+    return Instantiate(registry, id, std::unordered_map<std::string, 
std::string>());
+  }
+
+  // If the entity already exists, then 'initial_attrs' will replace all 
existing
+  // attributes.
+  scoped_refptr<MetricEntity> Instantiate(
+      MetricRegistry* registry,
+      const std::string& id,
+      const std::unordered_map<std::string, std::string>& initial_attrs) const;
+
+ private:
+  const char* const name_;
+
+  DISALLOW_COPY_AND_ASSIGN(MetricEntityPrototype);
+};
+
+class MetricEntity : public RefCountedThreadSafe<MetricEntity> {
+ public:
+  typedef std::unordered_map<const MetricPrototype*, scoped_refptr<Metric> > 
MetricMap;
+  typedef std::unordered_map<std::string, std::string> AttributeMap;
+
+  scoped_refptr<Counter> FindOrCreateCounter(const CounterPrototype* proto);
+  scoped_refptr<Histogram> FindOrCreateHistogram(const HistogramPrototype* 
proto);
+
+  template<typename T>
+  scoped_refptr<AtomicGauge<T> > FindOrCreateGauge(const GaugePrototype<T>* 
proto,
+                                                   const T& initial_value);
+
+  template<typename T>
+  scoped_refptr<FunctionGauge<T> > FindOrCreateFunctionGauge(const 
GaugePrototype<T>* proto,
+                                                             const 
Callback<T()>& function);
+
+  // Return the metric instantiated from the given prototype, or NULL if none 
has been
+  // instantiated. Primarily used by tests trying to read metric values.
+  scoped_refptr<Metric> FindOrNull(const MetricPrototype& prototype) const;
+
+  const std::string& id() const { return id_; }
+
+  // See MetricRegistry::WriteAsJson()
+  Status WriteAsJson(JsonWriter* writer,
+                     const std::vector<std::string>& requested_metrics,
+                     const MetricJsonOptions& opts) const;
+
+  const MetricMap& UnsafeMetricsMapForTests() const { return metric_map_; }
+
+  // Mark that the given metric should never be retired until the metric
+  // registry itself destructs. This is useful for system metrics such as
+  // tcmalloc, etc, which should live as long as the process itself.
+  void NeverRetire(const scoped_refptr<Metric>& metric);
+
+  // Scan the metrics map for metrics needing retirement, removing them as 
necessary.
+  //
+  // Metrics are retired when they are no longer referenced outside of the 
metrics system
+  // itself. Additionally, we only retire a metric that has been in this state 
for
+  // at least FLAGS_metrics_retirement_age_ms milliseconds.
+  void RetireOldMetrics();
+
+  // Replaces all attributes for this entity.
+  // Any attributes currently set, but not in 'attrs', are removed.
+  void SetAttributes(const AttributeMap& attrs);
+
+  // Set a particular attribute. Replaces any current value.
+  void SetAttribute(const std::string& key, const std::string& val);
+
+  int num_metrics() const {
+    std::lock_guard<simple_spinlock> l(lock_);
+    return metric_map_.size();
+  }
+
+ private:
+  friend class MetricRegistry;
+  friend class RefCountedThreadSafe<MetricEntity>;
+
+  MetricEntity(const MetricEntityPrototype* prototype, std::string id,
+               AttributeMap attributes);
+  ~MetricEntity();
+
+  // Ensure that the given metric prototype is allowed to be instantiated
+  // within this entity. This entity's type must match the expected entity
+  // type defined within the metric prototype.
+  void CheckInstantiation(const MetricPrototype* proto) const;
+
+  const MetricEntityPrototype* const prototype_;
+  const std::string id_;
+
+  mutable simple_spinlock lock_;
+
+  // Map from metric name to Metric object. Protected by lock_.
+  MetricMap metric_map_;
+
+  // The key/value attributes. Protected by lock_
+  AttributeMap attributes_;
+
+  // The set of metrics which should never be retired. Protected by lock_.
+  std::vector<scoped_refptr<Metric> > never_retire_metrics_;
+};
+
+// Base class to allow for putting all metrics into a single container.
+// See documentation at the top of this file for information on metrics 
ownership.
+class Metric : public RefCountedThreadSafe<Metric> {
+ public:
+  // All metrics must be able to render themselves as JSON.
+  virtual Status WriteAsJson(JsonWriter* writer,
+                             const MetricJsonOptions& opts) const = 0;
+
+  const MetricPrototype* prototype() const { return prototype_; }
+
+ protected:
+  explicit Metric(const MetricPrototype* prototype);
+  virtual ~Metric();
+
+  const MetricPrototype* const prototype_;
+
+ private:
+  friend class MetricEntity;
+  friend class RefCountedThreadSafe<Metric>;
+
+  // The time at which we should retire this metric if it is still 
un-referenced outside
+  // of the metrics subsystem. If this metric is not due for retirement, this 
member is
+  // uninitialized.
+  MonoTime retire_time_;
+
+  DISALLOW_COPY_AND_ASSIGN(Metric);
+};
+
+// Registry of all the metrics for a server.
+//
+// This aggregates the MetricEntity objects associated with the server.
+class MetricRegistry {
+ public:
+  MetricRegistry();
+  ~MetricRegistry();
+
+  scoped_refptr<MetricEntity> FindOrCreateEntity(const MetricEntityPrototype* 
prototype,
+                                                 const std::string& id,
+                                                 const 
MetricEntity::AttributeMap& initial_attrs);
+
+  // Writes metrics in this registry to 'writer'.
+  //
+  // 'requested_metrics' is a set of substrings to match metric names against,
+  // where '*' matches all metrics.
+  //
+  // The string matching can either match an entity ID or a metric name.
+  // If it matches an entity ID, then all metrics for that entity will be 
printed.
+  //
+  // See the MetricJsonOptions struct definition above for options changing the
+  // output of this function.
+  Status WriteAsJson(JsonWriter* writer,
+                     const std::vector<std::string>& requested_metrics,
+                     const MetricJsonOptions& opts) const;
+
+  // For each registered entity, retires orphaned metrics. If an entity has no 
more
+  // metrics and there are no external references, entities are removed as 
well.
+  //
+  // See MetricEntity::RetireOldMetrics().
+  void RetireOldMetrics();
+
+  // Return the number of entities in this registry.
+  int num_entities() const {
+    std::lock_guard<simple_spinlock> l(lock_);
+    return entities_.size();
+  }
+
+ private:
+  typedef std::unordered_map<std::string, scoped_refptr<MetricEntity> > 
EntityMap;
+  EntityMap entities_;
+
+  mutable simple_spinlock lock_;
+  DISALLOW_COPY_AND_ASSIGN(MetricRegistry);
+};
+
+// Registry of all of the metric and entity prototypes that have been
+// defined.
+//
+// Prototypes are typically defined as static variables in different 
compilation
+// units, and their constructors register themselves here. The registry is then
+// used in order to dump metrics metadata to generate a Cloudera Manager MDL
+// file.
+//
+// This class is thread-safe.
+class MetricPrototypeRegistry {
+ public:
+  // Get the singleton instance.
+  static MetricPrototypeRegistry* get();
+
+  // Dump a JSON document including all of the registered entity and metric
+  // prototypes.
+  void WriteAsJson(JsonWriter* writer) const;
+
+  // Convenience wrapper around WriteAsJson(...). This dumps the JSON 
information
+  // to stdout and then exits.
+  void WriteAsJsonAndExit() const;
+ private:
+  friend class Singleton<MetricPrototypeRegistry>;
+  friend class MetricPrototype;
+  friend class MetricEntityPrototype;
+  MetricPrototypeRegistry() {}
+  ~MetricPrototypeRegistry() {}
+
+  // Register a metric prototype in the registry.
+  void AddMetric(const MetricPrototype* prototype);
+
+  // Register a metric entity prototype in the registry.
+  void AddEntity(const MetricEntityPrototype* prototype);
+
+  mutable simple_spinlock lock_;
+  std::vector<const MetricPrototype*> metrics_;
+  std::vector<const MetricEntityPrototype*> entities_;
+
+  DISALLOW_COPY_AND_ASSIGN(MetricPrototypeRegistry);
+};
+
+enum PrototypeFlags {
+  // Flag which causes a Gauge prototype to expose itself as if it
+  // were a counter.
+  EXPOSE_AS_COUNTER = 1 << 0
+};
+
+class MetricPrototype {
+ public:
+  // Simple struct to aggregate the arguments common to all prototypes.
+  // This makes constructor chaining a little less tedious.
+  struct CtorArgs {
+    CtorArgs(const char* entity_type,
+             const char* name,
+             const char* label,
+             MetricUnit::Type unit,
+             const char* description,
+             uint32_t flags = 0)
+      : entity_type_(entity_type),
+        name_(name),
+        label_(label),
+        unit_(unit),
+        description_(description),
+        flags_(flags) {
+    }
+
+    const char* const entity_type_;
+    const char* const name_;
+    const char* const label_;
+    const MetricUnit::Type unit_;
+    const char* const description_;
+    const uint32_t flags_;
+  };
+
+  const char* entity_type() const { return args_.entity_type_; }
+  const char* name() const { return args_.name_; }
+  const char* label() const { return args_.label_; }
+  MetricUnit::Type unit() const { return args_.unit_; }
+  const char* description() const { return args_.description_; }
+  virtual MetricType::Type type() const = 0;
+
+  // Writes the fields of this prototype to the given JSON writer.
+  void WriteFields(JsonWriter* writer,
+                   const MetricJsonOptions& opts) const;
+
+ protected:
+  explicit MetricPrototype(CtorArgs args);
+  virtual ~MetricPrototype() {
+  }
+
+  const CtorArgs args_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MetricPrototype);
+};
+
+// A description of a Gauge.
+template<typename T>
+class GaugePrototype : public MetricPrototype {
+ public:
+  explicit GaugePrototype(const MetricPrototype::CtorArgs& args)
+    : MetricPrototype(args) {
+  }
+
+  // Instantiate a "manual" gauge.
+  scoped_refptr<AtomicGauge<T> > Instantiate(
+      const scoped_refptr<MetricEntity>& entity,
+      const T& initial_value) const {
+    return entity->FindOrCreateGauge(this, initial_value);
+  }
+
+  // Instantiate a gauge that is backed by the given callback.
+  scoped_refptr<FunctionGauge<T> > InstantiateFunctionGauge(
+      const scoped_refptr<MetricEntity>& entity,
+      const Callback<T()>& function) const {
+    return entity->FindOrCreateFunctionGauge(this, function);
+  }
+
+  virtual MetricType::Type type() const OVERRIDE {
+    if (args_.flags_ & EXPOSE_AS_COUNTER) {
+      return MetricType::kCounter;
+    } else {
+      return MetricType::kGauge;
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GaugePrototype);
+};
+
+// Abstract base class to provide point-in-time metric values.
+class Gauge : public Metric {
+ public:
+  explicit Gauge(const MetricPrototype* prototype)
+    : Metric(prototype) {
+  }
+  virtual ~Gauge() {}
+  virtual Status WriteAsJson(JsonWriter* w,
+                             const MetricJsonOptions& opts) const OVERRIDE;
+ protected:
+  virtual void WriteValue(JsonWriter* writer) const = 0;
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Gauge);
+};
+
+// Gauge implementation for string that uses locks to ensure thread safety.
+class StringGauge : public Gauge {
+ public:
+  StringGauge(const GaugePrototype<std::string>* proto,
+              std::string initial_value);
+  std::string value() const;
+  void set_value(const std::string& value);
+ protected:
+  virtual void WriteValue(JsonWriter* writer) const OVERRIDE;
+ private:
+  std::string value_;
+  mutable simple_spinlock lock_;  // Guards value_
+  DISALLOW_COPY_AND_ASSIGN(StringGauge);
+};
+
+// Lock-free implementation for types that are convertible to/from int64_t.
+template <typename T>
+class AtomicGauge : public Gauge {
+ public:
+  AtomicGauge(const GaugePrototype<T>* proto, T initial_value)
+    : Gauge(proto),
+      value_(initial_value) {
+  }
+  T value() const {
+    return static_cast<T>(value_.Load(kMemOrderRelease));
+  }
+  virtual void set_value(const T& value) {
+    value_.Store(static_cast<int64_t>(value), kMemOrderNoBarrier);
+  }
+  void Increment() {
+    value_.IncrementBy(1, kMemOrderNoBarrier);
+  }
+  virtual void IncrementBy(int64_t amount) {
+    value_.IncrementBy(amount, kMemOrderNoBarrier);
+  }
+  void Decrement() {
+    IncrementBy(-1);
+  }
+  void DecrementBy(int64_t amount) {
+    IncrementBy(-amount);
+  }
+
+ protected:
+  virtual void WriteValue(JsonWriter* writer) const OVERRIDE {
+    writer->Value(value());
+  }
+  AtomicInt<int64_t> value_;
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AtomicGauge);
+};
+
+// Utility class to automatically detach FunctionGauges when a class destructs.
+//
+// Because FunctionGauges typically access class instance state, it's 
important to ensure
+// that they are detached before the class destructs. One approach is to make 
all
+// FunctionGauge instances be members of the class, and then call 
gauge_->Detach() in your
+// class's destructor. However, it's easy to forget to do this, which would 
lead to
+// heap-use-after-free bugs. This type of bug is easy to miss in unit tests 
because the
+// tests don't always poll metrics. Using a FunctionGaugeDetacher member 
instead makes
+// the detaching automatic and thus less error-prone.
+//
+// Example usage:
+//
+// METRIC_define_gauge_int64(my_metric, MetricUnit::kOperations, "My metric 
docs");
+// class MyClassWithMetrics {
+//  public:
+//   MyClassWithMetrics(const scoped_refptr<MetricEntity>& entity) {
+//     METRIC_my_metric.InstantiateFunctionGauge(entity,
+//       Bind(&MyClassWithMetrics::ComputeMyMetric, Unretained(this)))
+//       ->AutoDetach(&metric_detacher_);
+//   }
+//   ~MyClassWithMetrics() {
+//   }
+//
+//   private:
+//    int64_t ComputeMyMetric() {
+//      // Compute some metric based on instance state.
+//    }
+//    FunctionGaugeDetacher metric_detacher_;
+// };
+class FunctionGaugeDetacher {
+ public:
+  FunctionGaugeDetacher();
+  ~FunctionGaugeDetacher();
+
+ private:
+  template<typename T>
+  friend class FunctionGauge;
+
+  void OnDestructor(const Closure& c) {
+    callbacks_.push_back(c);
+  }
+
+  std::vector<Closure> callbacks_;
+
+  DISALLOW_COPY_AND_ASSIGN(FunctionGaugeDetacher);
+};
+
+
+// A Gauge that calls back to a function to get its value.
+//
+// This metric type should be used in cases where it is difficult to keep a 
running
+// measure of a metric, but instead would like to compute the metric value 
whenever it is
+// requested by a user.
+//
+// The lifecycle should be carefully considered when using a FunctionGauge. In 
particular,
+// the bound function needs to always be safe to run -- so if it references a 
particular
+// non-singleton class instance, the instance must out-live the function. 
Typically,
+// the easiest way to ensure this is to use a FunctionGaugeDetacher (see 
above).
+template <typename T>
+class FunctionGauge : public Gauge {
+ public:
+  T value() const {
+    std::lock_guard<simple_spinlock> l(lock_);
+    return function_.Run();
+  }
+
+  virtual void WriteValue(JsonWriter* writer) const OVERRIDE {
+    writer->Value(value());
+  }
+
+  // Reset this FunctionGauge to return a specific value.
+  // This should be used during destruction. If you want a settable
+  // Gauge, use a normal Gauge instead of a FunctionGauge.
+  void DetachToConstant(T v) {
+    std::lock_guard<simple_spinlock> l(lock_);
+    function_ = Bind(&FunctionGauge::Return, v);
+  }
+
+  // Get the current value of the gauge, and detach so that it continues to 
return this
+  // value in perpetuity.
+  void DetachToCurrentValue() {
+    T last_value = value();
+    DetachToConstant(last_value);
+  }
+
+  // Automatically detach this gauge when the given 'detacher' destructs.
+  // After detaching, the metric will return 'value' in perpetuity.
+  void AutoDetach(FunctionGaugeDetacher* detacher, T value = T()) {
+    detacher->OnDestructor(Bind(&FunctionGauge<T>::DetachToConstant,
+                                this, value));
+  }
+
+  // Automatically detach this gauge when the given 'detacher' destructs.
+  // After detaching, the metric will return whatever its value was at the
+  // time of detaching.
+  //
+  // Note that, when using this method, you should be sure that the 
FunctionGaugeDetacher
+  // is destructed before any objects which are required by the gauge 
implementation.
+  // In typical usage (see the FunctionGaugeDetacher class documentation) this 
means you
+  // should declare the detacher member after all other class members that 
might be
+  // accessed by the gauge function implementation.
+  void AutoDetachToLastValue(FunctionGaugeDetacher* detacher) {
+    detacher->OnDestructor(Bind(&FunctionGauge<T>::DetachToCurrentValue,
+                                this));
+  }
+
+ private:
+  friend class MetricEntity;
+
+  FunctionGauge(const GaugePrototype<T>* proto, Callback<T()> function)
+      : Gauge(proto), function_(std::move(function)) {}
+
+  static T Return(T v) {
+    return v;
+  }
+
+  mutable simple_spinlock lock_;
+  Callback<T()> function_;
+  DISALLOW_COPY_AND_ASSIGN(FunctionGauge);
+};
+
+// Prototype for a counter.
+class CounterPrototype : public MetricPrototype {
+ public:
+  explicit CounterPrototype(const MetricPrototype::CtorArgs& args)
+    : MetricPrototype(args) {
+  }
+  scoped_refptr<Counter> Instantiate(const scoped_refptr<MetricEntity>& 
entity);
+
+  virtual MetricType::Type type() const OVERRIDE { return 
MetricType::kCounter; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CounterPrototype);
+};
+
+// Simple incrementing 64-bit integer.
+// Only use Counters in cases that we expect the count to only increase. For 
example,
+// a counter is appropriate for "number of transactions processed by the 
server",
+// but not for "number of transactions currently in flight". Monitoring 
software
+// knows that counters only increase and thus can compute rates over time, 
rates
+// across multiple servers, etc, which aren't appropriate in the case of 
gauges.
+class Counter : public Metric {
+ public:
+  int64_t value() const;
+  void Increment();
+  void IncrementBy(int64_t amount);
+  virtual Status WriteAsJson(JsonWriter* w,
+                             const MetricJsonOptions& opts) const OVERRIDE;
+
+ private:
+  FRIEND_TEST(MetricsTest, SimpleCounterTest);
+  FRIEND_TEST(MultiThreadedMetricsTest, CounterIncrementTest);
+  friend class MetricEntity;
+
+  explicit Counter(const CounterPrototype* proto);
+
+  LongAdder value_;
+  DISALLOW_COPY_AND_ASSIGN(Counter);
+};
+
+class HistogramPrototype : public MetricPrototype {
+ public:
+  HistogramPrototype(const MetricPrototype::CtorArgs& args,
+                     uint64_t max_trackable_value, int num_sig_digits);
+  scoped_refptr<Histogram> Instantiate(const scoped_refptr<MetricEntity>& 
entity);
+
+  uint64_t max_trackable_value() const { return max_trackable_value_; }
+  int num_sig_digits() const { return num_sig_digits_; }
+  virtual MetricType::Type type() const OVERRIDE { return 
MetricType::kHistogram; }
+
+ private:
+  const uint64_t max_trackable_value_;
+  const int num_sig_digits_;
+  DISALLOW_COPY_AND_ASSIGN(HistogramPrototype);
+};
+
+class Histogram : public Metric {
+ public:
+  // Increment the histogram for the given value.
+  // 'value' must be non-negative.
+  void Increment(int64_t value);
+
+  // Increment the histogram for the given value by the given amount.
+  // 'value' and 'amount' must be non-negative.
+  void IncrementBy(int64_t value, int64_t amount);
+
+  // Return the total number of values added to the histogram (via Increment()
+  // or IncrementBy()).
+  uint64_t TotalCount() const;
+
+  virtual Status WriteAsJson(JsonWriter* w,
+                             const MetricJsonOptions& opts) const OVERRIDE;
+
+  // Returns a snapshot of this histogram including the bucketed values and 
counts.
+  Status GetHistogramSnapshotPB(HistogramSnapshotPB* snapshot,
+                                const MetricJsonOptions& opts) const;
+
+  uint64_t CountInBucketForValueForTests(uint64_t value) const;
+  uint64_t MinValueForTests() const;
+  uint64_t MaxValueForTests() const;
+  double MeanValueForTests() const;
+
+ private:
+  FRIEND_TEST(MetricsTest, SimpleHistogramTest);
+  friend class MetricEntity;
+  explicit Histogram(const HistogramPrototype* proto);
+
+  const gscoped_ptr<HdrHistogram> histogram_;
+  DISALLOW_COPY_AND_ASSIGN(Histogram);
+};
+
+// Measures a duration while in scope. Adds this duration to specified 
histogram on destruction.
+class ScopedLatencyMetric {
+ public:
+  // NOTE: the given histogram must live as long as this object.
+  // If 'latency_hist' is NULL, this turns into a no-op.
+  explicit ScopedLatencyMetric(Histogram* latency_hist);
+  ~ScopedLatencyMetric();
+
+ private:
+  Histogram* latency_hist_;
+  MonoTime time_started_;
+};
+
+#define SCOPED_LATENCY_METRIC(_mtx, _h) \
+  ScopedLatencyMetric _h##_metric((_mtx) ? (_mtx)->_h.get() : NULL)
+
+
+////////////////////////////////////////////////////////////
+// Inline implementations of template methods
+////////////////////////////////////////////////////////////
+
+inline scoped_refptr<Counter> MetricEntity::FindOrCreateCounter(
+    const CounterPrototype* proto) {
+  CheckInstantiation(proto);
+  std::lock_guard<simple_spinlock> l(lock_);
+  scoped_refptr<Counter> m = down_cast<Counter*>(FindPtrOrNull(metric_map_, 
proto).get());
+  if (!m) {
+    m = new Counter(proto);
+    InsertOrDie(&metric_map_, proto, m);
+  }
+  return m;
+}
+
+inline scoped_refptr<Histogram> MetricEntity::FindOrCreateHistogram(
+    const HistogramPrototype* proto) {
+  CheckInstantiation(proto);
+  std::lock_guard<simple_spinlock> l(lock_);
+  scoped_refptr<Histogram> m = 
down_cast<Histogram*>(FindPtrOrNull(metric_map_, proto).get());
+  if (!m) {
+    m = new Histogram(proto);
+    InsertOrDie(&metric_map_, proto, m);
+  }
+  return m;
+}
+
+template<typename T>
+inline scoped_refptr<AtomicGauge<T> > MetricEntity::FindOrCreateGauge(
+    const GaugePrototype<T>* proto,
+    const T& initial_value) {
+  CheckInstantiation(proto);
+  std::lock_guard<simple_spinlock> l(lock_);
+  scoped_refptr<AtomicGauge<T> > m = down_cast<AtomicGauge<T>*>(
+      FindPtrOrNull(metric_map_, proto).get());
+  if (!m) {
+    m = new AtomicGauge<T>(proto, initial_value);
+    InsertOrDie(&metric_map_, proto, m);
+  }
+  return m;
+}
+
+template<typename T>
+inline scoped_refptr<FunctionGauge<T> > 
MetricEntity::FindOrCreateFunctionGauge(
+    const GaugePrototype<T>* proto,
+    const Callback<T()>& function) {
+  CheckInstantiation(proto);
+  std::lock_guard<simple_spinlock> l(lock_);
+  scoped_refptr<FunctionGauge<T> > m = down_cast<FunctionGauge<T>*>(
+      FindPtrOrNull(metric_map_, proto).get());
+  if (!m) {
+    m = new FunctionGauge<T>(proto, function);
+    InsertOrDie(&metric_map_, proto, m);
+  }
+  return m;
+}
+
+} // namespace kudu
+
+#endif // KUDU_UTIL_METRICS_H

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/d6abb29d/be/src/kudu/util/minidump-test.cc
----------------------------------------------------------------------
diff --git a/be/src/kudu/util/minidump-test.cc 
b/be/src/kudu/util/minidump-test.cc
new file mode 100644
index 0000000..f4c44e2
--- /dev/null
+++ b/be/src/kudu/util/minidump-test.cc
@@ -0,0 +1,144 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <gflags/gflags.h>
+
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/util/minidump.h"
+#include "kudu/util/path_util.h"
+#include "kudu/util/test_macros.h"
+#include "kudu/util/test_util.h"
+
+using std::string;
+using std::vector;
+
+DECLARE_bool(enable_minidumps);
+DECLARE_int32(max_minidumps);
+DECLARE_string(minidump_path);
+
+namespace kudu {
+
+class MinidumpDeathTest : public KuduTest {
+ protected:
+  void WaitForMinidumps(int expected, const string& dir);
+};
+
+void MinidumpDeathTest::WaitForMinidumps(int expected, const string& dir) {
+  ASSERT_EVENTUALLY([&] {
+    vector<string> matches;
+    ASSERT_OK(env_->Glob(JoinPathSegments(dir, "*.dmp"), &matches));
+    ASSERT_EQ(expected, matches.size());
+  });
+}
+
+// Test that registering the minidump exception handler results in creation of
+// minidump files on crash. Also test that deleting excess minidump files works
+// as expected.
+TEST_F(MinidumpDeathTest, TestRegisterAndDelete) {
+  FLAGS_enable_minidumps = true;
+  FLAGS_minidump_path = JoinPathSegments(test_dir_, "minidumps");
+  MinidumpExceptionHandler minidump_handler;
+  ASSERT_DEATH({
+    abort();
+  },
+  // Ensure that a stack trace is produced.
+  "kudu::MinidumpDeathTest_TestRegisterAndDelete_Test::TestBody()");
+
+  // Ensure that a minidump is produced.
+  string minidump_dir = minidump_handler.minidump_dir();
+  NO_FATALS(WaitForMinidumps(1, minidump_dir));
+
+  // Now create more minidumps so we can clean them up.
+  for (int num_dumps : {2, 3}) {
+    kill(getpid(), SIGUSR1);
+    NO_FATALS(WaitForMinidumps(num_dumps, minidump_dir));
+  }
+
+  FLAGS_max_minidumps = 2;
+  ASSERT_OK(minidump_handler.DeleteExcessMinidumpFiles(env_));
+  NO_FATALS(WaitForMinidumps(2, minidump_dir));
+}
+
+// Test that a CHECK() failure produces a stack trace and a minidump.
+TEST_F(MinidumpDeathTest, TestCheckStackTraceAndMinidump) {
+  FLAGS_enable_minidumps = true;
+  FLAGS_minidump_path = JoinPathSegments(test_dir_, "minidumps");
+  MinidumpExceptionHandler minidump_handler;
+  ASSERT_DEATH({
+    CHECK_EQ(1, 0);
+  },
+  // Ensure that a stack trace is produced.
+  "kudu::MinidumpDeathTest_TestCheckStackTraceAndMinidump_Test::TestBody()");
+
+  // Ensure that a minidump is produced.
+  string minidump_dir = minidump_handler.minidump_dir();
+  NO_FATALS(WaitForMinidumps(1, minidump_dir));
+}
+
+class MinidumpSignalDeathTest : public MinidumpDeathTest,
+                                public ::testing::WithParamInterface<int> {
+};
+
+// Test that we get both a minidump and a stack trace for each supported 
signal.
+TEST_P(MinidumpSignalDeathTest, TestHaveMinidumpAndStackTrace) {
+  FLAGS_enable_minidumps = true;
+  int signal = GetParam();
+
+#if defined(ADDRESS_SANITIZER)
+  // ASAN appears to catch SIGBUS, SIGSEGV, and SIGFPE and the process is not 
killed.
+  if (signal == SIGBUS || signal == SIGSEGV || signal == SIGFPE) {
+    return;
+  }
+#endif
+
+#if defined(THREAD_SANITIZER)
+  // TSAN appears to catch SIGTERM and the process is not killed.
+  if (signal == SIGTERM) {
+    return;
+  }
+#endif
+
+  LOG(INFO) << "Testing signal: " << strsignal(signal);
+
+  FLAGS_minidump_path = JoinPathSegments(test_dir_, "minidumps");
+  MinidumpExceptionHandler minidump_handler;
+  ASSERT_DEATH({
+    kill(getpid(), signal);
+  },
+  // Ensure that a stack trace is produced.
+  
"kudu::MinidumpSignalDeathTest_TestHaveMinidumpAndStackTrace_Test::TestBody()");
+
+  // Ensure that a mindump is produced, unless it's SIGTERM, which does not
+  // create a minidump.
+  int num_expected_minidumps = 1;
+  if (signal == SIGTERM) {
+    num_expected_minidumps = 0;
+  }
+  NO_FATALS(WaitForMinidumps(num_expected_minidumps, 
minidump_handler.minidump_dir()));
+}
+
+INSTANTIATE_TEST_CASE_P(DeadlySignals, MinidumpSignalDeathTest,
+    ::testing::Values(SIGABRT, SIGBUS, SIGSEGV, SIGILL, SIGFPE, SIGTERM));
+
+} // namespace kudu

Reply via email to