Home | History | Annotate | Download | only in tracing
      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
    387