Index: components/metrics/file_metrics_provider.cc |
diff --git a/components/metrics/file_metrics_provider.cc b/components/metrics/file_metrics_provider.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..94423716b9a4a0fb50d3f83ebd37f77eedfc2758 |
--- /dev/null |
+++ b/components/metrics/file_metrics_provider.cc |
@@ -0,0 +1,207 @@ |
+// Copyright 2016 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/file_metrics_provider.h" |
+ |
+#include "base/command_line.h" |
+#include "base/files/file.h" |
+#include "base/files/file_util.h" |
+#include "base/files/memory_mapped_file.h" |
+#include "base/logging.h" |
+#include "base/metrics/histogram_base.h" |
+#include "base/metrics/histogram_persistence.h" |
+#include "base/metrics/persistent_memory_allocator.h" |
+#include "base/prefs/pref_registry_simple.h" |
+#include "base/prefs/pref_service.h" |
+#include "base/threading/worker_pool.h" |
+#include "base/time/time.h" |
+#include "components/metrics/metrics_pref_names.h" |
+#include "components/metrics/metrics_service.h" |
+ |
+namespace metrics { |
+ |
+// Out-of-line constructor and destructor needed for code efficiency. |
+FileMetricsProvider::FileInformation::FileInformation() {} |
+FileMetricsProvider::FileInformation::~FileInformation() {} |
+ |
+FileMetricsProvider::FileMetricsProvider( |
+ metrics::MetricsService* metrics_service, |
+ PrefService* local_state) |
+ : metrics_service_(metrics_service), |
+ pref_service_(local_state) { |
+ // Start a background check for pending setup metrics so it can be uploaded |
+ // during the initial report. |
+ ScheduleFilesCheck(); |
+} |
+ |
+FileMetricsProvider::~FileMetricsProvider() { |
+ // Destruction of this object can lead to failures because of a task |
+ // posted on the Worker Pool or because the owned Memory Allocator has |
+ // been passed to the Metrics Service. Both cases can only occur if |
+ // a Metrics Service has been provided (i.e. not during testing). |
+ DCHECK(!metrics_service_); |
+} |
+ |
+void FileMetricsProvider::RegisterFile(const base::FilePath& path, |
+ FileType type, |
+ const base::StringPiece& prefs_key) { |
+ FileInformation* file = new FileInformation(); |
+ file->path = path; |
+ file->type = type; |
+ file->prefs_key = prefs_key.as_string(); |
+ |
+ if (pref_service_ && !prefs_key.empty()) { |
+ file->last_seen = base::Time::FromInternalValue( |
+ pref_service_->GetInt64(metrics::prefs::kSetupMetricsLastSeenPrefix + |
+ prefs_key.as_string())); |
+ } |
+ |
+ base::AutoLock lock(lock_); |
+ metrics_files_.push_back(make_scoped_ptr(file)); |
+} |
+ |
+// static |
+void FileMetricsProvider::RegisterPrefs(PrefRegistrySimple* prefs, |
+ const base::StringPiece& key) { |
+ prefs->RegisterInt64Pref(metrics::prefs::kSetupMetricsLastSeenPrefix + |
+ key.as_string(), 0); |
+} |
+ |
+void FileMetricsProvider::OnDidCreateMetricsLog() { |
grt (UTC plus 2)
2016/02/08 18:09:19
nit: move these two functions down below RecordFil
bcwhite
2016/02/09 21:08:46
Done.
|
+ // Hold off async updates to file lists while processing. |
+ base::AutoLock lock(lock_); |
+ |
+ // Move finished metric files back to list of monitored files. |
+ for (auto iter = metrics_found_.begin(); iter != metrics_found_.end();) { |
+ auto temp = iter++; |
+ const FileInformation* file = temp->get(); |
+ if (!file->allocator && !file->mapped) |
+ metrics_files_.splice(metrics_files_.end(), metrics_found_, temp); |
+ } |
+ |
+ // Schedule a check to see if there are new metrics to load. If so, they |
+ // will be reported during the next collection run after this one. The |
+ // check is run off of the worker-pool so as to not cause delays on the |
+ // main UI thread (which is currently where metric collection is done). |
+ ScheduleFilesCheck(); |
+} |
+ |
+void FileMetricsProvider::RecordHistogramSnapshots( |
+ base::HistogramSnapshotManager* hsm) { |
+ // Hold off async updates to file lists while processing. |
+ base::AutoLock lock(lock_); |
+ |
+ for (auto& file : metrics_found_) { |
+ if (!file->allocator) { |
+ DCHECK(file->mapped); |
+ if (!base::FilePersistentMemoryAllocator::IsFileAcceptable( |
+ *file->mapped)) { |
+ // Something is fundamentally wrong with the file. Ignore it. |
+ LOG(ERROR) << "Metrics file \"" << file->path.value() |
+ << "\" is not valid -- ignored."; |
+ file->mapped.reset(); |
+ RecordFileAsSeen(file.get()); |
+ NOTREACHED(); |
+ continue; |
+ } |
+ file->allocator.reset(new base::FilePersistentMemoryAllocator( |
+ file->mapped.release(), 0, std::string())); |
+ } |
+ |
+ base::PersistentMemoryAllocator::Iterator hist_iter; |
+ file->allocator->CreateIterator(&hist_iter); |
+ for (;;) { |
+ base::HistogramBase* histogram = |
+ base::GetNextPersistentHistogram(file->allocator.get(), &hist_iter); |
+ if (!histogram) |
+ break; |
+ if (file->type == FILE_HISTOGRAMS_ATOMIC) |
+ hsm->PrepareOnceTakingOwnership(histogram); |
+ else |
+ hsm->PrepareDeltaTakingOwnership(histogram); |
+ } |
+ |
+ if (file->type == FILE_HISTOGRAMS_ATOMIC) { |
+ DCHECK(!file->mapped); // Ownership should have moved to allocator. |
+ file->allocator.reset(); |
+ RecordFileAsSeen(file.get()); |
+ } |
+ } |
+} |
+ |
+void FileMetricsProvider::RecordFileAsSeen(FileInformation* file) { |
+ file->last_seen = base::Time::Now(); |
+ if (pref_service_ && !file->prefs_key.empty()) { |
+ pref_service_->SetInt64(metrics::prefs::kSetupMetricsLastSeenPrefix + |
+ file->prefs_key, |
+ file->last_seen.ToInternalValue()); |
+ } |
+} |
+ |
+// static |
+void FileMetricsProvider::CheckAndMapNewMetricFilesOnBlockingPool( |
+ FileMetricsProvider::FileInformationList* files, |
+ base::Callback<void(FileInformationList*)> callback) { |
+ for (auto& file : *files) |
+ CheckAndMapNewMetrics(file.get()); |
+ callback.Run(files); |
+} |
+ |
+// static |
+bool FileMetricsProvider::CheckAndMapNewMetrics( |
+ FileMetricsProvider::FileInformation* file) { |
+ DCHECK(!file->mapped); |
+ |
+ base::File::Info info; |
+ if (!base::GetFileInfo(file->path, &info) || |
+ info.is_directory || info.size == 0) { |
+ return false; |
+ } |
+ |
+ if (file->last_seen >= info.last_modified) |
+ return false; |
+ |
+ // A new file of setup metrics has been found. Map it into memory. |
+ file->mapped.reset(new base::MemoryMappedFile()); |
+ if (!file->mapped->Initialize(file->path)) { |
+ NOTREACHED(); |
+ return false; |
grt (UTC plus 2)
2016/02/08 18:09:19
the |mapped| scoped_ptr must also be reset since i
bcwhite
2016/02/09 21:08:46
Done.
|
+ } |
+ return true; |
+} |
+ |
+void FileMetricsProvider::ScheduleFilesCheck() { |
+ // Don't do anything if metrics_service is not valid (for testing). |
+ if (!metrics_service_) |
+ return; |
+ |
+ // TODO(bcwhite): Change this if metric collection moves off UI thread. |
+ FileInformationList* check_list = new FileInformationList(); |
+ std::swap(metrics_files_, *check_list); |
+ bool posted = base::WorkerPool::PostTask( |
+ FROM_HERE, |
+ base::Bind(&CheckAndMapNewMetricFilesOnBlockingPool, |
+ base::Owned(check_list), |
+ base::Bind(&FileMetricsProvider::RecordFileInformation, |
+ base::Unretained(this))), |
+ false); |
+ DCHECK(posted); |
+} |
+ |
+void FileMetricsProvider::RecordFileInformation(FileInformationList* files) { |
+ // This is a callback and thus possibly not on the same thread as the main |
+ // metrics collection. |
+ base::AutoLock lock(lock_); |
+ |
+ // Move each processed file to either the "found" list (for processing) or |
+ // the "files" list (for future checking). |
+ for (auto& iter : *files) { |
+ if (iter->mapped) |
+ metrics_found_.push_back(std::move(iter)); |
+ else |
+ metrics_files_.push_back(std::move(iter)); |
+ } |
+} |
+ |
+} // namespace metrics |