Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / backends / chrome / inspector_backend.py
index 4770c4e..5e242ca 100644 (file)
 # Copyright 2013 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
+import os
 import sys
 
+from telemetry import decorators
+from telemetry.core import bitmap
 from telemetry.core import exceptions
 from telemetry.core import util
-from telemetry.core.backends import png_bitmap
 from telemetry.core.backends.chrome import inspector_console
 from telemetry.core.backends.chrome import inspector_memory
 from telemetry.core.backends.chrome import inspector_network
 from telemetry.core.backends.chrome import inspector_page
 from telemetry.core.backends.chrome import inspector_runtime
 from telemetry.core.backends.chrome import inspector_timeline
+from telemetry.core.backends.chrome import inspector_websocket
 from telemetry.core.backends.chrome import websocket
 from telemetry.core.heap import model
+from telemetry.core.timeline import model as timeline_model
+from telemetry.core.timeline import recording_options
+
 
 class InspectorException(Exception):
   pass
 
-class InspectorBackend(object):
-  def __init__(self, browser, browser_backend, debugger_url):
-    assert debugger_url
-    self._browser = browser
+
+class InspectorBackend(inspector_websocket.InspectorWebsocket):
+  def __init__(self, browser_backend, context, timeout=60):
+    super(InspectorBackend, self).__init__(self._HandleNotification,
+                                           self._HandleError)
+
     self._browser_backend = browser_backend
-    self._debugger_url = debugger_url
-    self._socket = None
+    self._context = context
     self._domain_handlers = {}
-    self._cur_socket_timeout = 0
-    self._next_request_id = 0
+
+    logging.debug('InspectorBackend._Connect() to %s' % self.debugger_url)
+    try:
+      self.Connect(self.debugger_url)
+    except (websocket.WebSocketException, util.TimeoutException):
+      err_msg = sys.exc_info()[1]
+      if not self._browser_backend.IsBrowserRunning():
+        raise exceptions.BrowserGoneException(err_msg)
+      elif not self._browser_backend.HasBrowserFinishedLaunching():
+        raise exceptions.BrowserConnectionGoneException(err_msg)
+      else:
+        raise exceptions.TabCrashException(err_msg)
 
     self._console = inspector_console.InspectorConsole(self)
     self._memory = inspector_memory.InspectorMemory(self)
-    self._page = inspector_page.InspectorPage(self)
+    self._page = inspector_page.InspectorPage(self, timeout=timeout)
     self._runtime = inspector_runtime.InspectorRuntime(self)
     self._timeline = inspector_timeline.InspectorTimeline(self)
     self._network = inspector_network.InspectorNetwork(self)
+    self._timeline_model = None
 
   def __del__(self):
     self.Disconnect()
 
-  def _Connect(self):
-    if self._socket:
-      return
-    try:
-      self._socket = websocket.create_connection(self._debugger_url)
-    except (websocket.WebSocketException):
-      if self._browser_backend.IsBrowserRunning():
-        raise exceptions.TabCrashException(sys.exc_info()[1])
-      else:
-        raise exceptions.BrowserGoneException()
-
-    self._cur_socket_timeout = 0
-    self._next_request_id = 0
-
   def Disconnect(self):
     for _, handlers in self._domain_handlers.items():
       _, will_close_handler = handlers
       will_close_handler()
     self._domain_handlers = {}
 
-    if self._socket:
-      self._socket.close()
-      self._socket = None
-
-  # General public methods.
+    super(InspectorBackend, self).Disconnect()
 
   @property
   def browser(self):
-    return self._browser
+    return self._browser_backend.browser
 
   @property
   def url(self):
-    self.Disconnect()
-    return self._browser_backend.tab_list_backend.GetTabUrl(self._debugger_url)
+    for c in self._browser_backend.ListInspectableContexts():
+      if c['id'] == self.id:
+        return c['url']
+    return None
 
-  def Activate(self):
-    self._Connect()
-    self._browser_backend.tab_list_backend.ActivateTab(self._debugger_url)
+  @property
+  def id(self):
+    return self._context['id']
 
-  def Close(self):
-    self.Disconnect()
-    self._browser_backend.tab_list_backend.CloseTab(self._debugger_url)
+  @property
+  def debugger_url(self):
+    return self._context['webSocketDebuggerUrl']
 
   # Public methods implemented in JavaScript.
 
   @property
+  @decorators.Cache
   def screenshot_supported(self):
