1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import os 7import sys 8 9from telemetry.internal.backends.chrome import android_browser_finder 10from telemetry.internal.platform import profiler 11 12# Environment variables to (android properties, default value) mapping. 13_ENV_VARIABLES = { 14 'HEAP_PROFILE_TIME_INTERVAL': ('heapprof.time_interval', 20), 15 'HEAP_PROFILE_MMAP': ('heapprof.mmap', 1), 16 'DEEP_HEAP_PROFILE': ('heapprof.deep_heap_profile', 1), 17} 18 19 20class _TCMallocHeapProfilerAndroid(object): 21 """An internal class to set android properties and fetch dumps from device.""" 22 23 _DEFAULT_DEVICE_DIR = '/data/local/tmp/heap' 24 25 def __init__(self, browser_backend, output_path): 26 self._browser_backend = browser_backend 27 self._output_path = output_path 28 29 _ENV_VARIABLES['HEAPPROFILE'] = ('heapprof', 30 os.path.join(self._DEFAULT_DEVICE_DIR, 'dmprof')) 31 32 self._SetDeviceProperties(_ENV_VARIABLES) 33 34 def _SetDeviceProperties(self, properties): 35 device_configured = False 36 # This profiler requires adb root to set properties. 37 try: 38 self._browser_backend.device.EnableRoot() 39 except: 40 logging.exception('New exception caused by DeviceUtils conversion') 41 raise 42 for values in properties.itervalues(): 43 device_property = self._browser_backend.device.GetProp(values[0]) 44 if not device_property or not device_property.strip(): 45 self._browser_backend.device.SetProp(values[0], values[1]) 46 device_configured = True 47 if not self._browser_backend.device.FileExists( 48 self._DEFAULT_DEVICE_DIR): 49 self._browser_backend.device.RunShellCommand( 50 'mkdir -p ' + self._DEFAULT_DEVICE_DIR) 51 self._browser_backend.device.RunShellCommand( 52 'chmod 777 ' + self._DEFAULT_DEVICE_DIR) 53 device_configured = True 54 if device_configured: 55 raise Exception('Device required special config, run again.') 56 57 def CollectProfile(self): 58 try: 59 self._browser_backend.device.PullFile( 60 self._DEFAULT_DEVICE_DIR, self._output_path) 61 except: 62 logging.exception('New exception caused by DeviceUtils conversion') 63 raise 64 self._browser_backend.device.RunShellCommand( 65 'rm ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*')) 66 if os.path.exists(self._output_path): 67 logging.info('TCMalloc dumps pulled to %s', self._output_path) 68 with file(os.path.join(self._output_path, 69 'browser.pid'), 'w') as pid_file: 70 pid_file.write(str(self._browser_backend.pid).rjust(5, '0')) 71 return [self._output_path] 72 73 74class _TCMallocHeapProfilerLinux(object): 75 """An internal class to set environment variables and fetch dumps.""" 76 77 _DEFAULT_DIR = '/tmp/tcmalloc/' 78 79 def __init__(self, browser_backend): 80 self._browser_backend = browser_backend 81 _ENV_VARIABLES['HEAPPROFILE'] = ('heapprof', self._DEFAULT_DIR + 'dmprof') 82 self._CheckEnvironmentVariables(_ENV_VARIABLES) 83 84 def _CheckEnvironmentVariables(self, env_vars): 85 msg = '' 86 for key, values in env_vars.iteritems(): 87 if key not in os.environ: 88 msg += '%s=%s ' % (key, str(values[1])) 89 if msg: 90 raise Exception('Need environment variables, try again with:\n %s' % msg) 91 if not os.path.exists(os.environ['HEAPPROFILE']): 92 os.makedirs(os.environ['HEAPPROFILE']) 93 assert os.path.isdir(os.environ['HEAPPROFILE']), 'HEAPPROFILE is not a dir' 94 95 def CollectProfile(self): 96 with file(os.path.join(os.path.dirname(os.environ['HEAPPROFILE']), 97 'browser.pid'), 'w') as pid_file: 98 pid_file.write(str(self._browser_backend.pid)) 99 print 'TCMalloc dumps available ', os.environ['HEAPPROFILE'] 100 return [os.environ['HEAPPROFILE']] 101 102 103class TCMallocHeapProfiler(profiler.Profiler): 104 """A Factory to instantiate the platform-specific profiler.""" 105 def __init__(self, browser_backend, platform_backend, output_path, state): 106 super(TCMallocHeapProfiler, self).__init__( 107 browser_backend, platform_backend, output_path, state) 108 if platform_backend.GetOSName() == 'android': 109 self._platform_profiler = _TCMallocHeapProfilerAndroid( 110 browser_backend, output_path) 111 else: 112 self._platform_profiler = _TCMallocHeapProfilerLinux(browser_backend) 113 114 @classmethod 115 def name(cls): 116 return 'tcmalloc-heap' 117 118 @classmethod 119 def is_supported(cls, browser_type): 120 if browser_type.startswith('cros'): 121 return False 122 if sys.platform.startswith('linux'): 123 return True 124 if browser_type == 'any': 125 return android_browser_finder.CanFindAvailableBrowsers() 126 return browser_type.startswith('android') 127 128 @classmethod 129 def CustomizeBrowserOptions(cls, browser_type, options): 130 options.AppendExtraBrowserArgs('--no-sandbox') 131 options.AppendExtraBrowserArgs('--enable-memory-benchmarking') 132 133 @classmethod 134 def WillCloseBrowser(cls, browser_backend, platform_backend): 135 # The tcmalloc_heap_profiler dumps files at regular 136 # intervals (~20 secs). 137 # This is a minor optimization to ensure it'll dump the last file when 138 # the test completes. 139 for i in xrange(len(browser_backend.browser.tabs)): 140 browser_backend.browser.tabs[i].ExecuteJavaScript(""" 141 if (chrome && chrome.memoryBenchmarking) { 142 chrome.memoryBenchmarking.heapProfilerDump('renderer', 'final'); 143 chrome.memoryBenchmarking.heapProfilerDump('browser', 'final'); 144 } 145 """) 146 147 def CollectProfile(self): 148 return self._platform_profiler.CollectProfile() 149