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 "components/metrics/profiler/tracking_synchronizer.h"
6
7 #include "base/bind.h"
8 #include "base/metrics/histogram.h"
9 #include "base/threading/thread.h"
10 #include "base/tracked_objects.h"
11 #include "components/metrics/profiler/tracking_synchronizer_observer.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/profiler_controller.h"
14 #include "content/public/common/process_type.h"
15
16 using base::TimeTicks;
17 using content::BrowserThread;
18
19 namespace {
20
21 // Negative numbers are never used as sequence numbers. We explicitly pick a
22 // negative number that is "so negative" that even when we add one (as is done
23 // when we generated the next sequence number) that it will still be negative.
24 // We have code that handles wrapping around on an overflow into negative
25 // territory.
26 const int kNeverUsableSequenceNumber = -2;
27
28 // This singleton instance should be started during the single threaded
29 // portion of main(). It initializes globals to provide support for all future
30 // calls. This object is created on the UI thread, and it is destroyed after
31 // all the other threads have gone away. As a result, it is ok to call it
32 // from the UI thread, or for about:profiler.
33 static metrics::TrackingSynchronizer* g_tracking_synchronizer =
34 NULL;
35
36 } // anonymous namespace
37
38 namespace metrics {
39
40 // The "RequestContext" structure describes an individual request received
41 // from the UI. All methods are accessible on UI thread.
42 class TrackingSynchronizer::RequestContext {
43 public:
44 // A map from sequence_number_ to the actual RequestContexts.
45 typedef std::map<int, RequestContext*> RequestContextMap;
46
RequestContext(const base::WeakPtr<TrackingSynchronizerObserver> & callback_object,int sequence_number)47 RequestContext(
48 const base::WeakPtr<TrackingSynchronizerObserver>& callback_object,
49 int sequence_number)
50 : callback_object_(callback_object),
51 sequence_number_(sequence_number),
52 received_process_group_count_(0),
53 processes_pending_(0) {
54 }
~RequestContext()55 ~RequestContext() {}
56
SetReceivedProcessGroupCount(bool done)57 void SetReceivedProcessGroupCount(bool done) {
58 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59 received_process_group_count_ = done;
60 }
61
62 // Methods for book keeping of processes_pending_.
IncrementProcessesPending()63 void IncrementProcessesPending() {
64 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
65 ++processes_pending_;
66 }
67
AddProcessesPending(int processes_pending)68 void AddProcessesPending(int processes_pending) {
69 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
70 processes_pending_ += processes_pending;
71 }
72
DecrementProcessesPending()73 void DecrementProcessesPending() {
74 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
75 --processes_pending_;
76 }
77
78 // Records that we are waiting for one less tracking data from a process for
79 // the given sequence number. If |received_process_group_count_| and
80 // |processes_pending_| are zero, then delete the current object by calling
81 // Unregister.
DeleteIfAllDone()82 void DeleteIfAllDone() {
83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
84
85 if (processes_pending_ <= 0 && received_process_group_count_)
86 RequestContext::Unregister(sequence_number_);
87 }
88
89 // Register |callback_object| in |outstanding_requests_| map for the given
90 // |sequence_number|.
Register(int sequence_number,const base::WeakPtr<TrackingSynchronizerObserver> & callback_object)91 static RequestContext* Register(
92 int sequence_number,
93 const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
95
96 RequestContext* request = new RequestContext(
97 callback_object, sequence_number);
98 outstanding_requests_.Get()[sequence_number] = request;
99
100 return request;
101 }
102
103 // Find the |RequestContext| in |outstanding_requests_| map for the given
104 // |sequence_number|.
GetRequestContext(int sequence_number)105 static RequestContext* GetRequestContext(int sequence_number) {
106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
107
108 RequestContextMap::iterator it =
109 outstanding_requests_.Get().find(sequence_number);
110 if (it == outstanding_requests_.Get().end())
111 return NULL;
112
113 RequestContext* request = it->second;
114 DCHECK_EQ(sequence_number, request->sequence_number_);
115 return request;
116 }
117
118 // Delete the entry for the given |sequence_number| from
119 // |outstanding_requests_| map. This method is called when all changes have
120 // been acquired, or when the wait time expires (whichever is sooner).
Unregister(int sequence_number)121 static void Unregister(int sequence_number) {
122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
123
124 RequestContextMap::iterator it =
125 outstanding_requests_.Get().find(sequence_number);
126 if (it == outstanding_requests_.Get().end())
127 return;
128
129 RequestContext* request = it->second;
130 DCHECK_EQ(sequence_number, request->sequence_number_);
131 bool received_process_group_count = request->received_process_group_count_;
132 int unresponsive_processes = request->processes_pending_;
133
134 if (request->callback_object_.get())
135 request->callback_object_->FinishedReceivingProfilerData();
136
137 delete request;
138 outstanding_requests_.Get().erase(it);
139
140 UMA_HISTOGRAM_BOOLEAN("Profiling.ReceivedProcessGroupCount",
141 received_process_group_count);
142 UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding",
143 unresponsive_processes);
144 }
145
146 // Delete all the entries in |outstanding_requests_| map.
OnShutdown()147 static void OnShutdown() {
148 // Just in case we have any pending tasks, clear them out.
149 while (!outstanding_requests_.Get().empty()) {
150 RequestContextMap::iterator it = outstanding_requests_.Get().begin();
151 delete it->second;
152 outstanding_requests_.Get().erase(it);
153 }
154 }
155
156 // Requests are made to asynchronously send data to the |callback_object_|.
157 base::WeakPtr<TrackingSynchronizerObserver> callback_object_;
158
159 // The sequence number used by the most recent update request to contact all
160 // processes.
161 int sequence_number_;
162
163 // Indicates if we have received all pending processes count.
164 bool received_process_group_count_;
165
166 // The number of pending processes (browser, all renderer processes and
167 // browser child processes) that have not yet responded to requests.
168 int processes_pending_;
169
170 // Map of all outstanding RequestContexts, from sequence_number_ to
171 // RequestContext.
172 static base::LazyInstance<RequestContextMap>::Leaky outstanding_requests_;
173 };
174
175 // static
176 base::LazyInstance
177 <TrackingSynchronizer::RequestContext::RequestContextMap>::Leaky
178 TrackingSynchronizer::RequestContext::outstanding_requests_ =
179 LAZY_INSTANCE_INITIALIZER;
180
181 // TrackingSynchronizer methods and members.
182
TrackingSynchronizer()183 TrackingSynchronizer::TrackingSynchronizer()
184 : last_used_sequence_number_(kNeverUsableSequenceNumber) {
185 DCHECK(!g_tracking_synchronizer);
186 g_tracking_synchronizer = this;
187 content::ProfilerController::GetInstance()->Register(this);
188 }
189
~TrackingSynchronizer()190 TrackingSynchronizer::~TrackingSynchronizer() {
191 content::ProfilerController::GetInstance()->Unregister(this);
192
193 // Just in case we have any pending tasks, clear them out.
194 RequestContext::OnShutdown();
195
196 g_tracking_synchronizer = NULL;
197 }
198
199 // static
FetchProfilerDataAsynchronously(const base::WeakPtr<TrackingSynchronizerObserver> & callback_object)200 void TrackingSynchronizer::FetchProfilerDataAsynchronously(
201 const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203
204 if (!g_tracking_synchronizer) {
205 // System teardown is happening.
206 return;
207 }
208
209 int sequence_number = g_tracking_synchronizer->RegisterAndNotifyAllProcesses(
210 callback_object);
211
212 // Post a task that would be called after waiting for wait_time. This acts
213 // as a watchdog, to cancel the requests for non-responsive processes.
214 BrowserThread::PostDelayedTask(
215 BrowserThread::UI, FROM_HERE,
216 base::Bind(&RequestContext::Unregister, sequence_number),
217 base::TimeDelta::FromMinutes(1));
218 }
219
OnPendingProcesses(int sequence_number,int pending_processes,bool end)220 void TrackingSynchronizer::OnPendingProcesses(int sequence_number,
221 int pending_processes,
222 bool end) {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224
225 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
226 if (!request)
227 return;
228 request->AddProcessesPending(pending_processes);
229 request->SetReceivedProcessGroupCount(end);
230 request->DeleteIfAllDone();
231 }
232
OnProfilerDataCollected(int sequence_number,const tracked_objects::ProcessDataSnapshot & profiler_data,int process_type)233 void TrackingSynchronizer::OnProfilerDataCollected(
234 int sequence_number,
235 const tracked_objects::ProcessDataSnapshot& profiler_data,
236 int process_type) {
237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
238 DecrementPendingProcessesAndSendData(sequence_number, profiler_data,
239 process_type);
240 }
241
RegisterAndNotifyAllProcesses(const base::WeakPtr<TrackingSynchronizerObserver> & callback_object)242 int TrackingSynchronizer::RegisterAndNotifyAllProcesses(
243 const base::WeakPtr<TrackingSynchronizerObserver>& callback_object) {
244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
245
246 int sequence_number = GetNextAvailableSequenceNumber();
247
248 RequestContext* request =
249 RequestContext::Register(sequence_number, callback_object);
250
251 // Increment pending process count for sending browser's profiler data.
252 request->IncrementProcessesPending();
253
254 // Get profiler data from renderer and browser child processes.
255 content::ProfilerController::GetInstance()->GetProfilerData(sequence_number);
256
257 // Send profiler_data from browser process.
258 tracked_objects::ProcessDataSnapshot process_data;
259 tracked_objects::ThreadData::Snapshot(false, &process_data);
260 DecrementPendingProcessesAndSendData(sequence_number, process_data,
261 content::PROCESS_TYPE_BROWSER);
262
263 return sequence_number;
264 }
265
DecrementPendingProcessesAndSendData(int sequence_number,const tracked_objects::ProcessDataSnapshot & profiler_data,int process_type)266 void TrackingSynchronizer::DecrementPendingProcessesAndSendData(
267 int sequence_number,
268 const tracked_objects::ProcessDataSnapshot& profiler_data,
269 int process_type) {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271
272 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
273 if (!request)
274 return;
275
276 if (request->callback_object_.get()) {
277 request->callback_object_
278 ->ReceivedProfilerData(profiler_data, process_type);
279 }
280
281 // Delete request if we have heard back from all child processes.
282 request->DecrementProcessesPending();
283 request->DeleteIfAllDone();
284 }
285
GetNextAvailableSequenceNumber()286 int TrackingSynchronizer::GetNextAvailableSequenceNumber() {
287 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
288
289 ++last_used_sequence_number_;
290
291 // Watch out for wrapping to a negative number.
292 if (last_used_sequence_number_ < 0)
293 last_used_sequence_number_ = 1;
294 return last_used_sequence_number_;
295 }
296
297 } // namespace metrics
298