-    if self._runtime.Evaluate(
-        'window.chrome.gpuBenchmarking === undefined'):
-      return False
-
-    if self._runtime.Evaluate(
-        'window.chrome.gpuBenchmarking.beginWindowSnapshotPNG === undefined'):
+    if (self.browser.platform.GetOSName() == 'linux' and (
+        os.getenv('DISPLAY') not in [':0', ':0.0'])):
+      # Displays other than 0 mean we are likely running in something like
+      # xvfb where screenshotting doesn't work.
       return False
-
-    return (self._browser_backend.chrome_branch_number >= 1391 or
-            self._browser_backend.is_content_shell)
+    return not self.EvaluateJavaScript("""
+        window.chrome.gpuBenchmarking === undefined ||
+        window.chrome.gpuBenchmarking.beginWindowSnapshotPNG === undefined
+      """)
 
   def Screenshot(self, timeout):
-    if self._runtime.Evaluate(
-        'window.chrome.gpuBenchmarking === undefined'):
-      raise Exception("Browser was not started with --enable-gpu-benchmarking")
-
-    if self._runtime.Evaluate(
-        'window.chrome.gpuBenchmarking.beginWindowSnapshotPNG === undefined'):
-      raise Exception("Browser does not support window snapshot API.")
+    assert self.screenshot_supported, 'Browser does not support screenshotting'
 
-    self._runtime.Evaluate("""
+    self.EvaluateJavaScript("""
         if(!window.__telemetry) {
           window.__telemetry = {}
         }
@@ -124,11 +119,12 @@ class InspectorBackend(object):
     """)
 
     def IsSnapshotComplete():
-      return self._runtime.Evaluate('window.__telemetry.snapshotComplete')
+      return self.EvaluateJavaScript(
+          'window.__telemetry.snapshotComplete')
 
     util.WaitFor(IsSnapshotComplete, timeout)
 
-    snap = self._runtime.Evaluate("""
+    snap = self.EvaluateJavaScript("""
       (function() {
         var data = window.__telemetry.snapshotData;
         delete window.__telemetry.snapshotComplete;
@@ -137,7 +133,7 @@ class InspectorBackend(object):
       })()
     """)
     if snap:
-      return png_bitmap.PngBitmap.FromBase64(snap['data'])
+      return bitmap.Bitmap.FromBase64Png(snap['data'])
     return None
 
   # Console public methods.
@@ -173,23 +169,43 @@ class InspectorBackend(object):
 
   # Runtime public methods.
 
-  def ExecuteJavaScript(self, expr, timeout):
-    self._runtime.Execute(expr, timeout)
+  def ExecuteJavaScript(self, expr, context_id=None, timeout=60):
+    self._runtime.Execute(expr, context_id, timeout)
 
-  def EvaluateJavaScript(self, expr, timeout):
-    return self._runtime.Evaluate(expr, timeout)
+  def EvaluateJavaScript(self, expr, context_id=None, timeout=60):
+    return self._runtime.Evaluate(expr, context_id, timeout)
 
   # Timeline public methods.
 
   @property
   def timeline_model(self):
-    return self._timeline.timeline_model
+    return self._timeline_model
 
-  def StartTimelineRecording(self):
-    self._timeline.Start()
+  def StartTimelineRecording(self, options=None):
+    if not options:
+      options = recording_options.TimelineRecordingOptions()
+    if options.record_timeline:
+      self._timeline.Start()
+    if options.record_network:
+      self._network.timeline_recorder.Start()
 
   def StopTimelineRecording(self):
-    self._timeline.Stop()
+    data = []
+    timeline_data = self._timeline.Stop()
+    if timeline_data:
+      data.append(timeline_data)
+    network_data = self._network.timeline_recorder.Stop()
+    if network_data:
+      data.append(network_data)
+    if data:
+      self._timeline_model = timeline_model.TimelineModel(
+          timeline_data=data, shift_world_to_zero=False)
+    else:
+      self._timeline_model = None
+
+  @property
+  def is_timeline_recording_running(self):
+    return self._timeline.is_timeline_recording_running
 
   # Network public methods.
 
@@ -198,22 +214,9 @@ class InspectorBackend(object):
 
   # Methods used internally by other backends.
 
-  def DispatchNotifications(self, timeout=10):
-    self._Connect()
-    self._SetTimeout(timeout)
-
-    try:
-      data = self._socket.recv()
-    except (socket.error, websocket.WebSocketException):
-      if self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
-          self._debugger_url):
-        return
-      raise exceptions.TabCrashException(sys.exc_info()[1])
-
-    res = json.loads(data)
-    logging.debug('got [%s]', data)
-    if 'method' in res:
-      self._HandleNotification(res)
+  def _IsInspectable(self):
+    contexts = self._browser_backend.ListInspectableContexts()
+    return self.id in [c['id'] for c in contexts]
 
   def _HandleNotification(self, res):
     if (res['method'] == 'Inspector.detached' and
@@ -235,59 +238,35 @@ class InspectorBackend(object):
     else:
       logging.debug('Unhandled inspector message: %s', res)
 
-  def SendAndIgnoreResponse(self, req):
-    self._Connect()
-    req['id'] = self._next_request_id
-    self._next_request_id += 1
-    data = json.dumps(req)
-    self._socket.send(data)
-    logging.debug('sent [%s]', data)
-
-  def _SetTimeout(self, timeout):
-    if self._cur_socket_timeout != timeout:
-      self._socket.settimeout(timeout)
-      self._cur_socket_timeout = timeout
+  def _HandleError(self, elapsed_time):
+    if self._IsInspectable():
+      raise util.TimeoutException(
+          'Received a socket error in the browser connection and the tab '
+          'still exists, assuming it timed out. '
+          'Elapsed=%ds Error=%s' % (elapsed_time, sys.exc_info()[1]))
+    raise exceptions.TabCrashException(
+        'Received a socket error in the browser connection and the tab no '
+        'longer exists, assuming it crashed. Error=%s' % sys.exc_info()[1])
 
   def _WaitForInspectorToGoAwayAndReconnect(self):
     sys.stderr.write('The connection to Chrome was lost to the Inspector UI.\n')
     sys.stderr.write('Telemetry is waiting for the inspector to be closed...\n')
+    super(InspectorBackend, self).Disconnect()
     self._socket.close()
     self._socket = None
     def IsBack():
-      return self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
-        self._debugger_url)
-    util.WaitFor(IsBack, 512, 0.5)
+      if not self._IsInspectable():
+        return False
+      try:
+        self.Connect(self.debugger_url)
+      except exceptions.TabCrashException, ex:
+        if ex.message.message.find('Handshake Status 500') == 0:
+          return False
+        raise
+      return True
+    util.WaitFor(IsBack, 512)
     sys.stderr.write('\n')
     sys.stderr.write('Inspector\'s UI closed. Telemetry will now resume.\n')
