1 // Copyright 2014 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/protocol/tracing_handler.h"
10 #include "base/format_macros.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/trace_event/memory_dump_manager.h"
16 #include "base/trace_event/trace_event_impl.h"
17 #include "components/tracing/trace_config_file.h"
18 #include "content/browser/devtools/devtools_io_context.h"
24 using Response
= DevToolsProtocolClient::Response
;
28 const double kMinimumReportingInterval
= 250.0;
30 class DevToolsTraceSinkProxy
: public TracingController::TraceDataSink
{
32 explicit DevToolsTraceSinkProxy(base::WeakPtr
<TracingHandler
> handler
)
33 : tracing_handler_(handler
) {}
35 void AddTraceChunk(const std::string
& chunk
) override
{
36 if (TracingHandler
* h
= tracing_handler_
.get())
37 h
->OnTraceDataCollected(chunk
);
39 void Close() override
{
40 if (TracingHandler
* h
= tracing_handler_
.get())
45 ~DevToolsTraceSinkProxy() override
{}
47 base::WeakPtr
<TracingHandler
> tracing_handler_
;
50 class DevToolsStreamTraceSink
: public TracingController::TraceDataSink
{
52 explicit DevToolsStreamTraceSink(
53 base::WeakPtr
<TracingHandler
> handler
,
54 const scoped_refptr
<DevToolsIOContext::Stream
>& stream
)
56 tracing_handler_(handler
),
59 void AddTraceChunk(const std::string
& chunk
) override
{
60 // FIXME: change interface to pass chunks as refcounted strings.
61 scoped_refptr
<base::RefCountedString
> ref_counted_chunk
62 = new base::RefCountedString();
63 std::string prefix
= first_chunk_
? "[" : ",";
64 ref_counted_chunk
->data() = prefix
+ chunk
;
66 stream_
->Append(ref_counted_chunk
);
69 void Close() override
{
70 if (TracingHandler
* h
= tracing_handler_
.get()) {
71 std::string suffix
= "]";
72 stream_
->Append(base::RefCountedString::TakeString(&suffix
));
73 h
->OnTraceToStreamComplete(stream_
->handle());
78 ~DevToolsStreamTraceSink() override
{}
80 scoped_refptr
<DevToolsIOContext::Stream
> stream_
;
81 base::WeakPtr
<TracingHandler
> tracing_handler_
;
87 TracingHandler::TracingHandler(TracingHandler::Target target
,
88 DevToolsIOContext
* io_context
)
90 io_context_(io_context
),
91 did_initiate_recording_(false),
92 return_as_stream_(false),
93 weak_factory_(this) {}
95 TracingHandler::~TracingHandler() {
98 void TracingHandler::SetClient(scoped_ptr
<Client
> client
) {
102 void TracingHandler::Detached() {
103 if (did_initiate_recording_
)
104 DisableRecording(scoped_refptr
<TracingController::TraceDataSink
>());
107 void TracingHandler::OnTraceDataCollected(const std::string
& trace_fragment
) {
108 // Hand-craft protocol notification message so we can substitute JSON
109 // that we already got as string as a bare object, not a quoted string.
111 "{ \"method\": \"Tracing.dataCollected\", \"params\": { \"value\": [");
112 const size_t messageSuffixSize
= 10;
113 message
.reserve(message
.size() + trace_fragment
.size() + messageSuffixSize
);
114 message
+= trace_fragment
;
116 client_
->SendRawMessage(message
);
119 void TracingHandler::OnTraceComplete() {
120 client_
->TracingComplete(TracingCompleteParams::Create());
123 void TracingHandler::OnTraceToStreamComplete(const std::string
& stream_handle
) {
124 client_
->TracingComplete(
125 TracingCompleteParams::Create()->set_stream(stream_handle
));
128 Response
TracingHandler::Start(DevToolsCommandId command_id
,
129 const std::string
* categories
,
130 const std::string
* options
,
131 const double* buffer_usage_reporting_interval
,
132 const std::string
* transfer_mode
) {
134 return Response::InternalError("Tracing is already started");
136 did_initiate_recording_
= true;
138 transfer_mode
&& *transfer_mode
== start::kTransferModeReturnAsStream
;
139 base::trace_event::TraceConfig
trace_config(
140 categories
? *categories
: std::string(),
141 options
? *options
: std::string());
142 if (buffer_usage_reporting_interval
)
143 SetupTimer(*buffer_usage_reporting_interval
);
145 // If inspected target is a render process Tracing.start will be handled by
146 // tracing agent in the renderer.
147 if (target_
== Renderer
) {
148 TracingController::GetInstance()->EnableRecording(
150 TracingController::EnableRecordingDoneCallback());
151 return Response::FallThrough();
154 TracingController::GetInstance()->EnableRecording(
156 base::Bind(&TracingHandler::OnRecordingEnabled
,
157 weak_factory_
.GetWeakPtr(),
159 return Response::OK();
162 Response
TracingHandler::End(DevToolsCommandId command_id
) {
163 // Startup tracing triggered by --trace-config-file is a special case, where
164 // tracing is started automatically upon browser startup and can be stopped
166 if (!did_initiate_recording_
&& !IsStartupTracingActive())
167 return Response::InternalError("Tracing is not started");
169 scoped_refptr
<TracingController::TraceDataSink
> proxy
;
170 if (return_as_stream_
) {
171 proxy
= new DevToolsStreamTraceSink(
172 weak_factory_
.GetWeakPtr(), io_context_
->CreateTempFileBackedStream());
174 proxy
= new DevToolsTraceSinkProxy(weak_factory_
.GetWeakPtr());
176 DisableRecording(proxy
);
177 // If inspected target is a render process Tracing.end will be handled by
178 // tracing agent in the renderer.
179 return target_
== Renderer
? Response::FallThrough() : Response::OK();
182 Response
TracingHandler::GetCategories(DevToolsCommandId command_id
) {
183 TracingController::GetInstance()->GetCategories(
184 base::Bind(&TracingHandler::OnCategoriesReceived
,
185 weak_factory_
.GetWeakPtr(),
187 return Response::OK();
190 void TracingHandler::OnRecordingEnabled(DevToolsCommandId command_id
) {
191 client_
->SendStartResponse(command_id
, StartResponse::Create());
194 void TracingHandler::OnBufferUsage(float percent_full
,
195 size_t approximate_event_count
) {
196 // TODO(crbug426117): remove set_value once all clients have switched to
197 // the new interface of the event.
198 client_
->BufferUsage(BufferUsageParams::Create()
199 ->set_value(percent_full
)
200 ->set_percent_full(percent_full
)
201 ->set_event_count(approximate_event_count
));
204 void TracingHandler::OnCategoriesReceived(
205 DevToolsCommandId command_id
,
206 const std::set
<std::string
>& category_set
) {
207 std::vector
<std::string
> categories
;
208 for (const std::string
& category
: category_set
)
209 categories
.push_back(category
);
210 client_
->SendGetCategoriesResponse(command_id
,
211 GetCategoriesResponse::Create()->set_categories(categories
));
214 Response
TracingHandler::RequestMemoryDump(DevToolsCommandId command_id
) {
216 return Response::InternalError("Tracing is not started");
218 base::trace_event::MemoryDumpManager::GetInstance()->RequestGlobalDump(
219 base::trace_event::MemoryDumpType::EXPLICITLY_TRIGGERED
,
220 base::trace_event::MemoryDumpLevelOfDetail::DETAILED
,
221 base::Bind(&TracingHandler::OnMemoryDumpFinished
,
222 weak_factory_
.GetWeakPtr(), command_id
));
223 return Response::OK();
226 void TracingHandler::OnMemoryDumpFinished(DevToolsCommandId command_id
,
229 client_
->SendRequestMemoryDumpResponse(
231 RequestMemoryDumpResponse::Create()
232 ->set_dump_guid(base::StringPrintf("0x%" PRIx64
, dump_guid
))
233 ->set_success(success
));
236 void TracingHandler::SetupTimer(double usage_reporting_interval
) {
237 if (usage_reporting_interval
== 0) return;
239 if (usage_reporting_interval
< kMinimumReportingInterval
)
240 usage_reporting_interval
= kMinimumReportingInterval
;
242 base::TimeDelta interval
= base::TimeDelta::FromMilliseconds(
243 std::ceil(usage_reporting_interval
));
244 buffer_usage_poll_timer_
.reset(new base::Timer(
246 base::Bind(base::IgnoreResult(&TracingController::GetTraceBufferUsage
),
247 base::Unretained(TracingController::GetInstance()),
248 base::Bind(&TracingHandler::OnBufferUsage
,
249 weak_factory_
.GetWeakPtr())),
251 buffer_usage_poll_timer_
->Reset();
254 void TracingHandler::DisableRecording(
255 const scoped_refptr
<TracingController::TraceDataSink
>& trace_data_sink
) {
256 buffer_usage_poll_timer_
.reset();
257 TracingController::GetInstance()->DisableRecording(trace_data_sink
);
258 did_initiate_recording_
= false;
261 bool TracingHandler::IsRecording() const {
262 return TracingController::GetInstance()->IsRecording();
265 bool TracingHandler::IsStartupTracingActive() {
266 return ::tracing::TraceConfigFile::GetInstance()->IsEnabled() &&
267 TracingController::GetInstance()->IsRecording();
270 } // namespace tracing
271 } // namespace devtools
272 } // namespace content