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

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

Powered by Google App Engine
This is Rietveld 408576698