Index: tools/scan-build-py/libscanbuild/analyze.py =================================================================== --- tools/scan-build-py/libscanbuild/analyze.py +++ tools/scan-build-py/libscanbuild/analyze.py @@ -106,7 +106,8 @@ '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') @@ -138,7 +139,9 @@ '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 @@ -168,6 +171,8 @@ '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 @@ -450,6 +455,13 @@ 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 (to enable more precise + results).""") plugins = parser.add_argument_group('checker options') plugins.add_argument( Index: tools/scan-build-py/libscanbuild/runner.py =================================================================== --- tools/scan-build-py/libscanbuild/runner.py +++ tools/scan-build-py/libscanbuild/runner.py @@ -41,6 +41,7 @@ @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 @@ -164,9 +165,23 @@ opts.update({'output': ['-o', opts['output_dir']]}) return continuation(opts) - -@require(['file', 'directory', 'clang', 'direct_args', 'language', - 'output_dir', 'output_format', 'output_failures']) +def force_analyze_debug_code(cmd): + """ Remove definition of NDEBUG from command to enable assert()'s. """ + + args = iter(cmd) + result = [] + for arg in args: + if arg.startswith('-D'): + macro = arg[2:] if arg[2:] else next(args) + if macro == 'NDEBUG' or macro.startswith('NDEBUG='): + continue + arg = '-D' + macro + result.append(arg) + return result + +@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. @@ -177,7 +192,10 @@ common = [] if 'arch' in opts: common.extend(['-arch', opts.pop('arch')]) - common.extend(opts.pop('compile_options', [])) + compile_options = opts.pop('compile_options', []) + if opts['force_analyze_debug_code']: + compile_options = force_analyze_debug_code(compile_options) + common.extend(compile_options) common.extend(['-x', opts['language']]) common.append(os.path.relpath(opts['file'], opts['directory'])) Index: tools/scan-build-py/tests/unit/test_runner.py =================================================================== --- tools/scan-build-py/tests/unit/test_runner.py +++ tools/scan-build-py/tests/unit/test_runner.py @@ -211,3 +211,12 @@ 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): + self.assertEqual(sut.force_analyze_debug_code(['-O2']), ['-O2']) + self.assertEqual(sut.force_analyze_debug_code(['-D', 'XXX']), ['-DXXX']) + self.assertEqual(sut.force_analyze_debug_code(['-D', 'NDEBUG']), []) + self.assertEqual(sut.force_analyze_debug_code(['-D', 'NDEBUG=0']), []) + self.assertEqual(sut.force_analyze_debug_code(['-D', 'NDEBUG2']), ['-DNDEBUG2']) Index: tools/scan-build/bin/scan-build =================================================================== --- tools/scan-build/bin/scan-build +++ tools/scan-build/bin/scan-build @@ -69,7 +69,8 @@ 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); @@ -951,7 +952,8 @@ '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 } } @@ -1118,6 +1120,11 @@ 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 Specifies the output directory for analyzer reports. Subdirectories will be @@ -1681,6 +1688,12 @@ next; } + if ($arg eq "--force-analyze-debug-code") { + shift @$Args; + $Options{ForceAnalyzeDebugCode} = 1; + next; + } + DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); $NumArgs--; @@ -1796,7 +1809,8 @@ '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. Index: tools/scan-build/libexec/ccc-analyzer =================================================================== --- tools/scan-build/libexec/ccc-analyzer +++ tools/scan-build/libexec/ccc-analyzer @@ -492,6 +492,9 @@ # 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; @@ -576,6 +579,20 @@ next; } + # Forcedly enable debugging if requested by user. + if ($ForceAnalyzeDebugCode) { + my $Macro = substr($Arg, 2); + my $Skip = 0; + if ($Macro eq '') { + $Macro = $ARGV[$i + 1]; + $Skip = 1; + } + if ($Macro eq 'NDEBUG' || index($Macro, 'NDEBUG=') == 0) { + $i += $Skip; + next; + } + } + # Ignored options. if (defined $IgnoredOptionMap{$ArgKey}) { my $Cnt = $IgnoredOptionMap{$ArgKey}; Index: www/analyzer/scan-build.html =================================================================== --- www/analyzer/scan-build.html +++ www/analyzer/scan-build.html @@ -226,6 +226,9 @@ in some cases can greatly reduce the number of false positives (bogus error reports) emitted by the tool.

+

Another option is to use --force-analyze-debug-code flag of +scan-build tool which would enable assertions automatically.

+

Use verbose output when debugging scan-build

scan-build takes a -v option to emit verbose output about