Skip to content

Commit

Permalink
[MemProf] Pass down memory profile name with optional path from clang
Browse files Browse the repository at this point in the history
Similar to -fprofile-generate=, add -fmemory-profile= which takes a
directory path. This is passed down to LLVM via a new module flag
metadata. LLVM in turn provides this name to the runtime via the new
__memprof_profile_filename variable.

Additionally, always pass a default filename (in $cwd if a directory
name is not specified vi the = form of the option). This is also
consistent with the behavior of the PGO instrumentation. Since the
memory profiles will generally be fairly large, it doesn't make sense to
dump them to stderr. Also, importantly, the memory profiles will
eventually be dumped in a compact binary format, which is another reason
why it does not make sense to send these to stderr by default.

Change the existing memprof tests to specify log_path=stderr when that
was being relied on.

Depends on D89086.

Differential Revision: https://reviews.llvm.org/D89087
  • Loading branch information
teresajohnson committed Nov 2, 2020
1 parent cc91554 commit 0949f96
Show file tree
Hide file tree
Showing 24 changed files with 110 additions and 31 deletions.
1 change: 0 additions & 1 deletion clang/include/clang/Basic/CodeGenOptions.def
Expand Up @@ -151,7 +151,6 @@ CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can
///< linker.
CODEGENOPT(MergeAllConstants , 1, 1) ///< Merge identical constants.
CODEGENOPT(MergeFunctions , 1, 0) ///< Set when -fmerge-functions is enabled.
CODEGENOPT(MemProf , 1, 0) ///< Set when -fmemory-profile is enabled.
CODEGENOPT(MSVolatile , 1, 0) ///< Set when /volatile:ms is enabled.
CODEGENOPT(NoCommon , 1, 0) ///< Set when -fno-common or C++ is enabled.
CODEGENOPT(NoDwarfDirectoryAsm , 1, 0) ///< Set when -fno-dwarf-directory-asm is
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/CodeGenOptions.h
Expand Up @@ -231,6 +231,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// Name of the profile file to use with -fprofile-sample-use.
std::string SampleProfileFile;

/// Name of the profile file to use as output for with -fmemory-profile.
std::string MemoryProfileOutput;

/// Name of the profile file to use as input for -fprofile-instr-use
std::string ProfileInstrumentUsePath;

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Driver/Options.td
Expand Up @@ -1017,6 +1017,9 @@ def fsymbol_partition_EQ : Joined<["-"], "fsymbol-partition=">, Group<f_Group>,
Flags<[CC1Option]>;

defm memory_profile : OptInFFlag<"memory-profile", "Enable", "Disable", " heap memory profiling">;
def fmemory_profile_EQ : Joined<["-"], "fmemory-profile=">,
Group<f_Group>, Flags<[CC1Option]>, MetaVarName<"<directory>">,
HelpText<"Enable heap memory profiling and dump results into <directory>">;

