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

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

Issue 2965753002: [Cleanup] Migrate the FileMetricsProvider to use the Task Scheduler. (Closed)
Patch Set: Change DCHECK syntax Created 3 years, 5 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/metrics/file_metrics_provider.h" 5 #include "components/metrics/file_metrics_provider.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/files/file.h" 8 #include "base/files/file.h"
9 #include "base/files/file_enumerator.h" 9 #include "base/files/file_enumerator.h"
10 #include "base/files/file_util.h" 10 #include "base/files/file_util.h"
11 #include "base/files/memory_mapped_file.h" 11 #include "base/files/memory_mapped_file.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/memory/ptr_util.h" 13 #include "base/memory/ptr_util.h"
14 #include "base/metrics/histogram_base.h" 14 #include "base/metrics/histogram_base.h"
15 #include "base/metrics/histogram_macros.h" 15 #include "base/metrics/histogram_macros.h"
16 #include "base/metrics/persistent_histogram_allocator.h" 16 #include "base/metrics/persistent_histogram_allocator.h"
17 #include "base/metrics/persistent_memory_allocator.h" 17 #include "base/metrics/persistent_memory_allocator.h"
18 #include "base/strings/string_piece.h" 18 #include "base/strings/string_piece.h"
19 #include "base/task_runner.h" 19 #include "base/task_runner.h"
20 #include "base/task_scheduler/post_task.h"
21 #include "base/task_scheduler/task_traits.h"
20 #include "base/time/time.h" 22 #include "base/time/time.h"
21 #include "components/metrics/metrics_pref_names.h" 23 #include "components/metrics/metrics_pref_names.h"
22 #include "components/metrics/metrics_service.h" 24 #include "components/metrics/metrics_service.h"
23 #include "components/metrics/persistent_system_profile.h" 25 #include "components/metrics/persistent_system_profile.h"
24 #include "components/prefs/pref_registry_simple.h" 26 #include "components/prefs/pref_registry_simple.h"
25 #include "components/prefs/pref_service.h" 27 #include "components/prefs/pref_service.h"
26 28
27 namespace metrics { 29 namespace metrics {
28 30
29 namespace { 31 namespace {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
86 88
87 void DeleteFileWhenPossible(const base::FilePath& path) { 89 void DeleteFileWhenPossible(const base::FilePath& path) {
88 // Open (with delete) and then immediately close the file by going out of 90 // Open (with delete) and then immediately close the file by going out of
89 // scope. This is the only cross-platform safe way to delete a file that may 91 // scope. This is the only cross-platform safe way to delete a file that may
90 // be open elsewhere, a distinct possibility given the asynchronous nature 92 // be open elsewhere, a distinct possibility given the asynchronous nature
91 // of the delete task. 93 // of the delete task.
92 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ | 94 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
93 base::File::FLAG_DELETE_ON_CLOSE); 95 base::File::FLAG_DELETE_ON_CLOSE);
94 } 96 }
95 97
98 // A task runner to use for testing.
99 base::TaskRunner* g_task_runner_for_testing = nullptr;
100
101 // Returns a task runner appropriate for running background tasks that perform
102 // file I/O.
103 scoped_refptr<base::TaskRunner> CreateBackgroundTaskRunner() {
104 if (g_task_runner_for_testing)
105 return scoped_refptr<base::TaskRunner>(g_task_runner_for_testing);
106
107 return base::CreateTaskRunnerWithTraits(
108 {base::MayBlock(), base::TaskPriority::BACKGROUND,
109 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
110 }
111
96 } // namespace 112 } // namespace
97 113
98 // This structure stores all the information about the sources being monitored 114 // This structure stores all the information about the sources being monitored
99 // and their current reporting state. 115 // and their current reporting state.
100 struct FileMetricsProvider::SourceInfo { 116 struct FileMetricsProvider::SourceInfo {
101 SourceInfo(SourceType source_type, SourceAssociation source_association) 117 SourceInfo(SourceType source_type, SourceAssociation source_association)
102 : type(source_type), association(source_association) {} 118 : type(source_type), association(source_association) {}
103 ~SourceInfo() {} 119 ~SourceInfo() {}
104 120
105 // How to access this source (file/dir, atomic/active). 121 // How to access this source (file/dir, atomic/active).
(...skipping 20 matching lines...) Expand all
126 bool read_complete = false; 142 bool read_complete = false;
127 143
128 // Once a file has been recognized as needing to be read, it is mapped 144 // Once a file has been recognized as needing to be read, it is mapped
129 // into memory and assigned to an |allocator| object. 145 // into memory and assigned to an |allocator| object.
130 std::unique_ptr<base::PersistentHistogramAllocator> allocator; 146 std::unique_ptr<base::PersistentHistogramAllocator> allocator;
131 147
132 private: 148 private:
133 DISALLOW_COPY_AND_ASSIGN(SourceInfo); 149 DISALLOW_COPY_AND_ASSIGN(SourceInfo);
134 }; 150 };
135 151
136 FileMetricsProvider::FileMetricsProvider( 152 FileMetricsProvider::FileMetricsProvider(PrefService* local_state)
137 const scoped_refptr<base::TaskRunner>& task_runner, 153 : task_runner_(CreateBackgroundTaskRunner()),
138 PrefService* local_state)
139 : task_runner_(task_runner),
140 pref_service_(local_state), 154 pref_service_(local_state),
141 weak_factory_(this) { 155 weak_factory_(this) {
142 base::StatisticsRecorder::RegisterHistogramProvider( 156 base::StatisticsRecorder::RegisterHistogramProvider(
143 weak_factory_.GetWeakPtr()); 157 weak_factory_.GetWeakPtr());
144 } 158 }
145 159
146 FileMetricsProvider::~FileMetricsProvider() {} 160 FileMetricsProvider::~FileMetricsProvider() {}
147 161
148 void FileMetricsProvider::RegisterSource(const base::FilePath& path, 162 void FileMetricsProvider::RegisterSource(const base::FilePath& path,
149 SourceType type, 163 SourceType type,
150 SourceAssociation source_association, 164 SourceAssociation source_association,
151 const base::StringPiece prefs_key) { 165 const base::StringPiece prefs_key) {
152 DCHECK(thread_checker_.CalledOnValidThread()); 166 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
153 167
154 // Ensure that kSourceOptions has been filled for this type. 168 // Ensure that kSourceOptions has been filled for this type.
155 DCHECK_GT(arraysize(kSourceOptions), static_cast<size_t>(type)); 169 DCHECK_GT(arraysize(kSourceOptions), static_cast<size_t>(type));
156 170
157 std::unique_ptr<SourceInfo> source(new SourceInfo(type, source_association)); 171 std::unique_ptr<SourceInfo> source(new SourceInfo(type, source_association));
158 source->prefs_key = prefs_key.as_string(); 172 source->prefs_key = prefs_key.as_string();
159 173
160 switch (source->type) { 174 switch (source->type) {
161 case SOURCE_HISTOGRAMS_ACTIVE_FILE: 175 case SOURCE_HISTOGRAMS_ACTIVE_FILE:
162 DCHECK(prefs_key.empty()); 176 DCHECK(prefs_key.empty());
(...skipping 28 matching lines...) Expand all
191 } 205 }
192 206
193 // static 207 // static
194 void FileMetricsProvider::RegisterPrefs(PrefRegistrySimple* prefs, 208 void FileMetricsProvider::RegisterPrefs(PrefRegistrySimple* prefs,
195 const base::StringPiece prefs_key) { 209 const base::StringPiece prefs_key) {
196 prefs->RegisterInt64Pref(metrics::prefs::kMetricsLastSeenPrefix + 210 prefs->RegisterInt64Pref(metrics::prefs::kMetricsLastSeenPrefix +
197 prefs_key.as_string(), 0); 211 prefs_key.as_string(), 0);
198 } 212 }
199 213
200 // static 214 // static
215 void FileMetricsProvider::SetTaskRunnerForTesting(
216 const scoped_refptr<base::TaskRunner>& task_runner) {
217 DCHECK(!g_task_runner_for_testing);
218 g_task_runner_for_testing = task_runner.get();
219 }
220
221 // static
201 bool FileMetricsProvider::LocateNextFileInDirectory(SourceInfo* source) { 222 bool FileMetricsProvider::LocateNextFileInDirectory(SourceInfo* source) {
202 DCHECK_EQ(SOURCE_HISTOGRAMS_ATOMIC_DIR, source->type); 223 DCHECK_EQ(SOURCE_HISTOGRAMS_ATOMIC_DIR, source->type);
203 DCHECK(!source->directory.empty()); 224 DCHECK(!source->directory.empty());
204 225
205 // Open the directory and find all the files, remembering the oldest that 226 // Open the directory and find all the files, remembering the oldest that
206 // has not been read. They can be removed and/or ignored if they're older 227 // has not been read. They can be removed and/or ignored if they're older
207 // than the last-check time. 228 // than the last-check time.
208 base::Time oldest_file_time = base::Time::Now(); 229 base::Time oldest_file_time = base::Time::Now();
209 base::FilePath oldest_file_path; 230 base::FilePath oldest_file_path;
210 base::FilePath file_path; 231 base::FilePath file_path;
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after
431 snapshot_manager->PrepareFinalDelta(histogram.get()); 452 snapshot_manager->PrepareFinalDelta(histogram.get());
432 ++histogram_count; 453 ++histogram_count;
433 } 454 }
434 455
435 source->read_complete = true; 456 source->read_complete = true;
436 DVLOG(1) << "Reported " << histogram_count << " histograms from " 457 DVLOG(1) << "Reported " << histogram_count << " histograms from "
437 << source->path.value(); 458 << source->path.value();
438 } 459 }
439 460
440 void FileMetricsProvider::ScheduleSourcesCheck() { 461 void FileMetricsProvider::ScheduleSourcesCheck() {
441 DCHECK(thread_checker_.CalledOnValidThread()); 462 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
442 if (sources_to_check_.empty()) 463 if (sources_to_check_.empty())
443 return; 464 return;
444 465
445 // Create an independent list of sources for checking. This will be Owned() 466 // Create an independent list of sources for checking. This will be Owned()
446 // by the reply call given to the task-runner, to be deleted when that call 467 // by the reply call given to the task-runner, to be deleted when that call
447 // has returned. It is also passed Unretained() to the task itself, safe 468 // has returned. It is also passed Unretained() to the task itself, safe
448 // because that must complete before the reply runs. 469 // because that must complete before the reply runs.
449 SourceInfoList* check_list = new SourceInfoList(); 470 SourceInfoList* check_list = new SourceInfoList();
450 std::swap(sources_to_check_, *check_list); 471 std::swap(sources_to_check_, *check_list);
451 task_runner_->PostTaskAndReply( 472 task_runner_->PostTaskAndReply(
452 FROM_HERE, 473 FROM_HERE,
453 base::Bind(&FileMetricsProvider::CheckAndMergeMetricSourcesOnTaskRunner, 474 base::Bind(&FileMetricsProvider::CheckAndMergeMetricSourcesOnTaskRunner,
454 base::Unretained(check_list)), 475 base::Unretained(check_list)),
455 base::Bind(&FileMetricsProvider::RecordSourcesChecked, 476 base::Bind(&FileMetricsProvider::RecordSourcesChecked,
456 weak_factory_.GetWeakPtr(), base::Owned(check_list))); 477 weak_factory_.GetWeakPtr(), base::Owned(check_list)));
457 } 478 }
458 479
459 void FileMetricsProvider::RecordSourcesChecked(SourceInfoList* checked) { 480 void FileMetricsProvider::RecordSourcesChecked(SourceInfoList* checked) {
460 DCHECK(thread_checker_.CalledOnValidThread()); 481 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
461 482
462 // Sources that still have an allocator at this point are read/write "active" 483 // Sources that still have an allocator at this point are read/write "active"
463 // files that may need their contents merged on-demand. If there is no 484 // files that may need their contents merged on-demand. If there is no
464 // allocator (not a read/write file) but a read was done on the task-runner, 485 // allocator (not a read/write file) but a read was done on the task-runner,
465 // try again immediately to see if more is available (in a directory of 486 // try again immediately to see if more is available (in a directory of
466 // files). Otherwise, remember the source for checking again at a later time. 487 // files). Otherwise, remember the source for checking again at a later time.
467 bool did_read = false; 488 bool did_read = false;
468 for (auto iter = checked->begin(); iter != checked->end();) { 489 for (auto iter = checked->begin(); iter != checked->end();) {
469 auto temp = iter++; 490 auto temp = iter++;
470 SourceInfo* source = temp->get(); 491 SourceInfo* source = temp->get();
(...skipping 20 matching lines...) Expand all
491 // is possible. 512 // is possible.
492 if (did_read) 513 if (did_read)
493 ScheduleSourcesCheck(); 514 ScheduleSourcesCheck();
494 } 515 }
495 516
496 void FileMetricsProvider::DeleteFileAsync(const base::FilePath& path) { 517 void FileMetricsProvider::DeleteFileAsync(const base::FilePath& path) {
497 task_runner_->PostTask(FROM_HERE, base::Bind(DeleteFileWhenPossible, path)); 518 task_runner_->PostTask(FROM_HERE, base::Bind(DeleteFileWhenPossible, path));
498 } 519 }
499 520
500 void FileMetricsProvider::RecordSourceAsRead(SourceInfo* source) { 521 void FileMetricsProvider::RecordSourceAsRead(SourceInfo* source) {
501 DCHECK(thread_checker_.CalledOnValidThread()); 522 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
502 523
503 // Persistently record the "last seen" timestamp of the source file to 524 // Persistently record the "last seen" timestamp of the source file to
504 // ensure that the file is never read again unless it is modified again. 525 // ensure that the file is never read again unless it is modified again.
505 if (pref_service_ && !source->prefs_key.empty()) { 526 if (pref_service_ && !source->prefs_key.empty()) {
506 pref_service_->SetInt64( 527 pref_service_->SetInt64(
507 metrics::prefs::kMetricsLastSeenPrefix + source->prefs_key, 528 metrics::prefs::kMetricsLastSeenPrefix + source->prefs_key,
508 source->last_seen.ToInternalValue()); 529 source->last_seen.ToInternalValue());
509 } 530 }
510 } 531 }
511 532
512 void FileMetricsProvider::OnDidCreateMetricsLog() { 533 void FileMetricsProvider::OnDidCreateMetricsLog() {
513 DCHECK(thread_checker_.CalledOnValidThread()); 534 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
514 535
515 // Schedule a check to see if there are new metrics to load. If so, they 536 // Schedule a check to see if there are new metrics to load. If so, they
516 // will be reported during the next collection run after this one. The 537 // will be reported during the next collection run after this one. The
517 // check is run off of the worker-pool so as to not cause delays on the 538 // check is run off of the worker-pool so as to not cause delays on the
518 // main UI thread (which is currently where metric collection is done). 539 // main UI thread (which is currently where metric collection is done).
519 ScheduleSourcesCheck(); 540 ScheduleSourcesCheck();
520 541
521 // Clear any data for initial metrics since they're always reported 542 // Clear any data for initial metrics since they're always reported
522 // before the first call to this method. It couldn't be released after 543 // before the first call to this method. It couldn't be released after
523 // being reported in RecordInitialHistogramSnapshots because the data 544 // being reported in RecordInitialHistogramSnapshots because the data
524 // will continue to be used by the caller after that method returns. Once 545 // will continue to be used by the caller after that method returns. Once
525 // here, though, all actions to be done on the data have been completed. 546 // here, though, all actions to be done on the data have been completed.
526 for (const std::unique_ptr<SourceInfo>& source : sources_for_previous_run_) 547 for (const std::unique_ptr<SourceInfo>& source : sources_for_previous_run_)
527 DeleteFileAsync(source->path); 548 DeleteFileAsync(source->path);
528 sources_for_previous_run_.clear(); 549 sources_for_previous_run_.clear();
529 } 550 }
530 551
531 bool FileMetricsProvider::ProvideIndependentMetrics( 552 bool FileMetricsProvider::ProvideIndependentMetrics(
532 SystemProfileProto* system_profile_proto, 553 SystemProfileProto* system_profile_proto,
533 base::HistogramSnapshotManager* snapshot_manager) { 554 base::HistogramSnapshotManager* snapshot_manager) {
534 DCHECK(thread_checker_.CalledOnValidThread()); 555 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
535 556
536 while (!sources_with_profile_.empty()) { 557 while (!sources_with_profile_.empty()) {
537 SourceInfo* source = sources_with_profile_.begin()->get(); 558 SourceInfo* source = sources_with_profile_.begin()->get();
538 DCHECK(source->allocator); 559 DCHECK(source->allocator);
539 560
540 bool success = false; 561 bool success = false;
541 RecordEmbeddedProfileResult(EMBEDDED_PROFILE_ATTEMPT); 562 RecordEmbeddedProfileResult(EMBEDDED_PROFILE_ATTEMPT);
542 if (PersistentSystemProfile::GetSystemProfile( 563 if (PersistentSystemProfile::GetSystemProfile(
543 *source->allocator->memory_allocator(), system_profile_proto)) { 564 *source->allocator->memory_allocator(), system_profile_proto)) {
544 RecordHistogramSnapshotsFromSource(snapshot_manager, source); 565 RecordHistogramSnapshotsFromSource(snapshot_manager, source);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
577 ScheduleSourcesCheck(); 598 ScheduleSourcesCheck();
578 599
579 if (success) 600 if (success)
580 return true; 601 return true;
581 } 602 }
582 603
583 return false; 604 return false;
584 } 605 }
585 606
586 bool FileMetricsProvider::HasInitialStabilityMetrics() { 607 bool FileMetricsProvider::HasInitialStabilityMetrics() {
587 DCHECK(thread_checker_.CalledOnValidThread()); 608 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
588 609
589 // Measure the total time spent checking all sources as well as the time 610 // Measure the total time spent checking all sources as well as the time
590 // per individual file. This method is called during startup and thus blocks 611 // per individual file. This method is called during startup and thus blocks
591 // the initial showing of the browser window so it's important to know the 612 // the initial showing of the browser window so it's important to know the
592 // total delay. 613 // total delay.
593 SCOPED_UMA_HISTOGRAM_TIMER("UMA.FileMetricsProvider.InitialCheckTime.Total"); 614 SCOPED_UMA_HISTOGRAM_TIMER("UMA.FileMetricsProvider.InitialCheckTime.Total");
594 615
595 // Check all sources for previous run to see if they need to be read. 616 // Check all sources for previous run to see if they need to be read.
596 for (auto iter = sources_for_previous_run_.begin(); 617 for (auto iter = sources_for_previous_run_.begin();
597 iter != sources_for_previous_run_.end();) { 618 iter != sources_for_previous_run_.end();) {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
633 sources_for_previous_run_, temp); 654 sources_for_previous_run_, temp);
634 } 655 }
635 } 656 }
636 } 657 }
637 658
638 return !sources_for_previous_run_.empty(); 659 return !sources_for_previous_run_.empty();
639 } 660 }
640 661
641 void FileMetricsProvider::RecordInitialHistogramSnapshots( 662 void FileMetricsProvider::RecordInitialHistogramSnapshots(
642 base::HistogramSnapshotManager* snapshot_manager) { 663 base::HistogramSnapshotManager* snapshot_manager) {
643 DCHECK(thread_checker_.CalledOnValidThread()); 664 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
644 665
645 // Measure the total time spent processing all sources as well as the time 666 // Measure the total time spent processing all sources as well as the time
646 // per individual file. This method is called during startup and thus blocks 667 // per individual file. This method is called during startup and thus blocks
647 // the initial showing of the browser window so it's important to know the 668 // the initial showing of the browser window so it's important to know the
648 // total delay. 669 // total delay.
649 SCOPED_UMA_HISTOGRAM_TIMER( 670 SCOPED_UMA_HISTOGRAM_TIMER(
650 "UMA.FileMetricsProvider.InitialSnapshotTime.Total"); 671 "UMA.FileMetricsProvider.InitialSnapshotTime.Total");
651 672
652 for (const std::unique_ptr<SourceInfo>& source : sources_for_previous_run_) { 673 for (const std::unique_ptr<SourceInfo>& source : sources_for_previous_run_) {
653 SCOPED_UMA_HISTOGRAM_TIMER( 674 SCOPED_UMA_HISTOGRAM_TIMER(
654 "UMA.FileMetricsProvider.InitialSnapshotTime.File"); 675 "UMA.FileMetricsProvider.InitialSnapshotTime.File");
655 676
656 // The source needs to have an allocator attached to it in order to read 677 // The source needs to have an allocator attached to it in order to read
657 // histograms out of it. 678 // histograms out of it.
658 DCHECK(!source->read_complete); 679 DCHECK(!source->read_complete);
659 DCHECK(source->allocator); 680 DCHECK(source->allocator);
660 681
661 // Dump all histograms contained within the source to the snapshot-manager. 682 // Dump all histograms contained within the source to the snapshot-manager.
662 RecordHistogramSnapshotsFromSource(snapshot_manager, source.get()); 683 RecordHistogramSnapshotsFromSource(snapshot_manager, source.get());
663 684
664 // Update the last-seen time so it isn't read again unless it changes. 685 // Update the last-seen time so it isn't read again unless it changes.
665 RecordSourceAsRead(source.get()); 686 RecordSourceAsRead(source.get());
666 } 687 }
667 } 688 }
668 689
669 void FileMetricsProvider::MergeHistogramDeltas() { 690 void FileMetricsProvider::MergeHistogramDeltas() {
670 DCHECK(thread_checker_.CalledOnValidThread()); 691 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
671 692
672 // Measure the total time spent processing all sources as well as the time 693 // Measure the total time spent processing all sources as well as the time
673 // per individual file. This method is called on the UI thread so it's 694 // per individual file. This method is called on the UI thread so it's
674 // important to know how much total "jank" may be introduced. 695 // important to know how much total "jank" may be introduced.
675 SCOPED_UMA_HISTOGRAM_TIMER("UMA.FileMetricsProvider.SnapshotTime.Total"); 696 SCOPED_UMA_HISTOGRAM_TIMER("UMA.FileMetricsProvider.SnapshotTime.Total");
676 697
677 for (std::unique_ptr<SourceInfo>& source : sources_mapped_) { 698 for (std::unique_ptr<SourceInfo>& source : sources_mapped_) {
678 SCOPED_UMA_HISTOGRAM_TIMER("UMA.FileMetricsProvider.SnapshotTime.File"); 699 SCOPED_UMA_HISTOGRAM_TIMER("UMA.FileMetricsProvider.SnapshotTime.File");
679 MergeHistogramDeltasFromSource(source.get()); 700 MergeHistogramDeltasFromSource(source.get());
680 } 701 }
681 } 702 }
682 703
683 } // namespace metrics 704 } // namespace metrics
OLDNEW
« no previous file with comments | « components/metrics/file_metrics_provider.h ('k') | components/metrics/file_metrics_provider_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698