1 //===-- ProfiledBinary.cpp - Binary decoder ---------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "ProfiledBinary.h"
10 #include "ErrorHandling.h"
11 #include "ProfileGenerator.h"
12 #include "llvm/ADT/Triple.h"
13 #include "llvm/Demangle/Demangle.h"
14 #include "llvm/IR/DebugInfoMetadata.h"
15 #include "llvm/Support/CommandLine.h"
16 #include "llvm/Support/Format.h"
17 #include "llvm/Support/TargetRegistry.h"
18 #include "llvm/Support/TargetSelect.h"
19
20 #define DEBUG_TYPE "load-binary"
21
22 using namespace llvm;
23 using namespace sampleprof;
24
25 cl::opt<bool> ShowDisassemblyOnly("show-disassembly-only", cl::ReallyHidden,
26 cl::init(false), cl::ZeroOrMore,
27 cl::desc("Print disassembled code."));
28
29 cl::opt<bool> ShowSourceLocations("show-source-locations", cl::ReallyHidden,
30 cl::init(false), cl::ZeroOrMore,
31 cl::desc("Print source locations."));
32
33 cl::opt<bool> ShowPseudoProbe(
34 "show-pseudo-probe", cl::ReallyHidden, cl::init(false), cl::ZeroOrMore,
35 cl::desc("Print pseudo probe section and disassembled info."));
36
37 namespace llvm {
38 namespace sampleprof {
39
getTarget(const ObjectFile * Obj)40 static const Target *getTarget(const ObjectFile *Obj) {
41 Triple TheTriple = Obj->makeTriple();
42 std::string Error;
43 std::string ArchName;
44 const Target *TheTarget =
45 TargetRegistry::lookupTarget(ArchName, TheTriple, Error);
46 if (!TheTarget)
47 exitWithError(Error, Obj->getFileName());
48 return TheTarget;
49 }
50
51 template <class ELFT>
getELFImageLMAForSec(const ELFFile<ELFT> & Obj,const object::ELFSectionRef & Sec,StringRef FileName)52 static uint64_t getELFImageLMAForSec(const ELFFile<ELFT> &Obj,
53 const object::ELFSectionRef &Sec,
54 StringRef FileName) {
55 // Search for a PT_LOAD segment containing the requested section. Return this
56 // segment's p_addr as the image load address for the section.
57 const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName);
58 for (const typename ELFT::Phdr &Phdr : PhdrRange)
59 if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_vaddr <= Sec.getAddress()) &&
60 (Phdr.p_vaddr + Phdr.p_memsz > Sec.getAddress()))
61 // Segments will always be loaded at a page boundary.
62 return Phdr.p_paddr & ~(Phdr.p_align - 1U);
63 return 0;
64 }
65
66 // Get the image load address for a specific section. Note that an image is
67 // loaded by segments (a group of sections) and segments may not be consecutive
68 // in memory.
getELFImageLMAForSec(const object::ELFSectionRef & Sec)69 static uint64_t getELFImageLMAForSec(const object::ELFSectionRef &Sec) {
70 if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Sec.getObject()))
71 return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
72 ELFObj->getFileName());
73 else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Sec.getObject()))
74 return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
75 ELFObj->getFileName());
76 else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Sec.getObject()))
77 return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
78 ELFObj->getFileName());
79 const auto *ELFObj = cast<ELF64BEObjectFile>(Sec.getObject());
80 return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, ELFObj->getFileName());
81 }
82
load()83 void ProfiledBinary::load() {
84 // Attempt to open the binary.
85 OwningBinary<Binary> OBinary = unwrapOrError(createBinary(Path), Path);
86 Binary &Binary = *OBinary.getBinary();
87
88 auto *Obj = dyn_cast<ELFObjectFileBase>(&Binary);
89 if (!Obj)
90 exitWithError("not a valid Elf image", Path);
91
92 TheTriple = Obj->makeTriple();
93 // Current only support X86
94 if (!TheTriple.isX86())
95 exitWithError("unsupported target", TheTriple.getTriple());
96 LLVM_DEBUG(dbgs() << "Loading " << Path << "\n");
97
98 // Find the preferred base address for text sections.
99 setPreferredBaseAddress(Obj);
100
101 // Decode pseudo probe related section
102 decodePseudoProbe(Obj);
103
104 // Disassemble the text sections.
105 disassemble(Obj);
106
107 // Use function start and return address to infer prolog and epilog
108 ProEpilogTracker.inferPrologOffsets(FuncStartAddrMap);
109 ProEpilogTracker.inferEpilogOffsets(RetAddrs);
110
111 // TODO: decode other sections.
112 }
113
inlineContextEqual(uint64_t Address1,uint64_t Address2) const114 bool ProfiledBinary::inlineContextEqual(uint64_t Address1,
115 uint64_t Address2) const {
116 uint64_t Offset1 = virtualAddrToOffset(Address1);
117 uint64_t Offset2 = virtualAddrToOffset(Address2);
118 const FrameLocationStack &Context1 = getFrameLocationStack(Offset1);
119 const FrameLocationStack &Context2 = getFrameLocationStack(Offset2);
120 if (Context1.size() != Context2.size())
121 return false;
122 if (Context1.empty())
123 return false;
124 // The leaf frame contains location within the leaf, and it
125 // needs to be remove that as it's not part of the calling context
126 return std::equal(Context1.begin(), Context1.begin() + Context1.size() - 1,
127 Context2.begin(), Context2.begin() + Context2.size() - 1);
128 }
129
getExpandedContextStr(const SmallVectorImpl<uint64_t> & Stack) const130 std::string ProfiledBinary::getExpandedContextStr(
131 const SmallVectorImpl<uint64_t> &Stack) const {
132 std::string ContextStr;
133 SmallVector<std::string, 16> ContextVec;
134 // Process from frame root to leaf
135 for (auto Address : Stack) {
136 uint64_t Offset = virtualAddrToOffset(Address);
137 const FrameLocationStack &ExpandedContext = getFrameLocationStack(Offset);
138 // An instruction without a valid debug line will be ignored by sample
139 // processing
140 if (ExpandedContext.empty())
141 return std::string();
142 for (const auto &Loc : ExpandedContext) {
143 ContextVec.push_back(getCallSite(Loc));
144 }
145 }
146
147 assert(ContextVec.size() && "Context length should be at least 1");
148 // Compress the context string except for the leaf frame
149 std::string LeafFrame = ContextVec.back();
150 ContextVec.pop_back();
151 CSProfileGenerator::compressRecursionContext<std::string>(ContextVec);
152
153 std::ostringstream OContextStr;
154 for (uint32_t I = 0; I < (uint32_t)ContextVec.size(); I++) {
155 if (OContextStr.str().size()) {
156 OContextStr << " @ ";
157 }
158 OContextStr << ContextVec[I];
159 }
160 // Only keep the function name for the leaf frame
161 if (OContextStr.str().size())
162 OContextStr << " @ ";
163 OContextStr << StringRef(LeafFrame).split(":").first.str();
164 return OContextStr.str();
165 }
166
setPreferredBaseAddress(const ELFObjectFileBase * Obj)167 void ProfiledBinary::setPreferredBaseAddress(const ELFObjectFileBase *Obj) {
168 for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
169 SI != SE; ++SI) {
170 const SectionRef &Section = *SI;
171 if (Section.isText()) {
172 PreferredBaseAddress = getELFImageLMAForSec(Section);
173 return;
174 }
175 }
176 exitWithError("no text section found", Obj->getFileName());
177 }
178
decodePseudoProbe(const ELFObjectFileBase * Obj)179 void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) {
180 StringRef FileName = Obj->getFileName();
181 for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
182 SI != SE; ++SI) {
183 const SectionRef &Section = *SI;
184 StringRef SectionName = unwrapOrError(Section.getName(), FileName);
185
186 if (SectionName == ".pseudo_probe_desc") {
187 StringRef Contents = unwrapOrError(Section.getContents(), FileName);
188 ProbeDecoder.buildGUID2FuncDescMap(
189 reinterpret_cast<const uint8_t *>(Contents.data()), Contents.size());
190 } else if (SectionName == ".pseudo_probe") {
191 StringRef Contents = unwrapOrError(Section.getContents(), FileName);
192 ProbeDecoder.buildAddress2ProbeMap(
193 reinterpret_cast<const uint8_t *>(Contents.data()), Contents.size());
194 // set UsePseudoProbes flag, used for PerfReader
195 UsePseudoProbes = true;
196 }
197 }
198
199 if (ShowPseudoProbe)
200 ProbeDecoder.printGUID2FuncDescMap(outs());
201 }
202
dissassembleSymbol(std::size_t SI,ArrayRef<uint8_t> Bytes,SectionSymbolsTy & Symbols,const SectionRef & Section)203 bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
204 SectionSymbolsTy &Symbols,
205 const SectionRef &Section) {
206 std::size_t SE = Symbols.size();
207 uint64_t SectionOffset = Section.getAddress() - PreferredBaseAddress;
208 uint64_t SectSize = Section.getSize();
209 uint64_t StartOffset = Symbols[SI].Addr - PreferredBaseAddress;
210 uint64_t EndOffset = (SI + 1 < SE)
211 ? Symbols[SI + 1].Addr - PreferredBaseAddress
212 : SectionOffset + SectSize;
213 if (StartOffset >= EndOffset)
214 return true;
215
216 std::string &&SymbolName = Symbols[SI].Name.str();
217 if (ShowDisassemblyOnly)
218 outs() << '<' << SymbolName << ">:\n";
219
220 uint64_t Offset = StartOffset;
221 while (Offset < EndOffset) {
222 MCInst Inst;
223 uint64_t Size;
224 // Disassemble an instruction.
225 if (!DisAsm->getInstruction(Inst, Size, Bytes.slice(Offset - SectionOffset),
226 Offset + PreferredBaseAddress, nulls()))
227 return false;
228
229 if (ShowDisassemblyOnly) {
230 if (ShowPseudoProbe) {
231 ProbeDecoder.printProbeForAddress(outs(),
232 Offset + PreferredBaseAddress);
233 }
234 outs() << format("%8" PRIx64 ":", Offset);
235 size_t Start = outs().tell();
236 IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), outs());
237 if (ShowSourceLocations) {
238 unsigned Cur = outs().tell() - Start;
239 if (Cur < 40)
240 outs().indent(40 - Cur);
241 InstructionPointer Inst(this, Offset);
242 outs() << getReversedLocWithContext(symbolize(Inst));
243 }
244 outs() << "\n";
245 }
246
247 const MCInstrDesc &MCDesc = MII->get(Inst.getOpcode());
248
249 // Populate a vector of the symbolized callsite at this location
250 // We don't need symbolized info for probe-based profile, just use an empty
251 // stack as an entry to indicate a valid binary offset
252 FrameLocationStack SymbolizedCallStack;
253 if (!UsePseudoProbes) {
254 InstructionPointer IP(this, Offset);
255 SymbolizedCallStack = symbolize(IP, true);
256 }
257 Offset2LocStackMap[Offset] = SymbolizedCallStack;
258 // Populate address maps.
259 CodeAddrs.push_back(Offset);
260 if (MCDesc.isCall())
261 CallAddrs.insert(Offset);
262 else if (MCDesc.isReturn())
263 RetAddrs.insert(Offset);
264
265 Offset += Size;
266 }
267
268 if (ShowDisassemblyOnly)
269 outs() << "\n";
270
271 FuncStartAddrMap[StartOffset] = Symbols[SI].Name.str();
272 return true;
273 }
274
setUpDisassembler(const ELFObjectFileBase * Obj)275 void ProfiledBinary::setUpDisassembler(const ELFObjectFileBase *Obj) {
276 const Target *TheTarget = getTarget(Obj);
277 std::string TripleName = TheTriple.getTriple();
278 StringRef FileName = Obj->getFileName();
279
280 MRI.reset(TheTarget->createMCRegInfo(TripleName));
281 if (!MRI)
282 exitWithError("no register info for target " + TripleName, FileName);
283
284 MCTargetOptions MCOptions;
285 AsmInfo.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
286 if (!AsmInfo)
287 exitWithError("no assembly info for target " + TripleName, FileName);
288
289 SubtargetFeatures Features = Obj->getFeatures();
290 STI.reset(
291 TheTarget->createMCSubtargetInfo(TripleName, "", Features.getString()));
292 if (!STI)
293 exitWithError("no subtarget info for target " + TripleName, FileName);
294
295 MII.reset(TheTarget->createMCInstrInfo());
296 if (!MII)
297 exitWithError("no instruction info for target " + TripleName, FileName);
298
299 MCObjectFileInfo MOFI;
300 MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI);
301 MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx);
302 DisAsm.reset(TheTarget->createMCDisassembler(*STI, Ctx));
303 if (!DisAsm)
304 exitWithError("no disassembler for target " + TripleName, FileName);
305
306 MIA.reset(TheTarget->createMCInstrAnalysis(MII.get()));
307
308 int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
309 IPrinter.reset(TheTarget->createMCInstPrinter(
310 Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI));
311 IPrinter->setPrintBranchImmAsAddress(true);
312 }
313
disassemble(const ELFObjectFileBase * Obj)314 void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) {
315 // Set up disassembler and related components.
316 setUpDisassembler(Obj);
317
318 // Create a mapping from virtual address to symbol name. The symbols in text
319 // sections are the candidates to dissassemble.
320 std::map<SectionRef, SectionSymbolsTy> AllSymbols;
321 StringRef FileName = Obj->getFileName();
322 for (const SymbolRef &Symbol : Obj->symbols()) {
323 const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName);
324 const StringRef Name = unwrapOrError(Symbol.getName(), FileName);
325 section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName);
326 if (SecI != Obj->section_end())
327 AllSymbols[*SecI].push_back(SymbolInfoTy(Addr, Name, ELF::STT_NOTYPE));
328 }
329
330 // Sort all the symbols. Use a stable sort to stabilize the output.
331 for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
332 stable_sort(SecSyms.second);
333
334 if (ShowDisassemblyOnly)
335 outs() << "\nDisassembly of " << FileName << ":\n";
336
337 // Dissassemble a text section.
338 for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
339 SI != SE; ++SI) {
340 const SectionRef &Section = *SI;
341 if (!Section.isText())
342 continue;
343
344 uint64_t ImageLoadAddr = PreferredBaseAddress;
345 uint64_t SectionOffset = Section.getAddress() - ImageLoadAddr;
346 uint64_t SectSize = Section.getSize();
347 if (!SectSize)
348 continue;
349
350 // Register the text section.
351 TextSections.insert({SectionOffset, SectSize});
352
353 if (ShowDisassemblyOnly) {
354 StringRef SectionName = unwrapOrError(Section.getName(), FileName);
355 outs() << "\nDisassembly of section " << SectionName;
356 outs() << " [" << format("0x%" PRIx64, SectionOffset) << ", "
357 << format("0x%" PRIx64, SectionOffset + SectSize) << "]:\n\n";
358 }
359
360 // Get the section data.
361 ArrayRef<uint8_t> Bytes =
362 arrayRefFromStringRef(unwrapOrError(Section.getContents(), FileName));
363
364 // Get the list of all the symbols in this section.
365 SectionSymbolsTy &Symbols = AllSymbols[Section];
366
367 // Disassemble symbol by symbol.
368 for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
369 if (!dissassembleSymbol(SI, Bytes, Symbols, Section))
370 exitWithError("disassembling error", FileName);
371 }
372 }
373 }
374
setupSymbolizer()375 void ProfiledBinary::setupSymbolizer() {
376 symbolize::LLVMSymbolizer::Options SymbolizerOpts;
377 SymbolizerOpts.PrintFunctions =
378 DILineInfoSpecifier::FunctionNameKind::LinkageName;
379 SymbolizerOpts.Demangle = false;
380 SymbolizerOpts.DefaultArch = TheTriple.getArchName().str();
381 SymbolizerOpts.UseSymbolTable = false;
382 SymbolizerOpts.RelativeAddresses = false;
383 Symbolizer = std::make_unique<symbolize::LLVMSymbolizer>(SymbolizerOpts);
384 }
385
symbolize(const InstructionPointer & IP,bool UseCanonicalFnName)386 FrameLocationStack ProfiledBinary::symbolize(const InstructionPointer &IP,
387 bool UseCanonicalFnName) {
388 assert(this == IP.Binary &&
389 "Binary should only symbolize its own instruction");
390 auto Addr = object::SectionedAddress{IP.Offset + PreferredBaseAddress,
391 object::SectionedAddress::UndefSection};
392 DIInliningInfo InlineStack =
393 unwrapOrError(Symbolizer->symbolizeInlinedCode(Path, Addr), getName());
394
395 FrameLocationStack CallStack;
396
397 for (int32_t I = InlineStack.getNumberOfFrames() - 1; I >= 0; I--) {
398 const auto &CallerFrame = InlineStack.getFrame(I);
399 if (CallerFrame.FunctionName == "<invalid>")
400 break;
401 StringRef FunctionName(CallerFrame.FunctionName);
402 if (UseCanonicalFnName)
403 FunctionName = FunctionSamples::getCanonicalFnName(FunctionName);
404 LineLocation Line(CallerFrame.Line - CallerFrame.StartLine,
405 DILocation::getBaseDiscriminatorFromDiscriminator(
406 CallerFrame.Discriminator));
407 FrameLocation Callsite(FunctionName.str(), Line);
408 CallStack.push_back(Callsite);
409 }
410
411 return CallStack;
412 }
413
InstructionPointer(ProfiledBinary * Binary,uint64_t Address,bool RoundToNext)414 InstructionPointer::InstructionPointer(ProfiledBinary *Binary, uint64_t Address,
415 bool RoundToNext)
416 : Binary(Binary), Address(Address) {
417 Index = Binary->getIndexForAddr(Address);
418 if (RoundToNext) {
419 // we might get address which is not the code
420 // it should round to the next valid address
421 this->Address = Binary->getAddressforIndex(Index);
422 }
423 }
424
advance()425 void InstructionPointer::advance() {
426 Index++;
427 Address = Binary->getAddressforIndex(Index);
428 }
429
backward()430 void InstructionPointer::backward() {
431 Index--;
432 Address = Binary->getAddressforIndex(Index);
433 }
434
update(uint64_t Addr)435 void InstructionPointer::update(uint64_t Addr) {
436 Address = Addr;
437 Index = Binary->getIndexForAddr(Address);
438 }
439
440 } // end namespace sampleprof
441 } // end namespace llvm
442