// Begin sanitizer flags. These should all be core options exposed in all driver
// modes.
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/BackendUtil.cpp
Expand Up @@ -687,7 +687,7 @@ void EmitAssemblyHelper::CreatePasses(legacy::PassManager &MPM,
if (LangOpts.Coroutines)
addCoroutinePassesToExtensionPoints(PMBuilder);

if (CodeGenOpts.MemProf) {
if (!CodeGenOpts.MemoryProfileOutput.empty()) {
PMBuilder.addExtension(PassManagerBuilder::EP_OptimizerLast,
addMemProfilerPasses);
PMBuilder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
Expand Down Expand Up @@ -1421,7 +1421,7 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager(
if (CodeGenOpts.UniqueInternalLinkageNames)
MPM.addPass(UniqueInternalLinkageNamesPass());

if (CodeGenOpts.MemProf) {
if (!CodeGenOpts.MemoryProfileOutput.empty()) {
MPM.addPass(createModuleToFunctionPassAdaptor(MemProfilerPass()));
MPM.addPass(ModuleMemProfilerPass());
}
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Expand Up @@ -607,6 +607,13 @@ void CodeGenModule::Release() {
!LangOpts.isSignReturnAddressWithAKey());
}

if (!CodeGenOpts.MemoryProfileOutput.empty()) {
llvm::LLVMContext &Ctx = TheModule.getContext();
getModule().addModuleFlag(
llvm::Module::Error, "MemProfProfileFilename",
llvm::MDString::get(Ctx, CodeGenOpts.MemoryProfileOutput));
}

if (LangOpts.CUDAIsDevice && getTriple().isNVPTX()) {
// Indicate whether __nvvm_reflect should be configured to flush denormal
// floating point values to 0. (This corresponds to its "__CUDA_FTZ"
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Driver/SanitizerArgs.cpp
Expand Up @@ -868,6 +868,7 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
D.CCCIsCXX();

NeedsMemProfRt = Args.hasFlag(options::OPT_fmemory_profile,
options::OPT_fmemory_profile_EQ,
options::OPT_fno_memory_profile, false);

// Finally, initialize the set of available and recoverable sanitizers.
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/Driver/ToolChains/Clang.cpp
Expand Up @@ -4309,9 +4309,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
if (Args.getLastArg(options::OPT_save_temps_EQ))
Args.AddLastArg(CmdArgs, options::OPT_save_temps_EQ);

if (Args.hasFlag(options::OPT_fmemory_profile,
options::OPT_fno_memory_profile, false))
Args.AddLastArg(CmdArgs, options::OPT_fmemory_profile);
auto *MemProfArg = Args.getLastArg(options::OPT_fmemory_profile,
options::OPT_fmemory_profile_EQ,
options::OPT_fno_memory_profile);
if (MemProfArg &&
!MemProfArg->getOption().matches(options::OPT_fno_memory_profile))
MemProfArg->render(Args, CmdArgs);

// Embed-bitcode option.
// Only white-listed flags below are allowed to be embedded.
Expand Down
10 changes: 9 additions & 1 deletion clang/lib/Frontend/CompilerInvocation.cpp
Expand Up @@ -1038,7 +1038,15 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
Opts.ThinLinkBitcodeFile =
std::string(Args.getLastArgValue(OPT_fthin_link_bitcode_EQ));

Opts.MemProf = Args.hasArg(OPT_fmemory_profile);
// The memory profile runtime appends the pid to make this name more unique.
const char *MemProfileBasename = "memprof.profraw";
if (Args.hasArg(OPT_fmemory_profile_EQ)) {
SmallString<128> Path(
std::string(Args.getLastArgValue(OPT_fmemory_profile_EQ)));
llvm::sys::path::append(Path, MemProfileBasename);
Opts.MemoryProfileOutput = std::string(Path);
} else if (Args.hasArg(OPT_fmemory_profile))
Opts.MemoryProfileOutput = MemProfileBasename;

Opts.MSVolatile = Args.hasArg(OPT_fms_volatile);

Expand Down
12 changes: 12 additions & 0 deletions clang/test/CodeGen/memory-profile-filename.c
@@ -0,0 +1,12 @@
// Test that we get the expected module flag metadata for the memory profile
// filename.
// RUN: %clang -target x86_64-linux-gnu -S -emit-llvm -o - %s | FileCheck %s --check-prefix=NONE
// RUN: %clang -target x86_64-linux-gnu -fmemory-profile -S -emit-llvm -o - %s | FileCheck %s --check-prefix=DEFAULTNAME
// RUN: %clang -target x86_64-linux-gnu -fmemory-profile=/tmp -S -emit-llvm -o - %s | FileCheck %s --check-prefix=CUSTOMNAME
int main(void) {
return 0;
}

// NONE-NOT: MemProfProfileFilename
// DEFAULTNAME: !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"}
// CUSTOMNAME: !{i32 1, !"MemProfProfileFilename", !"/tmp/memprof.profraw"}
4 changes: 4 additions & 0 deletions clang/test/Driver/fmemprof.cpp
@@ -1,6 +1,10 @@
// RUN: %clangxx -target x86_64-linux-gnu -fmemory-profile %s -### 2>&1 | FileCheck %s
// RUN: %clangxx -target x86_64-linux-gnu -fmemory-profile=foo %s -### 2>&1 | FileCheck %s --check-prefix=DIR
// RUN: %clangxx -target x86_64-linux-gnu -fmemory-profile -fno-memory-profile %s -### 2>&1 | FileCheck %s --check-prefix=OFF
// RUN: %clangxx -target x86_64-linux-gnu -fmemory-profile=foo -fno-memory-profile %s -### 2>&1 | FileCheck %s --check-prefix=OFF
// CHECK: "-cc1" {{.*}} "-fmemory-profile"
// CHECK: ld{{.*}}libclang_rt.memprof{{.*}}libclang_rt.memprof_cxx
// DIR: "-cc1" {{.*}} "-fmemory-profile=foo"
// DIR: ld{{.*}}libclang_rt.memprof{{.*}}libclang_rt.memprof_cxx
// OFF-NOT: "-fmemory-profile"
// OFF-NOT: libclang_rt.memprof
4 changes: 2 additions & 2 deletions compiler-rt/test/memprof/TestCases/atexit_stats.cpp
@@ -1,8 +1,8 @@
// Check atexit option.

// RUN: %clangxx_memprof -O0 %s -o %t
// RUN: %env_memprof_opts=atexit=1 %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=atexit=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOATEXIT
// RUN: %env_memprof_opts=log_path=stderr:atexit=1 %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr:atexit=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOATEXIT

// CHECK: MemProfiler exit stats:
// CHECK: Stats: {{[0-9]+}}M malloced ({{[0-9]+}}M for overhead) by {{[0-9]+}} calls
Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/memprof/TestCases/dump_process_map.cpp
@@ -1,8 +1,8 @@
// Check print_module_map option.

// RUN: %clangxx_memprof -O0 %s -o %t
// RUN: %env_memprof_opts=print_module_map=1 %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=print_module_map=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOMAP
// RUN: %env_memprof_opts=log_path=stderr:print_module_map=1 %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr:print_module_map=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOMAP

// CHECK: Process memory map follows:
// CHECK: dump_process_map.cpp.tmp
Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/memprof/TestCases/log_path_test.cpp
Expand Up @@ -3,8 +3,8 @@
//
// RUN: %clangxx_memprof %s -o %t

// Regular run.
// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-GOOD --dump-input=always
// stderr log_path
// RUN: %env_memprof_opts=log_path=stderr %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-GOOD --dump-input=always

// Good log_path.
// RUN: rm -f %t.log.*
Expand Down
6 changes: 3 additions & 3 deletions compiler-rt/test/memprof/TestCases/malloc-size-too-big.cpp
@@ -1,8 +1,8 @@
// RUN: %clangxx_memprof -O0 %s -o %t
// RUN: %env_memprof_opts=allocator_may_return_null=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SUMMARY
// RUN: %env_memprof_opts=allocator_may_return_null=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NULL
// RUN: %env_memprof_opts=log_path=stderr:allocator_may_return_null=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SUMMARY
// RUN: %env_memprof_opts=log_path=stderr:allocator_may_return_null=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NULL
// Test print_summary
// RUN: %env_memprof_opts=allocator_may_return_null=0:print_summary=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOSUMMARY
// RUN: %env_memprof_opts=log_path=stderr:allocator_may_return_null=0:print_summary=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOSUMMARY

#include <stdio.h>
#include <stdlib.h>
Expand Down
@@ -1,6 +1,6 @@
// Check mem_info_cache_entries option.

// RUN: %clangxx_memprof -O0 %s -o %t && %env_memprof_opts=mem_info_cache_entries=15:print_mem_info_cache_miss_rate=1:print_mem_info_cache_miss_rate_details=1 %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_memprof -O0 %s -o %t && %env_memprof_opts=log_path=stderr:mem_info_cache_entries=15:print_mem_info_cache_miss_rate=1:print_mem_info_cache_miss_rate_details=1 %run %t 2>&1 | FileCheck %s

// CHECK: Set 14 miss rate: 0 / {{.*}} = 0.00%
// CHECK-NOT: Set
Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/memprof/TestCases/print_miss_rate.cpp
Expand Up @@ -2,8 +2,8 @@
// print_mem_info_cache_miss_rate_details options.

// RUN: %clangxx_memprof -O0 %s -o %t
// RUN: %env_memprof_opts=print_mem_info_cache_miss_rate=1 %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=print_mem_info_cache_miss_rate=1:print_mem_info_cache_miss_rate_details=1 %run %t 2>&1 | FileCheck %s --check-prefix=DETAILS
// RUN: %env_memprof_opts=log_path=stderr:print_mem_info_cache_miss_rate=1 %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr:print_mem_info_cache_miss_rate=1:print_mem_info_cache_miss_rate_details=1 %run %t 2>&1 | FileCheck %s --check-prefix=DETAILS

// CHECK: Overall miss rate: 0 / {{.*}} = 0.00%
// DETAILS: Set 0 miss rate: 0 / {{.*}} = 0.00%
Expand Down
6 changes: 3 additions & 3 deletions compiler-rt/test/memprof/TestCases/stress_dtls.c
Expand Up @@ -7,9 +7,9 @@
// RUN: %clangxx_memprof %s -ldl -pthread -o %t
// RUN: %run %t 0 3
// RUN: %run %t 2 3
// RUN: %env_memprof_opts=verbosity=2 %run %t 10 2 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=verbosity=2:intercept_tls_get_addr=1 %run %t 10 2 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=verbosity=2:intercept_tls_get_addr=0 %run %t 10 2 2>&1 | FileCheck %s --check-prefix=CHECK0
// RUN: %env_memprof_opts=log_path=stderr:verbosity=2 %run %t 10 2 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr:verbosity=2:intercept_tls_get_addr=1 %run %t 10 2 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr:verbosity=2:intercept_tls_get_addr=0 %run %t 10 2 2>&1 | FileCheck %s --check-prefix=CHECK0
// CHECK: __tls_get_addr
// CHECK: Creating thread 0
// CHECK: __tls_get_addr
Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/memprof/TestCases/test_malloc_load_store.c
Expand Up @@ -3,10 +3,10 @@
// before exit.

// RUN: %clangxx_memprof -O0 %s -o %t
// RUN: %env_memprof_opts= %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr %run %t 2>&1 | FileCheck %s

// RUN: %clangxx_memprof -DFREE -O0 %s -o %t
// RUN: %env_memprof_opts= %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr %run %t 2>&1 | FileCheck %s

// This is actually:
// Memory allocation stack id = STACKID
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/test/memprof/TestCases/test_memintrin.cpp
@@ -1,6 +1,6 @@
// Check profile with calls to memory intrinsics.

// RUN: %clangxx_memprof -O0 %s -o %t && %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_memprof -O0 %s -o %t && %env_memprof_opts=log_path=stderr %run %t 2>&1 | FileCheck %s

// This is actually:
// Memory allocation stack id = STACKIDP
Expand Down
6 changes: 3 additions & 3 deletions compiler-rt/test/memprof/TestCases/test_new_load_store.cpp
Expand Up @@ -3,14 +3,14 @@
// before exit.

// RUN: %clangxx_memprof -O0 %s -o %t
// RUN: %env_memprof_opts= %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr %run %t 2>&1 | FileCheck %s

// RUN: %clangxx_memprof -DFREE -O0 %s -o %t
// RUN: %env_memprof_opts= %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr %run %t 2>&1 | FileCheck %s

// Try again with callbacks instead of inline sequences
// RUN: %clangxx_memprof -mllvm -memprof-use-callbacks -O0 %s -o %t
// RUN: %env_memprof_opts= %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr %run %t 2>&1 | FileCheck %s

// This is actually:
// Memory allocation stack id = STACKID
Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/test/memprof/TestCases/test_terse.cpp
Expand Up @@ -3,10 +3,10 @@
// deallocated before exit.

// RUN: %clangxx_memprof -O0 %s -o %t
// RUN: %env_memprof_opts=print_terse=1 %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr:print_terse=1 %run %t 2>&1 | FileCheck %s

// RUN: %clangxx_memprof -DFREE -O0 %s -o %t
// RUN: %env_memprof_opts=print_terse=1 %run %t 2>&1 | FileCheck %s
// RUN: %env_memprof_opts=log_path=stderr:print_terse=1 %run %t 2>&1 | FileCheck %s

// CHECK: MIB:[[STACKID:[0-9]+]]/1/40.00/40/40/20.00/20/20/[[AVELIFETIME:[0-9]+]].00/[[AVELIFETIME]]/[[AVELIFETIME]]/0/0/0/0
// CHECK: Stack for id [[STACKID]]:
Expand Down
@@ -1,4 +1,4 @@
// RUN: %clangxx_memprof -O0 %s -o %t && %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_memprof -O0 %s -o %t && %env_memprof_opts=log_path=stderr %run %t 2>&1 | FileCheck %s

// This is actually:
// Memory allocation stack id = STACKID
Expand Down
24 changes: 24 additions & 0 deletions llvm/lib/Transforms/Instrumentation/MemProfiler.cpp
Expand Up @@ -60,6 +60,8 @@ constexpr char MemProfVersionCheckNamePrefix[] =
constexpr char MemProfShadowMemoryDynamicAddress[] =
"__memprof_shadow_memory_dynamic_address";

constexpr char MemProfFilenameVar[] = "__memprof_profile_filename";

// Command-line flags.

static cl::opt<bool> ClInsertVersionCheck(
Expand Down Expand Up @@ -486,6 +488,26 @@ void MemProfiler::instrumentAddress(Instruction *OrigIns,
IRB.CreateStore(ShadowValue, ShadowAddr);
}

// Create the variable for the profile file name.
void createProfileFileNameVar(Module &M) {
const MDString *MemProfFilename =
dyn_cast_or_null<MDString>(M.getModuleFlag("MemProfProfileFilename"));
if (!MemProfFilename)
return;
assert(!MemProfFilename->getString().empty() &&
"Unexpected MemProfProfileFilename metadata with empty string");
Constant *ProfileNameConst = ConstantDataArray::getString(
M.getContext(), MemProfFilename->getString(), true);
GlobalVariable *ProfileNameVar = new GlobalVariable(
M, ProfileNameConst->getType(), /*isConstant=*/true,
GlobalValue::WeakAnyLinkage, ProfileNameConst, MemProfFilenameVar);
Triple TT(M.getTargetTriple());
if (TT.supportsCOMDAT()) {
ProfileNameVar->setLinkage(GlobalValue::ExternalLinkage);
ProfileNameVar->setComdat(M.getOrInsertComdat(MemProfFilenameVar));
}
}

bool ModuleMemProfiler::instrumentModule(Module &M) {
// Create a module constructor.
std::string MemProfVersion = std::to_string(LLVM_MEM_PROFILER_VERSION);
Expand All @@ -500,6 +522,8 @@ bool ModuleMemProfiler::instrumentModule(Module &M) {
const uint64_t Priority = getCtorAndDtorPriority(TargetTriple);
appendToGlobalCtors(M, MemProfCtorFunction, Priority);

createProfileFileNameVar(M);

return true;
}

Expand Down
15 changes: 15 additions & 0 deletions llvm/test/Instrumentation/HeapProfiler/filename.ll
@@ -0,0 +1,15 @@
; Test to ensure that the filename provided by clang in the module flags
; metadata results in the expected __memprof_profile_filename insertion.

; RUN: opt < %s -mtriple=x86_64-unknown-linux -memprof -memprof-module -S | FileCheck --check-prefixes=CHECK %s

define i32 @main() {
entry:
ret i32 0
}

!llvm.module.flags = !{!0}
!0 = !{i32 1, !"MemProfProfileFilename", !"/tmp/memprof.profraw"}

; CHECK: $__memprof_profile_filename = comdat any
; CHECK: @__memprof_profile_filename = constant [21 x i8] c"/tmp/memprof.profraw\00", comdat

0 comments on commit 0949f96

Please sign in to comment.