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 "content/public/browser/browser_thread.h"
13 #include "content/public/browser/profiler_controller.h"
14 #include "content/public/common/process_type.h"
16 using base::TimeTicks
;
17 using content::BrowserThread
;
23 // Negative numbers are never used as sequence numbers. We explicitly pick a
24 // negative number that is "so negative" that even when we add one (as is done
25 // when we generated the next sequence number) that it will still be negative.
26 // We have code that handles wrapping around on an overflow into negative
28 const int kNeverUsableSequenceNumber
= -2;
30 // This singleton instance should be started during the single threaded
31 // portion of main(). It initializes globals to provide support for all future
32 // calls. This object is created on the UI thread, and it is destroyed after
33 // all the other threads have gone away. As a result, it is ok to call it
34 // from the UI thread, or for about:profiler.
35 static TrackingSynchronizer
* g_tracking_synchronizer
= NULL
;
39 // The "RequestContext" structure describes an individual request received
40 // from the UI. All methods are accessible on UI thread.
41 class TrackingSynchronizer::RequestContext
{
43 // A map from sequence_number_ to the actual RequestContexts.
44 typedef std::map
<int, RequestContext
*> RequestContextMap
;
47 const base::WeakPtr
<TrackingSynchronizerObserver
>& callback_object
,
49 : callback_object_(callback_object
),
50 sequence_number_(sequence_number
),
51 received_process_group_count_(0),
52 processes_pending_(0) {
56 void SetReceivedProcessGroupCount(bool done
) {
57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
58 received_process_group_count_
= done
;
61 // Methods for book keeping of processes_pending_.
62 void IncrementProcessesPending() {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
67 void AddProcessesPending(int processes_pending
) {
68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
69 processes_pending_
+= processes_pending
;
72 void DecrementProcessesPending() {
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
77 // Records that we are waiting for one less tracking data from a process for
78 // the given sequence number. If |received_process_group_count_| and
79 // |processes_pending_| are zero, then delete the current object by calling
81 void DeleteIfAllDone() {
82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
84 if (processes_pending_
<= 0 && received_process_group_count_
)
85 RequestContext::Unregister(sequence_number_
);
88 // Register |callback_object| in |outstanding_requests_| map for the given
90 static RequestContext
* Register(
92 const base::WeakPtr
<TrackingSynchronizerObserver
>& callback_object
) {
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
95 RequestContext
* request
= new RequestContext(
96 callback_object
, sequence_number
);
97 outstanding_requests_
.Get()[sequence_number
] = request
;
102 // Find the |RequestContext| in |outstanding_requests_| map for the given
103 // |sequence_number|.
104 static RequestContext
* GetRequestContext(int sequence_number
) {
105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
107 RequestContextMap::iterator it
=
108 outstanding_requests_
.Get().find(sequence_number
);
109 if (it
== outstanding_requests_
.Get().end())
112 RequestContext
* request
= it
->second
;
113 DCHECK_EQ(sequence_number
, request
->sequence_number_
);
117 // Delete the entry for the given |sequence_number| from
118 // |outstanding_requests_| map. This method is called when all changes have
119 // been acquired, or when the wait time expires (whichever is sooner).
120 static void Unregister(int sequence_number
) {
121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
123 RequestContextMap::iterator it
=
124 outstanding_requests_
.Get().find(sequence_number
);
125 if (it
== outstanding_requests_
.Get().end())
128 RequestContext
* request
= it
->second
;
129 DCHECK_EQ(sequence_number
, request
->sequence_number_
);
130 bool received_process_group_count
= request
->received_process_group_count_
;
131 int unresponsive_processes
= request
->processes_pending_
;
133 if (request
->callback_object_
.get())
134 request
->callback_object_
->FinishedReceivingProfilerData();
137 outstanding_requests_
.Get().erase(it
);
139 UMA_HISTOGRAM_BOOLEAN("Profiling.ReceivedProcessGroupCount",
140 received_process_group_count
);
141 UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding",
142 unresponsive_processes
);
145 // Delete all the entries in |outstanding_requests_| map.
146 static void OnShutdown() {
147 // Just in case we have any pending tasks, clear them out.
148 while (!outstanding_requests_
.Get().empty()) {
149 RequestContextMap::iterator it
= outstanding_requests_
.Get().begin();
151 outstanding_requests_
.Get().erase(it
);
155 // Requests are made to asynchronously send data to the |callback_object_|.
156 base::WeakPtr
<TrackingSynchronizerObserver
> callback_object_
;
158 // The sequence number used by the most recent update request to contact all
160 int sequence_number_
;
162 // Indicates if we have received all pending processes count.
163 bool received_process_group_count_
;
165 // The number of pending processes (browser, all renderer processes and
166 // browser child processes) that have not yet responded to requests.
167 int processes_pending_
;
169 // Map of all outstanding RequestContexts, from sequence_number_ to
171 static base::LazyInstance
<RequestContextMap
>::Leaky outstanding_requests_
;
176 <TrackingSynchronizer::RequestContext::RequestContextMap
>::Leaky
177 TrackingSynchronizer::RequestContext::outstanding_requests_
=
178 LAZY_INSTANCE_INITIALIZER
;
180 // TrackingSynchronizer methods and members.
182 TrackingSynchronizer::TrackingSynchronizer()
183 : last_used_sequence_number_(kNeverUsableSequenceNumber
) {
184 DCHECK(!g_tracking_synchronizer
);
185 g_tracking_synchronizer
= this;
186 content::ProfilerController::GetInstance()->Register(this);
189 TrackingSynchronizer::~TrackingSynchronizer() {
190 content::ProfilerController::GetInstance()->Unregister(this);
192 // Just in case we have any pending tasks, clear them out.
193 RequestContext::OnShutdown();
195 g_tracking_synchronizer
= NULL
;
199 void TrackingSynchronizer::FetchProfilerDataAsynchronously(
200 const base::WeakPtr
<TrackingSynchronizerObserver
>& callback_object
) {
201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
203 if (!g_tracking_synchronizer
) {
204 // System teardown is happening.
208 int sequence_number
= g_tracking_synchronizer
->RegisterAndNotifyAllProcesses(
211 // Post a task that would be called after waiting for wait_time. This acts
212 // as a watchdog, to cancel the requests for non-responsive processes.
213 BrowserThread::PostDelayedTask(
214 BrowserThread::UI
, FROM_HERE
,
215 base::Bind(&RequestContext::Unregister
, sequence_number
),
216 base::TimeDelta::FromMinutes(1));
219 void TrackingSynchronizer::OnPendingProcesses(int sequence_number
,
220 int pending_processes
,
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
224 RequestContext
* request
= RequestContext::GetRequestContext(sequence_number
);
227 request
->AddProcessesPending(pending_processes
);
228 request
->SetReceivedProcessGroupCount(end
);
229 request
->DeleteIfAllDone();
232 void TrackingSynchronizer::OnProfilerDataCollected(
234 const tracked_objects::ProcessDataSnapshot
& profiler_data
,
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
237 DecrementPendingProcessesAndSendData(sequence_number
, profiler_data
,
241 int TrackingSynchronizer::RegisterAndNotifyAllProcesses(
242 const base::WeakPtr
<TrackingSynchronizerObserver
>& callback_object
) {
243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
245 int sequence_number
= GetNextAvailableSequenceNumber();
247 RequestContext
* request
=
248 RequestContext::Register(sequence_number
, callback_object
);
250 // Increment pending process count for sending browser's profiler data.
251 request
->IncrementProcessesPending();
253 // Get profiler data from renderer and browser child processes.
254 content::ProfilerController::GetInstance()->GetProfilerData(sequence_number
);
256 // Send profiler_data from browser process.
257 tracked_objects::ProcessDataSnapshot process_data
;
258 tracked_objects::ThreadData::Snapshot(&process_data
);
259 DecrementPendingProcessesAndSendData(sequence_number
, process_data
,
260 content::PROCESS_TYPE_BROWSER
);
262 return sequence_number
;
265 void TrackingSynchronizer::DecrementPendingProcessesAndSendData(
267 const tracked_objects::ProcessDataSnapshot
& profiler_data
,
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
271 RequestContext
* request
= RequestContext::GetRequestContext(sequence_number
);
275 if (request
->callback_object_
.get()) {
276 request
->callback_object_
277 ->ReceivedProfilerData(profiler_data
, process_type
);
280 // Delete request if we have heard back from all child processes.
281 request
->DecrementProcessesPending();
282 request
->DeleteIfAllDone();
285 int TrackingSynchronizer::GetNextAvailableSequenceNumber() {
286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
288 ++last_used_sequence_number_
;
290 // Watch out for wrapping to a negative number.
291 if (last_used_sequence_number_
< 0)
292 last_used_sequence_number_
= 1;
293 return last_used_sequence_number_
;
296 } // namespace metrics