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', 'fonts_dir', 23 'lotties_dir', 'skp_dir', 'svg_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.joinpath('skia', 'resources'), 46 images_dir=self.m.path.start_dir.joinpath('skimage'), 47 fonts_dir=self.m.path.start_dir.joinpath('googlefonts_testdata', 'data'), 48 lotties_dir=self.m.path.start_dir.joinpath('lottie-samples'), 49 skp_dir=self.m.path.start_dir.joinpath('skp'), 50 svg_dir=self.m.path.start_dir.joinpath('svg'), 51 tmp_dir=self.m.vars.tmp_dir, 52 texttraces_dir=self.m.path.start_dir.joinpath('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.step, title, cmd=['python3', script]+args, 121 infra_step=infra_step) 122 123 def step(self, name, cmd, **unused_kwargs): 124 app = self.device_dirs.bin_dir.joinpath(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 = workdir.joinpath('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.joinpath('mesa_intel_driver_linux') 138 if ('IntelIrisXe' in self.m.vars.builder_cfg.get('cpu_or_gpu_value', '')): 139 dri_path = workdir.joinpath('mesa_intel_driver_linux_22') 140 ld_library_path.append(dri_path) 141 env['LIBGL_DRIVERS_PATH'] = str(dri_path) 142 env['VK_ICD_FILENAMES'] = str(dri_path.joinpath('intel_icd.x86_64.json')) 143 144 if 'Vulkan' in extra_tokens: 145 env['VULKAN_SDK'] = str(workdir.joinpath('linux_vulkan_sdk')) 146 path.append(workdir.joinpath('linux_vulkan_sdk', 'bin')) 147 ld_library_path.append(workdir.joinpath('linux_vulkan_sdk', 'lib')) 148 # Enable layers for Debug only to avoid affecting perf results on 149 # Release. 150 # ASAN reports leaks in the Vulkan SDK when the debug layer is enabled. 151 # TSAN runs out of memory. 152 if (self.m.vars.builder_cfg.get('configuration', '') != 'Release' and 153 'ASAN' not in extra_tokens and 154 'TSAN' not in extra_tokens): 155 env['VK_LAYER_PATH'] = str(workdir.joinpath( 156 'linux_vulkan_sdk', 'etc', 'vulkan', 'explicit_layer.d')) 157 158 if 'SwiftShader' in extra_tokens: 159 ld_library_path.append(self.host_dirs.bin_dir.joinpath('swiftshader_out')) 160 161 # Find the MSAN/TSAN-built libc++. 162 if 'MSAN' in extra_tokens: 163 ld_library_path.append(clang_linux.joinpath('msan')) 164 elif 'TSAN' in extra_tokens: 165 ld_library_path.append(clang_linux.joinpath('tsan')) 166 167 if any('SAN' in t for t in extra_tokens): 168 # Sanitized binaries may want to run clang_linux/bin/llvm-symbolizer. 169 path.append(clang_linux.joinpath('bin')) 170 # We find that testing sanitizer builds with libc++ uncovers more issues 171 # than with the system-provided C++ standard library, which is usually 172 # libstdc++. libc++ proactively hooks into sanitizers to help their 173 # analyses. We ship a copy of libc++ with our Linux toolchain in /lib. 174 ld_library_path.append(clang_linux.joinpath('lib', 'x86_64-unknown-linux-gnu')) 175 176 if 'ASAN' in extra_tokens: 177 os = self.m.vars.builder_cfg.get('os', '') 178 if 'Mac' in os or 'Win' in os: 179 # Mac and Win don't support detect_leaks. 180 env['ASAN_OPTIONS'] = 'symbolize=1' 181 else: 182 env['ASAN_OPTIONS'] = 'symbolize=1 detect_leaks=1' 183 env['ASAN_SYMBOLIZER_PATH'] = clang_linux.joinpath('bin', 'llvm-symbolizer') 184 env[ 'LSAN_OPTIONS'] = 'symbolize=1 print_suppressions=1' 185 env['UBSAN_OPTIONS'] = 'symbolize=1 print_stacktrace=1' 186 187 # If you see <unknown module> in stacktraces, try fast_unwind_on_malloc=0. 188 # This may cause a 2-25x slowdown, so use it only when you really need it. 189 if name == 'dm' and 'Vulkan' in extra_tokens: 190 env['ASAN_OPTIONS'] += ' fast_unwind_on_malloc=0' 191 env['LSAN_OPTIONS'] += ' fast_unwind_on_malloc=0' 192 193 if 'TSAN' in extra_tokens: 194 # We don't care about malloc(), fprintf, etc. used in signal handlers. 195 # If we're in a signal handler, we're already crashing... 196 env['TSAN_OPTIONS'] = 'report_signal_unsafe=0' 197 198 if 'Coverage' in extra_tokens: 199 # This is the output file for the coverage data. Just running the binary 200 # will produce the output. The output_file is in the swarming_out_dir and 201 # thus will be an isolated output of the Test step. 202 profname = '%s.profraw' % self.m.vars.builder_cfg.get('test_filter','o') 203 env['LLVM_PROFILE_FILE'] = self.m.path.join(self.m.vars.swarming_out_dir, 204 profname) 205 206 if 'DWriteCore' in extra_tokens: 207 path.append(workdir.joinpath('dwritecore', 'bin')) 208 209 if path: 210 env['PATH'] = self.m.path.pathsep.join( 211 ['%(PATH)s'] + ['%s' % p for p in path]) 212 if ld_library_path: 213 env['LD_LIBRARY_PATH'] = self.m.path.pathsep.join( 214 '%s' % p for p in ld_library_path) 215 216 to_symbolize = ['dm', 'nanobench'] 217 if name in to_symbolize and self.m.vars.is_linux: 218 # Convert path objects or placeholders into strings such that they can 219 # be passed to symbolize_stack_trace.py 220 args = [workdir] + [str(x) for x in cmd] 221 with self.m.context(cwd=self.m.path.start_dir.joinpath('skia'), env=env): 222 self._py('symbolized %s' % name, 223 self.module.resource('symbolize_stack_trace.py'), 224 args=args, 225 infra_step=False) 226 elif 'Win' in self.m.vars.builder_cfg.get('os', ''): 227 with self.m.context(env=env): 228 wrapped_cmd = ['powershell', '-ExecutionPolicy', 'Unrestricted', 229 '-File', 230 self.module.resource('win_run_and_check_log.ps1')] + cmd 231 self._run(name, wrapped_cmd) 232 else: 233 with self.m.context(env=env): 234 self._run(name, cmd) 235