Collect profiles for Clang
Bug: http://b/37574207
Add support in build.py for building an instrumented stage2. Add flag
to test_compiler.py that builds Android host and target modules using an
instrumented Clang, and merges the collected profiles.
Test: toolchain/llvm/android/build.py --build-instrumented
toolchain/llvm/android/test_compiler.py --build-only --target \
aosp_marlin-eng ./ --generate-clang-profile
Change-Id: I1b33d1b5edb5cd53fda811c4cc4832ac2adfa1f4
diff --git a/test_compiler.py b/test_compiler.py
index 0d3b20f..31f1055 100755
--- a/test_compiler.py
+++ b/test_compiler.py
@@ -34,6 +34,25 @@
'-clang-analyzer-alpha*')
+class ClangProfileHandler(object):
+ def __init__(self):
+ self.out_dir = os.environ.get('OUT_DIR', utils.android_path('out'))
+ self.profiles_dir = os.path.join(self.out_dir, 'clang-profiles')
+ self.profiles_format = os.path.join(self.profiles_dir, '%4m.profraw')
+
+ def getProfileFileEnvVar(self):
+ return ('LLVM_PROFILE_FILE', self.profiles_format)
+
+ def mergeProfiles(self):
+ profdata = os.path.join(self.out_dir, 'stage1-install', 'bin',
+ 'llvm-profdata')
+ dist_dir = os.environ.get('DIST_DIR', utils.android_path('out'))
+ out_file = os.path.join(dist_dir, 'clang.profdata')
+
+ cmd = [profdata, 'merge', '-o', out_file, self.profiles_dir]
+ subprocess.check_call(cmd)
+
+
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('android_path', help='Android source directory.')
@@ -69,6 +88,11 @@
redirect_stderr_group.add_argument(
'--no-redirect-stderr', action='store_false',
dest='redirect_stderr', help='Do not redirect clang stderr.')
+
+ parser.add_argument('--generate-clang-profile', action='store_true',
+ default=False, dest='profile',
+ help='Build instrumented compiler and gather profiles')
+
return parser.parse_args()
@@ -97,7 +121,7 @@
def build_target(android_base, clang_version, target, max_jobs,
- redirect_stderr, with_tidy):
+ redirect_stderr, with_tidy, profiler):
jobs = '-j{}'.format(
max(1, min(max_jobs, multiprocessing.cpu_count())))
result = True
@@ -144,8 +168,17 @@
env['DEFAULT_GLOBAL_TIDY_CHECKS'] = ','.join(DEFAULT_TIDY_CHECKS)
- print('Start building %s.' % target)
- subprocess.check_call(['/bin/bash', '-c', 'make ' + jobs + ' dist'],
+ modules = ['dist']
+ if profiler is not None:
+ # Build only a subset of targets and collect profiles
+ modules = ['libc', 'libLLVM-host64']
+
+ key, val = profiler.getProfileFileEnvVar()
+ env[key] = val
+
+ modules = ' '.join(modules)
+ print('Start building target %s and modules %s.' % (target, modules))
+ subprocess.check_call(['/bin/bash', '-c', 'make ' + jobs + ' ' + modules],
cwd=android_base, env=env)
@@ -207,11 +240,15 @@
shutil.copy2(wrapper_path, clang_tidy_path)
-def build_clang():
+def build_clang(instrumented=False):
stage1_install = utils.android_path('out', 'stage1-install')
stage2_install = utils.android_path('out', 'stage2-install')
- build.build_stage1(stage1_install)
- build.build_stage2(stage1_install, stage2_install, build.STAGE2_TARGETS)
+
+ # LLVM tool llvm-profdata from stage1 is needed to merge the collected
+ # profiles
+ build.build_stage1(stage1_install, build_llvm_tools=True)
+ build.build_stage2(stage1_install, stage2_install, build.STAGE2_TARGETS,
+ use_lld=False, build_instrumented=instrumented)
build.build_runtimes(stage2_install)
version = build.extract_clang_version(stage2_install)
return stage2_install, version
@@ -220,7 +257,7 @@
def main():
args = parse_args()
if args.clang_path is None:
- clang_path, clang_version = build_clang()
+ clang_path, clang_version = build_clang(instrumented=args.profile)
else:
clang_path = args.clang_path
clang_version = build.extract_clang_version(clang_path)
@@ -228,10 +265,17 @@
link_clang(args.android_path, clang_path)
if args.build_only:
+ profiler = ClangProfileHandler() if args.profile else None
+
targets = [args.target] if args.target else TARGETS
for target in targets:
build_target(args.android_path, clang_version, target,
- args.jobs, args.redirect_stderr, args.with_tidy)
+ args.jobs, args.redirect_stderr, args.with_tidy,
+ profiler)
+
+ if profiler is not None:
+ profiler.mergeProfiles()
+
else:
devices = get_connected_device_list()
if len(devices) == 0: