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.
5 #include "content/browser/devtools/devtools_tracing_handler.h"
10 #include "base/callback.h"
11 #include "base/debug/trace_event_impl.h"
12 #include "base/file_util.h"
13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h"
15 #include "base/location.h"
16 #include "base/memory/ref_counted_memory.h"
17 #include "base/strings/string_split.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/time/time.h"
20 #include "base/timer/timer.h"
21 #include "base/values.h"
22 #include "content/browser/devtools/devtools_http_handler_impl.h"
23 #include "content/browser/devtools/devtools_protocol_constants.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/tracing_controller.h"
31 const char kRecordUntilFull
[] = "record-until-full";
32 const char kRecordContinuously
[] = "record-continuously";
33 const char kRecordAsMuchAsPossible
[] = "record-as-much-as-possible";
34 const char kEnableSampling
[] = "enable-sampling";
37 const base::FilePath
& path
,
38 const base::Callback
<void(const scoped_refptr
<base::RefCountedString
>&)>
40 std::string trace_data
;
41 if (!base::ReadFileToString(path
, &trace_data
))
42 LOG(ERROR
) << "Failed to read file: " << path
.value();
43 base::DeleteFile(path
, false);
44 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
45 base::Bind(callback
, make_scoped_refptr(
46 base::RefCountedString::TakeString(&trace_data
))));
51 const char* DevToolsTracingHandler::kDefaultCategories
=
52 "-*,disabled-by-default-devtools.timeline*";
53 const double DevToolsTracingHandler::kDefaultReportingInterval
= 1000.0;
54 const double DevToolsTracingHandler::kMinimumReportingInterval
= 250.0;
56 DevToolsTracingHandler::DevToolsTracingHandler(
57 DevToolsTracingHandler::Target target
)
58 : weak_factory_(this), target_(target
), is_recording_(false) {
59 RegisterCommandHandler(devtools::Tracing::start::kName
,
60 base::Bind(&DevToolsTracingHandler::OnStart
,
61 base::Unretained(this)));
62 RegisterCommandHandler(devtools::Tracing::end::kName
,
63 base::Bind(&DevToolsTracingHandler::OnEnd
,
64 base::Unretained(this)));
65 RegisterCommandHandler(devtools::Tracing::getCategories::kName
,
66 base::Bind(&DevToolsTracingHandler::OnGetCategories
,
67 base::Unretained(this)));
68 RegisterNotificationHandler(devtools::Tracing::started::kName
,
69 base::Bind(&DevToolsTracingHandler::OnTracingStarted
,
70 base::Unretained(this)));
71 RegisterNotificationHandler(devtools::Tracing::stopped::kName
,
72 base::Bind(&DevToolsTracingHandler::OnTracingStopped
,
73 base::Unretained(this)));
76 DevToolsTracingHandler::~DevToolsTracingHandler() {
79 void DevToolsTracingHandler::BeginReadingRecordingResult(
80 const base::FilePath
& path
) {
81 BrowserThread::PostTask(
82 BrowserThread::FILE, FROM_HERE
,
83 base::Bind(&ReadFile
, path
,
84 base::Bind(&DevToolsTracingHandler::ReadRecordingResult
,
85 weak_factory_
.GetWeakPtr())));
88 void DevToolsTracingHandler::ReadRecordingResult(
89 const scoped_refptr
<base::RefCountedString
>& trace_data
) {
90 if (trace_data
->data().size()) {
91 scoped_ptr
<base::Value
> trace_value(base::JSONReader::Read(
93 base::DictionaryValue
* dictionary
= NULL
;
94 bool ok
= trace_value
->GetAsDictionary(&dictionary
);
96 base::ListValue
* list
= NULL
;
97 ok
= dictionary
->GetList("traceEvents", &list
);
100 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
102 base::Value
* item_value
;
103 list
->Get(i
, &item_value
);
104 base::JSONWriter::Write(item_value
, &item
);
108 const size_t kMessageSizeThreshold
= 1024 * 1024;
109 if (buffer
.size() > kMessageSizeThreshold
) {
110 OnTraceDataCollected(buffer
);
115 OnTraceDataCollected(buffer
);
118 SendNotification(devtools::Tracing::tracingComplete::kName
, NULL
);
121 void DevToolsTracingHandler::OnTraceDataCollected(
122 const std::string
& trace_fragment
) {
123 // Hand-craft protocol notification message so we can substitute JSON
124 // that we already got as string as a bare object, not a quoted string.
125 std::string message
= base::StringPrintf(
126 "{ \"method\": \"%s\", \"params\": { \"%s\": [ %s ] } }",
127 devtools::Tracing::dataCollected::kName
,
128 devtools::Tracing::dataCollected::kParamValue
,
129 trace_fragment
.c_str());
130 SendRawMessage(message
);
133 base::debug::TraceOptions
DevToolsTracingHandler::TraceOptionsFromString(
134 const std::string
& options
) {
135 std::vector
<std::string
> split
;
136 std::vector
<std::string
>::iterator iter
;
137 base::debug::TraceOptions ret
;
139 base::SplitString(options
, ',', &split
);
140 for (iter
= split
.begin(); iter
!= split
.end(); ++iter
) {
141 if (*iter
== kRecordUntilFull
) {
142 ret
.record_mode
= base::debug::RECORD_UNTIL_FULL
;
143 } else if (*iter
== kRecordContinuously
) {
144 ret
.record_mode
= base::debug::RECORD_CONTINUOUSLY
;
145 } else if (*iter
== kRecordAsMuchAsPossible
) {
146 ret
.record_mode
= base::debug::RECORD_AS_MUCH_AS_POSSIBLE
;
147 } else if (*iter
== kEnableSampling
) {
148 ret
.enable_sampling
= true;
154 scoped_refptr
<DevToolsProtocol::Response
>
155 DevToolsTracingHandler::OnStart(
156 scoped_refptr
<DevToolsProtocol::Command
> command
) {
157 is_recording_
= true;
159 std::string categories
;
160 base::debug::TraceOptions options
;
161 double usage_reporting_interval
= 0.0;
163 base::DictionaryValue
* params
= command
->params();
165 params
->GetString(devtools::Tracing::start::kParamCategories
, &categories
);
166 std::string options_param
;
167 if (params
->GetString(devtools::Tracing::start::kParamOptions
,
169 options
= TraceOptionsFromString(options_param
);
172 devtools::Tracing::start::kParamBufferUsageReportingInterval
,
173 &usage_reporting_interval
);
176 SetupTimer(usage_reporting_interval
);
178 // If inspected target is a render process Tracing.start will be handled by
179 // tracing agent in the renderer.
180 if (target_
== Renderer
) {
181 TracingController::GetInstance()->EnableRecording(
182 base::debug::CategoryFilter(categories
),
184 TracingController::EnableRecordingDoneCallback());
188 TracingController::GetInstance()->EnableRecording(
189 base::debug::CategoryFilter(categories
),
191 base::Bind(&DevToolsTracingHandler::OnRecordingEnabled
,
192 weak_factory_
.GetWeakPtr(),
194 return command
->AsyncResponsePromise();
197 void DevToolsTracingHandler::SetupTimer(double usage_reporting_interval
) {
198 if (usage_reporting_interval
== 0) return;
200 if (usage_reporting_interval
< kMinimumReportingInterval
)
201 usage_reporting_interval
= kMinimumReportingInterval
;
203 base::TimeDelta interval
= base::TimeDelta::FromMilliseconds(
204 std::ceil(usage_reporting_interval
));
205 buffer_usage_poll_timer_
.reset(new base::Timer(
209 base::IgnoreResult(&TracingController::GetTraceBufferPercentFull
),
210 base::Unretained(TracingController::GetInstance()),
211 base::Bind(&DevToolsTracingHandler::OnBufferUsage
,
212 weak_factory_
.GetWeakPtr())),
214 buffer_usage_poll_timer_
->Reset();
217 void DevToolsTracingHandler::OnRecordingEnabled(
218 scoped_refptr
<DevToolsProtocol::Command
> command
) {
219 SendAsyncResponse(command
->SuccessResponse(NULL
));
222 void DevToolsTracingHandler::OnBufferUsage(float usage
) {
223 base::DictionaryValue
* params
= new base::DictionaryValue();
224 params
->SetDouble(devtools::Tracing::bufferUsage::kParamValue
, usage
);
225 SendNotification(devtools::Tracing::bufferUsage::kName
, params
);
228 scoped_refptr
<DevToolsProtocol::Response
>
229 DevToolsTracingHandler::OnEnd(
230 scoped_refptr
<DevToolsProtocol::Command
> command
) {
232 base::Bind(&DevToolsTracingHandler::BeginReadingRecordingResult
,
233 weak_factory_
.GetWeakPtr()));
234 return command
->SuccessResponse(NULL
);
237 void DevToolsTracingHandler::DisableRecording(
238 const TracingController::TracingFileResultCallback
& callback
) {
239 is_recording_
= false;
240 buffer_usage_poll_timer_
.reset();
241 TracingController::GetInstance()->DisableRecording(base::FilePath(),
245 void DevToolsTracingHandler::OnClientDetached() {
250 scoped_refptr
<DevToolsProtocol::Response
>
251 DevToolsTracingHandler::OnGetCategories(
252 scoped_refptr
<DevToolsProtocol::Command
> command
) {
253 TracingController::GetInstance()->GetCategories(
254 base::Bind(&DevToolsTracingHandler::OnCategoriesReceived
,
255 weak_factory_
.GetWeakPtr(),
257 return command
->AsyncResponsePromise();
260 void DevToolsTracingHandler::OnCategoriesReceived(
261 scoped_refptr
<DevToolsProtocol::Command
> command
,
262 const std::set
<std::string
>& category_set
) {
263 base::DictionaryValue
* response
= new base::DictionaryValue
;
264 base::ListValue
* category_list
= new base::ListValue
;
265 for (std::set
<std::string
>::const_iterator it
= category_set
.begin();
266 it
!= category_set
.end(); ++it
) {
267 category_list
->AppendString(*it
);
270 response
->Set(devtools::Tracing::getCategories::kResponseCategories
,
272 SendAsyncResponse(command
->SuccessResponse(response
));
275 void DevToolsTracingHandler::OnTracingStarted(
276 scoped_refptr
<DevToolsProtocol::Notification
> notification
) {
279 is_recording_
= true;
281 SetupTimer(kDefaultReportingInterval
);
283 TracingController::GetInstance()->EnableRecording(
284 base::debug::CategoryFilter(kDefaultCategories
),
285 base::debug::TraceOptions(),
286 TracingController::EnableRecordingDoneCallback());
289 void DevToolsTracingHandler::OnTracingStopped(
290 scoped_refptr
<DevToolsProtocol::Notification
> notification
) {
293 is_recording_
= false;
295 base::Bind(&DevToolsTracingHandler::BeginReadingRecordingResult
,
296 weak_factory_
.GetWeakPtr()));
300 } // namespace content