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/callback.h"
     11 #include "base/file_util.h"
     12 #include "base/json/json_reader.h"
     13 #include "base/json/json_writer.h"
     14 #include "base/location.h"
     15 #include "base/memory/ref_counted_memory.h"
     16 #include "base/strings/string_split.h"
     17 #include "base/strings/stringprintf.h"
     18 #include "base/time/time.h"
     19 #include "base/timer/timer.h"
     20 #include "base/values.h"
     21 #include "content/browser/devtools/devtools_http_handler_impl.h"
     22 #include "content/browser/devtools/devtools_protocol_constants.h"
     23 #include "content/public/browser/browser_thread.h"
     24 #include "content/public/browser/tracing_controller.h"
     25 
     26 namespace content {
     27 
     28 namespace {
     29 
     30 const char kRecordUntilFull[]   = "record-until-full";
     31 const char kRecordContinuously[] = "record-continuously";
     32 const char kEnableSampling[] = "enable-sampling";
     33 
     34 void ReadFile(
     35     const base::FilePath& path,
     36     const base::Callback<void(const scoped_refptr<base::RefCountedString>&)>
     37         callback) {
     38   std::string trace_data;
     39   if (!base::ReadFileToString(path, &trace_data))
     40     LOG(ERROR) << "Failed to read file: " << path.value();
     41   base::DeleteFile(path, false);
     42   BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
     43       base::Bind(callback, make_scoped_refptr(
     44           base::RefCountedString::TakeString(&trace_data))));
     45 }
     46 
     47 }  // namespace
     48 
     49 DevToolsTracingHandler::DevToolsTracingHandler(
     50     DevToolsTracingHandler::Target target)
     51     : weak_factory_(this), target_(target) {
     52   RegisterCommandHandler(devtools::Tracing::start::kName,
     53                          base::Bind(&DevToolsTracingHandler::OnStart,
     54                                     base::Unretained(this)));
     55   RegisterCommandHandler(devtools::Tracing::end::kName,
     56                          base::Bind(&DevToolsTracingHandler::OnEnd,
     57                                     base::Unretained(this)));
     58   RegisterCommandHandler(devtools::Tracing::getCategories::kName,
     59                          base::Bind(&DevToolsTracingHandler::OnGetCategories,
     60                                     base::Unretained(this)));
     61 }
     62 
     63 DevToolsTracingHandler::~DevToolsTracingHandler() {
     64 }
     65 
     66 void DevToolsTracingHandler::BeginReadingRecordingResult(
     67     const base::FilePath& path) {
     68   BrowserThread::PostTask(
     69       BrowserThread::FILE, FROM_HERE,
     70       base::Bind(&ReadFile, path,
     71                  base::Bind(&DevToolsTracingHandler::ReadRecordingResult,
     72                             weak_factory_.GetWeakPtr())));
     73 }
     74 
     75 void DevToolsTracingHandler::ReadRecordingResult(
     76     const scoped_refptr<base::RefCountedString>& trace_data) {
     77   if (trace_data->data().size()) {
     78     scoped_ptr<base::Value> trace_value(base::JSONReader::Read(
     79         trace_data->data()));
     80     base::DictionaryValue* dictionary = NULL;
     81     bool ok = trace_value->GetAsDictionary(&dictionary);
     82     DCHECK(ok);
     83     base::ListValue* list = NULL;
     84     ok = dictionary->GetList("traceEvents", &list);
     85     DCHECK(ok);
     86     std::string buffer;
     87     for (size_t i = 0; i < list->GetSize(); ++i) {
     88       std::string item;
     89       base::Value* item_value;
     90       list->Get(i, &item_value);
     91       base::JSONWriter::Write(item_value, &item);
     92       if (buffer.size())
     93         buffer.append(",");
     94       buffer.append(item);
     95       if (i % 1000 == 0) {
     96         OnTraceDataCollected(buffer);
     97         buffer.clear();
     98       }
     99     }
    100     if (buffer.size())
    101       OnTraceDataCollected(buffer);
    102   }
    103 
    104   SendNotification(devtools::Tracing::tracingComplete::kName, NULL);
    105 }
    106 
    107 void DevToolsTracingHandler::OnTraceDataCollected(
    108     const std::string& trace_fragment) {
    109   // Hand-craft protocol notification message so we can substitute JSON
    110   // that we already got as string as a bare object, not a quoted string.
    111   std::string message = base::StringPrintf(
    112       "{ \"method\": \"%s\", \"params\": { \"%s\": [ %s ] } }",
    113       devtools::Tracing::dataCollected::kName,
    114       devtools::Tracing::dataCollected::kParamValue,
    115       trace_fragment.c_str());
    116   SendRawMessage(message);
    117 }
    118 
    119 TracingController::Options DevToolsTracingHandler::TraceOptionsFromString(
    120     const std::string& options) {
    121   std::vector<std::string> split;
    122   std::vector<std::string>::iterator iter;
    123   int ret = 0;
    124 
    125   base::SplitString(options, ',', &split);
    126   for (iter = split.begin(); iter != split.end(); ++iter) {
    127     if (*iter == kRecordUntilFull) {
    128       ret &= ~TracingController::RECORD_CONTINUOUSLY;
    129     } else if (*iter == kRecordContinuously) {
    130       ret |= TracingController::RECORD_CONTINUOUSLY;
    131     } else if (*iter == kEnableSampling) {
    132       ret |= TracingController::ENABLE_SAMPLING;
    133     }
    134   }
    135   return static_cast<TracingController::Options>(ret);
    136 }
    137 
    138 scoped_refptr<DevToolsProtocol::Response>
    139 DevToolsTracingHandler::OnStart(
    140     scoped_refptr<DevToolsProtocol::Command> command) {
    141   std::string categories;
    142   base::DictionaryValue* params = command->params();
    143   if (params)
    144     params->GetString(devtools::Tracing::start::kParamCategories, &categories);
    145 
    146   TracingController::Options options = TracingController::DEFAULT_OPTIONS;
    147   if (params && params->HasKey(devtools::Tracing::start::kParamOptions)) {
    148     std::string options_param;
    149     params->GetString(devtools::Tracing::start::kParamOptions, &options_param);
    150     options = TraceOptionsFromString(options_param);
    151   }
    152 
    153   if (params && params->HasKey(
    154       devtools::Tracing::start::kParamBufferUsageReportingInterval)) {
    155     double usage_reporting_interval = 0.0;
    156     params->GetDouble(
    157         devtools::Tracing::start::kParamBufferUsageReportingInterval,
    158         &usage_reporting_interval);
    159     if (usage_reporting_interval > 0) {
    160       base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
    161           std::ceil(usage_reporting_interval));
    162       buffer_usage_poll_timer_.reset(new base::Timer(
    163           FROM_HERE,
    164           interval,
    165           base::Bind(
    166               base::IgnoreResult(&TracingController::GetTraceBufferPercentFull),
    167               base::Unretained(TracingController::GetInstance()),
    168               base::Bind(&DevToolsTracingHandler::OnBufferUsage,
    169                          weak_factory_.GetWeakPtr())),
    170           true));
    171       buffer_usage_poll_timer_->Reset();
    172     }
    173   }
    174 
    175   // If inspected target is a render process Tracing.start will be handled by
    176   // tracing agent in the renderer.
    177   if (target_ == Renderer) {
    178     TracingController::GetInstance()->EnableRecording(
    179         categories, options, TracingController::EnableRecordingDoneCallback());
    180     return NULL;
    181   }
    182 
    183   TracingController::GetInstance()->EnableRecording(
    184       categories, options,
    185       base::Bind(&DevToolsTracingHandler::OnTracingStarted,
    186                  weak_factory_.GetWeakPtr(),
    187                  command));
    188 
    189   return command->AsyncResponsePromise();
    190 }
    191 
    192 void DevToolsTracingHandler::OnTracingStarted(
    193     scoped_refptr<DevToolsProtocol::Command> command) {
    194   SendAsyncResponse(command->SuccessResponse(NULL));
    195 }
    196 
    197 void DevToolsTracingHandler::OnBufferUsage(float usage) {
    198   base::DictionaryValue* params = new base::DictionaryValue();
    199   params->SetDouble(devtools::Tracing::bufferUsage::kParamValue, usage);
    200   SendNotification(devtools::Tracing::bufferUsage::kName, params);
    201 }
    202 
    203 scoped_refptr<DevToolsProtocol::Response>
    204 DevToolsTracingHandler::OnEnd(
    205     scoped_refptr<DevToolsProtocol::Command> command) {
    206   DisableRecording(
    207       base::Bind(&DevToolsTracingHandler::BeginReadingRecordingResult,
    208                  weak_factory_.GetWeakPtr()));
    209   return command->SuccessResponse(NULL);
    210 }
    211 
    212 void DevToolsTracingHandler::DisableRecording(
    213     const TracingController::TracingFileResultCallback& callback) {
    214   buffer_usage_poll_timer_.reset();
    215   TracingController::GetInstance()->DisableRecording(base::FilePath(),
    216                                                      callback);
    217 }
    218 
    219 void DevToolsTracingHandler::OnClientDetached() {
    220     DisableRecording();
    221 }
    222 
    223 scoped_refptr<DevToolsProtocol::Response>
    224 DevToolsTracingHandler::OnGetCategories(
    225     scoped_refptr<DevToolsProtocol::Command> command) {
    226   TracingController::GetInstance()->GetCategories(
    227       base::Bind(&DevToolsTracingHandler::OnCategoriesReceived,
    228                  weak_factory_.GetWeakPtr(),
    229                  command));
    230   return command->AsyncResponsePromise();
    231 }
    232 
    233 void DevToolsTracingHandler::OnCategoriesReceived(
    234     scoped_refptr<DevToolsProtocol::Command> command,
    235     const std::set<std::string>& category_set) {
    236   base::DictionaryValue* response = new base::DictionaryValue;
    237   base::ListValue* category_list = new base::ListValue;
    238   for (std::set<std::string>::const_iterator it = category_set.begin();
    239        it != category_set.end(); ++it) {
    240     category_list->AppendString(*it);
    241   }
    242 
    243   response->Set(devtools::Tracing::getCategories::kResponseCategories,
    244                 category_list);
    245   SendAsyncResponse(command->SuccessResponse(response));
    246 }
    247 
    248 }  // namespace content
    249