Index: chrome/test/functional/perf_endure.py |
diff --git a/chrome/test/functional/perf_endure.py b/chrome/test/functional/perf_endure.py |
index 2cd9280ba302db92677c64f04de2dce40592f805..592f11e399fa058df88bb9420c796071d9ab6f1d 100755 |
--- a/chrome/test/functional/perf_endure.py |
+++ b/chrome/test/functional/perf_endure.py |
@@ -45,6 +45,180 @@ class NotSupportedEnvironmentError(RuntimeError): |
pass |
+class DeepMemoryProfiler(object): |
+ """Controls Deep Memory Profiler (dmprof) for endurance tests.""" |
+ DEEP_MEMORY_PROFILE = False |
+ 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') |
+ |
+ def __init__(self): |
+ self._enabled = self.GetEnvironmentVariable( |
+ 'DEEP_MEMORY_PROFILE', bool, self.DEEP_MEMORY_PROFILE) |
+ self._save = self.GetEnvironmentVariable( |
+ 'DEEP_MEMORY_PROFILE_SAVE', bool, self.DEEP_MEMORY_PROFILE_SAVE) |
+ self._json_file = None |
+ self._last_json_filename = '' |
+ self._proc = None |
+ self._last_time = -1.0 |
+ |
+ def __nonzero__(self): |
+ return self._enabled |
+ |
+ @staticmethod |
+ def GetEnvironmentVariable(env_name, converter, default): |
+ """Returns a converted environment variable for 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'. |
+ """ |
+ return converter(os.environ.get(env_name, default)) |
+ |
+ def SetUp(self, is_linux): |
+ """Sets up Deep Memory Profiler settings for a Chrome process. |
+ |
+ It sets environment variables and makes a working directory. |
+ """ |
+ if not self._enabled: |
+ return |
+ |
+ if not is_linux: |
+ raise NotSupportedEnvironmentError( |
+ 'Deep Memory Profiler is not supported in this environment (OS).') |
+ dir_prefix = 'endure.%s.' % datetime.today().strftime('%Y%m%d.%H%M%S') |
+ self._tempdir = tempfile.mkdtemp(prefix=dir_prefix) |
+ os.environ['HEAPPROFILE'] = os.path.join(self._tempdir, 'endure') |
+ os.environ['HEAP_PROFILE_MMAP'] = '1' |
+ os.environ['DEEP_HEAP_PROFILE'] = '1' |
+ |
+ def TearDown(self): |
+ """Tear down Deep Memory Profiler settings for the Chrome process. |
+ |
+ It removes the environment variables and the temporary directory. |
+ Call it after Chrome finishes. Chrome may dump last files at the end. |
+ """ |
+ if not self._enabled: |
+ return |
+ |
+ del os.environ['DEEP_HEAP_PROFILE'] |
+ del os.environ['HEAP_PROFILE_MMAP'] |
+ del os.environ['HEAPPROFILE'] |
+ if not self._save and self._tempdir: |
+ pyauto_utils.RemovePath(self._tempdir) |
+ |
+ def LogFirstMessage(self): |
+ """Logs first messages.""" |
+ if not self._enabled: |
+ return |
+ |
+ logging.info('Running with the Deep Memory Profiler.') |
+ if self._save: |
+ logging.info(' Dumped files won\'t be cleaned.') |
+ else: |
+ logging.info(' Dumped files will be cleaned up after every test.') |
+ |
+ def StartProfiler(self, proc_info, is_last): |
+ """Starts Deep Memory Profiler in background.""" |
+ if not self._enabled: |
+ return |
+ |
+ 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._proc and self._proc.poll() is None: |
+ logging.info(' Last dmprof is still running.') |
+ else: |
+ if self._json_file: |
+ self._last_json_filename = self._json_file.name |
+ self._json_file.close() |
+ self._json_file = None |
+ first_dump = '' |
+ last_dump = '' |
+ for filename in sorted(os.listdir(self._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._json_file = open( |
+ os.path.join(self._tempdir, |
+ 'endure.%05d.%s.json' % (proc_info['tab_pid'], |
+ last_sequence_id)), 'w+') |
+ self._proc = subprocess.Popen( |
+ '%s json %s' % (self._DMPROF_SCRIPT_PATH, |
+ os.path.join(self._tempdir, first_dump)), |
+ shell=True, stdout=self._json_file) |
+ # Wait only when it is the last profiling. dmprof may take long time. |
+ if is_last: |
+ self._WaitForDeepMemoryProfiler() |
+ |
dennis_jeffrey
2012/09/11 20:57:29
maybe remove this blank line
Dai Mikurube (NOT FULLTIME)
2012/09/12 02:23:24
Done.
|
+ else: |
+ logging.info(' No dump files.') |
+ |
+ def ParseResultAndOutputPerfGraphValues( |
+ self, webapp_name, test_description, output_perf_graph_value): |
+ """Parses Deep Memory Profiler result, and outputs perf graph values.""" |
+ if not self._enabled: |
+ return |
+ |
+ results = {} |
+ if self._last_json_filename: |
+ json_data = {} |
+ with open(self._last_json_filename) as json_f: |
+ json_data = json.load(json_f) |
+ if json_data['version'] == 'JSON_DEEP_1': |
+ results = json_data['snapshots'] |
+ elif json_data['version'] == 'JSON_DEEP_2': |
+ results = json_data['policies']['l2']['snapshots'] |
+ if results and results[-1]['second'] > self._last_time: |
+ started = False |
+ for legend in json_data['policies']['l2']['legends']: |
+ if legend == 'FROM_HERE_FOR_TOTAL': |
+ started = True |
+ elif legend == 'UNTIL_HERE_FOR_TOTAL': |
+ break |
+ elif started: |
+ output_perf_graph_value( |
+ legend.encode('utf-8'), [ |
+ (int(round(snapshot['second'])), snapshot[legend] / 1024) |
+ for snapshot in results |
+ if snapshot['second'] > self._last_time], |
+ 'KB', |
+ graph_name='%s%s-DMP' % (webapp_name, test_description), |
+ units_x='seconds', is_stacked=True) |
+ self._last_time = results[-1]['second'] |
+ |
+ def _WaitForDeepMemoryProfiler(self): |
+ """Waits for the Deep Memory Profiler to finish if running.""" |
+ if not self._enabled or not self._proc: |
+ return |
+ |
+ self._proc.wait() |
+ self._proc = None |
+ if self._json_file: |
+ self._last_json_filename = self._json_file.name |
+ self._json_file.close() |
+ self._json_file = None |
+ |
+ |
class ChromeEndureBaseTest(perf.BasePerfTest): |
"""Implements common functionality for all Chrome Endure tests. |
@@ -55,14 +229,6 @@ class ChromeEndureBaseTest(perf.BasePerfTest): |
_GET_PERF_STATS_INTERVAL = 60 * 5 # Measure perf stats every 5 minutes. |
# TODO(dennisjeffrey): Do we still need to tolerate errors? |
_ERROR_COUNT_THRESHOLD = 50 # Number of ChromeDriver errors to tolerate. |
- _DEEP_MEMORY_PROFILE = False |
- _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') |
def setUp(self): |
# The Web Page Replay environment variables must be parsed before |
@@ -70,18 +236,9 @@ class ChromeEndureBaseTest(perf.BasePerfTest): |
self._ParseReplayEnv() |
# 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) |
- |
- if self._deep_memory_profile: |
- if not self.IsLinux(): |
- raise NotSupportedEnvironmentError( |
- 'Deep Memory Profiler is not supported in this environment (OS).') |
- 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' |
+ self._dmprof = DeepMemoryProfiler() |
+ if self._dmprof: |
+ self._dmprof.SetUp(self.IsLinux()) |
perf.BasePerfTest.setUp(self) |
@@ -90,18 +247,12 @@ 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.') |
- 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.') |
+ |
+ if self._dmprof: |
+ self._dmprof.LogFirstMessage() |
# Set up a remote inspector client associated with tab 0. |
logging.info('Setting up connection to remote inspector...') |
@@ -112,31 +263,21 @@ 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 |
self._StartReplayServerIfNecessary() |
def tearDown(self): |
logging.info('Terminating connection to remote inspector...') |
self._remote_inspector_client.Stop() |
logging.info('Connection to remote inspector terminated.') |
- if self._deep_memory_profile: |
- 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) |
# Must be done after perf.BasePerfTest.tearDown() |
self._StopReplayServerIfNecessary() |
+ if self._dmprof: |
+ self._dmprof.TearDown() |
def _GetArchiveName(self): |
"""Return the Web Page Replay archive name that corresponds to a test. |
@@ -180,30 +321,6 @@ class ChromeEndureBaseTest(perf.BasePerfTest): |
'Skipping Web Page Replay since archive file %sdoes not exist.', |
self._archive_path + ' ' if self._archive_path else '') |
- 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'. |
- """ |
- 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. |
@@ -212,8 +329,8 @@ class ChromeEndureBaseTest(perf.BasePerfTest): |
""" |
# 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) |
+ deep_memory_profile = DeepMemoryProfiler.GetEnvironmentVariable( |
+ 'DEEP_MEMORY_PROFILE', bool, DeepMemoryProfiler.DEEP_MEMORY_PROFILE) |
# Ensure Chrome enables remote debugging on port 9222. This is required to |
# interact with Chrome's remote inspector. |
@@ -268,7 +385,7 @@ class ChromeEndureBaseTest(perf.BasePerfTest): |
self._num_errors = 0 |
self._test_start_time = time.time() |
last_perf_stats_time = time.time() |
- if self._deep_memory_profile: |
+ if self._dmprof: |
self.HeapProfilerDump('renderer', 'Chrome Endure (first)') |
self._GetPerformanceStats( |
webapp_name, test_description, tab_title_substring) |
@@ -287,7 +404,7 @@ class ChromeEndureBaseTest(perf.BasePerfTest): |
if time.time() - last_perf_stats_time >= self._get_perf_stats_interval: |
last_perf_stats_time = time.time() |
- if self._deep_memory_profile: |
+ if self._dmprof: |
self.HeapProfilerDump('renderer', 'Chrome Endure') |
self._GetPerformanceStats( |
webapp_name, test_description, tab_title_substring) |
@@ -317,7 +434,7 @@ class ChromeEndureBaseTest(perf.BasePerfTest): |
self._remote_inspector_client.StopTimelineEventMonitoring() |
- if self._deep_memory_profile: |
+ if self._dmprof: |
self.HeapProfilerDump('renderer', 'Chrome Endure (last)') |
self._GetPerformanceStats( |
webapp_name, test_description, tab_title_substring, is_last=True) |
@@ -398,53 +515,8 @@ class ChromeEndureBaseTest(perf.BasePerfTest): |
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' % (self._DMPROF_SCRIPT_PATH, |
- 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.') |
+ if self._dmprof: |
+ self._dmprof.StartProfiler(proc_info, is_last) |
# DOM node count. |
dom_node_count = memory_counts['DOMNodeCount'] |
@@ -490,27 +562,9 @@ class ChromeEndureBaseTest(perf.BasePerfTest): |
graph_name='%s%s-V8MemAllocated' % (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'] |
- elif json_data['version'] == 'JSON_DEEP_2': |
- deep_memory_profile_results = json_data['policies']['l0']['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. |
+ if self._dmprof: |
+ self._dmprof.ParseResultAndOutputPerfGraphValues( |
+ webapp_name, test_description, self._OutputPerfGraphValue) |
logging.info(' Total DOM node count: %d nodes' % dom_node_count) |
logging.info(' Event listener count: %d listeners' % event_listener_count) |