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