aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Bosch <tbosch@google.com>2019-10-17 15:19:14 -0700
committerandroid-build-merger <android-build-merger@google.com>2019-10-17 15:19:14 -0700
commit35dac19ebcb62e1589fbc0f4cbbbc70e330e509c (patch)
treea3dce488f787cb6565a2ef22774142fc68f71d08
parent61b8a2052c5f19257b3effcc99b84115b8e85b0b (diff)
parent971c0250681954240bf7a4d512aedf787d5b9171 (diff)
downloadtoolchain-utils-35dac19ebcb62e1589fbc0f4cbbbc70e330e509c.tar.gz
Merging 11 commit(s) from Chromium's toolchain-utils am: 10b5935280 am: 1ba798e5ca
am: 971c025068 Change-Id: I36433166e23570ffa9a0c8dd1d83fc3c0679980a
-rw-r--r--afdo_metadata/chrome_afdo.json8
-rw-r--r--afdo_metadata/kernel_afdo.json2
-rwxr-xr-xafdo_redaction/remove_indirect_calls.py81
-rwxr-xr-xafdo_redaction/remove_indirect_calls_test.py67
-rwxr-xr-xafdo_tools/bisection/afdo_prof_analysis.py128
-rwxr-xr-xafdo_tools/bisection/afdo_prof_analysis_e2e_test.py68
-rw-r--r--compiler_wrapper/android_config_test.go13
-rw-r--r--compiler_wrapper/bisect_flag.go8
-rw-r--r--compiler_wrapper/bisect_flag_test.go7
-rw-r--r--compiler_wrapper/clang_flags.go9
-rw-r--r--compiler_wrapper/command.go3
-rw-r--r--compiler_wrapper/compile_with_fallback_test.go2
-rw-r--r--compiler_wrapper/compiler_wrapper.go30
-rw-r--r--compiler_wrapper/cros_hardened_config_test.go15
-rw-r--r--compiler_wrapper/goldenutil_test.go5
-rw-r--r--compiler_wrapper/sanitizer_flags.go26
-rw-r--r--compiler_wrapper/sanitizer_flags_test.go31
-rw-r--r--compiler_wrapper/testdata/android_golden/bisect.json11
-rw-r--r--compiler_wrapper/testdata/android_golden/clang_path.json48
-rw-r--r--compiler_wrapper/testdata/cros_clang_host_golden/bisect.json9
-rw-r--r--compiler_wrapper/testdata/cros_clang_host_golden/clang_sanitizer_args.json99
-rw-r--r--compiler_wrapper/testdata/cros_hardened_golden/bisect.json9
-rw-r--r--compiler_wrapper/testdata/cros_hardened_golden/clang_sanitizer_args.json145
-rw-r--r--compiler_wrapper/testdata/cros_hardened_golden/gcc_sanitizer_args.json119
-rw-r--r--compiler_wrapper/testdata/cros_hardened_llvmnext_golden/bisect.json9
-rw-r--r--compiler_wrapper/testdata/cros_hardened_noccache_golden/bisect.json9
-rw-r--r--compiler_wrapper/testdata/cros_nonhardened_golden/bisect.json9
-rw-r--r--compiler_wrapper/testdata/cros_nonhardened_golden/clang_sanitizer_args.json123
-rw-r--r--compiler_wrapper/testdata/cros_nonhardened_golden/gcc_sanitizer_args.json107
-rw-r--r--cros_utils/locks.py14
-rwxr-xr-x[-rw-r--r--]cros_utils/no_pseudo_terminal_test.py20
-rw-r--r--crosperf/benchmark_run.py21
-rwxr-xr-xcrosperf/benchmark_run_unittest.py85
-rw-r--r--crosperf/experiment.py3
-rw-r--r--crosperf/experiment_factory.py14
-rw-r--r--crosperf/experiment_runner.py19
-rwxr-xr-xcrosperf/experiment_runner_unittest.py24
-rw-r--r--crosperf/machine_manager.py20
-rw-r--r--crosperf/results_cache.py79
-rwxr-xr-xcrosperf/results_cache_unittest.py79
-rw-r--r--crosperf/results_report.py13
-rwxr-xr-xcrosperf/results_report_unittest.py33
-rw-r--r--crosperf/schedv2.py6
-rw-r--r--crosperf/settings_factory.py10
-rwxr-xr-x[-rw-r--r--]file_lock_machine_test.py (renamed from lock_machine_test.py)36
-rwxr-xr-xlock_machine.py (renamed from afe_lock_machine.py)33
46 files changed, 1344 insertions, 365 deletions
diff --git a/afdo_metadata/chrome_afdo.json b/afdo_metadata/chrome_afdo.json
index f52f61d2..e46e05f7 100644
--- a/afdo_metadata/chrome_afdo.json
+++ b/afdo_metadata/chrome_afdo.json
@@ -1,14 +1,14 @@
{
"silvermont": {
- "name": "R79-3904.41-1570446057.afdo"
+ "name": "R79-3928.0-1571049528.afdo"
},
"benchmark": {
- "name": "chromeos-chrome-amd64-79.0.3931.2_rc-r1.afdo"
+ "name": "chromeos-chrome-amd64-79.0.3940.0_rc-r1.afdo"
},
"airmont": {
- "name": "R79-3904.35-1570450409.afdo"
+ "name": "R79-3931.2-1571054549.afdo"
},
"broadwell": {
- "name": "R79-3904.35-1570441307.afdo"
+ "name": "R79-3904.41-1571046112.afdo"
}
} \ No newline at end of file
diff --git a/afdo_metadata/kernel_afdo.json b/afdo_metadata/kernel_afdo.json
index dbf52fbb..1f23b170 100644
--- a/afdo_metadata/kernel_afdo.json
+++ b/afdo_metadata/kernel_afdo.json
@@ -3,7 +3,7 @@
"name": "R79-12499.14-1569836097"
},
"chromeos-kernel-4_4": {
- "name": "R79-12564.0-1570440907"
+ "name": "R79-12576.0-1571045606"
},
"chromeos-kernel-3_18": {
"name": "R79-12564.0-1570440773"
diff --git a/afdo_redaction/remove_indirect_calls.py b/afdo_redaction/remove_indirect_calls.py
new file mode 100755
index 00000000..b879b2f0
--- /dev/null
+++ b/afdo_redaction/remove_indirect_calls.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Script to remove all indirect call targets from textual AFDO profiles.
+
+Indirect call samples can cause code to appear 'live' when it otherwise
+wouldn't be. This resurrection can happen either by the way of profile-based
+speculative devirtualization, or because of imprecision in LLVM's liveness
+calculations when performing LTO.
+
+This generally isn't a problem when an AFDO profile is applied to the binary it
+was collected on. However, because we e.g., build NaCl from the same set of
+objects as Chrome, this can become problematic, and lead to NaCl doubling in
+size (or worse). See crbug.com/1005023 and crbug.com/916130.
+"""
+
+from __future__ import division, print_function
+
+import argparse
+import re
+import sys
+
+
+def _remove_indirect_call_targets(lines):
+ # Lines with indirect call targets look like:
+ # 1.1: 1234 foo:111 bar:122
+ #
+ # Where 1.1 is the line info/discriminator, 1234 is the total number of
+ # samples seen for that line/discriminator, foo:111 is "111 of the calls here
+ # went to foo," and bar:122 is "122 of the calls here went to bar."
+ call_target_re = re.compile(
+ r"""
+ ^\s+ # Top-level lines are function records.
+ \d+(?:\.\d+)?: # Line info/discriminator
+ \s+
+ \d+ # Total sample count
+ \s+
+ ((?:[^\s:]+:\d+\s*)+) # Indirect call target(s)
+ $
+ """, re.VERBOSE)
+ for line in lines:
+ line = line.rstrip()
+
+ match = call_target_re.match(line)
+ if not match:
+ yield line + '\n'
+ continue
+
+ group_start, group_end = match.span(1)
+ assert group_end == len(line)
+ yield line[:group_start].rstrip() + '\n'
+
+
+def run(input_stream, output_stream):
+ for line in _remove_indirect_call_targets(input_stream):
+ output_stream.write(line)
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument(
+ '--input',
+ default='/dev/stdin',
+ help='File to read from. Defaults to stdin.')
+ parser.add_argument(
+ '--output',
+ default='/dev/stdout',
+ help='File to write to. Defaults to stdout.')
+ args = parser.parse_args()
+
+ with open(args.input) as stdin:
+ with open(args.output, 'w') as stdout:
+ run(stdin, stdout)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/afdo_redaction/remove_indirect_calls_test.py b/afdo_redaction/remove_indirect_calls_test.py
new file mode 100755
index 00000000..1499af25
--- /dev/null
+++ b/afdo_redaction/remove_indirect_calls_test.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Tests for remove_indirect_calls"""
+
+from __future__ import print_function
+
+import io
+import unittest
+
+import remove_indirect_calls
+
+
+def _run_test(input_lines):
+ input_buf = io.BytesIO('\n'.join(input_lines))
+ output_buf = io.BytesIO()
+ remove_indirect_calls.run(input_buf, output_buf)
+ return output_buf.getvalue().splitlines()
+
+
+class Test(unittest.TestCase):
+ """Tests"""
+
+ def test_empty_profile(self):
+ self.assertEqual(_run_test([]), [])
+
+ def test_removal_on_real_world_code(self):
+ # These are copied from an actual textual AFDO profile, but the names made
+ # lints unhappy due to their length, so I had to be creative.
+ profile_lines = """_ZLongSymbolName:52862:1766
+ 14: 2483
+ 8.1: _SomeInlinedSym:45413
+ 11: _AndAnother:35481
+ 2: 2483
+ 2.1: _YetAnother:25549
+ 3: 2483
+ 3.1: 351
+ 3.3: 2526 IndirectTarg1:675 Targ2:397 Targ3:77
+ 13.2: Whee:9932
+ 1.1: Whoo:9932
+ 0: BleepBloop:9932
+ 0: 2483
+ """.strip().splitlines()
+
+ expected_lines = """_ZLongSymbolName:52862:1766
+ 14: 2483
+ 8.1: _SomeInlinedSym:45413
+ 11: _AndAnother:35481
+ 2: 2483
+ 2.1: _YetAnother:25549
+ 3: 2483
+ 3.1: 351
+ 3.3: 2526
+ 13.2: Whee:9932
+ 1.1: Whoo:9932
+ 0: BleepBloop:9932
+ 0: 2483
+ """.strip().splitlines()
+
+ self.assertEqual(_run_test(profile_lines), expected_lines)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/afdo_tools/bisection/afdo_prof_analysis.py b/afdo_tools/bisection/afdo_prof_analysis.py
index 27ed7110..8455d2b3 100755
--- a/afdo_tools/bisection/afdo_prof_analysis.py
+++ b/afdo_tools/bisection/afdo_prof_analysis.py
@@ -13,14 +13,9 @@ to try and determine what exactly is bad about the bad profile. It does this
with three main techniques: bisecting search, range search, and rough diff-ing.
"""
-from __future__ import print_function
-
-from absl import app
-from absl import flags
-from datetime import date
-from enum import IntEnum
-from tempfile import mkstemp
+from __future__ import division, print_function
+import argparse
import json
# Pylint recommends we use "from chromite.lib import cros_logging as logging".
# Chromite specific policy message, we want to keep using the standard logging
@@ -29,29 +24,10 @@ import logging
import os
import random
import subprocess
-import sys
import time
-
-flags.DEFINE_string('good_prof', None, 'Text-based "Good" profile for'
- 'analysis')
-flags.DEFINE_string('bad_prof', None, 'Text-based "Bad" profile for' 'analysis')
-flags.DEFINE_string(
- 'external_decider', None, 'External script that, given an'
- 'AFDO profile, returns GOOD/BAD/SKIP')
-flags.DEFINE_string('analysis_output_file', None,
- 'File to output JSON results to')
-flags.DEFINE_string(
- 'state_file', '%s/afdo_analysis_state.json' % os.getcwd(),
- 'File path containing state to load from initially, and '
- 'will be overwritten with new state on each iteration')
-flags.DEFINE_boolean(
- 'no_resume', False, 'If enabled, no initial state will be '
- 'loaded and the program will run from the beginning')
-flags.DEFINE_boolean(
- 'remove_state_on_completion', False, 'If enabled, state '
- 'file will be removed once profile analysis is completed')
-flags.DEFINE_float('seed', None, 'Float specifying seed for randomness')
-FLAGS = flags.FLAGS
+from datetime import date
+from enum import IntEnum
+from tempfile import mkstemp
class StatusEnum(IntEnum):
@@ -113,11 +89,12 @@ def prof_to_tmp(prof):
class DeciderState(object):
"""Class for the external decider."""
- def __init__(self, state_file):
+ def __init__(self, state_file, external_decider, seed):
self.accumulated_results = [] # over this run of the script
+ self.external_decider = external_decider
self.saved_results = [] # imported from a previous run of this script
self.state_file = state_file
- self.seed = FLAGS.seed or time.time()
+ self.seed = seed if seed is not None else time.time()
def load_state(self):
if not os.path.exists(self.state_file):
@@ -142,8 +119,7 @@ class DeciderState(object):
def save_state(self):
state = {'seed': self.seed, 'accumulated_results': self.accumulated_results}
- fd, tmp_file = mkstemp()
- os.close(fd)
+ tmp_file = self.state_file + '.new'
with open(tmp_file, 'w') as f:
json.dump(state, f, indent=2)
os.rename(tmp_file, self.state_file)
@@ -160,7 +136,7 @@ class DeciderState(object):
filename = prof_to_tmp(prof)
try:
- return_code = subprocess.call([FLAGS.external_decider, filename])
+ return_code = subprocess.call([self.external_decider, filename])
finally:
os.remove(filename)
@@ -174,7 +150,7 @@ class DeciderState(object):
if save_run:
self.accumulated_results.append(status.value)
logging.info('Run %d of external script %s returned %s',
- len(self.accumulated_results), FLAGS.external_decider,
+ len(self.accumulated_results), self.external_decider,
status.name)
self.save_state()
return status
@@ -209,7 +185,7 @@ def bisect_profiles(decider, good, bad, common_funcs, lo, hi):
results['individuals'].append(common_funcs[lo])
return results
- mid = (lo + hi) / 2
+ mid = (lo + hi) // 2
lo_mid_prof = good.copy() # covers bad from lo:mid
mid_hi_prof = good.copy() # covers bad from mid:hi
for func in common_funcs[lo:mid]:
@@ -248,9 +224,9 @@ def bisect_profiles_wrapper(decider, good, bad, perform_check=True):
# Note that while decider is a random mock, these assertions may fail.
if perform_check:
if decider.run(good, save_run=False) != StatusEnum.GOOD_STATUS:
- raise ValueError("Supplied good profile is not actually GOOD")
+ raise ValueError('Supplied good profile is not actually GOOD')
if decider.run(bad, save_run=False) != StatusEnum.BAD_STATUS:
- raise ValueError("Supplied bad profile is not actually BAD")
+ raise ValueError('Supplied bad profile is not actually BAD')
common_funcs = sorted(func for func in good if func in bad)
if not common_funcs:
@@ -282,7 +258,7 @@ def range_search(decider, good, bad, common_funcs, lo, hi):
of functions in a reasonable timeframe.
"""
- average = lambda x, y: int(round((x + y) / 2.0))
+ average = lambda x, y: int(round((x + y) // 2.0))
def find_upper_border(good_copy, funcs, lo, hi, last_bad_val=None):
"""Finds the upper border of problematic range."""
@@ -329,7 +305,7 @@ def range_search(decider, good, bad, common_funcs, lo, hi):
random.shuffle(lo_mid_funcs)
random.shuffle(mid_hi_funcs)
else: # consider lo-mid and mid-hi separately bc must cross border
- mid = (lo + hi) / 2
+ mid = (lo + hi) // 2
lo_mid_funcs = common_funcs[lo:mid]
mid_hi_funcs = common_funcs[mid:hi]
@@ -380,20 +356,58 @@ def check_bad_not_good(decider, good, bad):
return decider.run(good_copy) == StatusEnum.BAD_STATUS
-def main(_):
-
- if not FLAGS.no_resume and FLAGS.seed: # conflicting seeds
+def parse_args():
+ parser = argparse.ArgumentParser(
+ description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument(
+ '--good_prof',
+ required=True,
+ help='Text-based "Good" profile for analysis')
+ parser.add_argument(
+ '--bad_prof', required=True, help='Text-based "Bad" profile for analysis')
+ parser.add_argument(
+ '--external_decider',
+ required=True,
+ help='External script that, given an AFDO profile, returns '
+ 'GOOD/BAD/SKIP')
+ parser.add_argument(
+ '--analysis_output_file',
+ required=True,
+ help='File to output JSON results to')
+ parser.add_argument(
+ '--state_file',
+ default='%s/afdo_analysis_state.json' % os.getcwd(),
+ help='File path containing state to load from initially, and will be '
+ 'overwritten with new state on each iteration')
+ parser.add_argument(
+ '--no_resume',
+ action='store_true',
+ help='If enabled, no initial state will be loaded and the program will '
+ 'run from the beginning')
+ parser.add_argument(
+ '--remove_state_on_completion',
+ action='store_true',
+ help='If enabled, state file will be removed once profile analysis is '
+ 'completed')
+ parser.add_argument(
+ '--seed', type=float, help='Float specifying seed for randomness')
+ return parser.parse_args()
+
+
+def main(flags):
+ if not flags.no_resume and flags.seed: # conflicting seeds
raise RuntimeError('Ambiguous seed value; do not resume from existing '
'state and also specify seed by command line flag')
- decider = DeciderState(FLAGS.state_file)
- if not FLAGS.no_resume:
+ decider = DeciderState(
+ flags.state_file, flags.external_decider, seed=flags.seed)
+ if not flags.no_resume:
decider.load_state()
random.seed(decider.seed)
- with open(FLAGS.good_prof) as good_f:
+ with open(flags.good_prof) as good_f:
good_items = text_to_json(good_f)
- with open(FLAGS.bad_prof) as bad_f:
+ with open(flags.bad_prof) as bad_f:
bad_items = text_to_json(bad_f)
bisect_results = bisect_profiles_wrapper(decider, good_items, bad_items)
@@ -406,25 +420,19 @@ def main(_):
'good_only_functions': gnb_result,
'bad_only_functions': bng_result
}
- with open(FLAGS.analysis_output_file, 'wb') as f:
+ with open(flags.analysis_output_file, 'wb') as f:
json.dump(results, f, indent=2)
- if FLAGS.remove_state_on_completion:
- os.remove(FLAGS.state_file)
+ if flags.remove_state_on_completion:
+ os.remove(flags.state_file)
logging.info('Removed state file %s following completion of script...',
- FLAGS.state_file)
+ flags.state_file)
else:
- completed_state_file = '%s.completed.%s' % (FLAGS.state_file,
+ completed_state_file = '%s.completed.%s' % (flags.state_file,
str(date.today()))
- os.rename(FLAGS.state_file, completed_state_file)
+ os.rename(flags.state_file, completed_state_file)
logging.info('Stored completed state file as %s...', completed_state_file)
return results
if __name__ == '__main__':
- flags.mark_flag_as_required('good_prof')
- flags.mark_flag_as_required('bad_prof')
- flags.mark_flag_as_required('external_decider')
- flags.mark_flag_as_required('analysis_output_file')
- app.run(main)
-else:
- FLAGS(sys.argv)
+ main(parse_args())
diff --git a/afdo_tools/bisection/afdo_prof_analysis_e2e_test.py b/afdo_tools/bisection/afdo_prof_analysis_e2e_test.py
index 24f9e4d0..85c1c175 100755
--- a/afdo_tools/bisection/afdo_prof_analysis_e2e_test.py
+++ b/afdo_tools/bisection/afdo_prof_analysis_e2e_test.py
@@ -6,18 +6,30 @@
"""End-to-end test for afdo_prof_analysis."""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-from datetime import date
-
-import afdo_prof_analysis as analysis
+from __future__ import absolute_import, division, print_function
import json
+import os
import shutil
import tempfile
-import os
import unittest
+from datetime import date
+
+import afdo_prof_analysis as analysis
+
+
+class ObjectWithFields(object):
+ """Turns kwargs given to the constructor into fields on an object.
+
+ Example usage:
+ x = ObjectWithFields(a=1, b=2)
+ assert x.a == 1
+ assert x.b == 2
+ """
+
+ def __init__(self, **kwargs):
+ for key, val in kwargs.items():
+ setattr(self, key, val)
class AfdoProfAnalysisE2ETest(unittest.TestCase):
@@ -233,27 +245,33 @@ class AfdoProfAnalysisE2ETest(unittest.TestCase):
with open(bad_prof_file, 'w') as f:
f.write(bad_prof_text)
- analysis.FLAGS.good_prof = good_prof_file
- analysis.FLAGS.bad_prof = bad_prof_file
- if state_file:
- actual_state_file = analysis.FLAGS.state_file
-
- def cleanup():
- analysis.FLAGS.state_file = actual_state_file
-
- self.addCleanup(cleanup)
-
- analysis.FLAGS.state_file = state_file
-
- analysis.FLAGS.seed = seed
- analysis.FLAGS.no_resume = no_resume
- analysis.FLAGS.analysis_output_file = out_file or '/dev/null'
-
dir_path = os.path.dirname(os.path.realpath(__file__)) # dir of this file
external_script = '%s/%s' % (dir_path, extern_decider or 'e2e_external.sh')
- analysis.FLAGS.external_decider = external_script
- actual = analysis.main(None)
+ # FIXME: This test ideally shouldn't be writing to $PWD
+ if state_file is None:
+ state_file = '%s/afdo_analysis_state.json' % os.getcwd()
+
+ def rm_state():
+ try:
+ os.unlink(state_file)
+ except OSError:
+ # Probably because the file DNE. That's fine.
+ pass
+
+ self.addCleanup(rm_state)
+
+ actual = analysis.main(
+ ObjectWithFields(
+ good_prof=good_prof_file,
+ bad_prof=bad_prof_file,
+ external_decider=external_script,
+ analysis_output_file=out_file or '/dev/null',
+ state_file=state_file,
+ no_resume=no_resume,
+ remove_state_on_completion=False,
+ seed=seed,
+ ))
actual_seed = actual.pop('seed') # nothing to check
self.assertEqual(actual, expected)
return actual_seed
diff --git a/compiler_wrapper/android_config_test.go b/compiler_wrapper/android_config_test.go
index 4e2f1f5c..104be6df 100644
--- a/compiler_wrapper/android_config_test.go
+++ b/compiler_wrapper/android_config_test.go
@@ -35,6 +35,7 @@ func createAndroidClangPathGoldenInputs(ctx *testContext) goldenFile {
gomaPath := path.Join(ctx.tempDir, "gomacc")
ctx.writeFile(gomaPath, "")
defaultPath := filepath.Join(ctx.tempDir, "clang")
+ clangTidyPath := filepath.Join(ctx.tempDir, "clang-tidy")
deepPath := "a/b/c/d/e/f/g/clang"
linkedDeepPath := "symlinked/clang_other"
@@ -52,11 +53,21 @@ func createAndroidClangPathGoldenInputs(ctx *testContext) goldenFile {
Cmds: errorResults,
},
{
+ Env: []string{"WITH_TIDY=1"},
+ WrapperCmd: newGoldenCmd(defaultPath, mainCc),
+ Cmds: okResults,
+ },
+ {
WrapperCmd: newGoldenCmd(filepath.Join(ctx.tempDir, "clang++"), mainCc),
Cmds: okResults,
},
{
- WrapperCmd: newGoldenCmd(filepath.Join(ctx.tempDir, "clang-tidy"), mainCc),
+ WrapperCmd: newGoldenCmd(clangTidyPath, mainCc),
+ Cmds: okResults,
+ },
+ {
+ Env: []string{"WITH_TIDY=1"},
+ WrapperCmd: newGoldenCmd(clangTidyPath, mainCc),
Cmds: okResults,
},
{
diff --git a/compiler_wrapper/bisect_flag.go b/compiler_wrapper/bisect_flag.go
index 62bcbd31..6271e23f 100644
--- a/compiler_wrapper/bisect_flag.go
+++ b/compiler_wrapper/bisect_flag.go
@@ -5,7 +5,7 @@
package main
import (
- "os"
+ "errors"
"path/filepath"
)
@@ -41,9 +41,9 @@ func calcBisectCommand(env env, cfg *config, bisectStage string, compilerCmd *co
bisectDir, _ := env.getenv("BISECT_DIR")
if bisectDir == "" {
if cfg.isAndroidWrapper {
- homeDir, err := os.UserHomeDir()
- if err != nil {
- return nil, err
+ homeDir, ok := env.getenv("HOME")
+ if !ok {
+ return nil, errors.New("$HOME is not set")
}
bisectDir = filepath.Join(homeDir, "ANDROID_BISECT")
} else {
diff --git a/compiler_wrapper/bisect_flag_test.go b/compiler_wrapper/bisect_flag_test.go
index 43c3095b..0bb6a820 100644
--- a/compiler_wrapper/bisect_flag_test.go
+++ b/compiler_wrapper/bisect_flag_test.go
@@ -8,7 +8,6 @@ import (
"errors"
"fmt"
"io"
- "os"
"path/filepath"
"strings"
"testing"
@@ -83,12 +82,12 @@ func TestDefaultBisectDirAndroid(t *testing.T) {
withBisectTestContext(t, func(ctx *testContext) {
ctx.env = []string{
"BISECT_STAGE=someBisectStage",
+ "HOME=/somehome",
}
ctx.cfg.isAndroidWrapper = true
- cmd := mustCallBisectDriver(ctx, callCompiler(ctx, ctx.cfg, ctx.newCommand(gccX86_64, mainCc)))
- userHome, _ := os.UserHomeDir()
+ cmd := mustCallBisectDriver(ctx, callCompiler(ctx, ctx.cfg, ctx.newCommand(clangAndroid, mainCc)))
if err := verifyArgOrder(cmd,
- "someBisectStage", filepath.Join(userHome, "ANDROID_BISECT")); err != nil {
+ "someBisectStage", filepath.Join("/somehome", "ANDROID_BISECT")); err != nil {
t.Error(err)
}
})
diff --git a/compiler_wrapper/clang_flags.go b/compiler_wrapper/clang_flags.go
index ee041a2e..8b76e965 100644
--- a/compiler_wrapper/clang_flags.go
+++ b/compiler_wrapper/clang_flags.go
@@ -12,15 +12,6 @@ import (
)
func processClangFlags(builder *commandBuilder) error {
- if builder.cfg.isAndroidWrapper {
- // FIXME: This combination of using the directory of the symlink but the
- // basename of the link target is strange but is the logic that old android
- // wrapper uses. Change this to use directory and basename either from the
- // absWrapperPath or from the builder.path, but don't mix anymore.
- builder.path = filepath.Join(filepath.Dir(builder.path), filepath.Base(builder.absWrapperPath)+".real")
- return nil
- }
-
env := builder.env
clangDir, _ := env.getenv("CLANG")
diff --git a/compiler_wrapper/command.go b/compiler_wrapper/command.go
index 26f85568..69578597 100644
--- a/compiler_wrapper/command.go
+++ b/compiler_wrapper/command.go
@@ -129,6 +129,8 @@ func newCommandBuilder(env env, cfg *config, cmd *command) (*commandBuilder, err
var compilerType compilerType
switch {
+ case strings.HasPrefix(target.compiler, "clang-tidy"):
+ compilerType = clangTidyType
case strings.HasPrefix(target.compiler, "clang"):
compilerType = clangType
default:
@@ -172,6 +174,7 @@ type compilerType int32
const (
gccType compilerType = iota
clangType
+ clangTidyType
)
type builderTarget struct {
diff --git a/compiler_wrapper/compile_with_fallback_test.go b/compiler_wrapper/compile_with_fallback_test.go
index 32d1915b..4ea847f6 100644
--- a/compiler_wrapper/compile_with_fallback_test.go
+++ b/compiler_wrapper/compile_with_fallback_test.go
@@ -169,7 +169,7 @@ func TestCompileWithFallbackExtraArgs(t *testing.T) {
}{
{"./clang", true},
{"./clang++", true},
- {"./some_clang", false},
+ {"./clang-tidy", false},
}
ctx.env = append(ctx.env, "ANDROID_LLVM_FALLBACK_DISABLED_WARNINGS=-a -b")
extraArgs := []string{"-fno-color-diagnostics", "-a", "-b"}
diff --git a/compiler_wrapper/compiler_wrapper.go b/compiler_wrapper/compiler_wrapper.go
index b157687f..17c62db7 100644
--- a/compiler_wrapper/compiler_wrapper.go
+++ b/compiler_wrapper/compiler_wrapper.go
@@ -71,7 +71,27 @@ func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int
env = mainBuilder.env
var compilerCmd *command
clangSyntax := processClangSyntaxFlag(mainBuilder)
- if mainBuilder.target.compilerType == clangType {
+ if cfg.isAndroidWrapper {
+ // FIXME: This combination of using the directory of the symlink but the
+ // basename of the link target is strange but is the logic that old android
+ // wrapper uses. Change this to use directory and basename either from the
+ // absWrapperPath or from the builder.path, but don't mix anymore.
+ mainBuilder.path = filepath.Join(filepath.Dir(mainBuilder.path), filepath.Base(mainBuilder.absWrapperPath)+".real")
+
+ switch mainBuilder.target.compilerType {
+ case clangType:
+ mainBuilder.addPreUserArgs(mainBuilder.cfg.clangFlags...)
+ mainBuilder.addPreUserArgs(mainBuilder.cfg.commonFlags...)
+ if _, err := processGomaCccFlags(mainBuilder); err != nil {
+ return 0, err
+ }
+ compilerCmd = mainBuilder.build()
+ case clangTidyType:
+ compilerCmd = mainBuilder.build()
+ default:
+ return 0, newErrorwithSourceLocf("unsupported compiler: %s", mainBuilder.target.compiler)
+ }
+ } else if mainBuilder.target.compilerType == clangType {
cSrcFile, useClangTidy := processClangTidyFlags(mainBuilder)
sysroot, err := prepareClangCommand(mainBuilder)
if err != nil {
@@ -146,7 +166,7 @@ func callCompilerInternal(env env, cfg *config, inputCmd *command) (exitCode int
func prepareClangCommand(builder *commandBuilder) (sysroot string, err error) {
sysroot = ""
- if !builder.cfg.isHostWrapper && !builder.cfg.isAndroidWrapper {
+ if !builder.cfg.isHostWrapper {
sysroot = processSysrootFlag(builder)
}
builder.addPreUserArgs(builder.cfg.clangFlags...)
@@ -189,15 +209,13 @@ func calcGccCommand(builder *commandBuilder) (*command, error) {
func calcCommonPreUserArgs(builder *commandBuilder) {
builder.addPreUserArgs(builder.cfg.commonFlags...)
- if !builder.cfg.isHostWrapper && !builder.cfg.isAndroidWrapper {
+ if !builder.cfg.isHostWrapper {
processPieFlags(builder)
processThumbCodeFlags(builder)
processStackProtectorFlags(builder)
processX86Flags(builder)
}
- if !builder.cfg.isAndroidWrapper {
- processSanitizerFlags(builder)
- }
+ processSanitizerFlags(builder)
}
func processGomaCCacheFlags(sysroot string, allowCCache bool, builder *commandBuilder) (err error) {
diff --git a/compiler_wrapper/cros_hardened_config_test.go b/compiler_wrapper/cros_hardened_config_test.go
index c3881303..10d8bf6f 100644
--- a/compiler_wrapper/cros_hardened_config_test.go
+++ b/compiler_wrapper/cros_hardened_config_test.go
@@ -196,6 +196,7 @@ func createBisectGoldenInputs(compiler string) goldenFile {
WrapperCmd: newGoldenCmd(compiler, mainCc),
Env: []string{
"BISECT_STAGE=someBisectStage",
+ "HOME=/user/home",
},
Cmds: okResults,
},
@@ -204,6 +205,7 @@ func createBisectGoldenInputs(compiler string) goldenFile {
Env: []string{
"BISECT_STAGE=someBisectStage",
"BISECT_DIR=someBisectDir",
+ "HOME=/user/home",
},
Cmds: okResults,
},
@@ -212,6 +214,7 @@ func createBisectGoldenInputs(compiler string) goldenFile {
Env: []string{
"BISECT_STAGE=someBisectStage",
"BISECT_DIR=someBisectDir",
+ "HOME=/user/home",
},
Cmds: errorResults,
},
@@ -512,6 +515,18 @@ func createSanitizerGoldenInputs(compiler string) goldenFile {
WrapperCmd: newGoldenCmd(wrapperPath, "-fsanitize=fuzzer", mainCc),
Cmds: okResults,
},
+ {
+ WrapperCmd: newGoldenCmd(wrapperPath, "-fsanitize=address", "-fprofile-instr-generate", mainCc),
+ Cmds: okResults,
+ },
+ {
+ WrapperCmd: newGoldenCmd(wrapperPath, "-fsanitize=address", mainCc),
+ Cmds: okResults,
+ },
+ {
+ WrapperCmd: newGoldenCmd(wrapperPath, "-fprofile-instr-generate", mainCc),
+ Cmds: okResults,
+ },
},
}
}
diff --git a/compiler_wrapper/goldenutil_test.go b/compiler_wrapper/goldenutil_test.go
index 0bb2b11c..4eff8738 100644
--- a/compiler_wrapper/goldenutil_test.go
+++ b/compiler_wrapper/goldenutil_test.go
@@ -175,11 +175,10 @@ func fillGoldenResults(ctx *testContext, files []goldenFile) []goldenFile {
func writeGoldenRecords(ctx *testContext, writer io.Writer, records []goldenRecord) {
// Replace the temp dir with a stable path so that the goldens stay stable.
stableTempDir := filepath.Join(filepath.Dir(ctx.tempDir), "stable")
- homeDir, _ := os.UserHomeDir()
writer = &replacingWriter{
Writer: writer,
- old: [][]byte{[]byte(ctx.tempDir), []byte(homeDir)},
- new: [][]byte{[]byte(stableTempDir), []byte("$HOME")},
+ old: [][]byte{[]byte(ctx.tempDir)},
+ new: [][]byte{[]byte(stableTempDir)},
}
enc := json.NewEncoder(writer)
enc.SetIndent("", " ")
diff --git a/compiler_wrapper/sanitizer_flags.go b/compiler_wrapper/sanitizer_flags.go
index 32f2cb78..fe8d1503 100644
--- a/compiler_wrapper/sanitizer_flags.go
+++ b/compiler_wrapper/sanitizer_flags.go
@@ -9,15 +9,20 @@ import (
)
func processSanitizerFlags(builder *commandBuilder) {
+ hasCoverageFlags := false
hasSanitizeFlags := false
hasSanitizeFuzzerFlags := false
for _, arg := range builder.args {
// TODO: This should probably be -fsanitize= to not match on
// e.g. -fsanitize-blacklist
- if arg.fromUser && strings.HasPrefix(arg.value, "-fsanitize") {
- hasSanitizeFlags = true
- if strings.Contains(arg.value, "fuzzer") {
- hasSanitizeFuzzerFlags = true
+ if arg.fromUser {
+ if strings.HasPrefix(arg.value, "-fsanitize") {
+ hasSanitizeFlags = true
+ if strings.Contains(arg.value, "fuzzer") {
+ hasSanitizeFuzzerFlags = true
+ }
+ } else if arg.value == "-fprofile-instr-generate" {
+ hasCoverageFlags = true
}
}
}
@@ -39,12 +44,15 @@ func processSanitizerFlags(builder *commandBuilder) {
}
return arg.value
})
- if hasSanitizeFuzzerFlags && builder.target.compilerType == clangType {
- fuzzerFlagsToAdd := []string{
- // TODO: This flag should be removed once fuzzer works with new pass manager
- "-fno-experimental-new-pass-manager",
+ if builder.target.compilerType == clangType {
+ // hasSanitizeFlags && hasCoverageFlags is to work around crbug.com/1013622
+ if hasSanitizeFuzzerFlags || (hasSanitizeFlags && hasCoverageFlags) {
+ fuzzerFlagsToAdd := []string{
+ // TODO: This flag should be removed once fuzzer works with new pass manager
+ "-fno-experimental-new-pass-manager",
+ }
+ builder.addPreUserArgs(fuzzerFlagsToAdd...)
}
- builder.addPreUserArgs(fuzzerFlagsToAdd...)
}
}
}
diff --git a/compiler_wrapper/sanitizer_flags_test.go b/compiler_wrapper/sanitizer_flags_test.go
index 741f7732..8f50a900 100644
--- a/compiler_wrapper/sanitizer_flags_test.go
+++ b/compiler_wrapper/sanitizer_flags_test.go
@@ -119,3 +119,34 @@ func TestOmitFuzzerFlagsForGcc(t *testing.T) {
}
})
}
+
+func TestAddSanitizerCoverageFlagsForClang(t *testing.T) {
+ withTestContext(t, func(ctx *testContext) {
+ cmd := ctx.must(callCompiler(ctx, ctx.cfg,
+ ctx.newCommand(clangX86_64, "-fsanitize=address", "-fprofile-instr-generate", mainCc)))
+ if err := verifyArgOrder(cmd, "-fno-experimental-new-pass-manager",
+ "-fsanitize=address", "-fprofile-instr-generate", mainCc); err != nil {
+ t.Error(err)
+ }
+ })
+}
+
+func TestOmitSanitizerCoverageFlagsForClang(t *testing.T) {
+ withTestContext(t, func(ctx *testContext) {
+ cmd := ctx.must(callCompiler(ctx, ctx.cfg,
+ ctx.newCommand(clangX86_64, "-fsanitize=address", mainCc)))
+ if err := verifyArgCount(cmd, 0, "-fno-experimental-new-pass-manager"); err != nil {
+ t.Error(err)
+ }
+ })
+}
+
+func TestKeepSanitizerCoverageFlagsForClang(t *testing.T) {
+ withTestContext(t, func(ctx *testContext) {
+ cmd := ctx.must(callCompiler(ctx, ctx.cfg,
+ ctx.newCommand(clangX86_64, "-fprofile-instr-generate", mainCc)))
+ if err := verifyArgCount(cmd, 0, "-fno-experimental-new-pass-manager"); err != nil {
+ t.Error(err)
+ }
+ })
+}
diff --git a/compiler_wrapper/testdata/android_golden/bisect.json b/compiler_wrapper/testdata/android_golden/bisect.json
index 41310ca1..a24222ab 100644
--- a/compiler_wrapper/testdata/android_golden/bisect.json
+++ b/compiler_wrapper/testdata/android_golden/bisect.json
@@ -2,7 +2,8 @@
{
"wd": "/tmp/stable",
"env": [
- "BISECT_STAGE=someBisectStage"
+ "BISECT_STAGE=someBisectStage",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -21,7 +22,7 @@
"-c",
"\nimport bisect_driver\nimport shlex\nimport sys\n\ndef ExpandArgs(args, target):\n\tfor arg in args:\n\t\tif arg[0] == '@':\n\t\t\twith open(arg[1:], 'rb') as f:\n\t\t\t\tExpandArgs(shlex.split(f.read()), target)\n\t\telse:\n\t\t\ttarget.append(arg)\n\treturn target\n\nstage = sys.argv[1]\ndir = sys.argv[2]\nexecargs = ExpandArgs(sys.argv[3:], [])\n\nsys.exit(bisect_driver.bisect_driver(stage, dir, execargs))\n",
"someBisectStage",
- "$HOME/ANDROID_BISECT",
+ "/user/home/ANDROID_BISECT",
"/tmp/stable/clang.real",
"main.cc"
]
@@ -33,7 +34,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -64,7 +66,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
diff --git a/compiler_wrapper/testdata/android_golden/clang_path.json b/compiler_wrapper/testdata/android_golden/clang_path.json
index b784feb5..5686b381 100644
--- a/compiler_wrapper/testdata/android_golden/clang_path.json
+++ b/compiler_wrapper/testdata/android_golden/clang_path.json
@@ -49,6 +49,30 @@
},
{
"wd": "/tmp/stable",
+ "env": [
+ "WITH_TIDY=1"
+ ],
+ "wrapper": {
+ "cmd": {
+ "path": "/tmp/stable/clang",
+ "args": [
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/tmp/stable/clang.real",
+ "args": [
+ "main.cc"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
"wrapper": {
"cmd": {
"path": "/tmp/stable/clang++",
@@ -91,6 +115,30 @@
},
{
"wd": "/tmp/stable",
+ "env": [
+ "WITH_TIDY=1"
+ ],
+ "wrapper": {
+ "cmd": {
+ "path": "/tmp/stable/clang-tidy",
+ "args": [
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/tmp/stable/clang-tidy.real",
+ "args": [
+ "main.cc"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
"wrapper": {
"cmd": {
"path": "a/b/c/d/e/f/g/clang",
diff --git a/compiler_wrapper/testdata/cros_clang_host_golden/bisect.json b/compiler_wrapper/testdata/cros_clang_host_golden/bisect.json
index 5a67a635..b9b1509f 100644
--- a/compiler_wrapper/testdata/cros_clang_host_golden/bisect.json
+++ b/compiler_wrapper/testdata/cros_clang_host_golden/bisect.json
@@ -2,7 +2,8 @@
{
"wd": "/tmp/stable",
"env": [
- "BISECT_STAGE=someBisectStage"
+ "BISECT_STAGE=someBisectStage",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -42,7 +43,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -82,7 +84,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
diff --git a/compiler_wrapper/testdata/cros_clang_host_golden/clang_sanitizer_args.json b/compiler_wrapper/testdata/cros_clang_host_golden/clang_sanitizer_args.json
index 7e7e54c0..be1a2922 100644
--- a/compiler_wrapper/testdata/cros_clang_host_golden/clang_sanitizer_args.json
+++ b/compiler_wrapper/testdata/cros_clang_host_golden/clang_sanitizer_args.json
@@ -163,5 +163,104 @@
}
}
]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-clang",
+ "args": [
+ "-fsanitize=address",
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/tmp/stable/clang",
+ "args": [
+ "-Qunused-arguments",
+ "-grecord-gcc-switches",
+ "-fno-addrsig",
+ "-fuse-ld=lld",
+ "-Wno-unused-local-typedefs",
+ "-Wno-deprecated-declarations",
+ "-Wno-tautological-constant-compare",
+ "-Wno-tautological-unsigned-enum-zero-compare",
+ "-Wno-unknown-warning-option",
+ "-fno-experimental-new-pass-manager",
+ "-fsanitize=address",
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-clang",
+ "args": [
+ "-fsanitize=address",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/tmp/stable/clang",
+ "args": [
+ "-Qunused-arguments",
+ "-grecord-gcc-switches",
+ "-fno-addrsig",
+ "-fuse-ld=lld",
+ "-Wno-unused-local-typedefs",
+ "-Wno-deprecated-declarations",
+ "-Wno-tautological-constant-compare",
+ "-Wno-tautological-unsigned-enum-zero-compare",
+ "-Wno-unknown-warning-option",
+ "-fsanitize=address",
+ "main.cc"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-clang",
+ "args": [
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/tmp/stable/clang",
+ "args": [
+ "-Qunused-arguments",
+ "-grecord-gcc-switches",
+ "-fno-addrsig",
+ "-fuse-ld=lld",
+ "-Wno-unused-local-typedefs",
+ "-Wno-deprecated-declarations",
+ "-Wno-tautological-constant-compare",
+ "-Wno-tautological-unsigned-enum-zero-compare",
+ "-Wno-unknown-warning-option",
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ }
+ ]
}
]
diff --git a/compiler_wrapper/testdata/cros_hardened_golden/bisect.json b/compiler_wrapper/testdata/cros_hardened_golden/bisect.json
index e0709e04..f9a503f3 100644
--- a/compiler_wrapper/testdata/cros_hardened_golden/bisect.json
+++ b/compiler_wrapper/testdata/cros_hardened_golden/bisect.json
@@ -2,7 +2,8 @@
{
"wd": "/tmp/stable",
"env": [
- "BISECT_STAGE=someBisectStage"
+ "BISECT_STAGE=someBisectStage",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -58,7 +59,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -114,7 +116,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
diff --git a/compiler_wrapper/testdata/cros_hardened_golden/clang_sanitizer_args.json b/compiler_wrapper/testdata/cros_hardened_golden/clang_sanitizer_args.json
index 9fa1f4f2..39094948 100644
--- a/compiler_wrapper/testdata/cros_hardened_golden/clang_sanitizer_args.json
+++ b/compiler_wrapper/testdata/cros_hardened_golden/clang_sanitizer_args.json
@@ -238,5 +238,150 @@
}
}
]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-clang",
+ "args": [
+ "-fsanitize=address",
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "../../usr/bin/clang",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-Qunused-arguments",
+ "-grecord-gcc-switches",
+ "-fno-addrsig",
+ "-Wno-tautological-constant-compare",
+ "-Wno-tautological-unsigned-enum-zero-compare",
+ "-Wno-unknown-warning-option",
+ "-Wno-section",
+ "-static-libgcc",
+ "-fuse-ld=lld",
+ "-fstack-protector-strong",
+ "-fPIE",
+ "-pie",
+ "-fno-omit-frame-pointer",
+ "-fno-experimental-new-pass-manager",
+ "-fsanitize=address",
+ "-fprofile-instr-generate",
+ "main.cc",
+ "-B../../bin",
+ "-target",
+ "x86_64-cros-linux-gnu"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002",
+ "CCACHE_CPP2=yes"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-clang",
+ "args": [
+ "-fsanitize=address",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "../../usr/bin/clang",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-Qunused-arguments",
+ "-grecord-gcc-switches",
+ "-fno-addrsig",
+ "-Wno-tautological-constant-compare",
+ "-Wno-tautological-unsigned-enum-zero-compare",
+ "-Wno-unknown-warning-option",
+ "-Wno-section",
+ "-static-libgcc",
+ "-fuse-ld=lld",
+ "-fstack-protector-strong",
+ "-fPIE",
+ "-pie",
+ "-fno-omit-frame-pointer",
+ "-fsanitize=address",
+ "main.cc",
+ "-B../../bin",
+ "-target",
+ "x86_64-cros-linux-gnu"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002",
+ "CCACHE_CPP2=yes"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-clang",
+ "args": [
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "../../usr/bin/clang",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-Qunused-arguments",
+ "-grecord-gcc-switches",
+ "-fno-addrsig",
+ "-Wno-tautological-constant-compare",
+ "-Wno-tautological-unsigned-enum-zero-compare",
+ "-Wno-unknown-warning-option",
+ "-Wno-section",
+ "-static-libgcc",
+ "-fuse-ld=lld",
+ "-fstack-protector-strong",
+ "-fPIE",
+ "-pie",
+ "-D_FORTIFY_SOURCE=2",
+ "-fno-omit-frame-pointer",
+ "-fprofile-instr-generate",
+ "main.cc",
+ "-B../../bin",
+ "-target",
+ "x86_64-cros-linux-gnu"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002",
+ "CCACHE_CPP2=yes"
+ ]
+ }
+ }
+ ]
}
]
diff --git a/compiler_wrapper/testdata/cros_hardened_golden/gcc_sanitizer_args.json b/compiler_wrapper/testdata/cros_hardened_golden/gcc_sanitizer_args.json
index bddac1d2..a63aa25c 100644
--- a/compiler_wrapper/testdata/cros_hardened_golden/gcc_sanitizer_args.json
+++ b/compiler_wrapper/testdata/cros_hardened_golden/gcc_sanitizer_args.json
@@ -197,5 +197,124 @@
}
}
]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-gcc",
+ "args": [
+ "-fsanitize=address",
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "./x86_64-cros-linux-gnu-gcc.real",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-fno-reorder-blocks-and-partition",
+ "-Wno-unused-local-typedefs",
+ "-Wno-maybe-uninitialized",
+ "-fstack-protector-strong",
+ "-fPIE",
+ "-pie",
+ "-D_FORTIFY_SOURCE=2",
+ "-fno-omit-frame-pointer",
+ "-fsanitize=address",
+ "-fprofile-instr-generate",
+ "main.cc",
+ "-mno-movbe"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-gcc",
+ "args": [
+ "-fsanitize=address",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "./x86_64-cros-linux-gnu-gcc.real",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-fno-reorder-blocks-and-partition",
+ "-Wno-unused-local-typedefs",
+ "-Wno-maybe-uninitialized",
+ "-fstack-protector-strong",
+ "-fPIE",
+ "-pie",
+ "-D_FORTIFY_SOURCE=2",
+ "-fno-omit-frame-pointer",
+ "-fsanitize=address",
+ "main.cc",
+ "-mno-movbe"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-gcc",
+ "args": [
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "./x86_64-cros-linux-gnu-gcc.real",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-fno-reorder-blocks-and-partition",
+ "-Wno-unused-local-typedefs",
+ "-Wno-maybe-uninitialized",
+ "-fstack-protector-strong",
+ "-fPIE",
+ "-pie",
+ "-D_FORTIFY_SOURCE=2",
+ "-fno-omit-frame-pointer",
+ "-fprofile-instr-generate",
+ "main.cc",
+ "-mno-movbe"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002"
+ ]
+ }
+ }
+ ]
}
]
diff --git a/compiler_wrapper/testdata/cros_hardened_llvmnext_golden/bisect.json b/compiler_wrapper/testdata/cros_hardened_llvmnext_golden/bisect.json
index 905a8692..56b7b3ed 100644
--- a/compiler_wrapper/testdata/cros_hardened_llvmnext_golden/bisect.json
+++ b/compiler_wrapper/testdata/cros_hardened_llvmnext_golden/bisect.json
@@ -2,7 +2,8 @@
{
"wd": "/tmp/stable",
"env": [
- "BISECT_STAGE=someBisectStage"
+ "BISECT_STAGE=someBisectStage",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -63,7 +64,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -124,7 +126,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
diff --git a/compiler_wrapper/testdata/cros_hardened_noccache_golden/bisect.json b/compiler_wrapper/testdata/cros_hardened_noccache_golden/bisect.json
index c8255e56..f3061817 100644
--- a/compiler_wrapper/testdata/cros_hardened_noccache_golden/bisect.json
+++ b/compiler_wrapper/testdata/cros_hardened_noccache_golden/bisect.json
@@ -2,7 +2,8 @@
{
"wd": "/tmp/stable",
"env": [
- "BISECT_STAGE=someBisectStage"
+ "BISECT_STAGE=someBisectStage",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -51,7 +52,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -100,7 +102,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
diff --git a/compiler_wrapper/testdata/cros_nonhardened_golden/bisect.json b/compiler_wrapper/testdata/cros_nonhardened_golden/bisect.json
index d81622fd..4bc696d3 100644
--- a/compiler_wrapper/testdata/cros_nonhardened_golden/bisect.json
+++ b/compiler_wrapper/testdata/cros_nonhardened_golden/bisect.json
@@ -2,7 +2,8 @@
{
"wd": "/tmp/stable",
"env": [
- "BISECT_STAGE=someBisectStage"
+ "BISECT_STAGE=someBisectStage",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -50,7 +51,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
@@ -98,7 +100,8 @@
"wd": "/tmp/stable",
"env": [
"BISECT_STAGE=someBisectStage",
- "BISECT_DIR=someBisectDir"
+ "BISECT_DIR=someBisectDir",
+ "HOME=/user/home"
],
"wrapper": {
"cmd": {
diff --git a/compiler_wrapper/testdata/cros_nonhardened_golden/clang_sanitizer_args.json b/compiler_wrapper/testdata/cros_nonhardened_golden/clang_sanitizer_args.json
index 05209ee3..386e82d6 100644
--- a/compiler_wrapper/testdata/cros_nonhardened_golden/clang_sanitizer_args.json
+++ b/compiler_wrapper/testdata/cros_nonhardened_golden/clang_sanitizer_args.json
@@ -203,5 +203,128 @@
}
}
]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-clang",
+ "args": [
+ "-fsanitize=address",
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "../../usr/bin/clang",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-Qunused-arguments",
+ "-Wno-tautological-constant-compare",
+ "-Wno-tautological-unsigned-enum-zero-compare",
+ "-Wno-unknown-warning-option",
+ "-Wno-section",
+ "-static-libgcc",
+ "-fno-experimental-new-pass-manager",
+ "-fsanitize=address",
+ "-fprofile-instr-generate",
+ "main.cc",
+ "-B../../bin",
+ "-target",
+ "x86_64-cros-linux-gnu"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002",
+ "CCACHE_CPP2=yes"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-clang",
+ "args": [
+ "-fsanitize=address",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "../../usr/bin/clang",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-Qunused-arguments",
+ "-Wno-tautological-constant-compare",
+ "-Wno-tautological-unsigned-enum-zero-compare",
+ "-Wno-unknown-warning-option",
+ "-Wno-section",
+ "-static-libgcc",
+ "-fsanitize=address",
+ "main.cc",
+ "-B../../bin",
+ "-target",
+ "x86_64-cros-linux-gnu"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002",
+ "CCACHE_CPP2=yes"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-clang",
+ "args": [
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "../../usr/bin/clang",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-Qunused-arguments",
+ "-Wno-tautological-constant-compare",
+ "-Wno-tautological-unsigned-enum-zero-compare",
+ "-Wno-unknown-warning-option",
+ "-Wno-section",
+ "-static-libgcc",
+ "-fprofile-instr-generate",
+ "main.cc",
+ "-B../../bin",
+ "-target",
+ "x86_64-cros-linux-gnu"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002",
+ "CCACHE_CPP2=yes"
+ ]
+ }
+ }
+ ]
}
]
diff --git a/compiler_wrapper/testdata/cros_nonhardened_golden/gcc_sanitizer_args.json b/compiler_wrapper/testdata/cros_nonhardened_golden/gcc_sanitizer_args.json
index 639dc524..7091f608 100644
--- a/compiler_wrapper/testdata/cros_nonhardened_golden/gcc_sanitizer_args.json
+++ b/compiler_wrapper/testdata/cros_nonhardened_golden/gcc_sanitizer_args.json
@@ -177,5 +177,112 @@
}
}
]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-gcc",
+ "args": [
+ "-fsanitize=address",
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "./x86_64-cros-linux-gnu-gcc.real",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-Wno-maybe-uninitialized",
+ "-Wno-unused-local-typedefs",
+ "-Wno-deprecated-declarations",
+ "-Wtrampolines",
+ "-fsanitize=address",
+ "-fprofile-instr-generate",
+ "main.cc",
+ "-mno-movbe"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-gcc",
+ "args": [
+ "-fsanitize=address",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "./x86_64-cros-linux-gnu-gcc.real",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-Wno-maybe-uninitialized",
+ "-Wno-unused-local-typedefs",
+ "-Wno-deprecated-declarations",
+ "-Wtrampolines",
+ "-fsanitize=address",
+ "main.cc",
+ "-mno-movbe"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002"
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "wd": "/tmp/stable",
+ "wrapper": {
+ "cmd": {
+ "path": "./x86_64-cros-linux-gnu-gcc",
+ "args": [
+ "-fprofile-instr-generate",
+ "main.cc"
+ ]
+ }
+ },
+ "cmds": [
+ {
+ "cmd": {
+ "path": "/usr/bin/ccache",
+ "args": [
+ "./x86_64-cros-linux-gnu-gcc.real",
+ "--sysroot=/usr/x86_64-cros-linux-gnu",
+ "-Wno-maybe-uninitialized",
+ "-Wno-unused-local-typedefs",
+ "-Wno-deprecated-declarations",
+ "-Wtrampolines",
+ "-fprofile-instr-generate",
+ "main.cc",
+ "-mno-movbe"
+ ],
+ "env_updates": [
+ "CCACHE_BASEDIR=/usr/x86_64-cros-linux-gnu",
+ "CCACHE_DIR=/var/cache/distfiles/ccache",
+ "CCACHE_UMASK=002"
+ ]
+ }
+ }
+ ]
}
]
diff --git a/cros_utils/locks.py b/cros_utils/locks.py
index 07434145..4ecbe0a9 100644
--- a/cros_utils/locks.py
+++ b/cros_utils/locks.py
@@ -10,20 +10,20 @@ from __future__ import print_function
import time
-import afe_lock_machine
+import lock_machine
import logger
def AcquireLock(machines, chromeos_root, timeout=1200):
- """Acquire lock for machine(s) with timeout, using AFE server for locking."""
+ """Acquire lock for machine(s) with timeout."""
start_time = time.time()
locked = True
sleep_time = min(10, timeout / 10.0)
while True:
try:
- afe_lock_machine.AFELockManager(machines, False,
- chromeos_root).UpdateMachines(True)
+ lock_machine.LockManager(machines, False,
+ chromeos_root).UpdateMachines(True)
break
except Exception as e:
if time.time() - start_time > timeout:
@@ -37,11 +37,11 @@ def AcquireLock(machines, chromeos_root, timeout=1200):
def ReleaseLock(machines, chromeos_root):
- """Release locked machine(s), using AFE server for locking."""
+ """Release locked machine(s)."""
unlocked = True
try:
- afe_lock_machine.AFELockManager(machines, False,
- chromeos_root).UpdateMachines(False)
+ lock_machine.LockManager(machines, False,
+ chromeos_root).UpdateMachines(False)
except Exception as e:
unlocked = False
logger.GetLogger().LogWarning(
diff --git a/cros_utils/no_pseudo_terminal_test.py b/cros_utils/no_pseudo_terminal_test.py
index 43eabb13..41d71539 100644..100755
--- a/cros_utils/no_pseudo_terminal_test.py
+++ b/cros_utils/no_pseudo_terminal_test.py
@@ -1,3 +1,10 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+#
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
"""Test to ensure we're not touching /dev/ptmx when running commands."""
from __future__ import print_function
@@ -18,9 +25,9 @@ class NoPsuedoTerminalTest(unittest.TestCase):
def _AttachStraceToSelf(self, output_file):
"""Attaches strace to the current process."""
- args = ['strace', '-o', output_file, '-p', str(os.getpid())]
+ args = ['sudo', 'strace', '-o', output_file, '-p', str(os.getpid())]
print(args)
- self._strace_process = subprocess.Popen(args)
+ self._strace_process = subprocess.Popen(args, preexec_fn=os.setpgrp)
# Wait until we see some activity.
start_time = time.time()
while time.time() - start_time < self.STRACE_TIMEOUT:
@@ -31,9 +38,12 @@ class NoPsuedoTerminalTest(unittest.TestCase):
def _KillStraceProcess(self):
"""Kills strace that was started by _AttachStraceToSelf()."""
- self._strace_process.terminate()
- self._strace_process.wait()
- return True
+ pgid = os.getpgid(self._strace_process.pid)
+ args = ['sudo', 'kill', str(pgid)]
+ if subprocess.call(args) == 0:
+ os.waitpid(pgid, 0)
+ return True
+ return False
def testNoPseudoTerminalWhenRunningCommand(self):
"""Test to make sure we're not touching /dev/ptmx when running commands."""
diff --git a/crosperf/benchmark_run.py b/crosperf/benchmark_run.py
index 0147599d..6512b8ea 100644
--- a/crosperf/benchmark_run.py
+++ b/crosperf/benchmark_run.py
@@ -91,25 +91,6 @@ class BenchmarkRun(threading.Thread):
self.cache_hit = (self.result is not None)
self.cache_has_been_read = True
- def PrintTop5Cmds(self, topcmds):
- """Print top 5 commands into log."""
-
- self._logger.LogOutput('%s' % str(self))
- self._logger.LogOutput('Top 5 commands with highest CPU usage:')
- # Header.
- print_line = '%20s %9s %6s %s' % ('COMMAND', 'AVG CPU%', 'COUNT',
- 'HIGHEST 5')
- self._logger.LogOutput(print_line)
- self._logger.LogOutput('-' * 50)
- if topcmds:
- for topcmd in topcmds[:5]:
- print_line = '%20s %9.2f %6s %s' % (topcmd['cmd'], topcmd['cpu_avg'],
- topcmd['count'], topcmd['top5'])
- self._logger.LogOutput(print_line)
- else:
- self._logger.LogOutput('[NO DATA FROM THE TOP LOG]')
- self._logger.LogOutput('-' * 50)
-
def run(self):
try:
if not self.cache_has_been_read:
@@ -138,8 +119,6 @@ class BenchmarkRun(threading.Thread):
self.cache.machine = self.machine
self.result = self.RunTest(self.machine)
# TODO(denik): Add Top5 report into html.
- if self.result:
- self.PrintTop5Cmds(self.result.GetTopCmds())
self.cache.remote = self.machine.name
self.label.chrome_version = self.machine_manager.GetChromeVersion(
diff --git a/crosperf/benchmark_run_unittest.py b/crosperf/benchmark_run_unittest.py
index 98bb96cd..51b287cf 100755
--- a/crosperf/benchmark_run_unittest.py
+++ b/crosperf/benchmark_run_unittest.py
@@ -206,7 +206,6 @@ class BenchmarkRunTest(unittest.TestCase):
br.ReadCache = FakeReadCache
br.RunTest = FakeRunTest
br.AcquireMachine = FakeAcquireMachine
- br.PrintTop5Cmds = mock.Mock()
# First test: No cache hit, all goes well.
ResetTestValues()
@@ -219,7 +218,6 @@ class BenchmarkRunTest(unittest.TestCase):
])
self.assertEqual(len(self.log_error), 0)
self.assertEqual(self.status, ['WAITING', 'SUCCEEDED'])
- br.PrintTop5Cmds.assert_called_once()
# Second test: No cached result found; test run was "terminated" for some
# reason.
@@ -436,89 +434,6 @@ class BenchmarkRunTest(unittest.TestCase):
br.SetCacheConditions(self.test_cache_conditions)
self.assertEqual(br.cache_conditions, self.test_cache_conditions)
- def test_print_top5_cmds(self):
- """Test print of top5 commands."""
- topcmds = [
- {
- 'cmd': 'chrome',
- 'cpu_avg': 119.753453465,
- 'count': 4,
- 'top5': [122.8, 107.9, 17.8, 1.0],
- },
- {
- 'cmd': 'irq/230-cros-ec',
- 'cpu_avg': 10.000000000000001,
- 'count': 1000,
- 'top5': [0.5, 0.4, 0.3, 0.2, 0.1],
- },
- {
- 'cmd': 'powerd',
- 'cpu_avg': 2.0,
- 'count': 2,
- 'top5': [3.0, 1.0]
- },
- {
- 'cmd': 'cmd1',
- 'cpu_avg': 1.0,
- 'count': 1,
- 'top5': [1.0],
- },
- {
- 'cmd': 'cmd2',
- 'cpu_avg': 1.0,
- 'count': 1,
- 'top5': [1.0],
- },
- {
- 'cmd': 'not_for_print',
- 'cpu_avg': 1.0,
- 'count': 1,
- 'top5': [1.0],
- },
- ]
- mock_logger = mock.Mock()
- br = benchmark_run.BenchmarkRun(
- 'test_run', self.test_benchmark, self.test_label, 1,
- self.test_cache_conditions, self.mock_machine_manager, mock_logger,
- 'average', '', {})
- br.PrintTop5Cmds(topcmds)
- # pylint: disable=line-too-long
- self.assertEqual(mock_logger.LogOutput.call_args_list, [
- mock.call('BenchmarkRun[name="test_run"]'),
- mock.call('Top 5 commands with highest CPU usage:'),
- mock.call(' COMMAND AVG CPU% COUNT HIGHEST 5'),
- mock.call('-' * 50),
- mock.call(
- ' chrome 119.75 4 [122.8, 107.9, 17.8, 1.0]'
- ),
- mock.call(
- ' irq/230-cros-ec 10.00 1000 [0.5, 0.4, 0.3, 0.2, 0.1]'
- ),
- mock.call(' powerd 2.00 2 [3.0, 1.0]'),
- mock.call(' cmd1 1.00 1 [1.0]'),
- mock.call(' cmd2 1.00 1 [1.0]'),
- mock.call('-' * 50),
- ])
- # pylint: enable=line-too-long
-
- def test_print_top5_calls_no_data(self):
- """Test print of top5 with no data."""
- topcmds = []
- mock_logger = mock.Mock()
- br = benchmark_run.BenchmarkRun(
- 'test_run', self.test_benchmark, self.test_label, 1,
- self.test_cache_conditions, self.mock_machine_manager, mock_logger,
- 'average', '', {})
- br.PrintTop5Cmds(topcmds)
- self.assertEqual(mock_logger.LogOutput.call_args_list, [
- mock.call('BenchmarkRun[name="test_run"]'),
- mock.call('Top 5 commands with highest CPU usage:'),
- mock.call(' COMMAND AVG CPU% COUNT HIGHEST 5'),
- mock.call('-' * 50),
- mock.call('[NO DATA FROM THE TOP LOG]'),
- mock.call('-' * 50),
- ])
-
if __name__ == '__main__':
unittest.main()
diff --git a/crosperf/experiment.py b/crosperf/experiment.py
index 7a3169b3..1d87b6e4 100644
--- a/crosperf/experiment.py
+++ b/crosperf/experiment.py
@@ -50,9 +50,6 @@ class Experiment(object):
self.num_run_complete = 0
self.share_cache = share_cache
self.active_threads = []
- # If locks_directory (self.lock_dir) not blank, we will use the file
- # locking mechanism; if it is blank then we will use the AFE server
- # locking mechanism.
self.locks_dir = locks_directory
self.locked_machines = []
self.lock_mgr = None
diff --git a/crosperf/experiment_factory.py b/crosperf/experiment_factory.py
index 563f3ac6..5b4d4b0d 100644
--- a/crosperf/experiment_factory.py
+++ b/crosperf/experiment_factory.py
@@ -143,14 +143,20 @@ class ExperimentFactory(object):
config.AddConfig('no_email', global_settings.GetField('no_email'))
share_cache = global_settings.GetField('share_cache')
results_dir = global_settings.GetField('results_dir')
+ # Warn user that option use_file_locks is deprecated.
use_file_locks = global_settings.GetField('use_file_locks')
+ if use_file_locks:
+ l = logger.GetLogger()
+ l.LogWarning('Option use_file_locks is deprecated, please remove it '
+ 'from your experiment settings.')
locks_dir = global_settings.GetField('locks_dir')
- # If we pass a blank locks_dir to the Experiment, it will use the AFE server
- # lock mechanism. So if the user specified use_file_locks, but did not
- # specify a locks dir, set the locks dir to the default locks dir in
+ # If not specified, set the locks dir to the default locks dir in
# file_lock_machine.
- if use_file_locks and not locks_dir:
+ if not locks_dir:
locks_dir = file_lock_machine.Machine.LOCKS_DIR
+ if not os.path.exists(locks_dir):
+ raise RuntimeError('Cannot access default lock directory. '
+ 'Please run prodaccess or specify a local directory')
chrome_src = global_settings.GetField('chrome_src')
show_all_results = global_settings.GetField('show_all_results')
cwp_dso = global_settings.GetField('cwp_dso')
diff --git a/crosperf/experiment_runner.py b/crosperf/experiment_runner.py
index 1bca6b8c..cb6e9785 100644
--- a/crosperf/experiment_runner.py
+++ b/crosperf/experiment_runner.py
@@ -11,7 +11,7 @@ import os
import shutil
import time
-import afe_lock_machine
+import lock_machine
import test_flag
from cros_utils import command_executer
@@ -129,7 +129,7 @@ class ExperimentRunner(object):
self.locked_machines = self._GetMachineList()
experiment.locked_machines = self.locked_machines
else:
- experiment.lock_mgr = afe_lock_machine.AFELockManager(
+ experiment.lock_mgr = lock_machine.LockManager(
self._GetMachineList(),
'',
experiment.labels[0].chromeos_root,
@@ -282,11 +282,24 @@ class ExperimentRunner(object):
self.l.LogOutput('Storing results of each benchmark run.')
for benchmark_run in experiment.benchmark_runs:
if benchmark_run.result:
- benchmark_run_name = filter(str.isalnum, benchmark_run.name)
+ benchmark_run_name = ''.join(
+ ch for ch in benchmark_run.name if ch.isalnum())
benchmark_run_path = os.path.join(results_directory, benchmark_run_name)
benchmark_run.result.CopyResultsTo(benchmark_run_path)
benchmark_run.result.CleanUp(benchmark_run.benchmark.rm_chroot_tmp)
+ topstats_file = os.path.join(results_directory, 'topstats.log')
+ self.l.LogOutput('Storing top5 statistics of each benchmark run into %s.' %
+ topstats_file)
+ with open(topstats_file, 'w') as top_fd:
+ for benchmark_run in experiment.benchmark_runs:
+ if benchmark_run.result:
+ # Header with benchmark run name.
+ top_fd.write('%s\n' % str(benchmark_run))
+ # Formatted string with top statistics.
+ top_fd.write(benchmark_run.result.FormatStringTop5())
+ top_fd.write('\n\n')
+
def Run(self):
try:
self._Run(self._experiment)
diff --git a/crosperf/experiment_runner_unittest.py b/crosperf/experiment_runner_unittest.py
index caac4265..2ec11ccd 100755
--- a/crosperf/experiment_runner_unittest.py
+++ b/crosperf/experiment_runner_unittest.py
@@ -407,8 +407,11 @@ class ExperimentRunnerTest(unittest.TestCase):
@mock.patch.object(TextResultsReport, 'FromExperiment')
@mock.patch.object(Result, 'CopyResultsTo')
@mock.patch.object(Result, 'CleanUp')
- def test_store_results(self, mock_cleanup, mock_copy, _mock_text_report,
- mock_report, mock_writefile, mock_mkdir, mock_rmdir):
+ @mock.patch.object(Result, 'FormatStringTop5')
+ @mock.patch('__builtin__.open', new_callable=mock.mock_open)
+ def test_store_results(self, mock_open, mock_top5, mock_cleanup, mock_copy,
+ _mock_text_report, mock_report, mock_writefile,
+ mock_mkdir, mock_rmdir):
self.mock_logger.Reset()
self.exp.results_directory = '/usr/local/crosperf-results'
@@ -434,6 +437,8 @@ class ExperimentRunnerTest(unittest.TestCase):
self.assertEqual(mock_mkdir.call_count, 0)
self.assertEqual(mock_rmdir.call_count, 0)
self.assertEqual(self.mock_logger.LogOutputCount, 0)
+ self.assertEqual(mock_open.call_count, 0)
+ self.assertEqual(mock_top5.call_count, 0)
# Test 2. _terminated is false; everything works properly.
fake_result = Result(self.mock_logger, self.exp.labels[0], 'average',
@@ -458,13 +463,24 @@ class ExperimentRunnerTest(unittest.TestCase):
mock_mkdir.assert_called_with('/usr/local/crosperf-results')
self.assertEqual(mock_rmdir.call_count, 1)
mock_rmdir.assert_called_with('/usr/local/crosperf-results')
- self.assertEqual(self.mock_logger.LogOutputCount, 4)
+ self.assertEqual(self.mock_logger.LogOutputCount, 5)
self.assertEqual(self.mock_logger.output_msgs, [
'Storing experiment file in /usr/local/crosperf-results.',
'Storing results report in /usr/local/crosperf-results.',
'Storing email message body in /usr/local/crosperf-results.',
- 'Storing results of each benchmark run.'
+ 'Storing results of each benchmark run.',
+ 'Storing top5 statistics of each benchmark run into'
+ ' /usr/local/crosperf-results/topstats.log.',
])
+ self.assertEqual(mock_open.call_count, 1)
+ # Check write to a topstats.log file.
+ mock_open.assert_called_with('/usr/local/crosperf-results/topstats.log',
+ 'w')
+ mock_open().write.assert_called()
+
+ # Check top5 calls with no arguments.
+ top5calls = [mock.call()] * 6
+ self.assertEqual(mock_top5.call_args_list, top5calls)
if __name__ == '__main__':
diff --git a/crosperf/machine_manager.py b/crosperf/machine_manager.py
index b9dda148..ea3d105a 100644
--- a/crosperf/machine_manager.py
+++ b/crosperf/machine_manager.py
@@ -1,6 +1,7 @@
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+
"""Machine Manager module."""
from __future__ import print_function
@@ -184,10 +185,10 @@ class MachineManager(object):
This class contains methods and calls to lock, unlock and image
machines and distribute machines to each benchmark run. The assumption is
that all of the machines for the experiment have been globally locked
- (using an AFE server) in the ExperimentRunner, but the machines still need
- to be locally locked/unlocked (allocated to benchmark runs) to prevent
- multiple benchmark runs within the same experiment from trying to use the
- same machine at the same time.
+ in the ExperimentRunner, but the machines still need to be locally
+ locked/unlocked (allocated to benchmark runs) to prevent multiple benchmark
+ runs within the same experiment from trying to use the same machine at the
+ same time.
"""
def __init__(self,
@@ -254,7 +255,8 @@ class MachineManager(object):
image_chromeos.__file__, '--no_lock',
'--chromeos_root=%s' % chromeos_root,
'--image=%s' % label.chromeos_image,
- '--image_args=%s' % label.image_args, '--remote=%s' % machine.name,
+ '--image_args=%s' % label.image_args,
+ '--remote=%s' % machine.name,
'--logging_level=%s' % self.log_level
]
if label.board:
@@ -401,10 +403,10 @@ class MachineManager(object):
self.acquire_timeout -= sleep_time
if self.acquire_timeout < 0:
- self.logger.LogFatal(
- 'Could not acquire any of the '
- "following machines: '%s'" % ', '.join(machine.name
- for machine in machines))
+ self.logger.LogFatal('Could not acquire any of the '
+ "following machines: '%s'" % ', '.join(
+ machine.name for machine in machines))
+
### for m in self._machines:
### if (m.locked and time.time() - m.released_time < 10 and
diff --git a/crosperf/results_cache.py b/crosperf/results_cache.py
index 135c7687..977e3e22 100644
--- a/crosperf/results_cache.py
+++ b/crosperf/results_cache.py
@@ -8,6 +8,7 @@
from __future__ import division
from __future__ import print_function
+import collections
import glob
import hashlib
import heapq
@@ -73,6 +74,27 @@ class Result(object):
"""Get the list of top commands consuming CPU on the machine."""
return self.top_cmds
+ def FormatStringTop5(self):
+ """Get formatted top5 string.
+
+ Get the formatted string with top5 commands consuming CPU on DUT machine.
+ """
+ format_list = [
+ 'Top 5 commands with highest CPU usage:',
+ # Header.
+ '%20s %9s %6s %s' % ('COMMAND', 'AVG CPU%', 'COUNT', 'HIGHEST 5'),
+ '-' * 50,
+ ]
+ if self.top_cmds:
+ for topcmd in self.top_cmds[:5]:
+ print_line = '%20s %9.2f %6s %s' % (topcmd['cmd'], topcmd['cpu_avg'],
+ topcmd['count'], topcmd['top5'])
+ format_list.append(print_line)
+ else:
+ format_list.append('[NO DATA FROM THE TOP LOG]')
+ format_list.append('-' * 50)
+ return '\n'.join(format_list)
+
def CopyFilesTo(self, dest_dir, files_to_copy):
file_index = 0
for file_to_copy in files_to_copy:
@@ -572,45 +594,54 @@ class Result(object):
if snapshot:
snapshots.append(snapshot)
- # Threshold of CPU usage when Chrome is busy, i.e. benchmark is running.
- # FIXME(denik): 70 is just a guess and needs empirical evidence.
- # (It does not need to be configurable.)
- chrome_high_cpu_load = 70
+ # Define threshold of CPU usage when Chrome is busy, i.e. benchmark is
+ # running.
+ # Ideally it should be 100% but it will be hardly reachable with 1 core.
+ # Statistics on DUT with 2-6 cores shows that chrome load of 100%, 95% and
+ # 90% equally occurs in 72-74% of all top log snapshots.
+ # Further decreasing of load threshold leads to a shifting percent of
+ # "high load" snapshots which might include snapshots when benchmark is
+ # not running.
+ # On 1-core DUT 90% chrome cpu load occurs in 55%, 95% in 33% and 100% in 2%
+ # of snapshots accordingly.
+ CHROME_HIGH_CPU_LOAD = 90
# Number of snapshots where chrome is heavily used.
- active_snapshots = 0
+ high_load_snapshots = 0
# Total CPU use per process in ALL active snapshots.
- cmd_total_cpu_use = {}
+ cmd_total_cpu_use = collections.defaultdict(float)
# Top CPU usages per command.
- cmd_top5_cpu_use = {}
+ cmd_top5_cpu_use = collections.defaultdict(list)
# List of Top Commands to be returned.
topcmds = []
for snapshot_processes in snapshots:
- if any(chrome_proc['cpu_use'] > chrome_high_cpu_load
- for chrome_proc in snapshot_processes
- if chrome_proc['cmd'] == 'chrome'):
- # This is a snapshot where at least one chrome command
- # has CPU usage above the threshold.
- active_snapshots += 1
- for process in snapshot_processes:
- cmd = process['cmd']
- cpu_use = process['cpu_use']
-
+ # CPU usage per command in one snapshot.
+ cmd_cpu_use_per_snapshot = collections.defaultdict(float)
+ for process in snapshot_processes:
+ cmd = process['cmd']
+ cpu_use = process['cpu_use']
+
+ # Collect CPU usage per command.
+ cmd_cpu_use_per_snapshot[cmd] += cpu_use
+
+ if cmd_cpu_use_per_snapshot.setdefault('chrome',
+ 0.0) > CHROME_HIGH_CPU_LOAD:
+ # Combined CPU usage of "chrome" command exceeds "High load" threshold
+ # which means DUT is busy running a benchmark.
+ high_load_snapshots += 1
+ for cmd, cpu_use in cmd_cpu_use_per_snapshot.items():
# Update total CPU usage.
- total_cpu_use = cmd_total_cpu_use.setdefault(cmd, 0.0)
- cmd_total_cpu_use[cmd] = total_cpu_use + cpu_use
+ cmd_total_cpu_use[cmd] += cpu_use
- # Add cpu_use into command top cpu usages, sorted in descending
- # order.
- top5_list = cmd_top5_cpu_use.setdefault(cmd, [])
- heapq.heappush(top5_list, cpu_use)
+ # Add cpu_use into command top cpu usages, sorted in descending order.
+ heapq.heappush(cmd_top5_cpu_use[cmd], round(cpu_use, 1))
for consumer, usage in sorted(
cmd_total_cpu_use.items(), key=lambda x: x[1], reverse=True):
# Iterate through commands by descending order of total CPU usage.
topcmd = {
'cmd': consumer,
- 'cpu_avg': usage / active_snapshots,
+ 'cpu_avg': usage / high_load_snapshots,
'count': len(cmd_top5_cpu_use[consumer]),
'top5': heapq.nlargest(5, cmd_top5_cpu_use[consumer]),
}
diff --git a/crosperf/results_cache_unittest.py b/crosperf/results_cache_unittest.py
index 28ce599c..7ce04221 100755
--- a/crosperf/results_cache_unittest.py
+++ b/crosperf/results_cache_unittest.py
@@ -226,8 +226,8 @@ TOP_DATA = [
{
'cmd': 'chrome',
'cpu_avg': 124.75,
- 'count': 4,
- 'top5': [122.8, 107.9, 17.8, 1.0],
+ 'count': 2,
+ 'top5': [125.7, 123.8],
},
{
'cmd': 'irq/cros-ec',
@@ -470,7 +470,7 @@ class ResultTest(unittest.TestCase):
mock_copyfiles.return_value = 0
- #test 1. dest_dir exists; CopyFiles returns 0.
+ # test 1. dest_dir exists; CopyFiles returns 0.
mock_isdir.return_value = True
self.result.CopyFilesTo(dest_dir, files)
self.assertEqual(mock_runcmd.call_count, 0)
@@ -484,7 +484,7 @@ class ResultTest(unittest.TestCase):
mock_runcmd.reset_mock()
mock_copyfiles.reset_mock()
- #test 2. dest_dir does not exist; CopyFiles returns 0.
+ # test 2. dest_dir does not exist; CopyFiles returns 0.
mock_isdir.return_value = False
self.result.CopyFilesTo(dest_dir, files)
self.assertEqual(mock_runcmd.call_count, 3)
@@ -495,7 +495,7 @@ class ResultTest(unittest.TestCase):
mock_runcmd.call_args_list[2])
self.assertEqual(mock_runcmd.call_args_list[0][0], ('mkdir -p /tmp/test',))
- #test 3. CopyFiles returns 1 (fails).
+ # test 3. CopyFiles returns 1 (fails).
mock_copyfiles.return_value = 1
self.assertRaises(Exception, self.result.CopyFilesTo, dest_dir, files)
@@ -990,6 +990,75 @@ class ResultTest(unittest.TestCase):
mo.assert_has_calls(calls)
self.assertEqual(topcalls, [])
+ def test_format_string_top5_cmds(self):
+ """Test formatted string with top5 commands."""
+ self.result.top_cmds = [
+ {
+ 'cmd': 'chrome',
+ 'cpu_avg': 119.753453465,
+ 'count': 44444,
+ 'top5': [222.8, 217.9, 217.8, 191.0, 189.9],
+ },
+ {
+ 'cmd': 'irq/230-cros-ec',
+ 'cpu_avg': 10.000000000000001,
+ 'count': 1000,
+ 'top5': [11.5, 11.4, 11.3, 11.2, 11.1],
+ },
+ {
+ 'cmd': 'powerd',
+ 'cpu_avg': 2.0,
+ 'count': 2,
+ 'top5': [3.0, 1.0]
+ },
+ {
+ 'cmd': 'cmd1',
+ 'cpu_avg': 1.0,
+ 'count': 1,
+ 'top5': [1.0],
+ },
+ {
+ 'cmd': 'cmd2',
+ 'cpu_avg': 1.0,
+ 'count': 1,
+ 'top5': [1.0],
+ },
+ {
+ 'cmd': 'not_for_print',
+ 'cpu_avg': 1.0,
+ 'count': 1,
+ 'top5': [1.0],
+ },
+ ]
+ form_str = self.result.FormatStringTop5()
+ self.assertEqual(
+ form_str, '\n'.join([
+ 'Top 5 commands with highest CPU usage:',
+ ' COMMAND AVG CPU% COUNT HIGHEST 5',
+ '-' * 50,
+ ' chrome 119.75 44444 '
+ '[222.8, 217.9, 217.8, 191.0, 189.9]',
+ ' irq/230-cros-ec 10.00 1000 '
+ '[11.5, 11.4, 11.3, 11.2, 11.1]',
+ ' powerd 2.00 2 [3.0, 1.0]',
+ ' cmd1 1.00 1 [1.0]',
+ ' cmd2 1.00 1 [1.0]',
+ '-' * 50,
+ ]))
+
+ def test_format_string_top5_calls_no_data(self):
+ """Test formatted string of top5 with no data."""
+ self.result.top_cmds = []
+ form_str = self.result.FormatStringTop5()
+ self.assertEqual(
+ form_str, '\n'.join([
+ 'Top 5 commands with highest CPU usage:',
+ ' COMMAND AVG CPU% COUNT HIGHEST 5',
+ '-' * 50,
+ '[NO DATA FROM THE TOP LOG]',
+ '-' * 50,
+ ]))
+
@mock.patch.object(misc, 'GetInsideChrootPath')
@mock.patch.object(command_executer.CommandExecuter, 'ChrootRunCommand')
def test_generate_perf_report_files(self, mock_chrootruncmd, mock_getpath):
diff --git a/crosperf/results_report.py b/crosperf/results_report.py
index 5f49872b..edbdd4d7 100644
--- a/crosperf/results_report.py
+++ b/crosperf/results_report.py
@@ -371,6 +371,11 @@ class TextResultsReport(ResultsReport):
cell_table = TableFormatter(table, columns).GetCellTable('status')
return [cell_table]
+ def _GetTotalWaitCooldownTime(self):
+ """Get cooldown wait time in seconds from experiment benchmark runs."""
+ return sum(br.suite_runner.GetCooldownWaitTime()
+ for br in self.experiment.benchmark_runs)
+
def GetReport(self):
"""Generate the report for email and console."""
output_type = 'EMAIL' if self.email else 'CONSOLE'
@@ -406,6 +411,9 @@ class TextResultsReport(ResultsReport):
cpu_info = experiment.machine_manager.GetAllCPUInfo(experiment.labels)
sections.append(self._MakeSection('CPUInfo', cpu_info))
+ waittime_str = '%d min' % (self._GetTotalWaitCooldownTime() // 60)
+ sections.append(self._MakeSection('Cooldown wait time', waittime_str))
+
return '\n'.join(sections)
@@ -511,8 +519,11 @@ def ParseStandardPerfReport(report_data):
"""
# This function fails silently on its if it's handed a string (as opposed to a
# list of lines). So, auto-split if we do happen to get a string.
- if isinstance(report_data, basestring):
+ if isinstance(report_data, str):
report_data = report_data.splitlines()
+ # When switching to python3 catch the case when bytes are passed.
+ elif isinstance(report_data, bytes):
+ raise TypeError()
# Samples: N{K,M,G} of event 'event-name'
samples_regex = re.compile(r"#\s+Samples: \d+\S? of event '([^']+)'")
diff --git a/crosperf/results_report_unittest.py b/crosperf/results_report_unittest.py
index 61e2a7c2..ae51fda6 100755
--- a/crosperf/results_report_unittest.py
+++ b/crosperf/results_report_unittest.py
@@ -10,14 +10,14 @@
from __future__ import division
from __future__ import print_function
-from StringIO import StringIO
-
import collections
-import mock
+import io
import os
-import test_flag
import unittest
+import mock
+import test_flag
+
from benchmark_run import MockBenchmarkRun
from cros_utils import logger
from experiment_factory import ExperimentFactory
@@ -83,7 +83,7 @@ def FakePath(ext):
def MakeMockExperiment(compiler='gcc'):
"""Mocks an experiment using the given compiler."""
- mock_experiment_file = StringIO("""
+ mock_experiment_file = io.BytesIO("""
board: x86-alex
remote: 127.0.0.1
perf_args: record -a -e cycles
@@ -146,6 +146,12 @@ def _InjectSuccesses(experiment, how_many, keyvals, for_benchmark=0,
return experiment
+def _InjectCooldownTime(experiment, cooldown_time):
+ """Inject cooldown wait time in every benchmark run."""
+ for br in experiment.benchmark_runs:
+ br.suite_runner.cooldown_wait_time = cooldown_time
+
+
class TextResultsReportTest(unittest.TestCase):
"""Tests that the output of a text report contains the things we pass in.
@@ -158,11 +164,18 @@ class TextResultsReportTest(unittest.TestCase):
success_keyvals = {'retval': 0, 'machine': 'some bot', 'a_float': 3.96}
experiment = _InjectSuccesses(MakeMockExperiment(), num_success,
success_keyvals)
- text_report = TextResultsReport.FromExperiment(experiment, email=email) \
- .GetReport()
+ # Set 120 sec cooldown time for every benchmark run.
+ cooldown_time = 120
+ _InjectCooldownTime(experiment, cooldown_time)
+ text_report = TextResultsReport.FromExperiment(
+ experiment, email=email).GetReport()
self.assertIn(str(success_keyvals['a_float']), text_report)
self.assertIn(success_keyvals['machine'], text_report)
self.assertIn(MockCrosMachine.CPUINFO_STRING, text_report)
+ self.assertIn('Cooldown wait time', text_report)
+ self.assertIn(
+ '%d min' % (len(experiment.benchmark_runs) * cooldown_time // 60),
+ text_report)
return text_report
def testOutput(self):
@@ -229,7 +242,7 @@ class HTMLResultsReportTest(unittest.TestCase):
_InjectSuccesses(MakeMockExperiment(), num_success, success_keyvals))
self.assertNotIn('no result', output.summary_table)
- #self.assertIn(success_keyvals['machine'], output.summary_table)
+ # self.assertIn(success_keyvals['machine'], output.summary_table)
self.assertIn('a_float', output.summary_table)
self.assertIn(str(success_keyvals['a_float']), output.summary_table)
self.assertIn('a_float', output.full_table)
@@ -418,7 +431,7 @@ class PerfReportParserTest(unittest.TestCase):
}
report_cycles = report['cycles']
self.assertEqual(len(report_cycles), 214)
- for k, v in known_cycles_percentages.iteritems():
+ for k, v in known_cycles_percentages.items():
self.assertIn(k, report_cycles)
self.assertEqual(v, report_cycles[k])
@@ -430,7 +443,7 @@ class PerfReportParserTest(unittest.TestCase):
}
report_instructions = report['instructions']
self.assertEqual(len(report_instructions), 492)
- for k, v in known_instrunctions_percentages.iteritems():
+ for k, v in known_instrunctions_percentages.items():
self.assertIn(k, report_instructions)
self.assertEqual(v, report_instructions[k])
diff --git a/crosperf/schedv2.py b/crosperf/schedv2.py
index 0cc8f746..768d29d8 100644
--- a/crosperf/schedv2.py
+++ b/crosperf/schedv2.py
@@ -243,10 +243,14 @@ class DutWorker(Thread):
if self._kerncmd_update_needed(intel_pstate):
self._update_kerncmd_intel_pstate(intel_pstate)
+ # When calculating cooldown wait time we assume that suite_runner is
+ # never reused so we can sum up the values across all benchmark_runs.
+ # If implementation changes causing the assert below to fail the
+ # calculation should be adjusted accordingly.
+ assert br.suite_runner.GetCooldownWaitTime() == 0
# Execute the br.
self._execute_benchmark_run(br)
total_waittime += br.suite_runner.GetCooldownWaitTime()
- br.suite_runner.ResetCooldownWaitTime()
finally:
self._logger.LogOutput(
'Total wait time for cooldown: %d min' % (total_waittime // 60))
diff --git a/crosperf/settings_factory.py b/crosperf/settings_factory.py
index 1f2693c6..9057703f 100644
--- a/crosperf/settings_factory.py
+++ b/crosperf/settings_factory.py
@@ -202,9 +202,8 @@ class GlobalSettings(Settings):
BooleanField(
'use_file_locks',
default=False,
- description='Whether to use the file locks '
- 'mechanism (deprecated) instead of the AFE '
- 'server lock mechanism.'))
+ description='DEPRECATED: Whether to use the file locks '
+ 'or AFE server lock mechanism.'))
self.AddField(
IntegerField(
'iterations',
@@ -286,8 +285,9 @@ class GlobalSettings(Settings):
'locks_dir',
default='',
description='An alternate directory to use for '
- 'storing/checking machine locks. Using this field '
- 'automatically sets use_file_locks to True.\n'
+ 'storing/checking machine file locks for local machines. '
+ 'By default the file locks directory is '
+ '/google/data/rw/users/mo/mobiletc-prebuild/locks.\n'
'WARNING: If you use your own locks directory, '
'there is no guarantee that someone else might not '
'hold a lock on the same machine in a different '
diff --git a/lock_machine_test.py b/file_lock_machine_test.py
index 0ffe094d..340f9149 100644..100755
--- a/lock_machine_test.py
+++ b/file_lock_machine_test.py
@@ -1,4 +1,10 @@
-# Copyright 2010 Google Inc. All Rights Reserved.
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
"""lock_machine.py related unit-tests.
MachineManagerTest tests MachineManager.
@@ -16,7 +22,7 @@ import file_lock_machine
def LockAndSleep(machine):
- file_lock_machine.Machine(machine, auto=True).Lock(exclusive=True)
+ file_lock_machine.Machine(machine, '/tmp', auto=True).Lock(exclusive=True)
time.sleep(1)
@@ -27,12 +33,12 @@ class MachineTest(unittest.TestCase):
pass
def testRepeatedUnlock(self):
- mach = file_lock_machine.Machine('qqqraymes.mtv')
+ mach = file_lock_machine.Machine('qqqraymes.mtv', '/tmp')
for _ in range(10):
- self.assertFalse(mach.Unlock())
- mach = file_lock_machine.Machine('qqqraymes.mtv', auto=True)
+ self.assertTrue(mach.Unlock())
+ mach = file_lock_machine.Machine('qqqraymes.mtv', '/tmp', auto=True)
for _ in range(10):
- self.assertFalse(mach.Unlock())
+ self.assertTrue(mach.Unlock())
def testLockUnlock(self):
mach = file_lock_machine.Machine('otter.mtv', '/tmp')
@@ -46,7 +52,7 @@ class MachineTest(unittest.TestCase):
self.assertTrue(mach.Unlock(exclusive=True))
def testSharedLock(self):
- mach = file_lock_machine.Machine('chrotomation.mtv')
+ mach = file_lock_machine.Machine('chrotomation.mtv', '/tmp')
for _ in range(10):
self.assertTrue(mach.Lock(exclusive=False))
for _ in range(10):
@@ -54,7 +60,7 @@ class MachineTest(unittest.TestCase):
self.assertTrue(mach.Lock(exclusive=True))
self.assertTrue(mach.Unlock(exclusive=True))
- mach = file_lock_machine.Machine('chrotomation.mtv', auto=True)
+ mach = file_lock_machine.Machine('chrotomation.mtv', '/tmp', auto=True)
for _ in range(10):
self.assertTrue(mach.Lock(exclusive=False))
for _ in range(10):
@@ -63,14 +69,14 @@ class MachineTest(unittest.TestCase):
self.assertTrue(mach.Unlock(exclusive=True))
def testExclusiveLock(self):
- mach = file_lock_machine.Machine('atree.mtv')
+ mach = file_lock_machine.Machine('atree.mtv', '/tmp')
self.assertTrue(mach.Lock(exclusive=True))
for _ in range(10):
self.assertFalse(mach.Lock(exclusive=True))
self.assertFalse(mach.Lock(exclusive=False))
self.assertTrue(mach.Unlock(exclusive=True))
- mach = file_lock_machine.Machine('atree.mtv', auto=True)
+ mach = file_lock_machine.Machine('atree.mtv', '/tmp', auto=True)
self.assertTrue(mach.Lock(exclusive=True))
for _ in range(10):
self.assertFalse(mach.Lock(exclusive=True))
@@ -78,20 +84,20 @@ class MachineTest(unittest.TestCase):
self.assertTrue(mach.Unlock(exclusive=True))
def testExclusiveState(self):
- mach = file_lock_machine.Machine('testExclusiveState')
+ mach = file_lock_machine.Machine('testExclusiveState', '/tmp')
self.assertTrue(mach.Lock(exclusive=True))
for _ in range(10):
self.assertFalse(mach.Lock(exclusive=False))
self.assertTrue(mach.Unlock(exclusive=True))
- mach = file_lock_machine.Machine('testExclusiveState', auto=True)
+ mach = file_lock_machine.Machine('testExclusiveState', '/tmp', auto=True)
self.assertTrue(mach.Lock(exclusive=True))
for _ in range(10):
self.assertFalse(mach.Lock(exclusive=False))
self.assertTrue(mach.Unlock(exclusive=True))
def testAutoLockGone(self):
- mach = file_lock_machine.Machine('lockgone', auto=True)
+ mach = file_lock_machine.Machine('lockgone', '/tmp', auto=True)
p = Process(target=LockAndSleep, args=('lockgone',))
p.start()
time.sleep(1.1)
@@ -99,7 +105,7 @@ class MachineTest(unittest.TestCase):
self.assertTrue(mach.Lock(exclusive=True))
def testAutoLockFromOther(self):
- mach = file_lock_machine.Machine('other_lock', auto=True)
+ mach = file_lock_machine.Machine('other_lock', '/tmp', auto=True)
p = Process(target=LockAndSleep, args=('other_lock',))
p.start()
time.sleep(0.5)
@@ -109,7 +115,7 @@ class MachineTest(unittest.TestCase):
self.assertTrue(mach.Lock(exclusive=True))
def testUnlockByOthers(self):
- mach = file_lock_machine.Machine('other_unlock', auto=True)
+ mach = file_lock_machine.Machine('other_unlock', '/tmp', auto=True)
p = Process(target=LockAndSleep, args=('other_unlock',))
p.start()
time.sleep(0.5)
diff --git a/afe_lock_machine.py b/lock_machine.py
index 5f3c7fc6..40c7d8fd 100755
--- a/afe_lock_machine.py
+++ b/lock_machine.py
@@ -22,28 +22,28 @@ from cros_utils import logger
from cros_utils import machines
-class AFELockException(Exception):
+class LockException(Exception):
"""Base class for exceptions in this module."""
-class MachineNotPingable(AFELockException):
+class MachineNotPingable(LockException):
"""Raised when machine does not respond to ping."""
-class LockingError(AFELockException):
+class LockingError(LockException):
"""Raised when server fails to lock/unlock machine as requested."""
-class DontOwnLock(AFELockException):
+class DontOwnLock(LockException):
"""Raised when user attmepts to unlock machine locked by someone else."""
# This should not be raised if the user specified '--force'
-class NoAFEServer(AFELockException):
+class NoAFEServer(LockException):
"""Raised when cannot find/access the autotest server."""
-class AFEAccessError(AFELockException):
+class AFEAccessError(LockException):
"""Raised when cannot get information about lab machine from lab server."""
@@ -54,8 +54,8 @@ class MachineType(enum.Enum):
SKYLAB = 'skylab'
-class AFELockManager(object):
- """Class for locking/unlocking machines vie Autotest Front End servers.
+class LockManager(object):
+ """Class for locking/unlocking machines vie three different modes.
This class contains methods for checking the locked status of machines,
and for changing the locked status. It handles HW lab machines (both AFE
@@ -82,7 +82,7 @@ class AFELockManager(object):
chromeos_root,
locks_dir='',
log=None):
- """Initializes an AFELockManager object.
+ """Initializes an LockManager object.
Args:
remotes: A list of machine names or ip addresses to be managed. Names
@@ -225,12 +225,12 @@ class AFELockManager(object):
"""Gets and prints the current status for a list of machines.
Prints out the current status for all of the machines in the current
- AFELockManager's list of machines (set when the object is initialized).
+ LockManager's list of machines (set when the object is initialized).
Args:
machine_states: A dictionary of the current state of every machine in
- the current AFELockManager's list of machines. Normally obtained by
- calling AFELockManager::GetMachineStates.
+ the current LockManager's list of machines. Normally obtained by
+ calling LockManager::GetMachineStates.
"""
self.PrintStatusHeader()
for m in machine_states:
@@ -368,8 +368,8 @@ class AFELockManager(object):
Args:
machine_states: A dictionary of the current state of every machine in
- the current AFELockManager's list of machines. Normally obtained by
- calling AFELockManager::GetMachineStates.
+ the current LockManager's list of machines. Normally obtained by
+ calling LockManager::GetMachineStates.
cmd: The user-requested action for the machines: 'lock' or 'unlock'.
Raises:
@@ -406,7 +406,7 @@ class AFELockManager(object):
an exception, unless the requested command is 'add'.
Returns:
- A dictionary of machine states for all the machines in the AFELockManager
+ A dictionary of machine states for all the machines in the LockManager
object.
Raises:
@@ -587,8 +587,7 @@ def Main(argv):
if options.remote:
machine_list = options.remote.split()
- lock_manager = AFELockManager(machine_list, options.force,
- options.chromeos_root)
+ lock_manager = LockManager(machine_list, options.force, options.chromeos_root)
machine_states = lock_manager.GetMachineStates(cmd=options.cmd)
cmd = options.cmd