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
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to