1 // Copyright (c) 2012 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 "content/browser/histogram_controller.h"
6
7 #include "base/bind.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/process/process_handle.h"
10 #include "content/browser/histogram_subscriber.h"
11 #include "content/common/histogram_fetcher.mojom.h"
12 #include "content/public/browser/browser_child_process_host_iterator.h"
13 #include "content/public/browser/browser_task_traits.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/child_process_data.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/common/child_process_host.h"
18 #include "content/public/common/process_type.h"
19 #include "mojo/public/cpp/bindings/callback_helpers.h"
20
21 namespace content {
22
GetInstance()23 HistogramController* HistogramController::GetInstance() {
24 return base::Singleton<HistogramController, base::LeakySingletonTraits<
25 HistogramController>>::get();
26 }
27
HistogramController()28 HistogramController::HistogramController() : subscriber_(nullptr) {}
29
~HistogramController()30 HistogramController::~HistogramController() {
31 }
32
OnPendingProcesses(int sequence_number,int pending_processes,bool end)33 void HistogramController::OnPendingProcesses(int sequence_number,
34 int pending_processes,
35 bool end) {
36 DCHECK_CURRENTLY_ON(BrowserThread::UI);
37 if (subscriber_)
38 subscriber_->OnPendingProcesses(sequence_number, pending_processes, end);
39 }
40
OnHistogramDataCollected(int sequence_number,const std::vector<std::string> & pickled_histograms)41 void HistogramController::OnHistogramDataCollected(
42 int sequence_number,
43 const std::vector<std::string>& pickled_histograms) {
44 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
45 GetUIThreadTaskRunner({})->PostTask(
46 FROM_HERE,
47 base::BindOnce(&HistogramController::OnHistogramDataCollected,
48 base::Unretained(this), sequence_number,
49 pickled_histograms));
50 return;
51 }
52 DCHECK_CURRENTLY_ON(BrowserThread::UI);
53 if (subscriber_) {
54 subscriber_->OnHistogramDataCollected(sequence_number,
55 pickled_histograms);
56 }
57 }
58
Register(HistogramSubscriber * subscriber)59 void HistogramController::Register(HistogramSubscriber* subscriber) {
60 DCHECK_CURRENTLY_ON(BrowserThread::UI);
61 DCHECK(!subscriber_);
62 subscriber_ = subscriber;
63 }
64
Unregister(const HistogramSubscriber * subscriber)65 void HistogramController::Unregister(
66 const HistogramSubscriber* subscriber) {
67 DCHECK_EQ(subscriber_, subscriber);
68 subscriber_ = nullptr;
69 }
70
71 template <class T>
NotifyChildDied(T * host)72 void HistogramController::NotifyChildDied(T* host) {
73 RemoveChildHistogramFetcherInterface(host);
74 }
75
76 template void HistogramController::NotifyChildDied(RenderProcessHost* host);
77
78 template <>
79 HistogramController::ChildHistogramFetcherMap<ChildProcessHost>&
GetChildHistogramFetcherMap()80 HistogramController::GetChildHistogramFetcherMap() {
81 return child_histogram_fetchers_;
82 }
83
84 template <>
85 HistogramController::ChildHistogramFetcherMap<RenderProcessHost>&
GetChildHistogramFetcherMap()86 HistogramController::GetChildHistogramFetcherMap() {
87 return renderer_histogram_fetchers_;
88 }
89
90 template void HistogramController::SetHistogramMemory(
91 ChildProcessHost* host,
92 base::WritableSharedMemoryRegion shared_region);
93
94 template void HistogramController::SetHistogramMemory(
95 RenderProcessHost* host,
96 base::WritableSharedMemoryRegion shared_region);
97
98 template <class T>
SetHistogramMemory(T * host,base::WritableSharedMemoryRegion shared_region)99 void HistogramController::SetHistogramMemory(
100 T* host,
101 base::WritableSharedMemoryRegion shared_region) {
102 mojo::Remote<content::mojom::ChildHistogramFetcherFactory> factory;
103 host->BindReceiver(factory.BindNewPipeAndPassReceiver());
104
105 mojo::Remote<content::mojom::ChildHistogramFetcher> fetcher;
106 factory->CreateFetcher(std::move(shared_region),
107 fetcher.BindNewPipeAndPassReceiver());
108 InsertChildHistogramFetcherInterface(host, std::move(fetcher));
109 }
110
111 template <class T>
InsertChildHistogramFetcherInterface(T * host,mojo::Remote<content::mojom::ChildHistogramFetcher> child_histogram_fetcher)112 void HistogramController::InsertChildHistogramFetcherInterface(
113 T* host,
114 mojo::Remote<content::mojom::ChildHistogramFetcher>
115 child_histogram_fetcher) {
116 // Broken pipe means remove this from the map. The map size is a proxy for
117 // the number of known processes
118 child_histogram_fetcher.set_disconnect_handler(base::BindOnce(
119 &HistogramController::RemoveChildHistogramFetcherInterface<T>,
120 base::Unretained(this), base::Unretained(host)));
121 GetChildHistogramFetcherMap<T>()[host] = std::move(child_histogram_fetcher);
122 }
123
124 template <class T>
125 content::mojom::ChildHistogramFetcher*
GetChildHistogramFetcherInterface(T * host)126 HistogramController::GetChildHistogramFetcherInterface(T* host) {
127 auto it = GetChildHistogramFetcherMap<T>().find(host);
128 if (it != GetChildHistogramFetcherMap<T>().end()) {
129 return (it->second).get();
130 }
131 return nullptr;
132 }
133
134 template <class T>
RemoveChildHistogramFetcherInterface(T * host)135 void HistogramController::RemoveChildHistogramFetcherInterface(T* host) {
136 GetChildHistogramFetcherMap<T>().erase(host);
137 }
138
GetHistogramDataFromChildProcesses(int sequence_number)139 void HistogramController::GetHistogramDataFromChildProcesses(
140 int sequence_number) {
141 DCHECK_CURRENTLY_ON(BrowserThread::IO);
142
143 int pending_processes = 0;
144 for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
145 const ChildProcessData& data = iter.GetData();
146
147 // Only get histograms from content process types; skip "embedder" process
148 // types.
149 if (data.process_type >= PROCESS_TYPE_CONTENT_END)
150 continue;
151
152 // In some cases, there may be no child process of the given type (for
153 // example, the GPU process may not exist and there may instead just be a
154 // GPU thread in the browser process). If that's the case, then the process
155 // will be invalid and we shouldn't ask it for data.
156 if (!data.GetProcess().IsValid())
157 continue;
158
159 if (auto* child_histogram_fetcher =
160 GetChildHistogramFetcherInterface(iter.GetHost())) {
161 child_histogram_fetcher->GetChildNonPersistentHistogramData(
162 mojo::WrapCallbackWithDefaultInvokeIfNotRun(
163 base::BindOnce(&HistogramController::OnHistogramDataCollected,
164 base::Unretained(this), sequence_number),
165 std::vector<std::string>()));
166 ++pending_processes;
167 }
168 }
169 GetUIThreadTaskRunner({})->PostTask(
170 FROM_HERE, base::BindOnce(&HistogramController::OnPendingProcesses,
171 base::Unretained(this), sequence_number,
172 pending_processes, true));
173 }
174
GetHistogramData(int sequence_number)175 void HistogramController::GetHistogramData(int sequence_number) {
176 DCHECK_CURRENTLY_ON(BrowserThread::UI);
177
178 int pending_processes = 0;
179 for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
180 !it.IsAtEnd() && it.GetCurrentValue()->IsReady(); it.Advance()) {
181 if (auto* child_histogram_fetcher =
182 GetChildHistogramFetcherInterface(it.GetCurrentValue())) {
183 child_histogram_fetcher->GetChildNonPersistentHistogramData(
184 mojo::WrapCallbackWithDefaultInvokeIfNotRun(
185 base::BindOnce(&HistogramController::OnHistogramDataCollected,
186 base::Unretained(this), sequence_number),
187 std::vector<std::string>()));
188 ++pending_processes;
189 }
190 }
191 OnPendingProcesses(sequence_number, pending_processes, false);
192
193 GetIOThreadTaskRunner({})->PostTask(
194 FROM_HERE,
195 base::BindOnce(&HistogramController::GetHistogramDataFromChildProcesses,
196 base::Unretained(this), sequence_number));
197 }
198
199 } // namespace content
200