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

Unified Diff: chrome/test/functional/perf_endure.py

Issue 9655016: Enable Deep Memory Profiler in perf_endure.py. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: reflected Dennis's comments. Created 8 years, 7 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/test/functional/perf_endure.py
diff --git a/chrome/test/functional/perf_endure.py b/chrome/test/functional/perf_endure.py
index cc0484171dde7475870cb43f062c916fb16bc6c3..d07b32d46a8949525805592631e70f591e08ac23 100755
--- a/chrome/test/functional/perf_endure.py
+++ b/chrome/test/functional/perf_endure.py
@@ -9,21 +9,35 @@ This module accepts the following environment variable inputs:
TEST_LENGTH: The number of seconds in which to run each test.
PERF_STATS_INTERVAL: The number of seconds to wait in-between each sampling
of performance/memory statistics.
+
+ DEEP_MEMORY_PROFILE: Enable the Deep Memory Profiler if it's set to 'True'.
+ DEEP_MEMORY_PROFILE_INTERVAL: The number of seconds to wait in-between each
+ sampling for the Deep Memory Profiler.
+ DEEP_MEMORY_PROFILE_SAVE: Don't clean up dump files if it's set to 'True'.
"""
+from datetime import datetime
+import json
import logging
import os
import re
+import subprocess
+import tempfile
import time
import perf
import pyauto_functional # Must be imported before pyauto.
import pyauto
+import pyauto_utils
import remote_inspector_client
import selenium.common.exceptions
from selenium.webdriver.support.ui import WebDriverWait
+class NotSupportedEnvironmentError(RuntimeError):
+ """Represent an error raised since the environment (OS) is not supported."""
+ pass
+
class ChromeEndureBaseTest(perf.BasePerfTest):
"""Implements common functionality for all Chrome Endure tests.
@@ -33,8 +47,38 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
_DEFAULT_TEST_LENGTH_SEC = 60 * 60 * 6 # Tests run for 6 hours.
_GET_PERF_STATS_INTERVAL = 60 * 10 # Measure perf stats every 10 minutes.
_ERROR_COUNT_THRESHOLD = 300 # Number of ChromeDriver errors to tolerate.
+ _DEEP_MEMORY_PROFILE = False
+ _DEEP_MEMORY_PROFILE_INTERVAL = _GET_PERF_STATS_INTERVAL
+ _DEEP_MEMORY_PROFILE_SAVE = False
+
+ _DMPROF_DIR_PATH = os.path.join(
+ os.path.dirname(__file__), os.pardir, os.pardir, os.pardir,
+ 'tools', 'deep_memory_profiler')
+
+ _DMPROF_SCRIPT_PATH = os.path.join(_DMPROF_DIR_PATH, 'dmprof')
+
+ _CHROME_BIN_PATH = os.path.join(perf.BasePerfTest.BrowserPath(), 'chrome')
def setUp(self):
+ # The environment variables for the Deep Memory Profiler must be set
+ # before perf.BasePerfTest.setUp() to inherit them to Chrome.
+ self._deep_memory_profile = self._GetDeepMemoryProfileEnv(
+ 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE)
+
+ self._deep_memory_profile_interval = self._GetDeepMemoryProfileEnv(
+ 'DEEP_MEMORY_PROFILE_INTERVAL', int, self._DEEP_MEMORY_PROFILE_INTERVAL)
+
+ if self._deep_memory_profile:
+ dir_prefix = 'endure.%s.' % datetime.today().strftime('%Y%m%d.%H%M%S')
+ self._deep_tempdir = tempfile.mkdtemp(prefix=dir_prefix)
+ os.environ['HEAPPROFILE'] = os.path.join(self._deep_tempdir, 'endure')
+ os.environ['HEAP_PROFILE_MMAP'] = 'True'
+ os.environ['DEEP_HEAP_PROFILE'] = 'True'
+ # TODO(dmikurube): Stop to set HEAP_PROFILE_TIME_INTERVAL when PyAuto
+ # supports to dump renderer heap profile.
+ os.environ['HEAP_PROFILE_TIME_INTERVAL'] = (
+ str(self._deep_memory_profile_interval))
+
perf.BasePerfTest.setUp(self)
self._test_length_sec = int(
@@ -42,9 +86,19 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
self._get_perf_stats_interval = int(
os.environ.get('PERF_STATS_INTERVAL', self._GET_PERF_STATS_INTERVAL))
+ self._deep_memory_profile_save = self._GetDeepMemoryProfileEnv(
+ 'DEEP_MEMORY_PROFILE_SAVE', bool, self._DEEP_MEMORY_PROFILE_SAVE)
+
logging.info('Running test for %d seconds.', self._test_length_sec)
logging.info('Gathering perf stats every %d seconds.',
self._get_perf_stats_interval)
+ if self._deep_memory_profile:
+ logging.info('Running with the Deep Memory Profiler every %d seconds.',
+ self._deep_memory_profile_interval)
+ if self._deep_memory_profile_save:
+ logging.info(' Dumped files won\'t be cleaned.')
+ else:
+ logging.info(' Dumped files will be cleaned up after every test.')
# Set up a remote inspector client associated with tab 0.
logging.info('Setting up connection to remote inspector...')
@@ -55,12 +109,61 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
self._test_start_time = 0
self._num_errors = 0
self._events_to_output = []
+ self._deep_memory_profile_json_file = None
+ self._deep_memory_profile_last_json_filename = ''
+ self._deep_memory_profile_proc = None
def tearDown(self):
logging.info('Terminating connection to remote inspector...')
self._remote_inspector_client.Stop()
logging.info('Connection to remote inspector terminated.')
- perf.BasePerfTest.tearDown(self) # Must be done at end of this function.
+ if self._deep_memory_profile:
+ # TODO(dmikurube): Stop to set HEAP_PROFILE_TIME_INTERVAL in setUp when
+ # PyAuto supports to dump renderer heap profile.
+ del os.environ['HEAP_PROFILE_TIME_INTERVAL']
+ del os.environ['DEEP_HEAP_PROFILE']
+ del os.environ['HEAP_PROFILE_MMAP']
+ del os.environ['HEAPPROFILE']
+
+ # Must be done at end of this function except for post-cleaning after
+ # Chrome finishes.
+ perf.BasePerfTest.tearDown(self)
+
+ # Remove the temporary directory after Chrome finishes in tearDown.
+ if (self._deep_memory_profile and
+ not self._deep_memory_profile_save and
+ self._deep_tempdir):
+ pyauto_utils.RemovePath(self._deep_tempdir)
+
+ def _GetDeepMemoryProfileEnv(self, env_name, converter, default):
+ """Returns a converted environment variable for the Deep Memory Profiler.
+
+ Args:
+ env_name: A string name of an environment variable.
+ converter: A function taking a string to convert an environment variable.
+ default: A value used if the environment variable is not specified.
+
+ Returns:
+ A value converted from the environment variable with 'converter'.
+
+ Raises:
+ NotSupportedEnvironmentError if the environment (OS) is not supported.
+ """
+ if not self.IsLinux():
+ raise NotSupportedEnvironmentError(
+ 'Deep Memory Profiler is not supported in this environment (OS).')
+ return converter(os.environ.get(env_name, default))
+
+ def _WaitForDeepMemoryProfiler(self):
+ """Waits for the Deep Memory Profiler to finish if running."""
+ if self._deep_memory_profile and self._deep_memory_profile_proc:
+ self._deep_memory_profile_proc.wait()
+ self._deep_memory_profile_proc = None
+ if self._deep_memory_profile_json_file:
+ self._deep_memory_profile_last_json_filename = (
+ self._deep_memory_profile_json_file.name)
+ self._deep_memory_profile_json_file.close()
+ self._deep_memory_profile_json_file = None
def ExtraChromeFlags(self):
"""Ensures Chrome is launched with custom flags.
@@ -68,10 +171,17 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
Returns:
A list of extra flags to pass to Chrome when it is launched.
"""
+ # The same with setUp, but need to fetch the environment variable since
+ # ExtraChromeFlags is called before setUp.
+ deep_memory_profile = self._GetDeepMemoryProfileEnv(
+ 'DEEP_MEMORY_PROFILE', bool, self._DEEP_MEMORY_PROFILE)
+
# Ensure Chrome enables remote debugging on port 9222. This is required to
# interact with Chrome's remote inspector.
- return (perf.BasePerfTest.ExtraChromeFlags(self) +
- ['--remote-debugging-port=9222'])
+ extra_flags = ['--remote-debugging-port=9222']
+ if deep_memory_profile:
+ extra_flags.append('--no-sandbox')
+ return (perf.BasePerfTest.ExtraChromeFlags(self) + extra_flags)
def _OnTimelineEvent(self, event_info):
"""Invoked by the Remote Inspector Client when a timeline event occurs.
@@ -129,6 +239,10 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
'early.' % self._ERROR_COUNT_THRESHOLD)
break
+ # TODO(dmikurube): Call HeapProfilerDump when PyAuto supports dumping for
+ # renderer processes.
+ # TODO(dmikurube): Need pid of the target process.
+
if time.time() - last_perf_stats_time >= self._get_perf_stats_interval:
last_perf_stats_time = time.time()
self._GetPerformanceStats(
@@ -143,8 +257,11 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
do_scenario()
self._remote_inspector_client.StopTimelineEventMonitoring()
+ # TODO(dmikurube): Call HeapProfilerDump when PyAuto supports dumping for
+ # renderer processes.
+
self._GetPerformanceStats(
- webapp_name, test_description, tab_title_substring)
+ webapp_name, test_description, tab_title_substring, is_last=True)
def _GetProcessInfo(self, tab_title_substring):
"""Gets process info associated with an open browser/tab.
@@ -161,6 +278,7 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
# browser process, in KB.
'tab_private_mem': integer, # Private memory associated with the tab
# process, in KB.
+ 'tab_pid': integer, # Process ID of the tab process.
}
"""
browser_process_name = (
@@ -197,10 +315,11 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
return {
'browser_private_mem': browser_proc_info['working_set_mem']['priv'],
'tab_private_mem': tab_proc_info['working_set_mem']['priv'],
+ 'tab_pid': tab_proc_info['pid'],
}
def _GetPerformanceStats(self, webapp_name, test_description,
- tab_title_substring):
+ tab_title_substring, is_last=False):
"""Gets performance statistics and outputs the results.
Args:
@@ -211,11 +330,66 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
example, 'ComposeDiscard' for Gmail.
tab_title_substring: A unique substring contained within the title of
the tab to use, for identifying the appropriate tab.
+ is_last: A boolean value which should be True if it's the last call of
+ _GetPerformanceStats. The default is False.
"""
logging.info('Gathering performance stats...')
elapsed_time = int(round(time.time() - self._test_start_time))
memory_counts = self._remote_inspector_client.GetMemoryObjectCounts()
+ proc_info = self._GetProcessInfo(tab_title_substring)
+
+ # Run Deep Memory Profiler in background.
+ if self._deep_memory_profile:
+ logging.info(' Profiling with the Deep Memory Profiler...')
+
+ # Wait for a running dmprof process for last _GetPerformanceStat call to
+ # cover last dump files.
+ if is_last:
+ logging.info(' Waiting for the last dmprof.')
+ self._WaitForDeepMemoryProfiler()
+
+ if (self._deep_memory_profile_proc and
+ self._deep_memory_profile_proc.poll() is None):
+ logging.info(' Last dmprof is still running.')
+ else:
+ if self._deep_memory_profile_json_file:
+ self._deep_memory_profile_last_json_filename = (
+ self._deep_memory_profile_json_file.name)
+ self._deep_memory_profile_json_file.close()
+ self._deep_memory_profile_json_file = None
+ first_dump = ''
+ last_dump = ''
+ for filename in sorted(os.listdir(self._deep_tempdir)):
+ if re.match('^endure.%05d.\d+.heap$' % proc_info['tab_pid'],
+ filename):
+ logging.info(' Profiled dump file: %s' % filename)
+ last_dump = filename
+ if not first_dump:
+ first_dump = filename
+ if first_dump:
+ logging.info(' First dump file: %s' % first_dump)
+ matched= re.match('^endure.\d+.(\d+).heap$', last_dump)
+ last_sequence_id = matched.group(1)
+ self._deep_memory_profile_json_file = open(
+ os.path.join(self._deep_tempdir,
+ 'endure.%05d.%s.json' % (proc_info['tab_pid'],
+ last_sequence_id)), 'w+')
+ self._deep_memory_profile_proc = subprocess.Popen(
+ '%s --json %s %s %s' % (self._DMPROF_SCRIPT_PATH,
+ self._CHROME_BIN_PATH,
+ os.path.join(self._DMPROF_DIR_PATH,
+ 'policy.l0.txt'),
+ os.path.join(self._deep_tempdir,
+ first_dump)),
+ shell=True, stdout=self._deep_memory_profile_json_file)
+ # Don't wait for the new process since dmprof may take long time.
+
+ if is_last:
+ self._WaitForDeepMemoryProfiler()
+
+ else:
+ logging.info(' No dump files.')
# DOM node count.
dom_node_count = memory_counts['DOMNodeCount']
@@ -233,7 +407,6 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
units_x='seconds')
# Browser process private memory.
- proc_info = self._GetProcessInfo(tab_title_substring)
self._OutputPerfGraphValue(
'BrowserPrivateMemory',
[(elapsed_time, proc_info['browser_private_mem'])], 'KB',
@@ -255,6 +428,26 @@ class ChromeEndureBaseTest(perf.BasePerfTest):
graph_name='%s%s-V8MemUsed' % (webapp_name, test_description),
units_x='seconds')
+ # Deep Memory Profiler result.
+ if self._deep_memory_profile:
+ deep_memory_profile_results = {}
+ if self._deep_memory_profile_last_json_filename:
+ json_data = {}
+ with open(self._deep_memory_profile_last_json_filename) as json_f:
+ json_data = json.load(json_f)
+ if json_data['version'] == 'JSON_DEEP_1':
+ deep_memory_profile_results = json_data['snapshots']
+ if deep_memory_profile_results:
+ self._OutputPerfGraphValue(
+ 'DMP-TCMallocUsed', [
+ (snapshot['second'], snapshot['tc-used-all'] / 1024.0)
+ for snapshot in deep_memory_profile_results],
+ 'KB',
+ graph_name='%s%s-DMP-TCUsed' % (webapp_name, test_description),
+ units_x='seconds')
+ # TODO(dmikurube): Output graph values (for multi-lined graphs), here.
+ # 'deep_memory_profile_results' is the value to be output.
+
logging.info(' Total DOM node count: %d nodes' % dom_node_count)
logging.info(' Event listener count: %d listeners' % event_listener_count)
logging.info(' Browser process private memory: %d KB' %
« 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