Consolidate serialization code in base::HistogramDeltasSerializer
Before patch code lived partially in base/ and content/

BUG=305019

Review URL: https://codereview.chromium.org/27460003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@230268 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/base/base.gyp b/base/base.gyp
index fb37803..5dfe08dd 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -551,6 +551,7 @@
         'metrics/bucket_ranges_unittest.cc',
         'metrics/field_trial_unittest.cc',
         'metrics/histogram_base_unittest.cc',
+        'metrics/histogram_delta_serialization_unittest.cc',
         'metrics/histogram_unittest.cc',
         'metrics/sparse_histogram_unittest.cc',
         'metrics/stats_table_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index 73e6ccc..b60ffa6 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -339,6 +339,8 @@
           'metrics/histogram.h',
           'metrics/histogram_base.cc',
           'metrics/histogram_base.h',
+          'metrics/histogram_delta_serialization.cc',
+          'metrics/histogram_delta_serialization.h',
           'metrics/histogram_flattener.h',
           'metrics/histogram_samples.cc',
           'metrics/histogram_samples.h',
diff --git a/base/metrics/histogram_base.cc b/base/metrics/histogram_base.cc
index 5b46d299..5c6e2d2 100644
--- a/base/metrics/histogram_base.cc
+++ b/base/metrics/histogram_base.cc
@@ -58,20 +58,6 @@
   }
 }
 
-void DeserializeHistogramAndAddSamples(PickleIterator* iter) {
-  HistogramBase* histogram = DeserializeHistogramInfo(iter);
-  if (!histogram)
-    return;
-
-  if (histogram->flags() & base::HistogramBase::kIPCSerializationSourceFlag) {
-    DVLOG(1) << "Single process mode, histogram observed and not copied: "
-             << histogram->histogram_name();
-    return;
-  }
-  histogram->AddSamplesFromPickle(iter);
-}
-
-
 const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX;
 
 HistogramBase::HistogramBase(const std::string& name)
diff --git a/base/metrics/histogram_base.h b/base/metrics/histogram_base.h
index 4663647..4248bd8 100644
--- a/base/metrics/histogram_base.h
+++ b/base/metrics/histogram_base.h
@@ -6,6 +6,7 @@
 #define BASE_METRICS_HISTOGRAM_BASE_H_
 
 #include <string>
+#include <vector>
 
 #include "base/atomicops.h"
 #include "base/base_export.h"
@@ -43,10 +44,6 @@
 BASE_EXPORT_PRIVATE HistogramBase* DeserializeHistogramInfo(
     PickleIterator* iter);
 
-// Create or find existing histogram and add the samples from pickle.
-// Silently returns when seeing any data problem in the pickle.
-BASE_EXPORT void DeserializeHistogramAndAddSamples(PickleIterator* iter);
-
 ////////////////////////////////////////////////////////////////////////////////
 
 class BASE_EXPORT HistogramBase {
diff --git a/base/metrics/histogram_base_unittest.cc b/base/metrics/histogram_base_unittest.cc
index 0e19d56..4a2963a 100644
--- a/base/metrics/histogram_base_unittest.cc
+++ b/base/metrics/histogram_base_unittest.cc
@@ -61,40 +61,6 @@
   EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, deserialized->flags());
 }
 
