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 "content/browser/tracing/tracing_ui.h"
11 #include "base/base64.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/format_macros.h"
15 #include "base/json/json_reader.h"
16 #include "base/json/json_writer.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/trace_event/trace_event.h"
23 #include "base/values.h"
24 #include "content/browser/tracing/grit/tracing_resources.h"
25 #include "content/browser/tracing/tracing_controller_impl.h"
26 #include "content/public/browser/browser_context.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/content_browser_client.h"
29 #include "content/public/browser/trace_uploader.h"
30 #include "content/public/browser/tracing_controller.h"
31 #include "content/public/browser/tracing_delegate.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/browser/web_ui.h"
34 #include "content/public/browser/web_ui_data_source.h"
35 #include "content/public/common/content_client.h"
36 #include "content/public/common/url_constants.h"
41 void OnGotCategories(const WebUIDataSource::GotDataCallback
& callback
,
42 const std::set
<std::string
>& categorySet
) {
43 scoped_ptr
<base::ListValue
> category_list(new base::ListValue());
44 for (std::set
<std::string
>::const_iterator it
= categorySet
.begin();
45 it
!= categorySet
.end(); it
++) {
46 category_list
->AppendString(*it
);
49 base::RefCountedString
* res
= new base::RefCountedString();
50 base::JSONWriter::Write(category_list
.get(), &res
->data());
54 bool GetTracingOptions(const std::string
& data64
,
55 base::trace_event::CategoryFilter
* category_filter
,
56 base::trace_event::TraceOptions
* tracing_options
) {
58 if (!base::Base64Decode(data64
, &data
)) {
59 LOG(ERROR
) << "Options were not base64 encoded.";
63 scoped_ptr
<base::Value
> optionsRaw(base::JSONReader::Read(data
));
65 LOG(ERROR
) << "Options were not valid JSON";
68 base::DictionaryValue
* options
;
69 if (!optionsRaw
->GetAsDictionary(&options
)) {
70 LOG(ERROR
) << "Options must be dict";
74 if (!category_filter
) {
75 LOG(ERROR
) << "category_filter can't be passed as NULL";
79 if (!tracing_options
) {
80 LOG(ERROR
) << "tracing_options can't be passed as NULL";
84 bool options_ok
= true;
85 std::string category_filter_string
;
86 options_ok
&= options
->GetString("categoryFilter", &category_filter_string
);
87 *category_filter
= base::trace_event::CategoryFilter(category_filter_string
);
89 std::string record_mode
;
91 options
->GetString("tracingRecordMode", &record_mode
);
92 options_ok
&= tracing_options
->SetFromString(record_mode
);
94 options_ok
&= options
->GetBoolean("useSystemTracing",
95 &tracing_options
->enable_systrace
);
97 options
->GetBoolean("useSampling", &tracing_options
->enable_sampling
);
100 LOG(ERROR
) << "Malformed options";
106 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback
& callback
);
108 bool BeginRecording(const std::string
& data64
,
109 const WebUIDataSource::GotDataCallback
& callback
) {
110 base::trace_event::CategoryFilter
category_filter("");
111 base::trace_event::TraceOptions tracing_options
;
112 if (!GetTracingOptions(data64
, &category_filter
, &tracing_options
))
115 return TracingController::GetInstance()->EnableRecording(
118 base::Bind(&OnRecordingEnabledAck
, callback
));
121 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback
& callback
) {
122 base::RefCountedString
* res
= new base::RefCountedString();
126 void OnTraceBufferUsageResult(const WebUIDataSource::GotDataCallback
& callback
,
128 size_t approximate_event_count
) {
129 std::string str
= base::DoubleToString(percent_full
);
130 callback
.Run(base::RefCountedString::TakeString(&str
));
133 void OnTraceBufferStatusResult(const WebUIDataSource::GotDataCallback
& callback
,
135 size_t approximate_event_count
) {
136 scoped_ptr
<base::DictionaryValue
> status(new base::DictionaryValue());
137 status
->SetDouble("percentFull", percent_full
);
138 status
->SetInteger("approximateEventCount", approximate_event_count
);
140 std::string status_json
;
141 base::JSONWriter::Write(status
.get(), &status_json
);
143 base::RefCountedString
* status_base64
= new base::RefCountedString();
144 base::Base64Encode(status_json
, &status_base64
->data());
145 callback
.Run(status_base64
);
148 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback
& callback
);
150 bool EnableMonitoring(const std::string
& data64
,
151 const WebUIDataSource::GotDataCallback
& callback
) {
152 base::trace_event::TraceOptions tracing_options
;
153 base::trace_event::CategoryFilter
category_filter("");
154 if (!GetTracingOptions(data64
, &category_filter
, &tracing_options
))
157 return TracingController::GetInstance()->EnableMonitoring(
160 base::Bind(OnMonitoringEnabledAck
, callback
));
163 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback
& callback
) {
164 base::RefCountedString
* res
= new base::RefCountedString();
168 void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback
& callback
) {
169 base::RefCountedString
* res
= new base::RefCountedString();
173 void GetMonitoringStatus(const WebUIDataSource::GotDataCallback
& callback
) {
175 base::trace_event::CategoryFilter
category_filter("");
176 base::trace_event::TraceOptions options
;
177 TracingController::GetInstance()->GetMonitoringStatus(
178 &is_monitoring
, &category_filter
, &options
);
180 scoped_ptr
<base::DictionaryValue
>
181 monitoring_options(new base::DictionaryValue());
182 monitoring_options
->SetBoolean("isMonitoring", is_monitoring
);
183 monitoring_options
->SetString("categoryFilter", category_filter
.ToString());
184 monitoring_options
->SetBoolean("useSystemTracing", options
.enable_systrace
);
185 monitoring_options
->SetBoolean(
186 "useContinuousTracing",
187 options
.record_mode
== base::trace_event::RECORD_CONTINUOUSLY
);
188 monitoring_options
->SetBoolean("useSampling", options
.enable_sampling
);
190 std::string monitoring_options_json
;
191 base::JSONWriter::Write(monitoring_options
.get(), &monitoring_options_json
);
193 base::RefCountedString
* monitoring_options_base64
=
194 new base::RefCountedString();
195 base::Base64Encode(monitoring_options_json
,
196 &monitoring_options_base64
->data());
197 callback
.Run(monitoring_options_base64
);
200 void TracingCallbackWrapper(const WebUIDataSource::GotDataCallback
& callback
,
201 base::RefCountedString
* data
) {
205 bool OnBeginJSONRequest(const std::string
& path
,
206 const WebUIDataSource::GotDataCallback
& callback
) {
207 if (path
== "json/categories") {
208 return TracingController::GetInstance()->GetCategories(
209 base::Bind(OnGotCategories
, callback
));
212 const char* beginRecordingPath
= "json/begin_recording?";
213 if (StartsWithASCII(path
, beginRecordingPath
, true)) {
214 std::string data
= path
.substr(strlen(beginRecordingPath
));
215 return BeginRecording(data
, callback
);
217 if (path
== "json/get_buffer_percent_full") {
218 return TracingController::GetInstance()->GetTraceBufferUsage(
219 base::Bind(OnTraceBufferUsageResult
, callback
));
221 if (path
== "json/get_buffer_status") {
222 return TracingController::GetInstance()->GetTraceBufferUsage(
223 base::Bind(OnTraceBufferStatusResult
, callback
));
225 if (path
== "json/end_recording") {
226 return TracingController::GetInstance()->DisableRecording(
227 TracingControllerImpl::CreateStringSink(
228 base::Bind(TracingCallbackWrapper
, callback
)));
231 const char* enableMonitoringPath
= "json/begin_monitoring?";
232 if (path
.find(enableMonitoringPath
) == 0) {
233 std::string data
= path
.substr(strlen(enableMonitoringPath
));
234 return EnableMonitoring(data
, callback
);
236 if (path
== "json/end_monitoring") {
237 return TracingController::GetInstance()->DisableMonitoring(
238 base::Bind(OnMonitoringDisabled
, callback
));
240 if (path
== "json/capture_monitoring") {
241 TracingController::GetInstance()->CaptureMonitoringSnapshot(
242 TracingControllerImpl::CreateStringSink(
243 base::Bind(TracingCallbackWrapper
, callback
)));
246 if (path
== "json/get_monitoring_status") {
247 GetMonitoringStatus(callback
);
251 LOG(ERROR
) << "Unhandled request to " << path
;
255 bool OnTracingRequest(const std::string
& path
,
256 const WebUIDataSource::GotDataCallback
& callback
) {
257 if (StartsWithASCII(path
, "json/", true)) {
258 if (!OnBeginJSONRequest(path
, callback
)) {
259 std::string
error("##ERROR##");
260 callback
.Run(base::RefCountedString::TakeString(&error
));
270 ////////////////////////////////////////////////////////////////////////////////
274 ////////////////////////////////////////////////////////////////////////////////
276 TracingUI::TracingUI(WebUI
* web_ui
)
277 : WebUIController(web_ui
),
278 delegate_(GetContentClient()->browser()->GetTracingDelegate()),
279 weak_factory_(this) {
280 web_ui
->RegisterMessageCallback(
282 base::Bind(&TracingUI::DoUpload
, base::Unretained(this)));
284 // Set up the chrome://tracing/ source.
285 BrowserContext
* browser_context
=
286 web_ui
->GetWebContents()->GetBrowserContext();
288 WebUIDataSource
* source
= WebUIDataSource::Create(kChromeUITracingHost
);
289 source
->SetJsonPath("strings.js");
290 source
->SetDefaultResource(IDR_TRACING_HTML
);
291 source
->AddResourcePath("tracing.js", IDR_TRACING_JS
);
292 source
->SetRequestFilter(base::Bind(OnTracingRequest
));
293 WebUIDataSource::Add(browser_context
, source
);
294 TracingControllerImpl::GetInstance()->RegisterTracingUI(this);
297 TracingUI::~TracingUI() {
298 TracingControllerImpl::GetInstance()->UnregisterTracingUI(this);
301 void TracingUI::OnMonitoringStateChanged(bool is_monitoring
) {
302 web_ui()->CallJavascriptFunction(
303 "onMonitoringStateChanged", base::FundamentalValue(is_monitoring
));
306 void TracingUI::DoUpload(const base::ListValue
* args
) {
307 std::string file_contents
;
308 if (!args
|| args
->empty() || !args
->GetString(0, &file_contents
)) {
309 web_ui()->CallJavascriptFunction("onUploadError",
310 base::StringValue("Missing data"));
315 web_ui()->CallJavascriptFunction("onUploadError",
316 base::StringValue("Not implemented"));
320 if (trace_uploader_
) {
321 web_ui()->CallJavascriptFunction("onUploadError",
322 base::StringValue("Upload in progress"));
326 TraceUploader::UploadProgressCallback progress_callback
=
327 base::Bind(&TracingUI::OnTraceUploadProgress
,
328 weak_factory_
.GetWeakPtr());
329 TraceUploader::UploadDoneCallback done_callback
=
330 base::Bind(&TracingUI::OnTraceUploadComplete
,
331 weak_factory_
.GetWeakPtr());
333 trace_uploader_
= delegate_
->GetTraceUploader(
334 web_ui()->GetWebContents()->GetBrowserContext()->GetRequestContext());
335 DCHECK(trace_uploader_
);
336 trace_uploader_
->DoUpload(file_contents
, progress_callback
, done_callback
);
337 // TODO(mmandlis): Add support for stopping the upload in progress.
340 void TracingUI::OnTraceUploadProgress(int64 current
, int64 total
) {
341 DCHECK(current
<= total
);
342 int percent
= (current
/ total
) * 100;
343 web_ui()->CallJavascriptFunction(
345 base::FundamentalValue(percent
),
346 base::StringValue(base::StringPrintf("%" PRId64
, current
)),
347 base::StringValue(base::StringPrintf("%" PRId64
, total
)));
350 void TracingUI::OnTraceUploadComplete(bool success
,
351 const std::string
& feedback
) {
353 web_ui()->CallJavascriptFunction("onUploadComplete",
354 base::StringValue(feedback
));
356 web_ui()->CallJavascriptFunction("onUploadError",
357 base::StringValue(feedback
));
359 trace_uploader_
.reset();
362 } // namespace content