Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / tracing / tracing_ui.cc
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.
4
5 #include "content/browser/tracing/tracing_ui.h"
6
7 #include <set>
8 #include <string>
9 #include <vector>
10
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"
37
38 namespace content {
39 namespace {
40
41 const char kUploadURL[] = "https://clients2.google.com/cr/staging_report";
42
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);
49   }
50
51   base::RefCountedString* res = new base::RefCountedString();
52   base::JSONWriter::Write(category_list.get(), &res->data());
53   callback.Run(res);
54 }
55
56 bool GetTracingOptions(const std::string& data64,
57                        base::debug::CategoryFilter* category_filter,
58                        base::debug::TraceOptions* tracing_options) {
59   std::string data;
60   if (!base::Base64Decode(data64, &data)) {
61     LOG(ERROR) << "Options were not base64 encoded.";
62     return false;
63   }
64
65   scoped_ptr<base::Value> optionsRaw(base::JSONReader::Read(data));
66   if (!optionsRaw) {
67     LOG(ERROR) << "Options were not valid JSON";
68     return false;
69   }
70   base::DictionaryValue* options;
71   if (!optionsRaw->GetAsDictionary(&options)) {
72     LOG(ERROR) << "Options must be dict";
73     return false;
74   }
75
76   if (!category_filter) {
77     LOG(ERROR) << "category_filter can't be passed as NULL";
78     return false;
79   }
80
81   if (!tracing_options) {
82     LOG(ERROR) << "tracing_options can't be passed as NULL";
83     return false;
84   }
85
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);
90
91   options_ok &= options->GetBoolean("useSystemTracing",
92                                     &tracing_options->enable_systrace);
93   options_ok &=
94       options->GetBoolean("useSampling", &tracing_options->enable_sampling);
95
96   bool use_continuous_tracing;
97   options_ok &=
98       options->GetBoolean("useContinuousTracing", &use_continuous_tracing);
99
100   if (use_continuous_tracing)
101     tracing_options->record_mode = base::debug::RECORD_CONTINUOUSLY;
102   else
103     tracing_options->record_mode = base::debug::RECORD_UNTIL_FULL;
104
105   if (!options_ok) {
106     LOG(ERROR) << "Malformed options";
107     return false;
108   }
109   return true;
110 }
111
112 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback);
113
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))
119     return false;
120
121   return TracingController::GetInstance()->EnableRecording(
122       category_filter,
123       tracing_options,
124       base::Bind(&OnRecordingEnabledAck, callback));
125 }
126
127 void OnRecordingEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
128   base::RefCountedString* res = new base::RefCountedString();
129   callback.Run(res);
130 }
131
132 void OnTraceBufferPercentFullResult(
133     const WebUIDataSource::GotDataCallback& callback, float result) {
134   std::string str = base::DoubleToString(result);
135   callback.Run(base::RefCountedString::TakeString(&str));
136 }
137
138 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback);
139
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))
145     return false;
146
147   return TracingController::GetInstance()->EnableMonitoring(
148       category_filter,
149       tracing_options,
150       base::Bind(OnMonitoringEnabledAck, callback));
151 }
152
153 void OnMonitoringEnabledAck(const WebUIDataSource::GotDataCallback& callback) {
154   base::RefCountedString* res = new base::RefCountedString();
155   callback.Run(res);
156 }
157
158 void OnMonitoringDisabled(const WebUIDataSource::GotDataCallback& callback) {
159   base::RefCountedString* res = new base::RefCountedString();
160   callback.Run(res);
161 }
162
163 void GetMonitoringStatus(const WebUIDataSource::GotDataCallback& callback) {
164   bool is_monitoring;
165   base::debug::CategoryFilter category_filter("");
166   base::debug::TraceOptions options;
167   TracingController::GetInstance()->GetMonitoringStatus(
168       &is_monitoring, &category_filter, &options);
169
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);
179
180   std::string monitoring_options_json;
181   base::JSONWriter::Write(monitoring_options.get(), &monitoring_options_json);
182
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);
188 }
189
190 void TracingCallbackWrapper(const WebUIDataSource::GotDataCallback& callback,
191                             base::RefCountedString* data) {
192   callback.Run(data);
193 }
194
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));
200   }
201
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);
206   }
207   if (path == "json/get_buffer_percent_full") {
208     return TracingController::GetInstance()->GetTraceBufferPercentFull(
209         base::Bind(OnTraceBufferPercentFullResult, callback));
210   }
211   if (path == "json/end_recording") {
212     return TracingController::GetInstance()->DisableRecording(
213         TracingControllerImpl::CreateStringSink(
214             base::Bind(TracingCallbackWrapper, callback)));
215   }
216
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);
221   }
222   if (path == "json/end_monitoring") {
223     return TracingController::GetInstance()->DisableMonitoring(
224         base::Bind(OnMonitoringDisabled, callback));
225   }
226   if (path == "json/capture_monitoring") {
227     TracingController::GetInstance()->CaptureMonitoringSnapshot(
228         TracingControllerImpl::CreateStringSink(
229             base::Bind(TracingCallbackWrapper, callback)));
230     return true;
231   }
232   if (path == "json/get_monitoring_status") {
233     GetMonitoringStatus(callback);
234     return true;
235   }
236
237   LOG(ERROR) << "Unhandled request to " << path;
238   return false;
239 }
240
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));
247     }
248     return true;
249   }
250   return false;
251 }
252
253 }  // namespace
254
255
256 ////////////////////////////////////////////////////////////////////////////////
257 //
258 // TracingUI
259 //
260 ////////////////////////////////////////////////////////////////////////////////
261
262 TracingUI::TracingUI(WebUI* web_ui)
263     : WebUIController(web_ui),
264       weak_factory_(this) {
265   web_ui->RegisterMessageCallback(
266         "doUpload",
267         base::Bind(&TracingUI::DoUpload, base::Unretained(this)));
268
269   // Set up the chrome://tracing/ source.
270   BrowserContext* browser_context =
271       web_ui->GetWebContents()->GetBrowserContext();
272
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);
280 }
281
282 TracingUI::~TracingUI() {
283   TracingControllerImpl::GetInstance()->UnregisterTracingUI(this);
284 }
285
286 void TracingUI::OnMonitoringStateChanged(bool is_monitoring) {
287   web_ui()->CallJavascriptFunction(
288       "onMonitoringStateChanged", base::FundamentalValue(is_monitoring));
289 }
290
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)) {
296     upload_url =
297         command_line.GetSwitchValueASCII(switches::kTraceUploadURL);
298   }
299   if (!GURL(upload_url).is_valid()) {
300     upload_url.clear();
301   }
302
303   if (upload_url.empty()) {
304     web_ui()->CallJavascriptFunction("onUploadError",
305         base::StringValue("Upload URL empty or invalid"));
306     return;
307   }
308
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"));
313     return;
314   }
315
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());
322
323 #if defined(OS_WIN)
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";
333 #else
334 #error Platform not supported.
335 #endif
336
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());
343   std::string version;
344   if (product_components.size() == 2U) {
345     version = product_components[1];
346   } else {
347     version = "unknown";
348   }
349
350   BrowserContext* browser_context =
351       web_ui()->GetWebContents()->GetBrowserContext();
352   TraceUploader* uploader = new TraceUploader(
353       product, version, upload_url, browser_context->GetRequestContext());
354
355   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind(
356      &TraceUploader::DoUpload,
357      base::Unretained(uploader),
358      file_contents,
359      progress_callback,
360      done_callback));
361   // TODO(mmandlis): Add support for stopping the upload in progress.
362 }
363
364 void TracingUI::OnTraceUploadProgress(int64 current, int64 total) {
365   DCHECK(current <= total);
366   int percent = (current / total) * 100;
367   web_ui()->CallJavascriptFunction(
368         "onUploadProgress",
369         base::FundamentalValue(percent),
370         base::StringValue(base::StringPrintf("%" PRId64, current)),
371         base::StringValue(base::StringPrintf("%" PRId64, total)));
372 }
373
374 void TracingUI::OnTraceUploadComplete(bool success,
375                                       const std::string& report_id,
376                                       const std::string& error_message) {
377   if (success) {
378     web_ui()->CallJavascriptFunction("onUploadComplete",
379                                      base::StringValue(report_id));
380   } else {
381     web_ui()->CallJavascriptFunction("onUploadError",
382                                      base::StringValue(error_message));
383   }
384 }
385
386 }  // namespace content