| Index: content/browser/devtools/devtools_tracing_handler.cc
|
| diff --git a/content/browser/devtools/devtools_tracing_handler.cc b/content/browser/devtools/devtools_tracing_handler.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f8539da4b067493b188f70fa4fd8344515e4e7f4
|
| --- /dev/null
|
| +++ b/content/browser/devtools/devtools_tracing_handler.cc
|
| @@ -0,0 +1,241 @@
|
| +// 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 <cmath>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/debug/trace_event_impl.h"
|
| +#include "base/strings/string_split.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "base/time/time.h"
|
| +#include "base/timer/timer.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 kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
|
| +const char kEnableSampling[] = "enable-sampling";
|
| +
|
| +class DevToolsTraceSinkProxy : public TracingController::TraceDataSink {
|
| + public:
|
| + explicit DevToolsTraceSinkProxy(base::WeakPtr<DevToolsTracingHandler> handler)
|
| + : tracing_handler_(handler) {}
|
| +
|
| + void AddTraceChunk(const std::string& chunk) override {
|
| + if (DevToolsTracingHandler* h = tracing_handler_.get())
|
| + h->OnTraceDataCollected(chunk);
|
| + }
|
| + void Close() override {
|
| + if (DevToolsTracingHandler* h = tracing_handler_.get())
|
| + h->OnTraceComplete();
|
| + }
|
| +
|
| + private:
|
| + ~DevToolsTraceSinkProxy() override {}
|
| +
|
| + base::WeakPtr<DevToolsTracingHandler> tracing_handler_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +const char* DevToolsTracingHandler::kDefaultCategories =
|
| + "-*,disabled-by-default-devtools.timeline*";
|
| +const double DevToolsTracingHandler::kDefaultReportingInterval = 1000.0;
|
| +const double DevToolsTracingHandler::kMinimumReportingInterval = 250.0;
|
| +
|
| +DevToolsTracingHandler::DevToolsTracingHandler(
|
| + DevToolsTracingHandler::Target target)
|
| + : target_(target), is_recording_(false), 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)));
|
| + RegisterCommandHandler(devtools::Tracing::getCategories::kName,
|
| + base::Bind(&DevToolsTracingHandler::OnGetCategories,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +DevToolsTracingHandler::~DevToolsTracingHandler() {
|
| +}
|
| +
|
| +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\": [",
|
| + devtools::Tracing::dataCollected::kName,
|
| + devtools::Tracing::dataCollected::kParamValue);
|
| + const size_t messageSuffixSize = 10;
|
| + message.reserve(message.size() + trace_fragment.size() + messageSuffixSize);
|
| + message += trace_fragment;
|
| + message += "] } }", SendRawMessage(message);
|
| +}
|
| +
|
| +void DevToolsTracingHandler::OnTraceComplete() {
|
| + SendNotification(devtools::Tracing::tracingComplete::kName, NULL);
|
| +}
|
| +
|
| +base::debug::TraceOptions DevToolsTracingHandler::TraceOptionsFromString(
|
| + const std::string& options) {
|
| + std::vector<std::string> split;
|
| + std::vector<std::string>::iterator iter;
|
| + base::debug::TraceOptions ret;
|
| +
|
| + base::SplitString(options, ',', &split);
|
| + for (iter = split.begin(); iter != split.end(); ++iter) {
|
| + if (*iter == kRecordUntilFull) {
|
| + ret.record_mode = base::debug::RECORD_UNTIL_FULL;
|
| + } else if (*iter == kRecordContinuously) {
|
| + ret.record_mode = base::debug::RECORD_CONTINUOUSLY;
|
| + } else if (*iter == kRecordAsMuchAsPossible) {
|
| + ret.record_mode = base::debug::RECORD_AS_MUCH_AS_POSSIBLE;
|
| + } else if (*iter == kEnableSampling) {
|
| + ret.enable_sampling = true;
|
| + }
|
| + }
|
| + return ret;
|
| +}
|
| +
|
| +scoped_refptr<DevToolsProtocol::Response>
|
| +DevToolsTracingHandler::OnStart(
|
| + scoped_refptr<DevToolsProtocol::Command> command) {
|
| + if (is_recording_) {
|
| + return command->InternalErrorResponse("Tracing is already started");
|
| + }
|
| + is_recording_ = true;
|
| +
|
| + std::string categories;
|
| + base::debug::TraceOptions options;
|
| + double usage_reporting_interval = 0.0;
|
| +
|
| + base::DictionaryValue* params = command->params();
|
| + if (params) {
|
| + params->GetString(devtools::Tracing::start::kParamCategories, &categories);
|
| + std::string options_param;
|
| + if (params->GetString(devtools::Tracing::start::kParamOptions,
|
| + &options_param)) {
|
| + options = TraceOptionsFromString(options_param);
|
| + }
|
| + params->GetDouble(
|
| + devtools::Tracing::start::kParamBufferUsageReportingInterval,
|
| + &usage_reporting_interval);
|
| + }
|
| +
|
| + SetupTimer(usage_reporting_interval);
|
| +
|
| + // If inspected target is a render process Tracing.start will be handled by
|
| + // tracing agent in the renderer.
|
| + if (target_ == Renderer) {
|
| + TracingController::GetInstance()->EnableRecording(
|
| + base::debug::CategoryFilter(categories),
|
| + options,
|
| + TracingController::EnableRecordingDoneCallback());
|
| + return NULL;
|
| + }
|
| +
|
| + TracingController::GetInstance()->EnableRecording(
|
| + base::debug::CategoryFilter(categories),
|
| + options,
|
| + base::Bind(&DevToolsTracingHandler::OnRecordingEnabled,
|
| + weak_factory_.GetWeakPtr(),
|
| + command));
|
| + return command->AsyncResponsePromise();
|
| +}
|
| +
|
| +void DevToolsTracingHandler::SetupTimer(double usage_reporting_interval) {
|
| + if (usage_reporting_interval == 0) return;
|
| +
|
| + if (usage_reporting_interval < kMinimumReportingInterval)
|
| + usage_reporting_interval = kMinimumReportingInterval;
|
| +
|
| + base::TimeDelta interval = base::TimeDelta::FromMilliseconds(
|
| + std::ceil(usage_reporting_interval));
|
| + buffer_usage_poll_timer_.reset(new base::Timer(
|
| + FROM_HERE,
|
| + interval,
|
| + base::Bind(
|
| + base::IgnoreResult(&TracingController::GetTraceBufferPercentFull),
|
| + base::Unretained(TracingController::GetInstance()),
|
| + base::Bind(&DevToolsTracingHandler::OnBufferUsage,
|
| + weak_factory_.GetWeakPtr())),
|
| + true));
|
| + buffer_usage_poll_timer_->Reset();
|
| +}
|
| +
|
| +void DevToolsTracingHandler::OnRecordingEnabled(
|
| + scoped_refptr<DevToolsProtocol::Command> command) {
|
| + SendAsyncResponse(command->SuccessResponse(NULL));
|
| +}
|
| +
|
| +void DevToolsTracingHandler::OnBufferUsage(float usage) {
|
| + base::DictionaryValue* params = new base::DictionaryValue();
|
| + params->SetDouble(devtools::Tracing::bufferUsage::kParamValue, usage);
|
| + SendNotification(devtools::Tracing::bufferUsage::kName, params);
|
| +}
|
| +
|
| +scoped_refptr<DevToolsProtocol::Response>
|
| +DevToolsTracingHandler::OnEnd(
|
| + scoped_refptr<DevToolsProtocol::Command> command) {
|
| + if (!is_recording_) {
|
| + return command->InternalErrorResponse("Tracing is not started");
|
| + }
|
| + DisableRecording(false);
|
| + // If inspected target is a render process Tracing.end will be handled by
|
| + // tracing agent in the renderer.
|
| + if (target_ == Renderer)
|
| + return NULL;
|
| + return command->SuccessResponse(NULL);
|
| +}
|
| +
|
| +void DevToolsTracingHandler::DisableRecording(bool abort) {
|
| + is_recording_ = false;
|
| + buffer_usage_poll_timer_.reset();
|
| + TracingController::GetInstance()->DisableRecording(
|
| + abort ? NULL : new DevToolsTraceSinkProxy(weak_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +void DevToolsTracingHandler::OnClientDetached() {
|
| + if (is_recording_)
|
| + DisableRecording(true);
|
| +}
|
| +
|
| +scoped_refptr<DevToolsProtocol::Response>
|
| +DevToolsTracingHandler::OnGetCategories(
|
| + scoped_refptr<DevToolsProtocol::Command> command) {
|
| + TracingController::GetInstance()->GetCategories(
|
| + base::Bind(&DevToolsTracingHandler::OnCategoriesReceived,
|
| + weak_factory_.GetWeakPtr(),
|
| + command));
|
| + return command->AsyncResponsePromise();
|
| +}
|
| +
|
| +void DevToolsTracingHandler::OnCategoriesReceived(
|
| + scoped_refptr<DevToolsProtocol::Command> command,
|
| + const std::set<std::string>& category_set) {
|
| + base::DictionaryValue* response = new base::DictionaryValue;
|
| + base::ListValue* category_list = new base::ListValue;
|
| + for (std::set<std::string>::const_iterator it = category_set.begin();
|
| + it != category_set.end(); ++it) {
|
| + category_list->AppendString(*it);
|
| + }
|
| +
|
| + response->Set(devtools::Tracing::getCategories::kResponseCategories,
|
| + category_list);
|
| + SendAsyncResponse(command->SuccessResponse(response));
|
| +}
|
| +
|
| +} // namespace content
|
|
|