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

Side by Side Diff: chrome/browser/metrics/subprocess_metrics_provider.cc

Issue 1880803003: Display histograms from subprocesses in chrome://histograms (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebased Created 4 years, 8 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 "chrome/browser/metrics/subprocess_metrics_provider.h" 5 #include "chrome/browser/metrics/subprocess_metrics_provider.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/memory/ptr_util.h" 8 #include "base/memory/ptr_util.h"
9 #include "base/metrics/histogram_base.h" 9 #include "base/metrics/histogram_base.h"
10 #include "base/metrics/histogram_flattener.h"
10 #include "base/metrics/histogram_macros.h" 11 #include "base/metrics/histogram_macros.h"
12 #include "base/metrics/histogram_samples.h"
11 #include "base/metrics/persistent_histogram_allocator.h" 13 #include "base/metrics/persistent_histogram_allocator.h"
12 #include "base/metrics/persistent_memory_allocator.h" 14 #include "base/metrics/persistent_memory_allocator.h"
15 #include "base/metrics/statistics_recorder.h"
Ilya Sherman 2016/04/25 20:53:14 nit: Already included in the header file.
bcwhite 2016/04/26 19:51:54 Done.
16 #include "base/stl_util.h"
17 #include "base/strings/stringprintf.h"
13 #include "components/metrics/metrics_service.h" 18 #include "components/metrics/metrics_service.h"
14 #include "content/public/browser/notification_service.h" 19 #include "content/public/browser/notification_service.h"
15 #include "content/public/browser/notification_types.h" 20 #include "content/public/browser/notification_types.h"
16 #include "content/public/browser/render_process_host.h" 21 #include "content/public/browser/render_process_host.h"
17 22
23 namespace {
24
25 class HistogramOutputFlattener : public base::HistogramFlattener {
26 public:
27 explicit HistogramOutputFlattener(
28 base::StatisticsRecorder::MetricsDisplayer::DisplayType format)
29 : format_(format) {}
30
31 void RecordDelta(const base::HistogramBase& histogram,
32 const base::HistogramSamples& snapshot) override {
33 std::string& graph = graphs_[histogram.histogram_name()];
Ilya Sherman 2016/04/25 20:53:14 Hmm. Suppose that multiple subprocesses recorded
bcwhite 2016/04/26 19:51:54 The HistogramSnapshotManager merges histograms wit
Ilya Sherman 2016/04/26 20:21:58 I see. It's kind of wacky that |histogram| mostly
bcwhite 2016/04/26 21:03:29 Actually, the merging in the HSM is new. It used
Ilya Sherman 2016/04/27 20:17:42 Hmm. Might be worth discussing when we meet tomor
bcwhite 2016/05/02 13:20:34 There's no easy way to create a new histogram base
Ilya Sherman 2016/05/04 06:00:26 Sorry, I'm still confused here. Are you saying th
bcwhite 2016/05/04 15:38:08 It wouldn't be too difficult. A virtual Duplicate
34 switch (format_) {
35 case base::StatisticsRecorder::MetricsDisplayer::DISPLAY_TEXT_PLAIN:
36 histogram.WriteAscii(&snapshot, &graph);
37 graph.append("\n");
38 break;
39 case base::StatisticsRecorder::MetricsDisplayer::DISPLAY_TEXT_HTML:
40 histogram.WriteHTMLGraph(&snapshot, &graph);
Ilya Sherman 2016/04/25 20:53:15 So, why do you need to pass the snapshot here? Ho
bcwhite 2016/04/26 19:51:54 The snapshot passed in is merged from all those wi
41 graph.append("<br><hr><br>");
42 break;
43 }
44 }
45
46 void InconsistencyDetected(
47 base::HistogramBase::Inconsistency problem) override {}
48
Ilya Sherman 2016/04/25 20:53:15 nit: Please remove this blank line.
bcwhite 2016/04/26 19:51:54 Done.
49 void UniqueInconsistencyDetected(
50 base::HistogramBase::Inconsistency problem) override {}
51
Ilya Sherman 2016/04/25 20:53:15 nit: Please remove this blank line.
bcwhite 2016/04/26 19:51:54 Done.
52 void InconsistencyDetectedInLoggedCount(int amount) override {}
53
54 void WriteOutput(std::string* output) {
55 for (auto& name_and_graph : graphs_) {
Ilya Sherman 2016/04/25 20:53:15 nit: const auto&?
bcwhite 2016/04/26 19:51:53 Done.
56 *output += name_and_graph.second;
57 }
58 }
59
60 private:
61 base::StatisticsRecorder::MetricsDisplayer::DisplayType format_;
Ilya Sherman 2016/04/25 20:53:15 nit: const?
bcwhite 2016/04/26 19:51:54 Done.
62
63 // Map of histogram names to histogram output. This is used to have the
64 // display appear in alphabetical order.
65 std::map<const std::string, std::string> graphs_;
66
67 DISALLOW_COPY_AND_ASSIGN(HistogramOutputFlattener);
68 };
69
70 } // namespace
71
18 SubprocessMetricsProvider::SubprocessMetricsProvider() 72 SubprocessMetricsProvider::SubprocessMetricsProvider()
19 : scoped_observer_(this) { 73 : scoped_observer_(this) {
74 base::StatisticsRecorder::RegisterMetricsDisplayer(this);
20 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED, 75 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CREATED,
21 content::NotificationService::AllBrowserContextsAndSources()); 76 content::NotificationService::AllBrowserContextsAndSources());
22 } 77 }
23 78
24 SubprocessMetricsProvider::~SubprocessMetricsProvider() {} 79 SubprocessMetricsProvider::~SubprocessMetricsProvider() {
80 base::StatisticsRecorder::DeregisterMetricsDisplayer(this);
81 }
25 82
26 void SubprocessMetricsProvider::RegisterSubprocessAllocator( 83 void SubprocessMetricsProvider::RegisterSubprocessAllocator(
27 int id, 84 int id,
28 std::unique_ptr<base::PersistentHistogramAllocator> allocator) { 85 std::unique_ptr<base::PersistentHistogramAllocator> allocator) {
29 DCHECK(thread_checker_.CalledOnValidThread()); 86 DCHECK(!ContainsKey(allocators_by_id_, id));
30 DCHECK(!allocators_by_id_.Lookup(id));
31 87
32 // Map is "MapOwnPointer" so transfer ownership to it. 88 base::AutoLock lock(lock_);
33 allocators_by_id_.AddWithID(allocator.release(), id); 89
90 // Insert has to be done with a pair in order to support "move" semantics.
91 allocators_by_id_.insert(std::make_pair(id, std::move(allocator)));
34 } 92 }
35 93
36 void SubprocessMetricsProvider::DeregisterSubprocessAllocator(int id) { 94 void SubprocessMetricsProvider::DeregisterSubprocessAllocator(int id) {
37 DCHECK(thread_checker_.CalledOnValidThread()); 95 base::AutoLock lock(lock_);
38 96
39 if (!allocators_by_id_.Lookup(id)) 97 auto existing = allocators_by_id_.find(id);
98 if (existing == allocators_by_id_.end())
40 return; 99 return;
41 100
42 // Extract the matching allocator from the list of active ones.
43 std::unique_ptr<base::PersistentHistogramAllocator> allocator(
44 allocators_by_id_.Replace(id, nullptr));
45 allocators_by_id_.Remove(id);
46 DCHECK(allocator);
47
48 // If metrics recording is enabled, transfer the allocator to the "release" 101 // If metrics recording is enabled, transfer the allocator to the "release"
49 // list. The allocator will continue to live (and keep the associated shared 102 // list. The allocator will continue to live (and keep the associated shared
50 // memory alive) until the next upload after which it will be released. 103 // memory alive) until the next upload after which it will be released.
51 // Otherwise, the allocator and its memory will be released when the 104 // Otherwise, the allocator and its memory will be released when the
52 // unique_ptr goes out of scope at the end of this method. 105 // unique_ptr goes out of scope at the end of this method.
53 if (metrics_recording_enabled_) 106 if (metrics_recording_enabled_)
54 allocators_for_exited_processes_.push_back(std::move(allocator)); 107 allocators_for_exited_processes_.push_back(std::move(existing->second));
108
109 allocators_by_id_.erase(existing);
55 } 110 }
56 111
57 void SubprocessMetricsProvider::OnDidCreateMetricsLog() { 112 void SubprocessMetricsProvider::OnDidCreateMetricsLog() {
58 DCHECK(thread_checker_.CalledOnValidThread());
59
60 // The previous reporting cycle is complete and the data used to create it 113 // The previous reporting cycle is complete and the data used to create it
61 // will never be needed again. Allocators for exited processes can finally 114 // will never be needed again. Allocators for exited processes can finally
62 // be released. 115 // be released.
116 base::AutoLock lock(lock_);
63 allocators_to_release_.clear(); 117 allocators_to_release_.clear();
64 } 118 }
65 119
66 void SubprocessMetricsProvider::OnRecordingEnabled() { 120 void SubprocessMetricsProvider::OnRecordingEnabled() {
67 DCHECK(thread_checker_.CalledOnValidThread());
68
69 metrics_recording_enabled_ = true; 121 metrics_recording_enabled_ = true;
70 } 122 }
71 123
72 void SubprocessMetricsProvider::OnRecordingDisabled() { 124 void SubprocessMetricsProvider::OnRecordingDisabled() {
73 DCHECK(thread_checker_.CalledOnValidThread()); 125 metrics_recording_enabled_ = false;
74 126
75 metrics_recording_enabled_ = false; 127 base::AutoLock lock(lock_);
76 allocators_for_exited_processes_.clear(); 128 allocators_for_exited_processes_.clear();
77 allocators_to_release_.clear(); 129 allocators_to_release_.clear();
78 } 130 }
79 131
80 void SubprocessMetricsProvider::RecordHistogramSnapshotsFromAllocator( 132 void SubprocessMetricsProvider::RecordHistogramSnapshotsFromAllocator(
81 base::HistogramSnapshotManager* snapshot_manager, 133 base::HistogramSnapshotManager* snapshot_manager,
134 const std::string& query,
135 bool absolute,
Ilya Sherman 2016/04/25 20:53:15 nit: The semantics of |absolute| are pretty hard t
bcwhite 2016/04/26 19:51:53 I didn't bother with an enum because it's all priv
Ilya Sherman 2016/04/26 20:21:58 I think you're overestimating the amount of duplic
bcwhite 2016/04/26 21:03:29 Acknowledged.
82 int id, 136 int id,
83 base::PersistentHistogramAllocator* allocator) { 137 base::PersistentHistogramAllocator* allocator) {
84 DCHECK(allocator); 138 DCHECK(allocator);
85 139
86 int histogram_count = 0; 140 int histogram_count = 0;
87 base::PersistentHistogramAllocator::Iterator hist_iter(allocator); 141 base::PersistentHistogramAllocator::Iterator hist_iter(allocator);
88 while (true) { 142 while (true) {
89 std::unique_ptr<base::HistogramBase> histogram = hist_iter.GetNext(); 143 std::unique_ptr<base::HistogramBase> histogram = hist_iter.GetNext();
90 if (!histogram) 144 if (!histogram)
91 break; 145 break;
92 snapshot_manager->PrepareDeltaTakingOwnership(std::move(histogram)); 146 if (histogram->histogram_name().find(query) == std::string::npos)
147 continue;
148
149 if (absolute)
150 snapshot_manager->PrepareAbsoluteTakingOwnership(std::move(histogram));
151 else
152 snapshot_manager->PrepareDeltaTakingOwnership(std::move(histogram));
93 ++histogram_count; 153 ++histogram_count;
94 } 154 }
95 155
96 DVLOG(1) << "Reported " << histogram_count << " histograms from subprocess #" 156 DVLOG(1) << "Reported " << histogram_count << " histograms from subprocess #"
97 << id; 157 << id;
98 } 158 }
99 159
100 void SubprocessMetricsProvider::RecordHistogramSnapshots( 160 void SubprocessMetricsProvider::RecordHistogramSnapshots(
101 base::HistogramSnapshotManager* snapshot_manager) { 161 base::HistogramSnapshotManager* snapshot_manager) {
102 DCHECK(thread_checker_.CalledOnValidThread()); 162 base::AutoLock lock(lock_);
103 163
104 for (AllocatorByIdMap::iterator iter(&allocators_by_id_); !iter.IsAtEnd(); 164 for (auto& id_and_allocator : allocators_by_id_) {
105 iter.Advance()) { 165 RecordHistogramSnapshotsFromAllocator(snapshot_manager, std::string(),
106 RecordHistogramSnapshotsFromAllocator( 166 false, id_and_allocator.first,
107 snapshot_manager, iter.GetCurrentKey(), iter.GetCurrentValue()); 167 id_and_allocator.second.get());
108 } 168 }
109 169
110 for (auto& allocator : allocators_for_exited_processes_) 170 for (auto& allocator : allocators_for_exited_processes_) {
111 RecordHistogramSnapshotsFromAllocator(snapshot_manager, 0, allocator.get()); 171 RecordHistogramSnapshotsFromAllocator(snapshot_manager, std::string(),
172 false, 0, allocator.get());
173 }
112 174
113 UMA_HISTOGRAM_COUNTS_100( 175 UMA_HISTOGRAM_COUNTS_100(
114 "UMA.SubprocessMetricsProvider.SubprocessCount", 176 "UMA.SubprocessMetricsProvider.SubprocessCount",
115 allocators_by_id_.size() + allocators_for_exited_processes_.size()); 177 allocators_by_id_.size() + allocators_for_exited_processes_.size());
116 178
117 // Move allocators for exited processes (which just had final reporting done 179 // Move allocators for exited processes (which just had final reporting done
118 // for them) to the queue for being released. The actual release is delayed 180 // for them) to the queue for being released. The actual release is delayed
119 // until after reporting is complete so as to not destruct objects that may 181 // until after reporting is complete so as to not destruct objects that may
120 // still be needed. 182 // still be needed.
121 DCHECK(allocators_to_release_.empty()); 183 DCHECK(allocators_to_release_.empty());
122 allocators_to_release_.swap(allocators_for_exited_processes_); 184 allocators_to_release_.swap(allocators_for_exited_processes_);
123 } 185 }
124 186
187 void SubprocessMetricsProvider::WriteTitleString(std::string* output) {
188 base::AutoLock lock(lock_);
189 base::StringAppendF(
190 output,
191 "Histograms belonging to %d currently active and %d recently exited "
192 "sub-processes (merged). Data from older processes are not included.",
193 static_cast<int>(allocators_by_id_.size()),
194 static_cast<int>(allocators_to_release_.size()));
195 if (!metrics_recording_enabled_) {
196 output->append(" UMA reporting is not currently enabled so data from"
197 " processes that have exited are not kept at all.");
198 }
199 }
200
201 void SubprocessMetricsProvider::WriteGraphs(const std::string& query,
202 DisplayType format,
203 std::string* output) {
204 HistogramOutputFlattener flattener(format);
205 base::HistogramSnapshotManager snapshot_manager(&flattener);
206 snapshot_manager.StartDeltas();
207
208 // Only the access to the allocators needs to be locked. Once their data
209 // has been recorded, the lock can be released for the processing of the
210 // merged data.
211 {
212 base::AutoLock lock(lock_);
213
214 for (auto& id_and_allocator : allocators_by_id_) {
Ilya Sherman 2016/04/25 20:53:15 nit: const auto?
bcwhite 2016/04/26 19:51:54 Done.
215 RecordHistogramSnapshotsFromAllocator(&snapshot_manager, query, true,
216 id_and_allocator.first,
217 id_and_allocator.second.get());
218 }
219
220 for (auto& allocator : allocators_to_release_) {
Ilya Sherman 2016/04/25 20:53:15 nit: const auto?
bcwhite 2016/04/26 19:51:54 Done.
221 RecordHistogramSnapshotsFromAllocator(&snapshot_manager, query, true,
222 0, allocator.get());
223 }
224 }
225
226 snapshot_manager.FinishDeltas();
227 flattener.WriteOutput(output);
228 }
229
125 void SubprocessMetricsProvider::Observe( 230 void SubprocessMetricsProvider::Observe(
126 int type, 231 int type,
127 const content::NotificationSource& source, 232 const content::NotificationSource& source,
128 const content::NotificationDetails& details) { 233 const content::NotificationDetails& details) {
129 DCHECK(thread_checker_.CalledOnValidThread());
130 DCHECK_EQ(content::NOTIFICATION_RENDERER_PROCESS_CREATED, type); 234 DCHECK_EQ(content::NOTIFICATION_RENDERER_PROCESS_CREATED, type);
131 235
236 base::AutoLock lock(lock_);
237
132 content::RenderProcessHost* host = 238 content::RenderProcessHost* host =
133 content::Source<content::RenderProcessHost>(source).ptr(); 239 content::Source<content::RenderProcessHost>(source).ptr();
134 240
135 // Sometimes, the same host will cause multiple notifications in tests so 241 // Sometimes, the same host will cause multiple notifications in tests so
136 // could possibly do the same in a release build. 242 // could possibly do the same in a release build.
137 if (!scoped_observer_.IsObserving(host)) 243 if (!scoped_observer_.IsObserving(host))
138 scoped_observer_.Add(host); 244 scoped_observer_.Add(host);
139 } 245 }
140 246
141 void SubprocessMetricsProvider::RenderProcessReady( 247 void SubprocessMetricsProvider::RenderProcessReady(
142 content::RenderProcessHost* host) { 248 content::RenderProcessHost* host) {
143 DCHECK(thread_checker_.CalledOnValidThread());
144
145 // If the render-process-host passed a persistent-memory-allocator to the 249 // If the render-process-host passed a persistent-memory-allocator to the
146 // renderer process, extract it and register it here. 250 // renderer process, extract it and register it here.
147 std::unique_ptr<base::SharedPersistentMemoryAllocator> allocator = 251 std::unique_ptr<base::SharedPersistentMemoryAllocator> allocator =
148 host->TakeMetricsAllocator(); 252 host->TakeMetricsAllocator();
149 if (allocator) { 253 if (allocator) {
150 RegisterSubprocessAllocator( 254 RegisterSubprocessAllocator(
151 host->GetID(), 255 host->GetID(),
152 WrapUnique(new base::PersistentHistogramAllocator( 256 WrapUnique(new base::PersistentHistogramAllocator(
153 std::move(allocator)))); 257 std::move(allocator))));
154 } 258 }
155 } 259 }
156 260
157 void SubprocessMetricsProvider::RenderProcessExited( 261 void SubprocessMetricsProvider::RenderProcessExited(
158 content::RenderProcessHost* host, 262 content::RenderProcessHost* host,
159 base::TerminationStatus status, 263 base::TerminationStatus status,
160 int exit_code) { 264 int exit_code) {
161 DCHECK(thread_checker_.CalledOnValidThread());
162
163 DeregisterSubprocessAllocator(host->GetID()); 265 DeregisterSubprocessAllocator(host->GetID());
164 } 266 }
165 267
166 void SubprocessMetricsProvider::RenderProcessHostDestroyed( 268 void SubprocessMetricsProvider::RenderProcessHostDestroyed(
167 content::RenderProcessHost* host) { 269 content::RenderProcessHost* host) {
168 DCHECK(thread_checker_.CalledOnValidThread());
169
170 // It's possible for a Renderer to terminate without RenderProcessExited 270 // It's possible for a Renderer to terminate without RenderProcessExited
171 // (above) being called so it's necessary to de-register also upon the 271 // (above) being called so it's necessary to de-register also upon the
172 // destruction of the host. If both get called, no harm is done. 272 // destruction of the host. If both get called, no harm is done.
273 DeregisterSubprocessAllocator(host->GetID());
173 274
174 DeregisterSubprocessAllocator(host->GetID()); 275 base::AutoLock lock(lock_);
175 scoped_observer_.Remove(host); 276 scoped_observer_.Remove(host);
176 } 277 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698