OLD | NEW |
| (Empty) |
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: | |
32 explicit DevToolsTraceSinkProxy(base::WeakPtr<DevToolsTracingHandler> handler) | |
33 : tracing_handler_(handler) {} | |
34 | |
35 virtual void AddTraceChunk(const std::string& chunk) override { | |
36 if (DevToolsTracingHandler* h = tracing_handler_.get()) | |
37 h->OnTraceDataCollected(chunk); | |
38 } | |
39 virtual void Close() override { | |
40 if (DevToolsTracingHandler* h = tracing_handler_.get()) | |
41 h->OnTraceComplete(); | |
42 } | |
43 | |
44 private: | |
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 | |
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 | |
71 DevToolsTracingHandler::~DevToolsTracingHandler() { | |
72 } | |
73 | |
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 | |
88 void DevToolsTracingHandler::OnTraceComplete() { | |
89 SendNotification(devtools::Tracing::tracingComplete::kName, NULL); | |
90 } | |
91 | |
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> | |
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 | |
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 | |
179 void DevToolsTracingHandler::OnRecordingEnabled( | |
180 scoped_refptr<DevToolsProtocol::Command> command) { | |
181 SendAsyncResponse(command->SuccessResponse(NULL)); | |
182 } | |
183 | |
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> | |
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 | |
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 | |
211 void DevToolsTracingHandler::OnClientDetached() { | |
212 if (is_recording_) | |
213 DisableRecording(true); | |
214 } | |
215 | |
216 scoped_refptr<DevToolsProtocol::Response> | |
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 | |
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 | |
OLD | NEW |