1# Copyright 2014 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 5 6# pylint: disable=W0201 7 8 9"""Default flavor, used for running code on desktop machines.""" 10 11 12import collections 13 14 15WIN_TOOLCHAIN_DIR = 't' 16 17 18# Notes: 19# dm_dir: Where DM writes. 20# skp_dir: Holds SKP files that are consumed by RenderSKPs and BenchPictures. 21DeviceDirs = collections.namedtuple( 22 'DeviceDirs', ['bin_dir', 'dm_dir', 'perf_data_dir', 'resource_dir', 'images_dir', 23 'lotties_dir', 'skp_dir', 'svg_dir', 'mskp_dir', 'tmp_dir', 'texttraces_dir']) 24 25 26class DefaultFlavor(object): 27 def __init__(self, module, app_name): 28 # Name of the app we're going to run. May be used in various ways by 29 # different flavors. 30 self.app_name = app_name 31 32 # Store a pointer to the parent recipe module (SkiaFlavorApi) so that 33 # FlavorUtils objects can do recipe module-like things, like run steps or 34 # access module-level resources. 35 self.module = module 36 37 # self.m is just a shortcut so that Flavor objects can use the same 38 # syntax as regular recipe modules to run steps, eg: self.m.step(...) 39 self.m = module.m 40 self._chrome_path = None 41 self.device_dirs = DeviceDirs( 42 bin_dir=self.m.vars.build_dir, 43 dm_dir=self.m.vars.swarming_out_dir, 44 perf_data_dir=self.m.vars.swarming_out_dir, 45 resource_dir=self.m.path['start_dir'].join('skia', 'resources'), 46 images_dir=self.m.path['start_dir'].join('skimage'), 47 lotties_dir=self.m.path['start_dir'].join('lottie-samples'), 48 skp_dir=self.m.path['start_dir'].join('skp'), 49 svg_dir=self.m.path['start_dir'].join('svg'), 50 mskp_dir=self.m.path['start_dir'].join('mskp'), 51 tmp_dir=self.m.vars.tmp_dir, 52 texttraces_dir=self.m.path['start_dir'].join('text_blob_traces')) 53 self.host_dirs = self.device_dirs 54 55 def device_path_join(self, *args): 56 """Like os.path.join(), but for paths on a connected device.""" 57 return self.m.path.join(*args) 58 59 def copy_directory_contents_to_device(self, host_dir, device_dir): 60 """Like shutil.copytree(), but for copying to a connected device.""" 61 # For "normal" builders who don't have an attached device, we expect 62 # host_dir and device_dir to be the same. 63 if str(host_dir) != str(device_dir): 64 raise ValueError('For builders who do not have attached devices, copying ' 65 'from host to device is undefined and only allowed if ' 66 'host_path and device_path are the same (%s vs %s).' % ( 67 str(host_dir), str(device_dir))) 68 69 def copy_directory_contents_to_host(self, device_dir, host_dir): 70 """Like shutil.copytree(), but for copying from a connected device.""" 71 # For "normal" builders who don't have an attached device, we expect 72 # host_dir and device_dir to be the same. 73 if str(host_dir) != str(device_dir): 74 raise ValueError('For builders who do not have attached devices, copying ' 75 'from device to host is undefined and only allowed if ' 76 'host_path and device_path are the same (%s vs %s).' % ( 77 str(host_dir), str(device_dir))) 78 79 def copy_file_to_device(self, host_path, device_path): 80 """Like shutil.copyfile, but for copying to a connected device.""" 81 # For "normal" builders who don't have an attached device, we expect 82 # host_dir and device_dir to be the same. 83 if str(host_path) != str(device_path): 84 raise ValueError('For builders who do not have attached devices, copying ' 85 'from host to device is undefined and only allowed if ' 86 'host_path and device_path are the same (%s vs %s).' % ( 87 str(host_path), str(device_path))) 88 89 def create_clean_device_dir(self, path): 90 """Like shutil.rmtree() + os.makedirs(), but on a connected device.""" 91 self.create_clean_host_dir(path) 92 93 def create_clean_host_dir(self, path): 94 """Convenience function for creating a clean directory.""" 95 self.m.run.rmtree(path) 96 self.m.file.ensure_directory( 97 'makedirs %s' % self.m.path.basename(path), path) 98 99 def read_file_on_device(self, path, **kwargs): 100 """Reads the specified file.""" 101 return self.m.file.read_text('read %s' % path, path) 102 103 def remove_file_on_device(self, path): 104 """Removes the specified file.""" 105 return self.m.file.remove('remove %s' % path, path) 106 107 def install(self): 108 """Run device-specific installation steps.""" 109 pass 110 111 def cleanup_steps(self): 112 """Run any device-specific cleanup steps.""" 113 pass 114 115 def _run(self, title, cmd, infra_step=False, **kwargs): 116 return self.m.run(self.m.step, title, cmd=cmd, 117 infra_step=infra_step, **kwargs) 118 119 def _py(self, title, script, infra_step=True, args=()): 120 return self.m.run(self.m.python, title, script=script, args=args, 121 infra_step=infra_step) 122 123 def step(self, name, cmd, **unused_kwargs): 124 app = self.device_dirs.bin_dir.join(cmd[0]) 125 cmd = [app] + cmd[1:] 126 env = self.m.context.env 127 path = [] 128 ld_library_path = [] 129 130 workdir = self.m.vars.workdir 131 clang_linux = str(workdir.join('clang_linux')) 132 extra_tokens = self.m.vars.extra_tokens 133 134 if self.m.vars.is_linux: 135 if (self.m.vars.builder_cfg.get('cpu_or_gpu', '') == 'GPU' 136 and 'Intel' in self.m.vars.builder_cfg.get('cpu_or_gpu_value', '')): 137 dri_path = workdir.join('mesa_intel_driver_linux') 138 ld_library_path.append(dri_path) 139 env['LIBGL_DRIVERS_PATH'] = str(dri_path) 140 env['VK_ICD_FILENAMES'] = str(dri_path.join('intel_icd.x86_64.json')) 141 142 if 'Vulkan' in extra_tokens: 143 env['VULKAN_SDK'] = str(workdir.join('linux_vulkan_sdk')) 144 path.append(workdir.join('linux_vulkan_sdk', 'bin')) 145 ld_library_path.append(workdir.join('linux_vulkan_sdk', 'lib')) 146 # Enable layers for Debug only to avoid affecting perf results on 147 # Release. 148 # ASAN reports leaks in the Vulkan SDK when the debug layer is enabled. 149 # TSAN runs out of memory. 150 if (self.m.vars.builder_cfg.get('configuration', '') != 'Release' and 151 'ASAN' not in extra_tokens and 152 'TSAN' not in extra_tokens): 153 env['VK_LAYER_PATH'] = str(workdir.join( 154 'linux_vulkan_sdk', 'etc', 'vulkan', 'explicit_layer.d')) 155 156 if 'SwiftShader' in extra_tokens: 157 ld_library_path.append(self.host_dirs.bin_dir.join('swiftshader_out')) 158 159 # Find the MSAN/TSAN-built libc++. 160 if 'MSAN' in extra_tokens: 161 ld_library_path.append(clang_linux + '/msan') 162 elif 'TSAN' in extra_tokens: 163 ld_library_path.append(clang_linux + '/tsan') 164 165 if any('SAN' in t for t in extra_tokens): 166 # Sanitized binaries may want to run clang_linux/bin/llvm-symbolizer. 167 path.append(clang_linux + '/bin') 168 # We find that testing sanitizer builds with libc++ uncovers more issues 169 # than with the system-provided C++ standard library, which is usually 170 # libstdc++. libc++ proactively hooks into sanitizers to help their 171 # analyses. We ship a copy of libc++ with our Linux toolchain in /lib. 172 ld_library_path.append(clang_linux + '/lib') 173 elif self.m.vars.is_linux: 174 cmd = ['catchsegv'] + cmd 175 elif 'ProcDump' in extra_tokens: 176 dumps_dir = self.m.path.join(self.m.vars.swarming_out_dir, 'dumps') 177 self.m.file.ensure_directory('makedirs dumps', dumps_dir) 178 procdump = str(self.m.vars.workdir.join('procdump_win', 179 'procdump64.exe')) 180 # Full docs for ProcDump here: 181 # https://docs.microsoft.com/en-us/sysinternals/downloads/procdump 182 # -accepteula automatically accepts the license agreement 183 # -mp saves a packed minidump to save space 184 # -e 1 tells procdump to dump once 185 # -x <dump dir> <exe> <args> launches exe and writes dumps to the 186 # specified dir 187 cmd = [procdump, '-accepteula', '-mp', '-e', '1', '-x', dumps_dir] + cmd 188 189 if 'ASAN' in extra_tokens: 190 os = self.m.vars.builder_cfg.get('os', '') 191 if 'Mac' in os or 'Win' in os: 192 # Mac and Win don't support detect_leaks. 193 env['ASAN_OPTIONS'] = 'symbolize=1' 194 else: 195 env['ASAN_OPTIONS'] = 'symbolize=1 detect_leaks=1' 196 env[ 'LSAN_OPTIONS'] = 'symbolize=1 print_suppressions=1' 197 env['UBSAN_OPTIONS'] = 'symbolize=1 print_stacktrace=1' 198 199 # If you see <unknown module> in stacktraces, try fast_unwind_on_malloc=0. 200 # This may cause a 2-25x slowdown, so use it only when you really need it. 201 if name == 'dm' and 'Vulkan' in extra_tokens: 202 env['ASAN_OPTIONS'] += ' fast_unwind_on_malloc=0' 203 env['LSAN_OPTIONS'] += ' fast_unwind_on_malloc=0' 204 205 if 'TSAN' in extra_tokens: 206 # We don't care about malloc(), fprintf, etc. used in signal handlers. 207 # If we're in a signal handler, we're already crashing... 208 env['TSAN_OPTIONS'] = 'report_signal_unsafe=0' 209 210 if 'Coverage' in extra_tokens: 211 # This is the output file for the coverage data. Just running the binary 212 # will produce the output. The output_file is in the swarming_out_dir and 213 # thus will be an isolated output of the Test step. 214 profname = '%s.profraw' % self.m.vars.builder_cfg.get('test_filter','o') 215 env['LLVM_PROFILE_FILE'] = self.m.path.join(self.m.vars.swarming_out_dir, 216 profname) 217 218 if path: 219 env['PATH'] = self.m.path.pathsep.join( 220 ['%(PATH)s'] + ['%s' % p for p in path]) 221 if ld_library_path: 222 env['LD_LIBRARY_PATH'] = self.m.path.pathsep.join( 223 '%s' % p for p in ld_library_path) 224 225 to_symbolize = ['dm', 'nanobench'] 226 if name in to_symbolize and self.m.vars.is_linux: 227 # Convert path objects or placeholders into strings such that they can 228 # be passed to symbolize_stack_trace.py 229 args = [workdir] + [str(x) for x in cmd] 230 with self.m.context(cwd=self.m.path['start_dir'].join('skia'), env=env): 231 self._py('symbolized %s' % name, 232 self.module.resource('symbolize_stack_trace.py'), 233 args=args, 234 infra_step=False) 235 elif 'Win' in self.m.vars.builder_cfg.get('os', ''): 236 with self.m.context(env=env): 237 wrapped_cmd = ['powershell', '-ExecutionPolicy', 'Unrestricted', 238 '-File', 239 self.module.resource('win_run_and_check_log.ps1')] + cmd 240 self._run(name, wrapped_cmd) 241 else: 242 with self.m.context(env=env): 243 self._run(name, cmd) 244