Move PersistentHistograms to components

In order to enable persistent histograms on iOS, the code needs to be moved to
components. The only dependancy on //chrome was the path to store the metrics
which was moved to a function parameter.

Bug: 963504
Change-Id: Id2240a1ae1c1bfb4124d783b210a59caa1fcc0a2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1614294
Reviewed-by: Robert Kaplow <rkaplow@chromium.org>
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#660894}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: c585702a543fae3f042ac9cff586fc414c905cd9
diff --git a/BUILD.gn b/BUILD.gn
index 8832d0c..2936b89 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -81,11 +81,8 @@
     "metrics_switches.h",
     "metrics_upload_scheduler.cc",
     "metrics_upload_scheduler.h",
-    "unsent_log_store.cc",
-    "unsent_log_store.h",
-    "unsent_log_store_metrics.h",
-    "unsent_log_store_metrics_impl.cc",
-    "unsent_log_store_metrics_impl.h",
+    "persistent_histograms.cc",
+    "persistent_histograms.h",
     "persistent_system_profile.cc",
     "persistent_system_profile.h",
     "reporting_service.cc",
@@ -99,6 +96,11 @@
     "system_memory_stats_recorder_win.cc",
     "system_session_analyzer_win.cc",
     "system_session_analyzer_win.h",
+    "unsent_log_store.cc",
+    "unsent_log_store.h",
+    "unsent_log_store_metrics.h",
+    "unsent_log_store_metrics_impl.cc",
+    "unsent_log_store_metrics_impl.h",
     "url_constants.cc",
     "url_constants.h",
     "version_utils.cc",
@@ -380,7 +382,6 @@
     "metrics_state_manager_unittest.cc",
     "net/net_metrics_log_uploader_unittest.cc",
     "net/network_metrics_provider_unittest.cc",
-    "unsent_log_store_unittest.cc",
     "persistent_system_profile_unittest.cc",
     "reporting_service_unittest.cc",
     "single_sample_metrics_factory_impl_unittest.cc",
@@ -388,6 +389,7 @@
     "stability_metrics_provider_unittest.cc",
     "system_session_analyzer_win_unittest.cc",
     "ui/screen_info_metrics_provider_unittest.cc",
