blob: 833d972017a4c2f8cceeb1dfa479d62a146fc4ce [file] [log] [blame]
[email protected]83ab4a282012-07-12 18:19:451// 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_synchronizer.h"
6
fdoray0c755de2016-10-19 15:28:447#include <utility>
8
[email protected]83ab4a282012-07-12 18:19:459#include "base/bind.h"
10#include "base/lazy_instance.h"
skyostil95082a62015-06-05 19:53:0711#include "base/location.h"
[email protected]83ab4a282012-07-12 18:19:4512#include "base/logging.h"
[email protected]6afa90f2013-10-23 01:16:0413#include "base/metrics/histogram_delta_serialization.h"
asvitkine30330812016-08-30 04:01:0814#include "base/metrics/histogram_macros.h"
[email protected]c50c21d2013-01-11 21:52:4415#include "base/pickle.h"
skyostil95082a62015-06-05 19:53:0716#include "base/single_thread_task_runner.h"
Eric Seckler8652dcd52018-09-20 10:42:2817#include "base/task/post_task.h"
[email protected]83ab4a282012-07-12 18:19:4518#include "base/threading/thread.h"
19#include "base/threading/thread_restrictions.h"
20#include "content/browser/histogram_controller.h"
Eric Seckler8652dcd52018-09-20 10:42:2821#include "content/public/browser/browser_task_traits.h"
[email protected]83ab4a282012-07-12 18:19:4522#include "content/public/browser/browser_thread.h"
23#include "content/public/browser/histogram_fetcher.h"
24#include "content/public/common/content_constants.h"
25
Daniel Bratellb8d076c2018-01-03 10:41:4726namespace content {
27
[email protected]83ab4a282012-07-12 18:19:4528using base::Time;
29using base::TimeDelta;
30using base::TimeTicks;
31
32namespace {
33
34// Negative numbers are never used as sequence numbers. We explicitly pick a
35// negative number that is "so negative" that even when we add one (as is done
36// when we generated the next sequence number) that it will still be negative.
37// We have code that handles wrapping around on an overflow into negative
38// territory.
39static const int kNeverUsableSequenceNumber = -2;
40
41} // anonymous namespace
42
[email protected]83ab4a282012-07-12 18:19:4543// The "RequestContext" structure describes an individual request received from
44// the UI. All methods are accessible on UI thread.
45class HistogramSynchronizer::RequestContext {
46 public:
47 // A map from sequence_number_ to the actual RequestContexts.
48 typedef std::map<int, RequestContext*> RequestContextMap;
49
Makoto Shimazu51176e62019-10-10 14:43:1750 RequestContext(base::OnceClosure callback, int sequence_number)
51 : callback_(std::move(callback)),
[email protected]83ab4a282012-07-12 18:19:4552 sequence_number_(sequence_number),
53 received_process_group_count_(0),
Makoto Shimazu51176e62019-10-10 14:43:1754 processes_pending_(0) {}
[email protected]83ab4a282012-07-12 18:19:4555 ~RequestContext() {}
56
57 void SetReceivedProcessGroupCount(bool done) {
mostynb4c27d042015-03-18 21:47:4758 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4559 received_process_group_count_ = done;
60 }
61
62 // Methods for book keeping of processes_pending_.
63 void AddProcessesPending(int processes_pending) {
mostynb4c27d042015-03-18 21:47:4764 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4565 processes_pending_ += processes_pending;
66 }
67
68 void DecrementProcessesPending() {
mostynb4c27d042015-03-18 21:47:4769 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4570 --processes_pending_;
71 }
72
73 // Records that we are waiting for one less histogram data from a process for
74 // the given sequence number. If |received_process_group_count_| and
75 // |processes_pending_| are zero, then delete the current object by calling
76 // Unregister.
77 void DeleteIfAllDone() {
mostynb4c27d042015-03-18 21:47:4778 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4579
80 if (processes_pending_ <= 0 && received_process_group_count_)
81 RequestContext::Unregister(sequence_number_);
82 }
83
84 // Register |callback| in |outstanding_requests_| map for the given
85 // |sequence_number|.
Makoto Shimazu51176e62019-10-10 14:43:1786 static void Register(base::OnceClosure callback, int sequence_number) {
mostynb4c27d042015-03-18 21:47:4787 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4588
Makoto Shimazu51176e62019-10-10 14:43:1789 RequestContext* request =
90 new RequestContext(std::move(callback), sequence_number);
[email protected]83ab4a282012-07-12 18:19:4591 outstanding_requests_.Get()[sequence_number] = request;
92 }
93
94 // Find the |RequestContext| in |outstanding_requests_| map for the given
95 // |sequence_number|.
96 static RequestContext* GetRequestContext(int sequence_number) {
mostynb4c27d042015-03-18 21:47:4797 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:4598
jdoerrie55ec69d2018-10-08 13:34:4699 auto it = outstanding_requests_.Get().find(sequence_number);
[email protected]83ab4a282012-07-12 18:19:45100 if (it == outstanding_requests_.Get().end())
Ivan Kotenkov2c0d2bb32017-11-01 15:41:28101 return nullptr;
[email protected]83ab4a282012-07-12 18:19:45102
103 RequestContext* request = it->second;
104 DCHECK_EQ(sequence_number, request->sequence_number_);
105 return request;
106 }
107
108 // Delete the entry for the given |sequence_number| from
109 // |outstanding_requests_| map. This method is called when all changes have
110 // been acquired, or when the wait time expires (whichever is sooner).
111 static void Unregister(int sequence_number) {
mostynb4c27d042015-03-18 21:47:47112 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:45113
jdoerrie55ec69d2018-10-08 13:34:46114 auto it = outstanding_requests_.Get().find(sequence_number);
[email protected]83ab4a282012-07-12 18:19:45115 if (it == outstanding_requests_.Get().end())
116 return;
117
118 RequestContext* request = it->second;
119 DCHECK_EQ(sequence_number, request->sequence_number_);
120 bool received_process_group_count = request->received_process_group_count_;
121 int unresponsive_processes = request->processes_pending_;
122
Makoto Shimazu51176e62019-10-10 14:43:17123 std::move(request->callback_).Run();
[email protected]83ab4a282012-07-12 18:19:45124
125 delete request;
126 outstanding_requests_.Get().erase(it);
127
128 UMA_HISTOGRAM_BOOLEAN("Histogram.ReceivedProcessGroupCount",
129 received_process_group_count);
Steven Holte95922222018-09-14 20:06:23130 UMA_HISTOGRAM_COUNTS_1M("Histogram.PendingProcessNotResponding",
131 unresponsive_processes);
[email protected]83ab4a282012-07-12 18:19:45132 }
133
134 // Delete all the entries in |outstanding_requests_| map.
135 static void OnShutdown() {
136 // Just in case we have any pending tasks, clear them out.
137 while (!outstanding_requests_.Get().empty()) {
jdoerrie55ec69d2018-10-08 13:34:46138 auto it = outstanding_requests_.Get().begin();
[email protected]83ab4a282012-07-12 18:19:45139 delete it->second;
140 outstanding_requests_.Get().erase(it);
141 }
142 }
143
144 // Requests are made to asynchronously send data to the |callback_|.
Makoto Shimazu51176e62019-10-10 14:43:17145 base::OnceClosure callback_;
[email protected]83ab4a282012-07-12 18:19:45146
147 // The sequence number used by the most recent update request to contact all
148 // processes.
149 int sequence_number_;
150
151 // Indicates if we have received all pending processes count.
152 bool received_process_group_count_;
153
154 // The number of pending processes (all renderer processes and browser child
155 // processes) that have not yet responded to requests.
156 int processes_pending_;
157
158 // Map of all outstanding RequestContexts, from sequence_number_ to
159 // RequestContext.
160 static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
161};
162
163// static
164base::LazyInstance
165 <HistogramSynchronizer::RequestContext::RequestContextMap>::Leaky
166 HistogramSynchronizer::RequestContext::outstanding_requests_ =
167 LAZY_INSTANCE_INITIALIZER;
168
169HistogramSynchronizer::HistogramSynchronizer()
170 : lock_(),
[email protected]83ab4a282012-07-12 18:19:45171 last_used_sequence_number_(kNeverUsableSequenceNumber),
172 async_sequence_number_(kNeverUsableSequenceNumber) {
[email protected]46488322012-10-30 03:22:20173 HistogramController::GetInstance()->Register(this);
[email protected]83ab4a282012-07-12 18:19:45174}
175
176HistogramSynchronizer::~HistogramSynchronizer() {
177 RequestContext::OnShutdown();
178
179 // Just in case we have any pending tasks, clear them out.
fdoray0c755de2016-10-19 15:28:44180 SetTaskRunnerAndCallback(nullptr, base::Closure());
[email protected]83ab4a282012-07-12 18:19:45181}
182
183HistogramSynchronizer* HistogramSynchronizer::GetInstance() {
olli.raula36aa8be2015-09-10 11:14:22184 return base::Singleton<
185 HistogramSynchronizer,
186 base::LeakySingletonTraits<HistogramSynchronizer>>::get();
[email protected]83ab4a282012-07-12 18:19:45187}
188
189// static
190void HistogramSynchronizer::FetchHistograms() {
191 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Sami Kyostila8e4d5a92019-08-02 12:45:05192 base::PostTask(FROM_HERE, {BrowserThread::UI},
193 base::BindOnce(&HistogramSynchronizer::FetchHistograms));
[email protected]83ab4a282012-07-12 18:19:45194 return;
195 }
mostynb4c27d042015-03-18 21:47:47196 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:45197
198 HistogramSynchronizer* current_synchronizer =
199 HistogramSynchronizer::GetInstance();
Ivan Kotenkov2c0d2bb32017-11-01 15:41:28200 if (current_synchronizer == nullptr)
[email protected]83ab4a282012-07-12 18:19:45201 return;
202
203 current_synchronizer->RegisterAndNotifyAllProcesses(
204 HistogramSynchronizer::UNKNOWN,
205 base::TimeDelta::FromMinutes(1));
206}
207
fdoray0c755de2016-10-19 15:28:44208void FetchHistogramsAsynchronously(scoped_refptr<base::TaskRunner> task_runner,
[email protected]83ab4a282012-07-12 18:19:45209 const base::Closure& callback,
210 base::TimeDelta wait_time) {
fdoray0c755de2016-10-19 15:28:44211 HistogramSynchronizer::FetchHistogramsAsynchronously(std::move(task_runner),
212 callback, wait_time);
[email protected]83ab4a282012-07-12 18:19:45213}
214
215// static
216void HistogramSynchronizer::FetchHistogramsAsynchronously(
fdoray0c755de2016-10-19 15:28:44217 scoped_refptr<base::TaskRunner> task_runner,
[email protected]83ab4a282012-07-12 18:19:45218 const base::Closure& callback,
219 base::TimeDelta wait_time) {
fdoray0c755de2016-10-19 15:28:44220 DCHECK(task_runner);
[email protected]83ab4a282012-07-12 18:19:45221 DCHECK(!callback.is_null());
222
223 HistogramSynchronizer* current_synchronizer =
224 HistogramSynchronizer::GetInstance();
fdoray0c755de2016-10-19 15:28:44225 current_synchronizer->SetTaskRunnerAndCallback(std::move(task_runner),
226 callback);
[email protected]83ab4a282012-07-12 18:19:45227
228 current_synchronizer->RegisterAndNotifyAllProcesses(
229 HistogramSynchronizer::ASYNC_HISTOGRAMS, wait_time);
230}
231
232void HistogramSynchronizer::RegisterAndNotifyAllProcesses(
233 ProcessHistogramRequester requester,
234 base::TimeDelta wait_time) {
mostynb4c27d042015-03-18 21:47:47235 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:45236
237 int sequence_number = GetNextAvailableSequenceNumber(requester);
238
Makoto Shimazu51176e62019-10-10 14:43:17239 base::OnceClosure callback = base::BindOnce(
[email protected]83ab4a282012-07-12 18:19:45240 &HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback,
Makoto Shimazu51176e62019-10-10 14:43:17241 base::Unretained(this), sequence_number);
[email protected]83ab4a282012-07-12 18:19:45242
Tommy Nyquist4b749d02018-03-20 21:46:29243 RequestContext::Register(std::move(callback), sequence_number);
[email protected]83ab4a282012-07-12 18:19:45244
245 // Get histogram data from renderer and browser child processes.
[email protected]46488322012-10-30 03:22:20246 HistogramController::GetInstance()->GetHistogramData(sequence_number);
[email protected]83ab4a282012-07-12 18:19:45247
248 // Post a task that would be called after waiting for wait_time. This acts
249 // as a watchdog, to cancel the requests for non-responsive processes.
Sami Kyostila8e4d5a92019-08-02 12:45:05250 base::PostDelayedTask(
Eric Seckler8652dcd52018-09-20 10:42:28251 FROM_HERE, {BrowserThread::UI},
tzike2aca992017-09-05 08:50:54252 base::BindOnce(&RequestContext::Unregister, sequence_number), wait_time);
[email protected]83ab4a282012-07-12 18:19:45253}
254
255void HistogramSynchronizer::OnPendingProcesses(int sequence_number,
256 int pending_processes,
257 bool end) {
mostynb4c27d042015-03-18 21:47:47258 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:45259
260 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
261 if (!request)
262 return;
263 request->AddProcessesPending(pending_processes);
264 request->SetReceivedProcessGroupCount(end);
265 request->DeleteIfAllDone();
266}
267
268void HistogramSynchronizer::OnHistogramDataCollected(
269 int sequence_number,
270 const std::vector<std::string>& pickled_histograms) {
mostynb4c27d042015-03-18 21:47:47271 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]83ab4a282012-07-12 18:19:45272
[email protected]6afa90f2013-10-23 01:16:04273 base::HistogramDeltaSerialization::DeserializeAndAddSamples(
274 pickled_histograms);
275
[email protected]83ab4a282012-07-12 18:19:45276 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
[email protected]83ab4a282012-07-12 18:19:45277 if (!request)
278 return;
279
280 // Delete request if we have heard back from all child processes.
281 request->DecrementProcessesPending();
282 request->DeleteIfAllDone();
283}
284
fdoray0c755de2016-10-19 15:28:44285void HistogramSynchronizer::SetTaskRunnerAndCallback(
286 scoped_refptr<base::TaskRunner> task_runner,
[email protected]83ab4a282012-07-12 18:19:45287 const base::Closure& callback) {
288 base::Closure old_callback;
fdoray0c755de2016-10-19 15:28:44289 scoped_refptr<base::TaskRunner> old_task_runner;
[email protected]83ab4a282012-07-12 18:19:45290 {
291 base::AutoLock auto_lock(lock_);
292 old_callback = callback_;
293 callback_ = callback;
fdoray0c755de2016-10-19 15:28:44294 old_task_runner = std::move(callback_task_runner_);
295 callback_task_runner_ = std::move(task_runner);
[email protected]83ab4a282012-07-12 18:19:45296 // Prevent premature calling of our new callbacks.
297 async_sequence_number_ = kNeverUsableSequenceNumber;
298 }
299 // Just in case there was a task pending....
Tommy Nyquist4b749d02018-03-20 21:46:29300 InternalPostTask(std::move(old_task_runner), std::move(old_callback));
[email protected]83ab4a282012-07-12 18:19:45301}
302
303void HistogramSynchronizer::ForceHistogramSynchronizationDoneCallback(
304 int sequence_number) {
305 base::Closure callback;
fdoray0c755de2016-10-19 15:28:44306 scoped_refptr<base::TaskRunner> task_runner;
[email protected]83ab4a282012-07-12 18:19:45307 {
308 base::AutoLock lock(lock_);
309 if (sequence_number != async_sequence_number_)
310 return;
311 callback = callback_;
fdoray0c755de2016-10-19 15:28:44312 task_runner = std::move(callback_task_runner_);
[email protected]83ab4a282012-07-12 18:19:45313 callback_.Reset();
[email protected]83ab4a282012-07-12 18:19:45314 }
Tommy Nyquist4b749d02018-03-20 21:46:29315 InternalPostTask(std::move(task_runner), std::move(callback));
[email protected]83ab4a282012-07-12 18:19:45316}
317
fdoray0c755de2016-10-19 15:28:44318void HistogramSynchronizer::InternalPostTask(
319 scoped_refptr<base::TaskRunner> task_runner,
320 const base::Closure& callback) {
321 if (callback.is_null() || !task_runner)
[email protected]83ab4a282012-07-12 18:19:45322 return;
fdoray0c755de2016-10-19 15:28:44323 task_runner->PostTask(FROM_HERE, callback);
[email protected]83ab4a282012-07-12 18:19:45324}
325
326int HistogramSynchronizer::GetNextAvailableSequenceNumber(
327 ProcessHistogramRequester requester) {
328 base::AutoLock auto_lock(lock_);
329 ++last_used_sequence_number_;
330 // Watch out for wrapping to a negative number.
331 if (last_used_sequence_number_ < 0) {
332 // Bypass the reserved number, which is used when a renderer spontaneously
333 // decides to send some histogram data.
334 last_used_sequence_number_ =
335 kHistogramSynchronizerReservedSequenceNumber + 1;
336 }
337 DCHECK_NE(last_used_sequence_number_,
338 kHistogramSynchronizerReservedSequenceNumber);
339 if (requester == ASYNC_HISTOGRAMS)
340 async_sequence_number_ = last_used_sequence_number_;
341 return last_used_sequence_number_;
342}
343
344} // namespace content