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 @@ -1007,8 +1007,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 (mangled, 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 @@ -1762,7 +1762,7 @@ /// ShouldInstrumentFunction - Return true if the current function should be /// instrumented with __cyg_profile_func_* calls - bool ShouldInstrumentFunction(); + bool ShouldInstrumentFunction(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 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. @@ -413,13 +414,81 @@ } } +#if !defined(_MSC_VER) +// Assume that __cxa_demangle is provided by libcxxabi (except for Windows). +extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer, + size_t *length, int *status); +#endif + /// ShouldInstrumentFunction - Return true if the current function should be /// instrumented with __cyg_profile_func_* calls -bool CodeGenFunction::ShouldInstrumentFunction() { +bool CodeGenFunction::ShouldInstrumentFunction(llvm::Function *Fn) { + typedef std::vector::const_iterator CIt; 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 FunctionDecl *ActualFuncDecl = dyn_cast(CurFuncDecl); + if (ActualFuncDecl && + ActualFuncDecl->isInlined() && + !ActualFuncDecl->isInlineDefinitionExternallyVisible()) { + return false; + } + + SourceLocation SLoc = CurFuncDecl->getLocation(); + static std::unordered_map cache; + + if (SLoc.isFileID()) { + unsigned key = SLoc.getRawEncoding(); + if (cache.find(key) == cache.end()) { + 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 (CIt i = PathSearch.begin(), e = PathSearch.end(); i != e; ++i) { + if(FunctionDeclPath.find(*i) != std::string::npos) { + return false; + } + } + } + + std::string FunctionName = Fn->getName(); + + // Skip demangling if decl is extern "C" +#if !defined(_MSC_VER) + if (ActualFuncDecl && !ActualFuncDecl->isExternC()) { + int status = 0; + char *result = __cxa_demangle(FunctionName.c_str(), 0, 0, &status); + + assert((status == 0 || status == -2) && "Couldn't demangle name."); + + if (status == 0) { + FunctionName = result; + free(result); + } + } +#endif + + const std::vector &FunctionSearch = + CGM.getCodeGenOpts().InstrumentFunctionExclusionsFunctions; + for (CIt i = FunctionSearch.begin(), e = FunctionSearch.end(); i != e; ++i) { + if(FunctionName.find(*i) != std::string::npos) { + return false; + } + } + return true; } @@ -920,7 +989,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/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -3519,7 +3519,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 @@ -771,6 +771,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; }