// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/devtools/devtools_tracing_handler.h" #include "base/bind.h" #include "base/callback.h" #include "base/file_util.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" #include "base/location.h" #include "base/memory/ref_counted_memory.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" #include "base/values.h" #include "content/browser/devtools/devtools_http_handler_impl.h" #include "content/browser/devtools/devtools_protocol_constants.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/tracing_controller.h" namespace content { namespace { const char kRecordUntilFull[] = "record-until-full"; const char kRecordContinuously[] = "record-continuously"; const char kEnableSampling[] = "enable-sampling"; void ReadFile( const base::FilePath& path, const base::Callback<void(const scoped_refptr<base::RefCountedString>&)> callback) { std::string trace_data; if (!base::ReadFileToString(path, &trace_data)) LOG(ERROR) << "Failed to read file: " << path.value(); base::DeleteFile(path, false); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(callback, make_scoped_refptr( base::RefCountedString::TakeString(&trace_data)))); } } // namespace DevToolsTracingHandler::DevToolsTracingHandler() : weak_factory_(this) { RegisterCommandHandler(devtools::Tracing::start::kName, base::Bind(&DevToolsTracingHandler::OnStart, base::Unretained(this))); RegisterCommandHandler(devtools::Tracing::end::kName, base::Bind(&DevToolsTracingHandler::OnEnd, base::Unretained(this))); } DevToolsTracingHandler::~DevToolsTracingHandler() { } void DevToolsTracingHandler::BeginReadingRecordingResult( const base::FilePath& path) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, base::Bind(&ReadFile, path, base::Bind(&DevToolsTracingHandler::ReadRecordingResult, weak_factory_.GetWeakPtr()))); } void DevToolsTracingHandler::ReadRecordingResult( const scoped_refptr<base::RefCountedString>& trace_data) { if (trace_data->data().size()) { scoped_ptr<base::Value> trace_value(base::JSONReader::Read( trace_data->data())); base::DictionaryValue* dictionary = NULL; bool ok = trace_value->GetAsDictionary(&dictionary); DCHECK(ok); base::ListValue* list = NULL; ok = dictionary->GetList("traceEvents", &list); DCHECK(ok); std::string buffer; for (size_t i = 0; i < list->GetSize(); ++i) { std::string item; base::Value* item_value; list->Get(i, &item_value); base::JSONWriter::Write(item_value, &item); if (buffer.size()) buffer.append(","); buffer.append(item); if (i % 1000 == 0) { OnTraceDataCollected(buffer); buffer.clear(); } } if (buffer.size()) OnTraceDataCollected(buffer); } SendNotification(devtools::Tracing::tracingComplete::kName, NULL); } void DevToolsTracingHandler::OnTraceDataCollected( const std::string& trace_fragment) { // Hand-craft protocol notification message so we can substitute JSON // that we already got as string as a bare object, not a quoted string. std::string message = base::StringPrintf( "{ \"method\": \"%s\", \"params\": { \"%s\": [ %s ] } }", devtools::Tracing::dataCollected::kName, devtools::Tracing::dataCollected::kParamValue, trace_fragment.c_str()); SendRawMessage(message); } TracingController::Options DevToolsTracingHandler::TraceOptionsFromString( const std::string& options) { std::vector<std::string> split; std::vector<std::string>::iterator iter; int ret = 0; base::SplitString(options, ',', &split); for (iter = split.begin(); iter != split.end(); ++iter) { if (*iter == kRecordUntilFull) { ret &= ~TracingController::RECORD_CONTINUOUSLY; } else if (*iter == kRecordContinuously) { ret |= TracingController::RECORD_CONTINUOUSLY; } else if (*iter == kEnableSampling) { ret |= TracingController::ENABLE_SAMPLING; } } return static_cast<TracingController::Options>(ret); } scoped_refptr<DevToolsProtocol::Response> DevToolsTracingHandler::OnStart( scoped_refptr<DevToolsProtocol::Command> command) { std::string categories; base::DictionaryValue* params = command->params(); if (params) params->GetString(devtools::Tracing::start::kParamCategories, &categories); TracingController::Options options = TracingController::DEFAULT_OPTIONS; if (params && params->HasKey(devtools::Tracing::start::kParamOptions)) { std::string options_param; params->GetString(devtools::Tracing::start::kParamOptions, &options_param); options = TraceOptionsFromString(options_param); } TracingController::GetInstance()->EnableRecording( categories, options, base::Bind(&DevToolsTracingHandler::OnTracingStarted, weak_factory_.GetWeakPtr(), command)); return command->AsyncResponsePromise(); } void DevToolsTracingHandler::OnTracingStarted( scoped_refptr<DevToolsProtocol::Command> command) { SendAsyncResponse(command->SuccessResponse(NULL)); } scoped_refptr<DevToolsProtocol::Response> DevToolsTracingHandler::OnEnd( scoped_refptr<DevToolsProtocol::Command> command) { TracingController::GetInstance()->DisableRecording( base::FilePath(), base::Bind(&DevToolsTracingHandler::BeginReadingRecordingResult, weak_factory_.GetWeakPtr())); return command->SuccessResponse(NULL); } } // namespace content