-    self._Connect()
-
-  def SyncRequest(self, req, timeout=10):
-    self._Connect()
-    # TODO(nduca): Listen to the timeout argument
-    # pylint: disable=W0613
-    self._SetTimeout(timeout)
-    self.SendAndIgnoreResponse(req)
-
-    while True:
-      try:
-        data = self._socket.recv()
-      except (socket.error, websocket.WebSocketException):
-        if self._browser_backend.tab_list_backend.DoesDebuggerUrlExist(
-            self._debugger_url):
-          raise util.TimeoutException(
-            'Timed out waiting for reply. This is unusual.')
-        raise exceptions.TabCrashException(sys.exc_info()[1])
-
-      res = json.loads(data)
-      logging.debug('got [%s]', data)
-      if 'method' in res:
-        self._HandleNotification(res)
-        continue
-
-      if 'id' not in res or res['id'] != req['id']:
-        logging.debug('Dropped reply: %s', json.dumps(res))
-        continue
-      return res
 
   def RegisterDomain(self,
       domain_name, notification_handler, will_close_handler):
@@ -316,15 +295,10 @@ class InspectorBackend(object):
     self._page.CollectGarbage()
 
   def TakeJSHeapSnapshot(self, timeout=120):
-    # This is a hack to make the nested function be able to modify the
-    # variables.
-    snapshot_uid = [0]
     snapshot = []
 
     def OnNotification(res):
-      if res['method'] == 'HeapProfiler.addProfileHeader':
-        snapshot_uid[0] = res['params']['header']['uid']
-      elif res['method'] == 'HeapProfiler.addHeapSnapshotChunk':
+      if res['method'] == 'HeapProfiler.addHeapSnapshotChunk':
         snapshot.append(res['params']['chunk'])
 
     def OnClose():
@@ -334,10 +308,7 @@ class InspectorBackend(object):
 
     self.SyncRequest({'method': 'Page.getResourceTree'}, timeout)
     self.SyncRequest({'method': 'Debugger.enable'}, timeout)
-    self.SyncRequest({'method': 'HeapProfiler.clearProfiles'}, timeout)
     self.SyncRequest({'method': 'HeapProfiler.takeHeapSnapshot'}, timeout)
-    self.SyncRequest({'method': 'HeapProfiler.getHeapSnapshot',
-                      'params': {'uid': snapshot_uid[0]}}, timeout)
     snapshot = ''.join(snapshot)
 
     self.UnregisterDomain('HeapProfiler')