xref: /llvm-project/llvm/lib/ExecutionEngine/Orc/DebugUtils.cpp (revision 3e11ae69abd17a80759ae1d9565d555f6a869304)
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(KV.second);
126   }
127 };
128 
129 struct PrintSymbolMapElemsMatchingCLOpts {
130   bool operator()(const orc::SymbolMap::value_type &KV) {
131     return flagsMatchCLOpts(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(Symbols, '{', '}', PrintAll<SymbolStringPtr>());
142 }
143 
144 raw_ostream &operator<<(raw_ostream &OS, const SymbolNameVector &Symbols) {
145   return OS << printSequence(Symbols, '[', ']', PrintAll<SymbolStringPtr>());
146 }
147 
148 raw_ostream &operator<<(raw_ostream &OS, ArrayRef<SymbolStringPtr> Symbols) {
149   return OS << printSequence(Symbols, '[', ']', 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(SymbolFlags, '{', '}',
184                              PrintSymbolFlagsMapElemsMatchingCLOpts());
185 }
186 
187 raw_ostream &operator<<(raw_ostream &OS, const SymbolMap &Symbols) {
188   return OS << printSequence(Symbols, '{', '}',
189                              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(Deps, '{', '}',
199                              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(LookupSet, '{', '}',
247                              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(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(KV.first(), KV.second);
299   llvm::sort(Vec, 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(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(*Obj);
322 
323   std::string DumpPath = DumpPathStem + ".o";
324   while (sys::fs::exists(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(Obj->getBufferStart(), 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(".o");
349   return Identifier;
350 }
351 
352 } // End namespace orc.
353 } // End namespace llvm.
354