Index: docs/ClangCommandLineReference.rst =================================================================== --- docs/ClangCommandLineReference.rst +++ docs/ClangCommandLineReference.rst @@ -1341,7 +1341,7 @@ .. option:: -finput-charset= -.. option:: -finstrument-functions +.. option:: -finstrument-functions, -fno-instrument-functions, -finstrument-functions-exclude-file-list=, -finstrument-functions-exclude-function-list= Generate calls to instrument function entry and exit Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1010,8 +1010,17 @@ HelpText<"Enables an experimental new pass manager in LLVM.">; def finput_charset_EQ : Joined<["-"], "finput-charset=">, Group; def fexec_charset_EQ : Joined<["-"], "fexec-charset=">, Group; + +def finstrument_functions_exclude_function_list : CommaJoined<["-"], + "finstrument-functions-exclude-function-list=">, Group, Flags<[CC1Option]>, + HelpText<"Exclude given (demangled, if C++) functions from function instrumentation.">; +def finstrument_functions_exclude_file_list : CommaJoined<["-"], + "finstrument-functions-exclude-file-list=">, Group, Flags<[CC1Option]>, + HelpText<"Exclude given path segments from function instrumentation.">; def finstrument_functions : Flag<["-"], "finstrument-functions">, Group, Flags<[CC1Option]>, HelpText<"Generate calls to instrument function entry and exit">; +def fno_instrument_functions : Flag<["-"], "fno-instrument-functions">, Group, + HelpText<"Don't generate calls to instrument function entry and exit">; def fxray_instrument : Flag<["-"], "fxray-instrument">, Group, Flags<[CC1Option]>, Index: include/clang/Frontend/CodeGenOptions.h =================================================================== --- include/clang/Frontend/CodeGenOptions.h +++ include/clang/Frontend/CodeGenOptions.h @@ -182,6 +182,12 @@ /// A list of dependent libraries. std::vector DependentLibraries; + /// Functions to exclude from function instrumentation. + std::vector InstrumentFunctionExclusionsFunctions; + + /// Path segments to exclude from function instrumentation, eg, '/bits' + std::vector InstrumentFunctionExclusionsPathSegments; + /// A list of linker options to embed in the object file. std::vector LinkerOptions; Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -1763,7 +1763,7 @@ /// ShouldInstrumentFunction - Return true if the current function should be /// instrumented with __cyg_profile_func_* calls - bool ShouldInstrumentFunction(); + bool ShouldInstrumentFunction(const llvm::Function *Fn); /// ShouldXRayInstrument - Return true if the current function should be /// instrumented with XRay nop sleds. Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -36,6 +36,7 @@ #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Operator.h" +#include "llvm/ADT/DenseMap.h" using namespace clang; using namespace CodeGen; @@ -344,7 +345,7 @@ // Emit function epilog (to return). llvm::DebugLoc Loc = EmitReturnBlock(); - if (ShouldInstrumentFunction()) + if (ShouldInstrumentFunction(CurFn)) EmitFunctionInstrumentation("__cyg_profile_func_exit"); // Emit debug descriptor for function end. @@ -415,11 +416,65 @@ /// ShouldInstrumentFunction - Return true if the current function should be /// instrumented with __cyg_profile_func_* calls -bool CodeGenFunction::ShouldInstrumentFunction() { +bool CodeGenFunction::ShouldInstrumentFunction(const llvm::Function *Fn) { if (!CGM.getCodeGenOpts().InstrumentFunctions) return false; if (!CurFuncDecl || CurFuncDecl->hasAttr()) return false; + + // Inline functions that are not externally visible mustn't be instrumented. + // They create a named reference to the inlined function, as the first + // parameter to __cyg_profile_* functions, which a linker will never be able + // to resolve. + const auto *ActualFuncDecl = dyn_cast(CurFuncDecl); + if (ActualFuncDecl && + ActualFuncDecl->isInlined() && + !ActualFuncDecl->isInlineDefinitionExternallyVisible()) { + return false; + } + + SourceLocation SLoc = CurFuncDecl->getLocation(); + + + if (SLoc.isFileID()) { + unsigned Key = SLoc.getRawEncoding(); + auto &Cache = CGM.GetSourceLocToFileNameMap(); + if (Cache.find(Key) == Cache.end()) { + const ASTContext &CTX = CurFuncDecl->getASTContext(); + const SourceManager &SM = CTX.getSourceManager(); + + PresumedLoc PLoc = SM.getPresumedLoc(SLoc); + Cache[Key] = PLoc.getFilename(); + } + std::string FunctionDeclPath = Cache[Key]; + + const std::vector &PathSearch = + CGM.getCodeGenOpts().InstrumentFunctionExclusionsPathSegments; + + for (const auto &FileMatch : PathSearch) { + if(FunctionDeclPath.find(FileMatch) != std::string::npos) { + return false; + } + } + } + + StringRef FunctionName(Fn->getName()); + + // For CPP files, use user-visible symbol name, not mangled name + // Skip if marked extern "C" + if (ActualFuncDecl && !ActualFuncDecl->isExternC()) { + FunctionName = StringRef(PredefinedExpr::ComputeName( + PredefinedExpr::PrettyFunctionNoVirtual, CurFuncDecl)); + } + + const std::vector &FunctionSearch = + CGM.getCodeGenOpts().InstrumentFunctionExclusionsFunctions; + for (const auto &FuncMatch : FunctionSearch) { + if(FunctionName.find(FuncMatch) != std::string::npos) { + return false; + } + } + return true; } @@ -971,7 +1026,7 @@ DI->EmitFunctionStart(GD, Loc, StartLoc, FnType, CurFn, Builder); } - if (ShouldInstrumentFunction()) + if (ShouldInstrumentFunction(CurFn)) EmitFunctionInstrumentation("__cyg_profile_func_enter"); // Since emitting the mcount call here impacts optimizations such as function Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -499,6 +499,9 @@ /// MDNodes. llvm::DenseMap MetadataIdMap; + /// Mapping from SourceLocation to PresumedLoc FileName + llvm::DenseMap SourceLocToFileNameMap; + public: CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts, const PreprocessorOptions &ppopts, @@ -1205,6 +1208,11 @@ /// \param QT is the clang QualType of the null pointer. llvm::Constant *getNullPointer(llvm::PointerType *T, QualType QT); + /// Get SourceLoc to FileName map cache + llvm::DenseMap &GetSourceLocToFileNameMap() { + return SourceLocToFileNameMap; + } + private: llvm::Constant *GetOrCreateLLVMFunction( StringRef MangledName, llvm::Type *Ty, GlobalDecl D, bool ForVTable, Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -3535,7 +3535,14 @@ options::OPT_fno_unique_section_names, true)) CmdArgs.push_back("-fno-unique-section-names"); - Args.AddAllArgs(CmdArgs, options::OPT_finstrument_functions); + if (Args.hasArg(options::OPT_finstrument_functions, + options::OPT_fno_instrument_functions, false)) { + Args.AddAllArgs(CmdArgs, options::OPT_finstrument_functions); + Args.AddAllArgs(CmdArgs, + options::OPT_finstrument_functions_exclude_file_list); + Args.AddAllArgs(CmdArgs, + options::OPT_finstrument_functions_exclude_function_list); + } addPGOAndCoverageFlags(C, D, Output, Args, CmdArgs); Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -773,6 +773,12 @@ Opts.PreserveVec3Type = Args.hasArg(OPT_fpreserve_vec3_type); Opts.InstrumentFunctions = Args.hasArg(OPT_finstrument_functions); + if (Opts.InstrumentFunctions) { + Opts.InstrumentFunctionExclusionsFunctions + = Args.getAllArgValues(OPT_finstrument_functions_exclude_function_list); + Opts.InstrumentFunctionExclusionsPathSegments + = Args.getAllArgValues(OPT_finstrument_functions_exclude_file_list); + } Opts.XRayInstrumentFunctions = Args.hasArg(OPT_fxray_instrument); Opts.XRayInstructionThreshold = getLastArgIntValue(Args, OPT_fxray_instruction_threshold_EQ, 200, Diags); Index: test/CodeGen/instrument-functions.c =================================================================== --- test/CodeGen/instrument-functions.c +++ test/CodeGen/instrument-functions.c @@ -1,18 +1,66 @@ -// RUN: %clang_cc1 -S -debug-info-kind=standalone -emit-llvm -o - %s -finstrument-functions | FileCheck %s +// RUN: %clang -S -emit-llvm -o - %s -finstrument-functions | FileCheck %s +// RUN: %clang -S -emit-llvm -o - %s -fno-instrument-functions | FileCheck %s --check-prefix=NOINSTR + +// RUN: %clang -S -emit-llvm -o - %s -finstrument-functions -finstrument-functions-exclude-file-list=instrument | FileCheck %s --check-prefix=NOFILE +// RUN: %clang -S -emit-llvm -o - %s -finstrument-functions -finstrument-functions-exclude-function-list=test3 | FileCheck %s --check-prefix=NOFUNC // CHECK: @test1 +// NOINSTR: @test1 +// NOFILE: @test1 +// NOFUNC: @test1 int test1(int x) { -// CHECK: call void @__cyg_profile_func_enter({{.*}}, !dbg -// CHECK: call void @__cyg_profile_func_exit({{.*}}, !dbg +// CHECK: __cyg_profile_func_enter +// CHECK: __cyg_profile_func_exit // CHECK: ret +// NOINSTR-NOT: __cyg_profile_func_enter +// NOINSTR-NOT: __cyg_profile_func_exit +// NOINSTR: ret +// NOFILE-NOT: __cyg_profile_func_enter +// NOFILE-NOT: __cyg_profile_func_exit +// NOFILE: ret +// NOFUNC: __cyg_profile_func_enter +// NOFUNC: __cyg_profile_func_exit +// NOFUNC: ret return x; } // CHECK: @test2 +// NOINSTR: @test2 +// NOFILE: @test2 +// NOFUNC: @test2 int test2(int) __attribute__((no_instrument_function)); int test2(int x) { // CHECK-NOT: __cyg_profile_func_enter // CHECK-NOT: __cyg_profile_func_exit // CHECK: ret +// NOINSTR-NOT: __cyg_profile_func_enter +// NOINSTR-NOT: __cyg_profile_func_exit +// NOINSTR: ret +// NOFILE-NOT: __cyg_profile_func_enter +// NOFILE-NOT: __cyg_profile_func_exit +// NOFILE: ret +// NOFUNC-NOT: __cyg_profile_func_enter +// NOFUNC-NOT: __cyg_profile_func_exit +// NOFUNC: ret + return x; +} + +// CHECK: @test3 +// NOINSTR: @test3 +// NOFILE: @test3 +// NOFUNC: @test3 +int test3(int x) { +// CHECK: __cyg_profile_func_enter +// CHECK: __cyg_profile_func_exit +// CHECK: ret +// NOINSTR-NOT: __cyg_profile_func_enter +// NOINSTR-NOT: __cyg_profile_func_exit +// NOINSTR: ret +// NOFILE-NOT: __cyg_profile_func_enter +// NOFILE-NOT: __cyg_profile_func_exit +// NOFILE: ret +// NOFUNC-NOT: __cyg_profile_func_enter +// NOFUNC-NOT: __cyg_profile_func_exit +// NOFUNC: ret return x; } Index: test/CodeGenCXX/instrument-functions.cpp =================================================================== --- test/CodeGenCXX/instrument-functions.cpp +++ test/CodeGenCXX/instrument-functions.cpp @@ -1,10 +1,32 @@ // RUN: %clang_cc1 -S -emit-llvm -triple %itanium_abi_triple -o - %s -finstrument-functions | FileCheck %s +// RUN: %clang -S -emit-llvm -o - %s -finstrument-functions | FileCheck %s +// RUN: %clang -S -emit-llvm -o - %s -fno-instrument-functions | FileCheck %s --check-prefix=NOINSTR + +// RUN: %clang -S -emit-llvm -o - %s -finstrument-functions -finstrument-functions-exclude-file-list=instrument | FileCheck %s --check-prefix=NOFILE +// RUN: %clang -S -emit-llvm -o - %s -finstrument-functions -finstrument-functions-exclude-function-list=test3 | FileCheck %s --check-prefix=NOFUNC + +// Below would see if mangled name partially matches. exclude-function-list matches demangled names, thus we expect to see instrument calls in test3. +// RUN: %clang -S -emit-llvm -o - %s -finstrument-functions -finstrument-functions-exclude-function-list=Z5test3 | FileCheck %s --check-prefix=NOFUNC2 + + // CHECK: @_Z5test1i +// NOINSTR: @_Z5test1i +// NOFILE: @_Z5test1i +// NOFUNC: @_Z5test1i int test1(int x) { // CHECK: __cyg_profile_func_enter // CHECK: __cyg_profile_func_exit // CHECK: ret +// NOINSTR-NOT: __cyg_profile_func_enter +// NOINSTR-NOT: __cyg_profile_func_exit +// NOINSTR: ret +// NOFILE-NOT: __cyg_profile_func_enter +// NOFILE-NOT: __cyg_profile_func_exit +// NOFILE: ret +// NOFUNC: __cyg_profile_func_enter +// NOFUNC: __cyg_profile_func_exit +// NOFUNC: ret return x; } @@ -17,6 +39,30 @@ return x; } +// CHECK: @_Z5test3i +// NOINSTR: @_Z5test3i +// NOFILE: @_Z5test3i +// NOFUNC: @_Z5test3i +// NOFUNC2: @_Z5test3i +int test3(int x){ +// CHECK: __cyg_profile_func_enter +// CHECK: __cyg_profile_func_exit +// CHECK: ret +// NOINSTR-NOT: __cyg_profile_func_enter +// NOINSTR-NOT: __cyg_profile_func_exit +// NOINSTR: ret +// NOFILE-NOT: __cyg_profile_func_enter +// NOFILE-NOT: __cyg_profile_func_exit +// NOFILE: ret +// NOFUNC-NOT: __cyg_profile_func_enter +// NOFUNC-NOT: __cyg_profile_func_exit +// NOFUNC: ret +// NOFUNC2: __cyg_profile_func_enter +// NOFUNC2: __cyg_profile_func_exit +// NOFUNC2: ret + return x; +} + // This test case previously crashed code generation. It exists solely // to test -finstrument-function does not crash codegen for this trivial // case.