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

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"
15 #include "content/public/browser/profiler_synchronizer.h"
19 16
20 using base::TimeTicks; 17 using base::TimeTicks;
21 using content::BrowserThread; 18 using content::BrowserThread;
22 19
23 namespace chrome_browser_metrics { 20 namespace chrome_browser_metrics {
24 21
25 // Negative numbers are never used as sequence numbers. We explicitly pick a 22 // 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 23 // 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. 24 // 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 25 // We have code that handles wrapping around on an overflow into negative
29 // territory. 26 // territory.
30 static const int kNeverUsableSequenceNumber = -2; 27 static const int kNeverUsableSequenceNumber = -2;
31 28
29 // TrackingSynchronizer methods and members.
30 //
31 // static
32 TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL;
33
32 TrackingSynchronizer::TrackingSynchronizer() 34 TrackingSynchronizer::TrackingSynchronizer()
33 : last_used_sequence_number_(kNeverUsableSequenceNumber) { 35 : last_used_sequence_number_(kNeverUsableSequenceNumber) {
34 DCHECK(tracking_synchronizer_ == NULL); 36 DCHECK(tracking_synchronizer_ == NULL);
35 tracking_synchronizer_ = this; 37 tracking_synchronizer_ = this;
38 content::ProfilerController::GetInstance()->Register(this);
36 } 39 }
37 40
38 TrackingSynchronizer::~TrackingSynchronizer() { 41 TrackingSynchronizer::~TrackingSynchronizer() {
42 content::ProfilerController::GetInstance()->Unregister(this);
43
39 // Just in case we have any pending tasks, clear them out. 44 // Just in case we have any pending tasks, clear them out.
40 while (!outstanding_requests_.empty()) { 45 RequestContext::OnShutdown();
41 RequestContextMap::iterator it = outstanding_requests_.begin();
42 delete it->second;
43 outstanding_requests_.erase(it);
44 }
45 46
46 tracking_synchronizer_ = NULL; 47 tracking_synchronizer_ = NULL;
47 } 48 }
48 49
49 // static 50 // static
50 TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() { 51 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) { 52 const base::WeakPtr<ProfilerUI>& callback_object) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
60 54
61 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer(); 55 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer();
62 if (current_synchronizer == NULL) { 56 if (current_synchronizer == NULL) {
63 // System teardown is happening. 57 // System teardown is happening.
64 return; 58 return;
65 } 59 }
66 60
67 int sequence_number = current_synchronizer->RegisterAndNotifyAllProcesses( 61 int sequence_number = current_synchronizer->RegisterAndNotifyAllProcesses(
68 callback_object); 62 callback_object);
69 63
70 // Post a task that would be called after waiting for wait_time. This acts 64 // 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. 65 // as a watchdog, to cancel the requests for non-responsive processes.
72 BrowserThread::PostDelayedTask( 66 BrowserThread::PostDelayedTask(
73 BrowserThread::UI, FROM_HERE, 67 BrowserThread::UI, FROM_HERE,
74 NewRunnableMethod( 68 base::Bind(&RequestContext::Unregister, sequence_number),
75 current_synchronizer,
76 &TrackingSynchronizer::ForceTrackingSynchronizationDoneCallback,
77 sequence_number),
78 60000); 69 60000);
79 } 70 }
80 71
81 // static 72 void TrackingSynchronizer::OnPendingProcesses(int sequence_number,
82 void TrackingSynchronizer::SetTrackingStatus(bool enable) { 73 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)); 74 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
86 75
87 for (content::RenderProcessHost::iterator it( 76 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
88 content::RenderProcessHost::AllHostsIterator()); 77 if (!request)
89 !it.IsAtEnd(); it.Advance()) { 78 return;
90 content::RenderProcessHost* render_process_host = it.GetCurrentValue(); 79 request->AddProcessesPending(pending_processes);
91 DCHECK(render_process_host); 80 request->DecrementProcessGroupCount();
92 // Ignore processes that don't have a connection, such as crashed tabs. 81 request->DeleteIfAllDone();
93 if (!render_process_host->HasConnection())
94 continue;
95
96 render_process_host->Send(new ChromeViewMsg_SetTrackingStatus(enable));
97 }
98 } 82 }
99 83
100 // static 84 void TrackingSynchronizer::OnProfilerDataCollected(
101 void TrackingSynchronizer::IsTrackingEnabled(int process_id) { 85 int sequence_number,
86 const base::DictionaryValue& profiler_data) {
102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
103 // To find the process, or to send messages to the hosts, we need to be on the 88 base::DictionaryValue* dictionary_value = new base::DictionaryValue;
104 // UI thread. 89 dictionary_value->MergeDictionary(&profiler_data);
105 BrowserThread::PostTask( 90 BrowserThread::PostTask(
106 BrowserThread::UI, FROM_HERE, 91 BrowserThread::UI, FROM_HERE,
107 base::Bind( 92 base::Bind(&TrackingSynchronizer::OnProfilerDataCollectedOnUI,
108 &TrackingSynchronizer::SetTrackingStatusInProcess, process_id)); 93 base::Unretained(this),
94 sequence_number,
95 dictionary_value));
109 } 96 }
110 97
111 // static 98 void TrackingSynchronizer::OnProfilerDataCollectedOnUI(
112 void TrackingSynchronizer::SetTrackingStatusInProcess(int process_id) { 99 int sequence_number,
113 // To find the process, or to send messages to the hosts, we need to be on the 100 base::DictionaryValue* profiler_data) {
114 // UI thread.
115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
116 102
117 bool enable = tracked_objects::ThreadData::tracking_status(); 103 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
118 104 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; 105 return;
150 106
151 base::Value* value = 107 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 } 108 }
162 109
163 int TrackingSynchronizer::RegisterAndNotifyAllProcesses( 110 int TrackingSynchronizer::RegisterAndNotifyAllProcesses(
164 const base::WeakPtr<ProfilerUI>& callback_object) { 111 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)); 112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168 113
169 int sequence_number = GetNextAvailableSequenceNumber(); 114 int sequence_number = GetNextAvailableSequenceNumber();
170 115
171 // Initialize processes_pending with one because we are going to send 116 RequestContext* request =
172 // browser's ThreadData. 117 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 118
177 DCHECK_GT(request->processes_pending_, 0); 119 // Increment pending process count for sending browser's profiler data.
178 for (content::RenderProcessHost::iterator it( 120 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 121
187 ++request->processes_pending_; 122 // Get profiler data from renderer and browser child processes.
188 if (!render_process_host->Send( 123 int process_group_count = content::GetProfilerData(sequence_number);
189 new ChromeViewMsg_GetRendererTrackedData(sequence_number))) {
190 DecrementPendingProcesses(sequence_number);
191 }
192 }
193 124
194 // Get the ThreadData for the browser process and send it back. 125 request->AddProcessGroupCount(process_group_count);
126
127 // Send profiler_data from browser process.
195 base::DictionaryValue* value = tracked_objects::ThreadData::ToValue(); 128 base::DictionaryValue* value = tracked_objects::ThreadData::ToValue();
196 const std::string process_type = 129 const std::string process_type =
197 ChildProcessInfo::GetTypeNameInEnglish(ChildProcessInfo::BROWSER_PROCESS); 130 ChildProcessInfo::GetTypeNameInEnglish(ChildProcessInfo::BROWSER_PROCESS);
198 value->SetString("process_type", process_type); 131 value->SetString("process_type", process_type);
199 value->SetInteger("process_id", base::GetCurrentProcId()); 132 value->SetInteger("process_id", base::GetCurrentProcId());
200 DCHECK_GT(request->processes_pending_, 0);
201 DecrementPendingProcessesAndSendData(sequence_number, value); 133 DecrementPendingProcessesAndSendData(sequence_number, value);
202 134
203 return sequence_number; 135 return sequence_number;
204 } 136 }
205 137
206 void TrackingSynchronizer::DecrementPendingProcessesAndSendData( 138 void TrackingSynchronizer::DecrementPendingProcessesAndSendData(
207 int sequence_number, 139 int sequence_number,
208 base::DictionaryValue* value) { 140 base::DictionaryValue* value) {
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210 142
211 RequestContextMap::iterator it = 143 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
212 outstanding_requests_.find(sequence_number); 144 if (!request) {
213 if (it == outstanding_requests_.end()) {
214 delete value; 145 delete value;
215 return; 146 return;
216 } 147 }
217 148
218 RequestContext* request = NULL;
219 request = it->second;
220 DCHECK(sequence_number == request->sequence_number_);
221
222 if (value && request->callback_object_) { 149 if (value && request->callback_object_) {
223 // Transfers ownership of |value| to |callback_object_|. 150 // Transfers ownership of |value| to |callback_object_|.
224 request->callback_object_->ReceivedData(value); 151 request->callback_object_->ReceivedData(value);
225 } else { 152 } else {
226 delete value; 153 delete value;
227 } 154 }
228 155
229 if (--request->processes_pending_ <= 0) 156 // Delete request if we have heard back from all child processes.
230 ForceTrackingSynchronizationDoneCallback(sequence_number); 157 request->DecrementProcessesPending();
231 } 158 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 } 159 }
259 160
260 int TrackingSynchronizer::GetNextAvailableSequenceNumber() { 161 int TrackingSynchronizer::GetNextAvailableSequenceNumber() {
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
262 163
263 ++last_used_sequence_number_; 164 ++last_used_sequence_number_;
264 165
265 // Watch out for wrapping to a negative number. 166 // Watch out for wrapping to a negative number.
266 if (last_used_sequence_number_ < 0) 167 if (last_used_sequence_number_ < 0)
267 last_used_sequence_number_ = 1; 168 last_used_sequence_number_ = 1;
268 return last_used_sequence_number_; 169 return last_used_sequence_number_;
269 } 170 }
270 171
271 // static 172 // static
272 TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL; 173 TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() {
174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
175 DCHECK(tracking_synchronizer_ != NULL);
176 return tracking_synchronizer_;
177 }
178
179 // RequestContext methods and members.
180 //
181 // static
182 base::LazyInstance<RequestContext::RequestContextMap>
183 RequestContext::outstanding_requests_ = LAZY_INSTANCE_INITIALIZER;
184
185 RequestContext::RequestContext(const base::WeakPtr<ProfilerUI>& callback_object,
186 int sequence_number)
187 : callback_object_(callback_object),
188 sequence_number_(sequence_number),
189 process_group_count_(0),
190 processes_pending_(0) {
191 }
192
193 RequestContext::~RequestContext() {
194 }
195
196 void RequestContext::AddProcessGroupCount(int process_group_count) {
197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
198 process_group_count_ += process_group_count;
199 }
200
201 void RequestContext::DecrementProcessGroupCount() {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203 --process_group_count_;
204 }
205
206 void RequestContext::IncrementProcessesPending() {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
208 ++processes_pending_;
209 }
210
211 void RequestContext::AddProcessesPending(int processes_pending) {
212 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
213 processes_pending_ += processes_pending;
214 }
215
216 void RequestContext::DecrementProcessesPending() {
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
218 --processes_pending_;
219 }
220
221 void RequestContext::DeleteIfAllDone() {
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
223
224 if (processes_pending_ <= 0 && process_group_count_ <= 0) {
jam 2011/11/28 18:48:16 one more thing: if one of the processes crash, wil
ramant (doing other things) 2011/11/29 01:32:20 In FetchProfilerDataAsynchronously, we post a dela
225 RequestContext::Unregister(sequence_number_);
226 }
227 }
228
229 // static
230 RequestContext* RequestContext::Register(
231 int sequence_number,
232 const base::WeakPtr<ProfilerUI>& callback_object) {
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234
235 RequestContext* request = new RequestContext(
236 callback_object, sequence_number);
237 outstanding_requests_.Get()[sequence_number] = request;
238
239 return request;
240 }
241
242 // static
243 RequestContext* RequestContext::GetRequestContext(
244 int sequence_number) {
245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
246
247 RequestContextMap::iterator it =
248 outstanding_requests_.Get().find(sequence_number);
249 if (it == outstanding_requests_.Get().end())
250 return NULL;
251
252 RequestContext* request = NULL;
253 request = it->second;
254 DCHECK(sequence_number == request->sequence_number_);
255 return request;
256 }
257
258 // static
259 void RequestContext::Unregister(int sequence_number) {
260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
261
262 RequestContextMap::iterator it =
263 outstanding_requests_.Get().find(sequence_number);
264 if (it == outstanding_requests_.Get().end())
265 return;
266
267 RequestContext* request = it->second;
268 DCHECK(sequence_number == request->sequence_number_);
269 int process_group_count = request->process_group_count_;
270 int unresponsive_processes = request->processes_pending_;
271
272 delete it->second;
273 outstanding_requests_.Get().erase(it);
274
275 UMA_HISTOGRAM_COUNTS("Profiling.ProcessGroupCount",
276 process_group_count);
277 UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding",
278 unresponsive_processes);
279 }
280
281 // static
282 void RequestContext::OnShutdown() {
283 // Just in case we have any pending tasks, clear them out.
284 while (!outstanding_requests_.Get().empty()) {
285 RequestContextMap::iterator it = outstanding_requests_.Get().begin();
286 delete it->second;
287 outstanding_requests_.Get().erase(it);
288 }
289 }
273 290
274 } // namespace chrome_browser_metrics 291 } // namespace chrome_browser_metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698