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/debug/trace_event.h"
16 #include "base/format_macros.h"
17 #include "base/json/json_reader.h"
18 #include "base/json/json_writer.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_split.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/values.h"
25 #include "content/browser/tracing/grit/tracing_resources.h"
26 #include "content/browser/tracing/trace_uploader.h"
27 #include "content/browser/tracing/tracing_controller_impl.h"
28 #include "content/public/browser/browser_context.h"
29 #include "content/public/browser/browser_thread.h"
30 #include "content/public/browser/tracing_controller.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/browser/web_ui.h"
33 #include "content/public/browser/web_ui_data_source.h"
34 #include "content/public/common/content_client.h"
35 #include "content/public/common/content_switches.h"
36 #include "content/public/common/url_constants.h"
41 const char kUploadURL[] = "https://clients2.google.com/cr/staging_report";
43 void OnGotCategories(const WebUIDataSource::GotDataCallback& callback,
44 const std::set<std::string>& categorySet) {
45 scoped_ptr<base::ListValue> category_list(new base::ListValue());
46 for (std::set<std::string>::const_iterator it = categorySet.begin();
47 it != categorySet.end(); it++) {
48 category_list->AppendString(*it);
51 base::RefCountedString* res = new base::RefCountedString();
52 base::JSONWriter::Write(category_list.get(), &res->data());
56 bool GetTracingOptions(const std::string& data64,
57 base::debug::CategoryFilter* category_filter,
58 base::debug::TraceOptions* tracing_options) {
60 if (!base::Base64Decode(data64, &data)) {
61 LOG(ERROR) << "Options were not base64 encoded.";
65 scoped_ptr<base::Value> optionsRaw(base::JSONReader::Read(data));
67 LOG(ERROR) << "Options were not valid JSON";
70 base::DictionaryValue* options;
71 if (!optionsRaw->GetAsDictionary(&options)) {
72 LOG(ERROR) << "Options must be dict";
76 if (!category_filter) {
77 LOG(ERROR) << "category_filter can't be passed as NULL";
81 if (!tracing_options) {
82 LOG(ERROR) << "tracing_options can't be passed as NULL";
86 bool options_ok = true;
87 std::string category_filter_string;
88 options_ok &= options->GetString("categoryFilter", &category_filter_string);
89 *category_filter = base::debug::CategoryFilter(category_filter_string);
91 options_ok &= options->GetBoolean("useSystemTracing",
92 &tracing_options->enable_systrace);
94 options->GetBoolean("useSampling", &tracing_options->enable_sampling);
96 bool use_continuous_tracing;
98 options->GetBoolean("useContinuousTracing", &use_continuous_tracing);
100 if (use_continuous_tracing)
101 tracing_options->record_mode = base::debug::RECORD_CONTINUOUSLY;
103 tracing_options->record_mode = base::debug::RECORD_UNTIL_FULL;
106 LOG(ERROR) << "Malformed options";
112 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback);
114 bool BeginRecording(const std::string& data64,
115 const WebUIDataSource::GotDataCallback& callback) {
116 base::debug::CategoryFilter category_filter("");
117 base::debug::TraceOptions tracing_options;
118 if (!GetTracingOptions(data64, &category_filter, &tracing_options))
121 return TracingController::GetInstance()->EnableRecording(
124 base::Bind(&OnRecordingEnabledAck, callback));
127 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
128 base::RefCountedString* res = new base::RefCountedString();
132 void OnTraceBufferPercentFullResult(
133 const WebUIDataSource::GotDataCallback& callback, float result) {
134 std::string str = base::DoubleToString(result);
135 callback.Run(base::RefCountedString::TakeString(&str));
138 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback);
140 bool EnableMonitoring(const std::string& data64,
141 const WebUIDataSource::GotDataCallback& callback) {
142 base::debug::TraceOptions tracing_options;
143 base::debug::CategoryFilter category_filter("");
144 if (!GetTracingOptions(data64, &category_filter, &tracing_options))
147 return TracingController::GetInstance()->EnableMonitoring(
150 base::Bind(OnMonitoringEnabledAck, callback));
153 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
154 base::RefCountedString* res = new base::RefCountedString();
158 void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback& callback) {
159 base::RefCountedString* res = new base::RefCountedString();
163 void GetMonitoringStatus(const WebUIDataSource::GotDataCallback& callback) {
165 base::debug::CategoryFilter category_filter("");
166 base::debug::TraceOptions options;
167 TracingController::GetInstance()->GetMonitoringStatus(
168 &is_monitoring, &category_filter, &options);
170 scoped_ptr<base::DictionaryValue>
171 monitoring_options(new base::DictionaryValue());
172 monitoring_options->SetBoolean("isMonitoring", is_monitoring);
173 monitoring_options->SetString("categoryFilter", category_filter.ToString());
174 monitoring_options->SetBoolean("useSystemTracing", options.enable_systrace);
175 monitoring_options->SetBoolean(
176 "useContinuousTracing",
177 options.record_mode == base::debug::RECORD_CONTINUOUSLY);
178 monitoring_options->SetBoolean("useSampling", options.enable_sampling);
180 std::string monitoring_options_json;
181 base::JSONWriter::Write(monitoring_options.get(), &monitoring_options_json);
183 base::RefCountedString* monitoring_options_base64 =
184 new base::RefCountedString();
185 base::Base64Encode(monitoring_options_json,
186 &monitoring_options_base64->data());
187 callback.Run(monitoring_options_base64);
190 void TracingCallbackWrapper(const WebUIDataSource::GotDataCallback& callback,
191 base::RefCountedString* data) {
195 bool OnBeginJSONRequest(const std::string& path,
196 const WebUIDataSource::GotDataCallback& callback) {
197 if (path == "json/categories") {
198 return TracingController::GetInstance()->GetCategories(
199 base::Bind(OnGotCategories, callback));
202 const char* beginRecordingPath = "json/begin_recording?";
203 if (StartsWithASCII(path, beginRecordingPath, true)) {
204 std::string data = path.substr(strlen(beginRecordingPath));
205 return BeginRecording(data, callback);
207 if (path == "json/get_buffer_percent_full") {
208 return TracingController::GetInstance()->GetTraceBufferPercentFull(
209 base::Bind(OnTraceBufferPercentFullResult, callback));
211 if (path == "json/end_recording") {
212 return TracingController::GetInstance()->DisableRecording(
213 TracingControllerImpl::CreateStringSink(
214 base::Bind(TracingCallbackWrapper, callback)));
217 const char* enableMonitoringPath = "json/begin_monitoring?";
218 if (path.find(enableMonitoringPath) == 0) {
219 std::string data = path.substr(strlen(enableMonitoringPath));
220 return EnableMonitoring(data, callback);
222 if (path == "json/end_monitoring") {
223 return TracingController::GetInstance()->DisableMonitoring(
224 base::Bind(OnMonitoringDisabled, callback));
226 if (path == "json/capture_monitoring") {
227 TracingController::GetInstance()->CaptureMonitoringSnapshot(
228 TracingControllerImpl::CreateStringSink(
229 base::Bind(TracingCallbackWrapper, callback)));
232 if (path == "json/get_monitoring_status") {
233 GetMonitoringStatus(callback);
237 LOG(ERROR) << "Unhandled request to " << path;
241 bool OnTracingRequest(const std::string& path,
242 const WebUIDataSource::GotDataCallback& callback) {
243 if (StartsWithASCII(path, "json/", true)) {
244 if (!OnBeginJSONRequest(path, callback)) {
245 std::string error("##ERROR##");
246 callback.Run(base::RefCountedString::TakeString(&error));
256 ////////////////////////////////////////////////////////////////////////////////
260 ////////////////////////////////////////////////////////////////////////////////
262 TracingUI::TracingUI(WebUI* web_ui)
263 : WebUIController(web_ui),
264 weak_factory_(this) {
265 web_ui->RegisterMessageCallback(
267 base::Bind(&TracingUI::DoUpload, base::Unretained(this)));
269 // Set up the chrome://tracing/ source.
270 BrowserContext* browser_context =
271 web_ui->GetWebContents()->GetBrowserContext();
273 WebUIDataSource* source = WebUIDataSource::Create(kChromeUITracingHost);
274 source->SetJsonPath("strings.js");
275 source->SetDefaultResource(IDR_TRACING_HTML);
276 source->AddResourcePath("tracing.js", IDR_TRACING_JS);
277 source->SetRequestFilter(base::Bind(OnTracingRequest));
278 WebUIDataSource::Add(browser_context, source);
279 TracingControllerImpl::GetInstance()->RegisterTracingUI(this);
282 TracingUI::~TracingUI() {
283 TracingControllerImpl::GetInstance()->UnregisterTracingUI(this);
286 void TracingUI::OnMonitoringStateChanged(bool is_monitoring) {
287 web_ui()->CallJavascriptFunction(
288 "onMonitoringStateChanged", base::FundamentalValue(is_monitoring));
291 void TracingUI::DoUpload(const base::ListValue* args) {
292 const base::CommandLine& command_line =
293 *base::CommandLine::ForCurrentProcess();
294 std::string upload_url = kUploadURL;
295 if (command_line.HasSwitch(switches::kTraceUploadURL)) {
297 command_line.GetSwitchValueASCII(switches::kTraceUploadURL);
299 if (!GURL(upload_url).is_valid()) {
303 if (upload_url.empty()) {
304 web_ui()->CallJavascriptFunction("onUploadError",
305 base::StringValue("Upload URL empty or invalid"));
309 std::string file_contents;
310 if (!args || args->empty() || !args->GetString(0, &file_contents)) {
311 web_ui()->CallJavascriptFunction("onUploadError",
312 base::StringValue("Missing data"));
316 TraceUploader::UploadProgressCallback progress_callback =
317 base::Bind(&TracingUI::OnTraceUploadProgress,
318 weak_factory_.GetWeakPtr());
319 TraceUploader::UploadDoneCallback done_callback =
320 base::Bind(&TracingUI::OnTraceUploadComplete,
321 weak_factory_.GetWeakPtr());
324 const char product[] = "Chrome";
325 #elif defined(OS_MACOSX)
326 const char product[] = "Chrome_Mac";
327 #elif defined(OS_LINUX)
328 const char product[] = "Chrome_Linux";
329 #elif defined(OS_ANDROID)
330 const char product[] = "Chrome_Android";
331 #elif defined(OS_CHROMEOS)
332 const char product[] = "Chrome_ChromeOS";
334 #error Platform not supported.
337 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
338 // the part before the "/".
339 std::vector<std::string> product_components;
340 base::SplitString(content::GetContentClient()->GetProduct(), '/',
341 &product_components);
342 DCHECK_EQ(2U, product_components.size());
344 if (product_components.size() == 2U) {
345 version = product_components[1];
350 BrowserContext* browser_context =
351 web_ui()->GetWebContents()->GetBrowserContext();
352 TraceUploader* uploader = new TraceUploader(
353 product, version, upload_url, browser_context->GetRequestContext());
355 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
356 &TraceUploader::DoUpload,
357 base::Unretained(uploader),
361 // TODO(mmandlis): Add support for stopping the upload in progress.
364 void TracingUI::OnTraceUploadProgress(int64 current, int64 total) {
365 DCHECK(current <= total);
366 int percent = (current / total) * 100;
367 web_ui()->CallJavascriptFunction(
369 base::FundamentalValue(percent),
370 base::StringValue(base::StringPrintf("%" PRId64, current)),
371 base::StringValue(base::StringPrintf("%" PRId64, total)));
374 void TracingUI::OnTraceUploadComplete(bool success,
375 const std::string& report_id,
376 const std::string& error_message) {
378 web_ui()->CallJavascriptFunction("onUploadComplete",
379 base::StringValue(report_id));
381 web_ui()->CallJavascriptFunction("onUploadError",
382 base::StringValue(error_message));
386 } // namespace content