Changeset 279984 in webkit
- Timestamp:
- Jul 16, 2021, 7:25:26 AM (4 years ago)
- Location:
- trunk/Tools
- Files:
-
- 5 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Tools/ChangeLog
r279982 r279984 1 2021-07-16 Angelos Oikonomopoulos <angelos@igalia.com> 2 3 Bundle libraries for remote execution in run-jsc-benchmarks 4 https://bugs.webkit.org/show_bug.cgi?id=227579 5 6 Reviewed by Carlos Alberto Lopez Perez. 7 8 Similarly to run-jsc-stress-tests, introduce a bundling step that 9 ships all library dependencies to the remote system when using 10 run-jsc-benchmarks --remote. 11 12 This patch factors out the code to 13 - lookup the ELF interpreter/libraries and to 14 - strip the rpath and create a wrapper script 15 from generate-bundle and places it webkitpy/binary_bundling. 16 17 It also introduces a simpler script that only bundles a single 18 binary (bundle-binary) and switches run-jsc-benchmarks and 19 run-jsc-stress-tests to use it. 20 21 It also updates run-jsc-benchmark to propagate any environment 22 variables intended for consumption by the JSC binary. 23 24 * Scripts/generate-bundle: Factor out reusable bundling code. 25 * Scripts/run-jsc-benchmarks: Do bundling and variable propagation. 26 * Scripts/run-jsc-stress-tests: Switch to bundle-binary. 27 * Scripts/webkitpy/binary_bundling/__init__.py: Added. 28 * Scripts/webkitpy/binary_bundling/bundle.py: Added. 29 (BinaryBundler): 30 (BinaryBundler.__init__): 31 (BinaryBundler.destination_dir): 32 (BinaryBundler.copy_and_remove_rpath): 33 (BinaryBundler.generate_wrapper_script): 34 * Scripts/webkitpy/binary_bundling/ldd.py: Added. 35 (SharedObjectResolver): 36 (SharedObjectResolver.__init__): 37 (SharedObjectResolver._run_cmd_and_get_output): 38 (SharedObjectResolver._get_interpreter_objname): 39 (SharedObjectResolver._get_libs_and_interpreter): 40 (SharedObjectResolver._ldd_recursive_get_libs_and_interpreter): 41 (SharedObjectResolver.get_libs_and_interpreter): 42 43 * Scripts/bundle-binary: Added. 44 * Scripts/generate-bundle: 45 * Scripts/run-jsc-benchmarks: 46 * Scripts/run-jsc-stress-tests: 47 * Scripts/webkitpy/binary_bundling/__init__.py: Added. 48 * Scripts/webkitpy/binary_bundling/bundle.py: Added. 49 (BinaryBundler): 50 (BinaryBundler.__init__): 51 (BinaryBundler.destination_dir): 52 (BinaryBundler.copy_and_remove_rpath): 53 (BinaryBundler.generate_wrapper_script): 54 * Scripts/webkitpy/binary_bundling/ldd.py: Added. 55 (SharedObjectResolver): 56 (SharedObjectResolver.__init__): 57 (SharedObjectResolver._run_cmd_and_get_output): 58 (SharedObjectResolver._get_interpreter_objname): 59 (SharedObjectResolver._get_libs_and_interpreter): 60 (SharedObjectResolver._ldd_recursive_get_libs_and_interpreter): 61 (SharedObjectResolver.get_libs_and_interpreter): 62 1 63 2021-07-16 Philippe Normand <pnormand@igalia.com> 2 64 -
trunk/Tools/Scripts/generate-bundle
r270828 r279984 41 41 sys.path.insert(0, os.path.join(top_level_directory, 'Tools', 'flatpak')) 42 42 sys.path.insert(0, os.path.join(top_level_directory, 'Tools', 'jhbuild')) 43 sys.path.insert(0, os.path.join(top_level_directory, 'Tools', 'Scripts', 'webkitpy')) 43 44 import jhbuildutils 44 45 import flatpakutils 45 46 from binary_bundling.ldd import SharedObjectResolver 47 from binary_bundling.bundle import BinaryBundler 46 48 47 49 INSTALL_DEPS_SCRIPT_TEMPLATE = """\ … … 128 130 self._buildername = builder_name 129 131 self._syslibs = syslibs 130 self._ ldd = ldd132 self._shared_object_resolver = SharedObjectResolver(ldd) 131 133 self._should_strip_objects = should_strip_objects 132 134 self._compression_type = compression_type … … 152 154 return tempfile.mkdtemp() 153 155 156 154 157 def _run_cmd_and_get_output(self, command): 155 command_process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding='utf-8') 158 _log.debug("EXEC %s" % command) 159 command_process = subprocess.Popen(command, 160 stdout=subprocess.PIPE, 161 stderr=subprocess.STDOUT, 162 encoding='utf-8') 156 163 stdout, stderr = command_process.communicate() 157 164 return command_process.returncode, stdout, stderr 158 159 160 def _get_interpreter_objname(self, object):161 # Note: we use patchelf to get the object name (not the path!)162 # of the interpreter because this works regardless of the163 # architecture of the ELF file.164 retcode, stdout, stderr = self._run_cmd_and_get_output(['patchelf', '--print-interpreter', object])165 if retcode != 0:166 _log.debug("patchelf stdout:\n%s\nPatchelf stderr:\n%s" % (stdout, stderr))167 if 'cannot find section' in stdout:168 # This is fine; we only expect an interpreter in the main binary.169 return None170 raise RuntimeError('The patchelf command returned non-zero status for object %s' % object)171 interpreter_path = PurePath(stdout.strip())172 return interpreter_path.name173 174 def _get_libs_and_interpreter(self, object):175 interpreter = None176 retcode, stdout, stderr = self._run_cmd_and_get_output([self._ldd, object])177 _log.debug("ldd stdout:\n%s" % stdout)178 if retcode != 0:179 raise RuntimeError('The %s command returned non-zero status for object %s' % (self._ldd, object))180 libs = []181 for line in stdout.splitlines():182 line = line.strip()183 if '=>' in line:184 line = line.split('=>')[1].strip()185 if 'not found' in line:186 raise RuntimeError('ldd can not resolve all dependencies for object %s.' % object)187 line = line.split(' ')[0].strip()188 if os.path.isfile(line):189 libs.append(line)190 else:191 line = line.split(' ')[0].strip()192 if os.path.isfile(line):193 interpreter = line194 if interpreter is None:195 # This is the case for non-native binaries. For those, we196 # can use a cross-ldd (xldd), but then the interpreter197 # looks like any other shared object in the output of198 # ldd. Try to identify it by looking at the object name199 # from the interpreter string.200 interpreter_objname = self._get_interpreter_objname(object)201 for lib in libs:202 if PurePath(lib).name == interpreter_objname:203 interpreter = lib204 break205 # If we found an interpreter, remove it from the libs.206 libs = [lib for lib in libs if lib != interpreter]207 return libs, interpreter208 209 210 def _ldd_recursive_get_libs_and_interpreter(self, object, already_checked_libs = []):211 libs, interpreter = self._get_libs_and_interpreter(object)212 if libs:213 for lib in libs:214 if lib in already_checked_libs:215 continue216 # avoid recursion loops (libfreetype.so.6 <-> libharfbuzz.so.0)217 already_checked_libs.append(lib)218 sub_libs, sub_interpreter = self._ldd_recursive_get_libs_and_interpreter(lib, already_checked_libs)219 libs.extend(sub_libs)220 if sub_interpreter and interpreter and sub_interpreter != interpreter:221 raise RuntimeError('library %s has interpreter %s but object %s has interpreter %s' % (lib, sub_interpreter, object, interpreter))222 return list(set(libs)), interpreter223 224 165 225 166 def _get_osprettyname(self): … … 259 200 260 201 def _generate_wrapper_script(self, interpreter, binary_to_wrap): 261 if not os.path.isfile(os.path.join(self._tmpdir, 'bin', binary_to_wrap)): 262 raise RuntimeError('Can not find binary to wrap for %s' % binary_to_wrap) 202 variables = dict() 203 mydir = self._bundler.VAR_MYDIR 204 if os.path.isdir(os.path.join(self._bundler.destination_dir(), 'gio')): 205 gio_var = 'GIO_MODULE_DIR' if self._syslibs == 'bundle-all' else 'GIO_EXTRA_MODULES' 206 variables[gio_var] = "${%s}/gio" % mydir 207 208 if os.path.isdir(os.path.join(self._bundler.destination_dir(), 'gst')): 209 gst_var = 'GST_PLUGIN_SYSTEM_PATH_1_0' if self._syslibs == 'bundle-all' else 'GST_PLUGIN_PATH_1_0' 210 variables[gst_var] = "${%s}/gst" % mydir 211 variables['GST_REGISTRY_1_0'] = "${%s}/gst/gstreamer-1.0.registry" % mydir 212 if binary_to_wrap != "jsc": 213 variables['WEBKIT_EXEC_PATH'] = "${%s}/bin" % mydir 214 variables['WEBKIT_INJECTED_BUNDLE_PATH'] = "${%s}/lib" % mydir 215 if self._syslibs == 'bundle-all': 216 if binary_to_wrap != "jsc": 217 for var in ['WEB_PROCESS_CMD_PREFIX', 218 'PLUGIN_PROCESS_CMD_PREFIX', 219 'NETWORK_PROCESS_CMD_PREFIX', 220 'GPU_PROCESS_CMD_PREFIX']: 221 variables[var] = "${%s}" % self._bundler.VAR_INTERPRETER 222 else: 223 interpreter = None 224 self._bundler.generate_wrapper_script(interpreter, binary_to_wrap, variables) 263 225 self._wrapper_scripts.append(binary_to_wrap) 264 _log.info('Generate wrapper script %s' % binary_to_wrap)265 script_file = os.path.join(self._tmpdir, binary_to_wrap)266 267 with open(script_file, 'w') as script_handle:268 script_handle.write('#!/bin/sh\n')269 script_handle.write('MYDIR="$(dirname $(readlink -f $0))"\n')270 script_handle.write('export LD_LIBRARY_PATH="${MYDIR}/lib"\n')271 if os.path.isdir(os.path.join(self._tmpdir, 'gio')):272 gio_var = 'GIO_MODULE_DIR' if self._syslibs == 'bundle-all' else 'GIO_EXTRA_MODULES'273 script_handle.write('export %s="${MYDIR}/gio"\n' % gio_var)274 if os.path.isdir(os.path.join(self._tmpdir, 'gst')):275 gst_var = 'GST_PLUGIN_SYSTEM_PATH_1_0' if self._syslibs == 'bundle-all' else 'GST_PLUGIN_PATH_1_0'276 script_handle.write('export %s="${MYDIR}/gst"\n' % gst_var)277 script_handle.write('export GST_REGISTRY_1_0="${MYDIR}/gst/gstreamer-1.0.registry"\n')278 if binary_to_wrap != "jsc":279 script_handle.write('export WEBKIT_EXEC_PATH="${MYDIR}/bin"\n')280 script_handle.write('export WEBKIT_INJECTED_BUNDLE_PATH="${MYDIR}/lib"\n')281 if self._syslibs == 'bundle-all':282 script_handle.write('INTERPRETER="${MYDIR}/lib/%s"\n' % os.path.basename(interpreter))283 if binary_to_wrap != "jsc":284 script_handle.write('export WEB_PROCESS_CMD_PREFIX="${INTERPRETER}"\n')285 script_handle.write('export PLUGIN_PROCESS_CMD_PREFIX="${INTERPRETER}"\n')286 script_handle.write('export NETWORK_PROCESS_CMD_PREFIX="${INTERPRETER}"\n')287 script_handle.write('export GPU_PROCESS_CMD_PREFIX="${INTERPRETER}"\n')288 script_handle.write('exec "${INTERPRETER}" "${MYDIR}/bin/%s" "$@"\n' % binary_to_wrap)289 else:290 script_handle.write('exec "${MYDIR}/bin/%s" "$@"\n' % binary_to_wrap)291 os.chmod(script_file, 0o755)292 226 293 227 def _generate_install_deps_script(self, system_packages_needed): … … 313 247 os.chmod(installdeps_file, 0o755) 314 248 315 316 def _copy_and_remove_rpath(self, orig_file, type='bin', destination_dir=None):317 if not destination_dir:318 dir_suffix = 'lib' if type == 'interpreter' else type319 destination_dir = os.path.join(self._tmpdir, dir_suffix)320 if not os.path.isdir(destination_dir):321 os.makedirs(destination_dir)322 323 if not os.path.isfile(orig_file):324 raise ValueError('Can not find file %s' % orig_file)325 326 _log.info('Add to bundle [%s]: %s' % (type, orig_file))327 shutil.copy(orig_file, destination_dir)328 329 if shutil.which('patchelf'):330 patch_elf_command = ['patchelf', '--remove-rpath', os.path.join(destination_dir, os.path.basename(orig_file))]331 if subprocess.call(patch_elf_command) != 0:332 _log.warning('The patchelf command returned non-zero status')333 else:334 _log.warning('patchelf not found. Not modifying rpath')335 336 if self._should_strip_objects:337 if shutil.which('strip'):338 strip_command = ['strip', '--strip-unneeded', os.path.join(destination_dir, os.path.basename(orig_file))]339 if subprocess.call(strip_command) != 0:340 _log.warning('The strip command returned non-zero status')341 else:342 _log.warning('strip not found. Not stripping object')343 344 249 def _remove_tempdir(self): 345 250 if not self._tmpdir: … … 350 255 def create(self): 351 256 self._tmpdir = self._create_tempdir(self._buildpath) 257 self._bundler = BinaryBundler(self._tmpdir, self._should_strip_objects) 352 258 353 259 if os.path.isfile(self._bundle_file_path): … … 457 363 provided_by_system_package = None 458 364 if self._syslibs == 'bundle-all': 459 self._ copy_and_remove_rpath(object, type=object_type)365 self._bundler.copy_and_remove_rpath(object, type=object_type) 460 366 else: 461 367 provided_by_system_package = self._get_system_package_name(object) 462 368 if not provided_by_system_package: 463 self._ copy_and_remove_rpath(object, type=object_type)369 self._bundler.copy_and_remove_rpath(object, type=object_type) 464 370 return provided_by_system_package 465 371 … … 507 413 system_packages_needed.add(system_package) 508 414 elif object.endswith('.so'): 509 self._ copy_and_remove_rpath(object, type='lib')415 self._bundler.copy_and_remove_rpath(object, type='lib') 510 416 else: 511 self._ copy_and_remove_rpath(object, type='bin')417 self._bundler.copy_and_remove_rpath(object, type='bin') 512 418 # There is no need to examine the libraries linked with objects coming from a system package, 513 419 # because system packages already declare dependencies between them. … … 515 421 # and everything will be examined and bundled as we don't account for system packages in that case. 516 422 if not system_package: 517 libraries, interpreter = self._ ldd_recursive_get_libs_and_interpreter(object)423 libraries, interpreter = self._shared_object_resolver.get_libs_and_interpreter(object) 518 424 if interpreter is None: 519 425 raise RuntimeError("Could not determine interpreter for binary %s" % object) 520 426 if copied_interpreter is None: 521 427 if self._syslibs == 'bundle-all': 522 self._ copy_and_remove_rpath(interpreter, type='interpreter')428 self._bundler.copy_and_remove_rpath(interpreter, type='interpreter') 523 429 copied_interpreter = interpreter 524 430 elif copied_interpreter != interpreter: … … 660 566 661 567 handler = LogHandler(sys.stdout) 662 logger = logging.getLogger( __name__)568 logger = logging.getLogger() 663 569 logger.addHandler(handler) 664 570 logger.setLevel(log_level) -
trunk/Tools/Scripts/run-jsc-benchmarks
r276765 r279984 238 238 $includeBigIntBench = false 239 239 $includePrivateFieldsBench = false 240 $ldd=nil 240 241 $measureGC=false 241 242 $benchmarkPattern=nil … … 1414 1415 @@extraEnvSet[key] = true 1415 1416 end 1416 1417 1418 def copyIntoBenchPathBundle(basename, outputdir) 1419 bundle_binary = (SCRIPT_PATH.dirname + 'bundle-binary').realpath 1420 cmd = Shellwords.join([ 1421 bundle_binary, 1422 '--dest-dir', outputdir, 1423 @path 1424 ]) 1425 $stderr.puts ">> #{cmd}" if $verbosity>=2 1426 raise unless system(cmd) 1427 @path = "#{basename}/jsc" 1428 end 1429 1417 1430 def copyIntoBenchPath 1418 1431 raise unless canCopyIntoBenchPath 1419 1432 basename, filename = Benchfile.uniqueFilename("vm") 1420 1433 raise unless Dir.mkdir(filename) 1434 if not $remoteHosts.empty? and `uname` == 'Linux' 1435 copyIntoBenchPathBundle(basename, Shellwords.shellescape(filename.to_s)) 1436 return 1437 end 1438 1421 1439 @libPath.each { 1422 1440 | libPathPart | … … 1425 1443 raise unless system(cmd) 1426 1444 } 1427 @path = "#{basename}/#{@relativeBinPath}" 1445 cmd = "cp -a #{@path} #{Shellwords.shellescape(filename.to_s)}" 1446 $stderr.puts ">> #{cmd}" if $verbosity>=2 1447 raise unless system(cmd) 1448 @path = "#{basename}/jsc" 1428 1449 @libPath = [basename] 1429 1450 end … … 2390 2411 result + curLine + "\n" 2391 2412 end 2392 2413 2414 def exportJSCEnvironmentVariables 2415 buf = "" 2416 ENV.each { 2417 | name, value | 2418 next unless name.start_with?("JSC_") 2419 buf << "export #{name}=#{Shellwords.shellescape(value)}\n" 2420 } 2421 buf 2422 end 2423 2393 2424 def runAndGetResults 2394 2425 results = nil … … 3056 3087 when '--config' 3057 3088 $configPath = Pathname.new(arg) 3089 when '--ldd' 3090 $ldd = arg 3058 3091 when '--help' 3059 3092 usage … … 3612 3645 File.open("#{BENCH_DATA_PATH}/runscript", "w") { 3613 3646 | file | 3647 file.puts exportJSCEnvironmentVariables 3614 3648 file.puts "echo -e \"HOSTNAME:\\c\"" 3615 3649 file.puts "hostname" -
trunk/Tools/Scripts/run-jsc-stress-tests
r279916 r279984 1920 1920 1921 1921 if $remote and $hostOS == "linux" 1922 generate_bundle = (Pathname.new(THIS_SCRIPT_PATH).dirname + 'generate-bundle').realpath1922 bundle_binary = (Pathname.new(THIS_SCRIPT_PATH).dirname + 'bundle-binary').realpath 1923 1923 Dir.mktmpdir { 1924 1924 | tmpdir | … … 1927 1927 # (it's the only zip file there). 1928 1928 cmdline = [ 1929 generate_bundle.to_s, 1930 "--platform=gtk", 1931 "--bundle=jsc", 1932 "--syslibs=bundle-all", 1933 "--no-strip", 1934 "--compression=tar.xz", 1935 ($buildType == "release") ? "--release" : "--debug", 1936 "--destination=#{tmpdir}" 1929 bundle_binary.to_s, 1930 "--dest-dir=#{$jscPath.dirname}", 1931 "--log-level=debug", 1932 $jscPath.to_s 1937 1933 ] 1938 1934 if not $ldd.nil? … … 1940 1936 end 1941 1937 mysys(cmdline) 1942 archives = Dir.glob("#{tmpdir}/*.tar.xz")1943 if archives.size != 11944 raise "Expected exactly one entry in tmpdir, not #{archives}"1945 end1946 # Note: we overwrite 'jsc'. This obviously conflicts with1947 # !copyVM but, then gain, so does $remote.1948 mysys(["tar",1949 "-C",1950 $jscPath.dirname.to_s,1951 "-xf",1952 archives[0]])1953 1938 } 1954 1939 end
Note:
See TracChangeset
for help on using the changeset viewer.