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 12WIN_TOOLCHAIN_DIR = 't' 13 14 15class DeviceDirs(object): 16 def __init__(self, 17 bin_dir, 18 dm_dir, 19 perf_data_dir, 20 resource_dir, 21 images_dir, 22 lotties_dir, 23 skp_dir, 24 svg_dir, 25 mskp_dir, 26 tmp_dir): 27 self._bin_dir = bin_dir 28 self._dm_dir = dm_dir 29 self._perf_data_dir = perf_data_dir 30 self._resource_dir = resource_dir 31 self._images_dir = images_dir 32 self._lotties_dir = lotties_dir 33 self._skp_dir = skp_dir 34 self._svg_dir = svg_dir 35 self._mskp_dir = mskp_dir 36 self._tmp_dir = tmp_dir 37 38 @property 39 def bin_dir(self): 40 return self._bin_dir 41 42 @property 43 def dm_dir(self): 44 """Where DM writes.""" 45 return self._dm_dir 46 47 @property 48 def perf_data_dir(self): 49 return self._perf_data_dir 50 51 @property 52 def resource_dir(self): 53 return self._resource_dir 54 55 @property 56 def images_dir(self): 57 return self._images_dir 58 59 @property 60 def lotties_dir(self): 61 return self._lotties_dir 62 63 @property 64 def skp_dir(self): 65 """Holds SKP files that are consumed by RenderSKPs and BenchPictures.""" 66 return self._skp_dir 67 68 @property 69 def svg_dir(self): 70 return self._svg_dir 71 72 @property 73 def mskp_dir(self): 74 return self._mskp_dir 75 76 @property 77 def tmp_dir(self): 78 return self._tmp_dir 79 80 81class DefaultFlavor(object): 82 def __init__(self, module): 83 # Store a pointer to the parent recipe module (SkiaFlavorApi) so that 84 # FlavorUtils objects can do recipe module-like things, like run steps or 85 # access module-level resources. 86 self.module = module 87 88 # self.m is just a shortcut so that Flavor objects can use the same 89 # syntax as regular recipe modules to run steps, eg: self.m.step(...) 90 self.m = module.m 91 self._chrome_path = None 92 self.device_dirs = DeviceDirs( 93 bin_dir=self.m.vars.build_dir, 94 dm_dir=self.m.vars.swarming_out_dir, 95 perf_data_dir=self.m.vars.swarming_out_dir, 96 resource_dir=self.m.path['start_dir'].join('skia', 'resources'), 97 images_dir=self.m.path['start_dir'].join('skimage'), 98 lotties_dir=self.m.path['start_dir'].join('lottie-samples'), 99 skp_dir=self.m.path['start_dir'].join('skp'), 100 svg_dir=self.m.path['start_dir'].join('svg'), 101 mskp_dir=self.m.path['start_dir'].join('mskp'), 102 tmp_dir=self.m.vars.tmp_dir) 103 self.host_dirs = self.device_dirs 104 105 def device_path_join(self, *args): 106 """Like os.path.join(), but for paths on a connected device.""" 107 return self.m.path.join(*args) 108 109 def copy_directory_contents_to_device(self, host_dir, device_dir): 110 """Like shutil.copytree(), but for copying to a connected device.""" 111 # For "normal" builders who don't have an attached device, we expect 112 # host_dir and device_dir to be the same. 113 if str(host_dir) != str(device_dir): 114 raise ValueError('For builders who do not have attached devices, copying ' 115 'from host to device is undefined and only allowed if ' 116 'host_path and device_path are the same (%s vs %s).' % ( 117 str(host_dir), str(device_dir))) 118 119 def copy_directory_contents_to_host(self, device_dir, host_dir): 120 """Like shutil.copytree(), but for copying from a connected device.""" 121 # For "normal" builders who don't have an attached device, we expect 122 # host_dir and device_dir to be the same. 123 if str(host_dir) != str(device_dir): 124 raise ValueError('For builders who do not have attached devices, copying ' 125 'from device to host is undefined and only allowed if ' 126 'host_path and device_path are the same (%s vs %s).' % ( 127 str(host_dir), str(device_dir))) 128 129 def copy_file_to_device(self, host_path, device_path): 130 """Like shutil.copyfile, but for copying to a connected device.""" 131 # For "normal" builders who don't have an attached device, we expect 132 # host_dir and device_dir to be the same. 133 if str(host_path) != str(device_path): 134 raise ValueError('For builders who do not have attached devices, copying ' 135 'from host to device is undefined and only allowed if ' 136 'host_path and device_path are the same (%s vs %s).' % ( 137 str(host_path), str(device_path))) 138 139 def create_clean_device_dir(self, path): 140 """Like shutil.rmtree() + os.makedirs(), but on a connected device.""" 141 self.create_clean_host_dir(path) 142 143 def create_clean_host_dir(self, path): 144 """Convenience function for creating a clean directory.""" 145 self.m.run.rmtree(path) 146 self.m.file.ensure_directory( 147 'makedirs %s' % self.m.path.basename(path), path) 148 149 def read_file_on_device(self, path, **kwargs): 150 """Reads the specified file.""" 151 return self.m.file.read_text('read %s' % path, path) 152 153 def remove_file_on_device(self, path): 154 """Removes the specified file.""" 155 return self.m.file.remove('remove %s' % path, path) 156 157 def install(self): 158 """Run device-specific installation steps.""" 159 pass 160 161 def cleanup_steps(self): 162 """Run any device-specific cleanup steps.""" 163 pass 164 165 def _run(self, title, cmd, infra_step=False, **kwargs): 166 return self.m.run(self.m.step, title, cmd=cmd, 167 infra_step=infra_step, **kwargs) 168 169 def _py(self, title, script, infra_step=True, args=()): 170 return self.m.run(self.m.python, title, script=script, args=args, 171 infra_step=infra_step) 172 173 def step(self, name, cmd, **unused_kwargs): 174 app = self.device_dirs.bin_dir.join(cmd[0]) 175 cmd = [app] + cmd[1:] 176 env = self.m.context.env 177 path = [] 178 ld_library_path = [] 179 180 slave_dir = self.m.vars.slave_dir 181 clang_linux = str(slave_dir.join('clang_linux')) 182 extra_tokens = self.m.vars.extra_tokens 183 184 if self.m.vars.is_linux: 185 if (self.m.vars.builder_cfg.get('cpu_or_gpu', '') == 'GPU' 186 and 'Intel' in self.m.vars.builder_cfg.get('cpu_or_gpu_value', '')): 187 dri_path = slave_dir.join('mesa_intel_driver_linux') 188 ld_library_path.append(dri_path) 189 env['LIBGL_DRIVERS_PATH'] = str(dri_path) 190 env['VK_ICD_FILENAMES'] = str(dri_path.join('intel_icd.x86_64.json')) 191 192 if 'Vulkan' in extra_tokens: 193 path.append(slave_dir.join('linux_vulkan_sdk', 'bin')) 194 ld_library_path.append(slave_dir.join('linux_vulkan_sdk', 'lib')) 195 196 if 'OpenCL' in extra_tokens: 197 ld_library_path.append(slave_dir.join('opencl_ocl_icd_linux')) 198 # TODO(dogben): Limit to the appropriate GPUs when we start running on 199 # GPUs other than IntelIris640. 200 # Skylake and later use the NEO driver. 201 neo_path = slave_dir.join('opencl_intel_neo_linux') 202 ld_library_path.append(neo_path) 203 # Generate vendors dir contaning the ICD file pointing to the NEO OpenCL 204 # library. 205 vendors_dir = self.m.vars.tmp_dir.join('OpenCL', 'vendors') 206 self.m.file.ensure_directory('mkdirs OpenCL/vendors', vendors_dir) 207 self.m.file.write_raw('write NEO OpenCL ICD', 208 vendors_dir.join('neo.icd'), 209 '%s\n' % neo_path.join('libigdrcl.so')) 210 env['OPENCL_VENDOR_PATH'] = vendors_dir 211 212 if 'SwiftShader' in extra_tokens: 213 ld_library_path.append(self.host_dirs.bin_dir.join('swiftshader_out')) 214 215 if 'MSAN' in extra_tokens: 216 # Find the MSAN-built libc++. 217 ld_library_path.append(clang_linux + '/msan') 218 219 if any('SAN' in t for t in extra_tokens): 220 # Sanitized binaries may want to run clang_linux/bin/llvm-symbolizer. 221 path.append(clang_linux + '/bin') 222 # We find that testing sanitizer builds with libc++ uncovers more issues 223 # than with the system-provided C++ standard library, which is usually 224 # libstdc++. libc++ proactively hooks into sanitizers to help their 225 # analyses. We ship a copy of libc++ with our Linux toolchain in /lib. 226 ld_library_path.append(clang_linux + '/lib') 227 elif self.m.vars.is_linux: 228 cmd = ['catchsegv'] + cmd 229 elif 'ProcDump' in extra_tokens: 230 dumps_dir = self.m.path.join(self.m.vars.swarming_out_dir, 'dumps') 231 self.m.file.ensure_directory('makedirs dumps', dumps_dir) 232 procdump = str(self.m.vars.slave_dir.join('procdump_win', 233 'procdump64.exe')) 234 # Full docs for ProcDump here: 235 # https://docs.microsoft.com/en-us/sysinternals/downloads/procdump 236 # -accepteula automatically accepts the license agreement 237 # -mp saves a packed minidump to save space 238 # -e 1 tells procdump to dump once 239 # -x <dump dir> <exe> <args> launches exe and writes dumps to the 240 # specified dir 241 cmd = [procdump, '-accepteula', '-mp', '-e', '1', '-x', dumps_dir] + cmd 242 243 if 'ASAN' in extra_tokens or 'UBSAN' in extra_tokens: 244 # Note: if you see "<unknown module>" in stacktraces for xSAN warnings, 245 # try adding "fast_unwind_on_malloc=0" to xSAN_OPTIONS. 246 if 'Mac' in self.m.vars.builder_cfg.get('os', ''): 247 env['ASAN_OPTIONS'] = 'symbolize=1' # Mac doesn't support detect_leaks. 248 else: 249 env['ASAN_OPTIONS'] = 'symbolize=1 detect_leaks=1' 250 env[ 'LSAN_OPTIONS'] = 'symbolize=1 print_suppressions=1' 251 env['UBSAN_OPTIONS'] = 'symbolize=1 print_stacktrace=1' 252 253 if 'TSAN' in extra_tokens: 254 # We don't care about malloc(), fprintf, etc. used in signal handlers. 255 # If we're in a signal handler, we're already crashing... 256 env['TSAN_OPTIONS'] = 'report_signal_unsafe=0' 257 258 if 'Coverage' in extra_tokens: 259 # This is the output file for the coverage data. Just running the binary 260 # will produce the output. The output_file is in the swarming_out_dir and 261 # thus will be an isolated output of the Test step. 262 profname = '%s.profraw' % self.m.vars.builder_cfg.get('test_filter','o') 263 env['LLVM_PROFILE_FILE'] = self.m.path.join(self.m.vars.swarming_out_dir, 264 profname) 265 266 if path: 267 env['PATH'] = self.m.path.pathsep.join( 268 ['%(PATH)s'] + ['%s' % p for p in path]) 269 if ld_library_path: 270 env['LD_LIBRARY_PATH'] = self.m.path.pathsep.join( 271 '%s' % p for p in ld_library_path) 272 273 to_symbolize = ['dm', 'nanobench'] 274 if name in to_symbolize and self.m.vars.is_linux: 275 # Convert path objects or placeholders into strings such that they can 276 # be passed to symbolize_stack_trace.py 277 args = [slave_dir] + [str(x) for x in cmd] 278 with self.m.context(cwd=self.m.path['start_dir'].join('skia'), env=env): 279 self._py('symbolized %s' % name, 280 self.module.resource('symbolize_stack_trace.py'), 281 args=args, 282 infra_step=False) 283 elif 'Win' in self.m.vars.builder_cfg.get('os', ''): 284 with self.m.context(env=env): 285 wrapped_cmd = ['powershell', '-ExecutionPolicy', 'Unrestricted', 286 '-File', 287 self.module.resource('win_run_and_check_log.ps1')] + cmd 288 self._run(name, wrapped_cmd) 289 else: 290 with self.m.context(env=env): 291 self._run(name, cmd) 292