diff options
Diffstat (limited to 'chrome/test')
-rw-r--r-- | chrome/test/functional/perf_endure.py | 108 | ||||
-rwxr-xr-x | chrome/test/pyautolib/perf_snapshot.py | 92 |
2 files changed, 168 insertions, 32 deletions
diff --git a/chrome/test/functional/perf_endure.py b/chrome/test/functional/perf_endure.py index 4ce65ee..eaa2fe6 100644 --- a/chrome/test/functional/perf_endure.py +++ b/chrome/test/functional/perf_endure.py @@ -30,7 +30,9 @@ class ChromeEndureBaseTest(perf.BasePerfTest): self._full_snapshot_results = [] self._snapshot_iteration = 0 self._heap_size_results = [] - self._node_count_results = [] + self._v8_node_count_results = [] + self._dom_node_count_results = [] + self._event_listener_count_results = [] def ExtraChromeFlags(self): """Ensures Chrome is launched with custom flags. @@ -43,18 +45,38 @@ class ChromeEndureBaseTest(perf.BasePerfTest): return (perf.BasePerfTest.ExtraChromeFlags(self) + ['--remote-debugging-port=9222']) - def _TakeHeapSnapshot(self): + def _TakeHeapSnapshot(self, webapp_name): """Takes a v8 heap snapshot and outputs/stores the results. - This function will fail the current test if no snapshot can be taken. + This function will fail the current test if no snapshot can be taken. A + snapshot stored by this function is represented by a dictionary with the + following format: + { + 'url': string, # URL of the webpage that was snapshotted. + 'timestamp': float, # Time when snapshot taken (seconds since epoch). + 'total_v8_node_count': integer, # Total number of nodes in the v8 heap. + 'total_heap_size': integer, # Total heap size (number of bytes). + 'total_dom_node_count': integer, # Total number of DOM nodes. + 'event_listener_count': integer, # Total number of event listeners. + } + + Args: + webapp_name: A string name for the webapp being testing. Should not + include spaces. For example, 'Gmail', 'Docs', or 'Plus'. """ # Take the snapshot and store the associated information. - logging.info('Taking heap snapshot...') + logging.info('Taking v8 heap snapshot...') start_time = time.time() snapshot = self._snapshotter.HeapSnapshot() elapsed_time = time.time() - start_time self.assertTrue(snapshot, msg='Failed to take a v8 heap snapshot.') snapshot_info = snapshot[0] + + # Collect other count information to add to the snapshot data. + memory_counts = self._snapshotter.GetMemoryObjectCounts() + snapshot_info['total_dom_node_count'] = memory_counts['DOMNodeCount'] + snapshot_info['event_listener_count'] = memory_counts['EventListenerCount'] + self._full_snapshot_results.append(snapshot_info) logging.info('Snapshot taken (%.2f sec).' % elapsed_time) @@ -66,38 +88,76 @@ class ChromeEndureBaseTest(perf.BasePerfTest): logging.info(' Total heap size: %.2f MB' % heap_size) self._heap_size_results.append((self._snapshot_iteration, heap_size)) - node_count = snapshot_info['total_node_count'] - logging.info(' Total node count: %d nodes' % node_count) - self._node_count_results.append((self._snapshot_iteration, node_count)) + v8_node_count = snapshot_info['total_v8_node_count'] + logging.info(' Total v8 node count: %d nodes' % v8_node_count) + self._v8_node_count_results.append((self._snapshot_iteration, + v8_node_count)) + + dom_node_count = snapshot_info['total_dom_node_count'] + logging.info(' Total DOM node count: %d nodes' % dom_node_count) + self._dom_node_count_results.append((self._snapshot_iteration, + dom_node_count)) + + event_listener_count = snapshot_info['event_listener_count'] + logging.info(' Event listener count: %d listeners' % event_listener_count) + self._event_listener_count_results.append((self._snapshot_iteration, + event_listener_count)) + self._snapshot_iteration += 1 # Output the results seen so far, to be graphed. self._OutputPerfGraphValue( - 'HeapSize', self._heap_size_results, 'MB', graph_name='Gmail-Heap', - units_x='iteration') + 'HeapSize', self._heap_size_results, 'MB', + graph_name='%s-Heap' % webapp_name, units_x='iteration') self._OutputPerfGraphValue( - 'TotalNodeCount', self._node_count_results, 'nodes', - graph_name='Gmail-Nodes', units_x='iteration') + 'TotalV8NodeCount', self._v8_node_count_results, 'nodes', + graph_name='%s-Nodes-V8' % webapp_name, units_x='iteration') + self._OutputPerfGraphValue( + 'TotalDOMNodeCount', self._dom_node_count_results, 'nodes', + graph_name='%s-Nodes-DOM' % webapp_name, units_x='iteration') + self._OutputPerfGraphValue( + 'EventListenerCount', self._event_listener_count_results, 'listeners', + graph_name='%s-EventListeners' % webapp_name, units_x='iteration') - def _OutputFinalHeapSnapshotResults(self): + def _OutputFinalHeapSnapshotResults(self, webapp_name): """Outputs final snapshot results to be graphed at the end of a test. Assumes that at least one snapshot was previously taken by the current test. + + Args: + webapp_name: A string name for the webapp being testing. Should not + include spaces. For example, 'Gmail', 'Docs', or 'Plus'. """ assert len(self._full_snapshot_results) >= 1 max_heap_size = 0 - max_node_count = 0 + max_v8_node_count = 0 + max_dom_node_count = 0 + max_event_listener_count = 0 for index, snapshot_info in enumerate(self._full_snapshot_results): heap_size = snapshot_info['total_heap_size'] / (1024.0 * 1024.0) if heap_size > max_heap_size: max_heap_size = heap_size - node_count = snapshot_info['total_node_count'] - if node_count > max_node_count: - max_node_count = node_count + v8_node_count = snapshot_info['total_v8_node_count'] + if v8_node_count > max_v8_node_count: + max_v8_node_count = v8_node_count + dom_node_count = snapshot_info['total_dom_node_count'] + if dom_node_count > max_dom_node_count: + max_dom_node_count = dom_node_count + event_listener_count = snapshot_info['event_listener_count'] + if event_listener_count > max_event_listener_count: + max_event_listener_count = event_listener_count + self._OutputPerfGraphValue( + 'MaxHeapSize', max_heap_size, 'MB', + graph_name='%s-Heap-Max' % webapp_name) + self._OutputPerfGraphValue( + 'MaxV8NodeCount', max_v8_node_count, 'nodes', + graph_name='%s-Nodes-V8-Max' % webapp_name) self._OutputPerfGraphValue( - 'HeapSize', max_heap_size, 'MB', graph_name='Gmail-Heap-Max') + 'MaxDOMNodeCount', max_dom_node_count, 'nodes', + graph_name='%s-Nodes-DOM-Max' % webapp_name) self._OutputPerfGraphValue( - 'TotalNodeCount', max_node_count, 'nodes', graph_name='Gmail-Nodes-Max') + 'MaxEventListenerCount', max_event_listener_count, 'listeners', + graph_name='%s-EventListeners-Max' % webapp_name) def _GetElement(self, find_by, value): """Gets a WebDriver element object from the webpage DOM. @@ -201,9 +261,9 @@ class ChromeEndureGmailTest(ChromeEndureBaseTest): # Snapshot after the first iteration, then every 50 iterations after that. if i % 50 == 0: - self._TakeHeapSnapshot() + self._TakeHeapSnapshot('Gmail') - self._OutputFinalHeapSnapshotResults() + self._OutputFinalHeapSnapshotResults('Gmail') class ChromeEndureDocsTest(ChromeEndureBaseTest): @@ -268,9 +328,9 @@ class ChromeEndureDocsTest(ChromeEndureBaseTest): # Snapshot after the first iteration, then every 100 iterations after # that. if i % 100 == 0: - self._TakeHeapSnapshot() + self._TakeHeapSnapshot('Docs') - self._OutputFinalHeapSnapshotResults() + self._OutputFinalHeapSnapshotResults('Docs') class ChromeEndurePlusTest(ChromeEndureBaseTest): @@ -331,9 +391,9 @@ class ChromeEndurePlusTest(ChromeEndureBaseTest): # Snapshot after the first iteration, then every 100 iterations after # that. if i % 100 == 0: - self._TakeHeapSnapshot() + self._TakeHeapSnapshot('Plus') - self._OutputFinalHeapSnapshotResults() + self._OutputFinalHeapSnapshotResults('Plus') if __name__ == '__main__': diff --git a/chrome/test/pyautolib/perf_snapshot.py b/chrome/test/pyautolib/perf_snapshot.py index e27d638..577a709 100755 --- a/chrome/test/pyautolib/perf_snapshot.py +++ b/chrome/test/pyautolib/perf_snapshot.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# 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. @@ -82,7 +82,7 @@ class _V8HeapSnapshotParser(object): Returns: A dictionary containing the summarized v8 heap snapshot data: { - 'total_node_count': integer, # Total number of nodes in the v8 heap. + 'total_v8_node_count': integer, # Total number of nodes in the v8 heap. 'total_shallow_size': integer, # Total heap size, in bytes. } """ @@ -162,7 +162,7 @@ class _V8HeapSnapshotParser(object): # TODO(dennisjeffrey): Have this function also return more detailed v8 # heap snapshot data when a need for it arises (e.g., using |constructors|). result = {} - result['total_node_count'] = total_node_count + result['total_v8_node_count'] = total_node_count result['total_shallow_size'] = total_shallow_size return result @@ -411,7 +411,7 @@ class _RemoteInspectorBaseThread(threading.Thread): result handling needs to be performed. run: Starts the thread of execution for this object. Invoked implicitly by calling the start() method on this object. Should be overridden - by a subclass. + by a subclass, and should call self._client.close() when done. """ def __init__(self, tab_index, verbose, show_socket_messages): """Initialize. @@ -692,7 +692,7 @@ class _PerformanceSnapshotterThread(_RemoteInspectorBaseThread): result = parser.ParseSnapshotData(raw_snapshot_data) self._logger.debug('Time to parse data: %.2f sec', time.time() - time_start) - num_nodes = result['total_node_count'] + num_nodes = result['total_v8_node_count'] total_size = result['total_shallow_size'] total_size_str = self._ConvertBytesToHumanReadableString(total_size) timestamp = time.time() @@ -700,7 +700,7 @@ class _PerformanceSnapshotterThread(_RemoteInspectorBaseThread): self.collected_heap_snapshot_data.append( {'url': self._url, 'timestamp': timestamp, - 'total_node_count': num_nodes, + 'total_v8_node_count': num_nodes, 'total_heap_size': total_size,}) if self._output_file: @@ -836,7 +836,56 @@ class _GarbageCollectThread(_RemoteInspectorBaseThread): return time.sleep(0.1) self._client.close() - return + + +class _MemoryCountThread(_RemoteInspectorBaseThread): + """Manages communication with a remote Chrome to get memory count info.""" + + _MEMORY_COUNT_MESSAGES = [ + 'Memory.getDOMNodeCount', + ] + + def HandleReply(self, reply_dict): + """Processes a reply message received from the remote Chrome instance. + + Args: + reply_dict: A dictionary object representing the reply message received + from the remote Chrome instance. + """ + if 'result' in reply_dict and 'count' in reply_dict['result']: + dom_group_list = reply_dict['result']['count'] + for dom_group in dom_group_list: + listener_array = dom_group['listenerCount'] + for listener in listener_array: + self.event_listener_count += listener['count'] + dom_node_array = dom_group['nodeCount'] + for dom_element in dom_node_array: + self.dom_node_count += dom_element['count'] + + def run(self): + """Start _MemoryCountThread; overridden from threading.Thread.""" + if self._killed: + return + + self.dom_node_count = 0 + self.event_listener_count = 0 + + # Prepare the request list. + for message in self._MEMORY_COUNT_MESSAGES: + self._requests.append( + _DevToolsSocketRequest(message, self._next_request_id)) + self._next_request_id += 1 + + # Send out each request. Wait until each request is complete before sending + # the next request. + for request in self._requests: + self._FillInParams(request) + self._client.SendMessage(str(request)) + while not request.is_complete: + if self._killed: + return + time.sleep(0.1) + self._client.close() # TODO(dennisjeffrey): The "verbose" option used in this file should re-use @@ -898,7 +947,7 @@ class PerformanceSnapshotter(object): { 'url': string, # URL of the webpage that was snapshotted. 'timestamp': float, # Time when snapshot taken (seconds since epoch). - 'total_node_count': integer, # Total number of nodes in the v8 heap. + 'total_v8_node_count': integer, # Total number of nodes in the v8 heap. 'total_heap_size': integer, # Total heap size (number of bytes). } """ @@ -932,6 +981,33 @@ class PerformanceSnapshotter(object): pass gc_thread.join() + def GetMemoryObjectCounts(self): + """Retrieves memory object count information. + + Returns: + A dictionary containing the memory object count information: + { + 'DOMNodeCount': integer, # Total number of DOM nodes. + 'EventListenerCount': integer, # Total number of event listeners. + } + """ + mem_count_thread = _MemoryCountThread(self._tab_index, self._verbose, + self._show_socket_messages) + mem_count_thread.start() + try: + while asyncore.socket_map: + if not mem_count_thread.is_alive(): + break + asyncore.loop(timeout=1, count=1) + except KeyboardInterrupt: + pass + mem_count_thread.join() + result = { + 'DOMNodeCount': mem_count_thread.dom_node_count, + 'EventListenerCount': mem_count_thread.event_listener_count, + } + return result + def SetInteractiveMode(self): """Sets the current object to take snapshots in interactive mode.""" self._interactive_mode = True |