1 | //===- DIContext.h ----------------------------------------------*- C++ -*-===// |
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 | // This file defines DIContext, an abstract data structure that holds |
10 | // debug information data. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_DEBUGINFO_DICONTEXT_H |
15 | #define LLVM_DEBUGINFO_DICONTEXT_H |
16 | |
17 | #include "llvm/ADT/SmallVector.h" |
18 | #include "llvm/Object/ObjectFile.h" |
19 | #include "llvm/Support/WithColor.h" |
20 | #include "llvm/Support/raw_ostream.h" |
21 | #include <cassert> |
22 | #include <cstdint> |
23 | #include <memory> |
24 | #include <optional> |
25 | #include <string> |
26 | #include <tuple> |
27 | #include <utility> |
28 | |
29 | namespace llvm { |
30 | |
31 | /// A format-neutral container for source line information. |
32 | struct DILineInfo { |
33 | // DILineInfo contains "<invalid>" for function/filename it cannot fetch. |
34 | static constexpr const char *const BadString = "<invalid>" ; |
35 | // Use "??" instead of "<invalid>" to make our output closer to addr2line. |
36 | static constexpr const char *const Addr2LineBadString = "??" ; |
37 | std::string FileName; |
38 | std::string FunctionName; |
39 | std::string StartFileName; |
40 | // Full source corresponding to `FileName` |
41 | std::optional<StringRef> Source; |
42 | // Source code for this particular line |
43 | // (in case if `Source` is not available) |
44 | std::optional<StringRef> LineSource; |
45 | uint32_t Line = 0; |
46 | uint32_t Column = 0; |
47 | uint32_t StartLine = 0; |
48 | std::optional<uint64_t> StartAddress; |
49 | |
50 | // DWARF-specific. |
51 | uint32_t Discriminator = 0; |
52 | |
53 | DILineInfo() |
54 | : FileName(BadString), FunctionName(BadString), StartFileName(BadString) { |
55 | } |
56 | |
57 | bool operator==(const DILineInfo &RHS) const { |
58 | return Line == RHS.Line && Column == RHS.Column && |
59 | FileName == RHS.FileName && FunctionName == RHS.FunctionName && |
60 | StartFileName == RHS.StartFileName && StartLine == RHS.StartLine && |
61 | Discriminator == RHS.Discriminator; |
62 | } |
63 | |
64 | bool operator!=(const DILineInfo &RHS) const { return !(*this == RHS); } |
65 | |
66 | bool operator<(const DILineInfo &RHS) const { |
67 | return std::tie(args: FileName, args: FunctionName, args: StartFileName, args: Line, args: Column, |
68 | args: StartLine, args: Discriminator) < |
69 | std::tie(args: RHS.FileName, args: RHS.FunctionName, args: RHS.StartFileName, args: RHS.Line, |
70 | args: RHS.Column, args: RHS.StartLine, args: RHS.Discriminator); |
71 | } |
72 | |
73 | explicit operator bool() const { return *this != DILineInfo(); } |
74 | |
75 | void dump(raw_ostream &OS) { |
76 | OS << "Line info: " ; |
77 | if (FileName != BadString) |
78 | OS << "file '" << FileName << "', " ; |
79 | if (FunctionName != BadString) |
80 | OS << "function '" << FunctionName << "', " ; |
81 | OS << "line " << Line << ", " ; |
82 | OS << "column " << Column << ", " ; |
83 | if (StartFileName != BadString) |
84 | OS << "start file '" << StartFileName << "', " ; |
85 | OS << "start line " << StartLine << '\n'; |
86 | } |
87 | }; |
88 | |
89 | using DILineInfoTable = SmallVector<std::pair<uint64_t, DILineInfo>, 16>; |
90 | |
91 | /// A format-neutral container for inlined code description. |
92 | class DIInliningInfo { |
93 | SmallVector<DILineInfo, 4> Frames; |
94 | |
95 | public: |
96 | DIInliningInfo() = default; |
97 | |
98 | /// Returns the frame at `Index`. Frames are stored in bottom-up |
99 | /// (leaf-to-root) order with increasing index. |
100 | const DILineInfo &getFrame(unsigned Index) const { |
101 | assert(Index < Frames.size()); |
102 | return Frames[Index]; |
103 | } |
104 | |
105 | DILineInfo *getMutableFrame(unsigned Index) { |
106 | assert(Index < Frames.size()); |
107 | return &Frames[Index]; |
108 | } |
109 | |
110 | uint32_t getNumberOfFrames() const { return Frames.size(); } |
111 | |
112 | void addFrame(const DILineInfo &Frame) { Frames.push_back(Elt: Frame); } |
113 | |
114 | void resize(unsigned i) { Frames.resize(N: i); } |
115 | }; |
116 | |
117 | /// Container for description of a global variable. |
118 | struct DIGlobal { |
119 | std::string Name; |
120 | uint64_t Start = 0; |
121 | uint64_t Size = 0; |
122 | std::string DeclFile; |
123 | uint64_t DeclLine = 0; |
124 | |
125 | DIGlobal() : Name(DILineInfo::BadString) {} |
126 | }; |
127 | |
128 | struct DILocal { |
129 | std::string FunctionName; |
130 | std::string Name; |
131 | std::string DeclFile; |
132 | uint64_t DeclLine = 0; |
133 | std::optional<int64_t> FrameOffset; |
134 | std::optional<uint64_t> Size; |
135 | std::optional<uint64_t> TagOffset; |
136 | }; |
137 | |
138 | /// A DINameKind is passed to name search methods to specify a |
139 | /// preference regarding the type of name resolution the caller wants. |
140 | enum class DINameKind { None, ShortName, LinkageName }; |
141 | |
142 | /// Controls which fields of DILineInfo container should be filled |
143 | /// with data. |
144 | struct DILineInfoSpecifier { |
145 | enum class FileLineInfoKind { |
146 | None, |
147 | // RawValue is whatever the compiler stored in the filename table. Could be |
148 | // a full path, could be something else. |
149 | RawValue, |
150 | BaseNameOnly, |
151 | // Relative to the compilation directory. |
152 | RelativeFilePath, |
153 | AbsoluteFilePath |
154 | }; |
155 | using FunctionNameKind = DINameKind; |
156 | |
157 | FileLineInfoKind FLIKind; |
158 | FunctionNameKind FNKind; |
159 | |
160 | DILineInfoSpecifier(FileLineInfoKind FLIKind = FileLineInfoKind::RawValue, |
161 | FunctionNameKind FNKind = FunctionNameKind::None) |
162 | : FLIKind(FLIKind), FNKind(FNKind) {} |
163 | |
164 | inline bool operator==(const DILineInfoSpecifier &RHS) const { |
165 | return FLIKind == RHS.FLIKind && FNKind == RHS.FNKind; |
166 | } |
167 | }; |
168 | |
169 | /// This is just a helper to programmatically construct DIDumpType. |
170 | enum DIDumpTypeCounter { |
171 | #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ |
172 | DIDT_ID_##ENUM_NAME, |
173 | #include "llvm/BinaryFormat/Dwarf.def" |
174 | #undef HANDLE_DWARF_SECTION |
175 | DIDT_ID_UUID, |
176 | DIDT_ID_Count |
177 | }; |
178 | static_assert(DIDT_ID_Count <= 32, "section types overflow storage" ); |
179 | |
180 | /// Selects which debug sections get dumped. |
181 | enum DIDumpType : unsigned { |
182 | DIDT_Null, |
183 | DIDT_All = ~0U, |
184 | #define HANDLE_DWARF_SECTION(ENUM_NAME, ELF_NAME, CMDLINE_NAME, OPTION) \ |
185 | DIDT_##ENUM_NAME = 1U << DIDT_ID_##ENUM_NAME, |
186 | #include "llvm/BinaryFormat/Dwarf.def" |
187 | #undef HANDLE_DWARF_SECTION |
188 | DIDT_UUID = 1 << DIDT_ID_UUID, |
189 | }; |
190 | |
191 | /// Container for dump options that control which debug information will be |
192 | /// dumped. |
193 | struct DIDumpOptions { |
194 | unsigned DumpType = DIDT_All; |
195 | unsigned ChildRecurseDepth = -1U; |
196 | unsigned ParentRecurseDepth = -1U; |
197 | uint16_t Version = 0; // DWARF version to assume when extracting. |
198 | uint8_t AddrSize = 4; // Address byte size to assume when extracting. |
199 | bool ShowAddresses = true; |
200 | bool ShowChildren = false; |
201 | bool ShowParents = false; |
202 | bool ShowForm = false; |
203 | bool SummarizeTypes = false; |
204 | bool Verbose = false; |
205 | bool DisplayRawContents = false; |
206 | bool IsEH = false; |
207 | bool DumpNonSkeleton = false; |
208 | bool ShowAggregateErrors = false; |
209 | std::function<llvm::StringRef(uint64_t DwarfRegNum, bool IsEH)> |
210 | GetNameForDWARFReg; |
211 | |
212 | /// Return default option set for printing a single DIE without children. |
213 | static DIDumpOptions getForSingleDIE() { |
214 | DIDumpOptions Opts; |
215 | Opts.ChildRecurseDepth = 0; |
216 | Opts.ParentRecurseDepth = 0; |
217 | return Opts; |
218 | } |
219 | |
220 | /// Return the options with RecurseDepth set to 0 unless explicitly required. |
221 | DIDumpOptions noImplicitRecursion() const { |
222 | DIDumpOptions Opts = *this; |
223 | if (ChildRecurseDepth == -1U && !ShowChildren) |
224 | Opts.ChildRecurseDepth = 0; |
225 | if (ParentRecurseDepth == -1U && !ShowParents) |
226 | Opts.ParentRecurseDepth = 0; |
227 | return Opts; |
228 | } |
229 | |
230 | std::function<void(Error)> RecoverableErrorHandler = |
231 | WithColor::defaultErrorHandler; |
232 | std::function<void(Error)> WarningHandler = WithColor::defaultWarningHandler; |
233 | }; |
234 | |
235 | class DIContext { |
236 | public: |
237 | enum DIContextKind { CK_DWARF, CK_PDB, CK_BTF }; |
238 | |
239 | DIContext(DIContextKind K) : Kind(K) {} |
240 | virtual ~DIContext() = default; |
241 | |
242 | DIContextKind getKind() const { return Kind; } |
243 | |
244 | virtual void dump(raw_ostream &OS, DIDumpOptions DumpOpts) = 0; |
245 | |
246 | virtual bool verify(raw_ostream &OS, DIDumpOptions DumpOpts = {}) { |
247 | // No verifier? Just say things went well. |
248 | return true; |
249 | } |
250 | |
251 | virtual DILineInfo getLineInfoForAddress( |
252 | object::SectionedAddress Address, |
253 | DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0; |
254 | virtual DILineInfo |
255 | getLineInfoForDataAddress(object::SectionedAddress Address) = 0; |
256 | virtual DILineInfoTable getLineInfoForAddressRange( |
257 | object::SectionedAddress Address, uint64_t Size, |
258 | DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0; |
259 | virtual DIInliningInfo getInliningInfoForAddress( |
260 | object::SectionedAddress Address, |
261 | DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0; |
262 | |
263 | virtual std::vector<DILocal> |
264 | getLocalsForAddress(object::SectionedAddress Address) = 0; |
265 | |
266 | private: |
267 | const DIContextKind Kind; |
268 | }; |
269 | |
270 | /// An inferface for inquiring the load address of a loaded object file |
271 | /// to be used by the DIContext implementations when applying relocations |
272 | /// on the fly. |
273 | class LoadedObjectInfo { |
274 | protected: |
275 | LoadedObjectInfo() = default; |
276 | LoadedObjectInfo(const LoadedObjectInfo &) = default; |
277 | |
278 | public: |
279 | virtual ~LoadedObjectInfo() = default; |
280 | |
281 | /// Obtain the Load Address of a section by SectionRef. |
282 | /// |
283 | /// Calculate the address of the given section. |
284 | /// The section need not be present in the local address space. The addresses |
285 | /// need to be consistent with the addresses used to query the DIContext and |
286 | /// the output of this function should be deterministic, i.e. repeated calls |
287 | /// with the same Sec should give the same address. |
288 | virtual uint64_t getSectionLoadAddress(const object::SectionRef &Sec) const { |
289 | return 0; |
290 | } |
291 | |
292 | /// If conveniently available, return the content of the given Section. |
293 | /// |
294 | /// When the section is available in the local address space, in relocated |
295 | /// (loaded) form, e.g. because it was relocated by a JIT for execution, this |
296 | /// function should provide the contents of said section in `Data`. If the |
297 | /// loaded section is not available, or the cost of retrieving it would be |
298 | /// prohibitive, this function should return false. In that case, relocations |
299 | /// will be read from the local (unrelocated) object file and applied on the |
300 | /// fly. Note that this method is used purely for optimzation purposes in the |
301 | /// common case of JITting in the local address space, so returning false |
302 | /// should always be correct. |
303 | virtual bool getLoadedSectionContents(const object::SectionRef &Sec, |
304 | StringRef &Data) const { |
305 | return false; |
306 | } |
307 | |
308 | // FIXME: This is untested and unused anywhere in the LLVM project, it's |
309 | // used/needed by Julia (an external project). It should have some coverage |
310 | // (at least tests, but ideally example functionality). |
311 | /// Obtain a copy of this LoadedObjectInfo. |
312 | virtual std::unique_ptr<LoadedObjectInfo> clone() const = 0; |
313 | }; |
314 | |
315 | template <typename Derived, typename Base = LoadedObjectInfo> |
316 | struct LoadedObjectInfoHelper : Base { |
317 | protected: |
318 | LoadedObjectInfoHelper(const LoadedObjectInfoHelper &) = default; |
319 | LoadedObjectInfoHelper() = default; |
320 | |
321 | public: |
322 | template <typename... Ts> |
323 | LoadedObjectInfoHelper(Ts &&...Args) : Base(std::forward<Ts>(Args)...) {} |
324 | |
325 | std::unique_ptr<llvm::LoadedObjectInfo> clone() const override { |
326 | return std::make_unique<Derived>(static_cast<const Derived &>(*this)); |
327 | } |
328 | }; |
329 | |
330 | } // end namespace llvm |
331 | |
332 | #endif // LLVM_DEBUGINFO_DICONTEXT_H |
333 | |