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/callback.h" 11 #include "base/file_util.h" 12 #include "base/json/json_reader.h" 13 #include "base/json/json_writer.h" 14 #include "base/location.h" 15 #include "base/memory/ref_counted_memory.h" 16 #include "base/strings/string_split.h" 17 #include "base/strings/stringprintf.h" 18 #include "base/time/time.h" 19 #include "base/timer/timer.h" 20 #include "base/values.h" 21 #include "content/browser/devtools/devtools_http_handler_impl.h" 22 #include "content/browser/devtools/devtools_protocol_constants.h" 23 #include "content/public/browser/browser_thread.h" 24 #include "content/public/browser/tracing_controller.h" 25 26 namespace content { 27 28 namespace { 29 30 const char kRecordUntilFull[] = "record-until-full"; 31 const char kRecordContinuously[] = "record-continuously"; 32 const char kEnableSampling[] = "enable-sampling"; 33 34 void ReadFile( 35 const base::FilePath& path, 36 const base::Callback<void(const scoped_refptr<base::RefCountedString>&)> 37 callback) { 38 std::string trace_data; 39 if (!base::ReadFileToString(path, &trace_data)) 40 LOG(ERROR) << "Failed to read file: " << path.value(); 41 base::DeleteFile(path, false); 42 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 43 base::Bind(callback, make_scoped_refptr( 44 base::RefCountedString::TakeString(&trace_data)))); 45 } 46 47 } // namespace 48 49 DevToolsTracingHandler::DevToolsTracingHandler( 50 DevToolsTracingHandler::Target target) 51 : weak_factory_(this), target_(target) { 52 RegisterCommandHandler(devtools::Tracing::start::kName, 53 base::Bind(&DevToolsTracingHandler::OnStart, 54 base::Unretained(this))); 55 RegisterCommandHandler(devtools::Tracing::end::kName, 56 base::Bind(&DevToolsTracingHandler::OnEnd, 57 base::Unretained(this))); 58 RegisterCommandHandler(devtools::Tracing::getCategories::kName, 59 base::Bind(&DevToolsTracingHandler::OnGetCategories, 60 base::Unretained(this))); 61 } 62 63 DevToolsTracingHandler::~DevToolsTracingHandler() { 64 } 65 66 void DevToolsTracingHandler::BeginReadingRecordingResult( 67 const base::FilePath& path) { 68 BrowserThread::PostTask( 69 BrowserThread::FILE, FROM_HERE, 70 base::Bind(&ReadFile, path, 71 base::Bind(&DevToolsTracingHandler::ReadRecordingResult, 72 weak_factory_.GetWeakPtr()))); 73 } 74 75 void DevToolsTracingHandler::ReadRecordingResult( 76 const scoped_refptr<base::RefCountedString>& trace_data) { 77 if (trace_data->data().size()) { 78 scoped_ptr<base::Value> trace_value(base::JSONReader::Read( 79 trace_data->data())); 80 base::DictionaryValue* dictionary = NULL; 81 bool ok = trace_value->GetAsDictionary(&dictionary); 82 DCHECK(ok); 83 base::ListValue* list = NULL; 84 ok = dictionary->GetList("traceEvents", &list); 85 DCHECK(ok); 86 std::string buffer; 87 for (size_t i = 0; i < list->GetSize(); ++i) { 88 std::string item; 89 base::Value* item_value; 90 list->Get(i, &item_value); 91 base::JSONWriter::Write(item_value, &item); 92 if (buffer.size()) 93 buffer.append(","); 94 buffer.append(item); 95 if (i % 1000 == 0) { 96 OnTraceDataCollected(buffer); 97 buffer.clear(); 98 } 99 } 100 if (buffer.size()) 101 OnTraceDataCollected(buffer); 102 } 103 104 SendNotification(devtools::Tracing::tracingComplete::kName, NULL); 105 } 106 107 void DevToolsTracingHandler::OnTraceDataCollected( 108 const std::string& trace_fragment) { 109 // Hand-craft protocol notification message so we can substitute JSON 110 // that we already got as string as a bare object, not a quoted string. 111 std::string message = base::StringPrintf( 112 "{ \"method\": \"%s\", \"params\": { \"%s\": [ %s ] } }", 113 devtools::Tracing::dataCollected::kName, 114 devtools::Tracing::dataCollected::kParamValue, 115 trace_fragment.c_str()); 116 SendRawMessage(message); 117 } 118 119 TracingController::Options DevToolsTracingHandler::TraceOptionsFromString( 120 const std::string& options) { 121 std::vector<std::string> split; 122 std::vector<std::string>::iterator iter; 123 int ret = 0; 124 125 base::SplitString(options, ',', &split); 126 for (iter = split.begin(); iter != split.end(); ++iter) { 127 if (*iter == kRecordUntilFull) { 128 ret &= ~TracingController::RECORD_CONTINUOUSLY; 129 } else if (*iter == kRecordContinuously) { 130 ret |= TracingController::RECORD_CONTINUOUSLY; 131 } else if (*iter == kEnableSampling) { 132 ret |= TracingController::ENABLE_SAMPLING; 133 } 134 } 135 return static_cast<TracingController::Options>(ret); 136 } 137 138 scoped_refptr<DevToolsProtocol::Response> 139 DevToolsTracingHandler::OnStart( 140 scoped_refptr<DevToolsProtocol::Command> command) { 141 std::string categories; 142 base::DictionaryValue* params = command->params(); 143 if (params) 144 params->GetString(devtools::Tracing::start::kParamCategories, &categories); 145 146 TracingController::Options options = TracingController::DEFAULT_OPTIONS; 147 if (params && params->HasKey(devtools::Tracing::start::kParamOptions)) { 148 std::string options_param; 149 params->GetString(devtools::Tracing::start::kParamOptions, &options_param); 150 options = TraceOptionsFromString(options_param); 151 } 152 153 if (params && params->HasKey( 154 devtools::Tracing::start::kParamBufferUsageReportingInterval)) { 155 double usage_reporting_interval = 0.0; 156 params->GetDouble( 157 devtools::Tracing::start::kParamBufferUsageReportingInterval, 158 &usage_reporting_interval); 159 if (usage_reporting_interval > 0) { 160 base::TimeDelta interval = base::TimeDelta::FromMilliseconds( 161 std::ceil(usage_reporting_interval)); 162 buffer_usage_poll_timer_.reset(new base::Timer( 163 FROM_HERE, 164 interval, 165 base::Bind( 166 base::IgnoreResult(&TracingController::GetTraceBufferPercentFull), 167 base::Unretained(TracingController::GetInstance()), 168 base::Bind(&DevToolsTracingHandler::OnBufferUsage, 169 weak_factory_.GetWeakPtr())), 170 true)); 171 buffer_usage_poll_timer_->Reset(); 172 } 173 } 174 175 // If inspected target is a render process Tracing.start will be handled by 176 // tracing agent in the renderer. 177 if (target_ == Renderer) { 178 TracingController::GetInstance()->EnableRecording( 179 categories, options, TracingController::EnableRecordingDoneCallback()); 180 return NULL; 181 } 182 183 TracingController::GetInstance()->EnableRecording( 184 categories, options, 185 base::Bind(&DevToolsTracingHandler::OnTracingStarted, 186 weak_factory_.GetWeakPtr(), 187 command)); 188 189 return command->AsyncResponsePromise(); 190 } 191 192 void DevToolsTracingHandler::OnTracingStarted( 193 scoped_refptr<DevToolsProtocol::Command> command) { 194 SendAsyncResponse(command->SuccessResponse(NULL)); 195 } 196 197 void DevToolsTracingHandler::OnBufferUsage(float usage) { 198 base::DictionaryValue* params = new base::DictionaryValue(); 199 params->SetDouble(devtools::Tracing::bufferUsage::kParamValue, usage); 200 SendNotification(devtools::Tracing::bufferUsage::kName, params); 201 } 202 203 scoped_refptr<DevToolsProtocol::Response> 204 DevToolsTracingHandler::OnEnd( 205 scoped_refptr<DevToolsProtocol::Command> command) { 206 DisableRecording( 207 base::Bind(&DevToolsTracingHandler::BeginReadingRecordingResult, 208 weak_factory_.GetWeakPtr())); 209 return command->SuccessResponse(NULL); 210 } 211 212 void DevToolsTracingHandler::DisableRecording( 213 const TracingController::TracingFileResultCallback& callback) { 214 buffer_usage_poll_timer_.reset(); 215 TracingController::GetInstance()->DisableRecording(base::FilePath(), 216 callback); 217 } 218 219 void DevToolsTracingHandler::OnClientDetached() { 220 DisableRecording(); 221 } 222 223 scoped_refptr<DevToolsProtocol::Response> 224 DevToolsTracingHandler::OnGetCategories( 225 scoped_refptr<DevToolsProtocol::Command> command) { 226 TracingController::GetInstance()->GetCategories( 227 base::Bind(&DevToolsTracingHandler::OnCategoriesReceived, 228 weak_factory_.GetWeakPtr(), 229 command)); 230 return command->AsyncResponsePromise(); 231 } 232 233 void DevToolsTracingHandler::OnCategoriesReceived( 234 scoped_refptr<DevToolsProtocol::Command> command, 235 const std::set<std::string>& category_set) { 236 base::DictionaryValue* response = new base::DictionaryValue; 237 base::ListValue* category_list = new base::ListValue; 238 for (std::set<std::string>::const_iterator it = category_set.begin(); 239 it != category_set.end(); ++it) { 240 category_list->AppendString(*it); 241 } 242 243 response->Set(devtools::Tracing::getCategories::kResponseCategories, 244 category_list); 245 SendAsyncResponse(command->SuccessResponse(response)); 246 } 247 248 } // namespace content 249