Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(15)

Side by Side Diff: components/metrics/file_metrics_provider.cc

Issue 1537743006: Persist setup metrics and have Chrome report them during UMA upload. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@shared-histograms
Patch Set: addressed review comments by Alexei & Greg Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/metrics/file_metrics_provider.h"
6
7 #include "base/command_line.h"
8 #include "base/files/file.h"
9 #include "base/files/file_util.h"
10 #include "base/files/memory_mapped_file.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram_base.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/metrics/histogram_persistence.h"
15 #include "base/metrics/persistent_memory_allocator.h"
16 #include "base/task_runner.h"
17 #include "base/time/time.h"
18 #include "components/metrics/metrics_pref_names.h"
19 #include "components/metrics/metrics_service.h"
20 #include "components/prefs/pref_registry_simple.h"
21 #include "components/prefs/pref_service.h"
22
23
24 namespace {
25
26 enum AccessResult {
27 // File was successfully mapped.
28 ACCESS_RESULT_SUCCESS,
29
30 // File does not exist.
31 ACCESS_RESULT_DOESNT_EXIST,
32
33 // File exists but not modified since last read.
34 ACCESS_RESULT_NOT_MODIFIED,
35
36 // File is not valid: is a directory or zero-size.
37 ACCESS_RESULT_INVALID_FILE,
38
39 // System could not map file into memory.
40 ACCESS_RESULT_SYSTEM_MAP_FAILURE,
41
42 // File had invalid contents.
43 ACCESS_RESULT_INVALID_CONTENTS,
44
45 ACCESS_RESULT_MAX
46 };
47
48 } // namespace
49
50 namespace metrics {
51
52 // This structure stores all the information about the files being monitored
53 // and their current reporting state.
54 struct FileMetricsProvider::FileInfo {
55 FileInfo() {}
56 ~FileInfo() {}
57
58 // Where on disk the file is located.
59 base::FilePath path;
60
61 // How to access this file (atomic/active).
62 FileType type;
63
64 // Name used inside prefs to persistent metadata.
65 std::string prefs_key;
66
67 // The last-seen time of this file to detect change.
68 base::Time last_seen;
69
70 // Once a file has been recognized as needing to be read, it is mapped into
71 // memory as a file ("active") or local memory ("atomic") and is assigned an
72 // allocator.
73 std::vector<uint8_t> data;
74 scoped_ptr<base::MemoryMappedFile> mapped;
75 scoped_ptr<base::PersistentMemoryAllocator> allocator;
76
77 private:
78 DISALLOW_COPY_AND_ASSIGN(FileInfo);
79 };
80
81 FileMetricsProvider::FileMetricsProvider(
82 const scoped_refptr<base::TaskRunner>& task_runner,
83 PrefService* local_state)
84 : task_runner_(task_runner),
85 pref_service_(local_state),
86 weak_factory_(this) {
87 }
88
89 FileMetricsProvider::~FileMetricsProvider() {}
90
91 void FileMetricsProvider::RegisterFile(const base::FilePath& path,
92 FileType type,
93 const base::StringPiece prefs_key) {
94 scoped_ptr<FileInfo> file(new FileInfo());
95 file->path = path;
96 file->type = type;
97 file->prefs_key = prefs_key.as_string();
98
99 // |prefs_key| may be empty if the caller does not wish to persist the
100 // state across instances of the program.
101 if (pref_service_ && !prefs_key.empty()) {
102 file->last_seen = base::Time::FromInternalValue(
103 pref_service_->GetInt64(metrics::prefs::kMetricsLastSeenPrefix +
104 prefs_key.as_string()));
105 }
106
107 files_to_check_.push_back(std::move(file));
108 }
109
110 // static
111 void FileMetricsProvider::RegisterPrefs(PrefRegistrySimple* prefs,
112 const base::StringPiece prefs_key) {
113 prefs->RegisterInt64Pref(metrics::prefs::kMetricsLastSeenPrefix +
114 prefs_key.as_string(), 0);
115 }
116
117 // This method has all state information passed in |files| and is intended
118 // to run on a worker thread rather than the UI thread.
119 // static
120 void FileMetricsProvider::CheckAndMapNewMetricFilesOnTaskRunner(
121 FileMetricsProvider::FileInfoList* files) {
122 for (auto& file : *files) {
123 int result = CheckAndMapNewMetrics(file.get());
124 // Some results are not reported in order to keep the dashboard clean.
125 if (result != ACCESS_RESULT_DOESNT_EXIST &&
126 result != ACCESS_RESULT_NOT_MODIFIED) {
127 UMA_HISTOGRAM_ENUMERATION(
128 "UMA.FileMetricsProvider.MapResult", result, ACCESS_RESULT_MAX);
129 }
130 }
131 }
132
133 // This method has all state information passed in |file| and is intended
134 // to run on a worker thread rather than the UI thread.
135 // static
136 int FileMetricsProvider::CheckAndMapNewMetrics(
137 FileMetricsProvider::FileInfo* file) {
138 DCHECK(!file->mapped);
139 DCHECK(file->data.empty());
140
141 base::File::Info info;
142 if (!base::GetFileInfo(file->path, &info))
143 return ACCESS_RESULT_DOESNT_EXIST;
144
145 if (info.is_directory || info.size == 0)
146 return ACCESS_RESULT_INVALID_FILE;
147
148 if (file->last_seen >= info.last_modified)
149 return ACCESS_RESULT_NOT_MODIFIED;
150
151 // A new file of metrics has been found. Map it into memory.
152 // TODO(bcwhite): Make this open read/write when supported for "active".
153 file->mapped.reset(new base::MemoryMappedFile());
154 if (!file->mapped->Initialize(file->path)) {
155 file->mapped.reset();
156 return ACCESS_RESULT_SYSTEM_MAP_FAILURE;
157 }
158
159 // Ensure any problems below don't occur repeatedly.
160 file->last_seen = info.last_modified;
161
162 // Test the validity of the file contents.
163 if (!base::FilePersistentMemoryAllocator::IsFileAcceptable(*file->mapped))
164 return ACCESS_RESULT_INVALID_CONTENTS;
165
166 // For an "atomic" file, immediately copy the data into local memory and
167 // release the file so that it is not held open.
168 if (file->type == FILE_HISTOGRAMS_ATOMIC) {
169 file->data.assign(file->mapped->data(),
170 file->mapped->data() + file->mapped->length());
171 file->mapped.reset();
172 }
173
174 return ACCESS_RESULT_SUCCESS;
175 }
176
177 void FileMetricsProvider::ScheduleFilesCheck() {
178 if (files_to_check_.empty())
179 return;
180
181 FileInfoList* check_list = new FileInfoList();
182 std::swap(files_to_check_, *check_list);
183 task_runner_->PostTaskAndReply(
184 FROM_HERE,
185 base::Bind(&FileMetricsProvider::CheckAndMapNewMetricFilesOnTaskRunner,
186 base::Unretained(check_list)),
187 base::Bind(&FileMetricsProvider::RecordFilesChecked,
188 weak_factory_.GetWeakPtr(), base::Owned(check_list)));
189 }
190
191 void FileMetricsProvider::RecordHistogramSnapshotsFromFile(
192 base::HistogramSnapshotManager* snapshot_manager,
193 FileInfo* file) {
194 int histogram_count = 0;
195 base::PersistentMemoryAllocator::Iterator hist_iter;
196 file->allocator->CreateIterator(&hist_iter);
197
198 while (true) {
199 scoped_ptr<base::HistogramBase> histogram(
200 base::GetNextPersistentHistogram(file->allocator.get(), &hist_iter));
201 if (!histogram)
202 break;
203 if (file->type == FILE_HISTOGRAMS_ATOMIC)
204 snapshot_manager->PrepareAbsoluteTakingOwnership(std::move(histogram));
205 else
206 snapshot_manager->PrepareDeltaTakingOwnership(std::move(histogram));
207 ++histogram_count;
208 }
209
210 DVLOG(1) << "Reported " << histogram_count << " histograms from "
211 << file->path.value();
212 }
213
214 void FileMetricsProvider::CreateAllocatorForFile(FileInfo* file) {
215 DCHECK(!file->allocator);
216
217 // File data was validated earlier. Files are not considered "untrusted"
218 // as some processes might be (e.g. Renderer) so there's no need to check
219 // again to try to thwart some malicious actor that may have modified the
220 // data between then and now.
221 if (file->mapped) {
222 DCHECK(file->data.empty());
223 // TODO(bcwhite): Make this do read/write when supported for "active".
224 file->allocator.reset(new base::FilePersistentMemoryAllocator(
225 std::move(file->mapped), 0, std::string()));
226 } else {
227 DCHECK(!file->mapped);
228 file->allocator.reset(new base::PersistentMemoryAllocator(
229 &file->data[0], file->data.size(), 0, 0, "", true));
230 }
231 }
232
233 void FileMetricsProvider::RecordFilesChecked(FileInfoList* checked) {
234 DCHECK(thread_checker_.CalledOnValidThread());
235
236 // Move each processed file to either the "to-read" list (for processing) or
237 // the "to-check" list (for future checking).
238 for (auto iter = checked->begin(); iter != checked->end();) {
239 auto temp = iter++;
240 const FileInfo* file = temp->get();
241 if (file->mapped || !file->data.empty())
242 files_to_read_.splice(files_to_read_.end(), *checked, temp);
243 else
244 files_to_check_.splice(files_to_check_.end(), *checked, temp);
245 }
246 }
247
248 void FileMetricsProvider::RecordFileAsSeen(FileInfo* file) {
249 if (pref_service_ && !file->prefs_key.empty()) {
250 pref_service_->SetInt64(metrics::prefs::kMetricsLastSeenPrefix +
251 file->prefs_key,
252 file->last_seen.ToInternalValue());
253 }
254 }
255
256 void FileMetricsProvider::OnDidCreateMetricsLog() {
257 DCHECK(thread_checker_.CalledOnValidThread());
258
259 // Move finished metric files back to list of monitored files.
260 for (auto iter = files_to_read_.begin(); iter != files_to_read_.end();) {
261 auto temp = iter++;
262 const FileInfo* file = temp->get();
263 if (!file->allocator && !file->mapped && file->data.empty())
264 files_to_check_.splice(files_to_check_.end(), files_to_read_, temp);
265 }
266
267 // Schedule a check to see if there are new metrics to load. If so, they
268 // will be reported during the next collection run after this one. The
269 // check is run off of the worker-pool so as to not cause delays on the
270 // main UI thread (which is currently where metric collection is done).
271 ScheduleFilesCheck();
272 }
273
274 void FileMetricsProvider::RecordHistogramSnapshots(
275 base::HistogramSnapshotManager* snapshot_manager) {
276 DCHECK(thread_checker_.CalledOnValidThread());
277
278 for (auto& file : files_to_read_) {
279 // If the file is mapped or loaded then it needs to have an allocator
280 // attached to it in order to read histograms out of it.
281 if (file->mapped || !file->data.empty())
282 CreateAllocatorForFile(file.get());
283
284 // A file should not be under "files to read" unless it has an allocator
285 // or is memory-mapped (at which point it will have received an allocator
286 // above). However, if this method gets called twice before the scheduled-
287 // files-check has a chance to clean up, this may trigger. This also
288 // catches the case where creating an allocator from the file has failed.
289 if (!file->allocator)
290 continue;
291
292 // Dump all histograms contained within the file to the snapshot-manager.
293 RecordHistogramSnapshotsFromFile(snapshot_manager, file.get());
294
295 // Atomic files are read once and then ignored unless they change.
296 if (file->type == FILE_HISTOGRAMS_ATOMIC) {
297 DCHECK(!file->mapped);
298 file->allocator.reset();
299 file->data.clear();
300 RecordFileAsSeen(file.get());
301 }
302 }
303 }
304
305 } // namespace metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698