devtools_tracing_handler.cc revision 2f22f038970e0d1927c41b04bbf5589bd12c5316
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  RegisterCommandHandler(devtools::Tracing::getCategories::kName,
59                         base::Bind(&DevToolsTracingHandler::OnGetCategories,
60                                    base::Unretained(this)));
61}
62
63DevToolsTracingHandler::~DevToolsTracingHandler() {
64}
65
66void 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
75void 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
107void 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
119TracingController::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
138scoped_refptr<DevToolsProtocol::Response>
139DevToolsTracingHandler::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
192void DevToolsTracingHandler::OnTracingStarted(
193    scoped_refptr<DevToolsProtocol::Command> command) {
194  SendAsyncResponse(command->SuccessResponse(NULL));
195}
196
197void 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
203scoped_refptr<DevToolsProtocol::Response>
204DevToolsTracingHandler::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
212void DevToolsTracingHandler::DisableRecording(
213    const TracingController::TracingFileResultCallback& callback) {
214  buffer_usage_poll_timer_.reset();
215  TracingController::GetInstance()->DisableRecording(base::FilePath(),
216                                                     callback);
217}
218
219void DevToolsTracingHandler::OnClientDetached() {
220    DisableRecording();
221}
222
223scoped_refptr<DevToolsProtocol::Response>
224DevToolsTracingHandler::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
233void 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