-TEST_F(HistogramBaseTest, DeserializeHistogramAndAddSamples) {
-  HistogramBase* histogram = Histogram::FactoryGet(
-      "TestHistogram", 1, 1000, 10, HistogramBase::kIPCSerializationSourceFlag);
-  histogram->Add(1);
-  histogram->Add(10);
-  histogram->Add(100);
-  histogram->Add(1000);
-
-  Pickle pickle;
-  ASSERT_TRUE(histogram->SerializeInfo(&pickle));
-  histogram->SnapshotSamples()->Serialize(&pickle);
-
-  PickleIterator iter(pickle);
-  DeserializeHistogramAndAddSamples(&iter);
-
-  // The histogram has kIPCSerializationSourceFlag. So samples will be ignored.
-  scoped_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
-  EXPECT_EQ(1, snapshot->GetCount(1));
-  EXPECT_EQ(1, snapshot->GetCount(10));
-  EXPECT_EQ(1, snapshot->GetCount(100));
-  EXPECT_EQ(1, snapshot->GetCount(1000));
-
-  // Clear kIPCSerializationSourceFlag to emulate multi-process usage.
-  histogram->ClearFlags(HistogramBase::kIPCSerializationSourceFlag);
-  PickleIterator iter2(pickle);
-  DeserializeHistogramAndAddSamples(&iter2);
-
-  scoped_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
-  EXPECT_EQ(2, snapshot2->GetCount(1));
-  EXPECT_EQ(2, snapshot2->GetCount(10));
-  EXPECT_EQ(2, snapshot2->GetCount(100));
-  EXPECT_EQ(2, snapshot2->GetCount(1000));
-}
-
 TEST_F(HistogramBaseTest, DeserializeLinearHistogram) {
   HistogramBase* histogram = LinearHistogram::FactoryGet(
       "TestHistogram", 1, 1000, 10,
diff --git a/base/metrics/histogram_delta_serialization.cc b/base/metrics/histogram_delta_serialization.cc
new file mode 100644
index 0000000..924916d
--- /dev/null
+++ b/base/metrics/histogram_delta_serialization.cc
@@ -0,0 +1,111 @@
+// Copyright 2013 The Chromium 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 "base/metrics/histogram_delta_serialization.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_snapshot_manager.h"
+#include "base/pickle.h"
+#include "base/safe_numerics.h"
+#include "base/values.h"
+
+namespace base {
+
+namespace {
+
+// Create or find existing histogram and add the samples from pickle.
+// Silently returns when seeing any data problem in the pickle.
+void DeserializeHistogramAndAddSamples(PickleIterator* iter) {
+  HistogramBase* histogram = DeserializeHistogramInfo(iter);
+  if (!histogram)
+    return;
+
+  if (histogram->flags() & HistogramBase::kIPCSerializationSourceFlag) {
+    DVLOG(1) << "Single process mode, histogram observed and not copied: "
+             << histogram->histogram_name();
+    return;
+  }
+  histogram->AddSamplesFromPickle(iter);
+}
+
+}  // namespace
+
+HistogramDeltaSerialization::HistogramDeltaSerialization(
+    const std::string& caller_name)
+    : histogram_snapshot_manager_(this),
+      serialized_deltas_(NULL) {
+  inconsistencies_histogram_ =
+      LinearHistogram::FactoryGet(
+          "Histogram.Inconsistencies" + caller_name, 1,
+          HistogramBase::NEVER_EXCEEDED_VALUE,
+          HistogramBase::NEVER_EXCEEDED_VALUE + 1,
+          HistogramBase::kUmaTargetedHistogramFlag);
+
+  inconsistencies_unique_histogram_ =
+      LinearHistogram::FactoryGet(
+          "Histogram.Inconsistencies" + caller_name + "Unique", 1,
+          HistogramBase::NEVER_EXCEEDED_VALUE,
+          HistogramBase::NEVER_EXCEEDED_VALUE + 1,
+          HistogramBase::kUmaTargetedHistogramFlag);
+
+  inconsistent_snapshot_histogram_ =
+      Histogram::FactoryGet(
+          "Histogram.InconsistentSnapshot" + caller_name, 1, 1000000, 50,
+          HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+HistogramDeltaSerialization::~HistogramDeltaSerialization() {
+}
+
+void HistogramDeltaSerialization::PrepareAndSerializeDeltas(
+    std::vector<std::string>* serialized_deltas) {
+  serialized_deltas_ = serialized_deltas;
+  // Note: Before serializing, we set the kIPCSerializationSourceFlag for all
+  // the histograms, so that the receiving process can distinguish them from the
+  // local histograms.
+  histogram_snapshot_manager_.PrepareDeltas(
+      Histogram::kIPCSerializationSourceFlag, false);
+  serialized_deltas_ = NULL;
+}
+
+// static
+void HistogramDeltaSerialization::DeserializeAndAddSamples(
+    const std::vector<std::string>& serialized_deltas) {
+  for (std::vector<std::string>::const_iterator it = serialized_deltas.begin();
+       it != serialized_deltas.end(); ++it) {
+    Pickle pickle(it->data(), checked_numeric_cast<int>(it->size()));
+    PickleIterator iter(pickle);
+    DeserializeHistogramAndAddSamples(&iter);
+  }
+}
+
+void HistogramDeltaSerialization::RecordDelta(
+    const HistogramBase& histogram,
+    const HistogramSamples& snapshot) {
+  DCHECK_NE(0, snapshot.TotalCount());
+
+  Pickle pickle;
+  histogram.SerializeInfo(&pickle);
+  snapshot.Serialize(&pickle);
+  serialized_deltas_->push_back(
+      std::string(static_cast<const char*>(pickle.data()), pickle.size()));
+}
+
+void HistogramDeltaSerialization::InconsistencyDetected(
+    HistogramBase::Inconsistency problem) {
+  inconsistencies_histogram_->Add(problem);
+}
+
+void HistogramDeltaSerialization::UniqueInconsistencyDetected(
+    HistogramBase::Inconsistency problem) {
+  inconsistencies_unique_histogram_->Add(problem);
+}
+
+void HistogramDeltaSerialization::InconsistencyDetectedInLoggedCount(
+    int amount) {
+  inconsistent_snapshot_histogram_->Add(std::abs(amount));
+}
+
+}  // namespace base
diff --git a/base/metrics/histogram_delta_serialization.h b/base/metrics/histogram_delta_serialization.h
new file mode 100644
index 0000000..ccadb12
--- /dev/null
+++ b/base/metrics/histogram_delta_serialization.h
@@ -0,0 +1,65 @@
+// Copyright 2013 The Chromium 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 BASE_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_
+#define BASE_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_flattener.h"
+#include "base/metrics/histogram_snapshot_manager.h"
+
+namespace base {
+
+class HistogramBase;
+
+// Serializes and restores histograms deltas.
+class BASE_EXPORT HistogramDeltaSerialization : public HistogramFlattener {
+ public:
+  // |caller_name| is string used in histograms for counting inconsistencies.
+  explicit HistogramDeltaSerialization(const std::string& caller_name);
+  virtual ~HistogramDeltaSerialization();
+
+  // Computes deltas in histogram bucket counts relative to the previous call to
+  // this method. Stores the deltas in serialized form into |serialized_deltas|.
+  // If |serialized_deltas| is NULL, no data is serialized, though the next call
+  // will compute the deltas relative to this one.
+  void PrepareAndSerializeDeltas(std::vector<std::string>* serialized_deltas);
+
+  // Deserialize deltas and add samples to corresponding histograms, creating
+  // them if necessary. Silently ignores errors in |serialized_deltas|.
+  static void DeserializeAndAddSamples(
+      const std::vector<std::string>& serialized_deltas);
+
+ private:
+  // HistogramFlattener implementation.
+  virtual void RecordDelta(const HistogramBase& histogram,
+                           const HistogramSamples& snapshot) OVERRIDE;
+  virtual void InconsistencyDetected(
+      HistogramBase::Inconsistency problem) OVERRIDE;
+  virtual void UniqueInconsistencyDetected(
+      HistogramBase::Inconsistency problem) OVERRIDE;
+  virtual void InconsistencyDetectedInLoggedCount(int amount) OVERRIDE;
+
+  // Calculates deltas in histogram counters.
+  HistogramSnapshotManager histogram_snapshot_manager_;
+
+  // Output buffer for serialized deltas.
+  std::vector<std::string>* serialized_deltas_;
+
+  // Histograms to count inconsistencies in snapshots.
+  HistogramBase* inconsistencies_histogram_;
+  HistogramBase* inconsistencies_unique_histogram_;
+  HistogramBase* inconsistent_snapshot_histogram_;
+
+  DISALLOW_COPY_AND_ASSIGN(HistogramDeltaSerialization);
+};
+
+}  // namespace base
+
+#endif  // BASE_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_
diff --git a/base/metrics/histogram_delta_serialization_unittest.cc b/base/metrics/histogram_delta_serialization_unittest.cc
new file mode 100644
index 0000000..b53520c5
--- /dev/null
+++ b/base/metrics/histogram_delta_serialization_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2013 The Chromium 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 "base/metrics/histogram_delta_serialization.h"
+
+#include <vector>
+
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/statistics_recorder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(HistogramDeltaSerializationTest, DeserializeHistogramAndAddSamples) {
+  StatisticsRecorder statistic_recorder;
+  HistogramDeltaSerialization serializer("HistogramDeltaSerializationTest");
+  std::vector<std::string> deltas;
+  // Nothing was changed yet.
+  serializer.PrepareAndSerializeDeltas(&deltas);
+  EXPECT_TRUE(deltas.empty());
+
+  HistogramBase* histogram = Histogram::FactoryGet(
+      "TestHistogram", 1, 1000, 10, HistogramBase::kIPCSerializationSourceFlag);
+  histogram->Add(1);
+  histogram->Add(10);
+  histogram->Add(100);
+  histogram->Add(1000);
+
+  serializer.PrepareAndSerializeDeltas(&deltas);
+  EXPECT_FALSE(deltas.empty());
+
+  HistogramDeltaSerialization::DeserializeAndAddSamples(deltas);
+
+  // The histogram has kIPCSerializationSourceFlag. So samples will be ignored.
+  scoped_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
+  EXPECT_EQ(1, snapshot->GetCount(1));
+  EXPECT_EQ(1, snapshot->GetCount(10));
+  EXPECT_EQ(1, snapshot->GetCount(100));
+  EXPECT_EQ(1, snapshot->GetCount(1000));
+
+  // Clear kIPCSerializationSourceFlag to emulate multi-process usage.
+  histogram->ClearFlags(HistogramBase::kIPCSerializationSourceFlag);
+  HistogramDeltaSerialization::DeserializeAndAddSamples(deltas);
+
+  scoped_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
+  EXPECT_EQ(2, snapshot2->GetCount(1));
+  EXPECT_EQ(2, snapshot2->GetCount(10));
+  EXPECT_EQ(2, snapshot2->GetCount(100));
+  EXPECT_EQ(2, snapshot2->GetCount(1000));
+}
+
+}  // namespace base
diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h
index 9a55225..0fdfb769 100644
--- a/base/metrics/statistics_recorder.h
+++ b/base/metrics/statistics_recorder.h
@@ -85,6 +85,8 @@
   friend class HistogramTest;
   friend class SparseHistogramTest;
   friend class StatisticsRecorderTest;
+  FRIEND_TEST_ALL_PREFIXES(HistogramDeltaSerializationTest,
+                           DeserializeHistogramAndAddSamples);
 
   // The constructor just initializes static members. Usually client code should
   // use Initialize to do this. But in test code, you can friend this class and
diff --git a/content/browser/histogram_synchronizer.cc b/content/browser/histogram_synchronizer.cc
index 5a1e6f9..16617237 100644
--- a/content/browser/histogram_synchronizer.cc
+++ b/content/browser/histogram_synchronizer.cc
@@ -8,6 +8,7 @@
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/metrics/histogram.h"
+#include "base/metrics/histogram_delta_serialization.h"
 #include "base/pickle.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_restrictions.h"
@@ -268,16 +269,10 @@
     const std::vector<std::string>& pickled_histograms) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
+  base::HistogramDeltaSerialization::DeserializeAndAddSamples(
+      pickled_histograms);
+
   RequestContext* request = RequestContext::GetRequestContext(sequence_number);
-
-  for (std::vector<std::string>::const_iterator it = pickled_histograms.begin();
-       it < pickled_histograms.end();
-       ++it) {
-    Pickle pickle(it->data(), it->size());
-    PickleIterator iter(pickle);
-    base::DeserializeHistogramAndAddSamples(&iter);
-  }
-
   if (!request)
     return;
 
diff --git a/content/child/child_histogram_message_filter.cc b/content/child/child_histogram_message_filter.cc
index 427dd0d..f05d111c 100644
--- a/content/child/child_histogram_message_filter.cc
+++ b/content/child/child_histogram_message_filter.cc
@@ -8,8 +8,7 @@
 
 #include "base/bind.h"
 #include "base/message_loop/message_loop.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/pickle.h"
+#include "base/metrics/histogram_delta_serialization.h"
 #include "content/child/child_process.h"
 #include "content/child/child_thread.h"
 #include "content/common/child_process_messages.h"
@@ -18,8 +17,7 @@
 
 ChildHistogramMessageFilter::ChildHistogramMessageFilter()
     : channel_(NULL),
-      io_message_loop_(ChildProcess::current()->io_message_loop_proxy()),
-      histogram_snapshot_manager_(this) {
+      io_message_loop_(ChildProcess::current()->io_message_loop_proxy()) {
 }
 
 ChildHistogramMessageFilter::~ChildHistogramMessageFilter() {
@@ -54,53 +52,19 @@
 }
 
 void ChildHistogramMessageFilter::UploadAllHistograms(int sequence_number) {
-  DCHECK_EQ(0u, pickled_histograms_.size());
+  if (!histogram_delta_serialization_) {
+    histogram_delta_serialization_.reset(
+        new base::HistogramDeltaSerialization("ChildProcess"));
+  }
 
-  // Push snapshots into our pickled_histograms_ vector.
-  // Note: Before serializing, we set the kIPCSerializationSourceFlag for all
-  // the histograms, so that the receiving process can distinguish them from the
-  // local histograms.
-  histogram_snapshot_manager_.PrepareDeltas(
-      base::Histogram::kIPCSerializationSourceFlag, false);
+  std::vector<std::string> deltas;
+  histogram_delta_serialization_->PrepareAndSerializeDeltas(&deltas);
+  channel_->Send(
+      new ChildProcessHostMsg_ChildHistogramData(sequence_number, deltas));
 
-  channel_->Send(new ChildProcessHostMsg_ChildHistogramData(
-      sequence_number, pickled_histograms_));
-
-  pickled_histograms_.clear();
   static int count = 0;
   count++;
   DHISTOGRAM_COUNTS("Histogram.ChildProcessHistogramSentCount", count);
 }
 
-void ChildHistogramMessageFilter::RecordDelta(
-    const base::HistogramBase& histogram,
-    const base::HistogramSamples& snapshot) {
-  DCHECK_NE(0, snapshot.TotalCount());
-
-  Pickle pickle;
-  histogram.SerializeInfo(&pickle);
-  snapshot.Serialize(&pickle);
-
-  pickled_histograms_.push_back(
-      std::string(static_cast<const char*>(pickle.data()), pickle.size()));
-}
-
-void ChildHistogramMessageFilter::InconsistencyDetected(
-    base::HistogramBase::Inconsistency problem) {
-  UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesChildProcess",
-                            problem, base::HistogramBase::NEVER_EXCEEDED_VALUE);
-}
-
-void ChildHistogramMessageFilter::UniqueInconsistencyDetected(
-    base::HistogramBase::Inconsistency problem) {
-  UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesChildProcessUnique",
-                            problem, base::HistogramBase::NEVER_EXCEEDED_VALUE);
-}
-
-void ChildHistogramMessageFilter::InconsistencyDetectedInLoggedCount(
-    int amount) {
-  UMA_HISTOGRAM_COUNTS("Histogram.InconsistentSnapshotChildProcess",
-                       std::abs(amount));
-}
-
 }  // namespace content
