#ifndef LLVM_TOOLS_LLVM_PROFGEN_PROFILEDBINARY_H
#define LLVM_TOOLS_LLVM_PROFGEN_PROFILEDBINARY_H
#include "CallContext.h"
#include "ErrorHandling.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrAnalysis.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCPseudoProbe.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/ProfileData/SampleProf.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Path.h"
#include "llvm/Transforms/IPO/SampleContextTracker.h"
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
extern cl::opt<bool> EnableCSPreInliner;
extern cl::opt<bool> UseContextCostForPreInliner;
using namespace llvm;
using namespace sampleprof;
using namespace llvm::object;
namespace llvm {
namespace sampleprof {
class ProfiledBinary;
struct InstructionPointer {
const ProfiledBinary *Binary;
union {
uint64_t Offset = 0;
uint64_t Address;
};
uint64_t Index = 0;
InstructionPointer(const ProfiledBinary *Binary, uint64_t Address,
bool RoundToNext = false);
bool advance();
bool backward();
void update(uint64_t Addr);
};
enum SpecialFrameAddr {
DummyRoot = 0,
ExternalAddr = 1,
};
using RangesTy = std::vector<std::pair<uint64_t, uint64_t>>;
struct BinaryFunction {
StringRef FuncName;
RangesTy Ranges;
uint64_t getFuncSize() {
uint64_t Sum = 0;
for (auto &R : Ranges) {
Sum += R.second - R.first;
}
return Sum;
}
};
struct FuncRange {
uint64_t StartOffset;
uint64_t EndOffset;
BinaryFunction *Func;
bool IsFuncEntry = false;
StringRef getFuncName() { return Func->FuncName; }
};
struct PrologEpilogTracker {
std::unordered_set<uint64_t> PrologEpilogSet;
ProfiledBinary *Binary;
PrologEpilogTracker(ProfiledBinary *Bin) : Binary(Bin){};
void inferPrologOffsets(std::map<uint64_t, FuncRange> &FuncStartOffsetMap) {
for (auto I : FuncStartOffsetMap) {
PrologEpilogSet.insert(I.first);
InstructionPointer IP(Binary, I.first);
if (!IP.advance())
break;
PrologEpilogSet.insert(IP.Offset);
}
}
void inferEpilogOffsets(std::unordered_set<uint64_t> &RetAddrs) {
for (auto Addr : RetAddrs) {
PrologEpilogSet.insert(Addr);
InstructionPointer IP(Binary, Addr);
if (!IP.backward())
break;
PrologEpilogSet.insert(IP.Offset);
}
}
};
class BinarySizeContextTracker {
public:
void addInstructionForContext(const SampleContextFrameVector &Context,
uint32_t InstrSize);
uint32_t getFuncSizeForContext(const ContextTrieNode *Context);
void trackInlineesOptimizedAway(MCPseudoProbeDecoder &ProbeDecoder);
using ProbeFrameStack = SmallVector<std::pair<StringRef, uint32_t>>;
void trackInlineesOptimizedAway(MCPseudoProbeDecoder &ProbeDecoder,
MCDecodedPseudoProbeInlineTree &ProbeNode,
ProbeFrameStack &Context);
void dump() { RootContext.dumpTree(); }
private:
ContextTrieNode RootContext;
};
using OffsetRange = std::pair<uint64_t, uint64_t>;
class ProfiledBinary {
std::string Path;
std::string DebugBinaryPath;
StringRef SymbolizerPath;
Triple TheTriple;
uint64_t BaseAddress = 0;
uint64_t FirstLoadableAddress = 0;
std::vector<uint64_t> PreferredTextSegmentAddresses;
std::vector<uint64_t> TextSegmentOffsets;
std::unique_ptr<const MCRegisterInfo> MRI;
std::unique_ptr<const MCAsmInfo> AsmInfo;
std::unique_ptr<const MCSubtargetInfo> STI;
std::unique_ptr<const MCInstrInfo> MII;
std::unique_ptr<MCDisassembler> DisAsm;
std::unique_ptr<const MCInstrAnalysis> MIA;
std::unique_ptr<MCInstPrinter> IPrinter;
std::set<std::pair<uint64_t, uint64_t>> TextSections;
std::unordered_map<std::string, BinaryFunction> BinaryFunctions;
std::unordered_set<const BinaryFunction *> ProfiledFunctions;
std::map<uint64_t, FuncRange> StartOffset2FuncRangeMap;
std::unordered_map<uint64_t, SampleContextFrameVector> Offset2LocStackMap;
std::unordered_map<uint64_t, uint64_t> Offset2InstSizeMap;
std::vector<uint64_t> CodeAddrOffsets;
std::unordered_set<uint64_t> CallOffsets;
std::unordered_set<uint64_t> RetOffsets;
std::set<uint64_t> UncondBranchOffsets;
std::unordered_set<uint64_t> BranchOffsets;
PrologEpilogTracker ProEpilogTracker;
BinarySizeContextTracker FuncSizeTracker;
std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer;
std::unordered_set<std::string> NameStrings;
StringSet<> DisassembleFunctionSet;
MCPseudoProbeDecoder ProbeDecoder;
StringMap<MCDecodedPseudoProbeInlineTree *> TopLevelProbeFrameMap;
bool UsePseudoProbes = false;
bool UseFSDiscriminator = false;
bool TrackFuncContextSize = false;
bool IsLoadedByMMap = false;
bool MissingMMapWarned = false;
void setPreferredTextSegmentAddresses(const ELFObjectFileBase *O);
template <class ELFT>
void setPreferredTextSegmentAddresses(const ELFFile<ELFT> &Obj, StringRef FileName);
void checkPseudoProbe(const ELFObjectFileBase *Obj);
void decodePseudoProbe(const ELFObjectFileBase *Obj);
void
checkUseFSDiscriminator(const ELFObjectFileBase *Obj,
std::map<SectionRef, SectionSymbolsTy> &AllSymbols);
void setUpDisassembler(const ELFObjectFileBase *Obj);
void setupSymbolizer();
void loadSymbolsFromDWARF(ObjectFile &Obj);
void loadSymbolsFromDWARFUnit(DWARFUnit &CompilationUnit);
void setIsFuncEntry(uint64_t Offset, StringRef RangeSymName);
void warnNoFuncEntry();
void disassemble(const ELFObjectFileBase *O);
bool dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
SectionSymbolsTy &Symbols, const SectionRef &Section);
SampleContextFrameVector symbolize(const InstructionPointer &IP,
bool UseCanonicalFnName = false,
bool UseProbeDiscriminator = false);
void load();
public:
ProfiledBinary(const StringRef ExeBinPath, const StringRef DebugBinPath)
: Path(ExeBinPath), DebugBinaryPath(DebugBinPath), ProEpilogTracker(this),
TrackFuncContextSize(EnableCSPreInliner &&
UseContextCostForPreInliner) {
SymbolizerPath = DebugBinPath.empty() ? ExeBinPath : DebugBinPath;
setupSymbolizer();
load();
}
void decodePseudoProbe();
uint64_t virtualAddrToOffset(uint64_t VirtualAddress) const {
return VirtualAddress - BaseAddress;
}
uint64_t offsetToVirtualAddr(uint64_t Offset) const {
return Offset + BaseAddress;
}
StringRef getPath() const { return Path; }
StringRef getName() const { return llvm::sys::path::filename(Path); }
uint64_t getBaseAddress() const { return BaseAddress; }
void setBaseAddress(uint64_t Address) { BaseAddress = Address; }
uint64_t getPreferredBaseAddress() const { return PreferredTextSegmentAddresses[0]; }
uint64_t getFirstLoadableAddress() const { return FirstLoadableAddress; }
uint64_t getTextSegmentOffset() const { return TextSegmentOffsets[0]; }
const std::vector<uint64_t> &getPreferredTextSegmentAddresses() const {
return PreferredTextSegmentAddresses;
}
const std::vector<uint64_t> &getTextSegmentOffsets() const {
return TextSegmentOffsets;
}
uint64_t getInstSize(uint64_t Offset) const {
auto I = Offset2InstSizeMap.find(Offset);
if (I == Offset2InstSizeMap.end())
return 0;
return I->second;
}
bool offsetIsCode(uint64_t Offset) const {
return Offset2InstSizeMap.find(Offset) != Offset2InstSizeMap.end();
}
bool addressIsCode(uint64_t Address) const {
uint64_t Offset = virtualAddrToOffset(Address);
return offsetIsCode(Offset);
}
bool addressIsCall(uint64_t Address) const {
uint64_t Offset = virtualAddrToOffset(Address);
return CallOffsets.count(Offset);
}
bool addressIsReturn(uint64_t Address) const {
uint64_t Offset = virtualAddrToOffset(Address);
return RetOffsets.count(Offset);
}
bool addressInPrologEpilog(uint64_t Address) const {
uint64_t Offset = virtualAddrToOffset(Address);
return ProEpilogTracker.PrologEpilogSet.count(Offset);
}
bool offsetIsTransfer(uint64_t Offset) {
return BranchOffsets.count(Offset) || RetOffsets.count(Offset) ||
CallOffsets.count(Offset);
}
bool rangeCrossUncondBranch(uint64_t Start, uint64_t End) {
if (Start >= End)
return false;
auto R = UncondBranchOffsets.lower_bound(Start);
return R != UncondBranchOffsets.end() && *R < End;
}
uint64_t getAddressforIndex(uint64_t Index) const {
return offsetToVirtualAddr(CodeAddrOffsets[Index]);
}
size_t getCodeOffsetsSize() const { return CodeAddrOffsets.size(); }
bool usePseudoProbes() const { return UsePseudoProbes; }
bool useFSDiscriminator() const { return UseFSDiscriminator; }
uint32_t getIndexForOffset(uint64_t Offset) const {
auto Low = llvm::lower_bound(CodeAddrOffsets, Offset);
return Low - CodeAddrOffsets.begin();
}
uint32_t getIndexForAddr(uint64_t Address) const {
uint64_t Offset = virtualAddrToOffset(Address);
return getIndexForOffset(Offset);
}
uint64_t getCallAddrFromFrameAddr(uint64_t FrameAddr) const {
if (FrameAddr == ExternalAddr)
return ExternalAddr;
auto I = getIndexForAddr(FrameAddr);
FrameAddr = I ? getAddressforIndex(I - 1) : 0;
if (FrameAddr && addressIsCall(FrameAddr))
return FrameAddr;
return 0;
}
FuncRange *findFuncRangeForStartOffset(uint64_t Offset) {
auto I = StartOffset2FuncRangeMap.find(Offset);
if (I == StartOffset2FuncRangeMap.end())
return nullptr;
return &I->second;
}
FuncRange *findFuncRangeForOffset(uint64_t Offset) {
auto I = StartOffset2FuncRangeMap.upper_bound(Offset);
if (I == StartOffset2FuncRangeMap.begin())
return nullptr;
I--;
if (Offset >= I->second.EndOffset)
return nullptr;
return &I->second;
}
RangesTy getRangesForOffset(uint64_t Offset) {
auto *FRange = findFuncRangeForOffset(Offset);
if (!FRange)
return RangesTy();
return FRange->Func->Ranges;
}
const std::unordered_map<std::string, BinaryFunction> &
getAllBinaryFunctions() {
return BinaryFunctions;
}
std::unordered_set<const BinaryFunction *> &getProfiledFunctions() {
return ProfiledFunctions;
}
void setProfiledFunctions(std::unordered_set<const BinaryFunction *> &Funcs) {
ProfiledFunctions = Funcs;
}
BinaryFunction *getBinaryFunction(StringRef FName) {
auto I = BinaryFunctions.find(FName.str());
if (I == BinaryFunctions.end())
return nullptr;
return &I->second;
}
uint32_t getFuncSizeForContext(const ContextTrieNode *ContextNode) {
return FuncSizeTracker.getFuncSizeForContext(ContextNode);
}
void populateSymbolListFromDWARF(ProfileSymbolList &SymbolList);
const SampleContextFrameVector &
getFrameLocationStack(uint64_t Offset, bool UseProbeDiscriminator = false) {
auto I = Offset2LocStackMap.emplace(Offset, SampleContextFrameVector());
if (I.second) {
InstructionPointer IP(this, Offset);
I.first->second = symbolize(IP, true, UseProbeDiscriminator);
}
return I.first->second;
}
Optional<SampleContextFrame> getInlineLeafFrameLoc(uint64_t Offset) {
const auto &Stack = getFrameLocationStack(Offset);
if (Stack.empty())
return {};
return Stack.back();
}
void flushSymbolizer() { Symbolizer.reset(); }
bool inlineContextEqual(uint64_t Add1, uint64_t Add2);
SampleContextFrameVector
getExpandedContext(const SmallVectorImpl<uint64_t> &Stack,
bool &WasLeafInlined);
void computeInlinedContextSizeForRange(uint64_t StartOffset,
uint64_t EndOffset);
void computeInlinedContextSizeForFunc(const BinaryFunction *Func);
const MCDecodedPseudoProbe *getCallProbeForAddr(uint64_t Address) const {
return ProbeDecoder.getCallProbeForAddr(Address);
}
void getInlineContextForProbe(const MCDecodedPseudoProbe *Probe,
SampleContextFrameVector &InlineContextStack,
bool IncludeLeaf = false) const {
SmallVector<MCPseduoProbeFrameLocation, 16> ProbeInlineContext;
ProbeDecoder.getInlineContextForProbe(Probe, ProbeInlineContext,
IncludeLeaf);
for (uint32_t I = 0; I < ProbeInlineContext.size(); I++) {
auto &Callsite = ProbeInlineContext[I];
if (Callsite.second == 0 && I != ProbeInlineContext.size() - 1) {
InlineContextStack.clear();
continue;
}
InlineContextStack.emplace_back(Callsite.first,
LineLocation(Callsite.second, 0));
}
}
const AddressProbesMap &getAddress2ProbesMap() const {
return ProbeDecoder.getAddress2ProbesMap();
}
const MCPseudoProbeFuncDesc *getFuncDescForGUID(uint64_t GUID) {
return ProbeDecoder.getFuncDescForGUID(GUID);
}
const MCPseudoProbeFuncDesc *
getInlinerDescForProbe(const MCDecodedPseudoProbe *Probe) {
return ProbeDecoder.getInlinerDescForProbe(Probe);
}
bool getTrackFuncContextSize() { return TrackFuncContextSize; }
bool getIsLoadedByMMap() { return IsLoadedByMMap; }
void setIsLoadedByMMap(bool Value) { IsLoadedByMMap = Value; }
bool getMissingMMapWarned() { return MissingMMapWarned; }
void setMissingMMapWarned(bool Value) { MissingMMapWarned = Value; }
};
} }
#endif