diff --git a/llvm/test/tools/llvm-profgen/separate-debuginfo-binary.test b/llvm/test/tools/llvm-profgen/separate-debuginfo-binary.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profgen/separate-debuginfo-binary.test @@ -0,0 +1,57 @@ +; RUN: llvm-objcopy --strip-debug %S/Inputs/inline-noprobe.perfbin %t1 +; RUN: llvm-objcopy --only-keep-debug %S/Inputs/inline-noprobe.perfbin %t2 +; RUN: echo -e "0\n0" > %t +; RUN: llvm-profgen --format=text --unsymbolized-profile=%t --binary=%t1 --debug-binary=%t2 --output=%t3 --fill-zero-for-all-funcs +; RUN: FileCheck %s --input-file %t3 --check-prefix=CHECK + +; RUN: llvm-objcopy --strip-debug %S/Inputs/inline-cs-pseudoprobe.perfbin %t4 +; RUN: llvm-objcopy --only-keep-debug %S/Inputs/inline-cs-pseudoprobe.perfbin %t5 +; RUN: llvm-profgen --format=text --perfscript=%S/Inputs/inline-cs-pseudoprobe.perfscript --binary=%t4 --debug-binary=%t5 --output=%t6 --profile-summary-hot-count=0 --csspgo-preinliner=0 +; RUN: FileCheck %s --input-file %t6 --check-prefix=CHECK-CS-PROBE + +; CHECK: bar:0:0 +; CHECK: 1: 0 +; CHECK: 5: 0 +; CHECK: foo:0:0 +; CHECK: 0: 0 +; CHECK: 2.1: 0 +; CHECK: 3: 0 +; CHECK: 3.2: 0 +; CHECK: 4: 0 +; CHECK: 3.1: bar:0 +; CHECK: 1: 0 +; CHECK: 65533: 0 +; CHECK: 3.2: bar:0 +; CHECK: 1: 0 +; CHECK: 7: 0 +; CHECK: main:0:0 +; CHECK: 0: 0 +; CHECK: 2: 0 +; CHECK: 1: foo:0 +; CHECK: 2.1: 0 +; CHECK: 3: 0 +; CHECK: 3.2: 0 +; CHECK: 4: 0 +; CHECK: 65526: 0 +; CHECK: 3.1: bar:0 +; CHECK: 1: 0 +; CHECK: 65533: 0 +; CHECK: 3.2: bar:0 +; CHECK: 1: 0 + + +; CHECK-CS-PROBE: [main:2 @ foo]:74:0 +; CHECK-CS-PROBE: 1: 0 +; CHECK-CS-PROBE: 2: 15 +; CHECK-CS-PROBE: 3: 15 +; CHECK-CS-PROBE: 4: 14 +; CHECK-CS-PROBE: 5: 1 +; CHECK-CS-PROBE: 6: 15 +; CHECK-CS-PROBE: 7: 0 +; CHECK-CS-PROBE: 8: 14 bar:14 +; CHECK-CS-PROBE: 9: 0 +; CHECK-CS-PROBE: !CFGChecksum: 563088904013236 +; CHECK-CS-PROBE: [main:2 @ foo:8 @ bar]:28:14 +; CHECK-CS-PROBE: 1: 14 +; CHECK-CS-PROBE: 4: 14 +; CHECK-CS-PROBE: !CFGChecksum: 72617220756 diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.h b/llvm/tools/llvm-profgen/ProfiledBinary.h --- a/llvm/tools/llvm-profgen/ProfiledBinary.h +++ b/llvm/tools/llvm-profgen/ProfiledBinary.h @@ -185,8 +185,12 @@ using OffsetRange = std::pair; class ProfiledBinary { - // Absolute path of the binary. + // Absolute path of the executable binary. std::string Path; + // Path of the debug info binary. + std::string DebugBinaryPath; + // Path of symbolizer path which should be pointed to binary with debug info. + StringRef SymbolizerPath; // The target triple. Triple TheTriple; // The runtime base address that the first executable segment is loaded at. @@ -311,10 +315,12 @@ void load(); public: - ProfiledBinary(const StringRef Path) - : Path(Path), ProEpilogTracker(this), + ProfiledBinary(const StringRef ExeBinPath, const StringRef DebugBinPath) + : Path(ExeBinPath), DebugBinaryPath(DebugBinPath), ProEpilogTracker(this), TrackFuncContextSize(EnableCSPreInliner && UseContextCostForPreInliner) { + // Point to executable binary if debug info binary is not specified. + SymbolizerPath = DebugBinPath.empty() ? ExeBinPath : DebugBinPath; setupSymbolizer(); load(); } diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.cpp b/llvm/tools/llvm-profgen/ProfiledBinary.cpp --- a/llvm/tools/llvm-profgen/ProfiledBinary.cpp +++ b/llvm/tools/llvm-profgen/ProfiledBinary.cpp @@ -187,9 +187,9 @@ void ProfiledBinary::load() { // Attempt to open the binary. OwningBinary OBinary = unwrapOrError(createBinary(Path), Path); - Binary &Binary = *OBinary.getBinary(); + Binary &ExeBinary = *OBinary.getBinary(); - auto *Obj = dyn_cast(&Binary); + auto *Obj = dyn_cast(&ExeBinary); if (!Obj) exitWithError("not a valid Elf image", Path); @@ -206,7 +206,15 @@ decodePseudoProbe(Obj); // Load debug info of subprograms from DWARF section. - loadSymbolsFromDWARF(*cast(&Binary)); + // If path of debug info binary is specified, use the debug info from it, + // otherwise use the debug info from the executable binary. + if (!DebugBinaryPath.empty()) { + OwningBinary DebugPath = + unwrapOrError(createBinary(DebugBinaryPath), DebugBinaryPath); + loadSymbolsFromDWARF(*dyn_cast(DebugPath.getBinary())); + } else { + loadSymbolsFromDWARF(*dyn_cast(&ExeBinary)); + } // Disassemble the text sections. disassemble(Obj); @@ -684,8 +692,9 @@ "Binary should only symbolize its own instruction"); auto Addr = object::SectionedAddress{IP.Offset + getPreferredBaseAddress(), object::SectionedAddress::UndefSection}; - DIInliningInfo InlineStack = - unwrapOrError(Symbolizer->symbolizeInlinedCode(Path, Addr), getName()); + DIInliningInfo InlineStack = unwrapOrError( + Symbolizer->symbolizeInlinedCode(SymbolizerPath.str(), Addr), + SymbolizerPath); SampleContextFrameVector CallStack; for (int32_t I = InlineStack.getNumberOfFrames() - 1; I >= 0; I--) { diff --git a/llvm/tools/llvm-profgen/llvm-profgen.cpp b/llvm/tools/llvm-profgen/llvm-profgen.cpp --- a/llvm/tools/llvm-profgen/llvm-profgen.cpp +++ b/llvm/tools/llvm-profgen/llvm-profgen.cpp @@ -48,9 +48,15 @@ static cl::alias UPA("up", cl::desc("Alias for --unsymbolized-profile"), cl::aliasopt(UnsymbolizedProfFilename)); -static cl::opt BinaryPath( - "binary", cl::value_desc("binary"), cl::Required, - cl::desc("Path of profiled binary, only one binary is supported."), +static cl::opt + BinaryPath("binary", cl::value_desc("binary"), cl::Required, + cl::desc("Path of profiled executable binary."), + cl::cat(ProfGenCategory)); + +static cl::opt DebugBinPath( + "debug-binary", cl::value_desc("debug-binary"), cl::ZeroOrMore, + cl::desc("Path of debug info binary, llvm-profgen will load the DWARF info " + "from it instead of the executable binary."), cl::cat(ProfGenCategory)); extern cl::opt ShowDisassemblyOnly; @@ -135,7 +141,7 @@ // Load symbols and disassemble the code of a given binary. std::unique_ptr Binary = - std::make_unique(BinaryPath); + std::make_unique(BinaryPath, DebugBinPath); if (ShowDisassemblyOnly) return EXIT_SUCCESS;