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