+    "unsent_log_store_unittest.cc",
   ]
 
   deps = [
diff --git a/persistent_histograms.cc b/persistent_histograms.cc
new file mode 100644
index 0000000..4be700d
--- /dev/null
+++ b/persistent_histograms.cc
@@ -0,0 +1,165 @@
+// Copyright 2018 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 "components/metrics/persistent_histograms.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/metrics/persistent_histogram_allocator.h"
+#include "base/system/sys_info.h"
+#include "base/task/post_task.h"
+#include "build/build_config.h"
+#include "components/metrics/persistent_system_profile.h"
+#include "components/variations/variations_associated_data.h"
+
+namespace {
+
+// Creating a "spare" file for persistent metrics involves a lot of I/O and
+// isn't important so delay the operation for a while after startup.
+#if defined(OS_ANDROID)
+// Android needs the spare file and also launches faster.
+constexpr bool kSpareFileRequired = true;
+constexpr int kSpareFileCreateDelaySeconds = 10;
+#else
+// Desktop may have to restore a lot of tabs so give it more time before doing
+// non-essential work. The spare file is still a performance boost but not as
+// significant of one so it's not required.
+constexpr bool kSpareFileRequired = false;
+constexpr int kSpareFileCreateDelaySeconds = 90;
+#endif
+
+}  // namespace
+
+const char kBrowserMetricsName[] = "BrowserMetrics";
+
+// Check for feature enabling the use of persistent histogram storage and
+// enable the global allocator if so.
+void InstantiatePersistentHistograms(const base::FilePath& metrics_dir) {
+  // Create a directory for storing completed metrics files. Files in this
+  // directory must have embedded system profiles. If the directory can't be
+  // created, the file will just be deleted below.
+  base::FilePath upload_dir = metrics_dir.AppendASCII(kBrowserMetricsName);
+  base::CreateDirectory(upload_dir);
+
+  // Metrics files are typically created as a |spare_file| in the profile
+  // directory (e.g. "BrowserMetrics-spare.pma") and are then rotated into
+  // a subdirectory as a stamped file for upload when no longer in use.
+  // (e.g. "BrowserMetrics/BrowserMetrics-1234ABCD-12345.pma")
+  base::FilePath upload_file;
+  base::FilePath active_file;
+  base::FilePath spare_file;
+  base::GlobalHistogramAllocator::ConstructFilePathsForUploadDir(
+      metrics_dir, upload_dir, kBrowserMetricsName, &upload_file, &active_file,
+      &spare_file);
+
+  // This is used to report results to an UMA histogram.
+  enum InitResult {
+    kLocalMemorySuccess,
+    kLocalMemoryFailed,
+    kMappedFileSuccess,
+    kMappedFileFailed,
+    kMappedFileExists,
+    kNoSpareFile,
+    kNoUploadDir,
+    kMaxValue = kNoUploadDir
+  };
+  InitResult result;
+
+  // Create persistent/shared memory and allow histograms to be stored in
+  // it. Memory that is not actualy used won't be physically mapped by the
+  // system. BrowserMetrics usage, as reported in UMA, has the 99.99
+  // percentile around 3MiB as of 2018-10-22.
+  const size_t kAllocSize = 4 << 20;     // 4 MiB
+  const uint32_t kAllocId = 0x935DDD43;  // SHA1(BrowserMetrics)
+  std::string storage = variations::GetVariationParamValueByFeature(
+      base::kPersistentHistogramsFeature, "storage");
+
+  static const char kMappedFile[] = "MappedFile";
+  static const char kLocalMemory[] = "LocalMemory";
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+  // Linux kernel 4.4.0.* shows a huge number of SIGBUS crashes with persistent
+  // histograms enabled using a mapped file.  Change this to use local memory.
+  // https://bugs.chromium.org/p/chromium/issues/detail?id=753741
+  if (storage.empty() || storage == kMappedFile) {
+    int major, minor, bugfix;
+    base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
+    if (major == 4 && minor == 4 && bugfix == 0)
+      storage = kLocalMemory;
+  }
+#endif
+
+  // Don't use mapped-file memory by default on low-end devices, especially
+  // Android. The extra disk consumption and/or extra disk access could have
+  // a significant performance impact. https://crbug.com/896394
+  if (storage.empty() && base::SysInfo::IsLowEndDevice())
+    storage = kLocalMemory;
+
+  // Create a global histogram allocator using the desired storage type.
+  if (storage.empty() || storage == kMappedFile) {
+    if (!base::PathExists(upload_dir)) {
+      // Handle failure to create the directory.
+      result = kNoUploadDir;
+    } else if (base::PathExists(upload_file)) {
+      // "upload" filename is supposed to be unique so this shouldn't happen.
+      result = kMappedFileExists;
+    } else {
+      // Move any sparse file into the upload position.
+      base::ReplaceFile(spare_file, upload_file, nullptr);
+      // Create global allocator using the "upload" file.
+      if (kSpareFileRequired && !base::PathExists(upload_file)) {
+        result = kNoSpareFile;
+      } else if (base::GlobalHistogramAllocator::CreateWithFile(
+                     upload_file, kAllocSize, kAllocId, kBrowserMetricsName)) {
+        result = kMappedFileSuccess;
+      } else {
+        result = kMappedFileFailed;
+      }
+    }
+    // Schedule the creation of a "spare" file for use on the next run.
+    base::PostDelayedTaskWithTraits(
+        FROM_HERE,
+        {base::MayBlock(), base::TaskPriority::LOWEST,
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+        base::BindOnce(base::IgnoreResult(
+                           &base::GlobalHistogramAllocator::CreateSpareFile),
+                       std::move(spare_file), kAllocSize),
+        base::TimeDelta::FromSeconds(kSpareFileCreateDelaySeconds));
+  } else if (storage == kLocalMemory) {
+    // Use local memory for storage even though it will not persist across
+    // an unclean shutdown. This sets the result but the actual creation is
+    // done below.
+    result = kLocalMemorySuccess;
+  } else {
+    // Persistent metric storage is disabled. Must return here.
+    return;
+  }
+
+  // Get the allocator that was just created and report result. Exit if the
+  // allocator could not be created.
+  UMA_HISTOGRAM_ENUMERATION("UMA.PersistentHistograms.InitResult", result);
+
+  base::GlobalHistogramAllocator* allocator =
+      base::GlobalHistogramAllocator::Get();
+  if (!allocator) {
+    // If no allocator was created above, try to create a LocalMemomory one
+    // here. This avoids repeating the call many times above. In the case where
+    // persistence is disabled, an early return is done above.
+    base::GlobalHistogramAllocator::CreateWithLocalMemory(kAllocSize, kAllocId,
+                                                          kBrowserMetricsName);
+    allocator = base::GlobalHistogramAllocator::Get();
+    if (!allocator)
+      return;
+  }
+
+  // Store a copy of the system profile in this allocator.
+  metrics::GlobalPersistentSystemProfile::GetInstance()
+      ->RegisterPersistentAllocator(allocator->memory_allocator());
+
+  // Create tracking histograms for the allocator and record storage file.
+  allocator->CreateTrackingHistograms(kBrowserMetricsName);
+}
diff --git a/persistent_histograms.h b/persistent_histograms.h
new file mode 100644
index 0000000..72ef923
--- /dev/null
+++ b/persistent_histograms.h
@@ -0,0 +1,20 @@
+// Copyright 2018 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 COMPONENTS_METRICS_PERSISTENT_HISTOGRAMS_H_
+#define COMPONENTS_METRICS_PERSISTENT_HISTOGRAMS_H_
+
+#include "base/files/file_path.h"
+
+// Persistent browser metrics need to be persisted somewhere. This constant
+// provides a known string to be used for both the allocator's internal name
+// and for a file on disk (relative to metrics_dir) to which they
+// can be saved. This is exported so the name can also be used as a "pref"
+// during configuration.
+extern const char kBrowserMetricsName[];
+
+// Do all the checking and work necessary to enable persistent histograms.
+void InstantiatePersistentHistograms(const base::FilePath& metrics_dir);
+
+#endif  // COMPONENTS_METRICS_PERSISTENT_HISTOGRAMS_H_