aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorGravatar borenet <borenet@google.com>2015-07-06 07:43:58 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-07-06 07:43:58 -0700
commit11271fe3a40280a233c9a1c583497844d067088e (patch)
treefca320949197009cb1f9ad1f297125379c01b80e /tools
parentf0ebdd7463275f8183df94f1f8a8ec3761652e7c (diff)
Add scripts for running LLVM coverage
Diffstat (limited to 'tools')
-rwxr-xr-xtools/llvm_coverage.sh16
-rwxr-xr-xtools/llvm_coverage_build28
-rwxr-xr-xtools/llvm_coverage_run.py107
3 files changed, 151 insertions, 0 deletions
diff --git a/tools/llvm_coverage.sh b/tools/llvm_coverage.sh
new file mode 100755
index 0000000000..59d99e7a27
--- /dev/null
+++ b/tools/llvm_coverage.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Copyright (c) 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Run from Skia repo like this:
+# $ tools/llvm_coverage.sh dm
+# or
+# $ tools/llvm_coverage.sh nanobench
+
+set -x
+set -e
+
+make clean
+tools/llvm_coverage_build $1
+python tools/llvm_coverage_run.py $@
diff --git a/tools/llvm_coverage_build b/tools/llvm_coverage_build
new file mode 100755
index 0000000000..f769d6c1ad
--- /dev/null
+++ b/tools/llvm_coverage_build
@@ -0,0 +1,28 @@
+#!/bin/bash
+# Copyright (c) 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Build Skia for use with LLVM's coverage tools.
+#
+# $ tools/llvm_coverage_build [any other flags to pass to make...]
+#
+# This script assumes the use of Clang >=3.6.
+#
+set -e
+
+export CC="$(which clang)"
+export CXX="$(which clang++)"
+
+if [[ -z "${CC}" ]] || [[ -z "${CXX}" ]]; then
+ echo "Couldn't find Clang on this machine!"
+ exit 1
+fi
+
+echo "CC=$CC"
+echo "CXX=$CXX"
+$CC --version
+
+export GYP_DEFINES="skia_warnings_as_errors=0 skia_clang_build=1"
+export BUILDTYPE=Coverage
+make $@
diff --git a/tools/llvm_coverage_run.py b/tools/llvm_coverage_run.py
new file mode 100755
index 0000000000..902b1c6b04
--- /dev/null
+++ b/tools/llvm_coverage_run.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# Copyright (c) 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+"""Run the given command through LLVM's coverage tools."""
+
+
+import argparse
+import json
+import os
+import shlex
+import subprocess
+import sys
+
+
+BUILDTYPE = 'Coverage'
+OUT_DIR = os.path.realpath(os.path.join('out', BUILDTYPE))
+PROFILE_DATA = 'default.profraw'
+PROFILE_DATA_MERGED = 'prof_merged'
+
+
+def _fix_filename(filename):
+ """Return a filename which we can use to identify the file.
+
+ The file paths printed by llvm-cov take the form:
+
+ /path/to/repo/out/dir/../../src/filename.cpp
+
+ And then they're truncated to 22 characters with leading ellipses:
+
+ ...../../src/filename.cpp
+
+ This makes it really tough to determine whether the file actually belongs in
+ the Skia repo. This function strips out the leading junk so that, if the file
+ exists in the repo, the returned string matches the end of some relative path
+ in the repo. This doesn't guarantee correctness, but it's about as close as
+ we can get.
+ """
+ return filename.split('..')[-1].lstrip('./')
+
+
+def _filter_results(results):
+ """Filter out any results for files not in the Skia repo.
+
+ We run through the list of checked-in files and determine whether each file
+ belongs in the repo. Unfortunately, llvm-cov leaves us with fragments of the
+ file paths, so we can't guarantee accuracy. See the docstring for
+ _fix_filename for more details.
+ """
+ all_files = subprocess.check_output(['git', 'ls-files']).splitlines()
+ filtered = []
+ for percent, filename in results:
+ new_file = _fix_filename(filename)
+ matched = []
+ for f in all_files:
+ if f.endswith(new_file):
+ matched.append(f)
+ if len(matched) == 1:
+ filtered.append((percent, matched[0]))
+ elif len(matched) > 1:
+ print >> sys.stderr, ('WARNING: multiple matches for %s; skipping:\n\t%s'
+ % (new_file, '\n\t'.join(matched)))
+ print 'Filtered out %d files.' % (len(results) - len(filtered))
+ return filtered
+
+
+def run_coverage(cmd):
+ """Run the given command and return per-file coverage data.
+
+ Assumes that the binary has been built using llvm_coverage_build and that
+ LLVM 3.6 or newer is installed.
+ """
+ binary_path = os.path.join(OUT_DIR, cmd[0])
+ subprocess.call([binary_path] + cmd[1:])
+ try:
+ subprocess.check_call(
+ ['llvm-profdata', 'merge', PROFILE_DATA,
+ '-output=%s' % PROFILE_DATA_MERGED])
+ finally:
+ os.remove(PROFILE_DATA)
+ try:
+ report = subprocess.check_output(
+ ['llvm-cov', 'report', '-instr-profile', PROFILE_DATA_MERGED,
+ binary_path])
+ finally:
+ os.remove(PROFILE_DATA_MERGED)
+ results = []
+ for line in report.splitlines()[2:-2]:
+ filename, _, _, cover, _, _ = shlex.split(line)
+ percent = float(cover.split('%')[0])
+ results.append((percent, filename))
+ results = _filter_results(results)
+ results.sort()
+ return results
+
+
+def main():
+ res = run_coverage(sys.argv[1:])
+ print '% Covered\tFilename'
+ for percent, f in res:
+ print '%f\t%s' % (percent, f)
+
+
+if __name__ == '__main__':
+ main()