devtools_tracing_handler.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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 26namespace content { 27 28namespace { 29 30const char kRecordUntilFull[] = "record-until-full"; 31const char kRecordContinuously[] = "record-continuously"; 32const char kEnableSampling[] = "enable-sampling"; 33 34void 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 49DevToolsTracingHandler::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} 59 60DevToolsTracingHandler::~DevToolsTracingHandler() { 61} 62 63void DevToolsTracingHandler::BeginReadingRecordingResult( 64 const base::FilePath& path) { 65 BrowserThread::PostTask( 66 BrowserThread::FILE, FROM_HERE, 67 base::Bind(&ReadFile, path, 68 base::Bind(&DevToolsTracingHandler::ReadRecordingResult, 69 weak_factory_.GetWeakPtr()))); 70} 71 72void DevToolsTracingHandler::ReadRecordingResult( 73 const scoped_refptr<base::RefCountedString>& trace_data) { 74 if (trace_data->data().size()) { 75 scoped_ptr<base::Value> trace_value(base::JSONReader::Read( 76 trace_data->data())); 77 base::DictionaryValue* dictionary = NULL; 78 bool ok = trace_value->GetAsDictionary(&dictionary); 79 DCHECK(ok); 80 base::ListValue* list = NULL; 81 ok = dictionary->GetList("traceEvents", &list); 82 DCHECK(ok); 83 std::string buffer; 84 for (size_t i = 0; i < list->GetSize(); ++i) { 85 std::string item; 86 base::Value* item_value; 87 list->Get(i, &item_value); 88 base::JSONWriter::Write(item_value, &item); 89 if (buffer.size()) 90 buffer.append(","); 91 buffer.append(item); 92 if (i % 1000 == 0) { 93 OnTraceDataCollected(buffer); 94 buffer.clear(); 95 } 96 } 97 if (buffer.size()) 98 OnTraceDataCollected(buffer); 99 } 100 101 SendNotification(devtools::Tracing::tracingComplete::kName, NULL); 102} 103 104void DevToolsTracingHandler::OnTraceDataCollected( 105 const std::string& trace_fragment) { 106 // Hand-craft protocol notification message so we can substitute JSON 107 // that we already got as string as a bare object, not a quoted string. 108 std::string message = base::StringPrintf( 109 "{ \"method\": \"%s\", \"params\": { \"%s\": [ %s ] } }", 110 devtools::Tracing::dataCollected::kName, 111 devtools::Tracing::dataCollected::kParamValue, 112 trace_fragment.c_str()); 113 SendRawMessage(message); 114} 115 116TracingController::Options DevToolsTracingHandler::TraceOptionsFromString( 117 const std::string& options) { 118 std::vector<std::string> split; 119 std::vector<std::string>::iterator iter; 120 int ret = 0; 121 122 base::SplitString(options, ',', &split); 123 for (iter = split.begin(); iter != split.end(); ++iter) { 124 if (*iter == kRecordUntilFull) { 125 ret &= ~TracingController::RECORD_CONTINUOUSLY; 126 } else if (*iter == kRecordContinuously) { 127 ret |= TracingController::RECORD_CONTINUOUSLY; 128 } else if (*iter == kEnableSampling) { 129 ret |= TracingController::ENABLE_SAMPLING; 130 } 131 } 132 return static_cast<TracingController::Options>(ret); 133} 134 135scoped_refptr<DevToolsProtocol::Response> 136DevToolsTracingHandler::OnStart( 137 scoped_refptr<DevToolsProtocol::Command> command) { 138 std::string categories; 139 base::DictionaryValue* params = command->params(); 140 if (params) 141 params->GetString(devtools::Tracing::start::kParamCategories, &categories); 142 143 TracingController::Options options = TracingController::DEFAULT_OPTIONS; 144 if (params && params->HasKey(devtools::Tracing::start::kParamOptions)) { 145 std::string options_param; 146 params->GetString(devtools::Tracing::start::kParamOptions, &options_param); 147 options = TraceOptionsFromString(options_param); 148 } 149 150 if (params && params->HasKey( 151 devtools::Tracing::start::kParamBufferUsageReportingInterval)) { 152 double usage_reporting_interval = 0.0; 153 params->GetDouble( 154 devtools::Tracing::start::kParamBufferUsageReportingInterval, 155 &usage_reporting_interval); 156 if (usage_reporting_interval > 0) { 157 base::TimeDelta interval = base::TimeDelta::FromMilliseconds( 158 std::ceil(usage_reporting_interval)); 159 buffer_usage_poll_timer_.reset(new base::Timer( 160 FROM_HERE, 161 interval, 162 base::Bind( 163 base::IgnoreResult(&TracingController::GetTraceBufferPercentFull), 164 base::Unretained(TracingController::GetInstance()), 165 base::Bind(&DevToolsTracingHandler::OnBufferUsage, 166 weak_factory_.GetWeakPtr())), 167 true)); 168 buffer_usage_poll_timer_->Reset(); 169 } 170 } 171 172 // If inspected target is a render process Tracing.start will be handled by 173 // tracing agent in the renderer. 174 if (target_ == Renderer) { 175 TracingController::GetInstance()->EnableRecording( 176 categories, options, TracingController::EnableRecordingDoneCallback()); 177 return NULL; 178 } 179 180 TracingController::GetInstance()->EnableRecording( 181 categories, options, 182 base::Bind(&DevToolsTracingHandler::OnTracingStarted, 183 weak_factory_.GetWeakPtr(), 184 command)); 185 186 return command->AsyncResponsePromise(); 187} 188 189void DevToolsTracingHandler::OnTracingStarted( 190 scoped_refptr<DevToolsProtocol::Command> command) { 191 SendAsyncResponse(command->SuccessResponse(NULL)); 192} 193 194void DevToolsTracingHandler::OnBufferUsage(float usage) { 195 base::DictionaryValue* params = new base::DictionaryValue(); 196 params->SetDouble(devtools::Tracing::bufferUsage::kParamValue, usage); 197 SendNotification(devtools::Tracing::bufferUsage::kName, params); 198} 199 200scoped_refptr<DevToolsProtocol::Response> 201DevToolsTracingHandler::OnEnd( 202 scoped_refptr<DevToolsProtocol::Command> command) { 203 DisableRecording( 204 base::Bind(&DevToolsTracingHandler::BeginReadingRecordingResult, 205 weak_factory_.GetWeakPtr())); 206 return command->SuccessResponse(NULL); 207} 208 209void DevToolsTracingHandler::DisableRecording( 210 const TracingController::TracingFileResultCallback& callback) { 211 buffer_usage_poll_timer_.reset(); 212 TracingController::GetInstance()->DisableRecording(base::FilePath(), 213 callback); 214} 215 216void DevToolsTracingHandler::OnClientDetached() { 217 DisableRecording(); 218} 219 220} // namespace content 221