133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Copyright 2013 The Chromium Authors. All rights reserved. 233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Use of this source code is governed by a BSD-style license that can be 333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# found in the LICENSE file. 433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport logging 633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport os 733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport sys 833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckfrom telemetry.internal.backends.chrome import android_browser_finder 1033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckfrom telemetry.internal.platform import profiler 1133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 1233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Environment variables to (android properties, default value) mapping. 1333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck_ENV_VARIABLES = { 1433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 'HEAP_PROFILE_TIME_INTERVAL': ('heapprof.time_interval', 20), 1533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 'HEAP_PROFILE_MMAP': ('heapprof.mmap', 1), 1633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 'DEEP_HEAP_PROFILE': ('heapprof.deep_heap_profile', 1), 1733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck} 1833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 1933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 2033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass _TCMallocHeapProfilerAndroid(object): 2133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck """An internal class to set android properties and fetch dumps from device.""" 2233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 2333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck _DEFAULT_DEVICE_DIR = '/data/local/tmp/heap' 2433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 2533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def __init__(self, browser_backend, output_path): 2633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._browser_backend = browser_backend 2733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._output_path = output_path 2833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 2933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck _ENV_VARIABLES['HEAPPROFILE'] = ('heapprof', 3033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck os.path.join(self._DEFAULT_DEVICE_DIR, 'dmprof')) 3133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 3233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._SetDeviceProperties(_ENV_VARIABLES) 3333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 3433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def _SetDeviceProperties(self, properties): 3533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck device_configured = False 3633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck # This profiler requires adb root to set properties. 37a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik self._browser_backend.device.EnableRoot() 3833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck for values in properties.itervalues(): 3933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck device_property = self._browser_backend.device.GetProp(values[0]) 4033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if not device_property or not device_property.strip(): 4133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._browser_backend.device.SetProp(values[0], values[1]) 4233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck device_configured = True 4333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if not self._browser_backend.device.FileExists( 4433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._DEFAULT_DEVICE_DIR): 4533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._browser_backend.device.RunShellCommand( 46a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik ['mkdir', '-p', self._DEFAULT_DEVICE_DIR], check_return=True) 4733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._browser_backend.device.RunShellCommand( 48a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik ['chmod', '777', self._DEFAULT_DEVICE_DIR], check_return=True) 4933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck device_configured = True 5033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if device_configured: 5133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck raise Exception('Device required special config, run again.') 5233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 5333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def CollectProfile(self): 5433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck try: 5533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._browser_backend.device.PullFile( 5633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._DEFAULT_DEVICE_DIR, self._output_path) 5733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck except: 5833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck logging.exception('New exception caused by DeviceUtils conversion') 5933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck raise 60a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik # Note: command must be passed as a string to expand wildcards. 6133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._browser_backend.device.RunShellCommand( 62a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik 'rm -f ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*'), 63a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craik check_return=True, shell=True) 6433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if os.path.exists(self._output_path): 6533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck logging.info('TCMalloc dumps pulled to %s', self._output_path) 6633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck with file(os.path.join(self._output_path, 6733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 'browser.pid'), 'w') as pid_file: 6833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck pid_file.write(str(self._browser_backend.pid).rjust(5, '0')) 6933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck return [self._output_path] 7033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 7133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 7233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass _TCMallocHeapProfilerLinux(object): 7333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck """An internal class to set environment variables and fetch dumps.""" 7433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 7533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck _DEFAULT_DIR = '/tmp/tcmalloc/' 7633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 7733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def __init__(self, browser_backend): 7833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._browser_backend = browser_backend 7933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck _ENV_VARIABLES['HEAPPROFILE'] = ('heapprof', self._DEFAULT_DIR + 'dmprof') 8033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._CheckEnvironmentVariables(_ENV_VARIABLES) 8133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 8233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def _CheckEnvironmentVariables(self, env_vars): 8333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck msg = '' 8433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck for key, values in env_vars.iteritems(): 8533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if key not in os.environ: 8633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck msg += '%s=%s ' % (key, str(values[1])) 8733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if msg: 8833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck raise Exception('Need environment variables, try again with:\n %s' % msg) 8933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if not os.path.exists(os.environ['HEAPPROFILE']): 9033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck os.makedirs(os.environ['HEAPPROFILE']) 9133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck assert os.path.isdir(os.environ['HEAPPROFILE']), 'HEAPPROFILE is not a dir' 9233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 9333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def CollectProfile(self): 9433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck with file(os.path.join(os.path.dirname(os.environ['HEAPPROFILE']), 9533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 'browser.pid'), 'w') as pid_file: 9633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck pid_file.write(str(self._browser_backend.pid)) 9733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck print 'TCMalloc dumps available ', os.environ['HEAPPROFILE'] 9833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck return [os.environ['HEAPPROFILE']] 9933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 10033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 10133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass TCMallocHeapProfiler(profiler.Profiler): 10233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck """A Factory to instantiate the platform-specific profiler.""" 10333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def __init__(self, browser_backend, platform_backend, output_path, state): 10433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck super(TCMallocHeapProfiler, self).__init__( 10533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck browser_backend, platform_backend, output_path, state) 10633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if platform_backend.GetOSName() == 'android': 10733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._platform_profiler = _TCMallocHeapProfilerAndroid( 10833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck browser_backend, output_path) 10933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck else: 11033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck self._platform_profiler = _TCMallocHeapProfilerLinux(browser_backend) 11133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 11233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck @classmethod 11333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def name(cls): 11433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck return 'tcmalloc-heap' 11533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 11633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck @classmethod 11733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def is_supported(cls, browser_type): 11833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if browser_type.startswith('cros'): 11933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck return False 12033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if sys.platform.startswith('linux'): 12133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck return True 12233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if browser_type == 'any': 12333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck return android_browser_finder.CanFindAvailableBrowsers() 12433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck return browser_type.startswith('android') 12533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 12633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck @classmethod 12733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def CustomizeBrowserOptions(cls, browser_type, options): 12833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck options.AppendExtraBrowserArgs('--no-sandbox') 12933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck options.AppendExtraBrowserArgs('--enable-memory-benchmarking') 13033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 13133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck @classmethod 13233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def WillCloseBrowser(cls, browser_backend, platform_backend): 13333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck # The tcmalloc_heap_profiler dumps files at regular 13433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck # intervals (~20 secs). 13533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck # This is a minor optimization to ensure it'll dump the last file when 13633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck # the test completes. 13733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck for i in xrange(len(browser_backend.browser.tabs)): 13833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck browser_backend.browser.tabs[i].ExecuteJavaScript(""" 13933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck if (chrome && chrome.memoryBenchmarking) { 14033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck chrome.memoryBenchmarking.heapProfilerDump('renderer', 'final'); 14133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck chrome.memoryBenchmarking.heapProfilerDump('browser', 'final'); 14233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck } 14333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck """) 14433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck 14533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck def CollectProfile(self): 14633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck return self._platform_profiler.CollectProfile() 147