xazax.hun updated this revision to Diff 94709.
xazax.hun edited the summary of this revision.
xazax.hun added a comment.
- Fixed some memory leaks.
- Removed some unrelated style changes.
- Simplifications to the python scripts.
- Removed debug prints.
https://reviews.llvm.org/D30691
Files:
include/clang/AST/ASTContext.h
include/clang/AST/Decl.h
include/clang/AST/Mangle.h
include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
lib/AST/ASTContext.cpp
lib/AST/ASTImporter.cpp
lib/AST/ItaniumMangle.cpp
lib/Basic/SourceManager.cpp
lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
lib/StaticAnalyzer/Core/CMakeLists.txt
lib/StaticAnalyzer/Core/CallEvent.cpp
lib/StaticAnalyzer/Core/ExprEngine.cpp
lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
test/Analysis/Inputs/ctu-chain.cpp
test/Analysis/Inputs/ctu-other.cpp
test/Analysis/Inputs/externalFnMap.txt
test/Analysis/ctu-main.cpp
tools/CMakeLists.txt
tools/clang-cmdline-arch-extractor/CMakeLists.txt
tools/clang-cmdline-arch-extractor/ClangCmdlineArchExtractor.cpp
tools/clang-func-mapping/CMakeLists.txt
tools/clang-func-mapping/ClangFnMapGen.cpp
tools/ctu-analysis/ctu-analyze.py
tools/ctu-analysis/ctu-build.py
tools/scan-build-py/libscanbuild/runner.py
Index: tools/scan-build-py/libscanbuild/runner.py
===================================================================
--- tools/scan-build-py/libscanbuild/runner.py
+++ tools/scan-build-py/libscanbuild/runner.py
@@ -162,7 +162,8 @@
def target():
""" Creates output file name for reports. """
- if opts['output_format'] in {'plist', 'plist-html'}:
+ if opts['output_format'] in {'plist', 'plist-html',
+ 'plist-multi-file'}:
(handle, name) = tempfile.mkstemp(prefix='report-',
suffix='.plist',
dir=opts['output_dir'])
Index: tools/ctu-analysis/ctu-build.py
===================================================================
--- /dev/null
+++ tools/ctu-analysis/ctu-build.py
@@ -0,0 +1,219 @@
+#!/usr/bin/env python
+
+import argparse
+import json
+import logging
+import multiprocessing
+import os
+import re
+import signal
+import subprocess
+
+
+SOURCE_PATTERN = re.compile('.*\.(C|c|cc|cpp|cxx|ii|m|mm)$', re.IGNORECASE)
+TIMEOUT = 86400
+DEFINED_FUNCTIONS_FILENAME = 'definedFns.txt'
+EXTERNAL_FUNCTIONS_FILENAME = 'externalFns.txt'
+EXTERNAL_FUNCTION_MAP_FILENAME = 'externalFnMap.txt'
+
+
+def get_args():
+ parser = argparse.ArgumentParser(
+ description='Executes 1st pass of CTU analysis where we preprocess '
+ 'all files in the compilation database and generate '
+ 'AST dumps and other necessary information from those '
+ 'to be used later by the 2nd pass of '
+ 'Cross Translation Unit analysis',
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.add_argument('-b', required=True, dest='buildlog',
+ metavar='build.json',
+ help='JSON Compilation Database to be used')
+ parser.add_argument('-p', metavar='preanalyze-dir', dest='ctuindir',
+ help='Target directory for preanalyzation data',
+ default='.ctu')
+ parser.add_argument('-j', metavar='threads', dest='threads', type=int,
+ help='Number of threads to be used',
+ default=int(multiprocessing.cpu_count() * 1.0))
+ parser.add_argument('-v', dest='verbose', action='store_true',
+ help='Verbose output')
+ parser.add_argument('--clang-path', metavar='clang-path',
+ dest='clang_path',
+ help='Set path to directory of clang binaries used '
+ '(default taken from CLANG_PATH envvar)',
+ default=os.environ.get('CLANG_PATH'))
+ mainargs = parser.parse_args()
+
+ if mainargs.verbose:
+ logging.getLogger().setLevel(logging.INFO)
+
+ if mainargs.clang_path is None:
+ clang_path = ''
+ else:
+ clang_path = os.path.abspath(mainargs.clang_path)
+ logging.info('CTU uses clang dir: ' +
+ (clang_path if clang_path != '' else '<taken from PATH>'))
+
+ return mainargs, clang_path
+
+
+def process_buildlog(buildlog_filename, src_2_dir, src_2_cmd, src_order,
+ cmd_2_src, cmd_order):
+ with open(buildlog_filename, 'r') as buildlog_file:
+ buildlog = json.load(buildlog_file)
+ for step in buildlog:
+ if SOURCE_PATTERN.match(step['file']):
+ if step['file'] not in src_2_dir:
+ src_2_dir[step['file']] = step['directory']
+ src_2_cmd[step['file']] = step['command']
+ src_order.append(step['file'])
+ if step['command'] not in cmd_2_src:
+ cmd_2_src[step['command']] = [step['file']]
+ cmd_order.append(step['command'])
+ else:
+ cmd_2_src[step['command']].append(step['file'])
+
+
+def clear_file(filename):
+ try:
+ os.remove(filename)
+ except OSError:
+ pass
+
+
+def clear_workspace(ctuindir):
+ clear_file(os.path.join(ctuindir, DEFINED_FUNCTIONS_FILENAME))
+ clear_file(os.path.join(ctuindir, EXTERNAL_FUNCTIONS_FILENAME))
+ clear_file(os.path.join(ctuindir, EXTERNAL_FUNCTION_MAP_FILENAME))
+
+
+def get_command_arguments(cmd):
+ had_command = False
+ args = []
+ for arg in cmd.split():
+ if had_command and not SOURCE_PATTERN.match(arg):
+ args.append(arg)
+ if not had_command and arg.find('=') == -1:
+ had_command = True
+ return args
+
+
+def generate_ast(params):
+ source, command, directory, clang_path, ctuindir = params
+ args = get_command_arguments(command)
+ arch_command = [os.path.join(clang_path, 'clang-cmdline-arch-extractor')]
+ arch_command.extend(args)
+ arch_command.append(source)
+ arch_command_str = ' '.join(arch_command)
+ logging.info(arch_command_str)
+ arch_output = subprocess.check_output(arch_command_str, shell=True)
+ arch = arch_output[arch_output.rfind('@')+1:].strip()
+ ast_joined_path = os.path.join(ctuindir, 'ast', arch,
+ os.path.realpath(source)[1:] + '.ast')
+ ast_path = os.path.abspath(ast_joined_path)
+ try:
+ os.makedirs(os.path.dirname(ast_path))
+ except OSError:
+ if os.path.isdir(os.path.dirname(ast_path)):
+ pass
+ else:
+ raise
+ dir_command = ['cd', directory]
+ ast_command = [os.path.join(clang_path, 'clang'), '-emit-ast']
+ ast_command.extend(args)
+ ast_command.append('-w')
+ ast_command.append(source)
+ ast_command.append('-o')
+ ast_command.append(ast_path)
+ ast_command_str = ' '.join(dir_command) + " && " + ' '.join(ast_command)
+ logging.info(ast_command_str)
+ subprocess.call(ast_command_str, shell=True)
+
+
+def map_functions(params):
+ command, sources, directory, clang_path, ctuindir = params
+ args = get_command_arguments(command)
+ dir_command = ['cd', directory]
+ funcmap_command = [os.path.join(clang_path, 'clang-func-mapping')]
+ funcmap_command.append('--ctu-dir')
+ funcmap_command.append(os.path.abspath(ctuindir))
+ funcmap_command.extend(sources)
+ funcmap_command.append('--')
+ funcmap_command.extend(args)
+ funcmap_command_str = ' '.join(dir_command) + \
+ " && " + ' '.join(funcmap_command)
+ logging.info(funcmap_command_str)
+ subprocess.call(funcmap_command_str, shell=True)
+
+
+def run_parallel(threads, workfunc, funcparams):
+ original_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
+ workers = multiprocessing.Pool(processes=threads)
+ signal.signal(signal.SIGINT, original_handler)
+ try:
+ # Block with timeout so that signals don't get ignored, python bug 8296
+ workers.map_async(workfunc, funcparams).get(TIMEOUT)
+ except KeyboardInterrupt:
+ workers.terminate()
+ workers.join()
+ exit(1)
+ else:
+ workers.close()
+ workers.join()
+
+
+def generate_external_funcmap(mainargs):
+ func_2_file = {}
+ extfunc_2_file = {}
+ func_2_fileset = {}
+ defined_fns_filename = os.path.join(mainargs.ctuindir,
+ DEFINED_FUNCTIONS_FILENAME)
+ with open(defined_fns_filename, 'r') as defined_fns_file:
+ for line in defined_fns_file:
+ funcname, filename = line.strip().split(' ')
+ if funcname.startswith('!'):
+ funcname = funcname[1:] # main function
+ if funcname not in func_2_file.keys():
+ func_2_fileset[funcname] = set([filename])
+ else:
+ func_2_fileset[funcname].add(filename)
+ func_2_file[funcname] = filename
+ extern_fns_filename = os.path.join(mainargs.ctuindir,
+ EXTERNAL_FUNCTIONS_FILENAME)
+ with open(extern_fns_filename, 'r') as extern_fns_file:
+ for line in extern_fns_file:
+ line = line.strip()
+ if line in func_2_file and line not in extfunc_2_file:
+ extfunc_2_file[line] = func_2_file[line]
+ extern_fns_map_filename = os.path.join(mainargs.ctuindir,
+ EXTERNAL_FUNCTION_MAP_FILENAME)
+ with open(extern_fns_map_filename, 'w') as out_file:
+ for func, fname in extfunc_2_file.items():
+ if len(func_2_fileset[func]) == 1:
+ out_file.write('%s %s.ast\n' % (func, fname))
+
+
+def main():
+ mainargs, clang_path = get_args()
+ clear_workspace(mainargs.ctuindir)
+
+ src_2_dir = {}
+ src_2_cmd = {}
+ src_order = []
+ cmd_2_src = {}
+ cmd_order = []
+ process_buildlog(mainargs.buildlog, src_2_dir, src_2_cmd, src_order,
+ cmd_2_src, cmd_order)
+
+ run_parallel(mainargs.threads, generate_ast,
+ [(src, src_2_cmd[src], src_2_dir[src], clang_path,
+ mainargs.ctuindir) for src in src_order])
+
+ run_parallel(mainargs.threads, map_functions,
+ [(cmd, cmd_2_src[cmd], src_2_dir[cmd_2_src[cmd][0]],
+ clang_path, mainargs.ctuindir) for cmd in cmd_order])
+
+ generate_external_funcmap(mainargs)
+
+
+if __name__ == "__main__":
+ main()
Index: tools/ctu-analysis/ctu-analyze.py
===================================================================
--- /dev/null
+++ tools/ctu-analysis/ctu-analyze.py
@@ -0,0 +1,250 @@
+#!/usr/bin/env python
+
+import argparse
+import json
+import logging
+import multiprocessing
+import os
+import re
+import signal
+import subprocess
+import threading
+import time
+
+
+SOURCE_PATTERN = re.compile('.*\.(C|c|cc|cpp|cxx|ii|m|mm)$', re.IGNORECASE)
+DIRCMD_SEPARATOR = ': '
+
+
+def get_args():
+ analyser_output_formats = ['plist-multi-file', 'plist', 'plist-html',
+ 'html', 'text']
+ analyser_output_format = analyser_output_formats[0]
+ parser = argparse.ArgumentParser(
+ description='Executes 2nd pass of CTU analysis where we do the '
+ 'static analysis taking all cross calls between '
+ 'translation units into account',
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ parser.add_argument('-b', required=True, dest='buildlog',
+ metavar='build.json',
+ help='JSON Compilation Database to be used')
+ parser.add_argument('-p', metavar='preanalyze-dir', dest='ctuindir',
+ help='Use directory for reading preanalyzation data ',
+ default='.ctu')
+ parser.add_argument('-o', metavar='output-dir', dest='ctuoutdir',
+ help='Target directory for analyzation results',
+ default='.ctu-out')
+ parser.add_argument('-e', metavar='enabled-checker', nargs='+',
+ dest='enabled_checkers',
+ help='List all enabled checkers')
+ parser.add_argument('-d', metavar='disabled-checker', nargs='+',
+ dest='disabled_checkers',
+ help='List all disabled checkers')
+ parser.add_argument('-j', metavar='threads', dest='threads', type=int,
+ help='Number of threads to be used',
+ default=int(multiprocessing.cpu_count() * 1.0))
+ parser.add_argument('-v', dest='verbose', action='store_true',
+ help='Verbose output')
+ parser.add_argument('--clang-path', metavar='clang-path',
+ dest='clang_path',
+ help='Set path to directory of clang binaries used '
+ '(default taken from CLANG_PATH envvar)',
+ default=os.environ.get('CLANG_PATH'))
+ parser.add_argument('--analyze-cc-path', metavar='analyze-cc-path',
+ dest='analyze_path',
+ help='Set path to directory of analyze-cc used '
+ '(default is taken from CLANG_ANALYZE_CC_PATH '
+ 'environment variable)',
+ default=os.environ.get('CLANG_ANALYZE_CC_PATH'))
+ parser.add_argument('--output-format', metavar='format',
+ choices=analyser_output_formats,
+ default=analyser_output_format,
+ help='Format for analysis reports '
+ '(one of %s; default is "%s").' %
+ (', '.join(analyser_output_formats),
+ analyser_output_format))
+ parser.add_argument('--no-ctu', dest='no_ctu', action='store_true',
+ help='Do not use CTU at all, '
+ 'only do normal static analysis')
+ mainargs = parser.parse_args()
+
+ if mainargs.verbose:
+ logging.getLogger().setLevel(logging.INFO)
+
+ mainargs.ctuindir = os.path.abspath(mainargs.ctuindir)
+ mainargs.ctuoutdir = os.path.abspath(mainargs.ctuoutdir)
+
+ if mainargs.clang_path is None:
+ clang_path = ''
+ else:
+ clang_path = os.path.abspath(mainargs.clang_path)
+ logging.info('CTU uses clang dir: ' +
+ (clang_path if clang_path != '' else '<taken from PATH>'))
+
+ if mainargs.analyze_path is None:
+ analyze_path = ''
+ else:
+ analyze_path = os.path.abspath(mainargs.analyze_path)
+ logging.info('CTU uses analyze-cc dir: ' +
+ (analyze_path if analyze_path != '' else '<taken from PATH>'))
+
+ return mainargs, clang_path, analyze_path
+
+
+def get_analyzer_env(mainargs, clang_path):
+ analyzer_params = []
+ if mainargs.enabled_checkers:
+ analyzer_params.append('-analyzer-checker')
+ analyzer_params.append(mainargs.enabled_checkers)
+ if mainargs.disabled_checkers:
+ analyzer_params.append('-analyzer-disable-checker')
+ analyzer_params.append(mainargs.disabled_checkers)
+ if not mainargs.no_ctu:
+ analyzer_params.append('-analyzer-config')
+ analyzer_params.append('ctu-dir=' + mainargs.ctuindir)
+ analyzer_params.append('-analyzer-config')
+ analyzer_params.append('reanalyze-ctu-visited=true')
+ analyzer_params.append('-analyzer-stats')
+ passthru_analyzer_params = []
+ for param in analyzer_params:
+ passthru_analyzer_params.append('-Xanalyzer')
+ passthru_analyzer_params.append(param)
+ passthru_analyzer_params.append('--analyzer-output')
+ passthru_analyzer_params.append(mainargs.output_format)
+ analyzer_env = os.environ.copy()
+ analyzer_env['ANALYZE_BUILD_CLANG'] = os.path.join(clang_path, 'clang')
+ analyzer_env['ANALYZE_BUILD_REPORT_DIR'] = mainargs.ctuoutdir
+ analyzer_env['ANALYZE_BUILD_REPORT_FORMAT'] = mainargs.output_format
+ analyzer_env['ANALYZE_BUILD_REPORT_FAILURES'] = 'yes'
+ analyzer_env['ANALYZE_BUILD_PARAMETERS'] = \
+ ' '.join(passthru_analyzer_params)
+ return analyzer_env
+
+
+def process_buildlog(buildlog_filename):
+ with open(buildlog_filename, 'r') as buildlog_file:
+ buildlog = json.load(buildlog_file)
+ dircmd_2_orders = {}
+ src_build_steps = 0
+ for step in buildlog:
+ if SOURCE_PATTERN.match(step['file']):
+ uid = step['directory'] + DIRCMD_SEPARATOR + step['command']
+ if uid not in dircmd_2_orders:
+ dircmd_2_orders[uid] = [src_build_steps]
+ else:
+ dircmd_2_orders[uid].append(src_build_steps)
+ src_build_steps += 1
+ return dircmd_2_orders
+
+
+def prepare_workspace(mainargs):
+ try:
+ os.makedirs(mainargs.ctuoutdir)
+ except OSError:
+ logging.warning('Output directory %s already exists!',
+ mainargs.ctuoutdir)
+ exit(1)
+
+
+def clean_up_workspace(mainargs):
+ try:
+ os.removedirs(mainargs.ctuoutdir)
+ logging.info('Removing directory %s because it contains no reports',
+ mainargs.ctuoutdir)
+ except OSError:
+ pass
+
+
+def get_compiler_and_arguments(cmd):
+ had_command = False
+ args = []
+ for arg in cmd.split():
+ if had_command:
+ args.append(arg)
+ if not had_command and arg.find('=') == -1:
+ had_command = True
+ compiler = arg
+ return compiler, args
+
+
+def analyze(mainargs, analyze_path, analyzer_env, directory, command):
+ compiler, args = get_compiler_and_arguments(command)
+ cmdenv = analyzer_env.copy()
+ cmdenv['INTERCEPT_BUILD'] = json.dumps({
+ 'verbose': 1 if mainargs.verbose else 0,
+ 'cc': [compiler],
+ 'cxx': [compiler]
+ })
+ analyze_cmd_str = os.path.join(analyze_path, 'analyze-cc') + ' ' + \
+ ' '.join(args)
+ logging.info(analyze_cmd_str)
+
+ # Buffer output of subprocess and dump it out at the end, so that
+ # the subprocess doesn't continue to write output after the user
+ # sends SIGTERM
+ run_ok = True
+ out = '******* Error running command'
+ try:
+ popen = subprocess.Popen(analyze_cmd_str, shell=True,
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE,
+ universal_newlines=True,
+ cwd=directory,
+ env=cmdenv)
+ out, _ = popen.communicate()
+ run_ok = not popen.returncode
+ except OSError:
+ run_ok = False
+ logging.info('Compile status: %s', 'ok' if run_ok else 'failed')
+ logging.info(out)
+
+
+def analyze_worker(mainargs, analyze_path, analyzer_env, lock,
+ dircmd_2_orders):
+ while len(dircmd_2_orders) > 0:
+ lock.acquire()
+ if len(dircmd_2_orders) > 0:
+ dircmd, orders = dircmd_2_orders.popitem()
+ lock.release()
+ dircmdsplit = dircmd.split(DIRCMD_SEPARATOR, 2)
+ assert len(dircmdsplit) == 2 and len(dircmdsplit[0]) > 0 and \
+ len(dircmdsplit[1]) > 0
+ assert len(orders) > 0
+ directory = dircmdsplit[0]
+ command = dircmdsplit[1]
+ analyze(mainargs, analyze_path, analyzer_env, directory, command)
+ else:
+ lock.release()
+ time.sleep(0.125)
+
+
+def run_parallel(mainargs, analyze_path, analyzer_env, dircmd_2_orders):
+ original_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
+ signal.signal(signal.SIGINT, original_handler)
+ lock = threading.Lock()
+ workers = []
+ for _ in range(mainargs.threads):
+ workers.append(threading.Thread(target=analyze_worker,
+ args=(mainargs, analyze_path,
+ analyzer_env, lock,
+ dircmd_2_orders)))
+ for worker in workers:
+ worker.start()
+ try:
+ for worker in workers:
+ worker.join(9999999999)
+ except KeyboardInterrupt:
+ exit(1)
+
+
+def main():
+ mainargs, clang_path, analyze_path = get_args()
+ analyzer_env = get_analyzer_env(mainargs, clang_path)
+ dircmd_2_orders = process_buildlog(mainargs.buildlog)
+ prepare_workspace(mainargs)
+ run_parallel(mainargs, analyze_path, analyzer_env, dircmd_2_orders)
+ clean_up_workspace(mainargs)
+
+
+if __name__ == "__main__":
+ main()
Index: tools/clang-func-mapping/ClangFnMapGen.cpp
===================================================================
--- /dev/null
+++ tools/clang-func-mapping/ClangFnMapGen.cpp
@@ -0,0 +1,208 @@
+//===- ClangFnMapGen.cpp -----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+//
+// Clang tool which creates a list of defined functions and the files in which
+// they are defined.
+//
+//===--------------------------------------------------------------------===//
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/GlobalDecl.h"
+#include "clang/AST/Mangle.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Signals.h"
+#include <assert.h>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <sys/file.h>
+#include <unistd.h>
+#include <vector>
+
+using namespace llvm;
+using namespace clang;
+using namespace clang::tooling;
+
+typedef StringSet<> StrSet;
+typedef StringMap<StrSet> CallGraph;
+
+static cl::OptionCategory ClangFnMapGenCategory("clang-fnmapgen options");
+static cl::opt<std::string> CTUDir(
+ "ctu-dir",
+ cl::desc(
+ "Directory that contains the CTU related files (e.g.: AST dumps)."),
+ cl::init(""), cl::cat(ClangFnMapGenCategory));
+
+static void lockedWrite(const std::string &fileName,
+ const std::string &content) {
+ if (!content.empty()) {
+ int fd = open(fileName.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
+ flock(fd, LOCK_EX);
+ ssize_t written = write(fd, content.c_str(), content.length());
+ assert(written == static_cast<ssize_t>(content.length()));
+ (void)written;
+ flock(fd, LOCK_UN);
+ close(fd);
+ }
+}
+
+static std::string getTripleSuffix(ASTContext &Ctx) {
+ // We are not going to support vendor and don't support OS and environment.
+ // FIXME: support OS and environment correctly.
+ Triple::ArchType T = Ctx.getTargetInfo().getTriple().getArch();
+ if (T == Triple::thumb)
+ T = Triple::arm;
+ return Ctx.getTargetInfo().getTriple().getArchTypeName(T);
+}
+
+class MapFunctionNamesConsumer : public ASTConsumer {
+private:
+ ASTContext &Ctx;
+ ItaniumMangleContext *ItaniumCtx;
+ std::stringstream DefinedFuncsStr;
+ std::stringstream ExternFuncStr;
+ const std::string Triple;
+
+public:
+ MapFunctionNamesConsumer(ASTContext &Context, ItaniumMangleContext *MangleCtx)
+ : Ctx(Context), ItaniumCtx(MangleCtx),
+ Triple(std::string("@") + getTripleSuffix(Context)) {}
+ std::string CurrentFileName;
+
+ ~MapFunctionNamesConsumer();
+ virtual void HandleTranslationUnit(ASTContext &Ctx) {
+ handleDecl(Ctx.getTranslationUnitDecl());
+ }
+
+private:
+ std::string getMangledName(const FunctionDecl *FD);
+
+ bool isCLibraryFunction(const FunctionDecl *FD);
+ void handleDecl(const Decl *D);
+};
+
+std::string MapFunctionNamesConsumer::getMangledName(const FunctionDecl *FD) {
+ std::string MangledName;
+ llvm::raw_string_ostream os(MangledName);
+ if (const auto *CCD = dyn_cast<CXXConstructorDecl>(FD))
+ // FIXME: Use correct Ctor/DtorType.
+ ItaniumCtx->mangleCXXCtor(CCD, Ctor_Complete, os);
+ else if (const auto *CDD = dyn_cast<CXXDestructorDecl>(FD))
+ ItaniumCtx->mangleCXXDtor(CDD, Dtor_Complete, os);
+ else
+ ItaniumCtx->mangleName(FD, os);
+ os.flush();
+ return MangledName;
+}
+
+void MapFunctionNamesConsumer::handleDecl(const Decl *D) {
+ if (!D)
+ return;
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ if (const Stmt *Body = FD->getBody()) {
+ std::string MangledName = getMangledName(FD);
+ const SourceManager &SM = Ctx.getSourceManager();
+ if (CurrentFileName.empty()) {
+ StringRef SMgrName =
+ SM.getFileEntryForID(SM.getMainFileID())->getName();
+ char *Path = realpath(SMgrName.str().c_str(), nullptr);
+ CurrentFileName = Path;
+ free(Path);
+ }
+
+ std::string FileName =
+ std::string("/ast/") + getTripleSuffix(Ctx) + CurrentFileName;
+ std::string FullName = MangledName + Triple;
+
+ switch (FD->getLinkageInternal()) {
+ case ExternalLinkage:
+ case VisibleNoLinkage:
+ case UniqueExternalLinkage:
+ if (SM.isInMainFile(Body->getLocStart()))
+ DefinedFuncsStr << "!";
+ DefinedFuncsStr << FullName << " " << FileName << "\n";
+ default:
+ break;
+ }
+ } else if (!FD->getBody() && !FD->getBuiltinID()) {
+ std::string MangledName = getMangledName(FD);
+ ExternFuncStr << MangledName << Triple << "\n";
+ }
+ }
+
+ if (const auto *DC = dyn_cast<DeclContext>(D))
+ for (const Decl *D : DC->decls())
+ handleDecl(D);
+}
+
+bool MapFunctionNamesConsumer::isCLibraryFunction(const FunctionDecl *FD) {
+ SourceManager &SM = Ctx.getSourceManager();
+ if (!FD)
+ return false;
+ SourceLocation Loc = FD->getLocation();
+ if (Loc.isValid())
+ return SM.isInSystemHeader(Loc);
+ return true;
+}
+
+MapFunctionNamesConsumer::~MapFunctionNamesConsumer() {
+ // Flush results to files.
+ std::string BuildDir = CTUDir;
+ lockedWrite(BuildDir + "/externalFns.txt", ExternFuncStr.str());
+ lockedWrite(BuildDir + "/definedFns.txt", DefinedFuncsStr.str());
+}
+
+class MapFunctionNamesAction : public ASTFrontendAction {
+protected:
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ llvm::StringRef) {
+ ItaniumMangleContext *ItaniumCtx =
+ ItaniumMangleContext::create(CI.getASTContext(), CI.getDiagnostics());
+ ItaniumCtx->setShouldForceMangleProto(true);
+ std::unique_ptr<ASTConsumer> PFC(
+ new MapFunctionNamesConsumer(CI.getASTContext(), ItaniumCtx));
+ return PFC;
+ }
+};
+
+int main(int argc, const char **argv) {
+ // Print a stack trace if we signal out.
+ sys::PrintStackTraceOnErrorSignal(argv[0], false);
+ PrettyStackTraceProgram X(argc, argv);
+
+ SmallVector<std::string, 4> Sources;
+ CommonOptionsParser OptionsParser(argc, argv, ClangFnMapGenCategory,
+ cl::ZeroOrMore);
+
+ if (CTUDir.getNumOccurrences() != 1) {
+ errs() << "Exactly one CTU dir should be provided\n";
+ return 1;
+ }
+ const StringRef cppFile = ".cpp", ccFile = ".cc", cFile = ".c",
+ cxxFile = ".cxx";
+ for (int i = 1; i < argc; i++) {
+ StringRef arg = argv[i];
+ if (arg.endswith(cppFile) || arg.endswith(ccFile) || arg.endswith(cFile) ||
+ arg.endswith(cxxFile)) {
+ Sources.push_back(arg);
+ }
+ }
+ ClangTool Tool(OptionsParser.getCompilations(), Sources);
+ Tool.run(newFrontendActionFactory<MapFunctionNamesAction>().get());
+ return 0;
+}
Index: tools/clang-func-mapping/CMakeLists.txt
===================================================================
--- /dev/null
+++ tools/clang-func-mapping/CMakeLists.txt
@@ -0,0 +1,21 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
+ )
+
+add_clang_executable(clang-func-mapping
+ ClangFnMapGen.cpp
+ )
+
+target_link_libraries(clang-func-mapping
+ clangTooling
+ clangAST
+ clangBasic
+ clangFrontend
+ clangRewriteFrontend
+ )
+
+install(TARGETS clang-func-mapping
+ RUNTIME DESTINATION bin)
Index: tools/clang-cmdline-arch-extractor/ClangCmdlineArchExtractor.cpp
===================================================================
--- /dev/null
+++ tools/clang-cmdline-arch-extractor/ClangCmdlineArchExtractor.cpp
@@ -0,0 +1,73 @@
+//===- ClangCmdlineArchExtractor.cpp ------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+//
+// Clang tool which prints architecture type for a given command line.
+//
+//===--------------------------------------------------------------------===//
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include <string>
+#include <vector>
+
+using namespace llvm;
+using namespace clang;
+
+static std::string getTripleSuffix(const Triple &Triple) {
+ // We are not going to support vendor and don't support OS and environment.
+ // FIXME: support OS and environment correctly
+ Triple::ArchType T = Triple.getArch();
+ if (T == Triple::thumb)
+ T = Triple::arm;
+ return Triple.getArchTypeName(T);
+}
+
+int main(int argc, const char **argv) {
+ // Print a stack trace if we signal out.
+ sys::PrintStackTraceOnErrorSignal(argv[0], true);
+ PrettyStackTraceProgram X(argc, argv);
+
+ SmallVector<StringRef, 4> Sources;
+ std::vector<const char*> Args;
+ const StringRef cppFile = ".cpp", ccFile = ".cc", cFile = ".c",
+ cxxFile = ".cxx";
+ for (int i = 1; i < argc; i++) {
+ StringRef Arg = argv[i];
+ if (Arg.endswith(cppFile) || Arg.endswith(ccFile) || Arg.endswith(cFile) ||
+ Arg.endswith(cxxFile)) {
+ Sources.push_back(argv[i]);
+ } else {
+ Args.push_back(argv[i]);
+ }
+ }
+
+ if (Sources.empty())
+ return 1;
+
+ Args.push_back(Sources[0].data());
+ std::unique_ptr<CompilerInvocation> CI(
+ createInvocationFromCommandLine(Args));
+
+ const std::string Suffix =
+ "@" + getTripleSuffix(llvm::Triple(CI->getTargetOpts().Triple));
+
+ for (StringRef SourceFile : Sources) {
+ char *Path = realpath(SourceFile.data(), nullptr);
+ if (Path)
+ outs() << Path << Suffix << " ";
+ free(Path);
+ }
+
+ return 0;
+}
Index: tools/clang-cmdline-arch-extractor/CMakeLists.txt
===================================================================
--- /dev/null
+++ tools/clang-cmdline-arch-extractor/CMakeLists.txt
@@ -0,0 +1,20 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
+ )
+
+add_clang_executable(clang-cmdline-arch-extractor
+ ClangCmdlineArchExtractor.cpp
+ )
+
+target_link_libraries(clang-cmdline-arch-extractor
+ clangTooling
+ clangBasic
+ clangFrontend
+ clangRewriteFrontend
+ )
+
+install(TARGETS clang-cmdline-arch-extractor
+ RUNTIME DESTINATION bin)
Index: tools/CMakeLists.txt
===================================================================
--- tools/CMakeLists.txt
+++ tools/CMakeLists.txt
@@ -19,6 +19,8 @@
add_clang_subdirectory(clang-check)
add_clang_subdirectory(scan-build)
add_clang_subdirectory(scan-view)
+ add_clang_subdirectory(clang-func-mapping)
+ add_clang_subdirectory(clang-cmdline-arch-extractor)
endif()
# We support checking out the clang-tools-extra repository into the 'extra'
Index: test/Analysis/ctu-main.cpp
===================================================================
--- /dev/null
+++ test/Analysis/ctu-main.cpp
@@ -0,0 +1,58 @@
+// RUN: mkdir -p %T/ctudir
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-pch -o %T/ctudir/ctu-other.cpp.ast %S/Inputs/ctu-other.cpp
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-pch -o %T/ctudir/ctu-chain.cpp.ast %S/Inputs/ctu-chain.cpp
+// RUN: cp %S/Inputs/externalFnMap.txt %T/ctudir/
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -analyze -analyzer-checker=core,debug.ExprInspection -analyzer-config ctu-dir=%T/ctudir -analyzer-config reanalyze-ctu-visited=true -verify %s
+
+void clang_analyzer_eval(int);
+
+int f(int);
+int g(int);
+int h(int);
+
+int callback_to_main(int x) { return x + 1; }
+
+namespace myns {
+int fns(int x);
+
+namespace embed_ns {
+int fens(int x);
+}
+
+class embed_cls {
+public:
+ int fecl(int x);
+};
+}
+
+class mycls {
+public:
+ int fcl(int x);
+ static int fscl(int x);
+
+ class embed_cls2 {
+ public:
+ int fecl2(int x);
+ };
+};
+
+namespace chns {
+int chf1(int x);
+}
+
+int main() {
+ clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(f(5) == 3); // expected-warning{{FALSE}}
+ clang_analyzer_eval(g(4) == 6); // expected-warning{{TRUE}}
+ clang_analyzer_eval(h(2) == 8); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(myns::fns(2) == 9); // expected-warning{{TRUE}}
+ clang_analyzer_eval(myns::embed_ns::fens(2) == -1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(mycls().fcl(1) == 6); // expected-warning{{TRUE}}
+ clang_analyzer_eval(mycls::fscl(1) == 7); // expected-warning{{TRUE}}
+ clang_analyzer_eval(myns::embed_cls().fecl(1) == -6); // expected-warning{{TRUE}}
+ clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}}
+}
Index: test/Analysis/Inputs/externalFnMap.txt
===================================================================
--- /dev/null
+++ test/Analysis/Inputs/externalFnMap.txt
@@ -0,0 +1,14 @@
+_Z7h_chaini@x86_64 ctu-chain.cpp.ast
+_ZN4chns4chf2Ei@x86_64 ctu-chain.cpp.ast
+_ZN4chns5chcls4chf4Ei@x86_64 ctu-chain.cpp.ast
+_Z1fi@x86_64 ctu-other.cpp.ast
+_Z1gi@x86_64 ctu-other.cpp.ast
+_Z1hi@x86_64 ctu-other.cpp.ast
+_ZN4myns9embed_cls4feclEi@x86_64 ctu-other.cpp.ast
+_ZN4myns3fnsEi@x86_64 ctu-other.cpp.ast
+_ZN5mycls10embed_cls25fecl2Ei@x86_64 ctu-other.cpp.ast
+_ZN5mycls4fsclEi@x86_64 ctu-other.cpp.ast
+_ZN4chns4chf3Ei@x86_64 ctu-other.cpp.ast
+_ZN5mycls3fclEi@x86_64 ctu-other.cpp.ast
+_ZN4chns4chf1Ei@x86_64 ctu-other.cpp.ast
+_ZN4myns8embed_ns4fensEi@x86_64 ctu-other.cpp.ast
Index: test/Analysis/Inputs/ctu-other.cpp
===================================================================
--- /dev/null
+++ test/Analysis/Inputs/ctu-other.cpp
@@ -0,0 +1,67 @@
+int callback_to_main(int x);
+int f(int x) {
+ return x - 1;
+}
+
+int g(int x) {
+ return callback_to_main(x) + 1;
+}
+
+int h_chain(int);
+
+int h(int x) {
+ return 2 * h_chain(x);
+}
+
+namespace myns {
+int fns(int x) {
+ return x + 7;
+}
+
+namespace embed_ns {
+int fens(int x) {
+ return x - 3;
+}
+}
+
+class embed_cls {
+public:
+ int fecl(int x) {
+ return x - 7;
+ }
+};
+}
+
+class mycls {
+public:
+ int fcl(int x) {
+ return x + 5;
+ }
+ static int fscl(int x) {
+ return x + 6;
+ }
+
+ class embed_cls2 {
+ public:
+ int fecl2(int x) {
+ return x - 11;
+ }
+ };
+};
+
+namespace chns {
+int chf2(int x);
+
+class chcls {
+public:
+ int chf4(int x);
+};
+
+int chf3(int x) {
+ return chcls().chf4(x);
+}
+
+int chf1(int x) {
+ return chf2(x);
+}
+}
Index: test/Analysis/Inputs/ctu-chain.cpp
===================================================================
--- /dev/null
+++ test/Analysis/Inputs/ctu-chain.cpp
@@ -0,0 +1,20 @@
+int h_chain(int x) {
+ return x * 2;
+}
+
+namespace chns {
+int chf3(int x);
+
+int chf2(int x) {
+ return chf3(x);
+}
+
+class chcls {
+public:
+ int chf4(int x);
+};
+
+int chcls::chf4(int x) {
+ return x * 3;
+}
+}
Index: lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
===================================================================
--- lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -16,13 +16,16 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Mangle.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CallGraph.h"
#include "clang/Analysis/CodeInjector.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
@@ -40,9 +43,11 @@
#include "llvm/Support/Program.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
+#include <cassert>
#include <memory>
#include <queue>
#include <utility>
+#include <fstream>
using namespace clang;
using namespace ento;
@@ -168,6 +173,7 @@
AnalyzerOptionsRef Opts;
ArrayRef<std::string> Plugins;
CodeInjector *Injector;
+ CompilerInstance &CI;
/// \brief Stores the declarations from the local translation unit.
/// Note, we pre-compute the local declarations at parse time as an
@@ -192,12 +198,12 @@
/// translation unit.
FunctionSummariesTy FunctionSummaries;
- AnalysisConsumer(const Preprocessor &pp, const std::string &outdir,
- AnalyzerOptionsRef opts, ArrayRef<std::string> plugins,
- CodeInjector *injector)
+ AnalysisConsumer(CompilerInstance &CI, const Preprocessor &pp,
+ const std::string &outdir, AnalyzerOptionsRef opts,
+ ArrayRef<std::string> plugins, CodeInjector *injector)
: RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), PP(pp),
OutDir(outdir), Opts(std::move(opts)), Plugins(plugins),
- Injector(injector) {
+ Injector(injector), CI(CI) {
DigestAnalyzerOptions();
if (Opts->PrintStats) {
llvm::EnableStatistics(false);
@@ -415,6 +421,8 @@
}
}
+std::string getMangledName(const NamedDecl *ND, MangleContext *MangleCtx);
+
static bool shouldSkipFunction(const Decl *D,
const SetOfConstDecls &Visited,
const SetOfConstDecls &VisitedAsTopLevel) {
@@ -706,7 +714,8 @@
if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
return;
- ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,IMode);
+ ExprEngine Eng(CI, *Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,
+ IMode);
// Set the graph auditor.
std::unique_ptr<ExplodedNode::Auditor> Auditor;
@@ -764,7 +773,7 @@
bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;
return llvm::make_unique<AnalysisConsumer>(
- CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts,
+ CI, CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts,
CI.getFrontendOpts().Plugins,
hasModelPath ? new ModelInjector(CI) : nullptr);
}
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -67,25 +67,19 @@
static const char* TagProviderName = "ExprEngine";
-ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled,
- SetOfConstDecls *VisitedCalleesIn,
- FunctionSummariesTy *FS,
- InliningModes HowToInlineIn)
- : AMgr(mgr),
- AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
- Engine(*this, FS),
- G(Engine.getGraph()),
- StateMgr(getContext(), mgr.getStoreManagerCreator(),
- mgr.getConstraintManagerCreator(), G.getAllocator(),
- this),
- SymMgr(StateMgr.getSymbolManager()),
- svalBuilder(StateMgr.getSValBuilder()),
- currStmtIdx(0), currBldrCtx(nullptr),
- ObjCNoRet(mgr.getASTContext()),
- ObjCGCEnabled(gcEnabled), BR(mgr, *this),
- VisitedCallees(VisitedCalleesIn),
- HowToInline(HowToInlineIn)
-{
+ExprEngine::ExprEngine(CompilerInstance &CI, AnalysisManager &mgr,
+ bool gcEnabled, SetOfConstDecls *VisitedCalleesIn,
+ FunctionSummariesTy *FS, InliningModes HowToInlineIn)
+ : CI(CI), AMgr(mgr),
+ AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
+ Engine(*this, FS), G(Engine.getGraph()),
+ StateMgr(getContext(), mgr.getStoreManagerCreator(),
+ mgr.getConstraintManagerCreator(), G.getAllocator(), this),
+ SymMgr(StateMgr.getSymbolManager()),
+ svalBuilder(StateMgr.getSValBuilder()), currStmtIdx(0),
+ currBldrCtx(nullptr), ObjCNoRet(mgr.getASTContext()),
+ ObjCGCEnabled(gcEnabled), BR(mgr, *this),
+ VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) {
unsigned TrimInterval = mgr.options.getGraphTrimInterval();
if (TrimInterval != 0) {
// Enable eager node reclaimation when constructing the ExplodedGraph.
Index: lib/StaticAnalyzer/Core/CallEvent.cpp
===================================================================
--- lib/StaticAnalyzer/Core/CallEvent.cpp
+++ lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -16,6 +16,9 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/AST/ParentMap.h"
#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
#include "llvm/ADT/SmallSet.h"
@@ -355,6 +358,43 @@
D->parameters());
}
+RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const {
+ const FunctionDecl *FD = getDecl();
+ // Note that the AnalysisDeclContext will have the FunctionDecl with
+ // the definition (if one exists).
+ if (!FD)
+ return RuntimeDefinition();
+
+ AnalysisDeclContext *AD =
+ getLocationContext()->getAnalysisDeclContext()->
+ getManager()->getContext(FD);
+ if (AD->getBody())
+ return RuntimeDefinition(AD->getDecl());
+
+ auto Engine = static_cast<ExprEngine *>(
+ getState()->getStateManager().getOwningEngine());
+ CompilerInstance &CI = Engine->getCompilerInstance();
+
+ auto ASTLoader = [&](StringRef ASTFileName) {
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ TextDiagnosticPrinter *DiagClient =
+ new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
+ IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
+ new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient));
+ return ASTUnit::LoadFromASTFile(
+ ASTFileName, CI.getPCHContainerOperations()->getRawReader(),
+ Diags, CI.getFileSystemOpts());
+ };
+
+ const FunctionDecl *CTUDecl = AD->getASTContext().getCTUDefinition(
+ FD, CI, Engine->getAnalysisManager().options.getCTUDir(),
+ CI.getDiagnostics(), ASTLoader);
+
+ return RuntimeDefinition(CTUDecl);
+}
+
+
bool AnyFunctionCall::argumentsMayEscape() const {
if (CallEvent::argumentsMayEscape() || hasVoidPointerToNonConstArg())
return true;
Index: lib/StaticAnalyzer/Core/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Core/CMakeLists.txt
+++ lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -56,6 +56,7 @@
clangAST
clangAnalysis
clangBasic
+ clangFrontend
clangLex
clangRewrite
${Z3_LINK_FILES}
Index: lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
===================================================================
--- lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -370,3 +370,9 @@
getBooleanOption("notes-as-events", /*Default=*/false);
return DisplayNotesAsEvents.getValue();
}
+
+StringRef AnalyzerOptions::getCTUDir() {
+ if (!CTUDir.hasValue() || !llvm::sys::fs::is_directory(*CTUDir))
+ CTUDir = getOptionAsString("ctu-dir", "");
+ return CTUDir.getValue();
+}
Index: lib/Basic/SourceManager.cpp
===================================================================
--- lib/Basic/SourceManager.cpp
+++ lib/Basic/SourceManager.cpp
@@ -2025,6 +2025,8 @@
}
/// \brief Determines the order of 2 source locations in the translation unit.
+/// FIXME: It also works when two locations are from different translation
+/// units. In that case it will return *some* order.
///
/// \returns true if LHS source location comes before RHS, false otherwise.
bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS,
@@ -2121,7 +2123,8 @@
return LIsScratch;
return LOffs.second < ROffs.second;
}
- llvm_unreachable("Unsortable locations found");
+ // FIXME: Source locations from different translation units.
+ return LOffs.first < ROffs.first;
}
void SourceManager::PrintStats() const {
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -79,6 +79,17 @@
if (FD->isExternC())
return FD->getASTContext().getTranslationUnitDecl();
+ // Avoid infinite recursion with code like:
+ // void f(struct S* p);
+ // Where this is the first declaration of S. This is only valid for C.
+ // For some tools it makes sense to mangle C functions (e.g. avoid collisions
+ // when indexing). It might be nicer to check whether the Decl is in
+ // FunctionPrototypeScope, but this information is lost after the Sema is
+ // done.
+ if (!D->getASTContext().getLangOpts().CPlusPlus && DC->isFunctionOrMethod() &&
+ isa<TagDecl>(D))
+ return D->getASTContext().getTranslationUnitDecl();
+
return DC->getRedeclContext();
}
@@ -653,9 +664,11 @@
// <encoding> ::= <function name> <bare-function-type>
// Don't mangle in the type if this isn't a decl we should typically mangle.
- if (!Context.shouldMangleDeclName(FD)) {
- mangleName(FD);
- return;
+ if (!Context.shouldMangleDeclName(FD) &&
+ !(Context.shouldForceMangleProto() &&
+ FD->getType()->getAs<FunctionProtoType>())){
+ mangleName(FD);
+ return;
}
AbiTagList ReturnTypeAbiTags = makeFunctionReturnTypeTags(FD);
Index: lib/AST/ASTImporter.cpp
===================================================================
--- lib/AST/ASTImporter.cpp
+++ lib/AST/ASTImporter.cpp
@@ -3206,6 +3206,8 @@
if (ToD)
return ToD;
+ const FunctionDecl *FoundWithoutBody = nullptr;
+
// Try to find a function in our own ("to") context with the same name, same
// type, and in the same context as the function we're importing.
if (!LexicalDC->isFunctionOrMethod()) {
@@ -3223,6 +3225,13 @@
if (Importer.IsStructurallyEquivalent(D->getType(),
FoundFunction->getType())) {
// FIXME: Actually try to merge the body and other attributes.
+ const FunctionDecl *FromBodyDecl = nullptr;
+ D->hasBody(FromBodyDecl);
+ if (D == FromBodyDecl && !FoundFunction->hasBody()) {
+ // This function is needed to merge completely.
+ FoundWithoutBody = FoundFunction;
+ break;
+ }
return Importer.Imported(D, FoundFunction);
}
@@ -3373,6 +3382,12 @@
}
ToFunction->setParams(Parameters);
+ if (FoundWithoutBody) {
+ auto *Recent = const_cast<FunctionDecl *>(
+ FoundWithoutBody->getMostRecentDecl());
+ ToFunction->setPreviousDecl(Recent);
+ }
+
if (usedDifferentExceptionSpec) {
// Update FunctionProtoType::ExtProtoInfo.
QualType T = Importer.Import(D->getType());
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -13,6 +13,7 @@
#include "clang/AST/ASTContext.h"
#include "CXXABI.h"
+#include "clang/AST/ASTImporter.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/Attr.h"
#include "clang/AST/CharUnits.h"
@@ -34,11 +35,15 @@
#include "clang/Basic/Builtins.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Support/Capacity.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
+#include <fstream>
#include <map>
using namespace clang;
@@ -1420,6 +1425,131 @@
}
}
+
+//===----------------------------------------------------------------------===//
+// Cross-translation unit support
+//===----------------------------------------------------------------------===//
+
+std::string getMangledName(const NamedDecl *ND, MangleContext *MangleCtx) {
+ std::string MangledName;
+ llvm::raw_string_ostream OS(MangledName);
+ if (const auto *CCD = dyn_cast<CXXConstructorDecl>(ND))
+ // FIXME: Use correct Ctor/DtorType
+ MangleCtx->mangleCXXCtor(CCD, Ctor_Complete, OS);
+ else if (const auto *CDD = dyn_cast<CXXDestructorDecl>(ND))
+ MangleCtx->mangleCXXDtor(CDD, Dtor_Complete, OS);
+ else
+ MangleCtx->mangleName(ND, OS);
+ ASTContext &Ctx = ND->getASTContext();
+ // We are not going to support vendor and don't support OS and environment.
+ // FIXME: support OS and environment correctly
+ llvm::Triple::ArchType T = Ctx.getTargetInfo().getTriple().getArch();
+ if (T == llvm::Triple::thumb)
+ T = llvm::Triple::arm;
+ OS << "@" << Ctx.getTargetInfo().getTriple().getArchTypeName(T);
+ return OS.str();
+}
+
+/// Recursively visit the funtion decls of a DeclContext, and looks up a
+/// function based on mangled name.
+static const FunctionDecl *
+findFunctionInDeclContext(const DeclContext *DC, StringRef MangledFnName,
+ std::unique_ptr<MangleContext> &MangleCtx) {
+ if (!DC)
+ return nullptr;
+ for (const Decl *D : DC->decls()) {
+ const auto *SubDC = dyn_cast<DeclContext>(D);
+ if (const auto *FD =
+ findFunctionInDeclContext(SubDC, MangledFnName, MangleCtx))
+ return FD;
+
+ const auto *ND = dyn_cast<FunctionDecl>(D);
+ const FunctionDecl *ResultDecl;
+ if (!ND || !ND->hasBody(ResultDecl))
+ continue;
+ std::string LookupMangledName = getMangledName(ResultDecl, MangleCtx.get());
+ // We are already sure that the triple is correct here.
+ if (LookupMangledName != MangledFnName)
+ continue;
+ return ResultDecl;
+ }
+ return nullptr;
+}
+
+const FunctionDecl *ASTContext::getCTUDefinition(
+ const FunctionDecl *FD, CompilerInstance &CI, StringRef CTUDir,
+ DiagnosticsEngine &Diags,
+ std::function<std::unique_ptr<clang::ASTUnit>(StringRef)> Loader) {
+ assert(!FD->hasBody() && "FD has a definition in current translation unit!");
+ if (!FD->getType()->getAs<FunctionProtoType>())
+ return nullptr; // Cannot even mangle that.
+ auto FoundImport = ImportMap.find(FD);
+ if (FoundImport != ImportMap.end())
+ return FoundImport->second;
+
+ std::unique_ptr<MangleContext> MangleCtx(
+ ItaniumMangleContext::create(FD->getASTContext(), Diags));
+ MangleCtx->setShouldForceMangleProto(true);
+ std::string MangledFnName = getMangledName(FD, MangleCtx.get());
+ ASTUnit *Unit = nullptr;
+ StringRef ASTFileName;
+ auto FnUnitCacheEntry = FunctionAstUnitMap.find(MangledFnName);
+ if (FnUnitCacheEntry == FunctionAstUnitMap.end()) {
+ if (FunctionFileMap.empty()) {
+ std::string ExternalFunctionMap = (CTUDir + "/externalFnMap.txt").str();
+ std::ifstream ExternalFnMapFile(ExternalFunctionMap);
+ std::string FunctionName, FileName;
+ while (ExternalFnMapFile >> FunctionName >> FileName)
+ FunctionFileMap[FunctionName] = (CTUDir + "/" + FileName).str();
+ }
+
+ auto It = FunctionFileMap.find(MangledFnName);
+ if (It != FunctionFileMap.end())
+ ASTFileName = It->second;
+ else // No definition found even in some other build unit.
+ return nullptr;
+ auto ASTCacheEntry = FileASTUnitMap.find(ASTFileName);
+ if (ASTCacheEntry == FileASTUnitMap.end()) {
+ std::unique_ptr<ASTUnit> LoadedUnit(Loader(ASTFileName));
+ Unit = LoadedUnit.get();
+ FileASTUnitMap[ASTFileName] = std::move(LoadedUnit);
+ } else {
+ Unit = ASTCacheEntry->second.get();
+ }
+ FunctionAstUnitMap[MangledFnName] = Unit;
+ } else {
+ Unit = FnUnitCacheEntry->second;
+ }
+
+ if (!Unit)
+ return nullptr;
+ assert(&Unit->getFileManager() ==
+ &Unit->getASTContext().getSourceManager().getFileManager());
+ ASTImporter &Importer = getOrCreateASTImporter(Unit->getASTContext());
+ TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
+ if (const FunctionDecl *ResultDecl =
+ findFunctionInDeclContext(TU, MangledFnName, MangleCtx)) {
+ // FIXME: Refactor const_cast
+ auto *ToDecl = cast<FunctionDecl>(
+ Importer.Import(const_cast<FunctionDecl *>(ResultDecl)));
+ assert(ToDecl->hasBody());
+ ImportMap[FD] = ToDecl;
+ return ToDecl;
+ }
+ return nullptr;
+}
+
+ASTImporter &ASTContext::getOrCreateASTImporter(ASTContext &From) {
+ auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
+ if (I != ASTUnitImporterMap.end())
+ return *I->second;
+ ASTImporter *NewImporter = new ASTImporter(
+ *this, getSourceManager().getFileManager(),
+ From, From.getSourceManager().getFileManager(), false);
+ ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
+ return *NewImporter;
+}
+
CharUnits ASTContext::getDeclAlign(const Decl *D, bool ForAlignof) const {
unsigned Align = Target->getCharWidth();
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -56,6 +56,8 @@
};
private:
+ CompilerInstance &CI;
+
AnalysisManager &AMgr;
AnalysisDeclContextManager &AnalysisDeclContexts;
@@ -97,9 +99,8 @@
InliningModes HowToInline;
public:
- ExprEngine(AnalysisManager &mgr, bool gcEnabled,
- SetOfConstDecls *VisitedCalleesIn,
- FunctionSummariesTy *FS,
+ ExprEngine(CompilerInstance &CI, AnalysisManager &mgr, bool gcEnabled,
+ SetOfConstDecls *VisitedCalleesIn, FunctionSummariesTy *FS,
InliningModes HowToInlineIn);
~ExprEngine() override;
@@ -132,6 +133,8 @@
BugReporter& getBugReporter() { return BR; }
+ CompilerInstance &getCompilerInstance() { return CI; }
+
const NodeBuilderContext &getBuilderContext() {
assert(currBldrCtx);
return *currBldrCtx;
Index: include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -436,20 +436,7 @@
return cast<FunctionDecl>(CallEvent::getDecl());
}
- RuntimeDefinition getRuntimeDefinition() const override {
- const FunctionDecl *FD = getDecl();
- // Note that the AnalysisDeclContext will have the FunctionDecl with
- // the definition (if one exists).
- if (FD) {
- AnalysisDeclContext *AD =
- getLocationContext()->getAnalysisDeclContext()->
- getManager()->getContext(FD);
- if (AD->getBody())
- return RuntimeDefinition(AD->getDecl());
- }
-
- return RuntimeDefinition();
- }
+ RuntimeDefinition getRuntimeDefinition() const override;
bool argumentsMayEscape() const override;
Index: include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
===================================================================
--- include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
+++ include/clang/StaticAnalyzer/Core/AnalyzerOptions.h
@@ -272,6 +272,9 @@
/// \sa shouldDisplayNotesAsEvents
Optional<bool> DisplayNotesAsEvents;
+ /// \sa getCTUDir
+ Optional<StringRef> CTUDir;
+
/// A helper function that retrieves option for a given full-qualified
/// checker name.
/// Options for checkers can be specified via 'analyzer-config' command-line
@@ -548,6 +551,9 @@
/// to false when unset.
bool shouldDisplayNotesAsEvents();
+ /// Returns the directory containing the CTU related files.
+ StringRef getCTUDir();
+
public:
AnalyzerOptions() :
AnalysisStoreOpt(RegionStoreModel),
Index: include/clang/AST/Mangle.h
===================================================================
--- include/clang/AST/Mangle.h
+++ include/clang/AST/Mangle.h
@@ -52,6 +52,11 @@
ASTContext &Context;
DiagnosticsEngine &Diags;
const ManglerKind Kind;
+ // Used for cross translation unit analysis.
+ // To reduce the risk of function name collision in C projects, we force
+ // name mangling for C functions when generating lookup identifiers for
+ // the static analyzer.
+ bool ShouldForceMangleProto;
llvm::DenseMap<const BlockDecl*, unsigned> GlobalBlockIds;
llvm::DenseMap<const BlockDecl*, unsigned> LocalBlockIds;
@@ -87,6 +92,11 @@
return Result.first->second;
}
+ bool shouldForceMangleProto() const { return ShouldForceMangleProto; }
+ void setShouldForceMangleProto(bool ForceMangleArguments) {
+ ShouldForceMangleProto = ForceMangleArguments;
+ }
+
/// @name Mangler Entry Points
/// @{
Index: include/clang/AST/Decl.h
===================================================================
--- include/clang/AST/Decl.h
+++ include/clang/AST/Decl.h
@@ -50,6 +50,7 @@
class TypeLoc;
class UnresolvedSetImpl;
class VarTemplateDecl;
+class CompilerInstance;
/// \brief A container of type source information.
///
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -63,6 +63,7 @@
#include <cassert>
#include <cstddef>
#include <cstdint>
+#include <functional>
#include <iterator>
#include <memory>
#include <new>
@@ -78,12 +79,14 @@
} // end namespace llvm
namespace clang {
-
+class ASTImporter;
class ASTMutationListener;
class ASTRecordLayout;
+class ASTUnit;
class AtomicExpr;
class BlockExpr;
class CharUnits;
+class CompilerInstance;
class CXXABI;
class DiagnosticsEngine;
class Expr;
@@ -1902,6 +1905,24 @@
}
//===--------------------------------------------------------------------===//
+ // Cross-translation unit support
+ //===--------------------------------------------------------------------===//
+private:
+ llvm::StringMap<std::unique_ptr<clang::ASTUnit>> FileASTUnitMap;
+ llvm::StringMap<clang::ASTUnit *> FunctionAstUnitMap;
+ llvm::StringMap<std::string> FunctionFileMap;
+ llvm::DenseMap<TranslationUnitDecl *, std::unique_ptr<ASTImporter>>
+ ASTUnitImporterMap;
+ llvm::DenseMap<const FunctionDecl *, const FunctionDecl *> ImportMap;
+ ASTImporter &getOrCreateASTImporter(ASTContext &From);
+
+public:
+ const FunctionDecl *getCTUDefinition(
+ const FunctionDecl *FD, CompilerInstance &CI, StringRef CTUDir,
+ DiagnosticsEngine &Diags,
+ std::function<std::unique_ptr<clang::ASTUnit>(StringRef)> Loader);
+
+ //===--------------------------------------------------------------------===//
// Type Sizing and Analysis
//===--------------------------------------------------------------------===//
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits