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

Side by Side Diff: content/browser/tracing/tracing_ui.cc

Issue 12194007: Revert 180055: Very likely caused http://crbug.com/173885 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 7 years, 10 months 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
(Empty)
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.
4
5 #include "content/browser/tracing/tracing_ui.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/command_line.h"
12 #include "base/debug/trace_event.h"
13 #include "base/file_util.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/string_number_conversions.h"
16 #include "base/stringprintf.h"
17 #include "base/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/content_browser_client.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "content/public/browser/trace_controller.h"
23 #include "content/public/browser/trace_subscriber.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/browser/web_contents_view.h"
26 #include "content/public/browser/web_ui.h"
27 #include "content/public/browser/web_ui_data_source.h"
28 #include "content/public/browser/web_ui_message_handler.h"
29 #include "content/public/common/url_constants.h"
30 #include "grit/content_resources.h"
31 #include "ipc/ipc_channel.h"
32 #include "ui/shell_dialogs/select_file_dialog.h"
33
34 #if defined(OS_CHROMEOS)
35 #include "chromeos/dbus/dbus_thread_manager.h"
36 #include "chromeos/dbus/debug_daemon_client.h"
37 #endif
38
39 namespace content {
40 namespace {
41
42 WebUIDataSource* CreateTracingHTMLSource() {
43 WebUIDataSource* source =
44 WebUIDataSource::Create(chrome::kChromeUITracingHost);
45
46 source->SetJsonPath("strings.js");
47 source->SetDefaultResource(IDR_TRACING_HTML);
48 source->AddResourcePath("tracing.js", IDR_TRACING_JS);
49 source->AddLocalizedString("tracingTitle", IDS_TRACING_TITLE);
50 return source;
51 }
52
53 // This class receives javascript messages from the renderer.
54 // Note that the WebUI infrastructure runs on the UI thread, therefore all of
55 // this class's methods are expected to run on the UI thread.
56 class TracingMessageHandler
57 : public WebUIMessageHandler,
58 public ui::SelectFileDialog::Listener,
59 public base::SupportsWeakPtr<TracingMessageHandler>,
60 public TraceSubscriber {
61 public:
62 TracingMessageHandler();
63 virtual ~TracingMessageHandler();
64
65 // WebUIMessageHandler implementation.
66 virtual void RegisterMessages();
67
68 // SelectFileDialog::Listener implementation
69 virtual void FileSelected(const FilePath& path, int index, void* params);
70 virtual void FileSelectionCanceled(void* params);
71
72 // TraceSubscriber implementation.
73 virtual void OnEndTracingComplete();
74 virtual void OnTraceDataCollected(
75 const scoped_refptr<base::RefCountedString>& trace_fragment);
76 virtual void OnTraceBufferPercentFullReply(float percent_full);
77
78 // Messages.
79 void OnTracingControllerInitialized(const ListValue* list);
80 void OnBeginTracing(const ListValue* list);
81 void OnEndTracingAsync(const ListValue* list);
82 void OnBeginRequestBufferPercentFull(const ListValue* list);
83 void OnLoadTraceFile(const ListValue* list);
84 void OnSaveTraceFile(const ListValue* list);
85
86 // Callbacks.
87 void LoadTraceFileComplete(string16* file_contents);
88 void SaveTraceFileComplete();
89
90 private:
91 // The file dialog to select a file for loading or saving traces.
92 scoped_refptr<ui::SelectFileDialog> select_trace_file_dialog_;
93
94 // The type of the file dialog as the same one is used for loading or saving
95 // traces.
96 ui::SelectFileDialog::Type select_trace_file_dialog_type_;
97
98 // The trace data that is to be written to the file on saving.
99 scoped_ptr<std::string> trace_data_to_save_;
100
101 // True while tracing is active.
102 bool trace_enabled_;
103
104 // True while system tracing is active.
105 bool system_trace_in_progress_;
106
107 void OnEndSystemTracingAck(
108 const scoped_refptr<base::RefCountedString>& events_str_ptr);
109
110 DISALLOW_COPY_AND_ASSIGN(TracingMessageHandler);
111 };
112
113 // A proxy passed to the Read and Write tasks used when loading or saving trace
114 // data.
115 class TaskProxy : public base::RefCountedThreadSafe<TaskProxy> {
116 public:
117 explicit TaskProxy(const base::WeakPtr<TracingMessageHandler>& handler)
118 : handler_(handler) {}
119 void LoadTraceFileCompleteProxy(string16* file_contents) {
120 if (handler_)
121 handler_->LoadTraceFileComplete(file_contents);
122 delete file_contents;
123 }
124
125 void SaveTraceFileCompleteProxy() {
126 if (handler_)
127 handler_->SaveTraceFileComplete();
128 }
129
130 private:
131 friend class base::RefCountedThreadSafe<TaskProxy>;
132 ~TaskProxy() {}
133
134 // The message handler to call callbacks on.
135 base::WeakPtr<TracingMessageHandler> handler_;
136
137 DISALLOW_COPY_AND_ASSIGN(TaskProxy);
138 };
139
140 ////////////////////////////////////////////////////////////////////////////////
141 //
142 // TracingMessageHandler
143 //
144 ////////////////////////////////////////////////////////////////////////////////
145
146 TracingMessageHandler::TracingMessageHandler()
147 : select_trace_file_dialog_type_(ui::SelectFileDialog::SELECT_NONE),
148 trace_enabled_(false),
149 system_trace_in_progress_(false) {
150 }
151
152 TracingMessageHandler::~TracingMessageHandler() {
153 if (select_trace_file_dialog_)
154 select_trace_file_dialog_->ListenerDestroyed();
155
156 // If we are the current subscriber, this will result in ending tracing.
157 TraceController::GetInstance()->CancelSubscriber(this);
158
159 // Shutdown any system tracing too.
160 if (system_trace_in_progress_) {
161 #if defined(OS_CHROMEOS)
162 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
163 RequestStopSystemTracing(
164 chromeos::DebugDaemonClient::EmptyStopSystemTracingCallback());
165 #endif
166 }
167 }
168
169 void TracingMessageHandler::RegisterMessages() {
170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
171
172 web_ui()->RegisterMessageCallback("tracingControllerInitialized",
173 base::Bind(&TracingMessageHandler::OnTracingControllerInitialized,
174 base::Unretained(this)));
175 web_ui()->RegisterMessageCallback("beginTracing",
176 base::Bind(&TracingMessageHandler::OnBeginTracing,
177 base::Unretained(this)));
178 web_ui()->RegisterMessageCallback("endTracingAsync",
179 base::Bind(&TracingMessageHandler::OnEndTracingAsync,
180 base::Unretained(this)));
181 web_ui()->RegisterMessageCallback("beginRequestBufferPercentFull",
182 base::Bind(&TracingMessageHandler::OnBeginRequestBufferPercentFull,
183 base::Unretained(this)));
184 web_ui()->RegisterMessageCallback("loadTraceFile",
185 base::Bind(&TracingMessageHandler::OnLoadTraceFile,
186 base::Unretained(this)));
187 web_ui()->RegisterMessageCallback("saveTraceFile",
188 base::Bind(&TracingMessageHandler::OnSaveTraceFile,
189 base::Unretained(this)));
190 }
191
192 void TracingMessageHandler::OnTracingControllerInitialized(
193 const ListValue* args) {
194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195
196 // Send the client info to the tracingController
197 {
198 scoped_ptr<DictionaryValue> dict(new DictionaryValue());
199 dict->SetString("version", GetContentClient()->GetProduct());
200
201 dict->SetString("command_line",
202 CommandLine::ForCurrentProcess()->GetCommandLineString());
203
204 web_ui()->CallJavascriptFunction("tracingController.onClientInfoUpdate",
205 *dict);
206 }
207 }
208
209 void TracingMessageHandler::OnBeginRequestBufferPercentFull(
210 const ListValue* list) {
211 TraceController::GetInstance()->GetTraceBufferPercentFullAsync(this);
212 }
213
214 // A callback used for asynchronously reading a file to a string. Calls the
215 // TaskProxy callback when reading is complete.
216 void ReadTraceFileCallback(TaskProxy* proxy, const FilePath& path) {
217 std::string file_contents;
218 if (!file_util::ReadFileToString(path, &file_contents))
219 return;
220
221 // We need to escape the file contents, because it will go into a javascript
222 // quoted string in TracingMessageHandler::LoadTraceFileComplete. We need to
223 // escape control characters (to have well-formed javascript statements), as
224 // well as \ and ' (the only special characters in a ''-quoted string).
225 // Do the escaping on this thread, it may take a little while for big files
226 // and we don't want to block the UI during that time. Also do the UTF-16
227 // conversion here.
228 // Note: we're using UTF-16 because we'll need to cut the string into slices
229 // to give to Javascript, and it's easier to cut than UTF-8 (since JS strings
230 // are arrays of 16-bit values, UCS-2 really, whereas we can't cut inside of a
231 // multibyte UTF-8 codepoint).
232 size_t size = file_contents.size();
233 std::string escaped_contents;
234 escaped_contents.reserve(size);
235 for (size_t i = 0; i < size; ++i) {
236 char c = file_contents[i];
237 if (c < ' ') {
238 escaped_contents += base::StringPrintf("\\u%04x", c);
239 continue;
240 }
241 if (c == '\\' || c == '\'')
242 escaped_contents.push_back('\\');
243 escaped_contents.push_back(c);
244 }
245 file_contents.clear();
246
247 scoped_ptr<string16> contents16(new string16);
248 UTF8ToUTF16(escaped_contents).swap(*contents16);
249
250 BrowserThread::PostTask(
251 BrowserThread::UI, FROM_HERE,
252 base::Bind(&TaskProxy::LoadTraceFileCompleteProxy, proxy,
253 contents16.release()));
254 }
255
256 // A callback used for asynchronously writing a file from a string. Calls the
257 // TaskProxy callback when writing is complete.
258 void WriteTraceFileCallback(TaskProxy* proxy,
259 const FilePath& path,
260 std::string* contents) {
261 if (!file_util::WriteFile(path, contents->c_str(), contents->size()))
262 return;
263
264 BrowserThread::PostTask(
265 BrowserThread::UI, FROM_HERE,
266 base::Bind(&TaskProxy::SaveTraceFileCompleteProxy, proxy));
267 }
268
269 void TracingMessageHandler::FileSelected(
270 const FilePath& path, int index, void* params) {
271 if (select_trace_file_dialog_type_ ==
272 ui::SelectFileDialog::SELECT_OPEN_FILE) {
273 BrowserThread::PostTask(
274 BrowserThread::FILE, FROM_HERE,
275 base::Bind(&ReadTraceFileCallback,
276 make_scoped_refptr(new TaskProxy(AsWeakPtr())), path));
277 } else {
278 BrowserThread::PostTask(
279 BrowserThread::FILE, FROM_HERE,
280 base::Bind(&WriteTraceFileCallback,
281 make_scoped_refptr(new TaskProxy(AsWeakPtr())), path,
282 trace_data_to_save_.release()));
283 }
284
285 select_trace_file_dialog_ = NULL;
286 }
287
288 void TracingMessageHandler::FileSelectionCanceled(void* params) {
289 select_trace_file_dialog_ = NULL;
290 if (select_trace_file_dialog_type_ ==
291 ui::SelectFileDialog::SELECT_OPEN_FILE) {
292 web_ui()->CallJavascriptFunction(
293 "tracingController.onLoadTraceFileCanceled");
294 } else {
295 web_ui()->CallJavascriptFunction(
296 "tracingController.onSaveTraceFileCanceled");
297 }
298 }
299
300 void TracingMessageHandler::OnLoadTraceFile(const ListValue* list) {
301 // Only allow a single dialog at a time.
302 if (select_trace_file_dialog_.get())
303 return;
304 select_trace_file_dialog_type_ = ui::SelectFileDialog::SELECT_OPEN_FILE;
305 select_trace_file_dialog_ = ui::SelectFileDialog::Create(
306 this,
307 GetContentClient()->browser()->CreateSelectFilePolicy(
308 web_ui()->GetWebContents()));
309 select_trace_file_dialog_->SelectFile(
310 ui::SelectFileDialog::SELECT_OPEN_FILE,
311 string16(),
312 FilePath(),
313 NULL, 0, FILE_PATH_LITERAL(""),
314 web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(), NULL);
315 }
316
317 void TracingMessageHandler::LoadTraceFileComplete(string16* contents) {
318 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
319
320 // We need to pass contents to tracingController.onLoadTraceFileComplete, but
321 // that may be arbitrarily big, and IPCs messages are limited in size. So we
322 // need to cut it into pieces and rebuild the string in Javascript.
323 // |contents| has already been escaped in ReadTraceFileCallback.
324 // IPC::Channel::kMaximumMessageSize is in bytes, and we need to account for
325 // overhead.
326 const size_t kMaxSize = IPC::Channel::kMaximumMessageSize / 2 - 128;
327 string16 first_prefix = UTF8ToUTF16("window.traceData = '");
328 string16 prefix = UTF8ToUTF16("window.traceData += '");
329 string16 suffix = UTF8ToUTF16("';");
330
331 RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost();
332 for (size_t i = 0; i < contents->size(); i += kMaxSize) {
333 string16 javascript = i == 0 ? first_prefix : prefix;
334 javascript += contents->substr(i, kMaxSize) + suffix;
335 rvh->ExecuteJavascriptInWebFrame(string16(), javascript);
336 }
337 rvh->ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(
338 "tracingController.onLoadTraceFileComplete(JSON.parse(window.traceData));"
339 "delete window.traceData;"));
340 }
341
342 void TracingMessageHandler::OnSaveTraceFile(const ListValue* list) {
343 // Only allow a single dialog at a time.
344 if (select_trace_file_dialog_.get())
345 return;
346
347 DCHECK(list->GetSize() == 1);
348
349 std::string* trace_data = new std::string();
350 bool ok = list->GetString(0, trace_data);
351 DCHECK(ok);
352 trace_data_to_save_.reset(trace_data);
353
354 select_trace_file_dialog_type_ = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
355 select_trace_file_dialog_ = ui::SelectFileDialog::Create(
356 this,
357 GetContentClient()->browser()->CreateSelectFilePolicy(
358 web_ui()->GetWebContents()));
359 select_trace_file_dialog_->SelectFile(
360 ui::SelectFileDialog::SELECT_SAVEAS_FILE,
361 string16(),
362 FilePath(),
363 NULL, 0, FILE_PATH_LITERAL(""),
364 web_ui()->GetWebContents()->GetView()->GetTopLevelNativeWindow(), NULL);
365 }
366
367 void TracingMessageHandler::SaveTraceFileComplete() {
368 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
369 web_ui()->CallJavascriptFunction("tracingController.onSaveTraceFileComplete");
370 }
371
372 void TracingMessageHandler::OnBeginTracing(const ListValue* args) {
373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
374 DCHECK_EQ(args->GetSize(), (size_t) 2);
375
376 bool system_tracing_requested = false;
377 bool ok = args->GetBoolean(0, &system_tracing_requested);
378 DCHECK(ok);
379
380 std::string chrome_categories;
381 ok = args->GetString(1, &chrome_categories);
382 DCHECK(ok);
383
384 trace_enabled_ = true;
385 // TODO(jbates) This may fail, but that's OK for current use cases.
386 // Ex: Multiple about:gpu traces can not trace simultaneously.
387 // TODO(nduca) send feedback to javascript about whether or not BeginTracing
388 // was successful.
389 TraceController::GetInstance()->BeginTracing(this, chrome_categories);
390
391 if (system_tracing_requested) {
392 #if defined(OS_CHROMEOS)
393 DCHECK(!system_trace_in_progress_);
394 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
395 StartSystemTracing();
396 // TODO(sleffler) async, could wait for completion
397 system_trace_in_progress_ = true;
398 #endif
399 }
400 }
401
402 void TracingMessageHandler::OnEndTracingAsync(const ListValue* list) {
403 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
404
405 // TODO(nduca): fix javascript code to make sure trace_enabled_ is always true
406 // here. triggered a false condition by just clicking stop
407 // trace a few times when it was going slow, and maybe switching
408 // between tabs.
409 if (trace_enabled_ &&
410 !TraceController::GetInstance()->EndTracingAsync(this)) {
411 // Set to false now, since it turns out we never were the trace subscriber.
412 OnEndTracingComplete();
413 }
414 }
415
416 void TracingMessageHandler::OnEndTracingComplete() {
417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
418 trace_enabled_ = false;
419 if (system_trace_in_progress_) {
420 // Disable system tracing now that the local trace has shutdown.
421 // This must be done last because we potentially need to push event
422 // records into the system event log for synchronizing system event
423 // timestamps with chrome event timestamps--and since the system event
424 // log is a ring-buffer (on linux) adding them at the end is the only
425 // way we're confident we'll have them in the final result.
426 system_trace_in_progress_ = false;
427 #if defined(OS_CHROMEOS)
428 chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
429 RequestStopSystemTracing(
430 base::Bind(&TracingMessageHandler::OnEndSystemTracingAck,
431 base::Unretained(this)));
432 return;
433 #endif
434 }
435 web_ui()->CallJavascriptFunction("tracingController.onEndTracingComplete");
436 }
437
438 void TracingMessageHandler::OnEndSystemTracingAck(
439 const scoped_refptr<base::RefCountedString>& events_str_ptr) {
440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
441
442 web_ui()->CallJavascriptFunction(
443 "tracingController.onSystemTraceDataCollected",
444 *scoped_ptr<Value>(Value::CreateStringValue(events_str_ptr->data())));
445 DCHECK(!system_trace_in_progress_);
446
447 OnEndTracingComplete();
448 }
449
450 void TracingMessageHandler::OnTraceDataCollected(
451 const scoped_refptr<base::RefCountedString>& trace_fragment) {
452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
453
454 base::debug::TraceResultBuffer::SimpleOutput output;
455 base::debug::TraceResultBuffer trace_buffer;
456 trace_buffer.SetOutputCallback(output.GetCallback());
457 output.Append("tracingController.onTraceDataCollected(");
458 trace_buffer.Start();
459 trace_buffer.AddFragment(trace_fragment->data());
460 trace_buffer.Finish();
461 output.Append(");");
462
463 web_ui()->GetWebContents()->GetRenderViewHost()->
464 ExecuteJavascriptInWebFrame(string16(), UTF8ToUTF16(output.json_output));
465 }
466
467 void TracingMessageHandler::OnTraceBufferPercentFullReply(float percent_full) {
468 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
469 web_ui()->CallJavascriptFunction(
470 "tracingController.onRequestBufferPercentFullComplete",
471 *scoped_ptr<Value>(Value::CreateDoubleValue(percent_full)));
472 }
473
474 } // namespace
475
476
477 ////////////////////////////////////////////////////////////////////////////////
478 //
479 // TracingUI
480 //
481 ////////////////////////////////////////////////////////////////////////////////
482
483 TracingUI::TracingUI(WebUI* web_ui) : WebUIController(web_ui) {
484 web_ui->AddMessageHandler(new TracingMessageHandler());
485
486 // Set up the chrome://tracing/ source.
487 BrowserContext* browser_context =
488 web_ui->GetWebContents()->GetBrowserContext();
489 WebUIDataSource::Add(browser_context, CreateTracingHTMLSource());
490 }
491
492 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/tracing/tracing_ui.h ('k') | content/browser/webui/content_web_ui_controller_factory.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698