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 242