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/command_line.h"
15 #include "base/format_macros.h"
16 #include "base/json/json_reader.h"
17 #include "base/json/json_writer.h"
18 #include "base/memory/scoped_ptr.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/trace_event/trace_event.h"
24 #include "base/values.h"
25 #include "content/browser/tracing/grit/tracing_resources.h"
26 #include "content/browser/tracing/tracing_controller_impl.h"
27 #include "content/public/browser/browser_context.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/content_browser_client.h"
30 #include "content/public/browser/trace_uploader.h"
31 #include "content/public/browser/tracing_controller.h"
32 #include "content/public/browser/tracing_delegate.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/browser/web_ui.h"
35 #include "content/public/browser/web_ui_data_source.h"
36 #include "content/public/common/content_client.h"
37 #include "content/public/common/url_constants.h"
42 void OnGotCategories(const WebUIDataSource::GotDataCallback
& callback
,
43 const std::set
<std::string
>& categorySet
) {
44 base::ListValue category_list
;
45 for (std::set
<std::string
>::const_iterator it
= categorySet
.begin();
46 it
!= categorySet
.end(); it
++) {
47 category_list
.AppendString(*it
);
50 base::RefCountedString
* res
= new base::RefCountedString();
51 base::JSONWriter::Write(category_list
, &res
->data());
55 bool GetTracingOptions(const std::string
& data64
,
56 base::trace_event::TraceConfig
* trace_config
) {
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";
75 LOG(ERROR
) << "trace_config can't be passed as NULL";
79 bool options_ok
= true;
80 std::string category_filter_string
;
81 options_ok
&= options
->GetString("categoryFilter", &category_filter_string
);
83 std::string record_mode
;
84 options_ok
&= options
->GetString("tracingRecordMode", &record_mode
);
86 *trace_config
= base::trace_event::TraceConfig(category_filter_string
,
90 options_ok
&= options
->GetBoolean("useSystemTracing", &enable_systrace
);
92 trace_config
->EnableSystrace();
95 options_ok
&= options
->GetBoolean("useSampling", &enable_sampling
);
97 trace_config
->EnableSampling();
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::TraceConfig
trace_config("", "");
111 if (!GetTracingOptions(data64
, &trace_config
))
114 return TracingController::GetInstance()->EnableRecording(
116 base::Bind(&OnRecordingEnabledAck
, callback
));
119 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback
& callback
) {
120 base::RefCountedString
* res
= new base::RefCountedString();
124 void OnTraceBufferUsageResult(const WebUIDataSource::GotDataCallback
& callback
,
126 size_t approximate_event_count
) {
127 std::string str
= base::DoubleToString(percent_full
);
128 callback
.Run(base::RefCountedString::TakeString(&str
));
131 void OnTraceBufferStatusResult(const WebUIDataSource::GotDataCallback
& callback
,
133 size_t approximate_event_count
) {
134 base::DictionaryValue status
;
135 status
.SetDouble("percentFull", percent_full
);
136 status
.SetInteger("approximateEventCount", approximate_event_count
);
138 std::string status_json
;
139 base::JSONWriter::Write(status
, &status_json
);
141 base::RefCountedString
* status_base64
= new base::RefCountedString();
142 base::Base64Encode(status_json
, &status_base64
->data());
143 callback
.Run(status_base64
);
146 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback
& callback
);
148 bool EnableMonitoring(const std::string
& data64
,
149 const WebUIDataSource::GotDataCallback
& callback
) {
150 base::trace_event::TraceConfig
trace_config("", "");
151 if (!GetTracingOptions(data64
, &trace_config
))
154 return TracingController::GetInstance()->EnableMonitoring(
156 base::Bind(OnMonitoringEnabledAck
, callback
));
159 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback
& callback
) {
160 base::RefCountedString
* res
= new base::RefCountedString();
164 void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback
& callback
) {
165 base::RefCountedString
* res
= new base::RefCountedString();
169 void GetMonitoringStatus(const WebUIDataSource::GotDataCallback
& callback
) {
171 base::trace_event::TraceConfig
config("", "");
172 TracingController::GetInstance()->GetMonitoringStatus(
173 &is_monitoring
, &config
);
175 base::DictionaryValue monitoring_options
;
176 monitoring_options
.SetBoolean("isMonitoring", is_monitoring
);
177 monitoring_options
.SetString("categoryFilter",
178 config
.ToCategoryFilterString());
179 monitoring_options
.SetBoolean("useSystemTracing", config
.IsSystraceEnabled());
180 monitoring_options
.SetBoolean(
181 "useContinuousTracing",
182 config
.GetTraceRecordMode() == base::trace_event::RECORD_CONTINUOUSLY
);
183 monitoring_options
.SetBoolean("useSampling", config
.IsSamplingEnabled());
185 std::string monitoring_options_json
;
186 base::JSONWriter::Write(monitoring_options
, &monitoring_options_json
);
188 base::RefCountedString
* monitoring_options_base64
=
189 new base::RefCountedString();
190 base::Base64Encode(monitoring_options_json
,
191 &monitoring_options_base64
->data());
192 callback
.Run(monitoring_options_base64
);
195 void TracingCallbackWrapperBase64(
196 const WebUIDataSource::GotDataCallback
& callback
,
197 base::RefCountedString
* data
) {
198 base::RefCountedString
* data_base64
= new base::RefCountedString();
199 base::Base64Encode(data
->data(), &data_base64
->data());
200 callback
.Run(data_base64
);
203 std::string
GenerateMetadataJSON() {
204 // Serialize metadata to json.
205 scoped_ptr
<base::DictionaryValue
> metadata_dict(new base::DictionaryValue());
206 metadata_dict
->SetString("version", GetContentClient()->GetProduct());
207 metadata_dict
->SetString(
209 base::CommandLine::ForCurrentProcess()->GetCommandLineString());
212 if (base::JSONWriter::Write(*metadata_dict
.get(), &results
))
214 return std::string();
217 bool OnBeginJSONRequest(const std::string
& path
,
218 const WebUIDataSource::GotDataCallback
& callback
) {
219 if (path
== "json/categories") {
220 return TracingController::GetInstance()->GetCategories(
221 base::Bind(OnGotCategories
, callback
));
224 const char* beginRecordingPath
= "json/begin_recording?";
225 if (base::StartsWith(path
, beginRecordingPath
,
226 base::CompareCase::SENSITIVE
)) {
227 std::string data
= path
.substr(strlen(beginRecordingPath
));
228 return BeginRecording(data
, callback
);
230 if (path
== "json/get_buffer_percent_full") {
231 return TracingController::GetInstance()->GetTraceBufferUsage(
232 base::Bind(OnTraceBufferUsageResult
, callback
));
234 if (path
== "json/get_buffer_status") {
235 return TracingController::GetInstance()->GetTraceBufferUsage(
236 base::Bind(OnTraceBufferStatusResult
, callback
));
238 if (path
== "json/end_recording_compressed") {
239 scoped_refptr
<TracingControllerImpl::TraceDataSink
> data_sink
=
240 TracingController::CreateCompressedStringSink(
241 TracingController::CreateCallbackEndpoint(
242 base::Bind(TracingCallbackWrapperBase64
, callback
)));
243 data_sink
->SetMetadata(GenerateMetadataJSON());
244 return TracingController::GetInstance()->DisableRecording(data_sink
);
247 const char* enableMonitoringPath
= "json/begin_monitoring?";
248 if (path
.find(enableMonitoringPath
) == 0) {
249 std::string data
= path
.substr(strlen(enableMonitoringPath
));
250 return EnableMonitoring(data
, callback
);
252 if (path
== "json/end_monitoring") {
253 return TracingController::GetInstance()->DisableMonitoring(
254 base::Bind(OnMonitoringDisabled
, callback
));
256 if (path
== "json/capture_monitoring_compressed") {
257 scoped_refptr
<TracingControllerImpl::TraceDataSink
> data_sink
=
258 TracingController::CreateCompressedStringSink(
259 TracingController::CreateCallbackEndpoint(
260 base::Bind(TracingCallbackWrapperBase64
, callback
)));
261 data_sink
->SetMetadata(GenerateMetadataJSON());
262 TracingController::GetInstance()->CaptureMonitoringSnapshot(data_sink
);
265 if (path
== "json/get_monitoring_status") {
266 GetMonitoringStatus(callback
);
270 LOG(ERROR
) << "Unhandled request to " << path
;
274 bool OnTracingRequest(const std::string
& path
,
275 const WebUIDataSource::GotDataCallback
& callback
) {
276 if (base::StartsWith(path
, "json/", base::CompareCase::SENSITIVE
)) {
277 if (!OnBeginJSONRequest(path
, callback
)) {
278 std::string
error("##ERROR##");
279 callback
.Run(base::RefCountedString::TakeString(&error
));
289 ////////////////////////////////////////////////////////////////////////////////
293 ////////////////////////////////////////////////////////////////////////////////
295 TracingUI::TracingUI(WebUI
* web_ui
)
296 : WebUIController(web_ui
),
297 delegate_(GetContentClient()->browser()->GetTracingDelegate()),
298 weak_factory_(this) {
299 web_ui
->RegisterMessageCallback(
301 base::Bind(&TracingUI::DoUpload
, base::Unretained(this)));
302 web_ui
->RegisterMessageCallback(
304 base::Bind(&TracingUI::DoUploadBase64Encoded
, base::Unretained(this)));
306 // Set up the chrome://tracing/ source.
307 BrowserContext
* browser_context
=
308 web_ui
->GetWebContents()->GetBrowserContext();
310 WebUIDataSource
* source
= WebUIDataSource::Create(kChromeUITracingHost
);
311 source
->SetJsonPath("strings.js");
312 source
->SetDefaultResource(IDR_TRACING_HTML
);
313 source
->AddResourcePath("tracing.js", IDR_TRACING_JS
);
314 source
->SetRequestFilter(base::Bind(OnTracingRequest
));
315 WebUIDataSource::Add(browser_context
, source
);
316 TracingControllerImpl::GetInstance()->RegisterTracingUI(this);
319 TracingUI::~TracingUI() {
320 TracingControllerImpl::GetInstance()->UnregisterTracingUI(this);
323 void TracingUI::OnMonitoringStateChanged(bool is_monitoring
) {
324 web_ui()->CallJavascriptFunction(
325 "onMonitoringStateChanged", base::FundamentalValue(is_monitoring
));
328 void TracingUI::DoUploadBase64Encoded(const base::ListValue
* args
) {
329 std::string file_contents_base64
;
330 if (!args
|| args
->empty() || !args
->GetString(0, &file_contents_base64
)) {
331 web_ui()->CallJavascriptFunction("onUploadError",
332 base::StringValue("Missing data"));
336 std::string file_contents
;
337 base::Base64Decode(file_contents_base64
, &file_contents
);
339 // doUploadBase64 is used to upload binary data which is assumed to already
341 DoUploadInternal(file_contents
, TraceUploader::UNCOMPRESSED_UPLOAD
);
344 void TracingUI::DoUpload(const base::ListValue
* args
) {
345 std::string file_contents
;
346 if (!args
|| args
->empty() || !args
->GetString(0, &file_contents
)) {
347 web_ui()->CallJavascriptFunction("onUploadError",
348 base::StringValue("Missing data"));
352 DoUploadInternal(file_contents
, TraceUploader::COMPRESSED_UPLOAD
);
355 void TracingUI::DoUploadInternal(const std::string
& file_contents
,
356 TraceUploader::UploadMode upload_mode
) {
358 web_ui()->CallJavascriptFunction("onUploadError",
359 base::StringValue("Not implemented"));
363 if (trace_uploader_
) {
364 web_ui()->CallJavascriptFunction("onUploadError",
365 base::StringValue("Upload in progress"));
369 TraceUploader::UploadProgressCallback progress_callback
=
370 base::Bind(&TracingUI::OnTraceUploadProgress
,
371 weak_factory_
.GetWeakPtr());
372 TraceUploader::UploadDoneCallback done_callback
=
373 base::Bind(&TracingUI::OnTraceUploadComplete
,
374 weak_factory_
.GetWeakPtr());
376 trace_uploader_
= delegate_
->GetTraceUploader(
377 web_ui()->GetWebContents()->GetBrowserContext()->GetRequestContext());
378 DCHECK(trace_uploader_
);
379 trace_uploader_
->DoUpload(file_contents
, upload_mode
, nullptr,
380 progress_callback
, done_callback
);
381 // TODO(mmandlis): Add support for stopping the upload in progress.
384 void TracingUI::OnTraceUploadProgress(int64 current
, int64 total
) {
385 DCHECK(current
<= total
);
386 int percent
= (current
/ total
) * 100;
387 web_ui()->CallJavascriptFunction(
389 base::FundamentalValue(percent
),
390 base::StringValue(base::StringPrintf("%" PRId64
, current
)),
391 base::StringValue(base::StringPrintf("%" PRId64
, total
)));
394 void TracingUI::OnTraceUploadComplete(bool success
,
395 const std::string
& feedback
) {
397 web_ui()->CallJavascriptFunction("onUploadComplete",
398 base::StringValue(feedback
));
400 web_ui()->CallJavascriptFunction("onUploadError",
401 base::StringValue(feedback
));
403 trace_uploader_
.reset();
406 } // namespace content