Home | History | Annotate | Download | only in devtools
      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/devtools/devtools_tracing_handler.h"
      6 
      7 #include <cmath>
      8 
      9 #include "base/bind.h"
     10 #include "base/debug/trace_event_impl.h"
     11 #include "base/strings/string_split.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/time/time.h"
     14 #include "base/timer/timer.h"
     15 #include "base/values.h"
     16 #include "content/browser/devtools/devtools_http_handler_impl.h"
     17 #include "content/browser/devtools/devtools_protocol_constants.h"
     18 #include "content/public/browser/browser_thread.h"
     19 #include "content/public/browser/tracing_controller.h"
     20 
     21 namespace content {
     22 
     23 namespace {
     24 
     25 const char kRecordUntilFull[]   = "record-until-full";
     26 const char kRecordContinuously[] = "record-continuously";
     27 const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
     28 const char kEnableSampling[] = "enable-sampling";
     29 
     30 class DevToolsTraceSinkProxy : public TracingController::TraceDataSink {
     31  public:
     32   explicit DevToolsTraceSinkProxy(base::WeakPtr<DevToolsTracingHandler> handler)
     33       : tracing_handler_(handler) {}
     34 
     35   virtual void AddTraceChunk(const std::string& chunk) OVERRIDE {
     36     if (DevToolsTracingHandler* h = tracing_handler_.get())
     37       h->OnTraceDataCollected(chunk);
     38   }
     39   virtual void Close() OVERRIDE {
     40     if (DevToolsTracingHandler* h = tracing_handler_.get())
     41       h->OnTraceComplete();
     42   }
     43 
     44  private:
     45   virtual ~DevToolsTraceSinkProxy() {}
     46 
     47   base::WeakPtr<DevToolsTracingHandler> tracing_handler_;
     48 };
     49 
     50 }  // namespace
     51 
     52 const char* DevToolsTracingHandler::kDefaultCategories =
     53     "-*,disabled-by-default-devtools.timeline*";
     54 const double DevToolsTracingHandler::kDefaultReportingInterval = 1000.0;
     55 const double DevToolsTracingHandler::kMinimumReportingInterval = 250.0;
     56 
     57 DevToolsTracingHandler::DevToolsTracingHandler(
     58     DevToolsTracingHandler::Target target)
     59     : target_(target), is_recording_(false), weak_factory_(this) {
     60   RegisterCommandHandler(devtools::Tracing::start::kName,
     61                          base::Bind(&DevToolsTracingHandler::OnStart,
     62                                     base::Unretained(this)));
     63   RegisterCommandHandler(devtools::Tracing::end::kName,
     64                          base::Bind(&DevToolsTracingHandler::OnEnd,
     65                                     base::Unretained(this)));
     66   RegisterCommandHandler(devtools::Tracing::getCategories::kName,
     67                          base::Bind(&DevToolsTracingHandler::OnGetCategories,
     68                                     base::Unretained(this)));
     69 }
     70 
     71 DevToolsTracingHandler::~DevToolsTracingHandler() {
     72 }
     73 
     74 void DevToolsTracingHandler::OnTraceDataCollected(
     75     const std::string& trace_fragment) {
     76   // Hand-craft protocol notification message so we can substitute JSON
     77   // that we already got as string as a bare object, not a quoted string.
     78   std::string message =
     79       base::StringPrintf("{ \"method\": \"%s\", \"params\": { \"%s\": [",
     80                          devtools::Tracing::dataCollected::kName,
     81                          devtools::Tracing::dataCollected::kParamValue);
     82   const size_t messageSuffixSize = 10;
     83   message.reserve(message.size() + trace_fragment.size() + messageSuffixSize);
     84   message += trace_fragment;
     85   message += "] } }", SendRawMessage(message);
     86 }
     87 
     88 void DevToolsTracingHandler::OnTraceComplete() {
     89   SendNotification(devtools::Tracing::tracingComplete::kName, NULL);
     90 }
     91 
     92 base::debug::TraceOptions DevToolsTracingHandler::TraceOptionsFromString(
     93     const std::string& options) {
     94   std::vector<std::string> split;
     95   std::vector<std::string>::iterator iter;
     96   base::debug::TraceOptions ret;
     97 
     98   base::SplitString(options, ',', &split);
     99   for (iter = split.begin(); iter != split.end(); ++iter) {
    100     if (*iter == kRecordUntilFull) {
    101       ret.record_mode = base::debug::RECORD_UNTIL_FULL;
    102     } else if (*iter == kRecordContinuously) {
    103       ret.record_mode = base::debug::RECORD_CONTINUOUSLY;
    104     } else if (*iter == kRecordAsMuchAsPossible) {
    105       ret.record_mode = base::debug::RECORD_AS_MUCH_AS_POSSIBLE;
    106     } else if (*iter == kEnableSampling) {
    107       ret.enable_sampling = true;
    108     }
    109   }
    110   return ret;
    111 }
    112 
    113 scoped_refptr<DevToolsProtocol::Response>
    114 DevToolsTracingHandler::OnStart(
    115     scoped_refptr<DevToolsProtocol::Command> command) {
    116   if (is_recording_) {
    117     return command->InternalErrorResponse("Tracing is already started");
    118   }
    119   is_recording_ = true;
    120 
    121   std::string categories;
    122   base::debug::TraceOptions options;
    123   double usage_reporting_interval = 0.0;
    124 
    125   base::DictionaryValue* params = command->params();
    126   if (params) {
    127     params->GetString(devtools::Tracing::start::kParamCategories, &categories);
    128     std::string options_param;
    129     if (params->GetString(devtools::Tracing::start::kParamOptions,
    130                           &options_param)) {
    131       options = TraceOptionsFromString(options_param);
    132     }
    133     params->GetDouble(
    134         devtools::Tracing::start::kParamBufferUsageReportingInterval,
    135         &usage_reporting_interval);
    136   }
    137 
    138   SetupTimer(usage_reporting_interval);
    139 
    140   // If inspected target is a render process Tracing.start will be handled by
    141   // tracing agent in the renderer.
    142   if (target_ == Renderer) {
    143     TracingController::GetInstance()->EnableRecording(
    144         base::debug::CategoryFilter(categories),
    145         options,
    146         TracingController::EnableRecordingDoneCallback());
    147     return NULL;
    148   }
    149 
    150   TracingController::GetInstance()->EnableRecording(
    151       base::debug::CategoryFilter(categories),
    152       options,
    153       base::Bind(&DevToolsTracingHandler::OnRecordingEnabled,
    154                  weak_factory_.GetWeakPtr(),
    155                  command));
    156   return command->AsyncResponsePromise();
    157 }
    158 
    159 void DevToolsTracingHandler::SetupTimer(double usage_reporting_interval) {
    160   if (usage_reporting_interval == 0) return;
    161 
    162   if (usage_reporting_interval < kMinimumReportingInterval)
    163       usage_reporting_interval = kMinimumReportingInterval;
    164 
    165   base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
    166       std::ceil(usage_reporting_interval));
    167   buffer_usage_poll_timer_.reset(new base::Timer(
    168       FROM_HERE,
    169       interval,
    170       base::Bind(
    171           base::IgnoreResult(&TracingController::GetTraceBufferPercentFull),
    172           base::Unretained(TracingController::GetInstance()),
    173           base::Bind(&DevToolsTracingHandler::OnBufferUsage,
    174                      weak_factory_.GetWeakPtr())),
    175       true));
    176   buffer_usage_poll_timer_->Reset();
    177 }
    178 
    179 void DevToolsTracingHandler::OnRecordingEnabled(
    180     scoped_refptr<DevToolsProtocol::Command> command) {
    181   SendAsyncResponse(command->SuccessResponse(NULL));
    182 }
    183 
    184 void DevToolsTracingHandler::OnBufferUsage(float usage) {
    185   base::DictionaryValue* params = new base::DictionaryValue();
    186   params->SetDouble(devtools::Tracing::bufferUsage::kParamValue, usage);
    187   SendNotification(devtools::Tracing::bufferUsage::kName, params);
    188 }
    189 
    190 scoped_refptr<DevToolsProtocol::Response>
    191 DevToolsTracingHandler::OnEnd(
    192     scoped_refptr<DevToolsProtocol::Command> command) {
    193   if (!is_recording_) {
    194     return command->InternalErrorResponse("Tracing is not started");
    195   }
    196   DisableRecording(false);
    197   // If inspected target is a render process Tracing.end will be handled by
    198   // tracing agent in the renderer.
    199   if (target_ == Renderer)
    200     return NULL;
    201   return command->SuccessResponse(NULL);
    202 }
    203 
    204 void DevToolsTracingHandler::DisableRecording(bool abort) {
    205   is_recording_ = false;
    206   buffer_usage_poll_timer_.reset();
    207   TracingController::GetInstance()->DisableRecording(
    208       abort ? NULL : new DevToolsTraceSinkProxy(weak_factory_.GetWeakPtr()));
    209 }
    210 
    211 void DevToolsTracingHandler::OnClientDetached() {
    212   if (is_recording_)
    213     DisableRecording(true);
    214 }
    215 
    216 scoped_refptr<DevToolsProtocol::Response>
    217 DevToolsTracingHandler::OnGetCategories(
    218     scoped_refptr<DevToolsProtocol::Command> command) {
    219   TracingController::GetInstance()->GetCategories(
    220       base::Bind(&DevToolsTracingHandler::OnCategoriesReceived,
    221                  weak_factory_.GetWeakPtr(),
    222                  command));
    223   return command->AsyncResponsePromise();
    224 }
    225 
    226 void DevToolsTracingHandler::OnCategoriesReceived(
    227     scoped_refptr<DevToolsProtocol::Command> command,
    228     const std::set<std::string>& category_set) {
    229   base::DictionaryValue* response = new base::DictionaryValue;
    230   base::ListValue* category_list = new base::ListValue;
    231   for (std::set<std::string>::const_iterator it = category_set.begin();
    232        it != category_set.end(); ++it) {
    233     category_list->AppendString(*it);
    234   }
    235 
    236   response->Set(devtools::Tracing::getCategories::kResponseCategories,
    237                 category_list);
    238   SendAsyncResponse(command->SuccessResponse(response));
    239 }
    240 
    241 }  // namespace content
    242