devtools_tracing_handler.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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
26namespace content {
27
28namespace {
29
30const char kRecordUntilFull[]   = "record-until-full";
31const char kRecordContinuously[] = "record-continuously";
32const char kEnableSampling[] = "enable-sampling";
33
34void 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
49DevToolsTracingHandler::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}
59
60DevToolsTracingHandler::~DevToolsTracingHandler() {
61}
62
63void DevToolsTracingHandler::BeginReadingRecordingResult(
64    const base::FilePath& path) {
65  BrowserThread::PostTask(
66      BrowserThread::FILE, FROM_HERE,
67      base::Bind(&ReadFile, path,
68                 base::Bind(&DevToolsTracingHandler::ReadRecordingResult,
69                            weak_factory_.GetWeakPtr())));
70}
71
72void DevToolsTracingHandler::ReadRecordingResult(
73    const scoped_refptr<base::RefCountedString>& trace_data) {
74  if (trace_data->data().size()) {
75    scoped_ptr<base::Value> trace_value(base::JSONReader::Read(
76        trace_data->data()));
77    base::DictionaryValue* dictionary = NULL;
78    bool ok = trace_value->GetAsDictionary(&dictionary);
79    DCHECK(ok);
80    base::ListValue* list = NULL;
81    ok = dictionary->GetList("traceEvents", &list);
82    DCHECK(ok);
83    std::string buffer;
84    for (size_t i = 0; i < list->GetSize(); ++i) {
85      std::string item;
86      base::Value* item_value;
87      list->Get(i, &item_value);
88      base::JSONWriter::Write(item_value, &item);
89      if (buffer.size())
90        buffer.append(",");
91      buffer.append(item);
92      if (i % 1000 == 0) {
93        OnTraceDataCollected(buffer);
94        buffer.clear();
95      }
96    }
97    if (buffer.size())
98      OnTraceDataCollected(buffer);
99  }
100
101  SendNotification(devtools::Tracing::tracingComplete::kName, NULL);
102}
103
104void DevToolsTracingHandler::OnTraceDataCollected(
105    const std::string& trace_fragment) {
106  // Hand-craft protocol notification message so we can substitute JSON
107  // that we already got as string as a bare object, not a quoted string.
108  std::string message = base::StringPrintf(
109      "{ \"method\": \"%s\", \"params\": { \"%s\": [ %s ] } }",
110      devtools::Tracing::dataCollected::kName,
111      devtools::Tracing::dataCollected::kParamValue,
112      trace_fragment.c_str());
113  SendRawMessage(message);
114}
115
116TracingController::Options DevToolsTracingHandler::TraceOptionsFromString(
117    const std::string& options) {
118  std::vector<std::string> split;
119  std::vector<std::string>::iterator iter;
120  int ret = 0;
121
122  base::SplitString(options, ',', &split);
123  for (iter = split.begin(); iter != split.end(); ++iter) {
124    if (*iter == kRecordUntilFull) {
125      ret &= ~TracingController::RECORD_CONTINUOUSLY;
126    } else if (*iter == kRecordContinuously) {
127      ret |= TracingController::RECORD_CONTINUOUSLY;
128    } else if (*iter == kEnableSampling) {
129      ret |= TracingController::ENABLE_SAMPLING;
130    }
131  }
132  return static_cast<TracingController::Options>(ret);
133}
134
135scoped_refptr<DevToolsProtocol::Response>
136DevToolsTracingHandler::OnStart(
137    scoped_refptr<DevToolsProtocol::Command> command) {
138  std::string categories;
139  base::DictionaryValue* params = command->params();
140  if (params)
141    params->GetString(devtools::Tracing::start::kParamCategories, &categories);
142
143  TracingController::Options options = TracingController::DEFAULT_OPTIONS;
144  if (params && params->HasKey(devtools::Tracing::start::kParamOptions)) {
145    std::string options_param;
146    params->GetString(devtools::Tracing::start::kParamOptions, &options_param);
147    options = TraceOptionsFromString(options_param);
148  }
149
150  if (params && params->HasKey(
151      devtools::Tracing::start::kParamBufferUsageReportingInterval)) {
152    double usage_reporting_interval = 0.0;
153    params->GetDouble(
154        devtools::Tracing::start::kParamBufferUsageReportingInterval,
155        &usage_reporting_interval);
156    if (usage_reporting_interval > 0) {
157      base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
158          std::ceil(usage_reporting_interval));
159      buffer_usage_poll_timer_.reset(new base::Timer(
160          FROM_HERE,
161          interval,
162          base::Bind(
163              base::IgnoreResult(&TracingController::GetTraceBufferPercentFull),
164              base::Unretained(TracingController::GetInstance()),
165              base::Bind(&DevToolsTracingHandler::OnBufferUsage,
166                         weak_factory_.GetWeakPtr())),
167          true));
168      buffer_usage_poll_timer_->Reset();
169    }
170  }
171
172  // If inspected target is a render process Tracing.start will be handled by
173  // tracing agent in the renderer.
174  if (target_ == Renderer) {
175    TracingController::GetInstance()->EnableRecording(
176        categories, options, TracingController::EnableRecordingDoneCallback());
177    return NULL;
178  }
179
180  TracingController::GetInstance()->EnableRecording(
181      categories, options,
182      base::Bind(&DevToolsTracingHandler::OnTracingStarted,
183                 weak_factory_.GetWeakPtr(),
184                 command));
185
186  return command->AsyncResponsePromise();
187}
188
189void DevToolsTracingHandler::OnTracingStarted(
190    scoped_refptr<DevToolsProtocol::Command> command) {
191  SendAsyncResponse(command->SuccessResponse(NULL));
192}
193
194void DevToolsTracingHandler::OnBufferUsage(float usage) {
195  base::DictionaryValue* params = new base::DictionaryValue();
196  params->SetDouble(devtools::Tracing::bufferUsage::kParamValue, usage);
197  SendNotification(devtools::Tracing::bufferUsage::kName, params);
198}
199
200scoped_refptr<DevToolsProtocol::Response>
201DevToolsTracingHandler::OnEnd(
202    scoped_refptr<DevToolsProtocol::Command> command) {
203  DisableRecording(
204      base::Bind(&DevToolsTracingHandler::BeginReadingRecordingResult,
205                 weak_factory_.GetWeakPtr()));
206  return command->SuccessResponse(NULL);
207}
208
209void DevToolsTracingHandler::DisableRecording(
210    const TracingController::TracingFileResultCallback& callback) {
211  buffer_usage_poll_timer_.reset();
212  TracingController::GetInstance()->DisableRecording(base::FilePath(),
213                                                     callback);
214}
215
216void DevToolsTracingHandler::OnClientDetached() {
217    DisableRecording();
218}
219
220}  // namespace content
221