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