Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / tracing / tracing_ui.cc
blob66ae8e8fe4ac6fcaa20feca8ba768d5526a3fc85
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"
7 #include <set>
8 #include <string>
9 #include <vector>
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"
38 namespace content {
39 namespace {
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());
51 callback.Run(res);
54 bool GetTracingOptions(const std::string& data64,
55 base::trace_event::CategoryFilter* category_filter,
56 base::trace_event::TraceOptions* tracing_options) {
57 std::string data;
58 if (!base::Base64Decode(data64, &data)) {
59 LOG(ERROR) << "Options were not base64 encoded.";
60 return false;
63 scoped_ptr<base::Value> optionsRaw(base::JSONReader::Read(data));
64 if (!optionsRaw) {
65 LOG(ERROR) << "Options were not valid JSON";
66 return false;
68 base::DictionaryValue* options;
69 if (!optionsRaw->GetAsDictionary(&options)) {
70 LOG(ERROR) << "Options must be dict";
71 return false;
74 if (!category_filter) {
75 LOG(ERROR) << "category_filter can't be passed as NULL";
76 return false;
79 if (!tracing_options) {
80 LOG(ERROR) << "tracing_options can't be passed as NULL";
81 return false;
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 options_ok &= options->GetBoolean("useSystemTracing",
90 &tracing_options->enable_systrace);
91 options_ok &=
92 options->GetBoolean("useSampling", &tracing_options->enable_sampling);
94 bool use_continuous_tracing;
95 options_ok &=
96 options->GetBoolean("useContinuousTracing", &use_continuous_tracing);
98 if (use_continuous_tracing)
99 tracing_options->record_mode = base::trace_event::RECORD_CONTINUOUSLY;
100 else
101 tracing_options->record_mode = base::trace_event::RECORD_UNTIL_FULL;
103 if (!options_ok) {
104 LOG(ERROR) << "Malformed options";
105 return false;
107 return true;
110 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback);
112 bool BeginRecording(const std::string& data64,
113 const WebUIDataSource::GotDataCallback& callback) {
114 base::trace_event::CategoryFilter category_filter("");
115 base::trace_event::TraceOptions tracing_options;
116 if (!GetTracingOptions(data64, &category_filter, &tracing_options))
117 return false;
119 return TracingController::GetInstance()->EnableRecording(
120 category_filter,
121 tracing_options,
122 base::Bind(&OnRecordingEnabledAck, callback));
125 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
126 base::RefCountedString* res = new base::RefCountedString();
127 callback.Run(res);
130 void OnTraceBufferUsageResult(const WebUIDataSource::GotDataCallback& callback,
131 float percent_full,
132 size_t approximate_event_count) {
133 std::string str = base::DoubleToString(percent_full);
134 callback.Run(base::RefCountedString::TakeString(&str));
137 void OnTraceBufferStatusResult(const WebUIDataSource::GotDataCallback& callback,
138 float percent_full,
139 size_t approximate_event_count) {
140 scoped_ptr<base::DictionaryValue> status(new base::DictionaryValue());
141 status->SetDouble("percentFull", percent_full);
142 status->SetInteger("approximateEventCount", approximate_event_count);
144 std::string status_json;
145 base::JSONWriter::Write(status.get(), &status_json);
147 base::RefCountedString* status_base64 = new base::RefCountedString();
148 base::Base64Encode(status_json, &status_base64->data());
149 callback.Run(status_base64);
152 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback);
154 bool EnableMonitoring(const std::string& data64,
155 const WebUIDataSource::GotDataCallback& callback) {
156 base::trace_event::TraceOptions tracing_options;
157 base::trace_event::CategoryFilter category_filter("");
158 if (!GetTracingOptions(data64, &category_filter, &tracing_options))
159 return false;
161 return TracingController::GetInstance()->EnableMonitoring(
162 category_filter,
163 tracing_options,
164 base::Bind(OnMonitoringEnabledAck, callback));
167 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
168 base::RefCountedString* res = new base::RefCountedString();
169 callback.Run(res);
172 void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback& callback) {
173 base::RefCountedString* res = new base::RefCountedString();
174 callback.Run(res);
177 void GetMonitoringStatus(const WebUIDataSource::GotDataCallback& callback) {
178 bool is_monitoring;
179 base::trace_event::CategoryFilter category_filter("");
180 base::trace_event::TraceOptions options;
181 TracingController::GetInstance()->GetMonitoringStatus(
182 &is_monitoring, &category_filter, &options);
184 scoped_ptr<base::DictionaryValue>
185 monitoring_options(new base::DictionaryValue());
186 monitoring_options->SetBoolean("isMonitoring", is_monitoring);
187 monitoring_options->SetString("categoryFilter", category_filter.ToString());
188 monitoring_options->SetBoolean("useSystemTracing", options.enable_systrace);
189 monitoring_options->SetBoolean(
190 "useContinuousTracing",
191 options.record_mode == base::trace_event::RECORD_CONTINUOUSLY);
192 monitoring_options->SetBoolean("useSampling", options.enable_sampling);
194 std::string monitoring_options_json;
195 base::JSONWriter::Write(monitoring_options.get(), &monitoring_options_json);
197 base::RefCountedString* monitoring_options_base64 =
198 new base::RefCountedString();
199 base::Base64Encode(monitoring_options_json,
200 &monitoring_options_base64->data());
201 callback.Run(monitoring_options_base64);
204 void TracingCallbackWrapper(const WebUIDataSource::GotDataCallback& callback,
205 base::RefCountedString* data) {
206 callback.Run(data);
209 bool OnBeginJSONRequest(const std::string& path,
210 const WebUIDataSource::GotDataCallback& callback) {
211 if (path == "json/categories") {
212 return TracingController::GetInstance()->GetCategories(
213 base::Bind(OnGotCategories, callback));
216 const char* beginRecordingPath = "json/begin_recording?";
217 if (StartsWithASCII(path, beginRecordingPath, true)) {
218 std::string data = path.substr(strlen(beginRecordingPath));
219 return BeginRecording(data, callback);
221 if (path == "json/get_buffer_percent_full") {
222 return TracingController::GetInstance()->GetTraceBufferUsage(
223 base::Bind(OnTraceBufferUsageResult, callback));
225 if (path == "json/get_buffer_status") {
226 return TracingController::GetInstance()->GetTraceBufferUsage(
227 base::Bind(OnTraceBufferStatusResult, callback));
229 if (path == "json/end_recording") {
230 return TracingController::GetInstance()->DisableRecording(
231 TracingControllerImpl::CreateStringSink(
232 base::Bind(TracingCallbackWrapper, callback)));
235 const char* enableMonitoringPath = "json/begin_monitoring?";
236 if (path.find(enableMonitoringPath) == 0) {
237 std::string data = path.substr(strlen(enableMonitoringPath));
238 return EnableMonitoring(data, callback);
240 if (path == "json/end_monitoring") {
241 return TracingController::GetInstance()->DisableMonitoring(
242 base::Bind(OnMonitoringDisabled, callback));
244 if (path == "json/capture_monitoring") {
245 TracingController::GetInstance()->CaptureMonitoringSnapshot(
246 TracingControllerImpl::CreateStringSink(
247 base::Bind(TracingCallbackWrapper, callback)));
248 return true;
250 if (path == "json/get_monitoring_status") {
251 GetMonitoringStatus(callback);
252 return true;
255 LOG(ERROR) << "Unhandled request to " << path;
256 return false;
259 bool OnTracingRequest(const std::string& path,
260 const WebUIDataSource::GotDataCallback& callback) {
261 if (StartsWithASCII(path, "json/", true)) {
262 if (!OnBeginJSONRequest(path, callback)) {
263 std::string error("##ERROR##");
264 callback.Run(base::RefCountedString::TakeString(&error));
266 return true;
268 return false;
271 } // namespace
274 ////////////////////////////////////////////////////////////////////////////////
276 // TracingUI
278 ////////////////////////////////////////////////////////////////////////////////
280 TracingUI::TracingUI(WebUI* web_ui)
281 : WebUIController(web_ui),
282 delegate_(GetContentClient()->browser()->GetTracingDelegate()),
283 weak_factory_(this) {
284 web_ui->RegisterMessageCallback(
285 "doUpload",
286 base::Bind(&TracingUI::DoUpload, base::Unretained(this)));
288 // Set up the chrome://tracing/ source.
289 BrowserContext* browser_context =
290 web_ui->GetWebContents()->GetBrowserContext();
292 WebUIDataSource* source = WebUIDataSource::Create(kChromeUITracingHost);
293 source->SetJsonPath("strings.js");
294 source->SetDefaultResource(IDR_TRACING_HTML);
295 source->AddResourcePath("tracing.js", IDR_TRACING_JS);
296 source->SetRequestFilter(base::Bind(OnTracingRequest));
297 WebUIDataSource::Add(browser_context, source);
298 TracingControllerImpl::GetInstance()->RegisterTracingUI(this);
301 TracingUI::~TracingUI() {
302 TracingControllerImpl::GetInstance()->UnregisterTracingUI(this);
305 void TracingUI::OnMonitoringStateChanged(bool is_monitoring) {
306 web_ui()->CallJavascriptFunction(
307 "onMonitoringStateChanged", base::FundamentalValue(is_monitoring));
310 void TracingUI::DoUpload(const base::ListValue* args) {
311 std::string file_contents;
312 if (!args || args->empty() || !args->GetString(0, &file_contents)) {
313 web_ui()->CallJavascriptFunction("onUploadError",
314 base::StringValue("Missing data"));
315 return;
318 if (!delegate_) {
319 web_ui()->CallJavascriptFunction("onUploadError",
320 base::StringValue("Not implemented"));
321 return;
324 if (trace_uploader_) {
325 web_ui()->CallJavascriptFunction("onUploadError",
326 base::StringValue("Upload in progress"));
327 return;
330 TraceUploader::UploadProgressCallback progress_callback =
331 base::Bind(&TracingUI::OnTraceUploadProgress,
332 weak_factory_.GetWeakPtr());
333 TraceUploader::UploadDoneCallback done_callback =
334 base::Bind(&TracingUI::OnTraceUploadComplete,
335 weak_factory_.GetWeakPtr());
337 trace_uploader_ = delegate_->GetTraceUploader(
338 web_ui()->GetWebContents()->GetBrowserContext()->GetRequestContext());
339 DCHECK(trace_uploader_);
340 trace_uploader_->DoUpload(file_contents, progress_callback, done_callback);
341 // TODO(mmandlis): Add support for stopping the upload in progress.
344 void TracingUI::OnTraceUploadProgress(int64 current, int64 total) {
345 DCHECK(current <= total);
346 int percent = (current / total) * 100;
347 web_ui()->CallJavascriptFunction(
348 "onUploadProgress",
349 base::FundamentalValue(percent),
350 base::StringValue(base::StringPrintf("%" PRId64, current)),
351 base::StringValue(base::StringPrintf("%" PRId64, total)));
354 void TracingUI::OnTraceUploadComplete(bool success,
355 const std::string& feedback) {
356 if (success) {
357 web_ui()->CallJavascriptFunction("onUploadComplete",
358 base::StringValue(feedback));
359 } else {
360 web_ui()->CallJavascriptFunction("onUploadError",
361 base::StringValue(feedback));
363 trace_uploader_.reset();
366 } // namespace content