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
22using namespace llvm;
23
24namespace {
25
26#ifndef NDEBUG
27
28cl::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
33cl::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
38cl::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.
46template <typename T> struct PrintAll {
47 bool operator()(const T &E) { return true; }
48};
49
50bool anyPrintSymbolOptionSet() {
51#ifndef NDEBUG
52 return PrintHidden || PrintCallable || PrintData;
53#else
54 return false;
55#endif // NDEBUG
56}
57
58bool 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.
80template <typename Sequence,
81 typename Pred = PrintAll<typename Sequence::value_type>>
82class SequencePrinter {
83public:
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
103private:
104 const Sequence &S;
105 char OpenSeq;
106 char CloseSeq;
107 mutable Pred ShouldPrint;
108};
109
110template <typename Sequence, typename Pred>
111SequencePrinter<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.
117template <typename Sequence, typename Pred>
118llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
119 const SequencePrinter<Sequence, Pred> &Printer) {
120 Printer.printTo(OS);
121 return OS;
122}
123
124struct PrintSymbolFlagsMapElemsMatchingCLOpts {
125 bool operator()(const orc::SymbolFlagsMap::value_type &KV) {
126 return flagsMatchCLOpts(Flags: KV.second);
127 }
128};
129
130struct 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
138namespace llvm {
139namespace orc {
140
141raw_ostream &operator<<(raw_ostream &OS, const SymbolStringPtr &Sym) {
142 return OS << *Sym;
143}
144
145raw_ostream &operator<<(raw_ostream &OS, NonOwningSymbolStringPtr Sym) {
146 return OS << *Sym;
147}
148
149raw_ostream &operator<<(raw_ostream &OS, const SymbolNameSet &Symbols) {
150 return OS << printSequence(S: Symbols, OpenSeq: '{', CloseSeq: '}', P: PrintAll<SymbolStringPtr>());
151}
152
153raw_ostream &operator<<(raw_ostream &OS, const SymbolNameVector &Symbols) {
154 return OS << printSequence(S: Symbols, OpenSeq: '[', CloseSeq: ']', P: PrintAll<SymbolStringPtr>());
155}
156
157raw_ostream &operator<<(raw_ostream &OS, ArrayRef<SymbolStringPtr> Symbols) {
158 return OS << printSequence(S: Symbols, OpenSeq: '[', CloseSeq: ']', P: PrintAll<SymbolStringPtr>());
159}
160
161raw_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
179raw_ostream &operator<<(raw_ostream &OS, const ExecutorSymbolDef &Sym) {
180 return OS << Sym.getAddress() << " " << Sym.getFlags();
181}
182
183raw_ostream &operator<<(raw_ostream &OS, const SymbolFlagsMap::value_type &KV) {
184 return OS << "(\"" << KV.first << "\", " << KV.second << ")";
185}
186
187raw_ostream &operator<<(raw_ostream &OS, const SymbolMap::value_type &KV) {
188 return OS << "(\"" << KV.first << "\": " << KV.second << ")";
189}
190
191raw_ostream &operator<<(raw_ostream &OS, const SymbolFlagsMap &SymbolFlags) {
192 return OS << printSequence(S: SymbolFlags, OpenSeq: '{', CloseSeq: '}',
193 P: PrintSymbolFlagsMapElemsMatchingCLOpts());
194}
195
196raw_ostream &operator<<(raw_ostream &OS, const SymbolMap &Symbols) {
197 return OS << printSequence(S: Symbols, OpenSeq: '{', CloseSeq: '}',
198 P: PrintSymbolMapElemsMatchingCLOpts());
199}
200
201raw_ostream &operator<<(raw_ostream &OS,
202 const SymbolDependenceMap::value_type &KV) {
203 return OS << "(" << KV.first->getName() << ", " << KV.second << ")";
204}
205
206raw_ostream &operator<<(raw_ostream &OS, const SymbolDependenceMap &Deps) {
207 return OS << printSequence(S: Deps, OpenSeq: '{', CloseSeq: '}',
208 P: PrintAll<SymbolDependenceMap::value_type>());
209}
210
211raw_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
218raw_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
228raw_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
239raw_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
249raw_ostream &operator<<(raw_ostream &OS,
250 const SymbolLookupSet::value_type &KV) {
251 return OS << "(" << KV.first << ", " << KV.second << ")";
252}
253
254raw_ostream &operator<<(raw_ostream &OS, const SymbolLookupSet &LookupSet) {
255 return OS << printSequence(S: LookupSet, OpenSeq: '{', CloseSeq: '}',
256 P: PrintAll<SymbolLookupSet::value_type>());
257}
258
259raw_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
276raw_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
285raw_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
303raw_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
314DumpObjects::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
324Expected<std::unique_ptr<MemoryBuffer>>
325DumpObjects::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
353StringRef 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