• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
DevToolsTraceSinkProxy(base::WeakPtr<DevToolsTracingHandler> handler)32   explicit DevToolsTraceSinkProxy(base::WeakPtr<DevToolsTracingHandler> handler)
33       : tracing_handler_(handler) {}
34 
AddTraceChunk(const std::string & chunk)35   virtual void AddTraceChunk(const std::string& chunk) OVERRIDE {
36     if (DevToolsTracingHandler* h = tracing_handler_.get())
37       h->OnTraceDataCollected(chunk);
38   }
Close()39   virtual void Close() OVERRIDE {
40     if (DevToolsTracingHandler* h = tracing_handler_.get())
41       h->OnTraceComplete();
42   }
43 
44  private:
~DevToolsTraceSinkProxy()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 
DevToolsTracingHandler(DevToolsTracingHandler::Target target)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 
~DevToolsTracingHandler()71 DevToolsTracingHandler::~DevToolsTracingHandler() {
72 }
73 
OnTraceDataCollected(const std::string & trace_fragment)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 
OnTraceComplete()88 void DevToolsTracingHandler::OnTraceComplete() {
89   SendNotification(devtools::Tracing::tracingComplete::kName, NULL);
90 }
91 
TraceOptionsFromString(const std::string & options)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>
OnStart(scoped_refptr<DevToolsProtocol::Command> command)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 
SetupTimer(double usage_reporting_interval)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 
OnRecordingEnabled(scoped_refptr<DevToolsProtocol::Command> command)179 void DevToolsTracingHandler::OnRecordingEnabled(
180     scoped_refptr<DevToolsProtocol::Command> command) {
181   SendAsyncResponse(command->SuccessResponse(NULL));
182 }
183 
OnBufferUsage(float usage)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>
OnEnd(scoped_refptr<DevToolsProtocol::Command> command)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 
DisableRecording(bool abort)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 
OnClientDetached()211 void DevToolsTracingHandler::OnClientDetached() {
212   if (is_recording_)
213     DisableRecording(true);
214 }
215 
216 scoped_refptr<DevToolsProtocol::Response>
OnGetCategories(scoped_refptr<DevToolsProtocol::Command> command)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 
OnCategoriesReceived(scoped_refptr<DevToolsProtocol::Command> command,const std::set<std::string> & category_set)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