Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(288)

Side by Side Diff: scripts/slave/goma_utils.py

Issue 2178193003: Move goma_teardown, goma_setup and determine_goma_jobs to goma_utils.py (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@master
Patch Set: separate option Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « scripts/slave/compile.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2015 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Functions specific to handle goma related info. 5 """Functions specific to handle goma related info.
6 """ 6 """
7 7
8 import base64 8 import base64
9 import datetime 9 import datetime
10 import getpass 10 import getpass
11 import glob 11 import glob
12 import gzip 12 import gzip
13 import json 13 import json
14 import multiprocessing
14 import os 15 import os
15 import re 16 import re
16 import shutil 17 import shutil
17 import socket 18 import socket
19 import subprocess
18 import sys 20 import sys
19 import tempfile 21 import tempfile
20 import time 22 import time
21 23
22 from common import chromium_utils 24 from common import chromium_utils
23 from slave import slave_utils 25 from slave import slave_utils
24 26
25 # The Google Cloud Storage bucket to store logs related to goma. 27 # The Google Cloud Storage bucket to store logs related to goma.
26 GOMA_LOG_GS_BUCKET = 'chrome-goma-log' 28 GOMA_LOG_GS_BUCKET = 'chrome-goma-log'
27 29
28 # Platform dependent location of run command. 30 # Platform dependent location of run command.
29 PLATFORM_RUN_CMD = { 31 PLATFORM_RUN_CMD = {
30 # os.name: run_cmd to use. 32 # os.name: run_cmd to use.
31 'nt': 'C:\\infra-python\\run.py', 33 'nt': 'C:\\infra-python\\run.py',
32 'posix': '/opt/infra-python/run.py', 34 'posix': '/opt/infra-python/run.py',
33 } 35 }
34 36
35 TIMESTAMP_PATTERN = re.compile('(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2})') 37 TIMESTAMP_PATTERN = re.compile('(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2})')
36 TIMESTAMP_FORMAT = '%Y/%m/%d %H:%M:%S' 38 TIMESTAMP_FORMAT = '%Y/%m/%d %H:%M:%S'
37 39
40 # Define a bunch of directory paths (same as bot_update.py)
41 CURRENT_DIR = os.path.abspath(os.getcwd())
42 BUILDER_DIR = os.path.dirname(CURRENT_DIR)
43 SLAVE_DIR = os.path.dirname(BUILDER_DIR)
44 # GOMA_CACHE_DIR used for caching long-term data.
45
46 DEFAULT_GOMA_CACHE_DIR = os.path.join(SLAVE_DIR, 'goma_cache')
47 # Path of the scripts/slave/ checkout on the slave, found by looking at the
48 # current compile.py script's path's dirname().
49 SLAVE_SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
50 # Path of the build/ checkout on the slave, found relative to the
51 # scripts/slave/ directory.
52 BUILD_DIR = os.path.dirname(os.path.dirname(SLAVE_SCRIPTS_DIR))
53
54
38 55
39 def GetShortHostname(): 56 def GetShortHostname():
40 """Get this machine's short hostname in lower case.""" 57 """Get this machine's short hostname in lower case."""
41 return socket.gethostname().split('.')[0].lower() 58 return socket.gethostname().split('.')[0].lower()
42 59
43 60
44 def GetGomaTmpDirectory(): 61 def GetGomaTmpDirectory():
45 """Get goma's temp directory.""" 62 """Get goma's temp directory."""
46 candidates = ['GOMA_TMP_DIR', 'TEST_TMPDIR', 'TMPDIR', 'TMP'] 63 candidates = ['GOMA_TMP_DIR', 'TEST_TMPDIR', 'TMPDIR', 'TMP']
47 for candidate in candidates: 64 for candidate in candidates:
(...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after
348 retcode = chromium_utils.RunCommand( 365 retcode = chromium_utils.RunCommand(
349 cmd, filter_obj=cmd_filter, 366 cmd, filter_obj=cmd_filter,
350 max_time=30) 367 max_time=30)
351 if retcode: 368 if retcode:
352 print('Execution of send_ts_mon_values failed with code %s' 369 print('Execution of send_ts_mon_values failed with code %s'
353 % retcode) 370 % retcode)
354 print '\n'.join(cmd_filter.text) 371 print '\n'.join(cmd_filter.text)
355 372
356 except Exception as ex: 373 except Exception as ex:
357 print('error while sending ts mon json_file=%s: %s' % (json_file, ex)) 374 print('error while sending ts mon json_file=%s: %s' % (json_file, ex))
375
376
377 def UpdateWindowsEnvironment(envfile_dir, env):
378 """Update windows environment in environment.{x86,x64}.
379
380 Args:
381 envfile_dir: a directory name environment.{x86,x64} are stored.
382 env: an instance of EchoDict that represents environment.
383 """
384 # envvars_to_save come from _ExtractImportantEnvironment in
385 # https://chromium.googlesource.com/external/gyp/+/\
386 # master/pylib/gyp/msvs_emuation.py
387 # You must update this when the original code is updated.
388 envvars_to_save = (
389 'goma_.*', # TODO(scottmg): This is ugly, but needed for goma.
390 'include',
391 'lib',
392 'libpath',
393 'path',
394 'pathext',
395 'systemroot',
396 'temp',
397 'tmp',
398 )
399 env_to_store = {}
400 for envvar in envvars_to_save:
401 compiled = re.compile(envvar, re.IGNORECASE)
402 for key in env.overrides:
403 if compiled.match(key):
404 if envvar == 'path':
405 env_to_store[key] = (os.path.dirname(sys.executable) +
406 os.pathsep + env[key])
407 else:
408 env_to_store[key] = env[key]
409
410 if not env_to_store:
411 return
412
413 nul = '\0'
414 for arch in ['x86', 'x64']:
415 path = os.path.join(envfile_dir, 'environment.%s' % arch)
416 print '%s will be updated with %s.' % (path, env_to_store)
417 env_in_file = {}
418 with open(path) as f:
419 for entry in f.read().split(nul):
420 if not entry:
421 continue
422 key, value = entry.split('=', 1)
423 env_in_file[key] = value
424 env_in_file.update(env_to_store)
425 with open(path, 'wb') as f:
426 f.write(nul.join(['%s=%s' % (k, v) for k, v in env_in_file.iteritems()]))
427 f.write(nul * 2)
428
429
430 def goma_setup(options, env):
431 """Sets up goma if necessary.
432
433 If using the Goma compiler, first call goma_ctl to ensure the proxy is
434 available, and returns (True, instance of cloudtail subprocess).
435 If it failed to start up compiler_proxy, modify options.compiler and
436 options.goma_dir and returns (False, None)
437
438 Args:
439 options (Option): options for goma
440 env (dict): env for compiler_proxy
441
442 Returns:
443 (bool, subprocess.Popen):
444 first element represents whether compiler_proxy starts successfully
445 second element is subprocess instance of cloudtail
446
447 """
448 if options.compiler not in ('goma', 'goma-clang'):
449 # Unset goma_dir to make sure we'll not use goma.
450 options.goma_dir = None
451 return False, None
452
453 hostname = GetShortHostname()
454 # HACK(shinyak, yyanagisawa, goma): Windows NO_NACL_GOMA (crbug.com/390764)
455 # Building NaCl untrusted code using goma brings large performance
456 # improvement but it sometimes cause build failure by race condition.
457 # Let me enable goma build on goma canary buildslaves to confirm the issue
458 # has been fixed by a workaround.
459 # vm*-m4 are trybots. build*-m1 and vm*-m1 are all goma canary bots.
460 if hostname in ['build28-m1', 'build58-m1', 'vm191-m1', 'vm480-m1',
461 'vm820-m1', 'vm821-m1', 'vm848-m1']:
462 env['NO_NACL_GOMA'] = 'false'
463
464 if options.goma_fail_fast:
465 # startup fails when initial ping failed.
466 env['GOMA_FAIL_FAST'] = 'true'
467 else:
468 # If a network error continues 30 minutes, compiler_proxy make the compile
469 # failed. When people use goma, they expect using goma is faster than
470 # compile locally. If goma cannot guarantee that, let it make compile
471 # as error.
472 env['GOMA_ALLOWED_NETWORK_ERROR_DURATION'] = '1800'
473
474 # HACK(yyanagisawa): reduce GOMA_BURST_MAX_PROCS crbug.com/592306
475 # Recently, I sometimes see buildbot slave time out, one possibility I come
476 # up with is burst mode use up resource.
477 # Let me temporary set small values to GOMA_BURST_MAX_PROCS to confirm
478 # the possibility is true or false.
479 max_subprocs = '3'
480 max_heavy_subprocs = '1'
481 number_of_processors = 0
482 try:
483 number_of_processors = multiprocessing.cpu_count()
484 except NotImplementedError:
485 print 'cpu_count() is not implemented, using default value.'
486 number_of_processors = 1
487 if number_of_processors > 3:
488 max_subprocs = str(number_of_processors - 1)
489 max_heavy_subprocs = str(number_of_processors / 2)
490 env['GOMA_BURST_MAX_SUBPROCS'] = max_subprocs
491 env['GOMA_BURST_MAX_SUBPROCS_LOW'] = max_subprocs
492 env['GOMA_BURST_MAX_SUBPROCS_HEAVY'] = max_heavy_subprocs
493
494 # Caches CRLs in GOMA_CACHE_DIR.
495 # Since downloading CRLs is usually slow, caching them may improves
496 # compiler_proxy start time.
497 if not os.path.exists(options.goma_cache_dir):
498 os.mkdir(options.goma_cache_dir, 0700)
499 env['GOMA_CACHE_DIR'] = options.goma_cache_dir
500
501 # Enable DepsCache. DepsCache caches the list of files to send goma server.
502 # This will greatly improve build speed when cache is warmed.
503 # The cache file is stored in the target output directory.
504 env['GOMA_DEPS_CACHE_DIR'] = (
505 options.goma_deps_cache_dir or options.target_output_dir)
506
507 if not env.get('GOMA_HERMETIC'):
508 env['GOMA_HERMETIC'] = options.goma_hermetic
509 if options.goma_enable_remote_link:
510 env['GOMA_ENABLE_REMOTE_LINK'] = options.goma_enable_remote_link
511 if options.goma_store_local_run_output:
512 env['GOMA_STORE_LOCAL_RUN_OUTPUT'] = options.goma_store_local_run_output
513 if options.goma_enable_compiler_info_cache:
514 # Will be stored in GOMA_CACHE_DIR.
515 env['GOMA_COMPILER_INFO_CACHE_FILE'] = 'goma-compiler-info.cache'
516
517 if options.build_data_dir:
518 env['GOMA_DUMP_STATS_FILE'] = os.path.join(options.build_data_dir,
519 'goma_stats_proto')
520
521 # goma is requested.
522 goma_key = os.path.join(options.goma_dir, 'goma.key')
523 if os.path.exists(goma_key):
524 env['GOMA_API_KEY_FILE'] = goma_key
525 if options.goma_service_account_json_file:
526 env['GOMA_SERVICE_ACCOUNT_JSON_FILE'] = \
527 options.goma_service_account_json_file
528 if chromium_utils.IsWindows():
529 env['GOMA_RPC_EXTRA_PARAMS'] = '?win'
530
531 # HACK(yyanagisawa): update environment files on |env| update.
532 # For compiling on Windows, environment in environment files are used.
533 # It means even if enviroment such as GOMA_DISABLED is updated in
534 # compile.py, the update will be ignored.
535 # We need to update environment files to reflect the update.
536 if chromium_utils.IsWindows() and NeedEnvFileUpdateOnWin(env):
537 print 'Updating environment.{x86,x64} files.'
538 UpdateWindowsEnvironment(options.target_output_dir, env)
539
540
541 goma_start_command = ['restart']
542 goma_ctl_cmd = [sys.executable,
543 os.path.join(options.goma_dir, 'goma_ctl.py')]
544 result = chromium_utils.RunCommand(goma_ctl_cmd + goma_start_command, env=env)
545 if not result:
546 # goma started sucessfully.
547 # Making cloudtail to upload the latest log.
548 # TODO(yyanagisawa): install cloudtail from CIPD.
549 cloudtail_path = '/opt/infra-tools/cloudtail'
550 if chromium_utils.IsWindows():
551 cloudtail_path = 'C:\\infra-tools\\cloudtail'
552 try:
553 cloudtail_proc = subprocess.Popen(
554 [cloudtail_path, 'tail', '--log-id', 'goma_compiler_proxy', '--path',
555 GetLatestGomaCompilerProxyInfo()])
556 except Exception as e:
557 print 'failed to invoke cloudtail: %s' % e
558 return True, None
559 return True, cloudtail_proc
560
561 if options.goma_jsonstatus:
562 chromium_utils.RunCommand(
563 goma_ctl_cmd + ['jsonstatus', options.goma_jsonstatus], env=env)
564 SendGomaTsMon(options.goma_jsonstatus, -1)
565
566 # Try to stop compiler_proxy so that it flushes logs and stores
567 # GomaStats.
568 if options.build_data_dir:
569 env['GOMACTL_CRASH_REPORT_ID_FILE'] = os.path.join(options.build_data_dir,
570 'crash_report_id_file')
571 chromium_utils.RunCommand(goma_ctl_cmd + ['stop'], env=env)
572
573 override_gsutil = None
574 if options.gsutil_py_path:
575 override_gsutil = [sys.executable, options.gsutil_py_path]
576
577 # Upload compiler_proxy.INFO to investigate the reason of compiler_proxy
578 # start-up failure.
579 UploadGomaCompilerProxyInfo(override_gsutil=override_gsutil)
580 # Upload GomaStats to make it monitored.
581 if env.get('GOMA_DUMP_STATS_FILE'):
582 SendGomaStats(env['GOMA_DUMP_STATS_FILE'],
583 env.get('GOMACTL_CRASH_REPORT_ID_FILE'),
584 options.build_data_dir)
585
586 if options.goma_disable_local_fallback:
587 print 'error: failed to start goma; fallback has been disabled'
588 raise Exception('failed to start goma')
589
590 print 'warning: failed to start goma. falling back to non-goma'
591 # Drop goma from options.compiler
592 options.compiler = options.compiler.replace('goma-', '')
593 if options.compiler == 'goma':
594 options.compiler = None
595 # Reset options.goma_dir.
596 options.goma_dir = None
597 env['GOMA_DISABLED'] = '1'
598 return False, None
599
600
601 def goma_teardown(options, env, exit_status, cloudtail_proc):
602 """Tears down goma if necessary. """
603 if (options.compiler in ('goma', 'goma-clang') and
604 options.goma_dir):
605 override_gsutil = None
606 if options.gsutil_py_path:
607 override_gsutil = [sys.executable, options.gsutil_py_path]
608
609 # If goma compiler_proxy crashes during the build, there could be crash
610 # dump.
611 if options.build_data_dir:
612 env['GOMACTL_CRASH_REPORT_ID_FILE'] = os.path.join(options.build_data_dir,
613 'crash_report_id_file')
614 goma_ctl_cmd = [sys.executable,
615 os.path.join(options.goma_dir, 'goma_ctl.py')]
616 if options.goma_jsonstatus:
617 chromium_utils.RunCommand(
618 goma_ctl_cmd + ['jsonstatus', options.goma_jsonstatus], env=env)
619 SendGomaTsMon(options.goma_jsonstatus, exit_status)
620 # Always stop the proxy for now to allow in-place update.
621 chromium_utils.RunCommand(goma_ctl_cmd + ['stop'], env=env)
622 UploadGomaCompilerProxyInfo(override_gsutil=override_gsutil)
623 if env.get('GOMA_DUMP_STATS_FILE'):
624 SendGomaStats(env['GOMA_DUMP_STATS_FILE'],
625 env.get('GOMACTL_CRASH_REPORT_ID_FILE'),
626 options.build_data_dir)
627 if cloudtail_proc:
628 cloudtail_proc.terminate()
629 cloudtail_proc.wait()
630
631
632 def determine_goma_jobs():
633 # We would like to speed up build on Windows a bit, since it is slowest.
634 number_of_processors = 0
635 try:
636 number_of_processors = multiprocessing.cpu_count()
637 except NotImplementedError:
638 print 'cpu_count() is not implemented, using default value 50.'
639 return 50
640
641 assert number_of_processors > 0
642
643 # When goma is used, 10 * number_of_processors is basically good in
644 # various situations according to our measurement. Build speed won't
645 # be improved if -j is larger than that.
646 #
647 # Since Mac had process number limitation before, we had to set
648 # the upper limit to 50. Now that the process number limitation is 2000,
649 # so we would be able to use 10 * number_of_processors.
650 # For the safety, we'd like to set the upper limit to 200.
651 #
652 # Note that currently most try-bot build slaves have 8 processors.
653 if chromium_utils.IsMac() or chromium_utils.IsWindows():
654 return min(10 * number_of_processors, 200)
655
656 # For Linux, we also would like to use 10 * cpu. However, not sure
657 # backend resource is enough, so let me set Linux and Linux x64 builder
658 # only for now.
659 hostname = GetShortHostname()
660 if hostname in (
661 ['build14-m1', 'build48-m1'] +
662 # Also increasing cpus for v8/blink trybots.
663 ['build%d-m4' % x for x in xrange(45, 48)] +
664 # Also increasing cpus for LTO buildbots.
665 ['slave%d-c1' % x for x in [20, 33] + range(78, 108)]):
666 return min(10 * number_of_processors, 200)
667
668 return 50
669
670
671 def NeedEnvFileUpdateOnWin(env):
672 """Returns true if environment file need to be updated."""
673 # Following GOMA_* are applied to compiler_proxy not gomacc,
674 # you do not need to update environment files.
675 ignore_envs = (
676 'GOMA_API_KEY_FILE',
677 'GOMA_DEPS_CACHE_DIR',
678 'GOMA_HERMETIC',
679 'GOMA_RPC_EXTRA_PARAMS',
680 'GOMA_ALLOWED_NETWORK_ERROR_DURATION'
681 )
682 for key in env.overrides:
683 if key not in ignore_envs:
684 return True
685 return False
686
687
688 def construct_optionparser(option_parser):
689 option_parser.add_option('--goma-cache-dir',
690 default=DEFAULT_GOMA_CACHE_DIR,
691 help='specify goma cache directory')
692 option_parser.add_option('--goma-deps-cache-dir',
693 help='specify goma deps cache directory')
694 option_parser.add_option('--goma-hermetic', default='error',
695 help='Set goma hermetic mode')
696 option_parser.add_option('--goma-enable-remote-link', default=None,
697 help='Enable goma remote link.')
698 option_parser.add_option('--goma-enable-compiler-info-cache',
699 action='store_true',
700 help='Enable goma CompilerInfo cache')
701 option_parser.add_option('--goma-store-local-run-output', default=None,
702 help='Store local run output to goma servers.')
703 option_parser.add_option('--goma-fail-fast', action='store_true')
704 option_parser.add_option('--goma-disable-local-fallback', action='store_true')
705 option_parser.add_option('--goma-jsonstatus',
706 help='Specify a file to dump goma_ctl jsonstatus.')
707 option_parser.add_option('--goma-service-account-json-file',
708 help='Specify a file containing goma service account'
709 ' credentials')
710 option_parser.add_option('--build-data-dir', action='store',
711 help='specify a build data directory.')
712
713 if not option_parser.has_option('--goma-dir'):
714 option_parser.add_option('--goma-dir',
715 default=os.path.join(BUILD_DIR, 'goma'),
716 help='specify goma directory')
717
718 if not option_parser.has_option('--goma-jobs'):
719 option_parser.add_option('--goma-jobs', default=None,
720 help='The number of jobs for ninja -j.')
721
722 if not option_parser.has_option('--gsutil-py-path'):
723 option_parser.add_option('--gsutil-py-path',
724 help='Specify path to gsutil.py script.')
725
726 if not option_parser.has_option('--compiler'):
727 option_parser.add_option('--compiler', default=None,
728 help='specify alternative compiler (e.g. clang)')
729
730 return option_parser
OLDNEW
« no previous file with comments | « scripts/slave/compile.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698