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..d65357fa17b989bc806f01c684070947205e6559 |
--- /dev/null |
+++ b/components/metrics/file_metrics_provider.cc |
@@ -0,0 +1,214 @@ |
+// Copyright 2015 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_found_(0), |
grt (UTC plus 2)
2016/02/04 15:36:42
nit: remove this initializer
bcwhite
2016/02/04 21:51:14
Leftover from a previous design. Odd that it isn'
|
+ metrics_service_(metrics_service), |
+ pref_service_(local_state) { |
+ // Start a background check for pending setup metrics so it can be uploaded |
grt (UTC plus 2)
2016/02/04 15:36:42
is it appropriate for this to be generalized so th
bcwhite
2016/02/04 21:51:14
I started it as something specific for Setup but t
grt (UTC plus 2)
2016/02/08 18:09:18
In that case, could you update this and other refe
bcwhite
2016/02/09 21:08:45
Done.
|
+ // 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 |
grt (UTC plus 2)
2016/02/04 15:36:42
the destruction problems are avoided by following
|
+ // 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 std::string& prefs_key) { |
+ FileInformation* file = new FileInformation(); |
+ file->path = path; |
+ file->type = type; |
+ file->prefs_key = prefs_key; |
+ |
+ if (!prefs_key.empty()) { |
grt (UTC plus 2)
2016/02/04 15:36:43
&& pref_service_
since it could be null as per the
bcwhite
2016/02/04 21:51:14
Done.
|
+ file->last_seen = base::Time::FromInternalValue( |
+ pref_service_->GetInt64(metrics::prefs::kSetupMetricsLastSeenPrefix + |
+ prefs_key)); |
+ } |
+ |
+ base::AutoLock lock(lock_); |
+ metrics_files_.push_back(scoped_ptr<FileInformation>(file)); |
grt (UTC plus 2)
2016/02/04 15:36:43
nit: scoped_ptr<FileInformation>(file) -> make_sco
bcwhite
2016/02/04 21:51:14
Done.
|
+} |
+ |
+// static |
+void FileMetricsProvider::RegisterPrefs(PrefRegistrySimple* prefs, |
+ const std::string& key) { |
+ prefs->RegisterInt64Pref(metrics::prefs::kSetupMetricsLastSeenPrefix + |
grt (UTC plus 2)
2016/02/04 15:36:43
nit: indentation
bcwhite
2016/02/04 21:51:14
Done.
|
+ key, 0); |
+} |
+ |
+void FileMetricsProvider::OnDidCreateMetricsLog() { |
+ // Hold off async updates to file lists while processing. |
+ base::AutoLock lock(lock_); |
+ |
+ // Move finished metric files back to list of monitored files. |
+ FileInformationList::iterator iter; |
+ for (iter = metrics_found_.begin(); iter != metrics_found_.end();) { |
grt (UTC plus 2)
2016/02/04 15:36:43
nit: define |iter| here with "auto iter = ..."
bcwhite
2016/02/04 21:51:14
Done.
|
+ auto temp = iter++; |
+ const FileInformation* file = temp->get(); |
grt (UTC plus 2)
2016/02/04 15:36:43
nit: omit this local var and use temp-> directly b
bcwhite
2016/02/04 21:51:14
I'd rather not. It looks ugly with all the temp->
|
+ if (!file->allocator && !file->mapped) { |
+ metrics_files_.push_back(std::move(*temp)); |
grt (UTC plus 2)
2016/02/04 15:36:43
i believe this line and the next can be replaced w
bcwhite
2016/02/04 21:51:14
Done.
|
+ metrics_found_.erase(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_iter : metrics_found_) { |
grt (UTC plus 2)
2016/02/04 15:36:43
nit: |file_iter| isn't an iterator here, but rathe
bcwhite
2016/02/04 21:51:14
Done.
|
+ FileInformation* file = file_iter.get(); |
grt (UTC plus 2)
2016/02/04 15:36:43
nit: omit this local var and use the auto'd |file|
bcwhite
2016/02/04 21:51:14
Done.
|
+ |
+ 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.AsUTF8Unsafe() |
grt (UTC plus 2)
2016/02/04 15:36:43
file->path.value() should be fine here
bcwhite
2016/02/04 21:51:14
Done.
|
+ << "\" is not valid -- ignored."; |
+ file->mapped.reset(); |
+ RecordFileAsSeen(file); |
+ NOTREACHED(); |
+ continue; |
+ } |
+ file->allocator.reset(new base::FilePersistentMemoryAllocator( |
grt (UTC plus 2)
2016/02/04 15:36:43
change FilePersistentMemoryAllocator so that it ta
bcwhite
2016/02/04 21:51:14
Doing that in a different CL:
https://codereview.c
|
+ file->mapped.release(), 0, std::string())); |
+ } |
+ |
+ if (file->allocator) { |
grt (UTC plus 2)
2016/02/04 15:36:43
remove this condition as it will always be true on
bcwhite
2016/02/04 21:51:14
Done.
|
+ base::PersistentMemoryAllocator::Iterator hist_iter; |
+ file->allocator->CreateIterator(&hist_iter); |
+ for (;;) { |
+ base::HistogramBase* histogram = |
grt (UTC plus 2)
2016/02/04 15:36:43
use scoped_ptr<base::HistogramBase> rather than a
bcwhite
2016/02/09 21:08:45
Done.
|
+ base::GetNextPersistentHistogram(file->allocator.get(), &hist_iter); |
grt (UTC plus 2)
2016/02/04 15:36:43
please update this and the related functions to re
bcwhite
2016/02/09 21:08:45
Okay. It doesn't belong here, though. I'll do it
|
+ if (!histogram) |
+ break; |
+ if (file->type == FILE_HISTOGRAMS_ATOMIC) { |
grt (UTC plus 2)
2016/02/04 15:36:43
nit: omit braces
bcwhite
2016/02/04 21:51:14
Done.
|
+ 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); |
+ } |
+ } |
+ } |
+} |
+ |
+void FileMetricsProvider::RecordFileAsSeen(FileInformation* file) { |
+ file->last_seen = base::Time::Now(); |
+ if (!file->prefs_key.empty()) { |
grt (UTC plus 2)
2016/02/04 15:36:42
&& pref_service_
bcwhite
2016/02/04 21:51:14
Done.
|
+ pref_service_->SetInt64(metrics::prefs::kSetupMetricsLastSeenPrefix + |
+ file->prefs_key, |
+ file->last_seen.ToInternalValue()); |
+ } |
+} |
+ |
+// static |
+void FileMetricsProvider::CheckAndMapNewMetricFiles( |
grt (UTC plus 2)
2016/02/04 15:36:43
nit: often a suffix such as "OnBlockingPool" is ad
bcwhite
2016/02/04 21:51:14
Done.
|
+ 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()); |
+ file->mapped->Initialize(file->path); |
grt (UTC plus 2)
2016/02/04 15:36:43
if (file->mapped->Initialize(file->path))
retu
bcwhite
2016/02/04 21:51:14
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( |
grt (UTC plus 2)
2016/02/04 15:36:42
This should use the BrowserThread BlockingPool rat
|
+ FROM_HERE, |
+ base::Bind(&CheckAndMapNewMetricFiles, |
+ base::Owned(check_list), |
+ base::Bind(&FileMetricsProvider::RecordFileInformation, |
+ base::Unretained(this))), |
grt (UTC plus 2)
2016/02/04 15:36:43
This will lead to a use-after-free if the metrics
|
+ false); |
+ DCHECK(posted); |
grt (UTC plus 2)
2016/02/04 15:36:42
I don't think this is needed. false from PostTask*
|
+} |
+ |
+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) { |
grt (UTC plus 2)
2016/02/04 15:36:43
nit: omit braces
bcwhite
2016/02/09 21:08:45
Done.
|
+ metrics_found_.push_back(std::move(iter)); |
grt (UTC plus 2)
2016/02/04 15:36:43
use splice to move the element. you'll have to cha
bcwhite
2016/02/04 21:51:14
Each move in that case has to delete the value fro
grt (UTC plus 2)
2016/02/08 18:09:18
metrics_files_ and metrics_found_ are both linked
|
+ } else { |
+ metrics_files_.push_back(std::move(iter)); |
grt (UTC plus 2)
2016/02/04 15:36:43
metrics_files_.splice(metrics_files_.end(), *files
bcwhite
2016/02/04 21:51:14
Done.
|
+ } |
+ } |
+} |
+ |
+} // namespace metrics |