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 void AddTraceChunk(const std::string& chunk) override { |
| 36 if (DevToolsTracingHandler* h = tracing_handler_.get()) |
| 37 h->OnTraceDataCollected(chunk); |
| 38 } |
| 39 void Close() override { |
| 40 if (DevToolsTracingHandler* h = tracing_handler_.get()) |
| 41 h->OnTraceComplete(); |
| 42 } |
| 43 |
| 44 private: |
| 45 ~DevToolsTraceSinkProxy() override {} |
| 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 |