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.
5 #include "components/metrics/profiler/tracking_synchronizer.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 "components/variations/variations_associated_data.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/profiler_controller.h"
15 #include "content/public/common/process_type.h"
17 using base::TimeTicks
;
18 using content::BrowserThread
;
24 // Negative numbers are never used as sequence numbers. We explicitly pick a
25 // negative number that is "so negative" that even when we add one (as is done
26 // when we generated the next sequence number) that it will still be negative.
27 // We have code that handles wrapping around on an overflow into negative
29 const int kNeverUsableSequenceNumber
= -2;
31 // This singleton instance should be started during the single threaded
32 // portion of main(). It initializes globals to provide support for all future
33 // calls. This object is created on the UI thread, and it is destroyed after
34 // all the other threads have gone away. As a result, it is ok to call it
35 // from the UI thread, or for about:profiler.
36 static TrackingSynchronizer
* g_tracking_synchronizer
= NULL
;
40 // The "RequestContext" structure describes an individual request received
41 // from the UI. All methods are accessible on UI thread.
42 class TrackingSynchronizer::RequestContext
{
44 // A map from sequence_number_ to the actual RequestContexts.
45 typedef std::map
<int, RequestContext
*> RequestContextMap
;
48 const base::WeakPtr
<TrackingSynchronizerObserver
>& callback_object
,
50 : callback_object_(callback_object
),
51 sequence_number_(sequence_number
),
52 received_process_group_count_(0),
53 processes_pending_(0) {
57 void SetReceivedProcessGroupCount(bool done
) {
58 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
59 received_process_group_count_
= done
;
62 // Methods for book keeping of processes_pending_.
63 void IncrementProcessesPending() {
64 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
68 void AddProcessesPending(int processes_pending
) {
69 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
70 processes_pending_
+= processes_pending
;
73 void DecrementProcessesPending() {
74 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
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
82 void DeleteIfAllDone() {
83 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
85 if (processes_pending_
<= 0 && received_process_group_count_
)
86 RequestContext::Unregister(sequence_number_
);
89 // Register |callback_object| in |outstanding_requests_| map for the given
91 static RequestContext
* Register(
93 const base::WeakPtr
<TrackingSynchronizerObserver
>& callback_object
) {
94 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
96 RequestContext
* request
= new RequestContext(
97 callback_object
, sequence_number
);
98 outstanding_requests_
.Get()[sequence_number
] = request
;
103 // Find the |RequestContext| in |outstanding_requests_| map for the given
104 // |sequence_number|.
105 static RequestContext
* GetRequestContext(int sequence_number
) {
106 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
108 RequestContextMap::iterator it
=
109 outstanding_requests_
.Get().find(sequence_number
);
110 if (it
== outstanding_requests_
.Get().end())
113 RequestContext
* request
= it
->second
;
114 DCHECK_EQ(sequence_number
, request
->sequence_number_
);
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).
121 static void Unregister(int sequence_number
) {
122 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
124 RequestContextMap::iterator it
=
125 outstanding_requests_
.Get().find(sequence_number
);
126 if (it
== outstanding_requests_
.Get().end())
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_
;
134 if (request
->callback_object_
.get())
135 request
->callback_object_
->FinishedReceivingProfilerData();
138 outstanding_requests_
.Get().erase(it
);
140 UMA_HISTOGRAM_BOOLEAN("Profiling.ReceivedProcessGroupCount",
141 received_process_group_count
);
142 UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding",
143 unresponsive_processes
);
146 // Delete all the entries in |outstanding_requests_| map.
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();
152 outstanding_requests_
.Get().erase(it
);
156 // Requests are made to asynchronously send data to the |callback_object_|.
157 base::WeakPtr
<TrackingSynchronizerObserver
> callback_object_
;
159 // The sequence number used by the most recent update request to contact all
161 int sequence_number_
;
163 // Indicates if we have received all pending processes count.
164 bool received_process_group_count_
;
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_
;
170 // Map of all outstanding RequestContexts, from sequence_number_ to
172 static base::LazyInstance
<RequestContextMap
>::Leaky outstanding_requests_
;
177 <TrackingSynchronizer::RequestContext::RequestContextMap
>::Leaky
178 TrackingSynchronizer::RequestContext::outstanding_requests_
=
179 LAZY_INSTANCE_INITIALIZER
;
181 // TrackingSynchronizer methods and members.
183 TrackingSynchronizer::TrackingSynchronizer(scoped_ptr
<base::TickClock
> clock
)
184 : last_used_sequence_number_(kNeverUsableSequenceNumber
),
185 clock_(clock
.Pass()) {
186 DCHECK(!g_tracking_synchronizer
);
187 g_tracking_synchronizer
= this;
188 phase_start_times_
.push_back(clock_
->NowTicks());
191 // TODO: This ifdef and other ifdefs for OS_IOS in this file are only
192 // short-term hacks to make this compile on iOS, and the proper solution is to
193 // refactor to remove content dependencies from shared code.
195 content::ProfilerController::GetInstance()->Register(this);
199 TrackingSynchronizer::~TrackingSynchronizer() {
201 content::ProfilerController::GetInstance()->Unregister(this);
204 // Just in case we have any pending tasks, clear them out.
205 RequestContext::OnShutdown();
207 g_tracking_synchronizer
= NULL
;
211 void TrackingSynchronizer::FetchProfilerDataAsynchronously(
212 const base::WeakPtr
<TrackingSynchronizerObserver
>& callback_object
) {
213 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
215 if (!g_tracking_synchronizer
) {
216 // System teardown is happening.
220 int sequence_number
= g_tracking_synchronizer
->RegisterAndNotifyAllProcesses(
223 // Post a task that would be called after waiting for wait_time. This acts
224 // as a watchdog, to cancel the requests for non-responsive processes.
225 BrowserThread::PostDelayedTask(
226 BrowserThread::UI
, FROM_HERE
,
227 base::Bind(&RequestContext::Unregister
, sequence_number
),
228 base::TimeDelta::FromMinutes(1));
232 void TrackingSynchronizer::OnProfilingPhaseCompleted(
233 ProfilerEventProto::ProfilerEvent profiling_event
) {
234 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
236 if (!g_tracking_synchronizer
) {
237 // System teardown is happening.
241 g_tracking_synchronizer
->NotifyAllProcessesOfProfilingPhaseCompletion(
245 void TrackingSynchronizer::OnPendingProcesses(int sequence_number
,
246 int pending_processes
,
248 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
250 RequestContext
* request
= RequestContext::GetRequestContext(sequence_number
);
253 request
->AddProcessesPending(pending_processes
);
254 request
->SetReceivedProcessGroupCount(end
);
255 request
->DeleteIfAllDone();
258 void TrackingSynchronizer::OnProfilerDataCollected(
260 const tracked_objects::ProcessDataSnapshot
& profiler_data
,
261 content::ProcessType process_type
) {
262 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
263 DecrementPendingProcessesAndSendData(sequence_number
, profiler_data
,
267 int TrackingSynchronizer::RegisterAndNotifyAllProcesses(
268 const base::WeakPtr
<TrackingSynchronizerObserver
>& callback_object
) {
269 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
271 int sequence_number
= GetNextAvailableSequenceNumber();
273 RequestContext
* request
=
274 RequestContext::Register(sequence_number
, callback_object
);
276 // Increment pending process count for sending browser's profiler data.
277 request
->IncrementProcessesPending();
279 const int current_profiling_phase
= phase_completion_events_sequence_
.size();
282 // Get profiler data from renderer and browser child processes.
283 content::ProfilerController::GetInstance()->GetProfilerData(
284 sequence_number
, current_profiling_phase
);
286 // On non-iOS platforms, |OnPendingProcesses()| is called from
287 // |content::ProfilerController|. On iOS, manually call the method to indicate
288 // that there is no need to wait for data from child processes. On iOS, there
289 // is only the main browser process.
290 OnPendingProcesses(sequence_number
, 0, true);
291 #endif // !defined(OS_IOS)
293 // Send process data snapshot from browser process.
294 tracked_objects::ProcessDataSnapshot process_data_snapshot
;
295 tracked_objects::ThreadData::Snapshot(current_profiling_phase
,
296 &process_data_snapshot
);
298 DecrementPendingProcessesAndSendData(sequence_number
, process_data_snapshot
,
299 content::PROCESS_TYPE_BROWSER
);
301 return sequence_number
;
304 void TrackingSynchronizer::RegisterPhaseCompletion(
305 ProfilerEventProto::ProfilerEvent profiling_event
) {
306 phase_completion_events_sequence_
.push_back(profiling_event
);
307 phase_start_times_
.push_back(clock_
->NowTicks());
310 void TrackingSynchronizer::NotifyAllProcessesOfProfilingPhaseCompletion(
311 ProfilerEventProto::ProfilerEvent profiling_event
) {
312 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
314 if (variations::GetVariationParamValue("UMALogPhasedProfiling",
315 "send_split_profiles") == "false") {
319 int profiling_phase
= phase_completion_events_sequence_
.size();
321 // If you hit this check, stop and think. You just added a new profiling
322 // phase. Each profiling phase takes additional memory in DeathData's list of
323 // snapshots. We cannot grow it indefinitely. Consider collapsing older phases
324 // after they were sent to UMA server, or other ways to save memory.
325 DCHECK_LT(profiling_phase
, 1);
327 RegisterPhaseCompletion(profiling_event
);
330 // Notify renderer and browser child processes.
331 content::ProfilerController::GetInstance()->OnProfilingPhaseCompleted(
335 // Notify browser process.
336 tracked_objects::ThreadData::OnProfilingPhaseCompleted(profiling_phase
);
339 void TrackingSynchronizer::SendData(
340 const tracked_objects::ProcessDataSnapshot
& profiler_data
,
341 content::ProcessType process_type
,
342 TrackingSynchronizerObserver
* observer
) const {
343 // We are going to loop though past profiling phases and notify the request
344 // about each phase that is contained in profiler_data. past_events
345 // will track the set of past profiling events as we go.
346 ProfilerEvents past_events
;
348 // Go through all completed phases, and through the current one. The current
349 // one is not in phase_completion_events_sequence_, but note the <=
351 for (size_t phase
= 0; phase
<= phase_completion_events_sequence_
.size();
353 auto it
= profiler_data
.phased_snapshots
.find(phase
);
355 if (it
!= profiler_data
.phased_snapshots
.end()) {
356 // If the phase is contained in the received snapshot, notify the
358 const base::TimeTicks phase_start
= phase_start_times_
[phase
];
359 const base::TimeTicks phase_finish
= phase
+ 1 < phase_start_times_
.size()
360 ? phase_start_times_
[phase
+ 1]
361 : clock_
->NowTicks();
362 observer
->ReceivedProfilerData(
363 ProfilerDataAttributes(phase
, profiler_data
.process_id
, process_type
,
364 phase_start
, phase_finish
),
365 it
->second
, past_events
);
368 if (phase
< phase_completion_events_sequence_
.size()) {
369 past_events
.push_back(phase_completion_events_sequence_
[phase
]);
374 void TrackingSynchronizer::DecrementPendingProcessesAndSendData(
376 const tracked_objects::ProcessDataSnapshot
& profiler_data
,
377 content::ProcessType process_type
) {
378 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
380 RequestContext
* request
= RequestContext::GetRequestContext(sequence_number
);
384 TrackingSynchronizerObserver
* observer
= request
->callback_object_
.get();
386 SendData(profiler_data
, process_type
, observer
);
388 // Delete request if we have heard back from all child processes.
389 request
->DecrementProcessesPending();
390 request
->DeleteIfAllDone();
393 int TrackingSynchronizer::GetNextAvailableSequenceNumber() {
394 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
396 ++last_used_sequence_number_
;
398 // Watch out for wrapping to a negative number.
399 if (last_used_sequence_number_
< 0)
400 last_used_sequence_number_
= 1;
401 return last_used_sequence_number_
;
404 } // namespace metrics