Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(180)

Side by Side Diff: chrome/test/pyautolib/remote_inspector_client.py

Issue 11826027: Allow platform apps to be leak checked by providing tab_filter to choose the tab to inspect. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Chrome remote inspector utility for pyauto tests. 6 """Chrome remote inspector utility for pyauto tests.
7 7
8 This script provides a python interface that acts as a front-end for Chrome's 8 This script provides a python interface that acts as a front-end for Chrome's
9 remote inspector module, communicating via sockets to interact with Chrome in 9 remote inspector module, communicating via sockets to interact with Chrome in
10 the same way that the Developer Tools does. This -- in theory -- should allow 10 the same way that the Developer Tools does. This -- in theory -- should allow
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 id: A unique integer id associated with this request. 65 id: A unique integer id associated with this request.
66 params: A dictionary of input parameters associated with this request. 66 params: A dictionary of input parameters associated with this request.
67 results: A dictionary of relevant results obtained from the remote Chrome 67 results: A dictionary of relevant results obtained from the remote Chrome
68 instance that are associated with this request. 68 instance that are associated with this request.
69 is_fulfilled: A boolean indicating whether or not this request has been sent 69 is_fulfilled: A boolean indicating whether or not this request has been sent
70 and all relevant results for it have been obtained (i.e., this value is 70 and all relevant results for it have been obtained (i.e., this value is
71 True only if all results for this request are known). 71 True only if all results for this request are known).
72 is_fulfilled_condition: A threading.Condition for waiting for the request to 72 is_fulfilled_condition: A threading.Condition for waiting for the request to
73 be fulfilled. 73 be fulfilled.
74 """ 74 """
75
75 def __init__(self, method, params, message_id): 76 def __init__(self, method, params, message_id):
76 """Initialize. 77 """Initialize.
77 78
78 Args: 79 Args:
79 method: The string method name for this request. 80 method: The string method name for this request.
80 message_id: An integer id for this request, which is assumed to be unique 81 message_id: An integer id for this request, which is assumed to be unique
81 from among all requests. 82 from among all requests.
82 """ 83 """
83 self.method = method 84 self.method = method
84 self.id = message_id 85 self.id = message_id
(...skipping 19 matching lines...) Expand all
104 communication protocol in WebKit. This class performs the lower-level work 105 communication protocol in WebKit. This class performs the lower-level work
105 of socket communication. 106 of socket communication.
106 107
107 Public Attributes: 108 Public Attributes:
108 handshake_done: A boolean indicating whether or not the client has completed 109 handshake_done: A boolean indicating whether or not the client has completed
109 the required protocol handshake with the remote Chrome instance. 110 the required protocol handshake with the remote Chrome instance.
110 inspector_thread: An instance of the _RemoteInspectorThread class that is 111 inspector_thread: An instance of the _RemoteInspectorThread class that is
111 working together with this class to communicate with a remote Chrome 112 working together with this class to communicate with a remote Chrome
112 instance. 113 instance.
113 """ 114 """
115
114 def __init__(self, verbose, show_socket_messages, hostname, port, path): 116 def __init__(self, verbose, show_socket_messages, hostname, port, path):
115 """Initialize. 117 """Initialize.
116 118
117 Args: 119 Args:
118 verbose: A boolean indicating whether or not to use verbose logging. 120 verbose: A boolean indicating whether or not to use verbose logging.
119 show_socket_messages: A boolean indicating whether or not to show the 121 show_socket_messages: A boolean indicating whether or not to show the
120 socket messages sent/received when communicating with the remote 122 socket messages sent/received when communicating with the remote
121 Chrome instance. 123 Chrome instance.
122 hostname: The string hostname of the DevToolsSocket to which to connect. 124 hostname: The string hostname of the DevToolsSocket to which to connect.
123 port: The integer port number of the DevToolsSocket to which to connect. 125 port: The integer port number of the DevToolsSocket to which to connect.
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
270 272
271 class _RemoteInspectorThread(threading.Thread): 273 class _RemoteInspectorThread(threading.Thread):
272 """Manages communication using Chrome's remote inspector protocol. 274 """Manages communication using Chrome's remote inspector protocol.
273 275
274 This class works in conjunction with the _DevToolsSocketClient class to 276 This class works in conjunction with the _DevToolsSocketClient class to
275 communicate with a remote Chrome instance following the remote inspector 277 communicate with a remote Chrome instance following the remote inspector
276 communication protocol in WebKit. This class performs the higher-level work 278 communication protocol in WebKit. This class performs the higher-level work
277 of managing request and reply messages, whereas _DevToolsSocketClient handles 279 of managing request and reply messages, whereas _DevToolsSocketClient handles
278 the lower-level work of socket communication. 280 the lower-level work of socket communication.
279 """ 281 """
280 def __init__(self, tab_index, verbose, show_socket_messages): 282
283 def __init__(self, tab_index, tab_filter, verbose, show_socket_messages):
281 """Initialize. 284 """Initialize.
282 285
283 Args: 286 Args:
284 tab_index: The integer index of the tab in the remote Chrome instance to 287 tab_index: The integer index of the tab in the remote Chrome instance to
285 use for snapshotting. 288 use for snapshotting.
289 tab_filter: When specified, is run over tabs of the remote Chrome
290 instances to choose which one to connect to.
286 verbose: A boolean indicating whether or not to use verbose logging. 291 verbose: A boolean indicating whether or not to use verbose logging.
287 show_socket_messages: A boolean indicating whether or not to show the 292 show_socket_messages: A boolean indicating whether or not to show the
288 socket messages sent/received when communicating with the remote 293 socket messages sent/received when communicating with the remote
289 Chrome instance. 294 Chrome instance.
290 """ 295 """
291 threading.Thread.__init__(self) 296 threading.Thread.__init__(self)
292 self._logger = logging.getLogger('_RemoteInspectorThread') 297 self._logger = logging.getLogger('_RemoteInspectorThread')
293 self._logger.setLevel([logging.WARNING, logging.DEBUG][verbose]) 298 self._logger.setLevel([logging.WARNING, logging.DEBUG][verbose])
294 299
295 self._killed = False 300 self._killed = False
296 self._requests = [] 301 self._requests = []
297 self._action_queue = [] 302 self._action_queue = []
298 self._action_queue_condition = threading.Condition() 303 self._action_queue_condition = threading.Condition()
299 self._action_specific_callback = None # Callback only for current action. 304 self._action_specific_callback = None # Callback only for current action.
300 self._action_specific_callback_lock = threading.Lock() 305 self._action_specific_callback_lock = threading.Lock()
301 self._general_callbacks = [] # General callbacks that can be long-lived. 306 self._general_callbacks = [] # General callbacks that can be long-lived.
302 self._general_callbacks_lock = threading.Lock() 307 self._general_callbacks_lock = threading.Lock()
303 self._condition_to_wait = None 308 self._condition_to_wait = None
304 309
305 # Create a DevToolsSocket client and wait for it to complete the remote 310 # Create a DevToolsSocket client and wait for it to complete the remote
306 # debugging protocol handshake with the remote Chrome instance. 311 # debugging protocol handshake with the remote Chrome instance.
307 result = self._IdentifyDevToolsSocketConnectionInfo(tab_index) 312 result = self._IdentifyDevToolsSocketConnectionInfo(tab_index, tab_filter)
308 self._client = _DevToolsSocketClient( 313 self._client = _DevToolsSocketClient(
309 verbose, show_socket_messages, result['host'], result['port'], 314 verbose, show_socket_messages, result['host'], result['port'],
310 result['path']) 315 result['path'])
311 self._client.inspector_thread = self 316 self._client.inspector_thread = self
312 while asyncore.socket_map: 317 while asyncore.socket_map:
313 if self._client.handshake_done or self._killed: 318 if self._client.handshake_done or self._killed:
314 break 319 break
315 asyncore.loop(timeout=1, count=1, use_poll=True) 320 asyncore.loop(timeout=1, count=1, use_poll=True)
316 321
317 def ClientSocketExceptionOccurred(self): 322 def ClientSocketExceptionOccurred(self):
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after
510 # To actually request the snapshot data from a previously-taken snapshot, 515 # To actually request the snapshot data from a previously-taken snapshot,
511 # we need to specify the unique uid of the snapshot we want. 516 # we need to specify the unique uid of the snapshot we want.
512 # The relevant uid should be contained in the last 517 # The relevant uid should be contained in the last
513 # 'Profiler.takeHeapSnapshot' request object. 518 # 'Profiler.takeHeapSnapshot' request object.
514 last_req = self._GetLatestRequestOfType(request, 519 last_req = self._GetLatestRequestOfType(request,
515 'Profiler.takeHeapSnapshot') 520 'Profiler.takeHeapSnapshot')
516 if last_req and 'uid' in last_req.results: 521 if last_req and 'uid' in last_req.results:
517 request.params = {'type': 'HEAP', 'uid': last_req.results['uid']} 522 request.params = {'type': 'HEAP', 'uid': last_req.results['uid']}
518 523
519 @staticmethod 524 @staticmethod
520 def _IdentifyDevToolsSocketConnectionInfo(tab_index): 525 def _IdentifyDevToolsSocketConnectionInfo(tab_index, tab_filter):
521 """Identifies DevToolsSocket connection info from a remote Chrome instance. 526 """Identifies DevToolsSocket connection info from a remote Chrome instance.
522 527
523 Args: 528 Args:
524 tab_index: The integer index of the tab in the remote Chrome instance to 529 tab_index: The integer index of the tab in the remote Chrome instance to
525 which to connect. 530 which to connect.
531 tab_filter: When specified, is run over tabs of the remote Chrome instance
532 to choose which one to connect to.
526 533
527 Returns: 534 Returns:
528 A dictionary containing the DevToolsSocket connection info: 535 A dictionary containing the DevToolsSocket connection info:
529 { 536 {
530 'host': string, 537 'host': string,
531 'port': integer, 538 'port': integer,
532 'path': string, 539 'path': string,
533 } 540 }
534 541
535 Raises: 542 Raises:
536 RuntimeError: When DevToolsSocket connection info cannot be identified. 543 RuntimeError: When DevToolsSocket connection info cannot be identified.
537 """ 544 """
538 try: 545 try:
539 # TODO(dennisjeffrey): Do not assume port 9222. The port should be passed 546 # TODO(dennisjeffrey): Do not assume port 9222. The port should be passed
540 # as input to this function. 547 # as input to this function.
541 f = urllib2.urlopen('http://localhost:9222/json') 548 f = urllib2.urlopen('http://localhost:9222/json')
542 result = f.read(); 549 result = f.read()
550 logging.debug(result)
543 result = simplejson.loads(result) 551 result = simplejson.loads(result)
544 except urllib2.URLError, e: 552 except urllib2.URLError, e:
545 raise RuntimeError( 553 raise RuntimeError(
546 'Error accessing Chrome instance debugging port: ' + str(e)) 554 'Error accessing Chrome instance debugging port: ' + str(e))
547 555
548 if tab_index >= len(result): 556 if tab_filter:
549 raise RuntimeError( 557 connect_to = filter(tab_filter, result)[0]
550 'Specified tab index %d doesn\'t exist (%d tabs found)' % 558 else:
551 (tab_index, len(result))) 559 if tab_index >= len(result):
560 raise RuntimeError(
561 'Specified tab index %d doesn\'t exist (%d tabs found)' %
562 (tab_index, len(result)))
563 connect_to = result[tab_index]
552 564
553 if 'webSocketDebuggerUrl' not in result[tab_index]: 565 logging.debug(simplejson.dumps(connect_to))
566
567 if 'webSocketDebuggerUrl' not in connect_to:
554 raise RuntimeError('No socket URL exists for the specified tab.') 568 raise RuntimeError('No socket URL exists for the specified tab.')
555 569
556 socket_url = result[tab_index]['webSocketDebuggerUrl'] 570 socket_url = connect_to['webSocketDebuggerUrl']
557 parsed = urlparse.urlparse(socket_url) 571 parsed = urlparse.urlparse(socket_url)
558 # On ChromeOS, the "ws://" scheme may not be recognized, leading to an 572 # On ChromeOS, the "ws://" scheme may not be recognized, leading to an
559 # incorrect netloc (and empty hostname and port attributes) in |parsed|. 573 # incorrect netloc (and empty hostname and port attributes) in |parsed|.
560 # Change the scheme to "http://" to fix this. 574 # Change the scheme to "http://" to fix this.
561 if not parsed.hostname or not parsed.port: 575 if not parsed.hostname or not parsed.port:
562 socket_url = 'http' + socket_url[socket_url.find(':'):] 576 socket_url = 'http' + socket_url[socket_url.find(':'):]
563 parsed = urlparse.urlparse(socket_url) 577 parsed = urlparse.urlparse(socket_url)
564 # Warning: |parsed.scheme| is incorrect after this point. 578 # Warning: |parsed.scheme| is incorrect after this point.
565 return ({'host': parsed.hostname, 579 return ({'host': parsed.hostname,
566 'port': parsed.port, 580 'port': parsed.port,
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after
790 804
791 Public Methods: 805 Public Methods:
792 Stop: Close the connection to the remote inspector. Should be called when 806 Stop: Close the connection to the remote inspector. Should be called when
793 a user is done using this module. 807 a user is done using this module.
794 HeapSnapshot: Takes a v8 heap snapshot and returns the summarized data. 808 HeapSnapshot: Takes a v8 heap snapshot and returns the summarized data.
795 GetMemoryObjectCounts: Retrieves memory object count information. 809 GetMemoryObjectCounts: Retrieves memory object count information.
796 CollectGarbage: Forces a garbage collection. 810 CollectGarbage: Forces a garbage collection.
797 StartTimelineEventMonitoring: Starts monitoring for timeline events. 811 StartTimelineEventMonitoring: Starts monitoring for timeline events.
798 StopTimelineEventMonitoring: Stops monitoring for timeline events. 812 StopTimelineEventMonitoring: Stops monitoring for timeline events.
799 """ 813 """
814
800 # TODO(dennisjeffrey): Allow a user to specify a window index too (not just a 815 # TODO(dennisjeffrey): Allow a user to specify a window index too (not just a
801 # tab index), when running through PyAuto. 816 # tab index), when running through PyAuto.
802 def __init__(self, tab_index=0, verbose=False, show_socket_messages=False): 817 def __init__(self, tab_index=0, tab_filter=None,
818 verbose=False, show_socket_messages=False):
803 """Initialize. 819 """Initialize.
804 820
805 Args: 821 Args:
806 tab_index: The integer index of the tab in the remote Chrome instance to 822 tab_index: The integer index of the tab in the remote Chrome instance to
807 which to connect. Defaults to 0 (the first tab). 823 which to connect. Defaults to 0 (the first tab).
824 tab_filter: When specified, is run over tabs of the remote Chrome
825 instance to choose which one to connect to.
808 verbose: A boolean indicating whether or not to use verbose logging. 826 verbose: A boolean indicating whether or not to use verbose logging.
809 show_socket_messages: A boolean indicating whether or not to show the 827 show_socket_messages: A boolean indicating whether or not to show the
810 socket messages sent/received when communicating 828 socket messages sent/received when communicating with the remote
811 with the remote Chrome instance. 829 Chrome instance.
812 """ 830 """
813 self._tab_index = tab_index 831 self._tab_index = tab_index
832 self._tab_filter = tab_filter
814 self._verbose = verbose 833 self._verbose = verbose
815 self._show_socket_messages = show_socket_messages 834 self._show_socket_messages = show_socket_messages
816 835
817 self._timeline_started = False 836 self._timeline_started = False
818 837
819 logging.basicConfig() 838 logging.basicConfig()
820 self._logger = logging.getLogger('RemoteInspectorClient') 839 self._logger = logging.getLogger('RemoteInspectorClient')
821 self._logger.setLevel([logging.WARNING, logging.DEBUG][verbose]) 840 self._logger.setLevel([logging.WARNING, logging.DEBUG][verbose])
822 841
823 # Creating _RemoteInspectorThread might raise an exception. This prevents an 842 # Creating _RemoteInspectorThread might raise an exception. This prevents an
824 # AttributeError in the destructor. 843 # AttributeError in the destructor.
825 self._remote_inspector_thread = None 844 self._remote_inspector_thread = None
826 self._remote_inspector_driver_thread = None 845 self._remote_inspector_driver_thread = None
827 846
828 # Start up a thread for long-term communication with the remote inspector. 847 # Start up a thread for long-term communication with the remote inspector.
829 self._remote_inspector_thread = _RemoteInspectorThread( 848 self._remote_inspector_thread = _RemoteInspectorThread(
830 tab_index, verbose, show_socket_messages) 849 tab_index, tab_filter, verbose, show_socket_messages)
831 self._remote_inspector_thread.start() 850 self._remote_inspector_thread.start()
832 # At this point, a connection has already been made to the remote inspector. 851 # At this point, a connection has already been made to the remote inspector.
833 852
834 # This thread calls asyncore.loop, which activates the channel service. 853 # This thread calls asyncore.loop, which activates the channel service.
835 self._remote_inspector_driver_thread = _RemoteInspectorDriverThread() 854 self._remote_inspector_driver_thread = _RemoteInspectorDriverThread()
836 self._remote_inspector_driver_thread.start() 855 self._remote_inspector_driver_thread.start()
837 856
838 def __del__(self): 857 def __del__(self):
839 """Called on destruction of this object.""" 858 """Called on destruction of this object."""
840 self.Stop() 859 self.Stop()
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
1053 ('Memory.getProcessMemoryDistribution', {}) 1072 ('Memory.getProcessMemoryDistribution', {})
1054 ] 1073 ]
1055 1074
1056 reply_holder = [None] 1075 reply_holder = [None]
1057 done_condition = threading.Condition() 1076 done_condition = threading.Condition()
1058 def HandleReply(reply_dict): 1077 def HandleReply(reply_dict):
1059 """Processes a reply message received from the remote Chrome instance. 1078 """Processes a reply message received from the remote Chrome instance.
1060 1079
1061 Args: 1080 Args:
1062 reply_dict: A dictionary object representing the reply message received 1081 reply_dict: A dictionary object representing the reply message received
1063 from the remote Chrome instance. 1082 from the remote Chrome instance.
1064 """ 1083 """
1065 request_id = reply_dict['id'] 1084 request_id = reply_dict['id']
1066 # GC command will have id = 0, the second command id = 1 1085 # GC command will have id = 0, the second command id = 1
1067 if request_id == 0: 1086 if request_id == 0:
1068 logging.info('Did garbage collection') 1087 logging.info('Did garbage collection')
1069 return 1088 return
1070 if request_id != 1: 1089 if request_id != 1:
1071 raise RuntimeError('Unexpected request_id: %d' % request_id) 1090 raise RuntimeError('Unexpected request_id: %d' % request_id)
1072 reply_holder[0] = reply_dict 1091 reply_holder[0] = reply_dict
1073 done_condition.acquire() 1092 done_condition.acquire()
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
1182 1201
1183 Returns: 1202 Returns:
1184 A human-readable string representation of the given number of bytes. 1203 A human-readable string representation of the given number of bytes.
1185 """ 1204 """
1186 if num_bytes < 1024: 1205 if num_bytes < 1024:
1187 return '%d B' % num_bytes 1206 return '%d B' % num_bytes
1188 elif num_bytes < 1048576: 1207 elif num_bytes < 1048576:
1189 return '%.2f KB' % (num_bytes / 1024.0) 1208 return '%.2f KB' % (num_bytes / 1024.0)
1190 else: 1209 else:
1191 return '%.2f MB' % (num_bytes / 1048576.0) 1210 return '%.2f MB' % (num_bytes / 1048576.0)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698