#include "llvm/Transforms/Instrumentation/MemProfiler.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/Analysis/MemoryProfileInfo.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Support/BLAKE3.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/HashBuilder.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
#include <map>
#include <set>
usingnamespacellvm;
usingnamespacellvm::memprof;
#define DEBUG_TYPE …
namespace llvm {
extern cl::opt<bool> PGOWarnMissing;
extern cl::opt<bool> NoPGOWarnMismatch;
extern cl::opt<bool> NoPGOWarnMismatchComdatWeak;
}
constexpr int LLVM_MEM_PROFILER_VERSION = …;
constexpr uint64_t DefaultMemGranularity = …;
constexpr uint64_t HistogramGranularity = …;
constexpr uint64_t DefaultShadowScale = …;
constexpr char MemProfModuleCtorName[] = …;
constexpr uint64_t MemProfCtorAndDtorPriority = …;
constexpr uint64_t MemProfEmscriptenCtorAndDtorPriority = …;
constexpr char MemProfInitName[] = …;
constexpr char MemProfVersionCheckNamePrefix[] = …;
constexpr char MemProfShadowMemoryDynamicAddress[] = …;
constexpr char MemProfFilenameVar[] = …;
constexpr char MemProfHistogramFlagVar[] = …;
static cl::opt<bool> ClInsertVersionCheck(
"memprof-guard-against-version-mismatch",
cl::desc("Guard against compiler/runtime version mismatch."), cl::Hidden,
cl::init(true));
static cl::opt<bool> ClInstrumentReads("memprof-instrument-reads",
cl::desc("instrument read instructions"),
cl::Hidden, cl::init(true));
static cl::opt<bool>
ClInstrumentWrites("memprof-instrument-writes",
cl::desc("instrument write instructions"), cl::Hidden,
cl::init(true));
static cl::opt<bool> ClInstrumentAtomics(
"memprof-instrument-atomics",
cl::desc("instrument atomic instructions (rmw, cmpxchg)"), cl::Hidden,
cl::init(true));
static cl::opt<bool> ClUseCalls(
"memprof-use-callbacks",
cl::desc("Use callbacks instead of inline instrumentation sequences."),
cl::Hidden, cl::init(false));
static cl::opt<std::string>
ClMemoryAccessCallbackPrefix("memprof-memory-access-callback-prefix",
cl::desc("Prefix for memory access callbacks"),
cl::Hidden, cl::init("__memprof_"));
static cl::opt<int> ClMappingScale("memprof-mapping-scale",
cl::desc("scale of memprof shadow mapping"),
cl::Hidden, cl::init(DefaultShadowScale));
static cl::opt<int>
ClMappingGranularity("memprof-mapping-granularity",
cl::desc("granularity of memprof shadow mapping"),
cl::Hidden, cl::init(DefaultMemGranularity));
static cl::opt<bool> ClStack("memprof-instrument-stack",
cl::desc("Instrument scalar stack variables"),
cl::Hidden, cl::init(false));
static cl::opt<int> ClDebug("memprof-debug", cl::desc("debug"), cl::Hidden,
cl::init(0));
static cl::opt<std::string> ClDebugFunc("memprof-debug-func", cl::Hidden,
cl::desc("Debug func"));
static cl::opt<int> ClDebugMin("memprof-debug-min", cl::desc("Debug min inst"),
cl::Hidden, cl::init(-1));
static cl::opt<int> ClDebugMax("memprof-debug-max", cl::desc("Debug max inst"),
cl::Hidden, cl::init(-1));
static cl::opt<bool> ClMemProfMatchHotColdNew(
"memprof-match-hot-cold-new",
cl::desc(
"Match allocation profiles onto existing hot/cold operator new calls"),
cl::Hidden, cl::init(false));
static cl::opt<bool> ClHistogram("memprof-histogram",
cl::desc("Collect access count histograms"),
cl::Hidden, cl::init(false));
static cl::opt<bool>
ClPrintMemProfMatchInfo("memprof-print-match-info",
cl::desc("Print matching stats for each allocation "
"context in this module's profiles"),
cl::Hidden, cl::init(false));
extern cl::opt<bool> MemProfReportHintedSizes;
STATISTIC(NumInstrumentedReads, "Number of instrumented reads");
STATISTIC(NumInstrumentedWrites, "Number of instrumented writes");
STATISTIC(NumSkippedStackReads, "Number of non-instrumented stack reads");
STATISTIC(NumSkippedStackWrites, "Number of non-instrumented stack writes");
STATISTIC(NumOfMemProfMissing, "Number of functions without memory profile.");
STATISTIC(NumOfMemProfMismatch,
"Number of functions having mismatched memory profile hash.");
STATISTIC(NumOfMemProfFunc, "Number of functions having valid memory profile.");
STATISTIC(NumOfMemProfAllocContextProfiles,
"Number of alloc contexts in memory profile.");
STATISTIC(NumOfMemProfCallSiteProfiles,
"Number of callsites in memory profile.");
STATISTIC(NumOfMemProfMatchedAllocContexts,
"Number of matched memory profile alloc contexts.");
STATISTIC(NumOfMemProfMatchedAllocs,
"Number of matched memory profile allocs.");
STATISTIC(NumOfMemProfMatchedCallSites,
"Number of matched memory profile callsites.");
namespace {
struct ShadowMapping { … };
static uint64_t getCtorAndDtorPriority(Triple &TargetTriple) { … }
struct InterestingMemoryAccess { … };
class MemProfiler { … };
class ModuleMemProfiler { … };
}
MemProfilerPass::MemProfilerPass() = default;
PreservedAnalyses MemProfilerPass::run(Function &F,
AnalysisManager<Function> &AM) { … }
ModuleMemProfilerPass::ModuleMemProfilerPass() = default;
PreservedAnalyses ModuleMemProfilerPass::run(Module &M,
AnalysisManager<Module> &AM) { … }
Value *MemProfiler::memToShadow(Value *Shadow, IRBuilder<> &IRB) { … }
void MemProfiler::instrumentMemIntrinsic(MemIntrinsic *MI) { … }
std::optional<InterestingMemoryAccess>
MemProfiler::isInterestingMemoryAccess(Instruction *I) const { … }
void MemProfiler::instrumentMaskedLoadOrStore(const DataLayout &DL, Value *Mask,
Instruction *I, Value *Addr,
Type *AccessTy, bool IsWrite) { … }
void MemProfiler::instrumentMop(Instruction *I, const DataLayout &DL,
InterestingMemoryAccess &Access) { … }
void MemProfiler::instrumentAddress(Instruction *OrigIns,
Instruction *InsertBefore, Value *Addr,
bool IsWrite) { … }
void createProfileFileNameVar(Module &M) { … }
void createMemprofHistogramFlagVar(Module &M) { … }
bool ModuleMemProfiler::instrumentModule(Module &M) { … }
void MemProfiler::initializeCallbacks(Module &M) { … }
bool MemProfiler::maybeInsertMemProfInitAtFunctionEntry(Function &F) { … }
bool MemProfiler::insertDynamicShadowAtFunctionEntry(Function &F) { … }
bool MemProfiler::instrumentFunction(Function &F) { … }
static void addCallsiteMetadata(Instruction &I,
std::vector<uint64_t> &InlinedCallStack,
LLVMContext &Ctx) { … }
static uint64_t computeStackId(GlobalValue::GUID Function, uint32_t LineOffset,
uint32_t Column) { … }
static uint64_t computeStackId(const memprof::Frame &Frame) { … }
static uint64_t
computeFullStackId(const std::vector<memprof::Frame> &CallStack) { … }
static AllocationType addCallStack(CallStackTrie &AllocTrie,
const AllocationInfo *AllocInfo) { … }
static bool
stackFrameIncludesInlinedCallStack(ArrayRef<Frame> ProfileCallStack,
ArrayRef<uint64_t> InlinedCallStack,
unsigned StartIndex = 0) { … }
static bool isAllocationWithHotColdVariant(Function *Callee,
const TargetLibraryInfo &TLI) { … }
struct AllocMatchInfo { … };
static void
readMemprof(Module &M, Function &F, IndexedInstrProfReader *MemProfReader,
const TargetLibraryInfo &TLI,
std::map<uint64_t, AllocMatchInfo> &FullStackIdToAllocMatchInfo) { … }
MemProfUsePass::MemProfUsePass(std::string MemoryProfileFile,
IntrusiveRefCntPtr<vfs::FileSystem> FS)
: … { … }
PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) { … }