diff options
-rw-r--r-- | content/browser/debugger/devtools_browser_target.cc | 118 | ||||
-rw-r--r-- | content/browser/debugger/devtools_browser_target.h | 68 | ||||
-rw-r--r-- | content/browser/debugger/devtools_http_handler_impl.cc | 36 | ||||
-rw-r--r-- | content/browser/debugger/devtools_http_handler_impl.h | 4 | ||||
-rw-r--r-- | content/browser/debugger/devtools_tracing_handler.cc | 103 | ||||
-rw-r--r-- | content/browser/debugger/devtools_tracing_handler.h | 50 | ||||
-rw-r--r-- | content/content_browser.gypi | 4 | ||||
-rw-r--r-- | tools/telemetry/telemetry/browser.py | 13 | ||||
-rw-r--r-- | tools/telemetry/telemetry/browser_backend.py | 20 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page_runner.py | 79 | ||||
-rw-r--r-- | tools/telemetry/telemetry/tracing_backend.py | 63 | ||||
-rw-r--r-- | tools/telemetry/telemetry/tracing_backend_unittest.py | 33 |
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", ¶ms); + + 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']) |