Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / tracing / tracing_ui.cc
blob193774a6ea091bba7ef026c9a6ccf0cf488e3a3d
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/command_line.h"
15 #include "base/debug/trace_event.h"
16 #include "base/file_util.h"
17 #include "base/format_macros.h"
18 #include "base/json/json_reader.h"
19 #include "base/json/json_writer.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/values.h"
26 #include "content/browser/tracing/grit/tracing_resources.h"
27 #include "content/browser/tracing/trace_uploader.h"
28 #include "content/browser/tracing/tracing_controller_impl.h"
29 #include "content/public/browser/browser_context.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/tracing_controller.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/content_switches.h"
37 #include "content/public/common/url_constants.h"
39 namespace content {
40 namespace {
42 const char kUploadURL[] = "https://clients2.google.com/cr/staging_report";
44 void OnGotCategories(const WebUIDataSource::GotDataCallback& callback,
45 const std::set<std::string>& categorySet) {
46 scoped_ptr<base::ListValue> category_list(new base::ListValue());
47 for (std::set<std::string>::const_iterator it = categorySet.begin();
48 it != categorySet.end(); it++) {
49 category_list->AppendString(*it);
52 base::RefCountedString* res = new base::RefCountedString();
53 base::JSONWriter::Write(category_list.get(), &res->data());
54 callback.Run(res);
57 bool GetTracingOptions(const std::string& data64,
58 base::debug::CategoryFilter* category_filter,
59 base::debug::TraceOptions* tracing_options) {
60 std::string data;
61 if (!base::Base64Decode(data64, &data)) {
62 LOG(ERROR) << "Options were not base64 encoded.";
63 return false;
66 scoped_ptr<base::Value> optionsRaw(base::JSONReader::Read(data));
67 if (!optionsRaw) {
68 LOG(ERROR) << "Options were not valid JSON";
69 return false;
71 base::DictionaryValue* options;
72 if (!optionsRaw->GetAsDictionary(&options)) {
73 LOG(ERROR) << "Options must be dict";
74 return false;
77 if (!category_filter) {
78 LOG(ERROR) << "category_filter can't be passed as NULL";
79 return false;
82 if (!tracing_options) {
83 LOG(ERROR) << "tracing_options can't be passed as NULL";
84 return false;
87 bool options_ok = true;
88 std::string category_filter_string;
89 options_ok &= options->GetString("categoryFilter", &category_filter_string);
90 *category_filter = base::debug::CategoryFilter(category_filter_string);
92 options_ok &= options->GetBoolean("useSystemTracing",
93 &tracing_options->enable_systrace);
94 options_ok &=
95 options->GetBoolean("useSampling", &tracing_options->enable_sampling);
97 bool use_continuous_tracing;
98 options_ok &=
99 options->GetBoolean("useContinuousTracing", &use_continuous_tracing);
101 if (use_continuous_tracing)
102 tracing_options->record_mode = base::debug::RECORD_CONTINUOUSLY;
103 else
104 tracing_options->record_mode = base::debug::RECORD_UNTIL_FULL;
106 if (!options_ok) {
107 LOG(ERROR) << "Malformed options";
108 return false;
110 return true;
113 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback);
115 bool BeginRecording(const std::string& data64,
116 const WebUIDataSource::GotDataCallback& callback) {
117 base::debug::CategoryFilter category_filter("");
118 base::debug::TraceOptions tracing_options;
119 if (!GetTracingOptions(data64, &category_filter, &tracing_options))
120 return false;
122 return TracingController::GetInstance()->EnableRecording(
123 category_filter,
124 tracing_options,
125 base::Bind(&OnRecordingEnabledAck, callback));
128 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
129 base::RefCountedString* res = new base::RefCountedString();
130 callback.Run(res);
133 void OnTraceBufferPercentFullResult(
134 const WebUIDataSource::GotDataCallback& callback, float result) {
135 std::string str = base::DoubleToString(result);
136 callback.Run(base::RefCountedString::TakeString(&str));
139 void ReadRecordingResult(const WebUIDataSource::GotDataCallback& callback,
140 const base::FilePath& path) {
141 std::string tmp;
142 if (!base::ReadFileToString(path, &tmp))
143 LOG(ERROR) << "Failed to read file " << path.value();
144 base::DeleteFile(path, false);
145 callback.Run(base::RefCountedString::TakeString(&tmp));
148 void BeginReadingRecordingResult(
149 const WebUIDataSource::GotDataCallback& callback,
150 const base::FilePath& path) {
151 BrowserThread::PostTask(
152 BrowserThread::FILE, FROM_HERE,
153 base::Bind(ReadRecordingResult, callback, path));
156 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback);
158 bool EnableMonitoring(const std::string& data64,
159 const WebUIDataSource::GotDataCallback& callback) {
160 base::debug::TraceOptions tracing_options;
161 base::debug::CategoryFilter category_filter("");
162 if (!GetTracingOptions(data64, &category_filter, &tracing_options))
163 return false;
165 return TracingController::GetInstance()->EnableMonitoring(
166 category_filter,
167 tracing_options,
168 base::Bind(OnMonitoringEnabledAck, callback));
171 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
172 base::RefCountedString* res = new base::RefCountedString();
173 callback.Run(res);
176 void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback& callback) {
177 base::RefCountedString* res = new base::RefCountedString();
178 callback.Run(res);
181 void GetMonitoringStatus(const WebUIDataSource::GotDataCallback& callback) {
182 bool is_monitoring;
183 base::debug::CategoryFilter category_filter("");
184 base::debug::TraceOptions options;
185 TracingController::GetInstance()->GetMonitoringStatus(
186 &is_monitoring, &category_filter, &options);
188 scoped_ptr<base::DictionaryValue>
189 monitoring_options(new base::DictionaryValue());
190 monitoring_options->SetBoolean("isMonitoring", is_monitoring);
191 monitoring_options->SetString("categoryFilter", category_filter.ToString());
192 monitoring_options->SetBoolean("useSystemTracing", options.enable_systrace);
193 monitoring_options->SetBoolean(
194 "useContinuousTracing",
195 options.record_mode == base::debug::RECORD_CONTINUOUSLY);
196 monitoring_options->SetBoolean("useSampling", options.enable_sampling);
198 std::string monitoring_options_json;
199 base::JSONWriter::Write(monitoring_options.get(), &monitoring_options_json);
201 base::RefCountedString* monitoring_options_base64 =
202 new base::RefCountedString();
203 base::Base64Encode(monitoring_options_json,
204 &monitoring_options_base64->data());
205 callback.Run(monitoring_options_base64);
208 void ReadMonitoringSnapshot(const WebUIDataSource::GotDataCallback& callback,
209 const base::FilePath& path) {
210 std::string tmp;
211 if (!base::ReadFileToString(path, &tmp))
212 LOG(ERROR) << "Failed to read file " << path.value();
213 base::DeleteFile(path, false);
214 callback.Run(base::RefCountedString::TakeString(&tmp));
217 void OnMonitoringSnapshotCaptured(
218 const WebUIDataSource::GotDataCallback& callback,
219 const base::FilePath& path) {
220 BrowserThread::PostTask(
221 BrowserThread::FILE, FROM_HERE,
222 base::Bind(ReadMonitoringSnapshot, callback, path));
225 bool OnBeginJSONRequest(const std::string& path,
226 const WebUIDataSource::GotDataCallback& callback) {
227 if (path == "json/categories") {
228 return TracingController::GetInstance()->GetCategories(
229 base::Bind(OnGotCategories, callback));
232 const char* beginRecordingPath = "json/begin_recording?";
233 if (StartsWithASCII(path, beginRecordingPath, true)) {
234 std::string data = path.substr(strlen(beginRecordingPath));
235 return BeginRecording(data, callback);
237 if (path == "json/get_buffer_percent_full") {
238 return TracingController::GetInstance()->GetTraceBufferPercentFull(
239 base::Bind(OnTraceBufferPercentFullResult, callback));
241 if (path == "json/end_recording") {
242 return TracingController::GetInstance()->DisableRecording(
243 base::FilePath(), base::Bind(BeginReadingRecordingResult, callback));
246 const char* enableMonitoringPath = "json/begin_monitoring?";
247 if (path.find(enableMonitoringPath) == 0) {
248 std::string data = path.substr(strlen(enableMonitoringPath));
249 return EnableMonitoring(data, callback);
251 if (path == "json/end_monitoring") {
252 return TracingController::GetInstance()->DisableMonitoring(
253 base::Bind(OnMonitoringDisabled, callback));
255 if (path == "json/capture_monitoring") {
256 TracingController::GetInstance()->CaptureMonitoringSnapshot(
257 base::FilePath(), base::Bind(OnMonitoringSnapshotCaptured, callback));
258 return true;
260 if (path == "json/get_monitoring_status") {
261 GetMonitoringStatus(callback);
262 return true;
265 LOG(ERROR) << "Unhandled request to " << path;
266 return false;
269 bool OnTracingRequest(const std::string& path,
270 const WebUIDataSource::GotDataCallback& callback) {
271 if (StartsWithASCII(path, "json/", true)) {
272 if (!OnBeginJSONRequest(path, callback)) {
273 std::string error("##ERROR##");
274 callback.Run(base::RefCountedString::TakeString(&error));
276 return true;
278 return false;
281 } // namespace
284 ////////////////////////////////////////////////////////////////////////////////
286 // TracingUI
288 ////////////////////////////////////////////////////////////////////////////////
290 TracingUI::TracingUI(WebUI* web_ui)
291 : WebUIController(web_ui),
292 weak_factory_(this) {
293 web_ui->RegisterMessageCallback(
294 "doUpload",
295 base::Bind(&TracingUI::DoUpload, base::Unretained(this)));
297 // Set up the chrome://tracing/ source.
298 BrowserContext* browser_context =
299 web_ui->GetWebContents()->GetBrowserContext();
301 WebUIDataSource* source = WebUIDataSource::Create(kChromeUITracingHost);
302 source->SetJsonPath("strings.js");
303 source->SetDefaultResource(IDR_TRACING_HTML);
304 source->AddResourcePath("tracing.js", IDR_TRACING_JS);
305 source->SetRequestFilter(base::Bind(OnTracingRequest));
306 WebUIDataSource::Add(browser_context, source);
307 TracingControllerImpl::GetInstance()->RegisterTracingUI(this);
310 TracingUI::~TracingUI() {
311 TracingControllerImpl::GetInstance()->UnregisterTracingUI(this);
314 void TracingUI::OnMonitoringStateChanged(bool is_monitoring) {
315 web_ui()->CallJavascriptFunction(
316 "onMonitoringStateChanged", base::FundamentalValue(is_monitoring));
319 void TracingUI::DoUpload(const base::ListValue* args) {
320 const base::CommandLine& command_line =
321 *base::CommandLine::ForCurrentProcess();
322 std::string upload_url = kUploadURL;
323 if (command_line.HasSwitch(switches::kTraceUploadURL)) {
324 upload_url =
325 command_line.GetSwitchValueASCII(switches::kTraceUploadURL);
327 if (!GURL(upload_url).is_valid()) {
328 upload_url.clear();
331 if (upload_url.empty()) {
332 web_ui()->CallJavascriptFunction("onUploadError",
333 base::StringValue("Upload URL empty or invalid"));
334 return;
337 std::string file_contents;
338 if (!args || args->empty() || !args->GetString(0, &file_contents)) {
339 web_ui()->CallJavascriptFunction("onUploadError",
340 base::StringValue("Missing data"));
341 return;
344 TraceUploader::UploadProgressCallback progress_callback =
345 base::Bind(&TracingUI::OnTraceUploadProgress,
346 weak_factory_.GetWeakPtr());
347 TraceUploader::UploadDoneCallback done_callback =
348 base::Bind(&TracingUI::OnTraceUploadComplete,
349 weak_factory_.GetWeakPtr());
351 #if defined(OS_WIN)
352 const char product[] = "Chrome";
353 #elif defined(OS_MACOSX)
354 const char product[] = "Chrome_Mac";
355 #elif defined(OS_LINUX)
356 const char product[] = "Chrome_Linux";
357 #elif defined(OS_ANDROID)
358 const char product[] = "Chrome_Android";
359 #elif defined(OS_CHROMEOS)
360 const char product[] = "Chrome_ChromeOS";
361 #else
362 #error Platform not supported.
363 #endif
365 // GetProduct() returns a string like "Chrome/aa.bb.cc.dd", split out
366 // the part before the "/".
367 std::vector<std::string> product_components;
368 base::SplitString(content::GetContentClient()->GetProduct(), '/',
369 &product_components);
370 DCHECK_EQ(2U, product_components.size());
371 std::string version;
372 if (product_components.size() == 2U) {
373 version = product_components[1];
374 } else {
375 version = "unknown";
378 BrowserContext* browser_context =
379 web_ui()->GetWebContents()->GetBrowserContext();
380 TraceUploader* uploader = new TraceUploader(
381 product, version, upload_url, browser_context->GetRequestContext());
383 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
384 &TraceUploader::DoUpload,
385 base::Unretained(uploader),
386 file_contents,
387 progress_callback,
388 done_callback));
389 // TODO(mmandlis): Add support for stopping the upload in progress.
392 void TracingUI::OnTraceUploadProgress(int64 current, int64 total) {
393 DCHECK(current <= total);
394 int percent = (current / total) * 100;
395 web_ui()->CallJavascriptFunction(
396 "onUploadProgress",
397 base::FundamentalValue(percent),
398 base::StringValue(base::StringPrintf("%" PRId64, current)),
399 base::StringValue(base::StringPrintf("%" PRId64, total)));
402 void TracingUI::OnTraceUploadComplete(bool success,
403 const std::string& report_id,
404 const std::string& error_message) {
405 if (success) {
406 web_ui()->CallJavascriptFunction("onUploadComplete",
407 base::StringValue(report_id));
408 } else {
409 web_ui()->CallJavascriptFunction("onUploadError",
410 base::StringValue(error_message));
414 } // namespace content