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

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 #include "content/public/common/process_type.h" 15 #include "content/public/common/process_type.h"
20 16
21 using base::TimeTicks; 17 using base::TimeTicks;
22 using content::BrowserThread; 18 using content::BrowserThread;
23 19
24 namespace chrome_browser_metrics { 20 namespace chrome_browser_metrics {
25 21
22 // The "RequestContext" structure describes an individual request received
23 // from the UI. All methods are accessible on UI thread.
24 class RequestContext {
25 public:
26 // A map from sequence_number_ to the actual RequestContexts.
27 typedef std::map<int, RequestContext*> RequestContextMap;
28
29 ~RequestContext() {}
30
31 RequestContext(const base::WeakPtr<ProfilerUI>& callback_object,
32 int sequence_number)
33 : callback_object_(callback_object),
34 sequence_number_(sequence_number),
35 received_process_group_count_(0),
36 processes_pending_(0) {
37 }
38
39 void SetReceivedProcessGroupCount(bool done) {
40 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
41 received_process_group_count_ = done;
42 }
43
44 // Methods for book keeping of processes_pending_.
45 void IncrementProcessesPending() {
46 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
47 ++processes_pending_;
48 }
49
50 void AddProcessesPending(int processes_pending) {
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52 processes_pending_ += processes_pending;
53 }
54
55 void DecrementProcessesPending() {
56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
57 --processes_pending_;
58 }
59
60 // Records that we are waiting for one less tracking data from a process for
61 // the given sequence number. If |received_process_group_count_| and
62 // |processes_pending_| are zero, then delete the current object by calling
63 // Unregister.
64 void DeleteIfAllDone() {
65 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
66
67 if (processes_pending_ <= 0 && received_process_group_count_) {
68 RequestContext::Unregister(sequence_number_);
69 }
70 }
71
72
73 // Register |callback_object| in |outstanding_requests_| map for the given
74 // |sequence_number|.
75 static RequestContext* Register(
76 int sequence_number,
77 const base::WeakPtr<ProfilerUI>& callback_object) {
78 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
79
80 RequestContext* request = new RequestContext(
81 callback_object, sequence_number);
82 outstanding_requests_.Get()[sequence_number] = request;
83
84 return request;
85 }
86
87 // Find the |RequestContext| in |outstanding_requests_| map for the given
88 // |sequence_number|.
89 static RequestContext* GetRequestContext(int sequence_number) {
90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
91
92 RequestContextMap::iterator it =
93 outstanding_requests_.Get().find(sequence_number);
94 if (it == outstanding_requests_.Get().end())
95 return NULL;
96
97 RequestContext* request = NULL;
98 request = it->second;
99 DCHECK(sequence_number == request->sequence_number_);
100 return request;
101 }
102
103 // Delete the entry for the given sequence_number| from
104 // |outstanding_requests_| map. This method is called when all changes have
105 // been acquired, or when the wait time expires (whichever is sooner).
106 static void Unregister(int sequence_number) {
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
108
109 RequestContextMap::iterator it =
110 outstanding_requests_.Get().find(sequence_number);
111 if (it == outstanding_requests_.Get().end())
112 return;
113
114 RequestContext* request = it->second;
115 DCHECK(sequence_number == request->sequence_number_);
116 bool received_process_group_count = request->received_process_group_count_;
117 int unresponsive_processes = request->processes_pending_;
118
119 delete it->second;
120 outstanding_requests_.Get().erase(it);
121
122 UMA_HISTOGRAM_BOOLEAN("Profiling.ReceivedProcessGroupCount",
123 received_process_group_count);
124 UMA_HISTOGRAM_COUNTS("Profiling.PendingProcessNotResponding",
125 unresponsive_processes);
126 }
127
128
129 // Delete all the entries in |outstanding_requests_| map.
130 static void OnShutdown() {
131 // Just in case we have any pending tasks, clear them out.
132 while (!outstanding_requests_.Get().empty()) {
133 RequestContextMap::iterator it = outstanding_requests_.Get().begin();
134 delete it->second;
135 outstanding_requests_.Get().erase(it);
136 }
137 }
138
139 // Requests are made to asynchronously send data to the |callback_object_|.
140 base::WeakPtr<ProfilerUI> callback_object_;
141
142 // The sequence number used by the most recent update request to contact all
143 // processes.
144 int sequence_number_;
145
146 // Indicates if we have received all pending processes count.
147 bool received_process_group_count_;
148
149 // The number of pending processes (browser, all renderer processes and
150 // browser child processes) that have not yet responded to requests.
151 int processes_pending_;
152
153 // Map of all outstanding RequestContexts, from sequence_number_ to
154 // RequestContext.
155 static base::LazyInstance<RequestContextMap> outstanding_requests_;
156 };
157
26 // Negative numbers are never used as sequence numbers. We explicitly pick a 158 // Negative numbers are never used as sequence numbers. We explicitly pick a
27 // negative number that is "so negative" that even when we add one (as is done 159 // negative number that is "so negative" that even when we add one (as is done
28 // when we generated the next sequence number) that it will still be negative. 160 // when we generated the next sequence number) that it will still be negative.
29 // We have code that handles wrapping around on an overflow into negative 161 // We have code that handles wrapping around on an overflow into negative
30 // territory. 162 // territory.
31 static const int kNeverUsableSequenceNumber = -2; 163 static const int kNeverUsableSequenceNumber = -2;
32 164
165 // TrackingSynchronizer methods and members.
166 //
167 // static
168 TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL;
169
33 TrackingSynchronizer::TrackingSynchronizer() 170 TrackingSynchronizer::TrackingSynchronizer()
34 : last_used_sequence_number_(kNeverUsableSequenceNumber) { 171 : last_used_sequence_number_(kNeverUsableSequenceNumber) {
35 DCHECK(tracking_synchronizer_ == NULL); 172 DCHECK(tracking_synchronizer_ == NULL);
36 tracking_synchronizer_ = this; 173 tracking_synchronizer_ = this;
174 content::ProfilerController::GetInstance()->Register(this);
37 } 175 }
38 176
39 TrackingSynchronizer::~TrackingSynchronizer() { 177 TrackingSynchronizer::~TrackingSynchronizer() {
178 content::ProfilerController::GetInstance()->Unregister(this);
179
40 // Just in case we have any pending tasks, clear them out. 180 // Just in case we have any pending tasks, clear them out.
41 while (!outstanding_requests_.empty()) { 181 RequestContext::OnShutdown();
42 RequestContextMap::iterator it = outstanding_requests_.begin();
43 delete it->second;
44 outstanding_requests_.erase(it);
45 }
46 182
47 tracking_synchronizer_ = NULL; 183 tracking_synchronizer_ = NULL;
48 } 184 }
49 185
50 // static 186 // static
51 TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() { 187 void TrackingSynchronizer::FetchProfilerDataAsynchronously(
52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
53 DCHECK(tracking_synchronizer_ != NULL);
54 return tracking_synchronizer_;
55 }
56
57 // static
58 void TrackingSynchronizer::FetchTrackingDataAsynchronously(
59 const base::WeakPtr<ProfilerUI>& callback_object) { 188 const base::WeakPtr<ProfilerUI>& callback_object) {
60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
61 190
62 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer(); 191 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer();
63 if (current_synchronizer == NULL) { 192 if (current_synchronizer == NULL) {
64 // System teardown is happening. 193 // System teardown is happening.
65 return; 194 return;
66 } 195 }
67 196
68 int sequence_number = current_synchronizer->RegisterAndNotifyAllProcesses( 197 int sequence_number = current_synchronizer->RegisterAndNotifyAllProcesses(
69 callback_object); 198 callback_object);
70 199
71 // Post a task that would be called after waiting for wait_time. This acts 200 // Post a task that would be called after waiting for wait_time. This acts
72 // as a watchdog, to cancel the requests for non-responsive processes. 201 // as a watchdog, to cancel the requests for non-responsive processes.
73 BrowserThread::PostDelayedTask( 202 BrowserThread::PostDelayedTask(
74 BrowserThread::UI, FROM_HERE, 203 BrowserThread::UI, FROM_HERE,
75 NewRunnableMethod( 204 base::Bind(&RequestContext::Unregister, sequence_number),
76 current_synchronizer,
77 &TrackingSynchronizer::ForceTrackingSynchronizationDoneCallback,
78 sequence_number),
79 60000); 205 60000);
80 } 206 }
81 207
82 // static 208 void TrackingSynchronizer::OnPendingProcesses(int sequence_number,
83 void TrackingSynchronizer::SetTrackingStatus(bool enable) { 209 int pending_processes,
84 // To iterate over all processes, or to send messages to the hosts, we need 210 bool end) {
85 // to be on the UI thread.
86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
87 212
88 for (content::RenderProcessHost::iterator it( 213 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
89 content::RenderProcessHost::AllHostsIterator()); 214 if (!request)
90 !it.IsAtEnd(); it.Advance()) { 215 return;
91 content::RenderProcessHost* render_process_host = it.GetCurrentValue(); 216 request->AddProcessesPending(pending_processes);
92 DCHECK(render_process_host); 217 request->SetReceivedProcessGroupCount(end);
93 // Ignore processes that don't have a connection, such as crashed tabs. 218 request->DeleteIfAllDone();
94 if (!render_process_host->HasConnection())
95 continue;
96
97 render_process_host->Send(new ChromeViewMsg_SetTrackingStatus(enable));
98 }
99 } 219 }
100 220
101 // static 221 void TrackingSynchronizer::OnProfilerDataCollected(
102 void TrackingSynchronizer::IsTrackingEnabled(int process_id) { 222 int sequence_number,
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 223 base::DictionaryValue* profiler_data) {
104 // To find the process, or to send messages to the hosts, we need to be on the
105 // UI thread.
106 BrowserThread::PostTask(
107 BrowserThread::UI, FROM_HERE,
108 base::Bind(
109 &TrackingSynchronizer::SetTrackingStatusInProcess, process_id));
110 }
111
112 // static
113 void TrackingSynchronizer::SetTrackingStatusInProcess(int process_id) {
114 // To find the process, or to send messages to the hosts, we need to be on the
115 // UI thread.
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
117 225
118 bool enable = tracked_objects::ThreadData::tracking_status(); 226 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
119 227 if (!request)
120 content::RenderProcessHost* process =
121 content::RenderProcessHost::FromID(process_id);
122 // Ignore processes that don't have a connection, such as crashed tabs.
123 if (!process || !process->HasConnection())
124 return;
125 process->Send(new ChromeViewMsg_SetTrackingStatus(enable));
126 }
127
128 // static
129 void TrackingSynchronizer::DeserializeTrackingList(
130 int sequence_number,
131 const std::string& tracking_data,
132 content::ProcessType process_type) {
133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
134 BrowserThread::PostTask(
135 BrowserThread::UI, FROM_HERE,
136 base::Bind(
137 &TrackingSynchronizer::DeserializeTrackingListOnUI,
138 sequence_number, tracking_data, process_type));
139 }
140
141 // static
142 void TrackingSynchronizer::DeserializeTrackingListOnUI(
143 int sequence_number,
144 const std::string& tracking_data,
145 content::ProcessType process_type) {
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
147
148 TrackingSynchronizer* current_synchronizer = CurrentSynchronizer();
149 if (current_synchronizer == NULL)
150 return; 228 return;
151 229
152 base::Value* value = 230 DecrementPendingProcessesAndSendData(sequence_number, profiler_data);
153 base::JSONReader().JsonToValue(tracking_data, false, true);
154 DCHECK(value->GetType() == base::Value::TYPE_DICTIONARY);
155 base::DictionaryValue* dictionary_value =
156 static_cast<DictionaryValue*>(value);
157 dictionary_value->SetString(
158 "process_type", content::GetProcessTypeNameInEnglish(process_type));
159
160 current_synchronizer->DecrementPendingProcessesAndSendData(
161 sequence_number, dictionary_value);
162 } 231 }
163 232
164 int TrackingSynchronizer::RegisterAndNotifyAllProcesses( 233 int TrackingSynchronizer::RegisterAndNotifyAllProcesses(
165 const base::WeakPtr<ProfilerUI>& callback_object) { 234 const base::WeakPtr<ProfilerUI>& callback_object) {
166 // To iterate over all processes, or to send messages to the hosts, we need
167 // to be on the UI thread.
168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
169 236
170 int sequence_number = GetNextAvailableSequenceNumber(); 237 int sequence_number = GetNextAvailableSequenceNumber();
171 238
172 // Initialize processes_pending with one because we are going to send 239 RequestContext* request =
173 // browser's ThreadData. 240 RequestContext::Register(sequence_number, callback_object);
174 RequestContext* request = new RequestContext(
175 callback_object, sequence_number, 1, TimeTicks::Now());
176 outstanding_requests_[sequence_number] = request;
177 241
178 DCHECK_GT(request->processes_pending_, 0); 242 // Increment pending process count for sending browser's profiler data.
179 for (content::RenderProcessHost::iterator it( 243 request->IncrementProcessesPending();
180 content::RenderProcessHost::AllHostsIterator());
181 !it.IsAtEnd(); it.Advance()) {
182 content::RenderProcessHost* render_process_host = it.GetCurrentValue();
183 DCHECK(render_process_host);
184 // Ignore processes that don't have a connection, such as crashed tabs.
185 if (!render_process_host->HasConnection())
186 continue;
187 244
188 ++request->processes_pending_; 245 // Get profiler data from renderer and browser child processes.
189 if (!render_process_host->Send( 246 content::ProfilerController::GetInstance()->GetProfilerData(sequence_number);
190 new ChromeViewMsg_GetRendererTrackedData(sequence_number))) {
191 DecrementPendingProcesses(sequence_number);
192 }
193 }
194 247
195 // Get the ThreadData for the browser process and send it back. 248 // Send profiler_data from browser process.
196 base::DictionaryValue* value = tracked_objects::ThreadData::ToValue(); 249 base::DictionaryValue* value = tracked_objects::ThreadData::ToValue();
197 const std::string process_type = 250 const std::string process_type =
198 content::GetProcessTypeNameInEnglish(content::PROCESS_TYPE_BROWSER); 251 content::GetProcessTypeNameInEnglish(content::PROCESS_TYPE_BROWSER);
199 value->SetString("process_type", process_type); 252 value->SetString("process_type", process_type);
200 value->SetInteger("process_id", base::GetCurrentProcId()); 253 value->SetInteger("process_id", base::GetCurrentProcId());
201 DCHECK_GT(request->processes_pending_, 0);
202 DecrementPendingProcessesAndSendData(sequence_number, value); 254 DecrementPendingProcessesAndSendData(sequence_number, value);
203 255
204 return sequence_number; 256 return sequence_number;
205 } 257 }
206 258
207 void TrackingSynchronizer::DecrementPendingProcessesAndSendData( 259 void TrackingSynchronizer::DecrementPendingProcessesAndSendData(
208 int sequence_number, 260 int sequence_number,
209 base::DictionaryValue* value) { 261 base::DictionaryValue* value) {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
211 263
212 RequestContextMap::iterator it = 264 RequestContext* request = RequestContext::GetRequestContext(sequence_number);
213 outstanding_requests_.find(sequence_number); 265 if (!request) {
214 if (it == outstanding_requests_.end()) {
215 delete value; 266 delete value;
216 return; 267 return;
217 } 268 }
218 269
219 RequestContext* request = NULL;
220 request = it->second;
221 DCHECK(sequence_number == request->sequence_number_);
222
223 if (value && request->callback_object_) { 270 if (value && request->callback_object_) {
224 // Transfers ownership of |value| to |callback_object_|. 271 // Transfers ownership of |value| to |callback_object_|.
225 request->callback_object_->ReceivedData(value); 272 request->callback_object_->ReceivedData(value);
226 } else { 273 } else {
227 delete value; 274 delete value;
228 } 275 }
229 276
230 if (--request->processes_pending_ <= 0) 277 // Delete request if we have heard back from all child processes.
231 ForceTrackingSynchronizationDoneCallback(sequence_number); 278 request->DecrementProcessesPending();
232 } 279 request->DeleteIfAllDone();
233
234 void TrackingSynchronizer::DecrementPendingProcesses(int sequence_number) {
235 DecrementPendingProcessesAndSendData(sequence_number, NULL);
236 }
237
238 void TrackingSynchronizer::ForceTrackingSynchronizationDoneCallback(
239 int sequence_number) {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
241
242 int unresponsive_processes;
243 RequestContextMap::iterator it =
244 outstanding_requests_.find(sequence_number);
245 if (it == outstanding_requests_.end())
246 return;
247
248 RequestContext* request = it->second;
249
250 DCHECK(sequence_number == request->sequence_number_);
251
252 unresponsive_processes = request->processes_pending_;
253
254 delete it->second;
255 outstanding_requests_.erase(it);
256
257 UMA_HISTOGRAM_COUNTS("Tracking.ProcessNotRespondingAsynchronous",
258 unresponsive_processes);
259 } 280 }
260 281
261 int TrackingSynchronizer::GetNextAvailableSequenceNumber() { 282 int TrackingSynchronizer::GetNextAvailableSequenceNumber() {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
263 284
264 ++last_used_sequence_number_; 285 ++last_used_sequence_number_;
265 286
266 // Watch out for wrapping to a negative number. 287 // Watch out for wrapping to a negative number.
267 if (last_used_sequence_number_ < 0) 288 if (last_used_sequence_number_ < 0)
268 last_used_sequence_number_ = 1; 289 last_used_sequence_number_ = 1;
269 return last_used_sequence_number_; 290 return last_used_sequence_number_;
270 } 291 }
271 292
272 // static 293 // static
273 TrackingSynchronizer* TrackingSynchronizer::tracking_synchronizer_ = NULL; 294 TrackingSynchronizer* TrackingSynchronizer::CurrentSynchronizer() {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
296 DCHECK(tracking_synchronizer_ != NULL);
297 return tracking_synchronizer_;
298 }
299
300 // static
301 base::LazyInstance<RequestContext::RequestContextMap>
302 RequestContext::outstanding_requests_ = LAZY_INSTANCE_INITIALIZER;
274 303
275 } // namespace chrome_browser_metrics 304 } // namespace chrome_browser_metrics
OLDNEW
« no previous file with comments | « chrome/browser/metrics/tracking_synchronizer.h ('k') | chrome/browser/renderer_host/chrome_render_message_filter.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698