diff --git a/content/child/child_histogram_message_filter.h b/content/child/child_histogram_message_filter.h
index 85d2e2097..98a30a2 100644
--- a/content/child/child_histogram_message_filter.h
+++ b/content/child/child_histogram_message_filter.h
@@ -9,20 +9,16 @@
 #include <vector>
 
 #include "base/basictypes.h"
-#include "base/metrics/histogram_base.h"
-#include "base/metrics/histogram_flattener.h"
-#include "base/metrics/histogram_snapshot_manager.h"
 #include "ipc/ipc_channel_proxy.h"
 
 namespace base {
-class HistogramSamples;
+class HistogramDeltaSerialization;
 class MessageLoopProxy;
 }  // namespace base
 
 namespace content {
 
-class ChildHistogramMessageFilter : public base::HistogramFlattener,
-                                    public IPC::ChannelProxy::MessageFilter {
+class ChildHistogramMessageFilter : public IPC::ChannelProxy::MessageFilter {
  public:
   ChildHistogramMessageFilter();
 
@@ -33,15 +29,6 @@
 
   void SendHistograms(int sequence_number);
 
-  // HistogramFlattener interface (override) methods.
-  virtual void RecordDelta(const base::HistogramBase& histogram,
-                           const base::HistogramSamples& snapshot) OVERRIDE;
-  virtual void InconsistencyDetected(
-      base::HistogramBase::Inconsistency problem) OVERRIDE;
-  virtual void UniqueInconsistencyDetected(
-      base::HistogramBase::Inconsistency problem) OVERRIDE;
-  virtual void InconsistencyDetectedInLoggedCount(int amount) OVERRIDE;
-
  private:
   typedef std::vector<std::string> HistogramPickledList;
 
@@ -58,11 +45,8 @@
 
   scoped_refptr<base::MessageLoopProxy> io_message_loop_;
 
-  // Collection of histograms to send to the browser.
-  HistogramPickledList pickled_histograms_;
-
-  // |histogram_snapshot_manager_| prepares histogram deltas for transmission.
-  base::HistogramSnapshotManager histogram_snapshot_manager_;
+  // Prepares histogram deltas for transmission.
+  scoped_ptr<base::HistogramDeltaSerialization> histogram_delta_serialization_;
 
   DISALLOW_COPY_AND_ASSIGN(ChildHistogramMessageFilter);
 };