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

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

Powered by Google App Engine
This is Rietveld 408576698