summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/browser/debugger/devtools_browser_target.cc118
-rw-r--r--content/browser/debugger/devtools_browser_target.h68
-rw-r--r--content/browser/debugger/devtools_http_handler_impl.cc36
-rw-r--r--content/browser/debugger/devtools_http_handler_impl.h4
-rw-r--r--content/browser/debugger/devtools_tracing_handler.cc103
-rw-r--r--content/browser/debugger/devtools_tracing_handler.h50
-rw-r--r--content/content_browser.gypi4
-rw-r--r--tools/telemetry/telemetry/browser.py13
-rw-r--r--tools/telemetry/telemetry/browser_backend.py20
-rw-r--r--tools/telemetry/telemetry/page_runner.py79
-rw-r--r--tools/telemetry/telemetry/tracing_backend.py63
-rw-r--r--tools/telemetry/telemetry/tracing_backend_unittest.py33
12 files changed, 534 insertions, 57 deletions
diff --git a/content/browser/debugger/devtools_browser_target.cc b/content/browser/debugger/devtools_browser_target.cc
new file mode 100644
index 0000000..95d57a9
--- /dev/null
+++ b/content/browser/debugger/devtools_browser_target.cc
@@ -0,0 +1,118 @@
+// 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/debugger/devtools_browser_target.h"
+
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+
+namespace content {
+
+DevToolsBrowserTarget::DevToolsBrowserTarget(int connection_id)
+ : connection_id_(connection_id) {
+}
+
+DevToolsBrowserTarget::~DevToolsBrowserTarget() {
+ for (HandlersMap::iterator i = handlers_.begin(); i != handlers_.end(); ++i)
+ delete i->second;
+}
+
+void DevToolsBrowserTarget::RegisterHandler(Handler* handler) {
+ std::string domain = handler->Domain();
+ DCHECK(handlers_.find(domain) == handlers_.end());
+ handlers_[domain] = handler;
+}
+
+std::string DevToolsBrowserTarget::HandleMessage(const std::string& data) {
+ int error_code;
+ std::string error_message;
+ scoped_ptr<base::Value> command(
+ base::JSONReader::ReadAndReturnError(
+ data, 0, &error_code, &error_message));
+
+ if (!command || !command->IsType(base::Value::TYPE_DICTIONARY))
+ return SerializeErrorResponse(
+ -1, CreateErrorObject(error_code, error_message));
+
+ int request_id;
+ std::string domain;
+ std::string method;
+ base::DictionaryValue* command_dict = NULL;
+ bool ok = true;
+ ok &= command->GetAsDictionary(&command_dict);
+ ok &= command_dict->GetInteger("id", &request_id);
+ ok &= command_dict->GetString("method", &method);
+ if (!ok)
+ return SerializeErrorResponse(
+ request_id, CreateErrorObject(-1, "Malformed request"));
+
+ base::DictionaryValue* params = NULL;
+ command_dict->GetDictionary("params", &params);
+
+ size_t pos = method.find(".");
+ if (pos == std::string::npos)
+ return SerializeErrorResponse(
+ request_id, CreateErrorObject(-1, "Method unsupported"));
+
+ domain = method.substr(0, pos);
+ if (domain.empty() || handlers_.find(domain) == handlers_.end())
+ return SerializeErrorResponse(
+ request_id, CreateErrorObject(-1, "Domain unsupported"));
+
+ base::Value* error_object = NULL;
+ base::Value* domain_result = handlers_[domain]->OnProtocolCommand(
+ method, params, &error_object);
+
+ if (error_object)
+ return SerializeErrorResponse(request_id, error_object);
+
+ if (!domain_result)
+ return SerializeErrorResponse(
+ request_id, CreateErrorObject(-1, "Invalid call"));
+
+ DictionaryValue* response = new DictionaryValue();
+ response->Set("result", domain_result);
+ return SerializeResponse(request_id, response);
+}
+
+std::string DevToolsBrowserTarget::SerializeErrorResponse(
+ int request_id, base::Value* error_object) {
+ scoped_ptr<base::DictionaryValue> error_response(new base::DictionaryValue());
+ error_response->SetInteger("id", request_id);
+ error_response->Set("error", error_object);
+ // Serialize response.
+ std::string json_response;
+ base::JSONWriter::WriteWithOptions(error_response.get(),
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &json_response);
+ return json_response;
+}
+
+base::Value* DevToolsBrowserTarget::CreateErrorObject(
+ int error_code, const std::string& message) {
+ base::DictionaryValue* error_object = new base::DictionaryValue();
+ error_object->SetInteger("code", error_code);
+ error_object->SetString("message", message);
+ return error_object;
+}
+
+std::string DevToolsBrowserTarget::SerializeResponse(
+ int request_id, base::Value* response) {
+ scoped_ptr<base::DictionaryValue> ret(new base::DictionaryValue());
+ ret->SetInteger("id", request_id);
+ ret->Set("response", response);
+
+ // Serialize response.
+ std::string json_response;
+ base::JSONWriter::WriteWithOptions(ret.get(),
+ base::JSONWriter::OPTIONS_PRETTY_PRINT,
+ &json_response);
+ return json_response;
+}
+
+} // namespace content
diff --git a/content/browser/debugger/devtools_browser_target.h b/content/browser/debugger/devtools_browser_target.h
new file mode 100644
index 0000000..14e141f
--- /dev/null
+++ b/content/browser/debugger/devtools_browser_target.h
@@ -0,0 +1,68 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_DEBUGGER_DEVTOOLS_BROWSER_TARGET_H_
+#define CONTENT_BROWSER_DEBUGGER_DEVTOOLS_BROWSER_TARGET_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace base {
+
+class DictionaryValue;
+class Value;
+
+} // namespace base
+
+namespace content {
+
+// This class handles the "Browser" target for remote debugging.
+class DevToolsBrowserTarget {
+ public:
+ class Handler {
+ public:
+ virtual ~Handler() {}
+
+ // Returns the domain name for this handler.
+ virtual std::string Domain() = 0;
+
+ // |return_value| and |error_message_out| ownership is transferred to the
+ // caller.
+ virtual base::Value* OnProtocolCommand(
+ const std::string& method,
+ const base::DictionaryValue* params,
+ base::Value** error_message_out) = 0;
+ };
+
+ explicit DevToolsBrowserTarget(int connection_id);
+ ~DevToolsBrowserTarget();
+
+ int connection_id() const { return connection_id_; }
+
+ // Takes ownership of |handler|.
+ void RegisterHandler(Handler* handler);
+
+ std::string HandleMessage(const std::string& data);
+
+ private:
+ const int connection_id_;
+
+ typedef std::map<std::string, Handler*> HandlersMap;
+ HandlersMap handlers_;
+
+ // Takes ownership of |error_object|.
+ std::string SerializeErrorResponse(int request_id, base::Value* error_object);
+
+ base::Value* CreateErrorObject(int error_code, const std::string& message);
+
+ std::string SerializeResponse(int request_id, base::Value* response);
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsBrowserTarget);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEBUGGER_DEVTOOLS_BROWSER_TARGET_H_
diff --git a/content/browser/debugger/devtools_http_handler_impl.cc b/content/browser/debugger/devtools_http_handler_impl.cc
index a9feb7e..a62d4bd 100644
--- a/content/browser/debugger/devtools_http_handler_impl.cc
+++ b/content/browser/debugger/devtools_http_handler_impl.cc
@@ -19,6 +19,8 @@
#include "base/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
+#include "content/browser/debugger/devtools_browser_target.h"
+#include "content/browser/debugger/devtools_tracing_handler.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/devtools_messages.h"
#include "content/public/browser/browser_thread.h"
@@ -597,15 +599,27 @@ void DevToolsHttpHandlerImpl::OnWebSocketRequestUI(
const net::HttpServerRequestInfo& request) {
if (!thread_.get())
return;
+ std::string browser_prefix = "/devtools/browser";
+ size_t browser_pos = request.path.find(browser_prefix);
+ if (browser_pos == 0) {
+ if (browser_target_) {
+ Send500(connection_id, "Another client already attached");
+ return;
+ }
+ browser_target_.reset(new DevToolsBrowserTarget(connection_id));
+ browser_target_->RegisterHandler(new DevToolsTracingHandler());
+ AcceptWebSocket(connection_id, request);
+ return;
+ }
- std::string prefix = "/devtools/page/";
- size_t pos = request.path.find(prefix);
+ std::string page_prefix = "/devtools/page/";
+ size_t pos = request.path.find(page_prefix);
if (pos != 0) {
Send404(connection_id);
return;
}
- std::string page_id = request.path.substr(prefix.length());
+ std::string page_id = request.path.substr(page_prefix.length());
RenderViewHost* rvh = binding_->ForIdentifier(page_id);
if (!rvh) {
Send500(connection_id, "No such target id: " + page_id);
@@ -635,6 +649,18 @@ void DevToolsHttpHandlerImpl::OnWebSocketRequestUI(
void DevToolsHttpHandlerImpl::OnWebSocketMessageUI(
int connection_id,
const std::string& data) {
+ if (browser_target_ && connection_id == browser_target_->connection_id()) {
+ std::string json_response = browser_target_->HandleMessage(data);
+
+ thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::SendOverWebSocket,
+ server_.get(),
+ connection_id,
+ json_response));
+ return;
+ }
+
ConnectionToClientHostMap::iterator it =
connection_to_client_host_ui_.find(connection_id);
if (it == connection_to_client_host_ui_.end())
@@ -654,6 +680,10 @@ void DevToolsHttpHandlerImpl::OnCloseUI(int connection_id) {
delete client_host;
connection_to_client_host_ui_.erase(connection_id);
}
+ if (browser_target_ && browser_target_->connection_id() == connection_id) {
+ browser_target_.reset();
+ return;
+ }
}
DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
diff --git a/content/browser/debugger/devtools_http_handler_impl.h b/content/browser/debugger/devtools_http_handler_impl.h
index c912b92..ef36be8 100644
--- a/content/browser/debugger/devtools_http_handler_impl.h
+++ b/content/browser/debugger/devtools_http_handler_impl.h
@@ -32,6 +32,7 @@ class URLRequestContextGetter;
namespace content {
+class DevToolsBrowserTarget;
class DevToolsClientHost;
class RenderViewHost;
@@ -79,7 +80,7 @@ class DevToolsHttpHandlerImpl
void OnJsonRequestUI(int connection_id,
const net::HttpServerRequestInfo& info);
void OnThumbnailRequestUI(int connection_id,
- const net::HttpServerRequestInfo& info);
+ const net::HttpServerRequestInfo& info);
void OnDiscoveryPageRequestUI(int connection_id);
void OnWebSocketRequestUI(int connection_id,
@@ -133,6 +134,7 @@ class DevToolsHttpHandlerImpl
RenderViewHostBinding* binding_;
scoped_ptr<RenderViewHostBinding> default_binding_;
NotificationRegistrar registrar_;
+ scoped_ptr<DevToolsBrowserTarget> browser_target_;
DISALLOW_COPY_AND_ASSIGN(DevToolsHttpHandlerImpl);
};
diff --git a/content/browser/debugger/devtools_tracing_handler.cc b/content/browser/debugger/devtools_tracing_handler.cc
new file mode 100644
index 0000000..f1eb740
--- /dev/null
+++ b/content/browser/debugger/devtools_tracing_handler.cc
@@ -0,0 +1,103 @@
+// 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/debugger/devtools_tracing_handler.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/values.h"
+#include "content/browser/debugger/devtools_http_handler_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/trace_controller.h"
+#include "content/public/browser/trace_subscriber.h"
+
+namespace content {
+
+DevToolsTracingHandler::DevToolsTracingHandler()
+ : has_completed_(false),
+ buffer_data_size_(0) {
+}
+
+DevToolsTracingHandler::~DevToolsTracingHandler() {
+}
+
+std::string DevToolsTracingHandler::Domain() {
+ return "Tracing";
+}
+
+void DevToolsTracingHandler::OnEndTracingComplete() {
+ has_completed_ = true;
+}
+
+void DevToolsTracingHandler::OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& trace_fragment) {
+ buffer_.push_back(trace_fragment->data());
+ buffer_data_size_ += trace_fragment->data().size();
+}
+
+base::Value* DevToolsTracingHandler::OnProtocolCommand(
+ const std::string& method,
+ const base::DictionaryValue* params,
+ base::Value** error_out) {
+ if (method == "Tracing.start")
+ return Start(params);
+ else if (method == "Tracing.end")
+ return End(params);
+ else if (method == "Tracing.hasCompleted")
+ return HasCompleted(params);
+ else if (method == "Tracing.getTraceAndReset")
+ return GetTraceAndReset(params);
+
+ base::DictionaryValue* error_object = new base::DictionaryValue();
+ error_object->SetInteger("code", -1);
+ error_object->SetString("message", "Invalid method");
+
+ *error_out = error_object;
+
+ return NULL;
+}
+
+base::Value* DevToolsTracingHandler::Start(
+ const base::DictionaryValue* params) {
+ std::string categories;
+ if (params && params->HasKey("categories"))
+ params->GetString("categories", &categories);
+ TraceController::GetInstance()->BeginTracing(this, categories);
+
+ return base::Value::CreateBooleanValue(true);
+}
+
+base::Value* DevToolsTracingHandler::End(
+ const base::DictionaryValue* /* params */) {
+ TraceController::GetInstance()->EndTracingAsync(this);
+
+ return base::Value::CreateBooleanValue(true);
+}
+
+
+base::Value* DevToolsTracingHandler::HasCompleted(
+ const base::DictionaryValue* /* params */) {
+
+ return base::Value::CreateBooleanValue(has_completed_);
+}
+
+base::Value* DevToolsTracingHandler::GetTraceAndReset(
+ const base::DictionaryValue* /* params */) {
+ std::string ret;
+ ret.reserve(buffer_data_size_);
+ for (std::vector<std::string>::const_iterator i = buffer_.begin();
+ i != buffer_.end(); ++i) {
+ if (!ret.empty())
+ ret.append(",");
+ ret.append(*i);
+ }
+ buffer_.clear();
+ has_completed_ = false;
+ buffer_data_size_ = 0;
+
+ return base::Value::CreateStringValue(ret);
+}
+
+} // namespace content
diff --git a/content/browser/debugger/devtools_tracing_handler.h b/content/browser/debugger/devtools_tracing_handler.h
new file mode 100644
index 0000000..a5858fc
--- /dev/null
+++ b/content/browser/debugger/devtools_tracing_handler.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_DEBUGGER_DEVTOOLS_TRACING_HANDLER_H_
+#define CONTENT_BROWSER_DEBUGGER_DEVTOOLS_TRACING_HANDLER_H_
+
+#include "content/browser/debugger/devtools_browser_target.h"
+#include "content/public/browser/trace_subscriber.h"
+
+namespace content {
+
+// This class bridges DevTools remote debugging server with the trace
+// infrastructure.
+class DevToolsTracingHandler
+ : public TraceSubscriber,
+ public DevToolsBrowserTarget::Handler {
+ public:
+ DevToolsTracingHandler();
+ virtual ~DevToolsTracingHandler();
+
+ // TraceSubscriber:
+ virtual void OnEndTracingComplete() OVERRIDE;;
+ virtual void OnTraceDataCollected(
+ const scoped_refptr<base::RefCountedString>& trace_fragment) OVERRIDE;
+
+ // DevToolBrowserTarget::Handler:
+ virtual std::string Domain() OVERRIDE;
+ virtual base::Value* OnProtocolCommand(
+ const std::string& method,
+ const base::DictionaryValue* params,
+ base::Value** error_out) OVERRIDE;
+
+ private:
+ base::Value* Start(const base::DictionaryValue* params);
+ base::Value* End(const base::DictionaryValue* params);
+ base::Value* HasCompleted(const base::DictionaryValue* params);
+ base::Value* GetTraceAndReset(const base::DictionaryValue* params);
+
+ bool has_completed_;
+
+ std::vector<std::string> buffer_;
+ int buffer_data_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(DevToolsTracingHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_DEBUGGER_DEVTOOLS_TRACING_HANDLER_H_
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 03c9331..3fdb8fd 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -287,6 +287,8 @@
'browser/cross_site_request_manager.h',
'browser/debugger/devtools_agent_host.cc',
'browser/debugger/devtools_agent_host.h',
+ 'browser/debugger/devtools_browser_target.cc',
+ 'browser/debugger/devtools_browser_target.h',
'browser/debugger/devtools_frontend_host.cc',
'browser/debugger/devtools_frontend_host.h',
'browser/debugger/devtools_http_handler_impl.cc',
@@ -295,6 +297,8 @@
'browser/debugger/devtools_manager_impl.h',
'browser/debugger/devtools_netlog_observer.cc',
'browser/debugger/devtools_netlog_observer.h',
+ 'browser/debugger/devtools_tracing_handler.h',
+ 'browser/debugger/devtools_tracing_handler.cc',
'browser/debugger/render_view_devtools_agent_host.cc',
'browser/debugger/render_view_devtools_agent_host.h',
'browser/debugger/worker_devtools_manager.cc',
diff --git a/tools/telemetry/telemetry/browser.py b/tools/telemetry/telemetry/browser.py
index a01cbfc..cfa2569 100644
--- a/tools/telemetry/telemetry/browser.py
+++ b/tools/telemetry/telemetry/browser.py
@@ -54,6 +54,19 @@ class Browser(object):
def tabs(self):
return self._backend.tabs
+ @property
+ def supports_tracing(self):
+ return self._backend.supports_tracing
+
+ def StartTracing(self):
+ return self._backend.StartTracing()
+
+ def StopTracing(self):
+ return self._backend.StopTracing()
+
+ def GetTrace(self):
+ return self._backend.GetTrace()
+
def Close(self):
"""Closes this browser."""
if self._wpr_server:
diff --git a/tools/telemetry/telemetry/browser_backend.py b/tools/telemetry/telemetry/browser_backend.py
index 5afa946..11eecf9 100644
--- a/tools/telemetry/telemetry/browser_backend.py
+++ b/tools/telemetry/telemetry/browser_backend.py
@@ -1,6 +1,7 @@
# 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.
+
import urllib2
import httplib
import socket
@@ -10,6 +11,7 @@ import weakref
from telemetry import browser_gone_exception
from telemetry import tab
+from telemetry import tracing_backend
from telemetry import user_agent
from telemetry import util
from telemetry import wpr_modes
@@ -123,6 +125,7 @@ class BrowserBackend(object):
self._inspector_protocol_version = 0
self._chrome_branch_number = 0
self._webkit_base_revision = 0
+ self._tracing_backend = None
def SetBrowser(self, browser):
self.tabs = TabController(browser, self)
@@ -186,6 +189,23 @@ class BrowserBackend(object):
def supports_tab_control(self):
return self._chrome_branch_number >= 1303
+ @property
+ def supports_tracing(self):
+ return True
+
+ def StartTracing(self):
+ self._tracing_backend = tracing_backend.TracingBackend(self._port)
+ self._tracing_backend.BeginTracing()
+
+ def StopTracing(self):
+ self._tracing_backend.EndTracingAsync()
+
+ def GetTrace(self):
+ def IsTracingRunning(self):
+ return not self._tracing_backend.HasCompleted()
+ util.WaitFor(lambda: not IsTracingRunning(self), 10)
+ return self._tracing_backend.GetTraceAndReset()
+
def CreateForwarder(self, host_port):
raise NotImplementedError()
diff --git a/tools/telemetry/telemetry/page_runner.py b/tools/telemetry/telemetry/page_runner.py
index ffe2251..c19d74a 100644
--- a/tools/telemetry/telemetry/page_runner.py
+++ b/tools/telemetry/telemetry/page_runner.py
@@ -23,13 +23,9 @@ class _RunState(object):
self.first_browser = True
self.browser = None
self.tab = None
- self.trace_tab = None
+ self.is_tracing = False
def Close(self):
- if self.trace_tab:
- self.trace_tab.Disconnect()
- self.trace_tab = None
-
if self.tab:
self.tab.Disconnect()
self.tab = None
@@ -136,7 +132,7 @@ http://goto/read-src-internal, or create a new archive using record_wpr.
logging.warning('Tab crashed: %s%s', page.url, stdout)
state.Close()
- if options.trace_dir and state.trace_tab:
+ if options.trace_dir:
self._EndTracing(state, options, page)
break
except browser_gone_exception.BrowserGoneException:
@@ -228,56 +224,33 @@ http://goto/read-src-internal, or create a new archive using record_wpr.
state.browser.SetReplayArchivePath(archive_path)
def _SetupTracingTab(self, state):
- if not state.trace_tab:
- # Swap the two tabs because new tabs open to about:blank, and we
- # can't navigate across protocols to chrome://tracing. The initial
- # tab starts at chrome://newtab, so it works for that tab.
- # TODO(dtu): If the trace_tab crashes, we're hosed.
- state.trace_tab = state.tab
- state.tab = state.browser.tabs.New()
+ if state.browser.supports_tracing:
+ state.is_tracing = True
+ state.browser.StartTracing()
- state.trace_tab.page.Navigate('chrome://tracing')
- state.trace_tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
+ def _EndTracing(self, state, options, page):
+ if state.is_tracing:
+ state.is_tracing = False
+ state.browser.StopTracing()
+ trace = state.browser.GetTrace()
+ logging.info('Processing trace...')
- # Start tracing.
- state.trace_tab.runtime.Execute('tracingController.beginTracing('
- 'tracingController.supportsSystemTracing);')
+ trace_file_base = os.path.join(
+ options.trace_dir, page.url_as_file_safe_name)
- def _EndTracing(self, state, options, page):
- def IsTracingRunning():
- return state.trace_tab.runtime.Evaluate(
- 'tracingController.isTracingEnabled')
- # Tracing might have ended already if the buffer filled up.
- if IsTracingRunning():
- state.trace_tab.runtime.Execute('tracingController.endTracing()')
- util.WaitFor(lambda: not IsTracingRunning(), 10)
-
- logging.info('Processing trace...')
-
- trace_file_base = os.path.join(
- options.trace_dir, page.url_as_file_safe_name)
-
- if options.page_repeat != 1 or options.pageset_repeat != 1:
- trace_file_index = 0
-
- while True:
- trace_file = '%s_%03d.json' % (trace_file_base, trace_file_index)
- if not os.path.exists(trace_file):
- break
- trace_file_index = trace_file_index + 1
- else:
- trace_file = '%s.json' % trace_file_base
-
- with open(trace_file, 'w') as trace_file:
- trace_file.write(state.trace_tab.runtime.Evaluate("""
- JSON.stringify({
- traceEvents: tracingController.traceEvents,
- systemTraceEvents: tracingController.systemTraceEvents,
- clientInfo: tracingController.clientInfo_,
- gpuInfo: tracingController.gpuInfo_
- });
- """))
- logging.info('Trace saved.')
+ if options.page_repeat != 1 or options.pageset_repeat != 1:
+ trace_file_index = 0
+
+ while True:
+ trace_file = '%s_%03d.json' % (trace_file_base, trace_file_index)
+ if not os.path.exists(trace_file):
+ break
+ trace_file_index = trace_file_index + 1
+ else:
+ trace_file = '%s.json' % trace_file_base
+ with open(trace_file, 'w') as trace_file:
+ trace_file.write(trace)
+ logging.info('Trace saved.')
def _PreparePage(self, page, tab, page_state, results):
parsed_url = urlparse.urlparse(page.url)
diff --git a/tools/telemetry/telemetry/tracing_backend.py b/tools/telemetry/telemetry/tracing_backend.py
new file mode 100644
index 0000000..54f9057
--- /dev/null
+++ b/tools/telemetry/telemetry/tracing_backend.py
@@ -0,0 +1,63 @@
+# 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.
+
+import json
+import logging
+import socket
+
+from telemetry import util
+from telemetry import websocket
+
+
+class TracingBackend(object):
+ def __init__(self, devtools_port):
+ debugger_url = 'ws://localhost:%i/devtools/browser' % devtools_port
+ self._socket = websocket.create_connection(debugger_url)
+ self._next_request_id = 0
+ self._cur_socket_timeout = 0
+
+ def BeginTracing(self):
+ req = {'method': 'Tracing.start'}
+ self._SyncRequest(req)
+
+ def EndTracingAsync(self):
+ req = {'method': 'Tracing.end'}
+ self._SyncRequest(req)
+
+ def HasCompleted(self):
+ req = {'method': 'Tracing.hasCompleted'}
+ r = self._SyncRequest(req)
+ return r['response']['result']
+
+ def GetTraceAndReset(self):
+ req = {'method': 'Tracing.getTraceAndReset'}
+ r = self._SyncRequest(req)
+ return '{"traceEvents":[' + r['response']['result'] + ']}'
+
+ def _SyncRequest(self, req, timeout=10):
+ self._SetTimeout(timeout)
+ req['id'] = self._next_request_id
+ self._next_request_id += 1
+ data = json.dumps(req)
+ logging.debug('will send [%s]', data)
+ self._socket.send(data)
+
+ while True:
+ try:
+ data = self._socket.recv()
+ except (socket.error, websocket.WebSocketException):
+ raise util.TimeoutException(
+ 'Timed out waiting for reply. This is unusual.')
+
+ logging.debug('got [%s]', data)
+ res = json.loads(data)
+ if res['id'] != req['id']:
+ logging.debug('Dropped reply: %s', json.dumps(res))
+ continue
+ return res
+
+ def _SetTimeout(self, timeout):
+ if self._cur_socket_timeout != timeout:
+ self._socket.settimeout(timeout)
+ self._cur_socket_timeout = timeout
diff --git a/tools/telemetry/telemetry/tracing_backend_unittest.py b/tools/telemetry/telemetry/tracing_backend_unittest.py
new file mode 100644
index 0000000..7a8370d
--- /dev/null
+++ b/tools/telemetry/telemetry/tracing_backend_unittest.py
@@ -0,0 +1,33 @@
+# 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.
+
+import json
+import os
+
+from telemetry import tab_test_case
+from telemetry import util
+
+
+class TracingBackendTest(tab_test_case.TabTestCase):
+ def _StartServer(self):
+ base_dir = os.path.dirname(__file__)
+ self._browser.SetHTTPServerDirectory(os.path.join(base_dir, '..',
+ 'unittest_data'))
+
+ def _WaitForAnimationFrame(self):
+ def _IsDone():
+ js_is_done = """done"""
+ return bool(self._tab.runtime.Evaluate(js_is_done))
+ util.WaitFor(_IsDone, 5)
+
+ def testGotTrace(self):
+ self._StartServer()
+ self._browser.StartTracing()
+ self._browser.http_server.UrlOf('image.png')
+ self.assertTrue(self._browser.supports_tracing)
+ self._browser.StopTracing()
+ trace = self._browser.GetTrace()
+ json_trace = json.loads(trace)
+ self.assertTrue('traceEvents' in json_trace)
+ self.assertTrue(json_trace['traceEvents'])