| 1 | //===---------- DebugUtils.cpp - Utilities for debugging ORC JITs ---------===// |
| 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 "llvm/ExecutionEngine/Orc/DebugUtils.h" |
| 10 | |
| 11 | #include "llvm/ExecutionEngine/Orc/Core.h" |
| 12 | #include "llvm/Support/CommandLine.h" |
| 13 | #include "llvm/Support/Debug.h" |
| 14 | #include "llvm/Support/FileSystem.h" |
| 15 | #include "llvm/Support/MemoryBuffer.h" |
| 16 | #include "llvm/Support/Path.h" |
| 17 | #include "llvm/Support/raw_ostream.h" |
| 18 | |
| 19 | #define DEBUG_TYPE "orc" |
| 20 | |
| 21 | using namespace llvm; |
| 22 | |
| 23 | namespace { |
| 24 | |
| 25 | #ifndef NDEBUG |
| 26 | |
| 27 | cl::opt<bool> PrintHidden("debug-orc-print-hidden" , cl::init(true), |
| 28 | cl::desc("debug print hidden symbols defined by " |
| 29 | "materialization units" ), |
| 30 | cl::Hidden); |
| 31 | |
| 32 | cl::opt<bool> PrintCallable("debug-orc-print-callable" , cl::init(true), |
| 33 | cl::desc("debug print callable symbols defined by " |
| 34 | "materialization units" ), |
| 35 | cl::Hidden); |
| 36 | |
| 37 | cl::opt<bool> PrintData("debug-orc-print-data" , cl::init(true), |
| 38 | cl::desc("debug print data symbols defined by " |
| 39 | "materialization units" ), |
| 40 | cl::Hidden); |
| 41 | |
| 42 | #endif // NDEBUG |
| 43 | |
| 44 | // SetPrinter predicate that prints every element. |
| 45 | template <typename T> struct PrintAll { |
| 46 | bool operator()(const T &E) { return true; } |
| 47 | }; |
| 48 | |
| 49 | bool anyPrintSymbolOptionSet() { |
| 50 | #ifndef NDEBUG |
| 51 | return PrintHidden || PrintCallable || PrintData; |
| 52 | #else |
| 53 | return false; |
| 54 | #endif // NDEBUG |
| 55 | } |
| 56 | |
| 57 | bool flagsMatchCLOpts(const JITSymbolFlags &Flags) { |
| 58 | #ifndef NDEBUG |
| 59 | // Bail out early if this is a hidden symbol and we're not printing hiddens. |
| 60 | if (!PrintHidden && !Flags.isExported()) |
| 61 | return false; |
| 62 | |
| 63 | // Return true if this is callable and we're printing callables. |
| 64 | if (PrintCallable && Flags.isCallable()) |
| 65 | return true; |
| 66 | |
| 67 | // Return true if this is data and we're printing data. |
| 68 | if (PrintData && !Flags.isCallable()) |
| 69 | return true; |
| 70 | |
| 71 | // otherwise return false. |
| 72 | return false; |
| 73 | #else |
| 74 | return false; |
| 75 | #endif // NDEBUG |
| 76 | } |
| 77 | |
| 78 | // Prints a sequence of items, filtered by an user-supplied predicate. |
| 79 | template <typename Sequence, |
| 80 | typename Pred = PrintAll<typename Sequence::value_type>> |
| 81 | class SequencePrinter { |
| 82 | public: |
| 83 | SequencePrinter(const Sequence &S, char OpenSeq, char CloseSeq, |
| 84 | Pred ShouldPrint = Pred()) |
| 85 | : S(S), OpenSeq(OpenSeq), CloseSeq(CloseSeq), |
| 86 | ShouldPrint(std::move(ShouldPrint)) {} |
| 87 | |
| 88 | void printTo(llvm::raw_ostream &OS) const { |
| 89 | bool PrintComma = false; |
| 90 | OS << OpenSeq; |
| 91 | for (auto &E : S) { |
| 92 | if (ShouldPrint(E)) { |
| 93 | if (PrintComma) |
| 94 | OS << ','; |
| 95 | OS << ' ' << E; |
| 96 | PrintComma = true; |
| 97 | } |
| 98 | } |
| 99 | OS << ' ' << CloseSeq; |
| 100 | } |
| 101 | |
| 102 | private: |
| 103 | const Sequence &S; |
| 104 | char OpenSeq; |
| 105 | char CloseSeq; |
| 106 | mutable Pred ShouldPrint; |
| 107 | }; |
| 108 | |
| 109 | template <typename Sequence, typename Pred> |
| 110 | SequencePrinter<Sequence, Pred> printSequence(const Sequence &S, char OpenSeq, |
| 111 | char CloseSeq, Pred P = Pred()) { |
| 112 | return SequencePrinter<Sequence, Pred>(S, OpenSeq, CloseSeq, std::move(P)); |
| 113 | } |
| 114 | |
| 115 | // Render a SequencePrinter by delegating to its printTo method. |
| 116 | template <typename Sequence, typename Pred> |
| 117 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
| 118 | const SequencePrinter<Sequence, Pred> &Printer) { |
| 119 | Printer.printTo(OS); |
| 120 | return OS; |
| 121 | } |
| 122 | |
| 123 | struct PrintSymbolFlagsMapElemsMatchingCLOpts { |
| 124 | bool operator()(const orc::SymbolFlagsMap::value_type &KV) { |
| 125 | return flagsMatchCLOpts(Flags: KV.second); |
| 126 | } |
| 127 | }; |
| 128 | |
| 129 | struct PrintSymbolMapElemsMatchingCLOpts { |
| 130 | bool operator()(const orc::SymbolMap::value_type &KV) { |
| 131 | return flagsMatchCLOpts(Flags: KV.second.getFlags()); |
| 132 | } |
| 133 | }; |
| 134 | |
| 135 | } // end anonymous namespace |
| 136 | |
| 137 | namespace llvm { |
| 138 | namespace orc { |
| 139 | |
| 140 | raw_ostream &operator<<(raw_ostream &OS, const SymbolNameSet &Symbols) { |
| 141 | return OS << printSequence(S: Symbols, OpenSeq: '{', CloseSeq: '}', P: PrintAll<SymbolStringPtr>()); |
| 142 | } |
| 143 | |
| 144 | raw_ostream &operator<<(raw_ostream &OS, const SymbolNameVector &Symbols) { |
| 145 | return OS << printSequence(S: Symbols, OpenSeq: '[', CloseSeq: ']', P: PrintAll<SymbolStringPtr>()); |
| 146 | } |
| 147 | |
| 148 | raw_ostream &operator<<(raw_ostream &OS, ArrayRef<SymbolStringPtr> Symbols) { |
| 149 | return OS << printSequence(S: Symbols, OpenSeq: '[', CloseSeq: ']', P: PrintAll<SymbolStringPtr>()); |
| 150 | } |
| 151 | |
| 152 | raw_ostream &operator<<(raw_ostream &OS, const JITSymbolFlags &Flags) { |
| 153 | if (Flags.hasError()) |
| 154 | OS << "[*ERROR*]" ; |
| 155 | if (Flags.isCallable()) |
| 156 | OS << "[Callable]" ; |
| 157 | else |
| 158 | OS << "[Data]" ; |
| 159 | if (Flags.isWeak()) |
| 160 | OS << "[Weak]" ; |
| 161 | else if (Flags.isCommon()) |
| 162 | OS << "[Common]" ; |
| 163 | |
| 164 | if (!Flags.isExported()) |
| 165 | OS << "[Hidden]" ; |
| 166 | |
| 167 | return OS; |
| 168 | } |
| 169 | |
| 170 | raw_ostream &operator<<(raw_ostream &OS, const ExecutorSymbolDef &Sym) { |
| 171 | return OS << Sym.getAddress() << " " << Sym.getFlags(); |
| 172 | } |
| 173 | |
| 174 | raw_ostream &operator<<(raw_ostream &OS, const SymbolFlagsMap::value_type &KV) { |
| 175 | return OS << "(\"" << KV.first << "\", " << KV.second << ")" ; |
| 176 | } |
| 177 | |
| 178 | raw_ostream &operator<<(raw_ostream &OS, const SymbolMap::value_type &KV) { |
| 179 | return OS << "(\"" << KV.first << "\": " << KV.second << ")" ; |
| 180 | } |
| 181 | |
| 182 | raw_ostream &operator<<(raw_ostream &OS, const SymbolFlagsMap &SymbolFlags) { |
| 183 | return OS << printSequence(S: SymbolFlags, OpenSeq: '{', CloseSeq: '}', |
| 184 | P: PrintSymbolFlagsMapElemsMatchingCLOpts()); |
| 185 | } |
| 186 | |
| 187 | raw_ostream &operator<<(raw_ostream &OS, const SymbolMap &Symbols) { |
| 188 | return OS << printSequence(S: Symbols, OpenSeq: '{', CloseSeq: '}', |
| 189 | P: PrintSymbolMapElemsMatchingCLOpts()); |
| 190 | } |
| 191 | |
| 192 | raw_ostream &operator<<(raw_ostream &OS, |
| 193 | const SymbolDependenceMap::value_type &KV) { |
| 194 | return OS << "(" << KV.first->getName() << ", " << KV.second << ")" ; |
| 195 | } |
| 196 | |
| 197 | raw_ostream &operator<<(raw_ostream &OS, const SymbolDependenceMap &Deps) { |
| 198 | return OS << printSequence(S: Deps, OpenSeq: '{', CloseSeq: '}', |
| 199 | P: PrintAll<SymbolDependenceMap::value_type>()); |
| 200 | } |
| 201 | |
| 202 | raw_ostream &operator<<(raw_ostream &OS, const MaterializationUnit &MU) { |
| 203 | OS << "MU@" << &MU << " (\"" << MU.getName() << "\"" ; |
| 204 | if (anyPrintSymbolOptionSet()) |
| 205 | OS << ", " << MU.getSymbols(); |
| 206 | return OS << ")" ; |
| 207 | } |
| 208 | |
| 209 | raw_ostream &operator<<(raw_ostream &OS, const LookupKind &K) { |
| 210 | switch (K) { |
| 211 | case LookupKind::Static: |
| 212 | return OS << "Static" ; |
| 213 | case LookupKind::DLSym: |
| 214 | return OS << "DLSym" ; |
| 215 | } |
| 216 | llvm_unreachable("Invalid lookup kind" ); |
| 217 | } |
| 218 | |
| 219 | raw_ostream &operator<<(raw_ostream &OS, |
| 220 | const JITDylibLookupFlags &JDLookupFlags) { |
| 221 | switch (JDLookupFlags) { |
| 222 | case JITDylibLookupFlags::MatchExportedSymbolsOnly: |
| 223 | return OS << "MatchExportedSymbolsOnly" ; |
| 224 | case JITDylibLookupFlags::MatchAllSymbols: |
| 225 | return OS << "MatchAllSymbols" ; |
| 226 | } |
| 227 | llvm_unreachable("Invalid JITDylib lookup flags" ); |
| 228 | } |
| 229 | |
| 230 | raw_ostream &operator<<(raw_ostream &OS, const SymbolLookupFlags &LookupFlags) { |
| 231 | switch (LookupFlags) { |
| 232 | case SymbolLookupFlags::RequiredSymbol: |
| 233 | return OS << "RequiredSymbol" ; |
| 234 | case SymbolLookupFlags::WeaklyReferencedSymbol: |
| 235 | return OS << "WeaklyReferencedSymbol" ; |
| 236 | } |
| 237 | llvm_unreachable("Invalid symbol lookup flags" ); |
| 238 | } |
| 239 | |
| 240 | raw_ostream &operator<<(raw_ostream &OS, |
| 241 | const SymbolLookupSet::value_type &KV) { |
| 242 | return OS << "(" << KV.first << ", " << KV.second << ")" ; |
| 243 | } |
| 244 | |
| 245 | raw_ostream &operator<<(raw_ostream &OS, const SymbolLookupSet &LookupSet) { |
| 246 | return OS << printSequence(S: LookupSet, OpenSeq: '{', CloseSeq: '}', |
| 247 | P: PrintAll<SymbolLookupSet::value_type>()); |
| 248 | } |
| 249 | |
| 250 | raw_ostream &operator<<(raw_ostream &OS, |
| 251 | const JITDylibSearchOrder &SearchOrder) { |
| 252 | OS << "[" ; |
| 253 | if (!SearchOrder.empty()) { |
| 254 | assert(SearchOrder.front().first && |
| 255 | "JITDylibList entries must not be null" ); |
| 256 | OS << " (\"" << SearchOrder.front().first->getName() << "\", " |
| 257 | << SearchOrder.begin()->second << ")" ; |
| 258 | for (auto &KV : llvm::drop_begin(RangeOrContainer: SearchOrder)) { |
| 259 | assert(KV.first && "JITDylibList entries must not be null" ); |
| 260 | OS << ", (\"" << KV.first->getName() << "\", " << KV.second << ")" ; |
| 261 | } |
| 262 | } |
| 263 | OS << " ]" ; |
| 264 | return OS; |
| 265 | } |
| 266 | |
| 267 | raw_ostream &operator<<(raw_ostream &OS, const SymbolAliasMap &Aliases) { |
| 268 | OS << "{" ; |
| 269 | for (auto &KV : Aliases) |
| 270 | OS << " " << *KV.first << ": " << KV.second.Aliasee << " " |
| 271 | << KV.second.AliasFlags; |
| 272 | OS << " }" ; |
| 273 | return OS; |
| 274 | } |
| 275 | |
| 276 | raw_ostream &operator<<(raw_ostream &OS, const SymbolState &S) { |
| 277 | switch (S) { |
| 278 | case SymbolState::Invalid: |
| 279 | return OS << "Invalid" ; |
| 280 | case SymbolState::NeverSearched: |
| 281 | return OS << "Never-Searched" ; |
| 282 | case SymbolState::Materializing: |
| 283 | return OS << "Materializing" ; |
| 284 | case SymbolState::Resolved: |
| 285 | return OS << "Resolved" ; |
| 286 | case SymbolState::Emitted: |
| 287 | return OS << "Emitted" ; |
| 288 | case SymbolState::Ready: |
| 289 | return OS << "Ready" ; |
| 290 | } |
| 291 | llvm_unreachable("Invalid state" ); |
| 292 | } |
| 293 | |
| 294 | raw_ostream &operator<<(raw_ostream &OS, const SymbolStringPool &SSP) { |
| 295 | std::lock_guard<std::mutex> Lock(SSP.PoolMutex); |
| 296 | SmallVector<std::pair<StringRef, int>, 0> Vec; |
| 297 | for (auto &KV : SSP.Pool) |
| 298 | Vec.emplace_back(Args: KV.first(), Args: KV.second); |
| 299 | llvm::sort(C&: Vec, Comp: less_first()); |
| 300 | for (auto &[K, V] : Vec) |
| 301 | OS << K << ": " << V << "\n" ; |
| 302 | return OS; |
| 303 | } |
| 304 | |
| 305 | DumpObjects::DumpObjects(std::string DumpDir, std::string IdentifierOverride) |
| 306 | : DumpDir(std::move(DumpDir)), |
| 307 | IdentifierOverride(std::move(IdentifierOverride)) { |
| 308 | |
| 309 | /// Discard any trailing separators. |
| 310 | while (!this->DumpDir.empty() && |
| 311 | sys::path::is_separator(value: this->DumpDir.back())) |
| 312 | this->DumpDir.pop_back(); |
| 313 | } |
| 314 | |
| 315 | Expected<std::unique_ptr<MemoryBuffer>> |
| 316 | DumpObjects::operator()(std::unique_ptr<MemoryBuffer> Obj) { |
| 317 | size_t Idx = 1; |
| 318 | |
| 319 | std::string DumpPathStem; |
| 320 | raw_string_ostream(DumpPathStem) |
| 321 | << DumpDir << (DumpDir.empty() ? "" : "/" ) << getBufferIdentifier(B&: *Obj); |
| 322 | |
| 323 | std::string DumpPath = DumpPathStem + ".o" ; |
| 324 | while (sys::fs::exists(Path: DumpPath)) { |
| 325 | DumpPath.clear(); |
| 326 | raw_string_ostream(DumpPath) << DumpPathStem << "." << (++Idx) << ".o" ; |
| 327 | } |
| 328 | |
| 329 | LLVM_DEBUG({ |
| 330 | dbgs() << "Dumping object buffer [ " << (const void *)Obj->getBufferStart() |
| 331 | << " -- " << (const void *)(Obj->getBufferEnd() - 1) << " ] to " |
| 332 | << DumpPath << "\n" ; |
| 333 | }); |
| 334 | |
| 335 | std::error_code EC; |
| 336 | raw_fd_ostream DumpStream(DumpPath, EC); |
| 337 | if (EC) |
| 338 | return errorCodeToError(EC); |
| 339 | DumpStream.write(Ptr: Obj->getBufferStart(), Size: Obj->getBufferSize()); |
| 340 | |
| 341 | return std::move(Obj); |
| 342 | } |
| 343 | |
| 344 | StringRef DumpObjects::getBufferIdentifier(MemoryBuffer &B) { |
| 345 | if (!IdentifierOverride.empty()) |
| 346 | return IdentifierOverride; |
| 347 | StringRef Identifier = B.getBufferIdentifier(); |
| 348 | Identifier.consume_back(Suffix: ".o" ); |
| 349 | return Identifier; |
| 350 | } |
| 351 | |
| 352 | } // End namespace orc. |
| 353 | } // End namespace llvm. |
| 354 | |