Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(241)

Side by Side Diff: chrome/browser/metrics/tracking_synchronizer.cc

Issue 8588023: Collect profiler stats from browser child processes. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/metrics/tracking_synchronizer.h" 5 #include "chrome/browser/metrics/tracking_synchronizer.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h" 8 #include "base/metrics/histogram.h"
9 #include "base/process_util.h"
12 #include "base/threading/thread.h" 10 #include "base/threading/thread.h"
13 #include "base/tracked_objects.h" 11 #include "base/tracked_objects.h"
14 #include "chrome/browser/ui/webui/tracing_ui.h" 12 #include "content/common/child_process_info.h"
15 #include "chrome/common/chrome_constants.h"
16 #include "chrome/common/render_messages.h"
17 #include "content/public/browser/browser_thread.h" 13 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_process_host.h" 14 #include "content/public/browser/profiler_controller.h"
19 15
20 using base::TimeTicks; 16 using base::TimeTicks;
21 using content::BrowserThread; 17 using content::BrowserThread;
22 18
23 namespace chrome_browser_metrics { 19 namespace chrome_browser_metrics {
24 20
21 // The "RequestContext" structure describes an individual request received
22 // from the UI. All methods are accessible on UI thread.
23 class RequestContext {
24 public:
25 // A map from sequence_number_ to the actual RequestContexts.
26 typedef std::map<int, RequestContext*> RequestContextMap;
27
28 ~RequestContext();
jam 2011/11/30 00:22:25 nit: up to you, but since this class is in the cc
ramant (doing other things) 2011/11/30 19:37:21 Done.
29
30 private:
31 friend class TrackingSynchronizer;
jam 2011/11/30 00:22:25 nit: you can get rid fo the private constructor an
ramant (doing other things) 2011/11/30 19:37:21 Done.
32
33 RequestContext(const base::WeakPtr<ProfilerUI>& callback_object,
34 int sequence_number);
35
36 // Methods for book keeping of process_group_count_.
37 void AddProcessGroupCount(int process_group_count);
38 void DecrementProcessGroupCount();
39
40 // Methods for book keeping of processes_pending_.
41 void IncrementProcessesPending();
42 void AddProcessesPending(int processes_pending);
43 void DecrementProcessesPending();
44
45 // Records that we are waiting for one less tracking data from a process for
46 // the given sequence number. If |process_group_count_| and
47 // |processes_pending_| are zero, then delete the current object by calling
48 // Unregister.
49 void DeleteIfAllDone();
50
51 // Register |callback_object| in |outstanding_requests_| map for the given
52 // |sequence_number|.
53 static RequestContext* Register(
54 int sequence_number,
55 const base::WeakPtr<ProfilerUI>& callback_object);
56
57 // Find the |RequestContext| in |outstanding_requests_| map for the given
58 // |sequence_number|.
59 static RequestContext* GetRequestContext(int sequence_number);
60
61 // Delete the entry for the given sequence_number| from
62 // |outstanding_requests_| map. This method is called when all changes have
63 // been acquired, or when the wait time expires (whichever is sooner).
64 static void Unregister(int sequence_number);
65
66 // Delete all the entries in |outstanding_requests_| map.
67 static void OnShutdown();
68
69 // Requests are made to asynchronously send data to the |callback_object_|.
70 base::WeakPtr<ProfilerUI> callback_object_;
71
72 // The sequence number used by the most recent update request to contact all
73 // processes.
74 int sequence_number_;
75
76 // The number of times we need to hear from |content| about number of
77 // pending processes. |content| code calls OnPendingProcesses once for
78 // RenderProcessHosts and another time for BrowserChildProcessHosts after
79 // sending GetChildProfilerData message to all respective child processes.
80 // RegisterAndNotifyAllProcesses initializes it by the value returned by the
81 // content::GetProfilerData and OnPendingProcesses decrements it when it gets
82 // processes pending count.
83 int process_group_count_;
84
85 // The number of pending processes (browser, all renderer processes and
86 // browser child processes) that have not yet responded to requests.
87 int processes_pending_;
88
89 // Map of all outstanding RequestContexts, from sequence_number_ to
90 // RequestContext.
91 static base::LazyInstance<RequestContextMap> outstanding_requests_;
92 };
93
25 // Negative numbers are never used as sequence numbers. We explicitly pick a 94 // Negative numbers are never used as sequence numbers. We explicitly pick a
26 // negative number that is "so negative" that even when we add one (as is done 95 // negative number that is "so negative" that even when we add one (as is done
27 // when we generated the next sequence number) that it will still be negative. 96 // when we generated the next sequence number) that it will still be negative.
28 // We have code that handles wrapping around on an overflow into negative 97 // We have code that handles wrapping around on an overflow into negative
29 // territory. 98 // territory.
30 static const int kNeverUsableSequenceNumber = -2; 99 static const int kNeverUsableSequenceNumber = -2;
31 100
101 // TrackingSynchronizer methods and members.
102 //
103 // static
104 TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL;
105
32 TrackingSynchronizer::TrackingSynchronizer() 106 TrackingSynchronizer::TrackingSynchronizer()
33 : last_used_sequence_number_(kNeverUsableSequenceNumber) { 107 : last_used_sequence_number_(kNeverUsableSequenceNumber) {
34 DCHECK(tracking_synchronizer_ == NULL); 108 DCHECK(tracking_synchronizer_ == NULL);
35 tracking_synchronizer_ = this; 109 tracking_synchronizer_ = this;
110 content::ProfilerController::GetInstance()->Register(this);
36 } 111 }
37 112
38 TrackingSynchronizer::~TrackingSynchronizer() { 113 TrackingSynchronizer::~TrackingSynchronizer() {
114 content::ProfilerController::GetInstance()->Unregister(this);
115
39 // Just in case we have any pending tasks, clear them out. 116 // Just in case we have any pending tasks, clear them out.
40 while (!outstanding_requests_.empty()) { 117 RequestContext::OnShutdown();
41 RequestContextMap::iterator it = outstanding_requests_.begin();
42 delete it->second;
43 outstanding_requests_.erase(it);
44 }
45 118
46 tracking_synchronizer_ = NULL; 119 tracking_synchronizer_ = NULL;
47 } 120 }
48 121
49 // static 122 // static
50 TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() { 123 void TrackingSynchronizer::FetchProfilerDataAsynchronously(
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52 DCHECK(tracking_synchronizer_ != NULL);
53 return tracking_synchronizer_;
54 }
55
56 // static
57 void TrackingSynchronizer::FetchTrackingDataAsynchronously(
58 const base::WeakPtr<ProfilerUI>& callback_object) { 124 const base::WeakPtr<ProfilerUI>& callback_object) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
60 126
61 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer(); 127 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer();
62 if (current_synchronizer == NULL) { 128 if (current_synchronizer == NULL) {
63 // System teardown is happening. 129 // System teardown is happening.
64 return; 130 return;
65 } 131 }
66 132
67 int sequence_number = current_synchronizer->RegisterAndNotifyAllProcesses( 133 int sequence_number = current_synchronizer->RegisterAndNotifyAllProcesses(
68 callback_object); 134 callback_object);
69 135
70 // Post a task that would be called after waiting for wait_time. This acts 136 // Post a task that would be called after waiting for wait_time. This acts
71 // as a watchdog, to cancel the requests for non-responsive processes. 137 // as a watchdog, to cancel the requests for non-responsive processes.
72 BrowserThread::PostDelayedTask( 138 BrowserThread::PostDelayedTask(
73 BrowserThread::UI, FROM_HERE, 139 BrowserThread::UI, FROM_HERE,
74 NewRunnableMethod( 140 base::Bind(&RequestContext::Unregister, sequence_number),
75 current_synchronizer,
76 &TrackingSynchronizer::ForceTrackingSynchronizationDoneCallback,
77 sequence_number),
78 60000); 141 60000);
79 } 142 }
80 143
81 // static 144 void TrackingSynchronizer::OnPendingProcesses(int sequence_number,
82 void TrackingSynchronizer::SetTrackingStatus(bool enable) { 145 int pending_processes) {
83 // To iterate over all processes, or to send messages to the hosts, we need
84 // to be on the UI thread.
85 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
86 147
87 for (content::RenderProcessHost::iterator it( 148 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
88 content::RenderProcessHost::AllHostsIterator()); 149 if (!request)
89 !it.IsAtEnd(); it.Advance()) { 150 return;
90 content::RenderProcessHost* render_process_host = it.GetCurrentValue(); 151 request->AddProcessesPending(pending_processes);
91 DCHECK(render_process_host); 152 request->DecrementProcessGroupCount();
92 // Ignore processes that don't have a connection, such as crashed tabs. 153 request->DeleteIfAllDone();
93 if (!render_process_host->HasConnection())
94 continue;
95
96 render_process_host->Send(new ChromeViewMsg_SetTrackingStatus(enable));
97 }
98 } 154 }
99 155
100 // static 156 void TrackingSynchronizer::OnProfilerDataCollected(
101 void TrackingSynchronizer::IsTrackingEnabled(int process_id) { 157 int sequence_number,
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 158 base::DictionaryValue* profiler_data) {
103 // To find the process, or to send messages to the hosts, we need to be on the
104 // UI thread.
105 BrowserThread::PostTask(
106 BrowserThread::UI, FROM_HERE,
107 base::Bind(
108 &TrackingSynchronizer::SetTrackingStatusInProcess, process_id));
109 }
110
111 // static
112 void TrackingSynchronizer::SetTrackingStatusInProcess(int process_id) {
113 // To find the process, or to send messages to the hosts, we need to be on the
114 // UI thread.
115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
116 160
117 bool enable = tracked_objects::ThreadData::tracking_status(); 161 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
118 162 if (!request)
119 content::RenderProcessHost* process =
120 content::RenderProcessHost::FromID(process_id);
121 // Ignore processes that don't have a connection, such as crashed tabs.
122 if (!process || !process->HasConnection())
123 return;
124 process->Send(new ChromeViewMsg_SetTrackingStatus(enable));
125 }
126
127 // static
128 void TrackingSynchronizer::DeserializeTrackingList(
129 int sequence_number,
130 const std::string& tracking_data,
131 ChildProcessInfo::ProcessType process_type) {
132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
133 BrowserThread::PostTask(
134 BrowserThread::UI, FROM_HERE,
135 base::Bind(
136 &TrackingSynchronizer::DeserializeTrackingListOnUI,
137 sequence_number, tracking_data, process_type));
138 }
139
140 // static
141 void TrackingSynchronizer::DeserializeTrackingListOnUI(
142 int sequence_number,
143 const std::string& tracking_data,
144 ChildProcessInfo::ProcessType process_type) {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
146
147 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer();
148 if (current_synchronizer == NULL)
149 return; 163 return;
150 164
151 base::Value* value = 165 DecrementPendingProcessesAndSendData(sequence_number, profiler_data);
152 base::JSONReader().JsonToValue(tracking_data, false, true);
153 DCHECK(value->GetType() == base::Value::TYPE_DICTIONARY);
154 base::DictionaryValue* dictionary_value =
155 static_cast<DictionaryValue*>(value);
156 dictionary_value->SetString(
157 "process_type", ChildProcessInfo::GetTypeNameInEnglish(process_type));
158
159 current_synchronizer->DecrementPendingProcessesAndSendData(
160 sequence_number, dictionary_value);
161 } 166 }
162 167
163 int TrackingSynchronizer::RegisterAndNotifyAllProcesses( 168 int TrackingSynchronizer::RegisterAndNotifyAllProcesses(
164 const base::WeakPtr<ProfilerUI>& callback_object) { 169 const base::WeakPtr<ProfilerUI>& callback_object) {
165 // To iterate over all processes, or to send messages to the hosts, we need
166 // to be on the UI thread.
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168 171
169 int sequence_number = GetNextAvailableSequenceNumber(); 172 int sequence_number = GetNextAvailableSequenceNumber();
170 173
171 // Initialize processes_pending with one because we are going to send 174 RequestContext* request =
172 // browser's ThreadData. 175 RequestContext::Register(sequence_number, callback_object);
173 RequestContext* request = new RequestContext(
174 callback_object, sequence_number, 1, TimeTicks::Now());
175 outstanding_requests_[sequence_number] = request;
176 176
177 DCHECK_GT(request->processes_pending_, 0); 177 // Increment pending process count for sending browser's profiler data.
178 for (content::RenderProcessHost::iterator it( 178 request->IncrementProcessesPending();
179 content::RenderProcessHost::AllHostsIterator());
180 !it.IsAtEnd(); it.Advance()) {
181 content::RenderProcessHost* render_process_host = it.GetCurrentValue();
182 DCHECK(render_process_host);
183 // Ignore processes that don't have a connection, such as crashed tabs.
184 if (!render_process_host->HasConnection())
185 continue;
186 179
187 ++request->processes_pending_; 180 // Get profiler data from renderer and browser child processes.
188 if (!render_process_host->Send( 181 int process_group_count = content::GetProfilerData(sequence_number);
189 new ChromeViewMsg_GetRendererTrackedData(sequence_number))) {
190 DecrementPendingProcesses(sequence_number);
191 }
192 }
193 182
194 // Get the ThreadData for the browser process and send it back. 183 request->AddProcessGroupCount(process_group_count);
184
185 // Send profiler_data from browser process.
195 base::DictionaryValue* value = tracked_objects::ThreadData::ToValue(); 186 base::DictionaryValue* value = tracked_objects::ThreadData::ToValue();
196 const std::string process_type = 187 const std::string process_type =
197 ChildProcessInfo::GetTypeNameInEnglish(ChildProcessInfo::BROWSER_PROCESS); 188 ChildProcessInfo::GetTypeNameInEnglish(ChildProcessInfo::BROWSER_PROCESS);
198 value->SetString("process_type", process_type); 189 value->SetString("process_type", process_type);
199 value->SetInteger("process_id", base::GetCurrentProcId()); 190 value->SetInteger("process_id", base::GetCurrentProcId());
200 DCHECK_GT(request->processes_pending_, 0);
201 DecrementPendingProcessesAndSendData(sequence_number, value); 191 DecrementPendingProcessesAndSendData(sequence_number, value);
202 192
203 return sequence_number; 193 return sequence_number;
204 } 194 }
205 195
206 void TrackingSynchronizer::DecrementPendingProcessesAndSendData( 196 void TrackingSynchronizer::DecrementPendingProcessesAndSendData(
207 int sequence_number, 197 int sequence_number,
208 base::DictionaryValue* value) { 198 base::DictionaryValue* value) {
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210 200
211 RequestContextMap::iterator it = 201 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
212 outstanding_requests_.find(sequence_number); 202 if (!request) {
213 if (it == outstanding_requests_.end()) {
214 delete value; 203 delete value;
215 return; 204 return;
216 } 205 }
217 206
218 RequestContext* request = NULL;
219 request = it->second;
220 DCHECK(sequence_number == request->sequence_number_);
221
222 if (value && request->callback_object_) { 207 if (value && request->callback_object_) {
223 // Transfers ownership of |value| to |callback_object_|. 208 // Transfers ownership of |value| to |callback_object_|.
224 request->callback_object_->ReceivedData(value); 209 request->callback_object_->ReceivedData(value);
225 } else { 210 } else {
226 delete value; 211 delete value;
227 } 212 }
228 213
229 if (--request->processes_pending_ <= 0) 214 // Delete request if we have heard back from all child processes.
230 ForceTrackingSynchronizationDoneCallback(sequence_number); 215 request->DecrementProcessesPending();
231 } 216 request->DeleteIfAllDone();
232
233 void TrackingSynchronizer::DecrementPendingProcesses(int sequence_number) {
234 DecrementPendingProcessesAndSendData(sequence_number, NULL);
235 }
236
237 void TrackingSynchronizer::ForceTrackingSynchronizationDoneCallback(
238 int sequence_number) {
239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
240
241 int unresponsive_processes;
242 RequestContextMap::iterator it =
243 outstanding_requests_.find(sequence_number);
244 if (it == outstanding_requests_.end())
245 return;
246
247 RequestContext* request = it->second;
248
249 DCHECK(sequence_number == request->sequence_number_);
250
251 unresponsive_processes = request->processes_pending_;
252
253 delete it->second;
254 outstanding_requests_.erase(it);
255
256 UMA_HISTOGRAM_COUNTS("Tracking.ProcessNotRespondingAsynchronous",
257 unresponsive_processes);
258 } 217 }
259 218
260 int TrackingSynchronizer::GetNextAvailableSequenceNumber() { 219 int TrackingSynchronizer::GetNextAvailableSequenceNumber() {
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
262 221
263 ++last_used_sequence_number_; 222 ++last_used_sequence_number_;
264 223
265 // Watch out for wrapping to a negative number. 224 // Watch out for wrapping to a negative number.
266 if (last_used_sequence_number_ < 0) 225 if (last_used_sequence_number_ < 0)
267 last_used_sequence_number_ = 1; 226 last_used_sequence_number_ = 1;
268 return last_used_sequence_number_; 227 return last_used_sequence_number_;
269 } 228 }
270 229
271 // static 230 // static
272 TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL; 231 TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() {
232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
233 DCHECK(tracking_synchronizer_ != NULL);
234 return tracking_synchronizer_;
235 }
236
237 // RequestContext methods and members.
238 //
239 // static
240 base::LazyInstance<RequestContext::RequestContextMap>
241 RequestContext::outstanding_requests_ = LAZY_INSTANCE_INITIALIZER;
242
243 RequestContext::RequestContext(const base::WeakPtr<ProfilerUI>& callback_object,
244 int sequence_number)
245 : callback_object_(callback_object),
246 sequence_number_(sequence_number),
247 process_group_count_(0),
248 processes_pending_(0) {
249 }
250
251 RequestContext::~RequestContext() {
252 }
253
254 void RequestContext::AddProcessGroupCount(int process_group_count) {
255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256 process_group_count_ += process_group_count;
257 }
258
259 void RequestContext::DecrementProcessGroupCount() {
260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
261 --process_group_count_;
262 }
263
264 void RequestContext::IncrementProcessesPending() {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266 ++processes_pending_;
267 }
268
269 void RequestContext::AddProcessesPending(int processes_pending) {
270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
271 processes_pending_ += processes_pending;
272 }
273
274 void RequestContext::DecrementProcessesPending() {
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
276 --processes_pending_;
277 }
278
279 void RequestContext::DeleteIfAllDone() {
280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
281
282 if (processes_pending_ <= 0 && process_group_count_ <= 0) {
283 RequestContext::Unregister(sequence_number_);
284 }
285 }
286
287 // static
288 RequestContext* RequestContext::Register(
289 int sequence_number,
290 const base::WeakPtr<ProfilerUI>& callback_object) {
291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292
293 RequestContext* request = new RequestContext(
294 callback_object, sequence_number);
295 outstanding_requests_.Get()[sequence_number] = request;
296
297 return request;
298 }
299
300 // static
301 RequestContext* RequestContext::GetRequestContext(
302 int sequence_number) {
303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
304
305 RequestContextMap::iterator it =
306 outstanding_requests_.Get().find(sequence_number);
307 if (it == outstanding_requests_.Get().end())
308 return NULL;
309
310 RequestContext* request = NULL;
311 request = it->second;
312 DCHECK(sequence_number == request->sequence_number_);
313 return request;
314 }
315
316 // static
317 void RequestContext::Unregister(int sequence_number) {
318 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
319
320 RequestContextMap::iterator it =
321 outstanding_requests_.Get().find(sequence_number);
322 if (it == outstanding_requests_.Get().end())
323 return;
324
325 RequestContext* request = it->second;
326 DCHECK(sequence_number == request->sequence_number_);
327 int process_group_count = request->process_group_count_;
328 int unresponsive_processes = request->processes_pending_;
329
330 delete it->second;
331 outstanding_requests_.Get().erase(it);
332
333 UMA_HISTOGRAM_COUNTS("Profiling.ProcessGroupCount",
334 process_group_count);
335 UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding",
336 unresponsive_processes);
337 }
338
339 // static
340 void RequestContext::OnShutdown() {
341 // Just in case we have any pending tasks, clear them out.
342 while (!outstanding_requests_.Get().empty()) {
343 RequestContextMap::iterator it = outstanding_requests_.Get().begin();
344 delete it->second;
345 outstanding_requests_.Get().erase(it);
346 }
347 }
273 348
274 } // namespace chrome_browser_metrics 349 } // namespace chrome_browser_metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698