Commit 6694c0e5 authored by kmarshall's avatar kmarshall Committed by Commit bot

Remove dependency on scan-build wrapper script for analysis builds.

This CL replaces the Clang 'c++-analyzer' Perl script with logic added to the Python wrapper script. This gives us the hooks we need to run analysis builds with "gomacc" (necessary for distributed builds) and gives us a convenient place to specify analyzer config flags which aren't exposed by scan-build.

Adds a warning suppression rule which prevents the analyzer from raising false positives from stdlib code (code w/namespace "std").

Other changes:
* Modify Goma portions of the GCC toolchain GNI to build paths to
  the Clang static analyzer.
* Create an exception for ASM tool invocations, which don't play
  well with the analysis command line flags.
* Removed 'scan-build' DEP.

BUG=687245
R=thakis@chromium.org,wez@chromium.org

Review-Url: https://codereview.chromium.org/2667853004
Cr-Commit-Position: refs/heads/master@{#456136}
parent c58e8f82
...@@ -106,10 +106,6 @@ vars = { ...@@ -106,10 +106,6 @@ vars = {
# and whatever else without interference from each other. # and whatever else without interference from each other.
'devtools_node_modules_revision': '6226d6cd80aaf2e5295ed460cf73ef6a582e4d78', 'devtools_node_modules_revision': '6226d6cd80aaf2e5295ed460cf73ef6a582e4d78',
# Three lines of non-changing comments so that # Three lines of non-changing comments so that
# the commit queue can handle CLs rolling libFuzzer
# and whatever else without interference from each other.
'scanbuild_revision': '15bd7ca2934162c51654ddffc52933e45f95e7ef',
# Three lines of non-changing comments so that
# the commit queue can handle CLs rolling libprotobuf-mutator # the commit queue can handle CLs rolling libprotobuf-mutator
# and whatever else without interference from each other. # and whatever else without interference from each other.
'libprotobuf-mutator': '2d609dfa69f670f6a1daf80eaa2d538f96120b2d', 'libprotobuf-mutator': '2d609dfa69f670f6a1daf80eaa2d538f96120b2d',
...@@ -314,9 +310,6 @@ deps = { ...@@ -314,9 +310,6 @@ deps = {
'src/third_party/visualmetrics/src': 'src/third_party/visualmetrics/src':
Var('chromium_git') + '/external/github.com/WPO-Foundation/visualmetrics.git' + '@' + '1edde9d2fe203229c895b648fdec355917200ad6', Var('chromium_git') + '/external/github.com/WPO-Foundation/visualmetrics.git' + '@' + '1edde9d2fe203229c895b648fdec355917200ad6',
'src/third_party/scan-build/src':
Var('chromium_git') + '/chromium/llvm-project/cfe/tools/scan-build.git' + '@' + Var('scanbuild_revision'),
} }
......
#!/usr/bin/env python #!/usr/bin/env python
# Copyright 2016 The Chromium Authors. All rights reserved. # Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
"""Invokes the Clang static analysis command using arguments provided on the """Adds an analysis build step to invocations of the Clang C/C++ compiler.
command line.
Usage: clang_static_analyzer_wrapper.py <compiler> [args...]
""" """
import argparse import argparse
import fnmatch import fnmatch
import itertools
import os import os
import shutil
import sys import sys
import tempfile
import wrapper_utils import wrapper_utils
# Flags used to enable analysis for Clang invocations.
analyzer_enable_flags = [
'--analyze',
'-fdiagnostics-show-option',
]
# Flags used to configure the analyzer's behavior.
analyzer_option_flags = [
'-analyzer-checker=cplusplus',
'-analyzer-opt-analyze-nested-blocks',
'-analyzer-eagerly-assume',
'-analyzer-output=text',
'-analyzer-config',
'suppress-c++-stdlib=true',
# List of checkers to execute.
# The full list of checkers can be found at
# https://clang-analyzer.llvm.org/available_checks.html.
'-analyzer-checker=core',
'-analyzer-checker=unix',
'-analyzer-checker=deadcode',
]
def main(): def main():
parser = argparse.ArgumentParser(description=__doc__) args = sys.argv[1:]
parser.add_argument('--clang-cc-path', assert args
help='Path to the clang compiler.',
metavar='PATH') # Build the object file and proceed with analysis if it is buildable.
parser.add_argument('--clang-cxx-path', returncode, stderr = wrapper_utils.CaptureCommandStderr(
help='Path to the clang++ compiler', wrapper_utils.CommandToRun(args))
metavar='PATH') sys.stderr.write(stderr)
parser.add_argument('--analyzer', if returncode != 0:
help='Path to the language-specific Clang analysis tool.',
required=True,
metavar='PATH')
args, compile_args = parser.parse_known_args()
# Check that only one of --clang-cc-path or --clang-cxx-path are set.
assert ((args.clang_cc_path != None) != (args.clang_cxx_path != None))
is_cxx = args.clang_cxx_path != None
env = os.environ
env['CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE'] = '0'
env['CCC_ANALYZER_OUTPUT_FORMAT'] = 'text'
clang_path = args.clang_cxx_path or args.clang_cc_path
if is_cxx:
env['CCC_CXX'] = clang_path
env['CLANG_CXX'] = clang_path
else:
env['CCC_CC'] = clang_path
env['CLANG'] = clang_path
# TODO(kmarshall): Place the summarized output in a useful directory.
temp_dir = tempfile.mkdtemp()
try:
env['CCC_ANALYZER_HTML'] = temp_dir
returncode, stderr = wrapper_utils.CaptureCommandStderr(
wrapper_utils.CommandToRun([args.analyzer] + compile_args), env)
sys.stderr.write(stderr)
return returncode return returncode
finally:
shutil.rmtree(temp_dir)
if __name__ == "__main__": # Now run the analyzer.
# Interleave 'analyzer_option_flags' flags w/'-Xanalyzer' so that Clang
# passes them to the analysis tool.
# e.g. ['-analyzer-foo', '-analyzer-bar'] => ['-Xanalyzer', '-analyzer-foo',
# '-Xanalyzer', '-analyzer-bar']
interleaved_analyzer_flags = list(sum(zip(
['-Xanalyzer'] * len(analyzer_option_flags),
analyzer_option_flags), ()))
returncode, stderr = wrapper_utils.CaptureCommandStderr(
wrapper_utils.CommandToRun(args + analyzer_enable_flags +
interleaved_analyzer_flags))
sys.stderr.write(stderr)
if returncode != 0:
sys.stderr.write(
"""WARNING! The Clang static analyzer exited with error code %d.
Please share the error details in crbug.com/695243 if this looks like
a new regression.\n""" % (returncode))
return 0
if __name__ == '__main__':
sys.exit(main()) sys.exit(main())
...@@ -12,6 +12,13 @@ import("//build/toolchain/clang_static_analyzer.gni") ...@@ -12,6 +12,13 @@ import("//build/toolchain/clang_static_analyzer.gni")
import("//build/toolchain/goma.gni") import("//build/toolchain/goma.gni")
import("//build/toolchain/toolchain.gni") import("//build/toolchain/toolchain.gni")
# Path to the Clang static analysis wrapper script.
# REVIEWERS: can you suggest a better location for this?
# GN is really picky about dead stores of variables except at the global scope.
analyzer_wrapper =
rebase_path("//build/toolchain/clang_static_analyzer_wrapper.py",
root_build_dir)
# This template defines a toolchain for something that works like gcc # This template defines a toolchain for something that works like gcc
# (including clang). # (including clang).
# #
...@@ -126,22 +133,49 @@ template("gcc_toolchain") { ...@@ -126,22 +133,49 @@ template("gcc_toolchain") {
} else { } else {
toolchain_cc_wrapper = cc_wrapper toolchain_cc_wrapper = cc_wrapper
} }
assert(!(toolchain_cc_wrapper != "" && toolchain_uses_goma),
"Goma and cc_wrapper can't be used together.")
# Compute the compiler prefix. # When the invoker has explicitly overridden use_goma or cc_wrapper in the
# toolchain args, use those values, otherwise default to the global one.
# This works because the only reasonable override that toolchains might
# supply for these values are to force-disable them.
if (toolchain_uses_goma) { if (toolchain_uses_goma) {
assert(toolchain_cc_wrapper == "", goma_path = "$goma_dir/gomacc"
"Goma and cc_wrapper can't be used together.")
compiler_prefix = "$goma_dir/gomacc " # Use the static analysis script if static analysis is turned on
} else if (toolchain_cc_wrapper != "") { # AND the tool has not opted out by setting
compiler_prefix = toolchain_cc_wrapper + " " # 'is_clang_static_analysis_supported' to false.
if (is_clang && use_clang_static_analyzer &&
(!defined(invoker.is_clang_analysis_supported) ||
invoker.is_clang_analysis_supported)) {
compiler_prefix = "${analyzer_wrapper} ${goma_path} "
# Create a distinct variable for "asm", since analysis runs pass
# a bunch of flags to clang/clang++ that are nonsensical on assembler
# runs.
asm = "${goma_path} ${invoker.cc}"
} else {
compiler_prefix = "${goma_path} "
}
} else { } else {
compiler_prefix = "" if (is_clang && use_clang_static_analyzer &&
(!defined(invoker.is_clang_analysis_supported) ||
invoker.is_clang_analysis_supported)) {
compiler_prefix = "${analyzer_wrapper} "
asm = invoker.cc
} else {
compiler_prefix = "${toolchain_cc_wrapper} "
}
} }
cc = compiler_prefix + invoker.cc cc = compiler_prefix + invoker.cc
cxx = compiler_prefix + invoker.cxx cxx = compiler_prefix + invoker.cxx
ar = invoker.ar ar = invoker.ar
ld = invoker.ld ld = invoker.ld
if (!defined(asm)) {
asm = cc
}
if (defined(invoker.readelf)) { if (defined(invoker.readelf)) {
readelf = invoker.readelf readelf = invoker.readelf
} else { } else {
...@@ -263,7 +297,7 @@ template("gcc_toolchain") { ...@@ -263,7 +297,7 @@ template("gcc_toolchain") {
tool("asm") { tool("asm") {
# For GCC we can just use the C compiler to compile assembly. # For GCC we can just use the C compiler to compile assembly.
depfile = "{{output}}.d" depfile = "{{output}}.d"
command = "$cc -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}" command = "$asm -MMD -MF $depfile ${rebuild_string}{{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
depsformat = "gcc" depsformat = "gcc"
description = "ASM {{output}}" description = "ASM {{output}}"
outputs = [ outputs = [
...@@ -518,34 +552,15 @@ template("clang_toolchain") { ...@@ -518,34 +552,15 @@ template("clang_toolchain") {
cc = "$prefix/clang" cc = "$prefix/clang"
cxx = "$prefix/clang++" cxx = "$prefix/clang++"
ld = cxx ld = cxx
if (use_clang_static_analyzer) {
# Static analysis isn't supported under GOMA. See crbug.com/687245
# for progress on this issue.
assert(!use_goma, "'use_clang_static_analyzer' cannot be used with GOMA.")
# Call "ccc-analyzer" or "c++-analyzer" instead of directly calling Clang.
# |wrapper_tool| sets the environment variables which are read by the
# analyzer tools.
analyzer_wrapper =
rebase_path("//build/toolchain/clang_static_analyzer_wrapper.py",
root_build_dir)
cc = analyzer_wrapper + " --clang-cc-path=${cc} --analyzer=" +
rebase_path("//third_party/scan-build/src/libexec/ccc-analyzer",
root_build_dir)
cxx = analyzer_wrapper + " --clang-cxx-path=${cxx} --analyzer=" +
rebase_path("//third_party/scan-build/src/libexec/c++-analyzer",
root_build_dir)
}
readelf = "${toolprefix}readelf" readelf = "${toolprefix}readelf"
ar = "${toolprefix}ar" ar = "${toolprefix}ar"
nm = "${toolprefix}nm" nm = "${toolprefix}nm"
forward_variables_from(invoker, forward_variables_from(invoker,
[ [
"enable_linker_map",
"strip", "strip",
"is_clang_analysis_supported",
"enable_linker_map",
]) ])
toolchain_args = { toolchain_args = {
......
...@@ -74,13 +74,16 @@ template("pnacl_toolchain") { ...@@ -74,13 +74,16 @@ template("pnacl_toolchain") {
if (defined(invoker.strip)) { if (defined(invoker.strip)) {
strip = scriptprefix + toolprefix + invoker.strip + scriptsuffix strip = scriptprefix + toolprefix + invoker.strip + scriptsuffix
} }
forward_variables_from(invoker,
[
"executable_extension",
"is_clang_analysis_supported",
])
# Note this is not the usual "ld = cxx" because "ld" uses are # Note this is not the usual "ld = cxx" because "ld" uses are
# never run via goma, so this needs scriptprefix. # never run via goma, so this needs scriptprefix.
ld = scriptprefix + toolprefix + "clang++" + scriptsuffix ld = scriptprefix + toolprefix + "clang++" + scriptsuffix
executable_extension = invoker.executable_extension
toolchain_args = { toolchain_args = {
is_clang = true is_clang = true
current_cpu = "pnacl" current_cpu = "pnacl"
...@@ -104,6 +107,10 @@ pnacl_toolchain("newlib_pnacl") { ...@@ -104,6 +107,10 @@ pnacl_toolchain("newlib_pnacl") {
pnacl_toolchain("newlib_pnacl_nonsfi") { pnacl_toolchain("newlib_pnacl_nonsfi") {
executable_extension = "" executable_extension = ""
strip = "strip" strip = "strip"
if (use_clang_static_analyzer) {
is_clang_analysis_supported = false
}
} }
template("nacl_glibc_toolchain") { template("nacl_glibc_toolchain") {
......
...@@ -32,6 +32,7 @@ template("nacl_toolchain") { ...@@ -32,6 +32,7 @@ template("nacl_toolchain") {
"cc", "cc",
"cxx", "cxx",
"deps", "deps",
"is_clang_analysis_supported",
"ld", "ld",
"link_outputs", "link_outputs",
"nm", "nm",
......
...@@ -17,6 +17,3 @@ use_clang_static_analyzer = true ...@@ -17,6 +17,3 @@ use_clang_static_analyzer = true
The next time you rebuild, you should see static analysis warnings appear inline The next time you rebuild, you should see static analysis warnings appear inline
with the usual Clang build warnings and errors. with the usual Clang build warnings and errors.
## Future plans/potential issues
* Support for running under GOMA is untested, but will be added shortly if
feasible.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment