Skip to content

Commit

Permalink
[analyzer] Add --force-analyze-debug-code option to scan-build
Browse files Browse the repository at this point in the history
to force debug build and hopefully enable more precise warnings.

Static Analyzer is much more efficient when built in debug mode
(-UNDEBUG) so we advice users to enable it manually. This may be
inconvenient in case of large complex projects (think about Linux
distros e.g. Android or Tizen). This patch adds a flag to scan-build
which inserts -UNDEBUG automatically.

Differential Revision: http://reviews.llvm.org/D16200

llvm-svn: 261204
  • Loading branch information
Yury Gribov committed Feb 18, 2016
1 parent 9c4ed17 commit a6560eb
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 7 deletions.
16 changes: 14 additions & 2 deletions clang/tools/scan-build-py/libscanbuild/analyze.py
Expand Up @@ -106,7 +106,8 @@ def exclude(filename):
'output_dir': output_dir,
'output_format': args.output_format,
'output_failures': args.output_failures,
'direct_args': analyzer_params(args)
'direct_args': analyzer_params(args),
'force_analyze_debug_code' : args.force_analyze_debug_code
}

logging.debug('run analyzer against compilation database')
Expand Down Expand Up @@ -138,7 +139,9 @@ def setup_environment(args, destination, bin_dir):
'ANALYZE_BUILD_REPORT_DIR': destination,
'ANALYZE_BUILD_REPORT_FORMAT': args.output_format,
'ANALYZE_BUILD_REPORT_FAILURES': 'yes' if args.output_failures else '',
'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args))
'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)),
'ANALYZE_BUILD_FORCE_ANALYZE_DEBUG_CODE'
: 'yes' if args.force_analyze_debug_code else ''
})
return environment

Expand Down Expand Up @@ -168,6 +171,8 @@ def analyze_build_wrapper(cplusplus):
'output_failures': os.getenv('ANALYZE_BUILD_REPORT_FAILURES'),
'direct_args': os.getenv('ANALYZE_BUILD_PARAMETERS',
'').split(' '),
'force_analyze_debug_code':
os.getenv('ANALYZE_BUILD_FORCE_ANALYZE_DEBUG_CODE'),
'directory': os.getcwd(),
}
# get relevant parameters from command line arguments
Expand Down Expand Up @@ -450,6 +455,13 @@ def create_parser(from_build_command):
Could be usefull when project contains 3rd party libraries.
The directory path shall be absolute path as file names in
the compilation database.""")
advanced.add_argument(
'--force-analyze-debug-code',
dest='force_analyze_debug_code',
action='store_true',
help="""Tells analyzer to enable assertions in code even if they were
disabled during compilation, enabling more precise
results.""")

plugins = parser.add_argument_group('checker options')
plugins.add_argument(
Expand Down
11 changes: 9 additions & 2 deletions clang/tools/scan-build-py/libscanbuild/runner.py
Expand Up @@ -41,6 +41,7 @@ def wrapper(*args, **kwargs):

@require(['command', 'directory', 'file', # an entry from compilation database
'clang', 'direct_args', # compiler name, and arguments from command
'force_analyze_debug_code', # preprocessing options
'output_dir', 'output_format', 'output_failures'])
def run(opts):
""" Entry point to run (or not) static analyzer against a single entry
Expand Down Expand Up @@ -164,9 +165,13 @@ def set_analyzer_output(opts, continuation=run_analyzer):
opts.update({'output': ['-o', opts['output_dir']]})
return continuation(opts)

def force_analyze_debug_code(cmd):
""" Enable assert()'s by undefining NDEBUG. """
cmd.append('-UNDEBUG')

@require(['file', 'directory', 'clang', 'direct_args', 'language',
'output_dir', 'output_format', 'output_failures'])
@require(['file', 'directory', 'clang', 'direct_args',
'force_analyze_debug_code', 'language', 'output_dir',
'output_format', 'output_failures'])
def create_commands(opts, continuation=set_analyzer_output):
""" Create command to run analyzer or failure report generation.
Expand All @@ -178,6 +183,8 @@ def create_commands(opts, continuation=set_analyzer_output):
if 'arch' in opts:
common.extend(['-arch', opts.pop('arch')])
common.extend(opts.pop('compile_options', []))
if opts['force_analyze_debug_code']:
force_analyze_debug_code(common)
common.extend(['-x', opts['language']])
common.append(os.path.relpath(opts['file'], opts['directory']))

Expand Down
11 changes: 11 additions & 0 deletions clang/tools/scan-build-py/tests/unit/test_runner.py
Expand Up @@ -211,3 +211,14 @@ def test_method_with_expecteds(self):

def test_method_exception_not_caught(self):
self.assertRaises(Exception, method_exception_from_inside, dict())

class ForceAnalyzeDebugTest(unittest.TestCase):

def test_force_analyze_debug_code(self):
for a, b in [
([], ['-UNDEBUG']),
(['-O2'], ['-O2', '-UNDEBUG']),
(['-Dkey=val'], ['-Dkey=val', '-UNDEBUG']),
(['-D', 'NDEBUG'], ['-D', 'NDEBUG', '-UNDEBUG']) ]:
sut.force_analyze_debug_code(a)
self.assertEqual(a, b)
20 changes: 17 additions & 3 deletions clang/tools/scan-build/bin/scan-build
Expand Up @@ -69,7 +69,8 @@ my %Options = (
MaxLoop => 0,
PluginsToLoad => [],
AnalyzerDiscoveryMethod => undef,
OverrideCompiler => 0 # The flag corresponding to the --override-compiler command line option.
OverrideCompiler => 0, # The flag corresponding to the --override-compiler command line option.
ForceAnalyzeDebugCode => 0
);
lock_keys(%Options);

Expand Down Expand Up @@ -951,7 +952,8 @@ sub SetEnv {
'CCC_CC',
'CCC_CXX',
'CCC_REPORT_FAILURES',
'CLANG_ANALYZER_TARGET') {
'CLANG_ANALYZER_TARGET',
'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE') {
my $x = $EnvVars->{$var};
if (defined $x) { $ENV{$var} = $x }
}
Expand Down Expand Up @@ -1118,6 +1120,11 @@ OPTIONS:
Also analyze functions in #included files. By default, such functions
are skipped unless they are called by functions within the main source file.
--force-analyze-debug-code
Tells analyzer to enable assertions in code even if they were disabled
during compilation to enable more precise results.
-o <output location>
Specifies the output directory for analyzer reports. Subdirectories will be
Expand Down Expand Up @@ -1681,6 +1688,12 @@ sub ProcessArgs {
next;
}

if ($arg eq "--force-analyze-debug-code") {
shift @$Args;
$Options{ForceAnalyzeDebugCode} = 1;
next;
}

DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/);

$NumArgs--;
Expand Down Expand Up @@ -1796,7 +1809,8 @@ my %EnvVars = (
'CCC_ANALYZER_CONSTRAINTS_MODEL' => $Options{ConstraintsModel},
'CCC_ANALYZER_INTERNAL_STATS' => $Options{InternalStats},
'CCC_ANALYZER_OUTPUT_FORMAT' => $Options{OutputFormat},
'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget}
'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget},
'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE' => $Options{ForceAnalyzeDebugCode}
);

# Run the build.
Expand Down
8 changes: 8 additions & 0 deletions clang/tools/scan-build/libexec/ccc-analyzer
Expand Up @@ -492,6 +492,9 @@ if (defined $ENV{'CCC_ANALYZER_LOG'}) { $Verbose = 2; }
# Get the HTML output directory.
my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'};

# Get force-analyze-debug-code option.
my $ForceAnalyzeDebugCode = $ENV{'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE'};

my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1);
my %ArchsSeen;
my $HadArch = 0;
Expand Down Expand Up @@ -682,6 +685,11 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) {
}
}

# Forcedly enable debugging if requested by user.
if ($ForceAnalyzeDebugCode) {
push @CompileOpts, '-UNDEBUG';
}

# If we are on OSX and have an installation where the
# default SDK is inferred by xcrun use xcrun to infer
# the SDK.
Expand Down
3 changes: 3 additions & 0 deletions clang/www/analyzer/scan-build.html
Expand Up @@ -226,6 +226,9 @@ <h3 id="recommended_debug">ALWAYS analyze a project in its &quot;debug&quot; con
in some cases can greatly reduce the number of false positives (bogus error
reports) emitted by the tool.</p>

<p>Another option is to use <tt>--force-analyze-debug-code</tt> flag of
<b>scan-build</b> tool which would enable assertions automatically.</p>

<h3 id="recommend_verbose">Use verbose output when debugging scan-build</h3>

<p><tt>scan-build</tt> takes a <b>-v</b> option to emit verbose output about
Expand Down

0 comments on commit a6560eb

Please sign in to comment.