Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 1 | //===- GsymReader.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 | #ifndef LLVM_DEBUGINFO_GSYM_GSYMREADER_H |
||
10 | #define LLVM_DEBUGINFO_GSYM_GSYMREADER_H |
||
11 | |||
12 | #include "llvm/ADT/ArrayRef.h" |
||
13 | #include "llvm/DebugInfo/GSYM/FileEntry.h" |
||
14 | #include "llvm/DebugInfo/GSYM/FunctionInfo.h" |
||
15 | #include "llvm/DebugInfo/GSYM/Header.h" |
||
16 | #include "llvm/DebugInfo/GSYM/LineEntry.h" |
||
17 | #include "llvm/DebugInfo/GSYM/StringTable.h" |
||
18 | #include "llvm/Support/DataExtractor.h" |
||
19 | #include "llvm/Support/Endian.h" |
||
20 | #include "llvm/Support/ErrorOr.h" |
||
21 | #include <inttypes.h> |
||
22 | #include <memory> |
||
23 | #include <stdint.h> |
||
24 | #include <vector> |
||
25 | |||
26 | namespace llvm { |
||
27 | class MemoryBuffer; |
||
28 | class raw_ostream; |
||
29 | |||
30 | namespace gsym { |
||
31 | |||
32 | /// GsymReader is used to read GSYM data from a file or buffer. |
||
33 | /// |
||
34 | /// This class is optimized for very quick lookups when the endianness matches |
||
35 | /// the host system. The Header, address table, address info offsets, and file |
||
36 | /// table is designed to be mmap'ed as read only into memory and used without |
||
37 | /// any parsing needed. If the endianness doesn't match, we swap these objects |
||
38 | /// and tables into GsymReader::SwappedData and then point our header and |
||
39 | /// ArrayRefs to this swapped internal data. |
||
40 | /// |
||
41 | /// GsymReader objects must use one of the static functions to create an |
||
42 | /// instance: GsymReader::openFile(...) and GsymReader::copyBuffer(...). |
||
43 | |||
44 | class GsymReader { |
||
45 | GsymReader(std::unique_ptr<MemoryBuffer> Buffer); |
||
46 | llvm::Error parse(); |
||
47 | |||
48 | std::unique_ptr<MemoryBuffer> MemBuffer; |
||
49 | StringRef GsymBytes; |
||
50 | llvm::support::endianness Endian; |
||
51 | const Header *Hdr = nullptr; |
||
52 | ArrayRef<uint8_t> AddrOffsets; |
||
53 | ArrayRef<uint32_t> AddrInfoOffsets; |
||
54 | ArrayRef<FileEntry> Files; |
||
55 | StringTable StrTab; |
||
56 | /// When the GSYM file's endianness doesn't match the host system then |
||
57 | /// we must decode all data structures that need to be swapped into |
||
58 | /// local storage and set point the ArrayRef objects above to these swapped |
||
59 | /// copies. |
||
60 | struct SwappedData { |
||
61 | Header Hdr; |
||
62 | std::vector<uint8_t> AddrOffsets; |
||
63 | std::vector<uint32_t> AddrInfoOffsets; |
||
64 | std::vector<FileEntry> Files; |
||
65 | }; |
||
66 | std::unique_ptr<SwappedData> Swap; |
||
67 | |||
68 | public: |
||
69 | GsymReader(GsymReader &&RHS); |
||
70 | ~GsymReader(); |
||
71 | |||
72 | /// Construct a GsymReader from a file on disk. |
||
73 | /// |
||
74 | /// \param Path The file path the GSYM file to read. |
||
75 | /// \returns An expected GsymReader that contains the object or an error |
||
76 | /// object that indicates reason for failing to read the GSYM. |
||
77 | static llvm::Expected<GsymReader> openFile(StringRef Path); |
||
78 | |||
79 | /// Construct a GsymReader from a buffer. |
||
80 | /// |
||
81 | /// \param Bytes A set of bytes that will be copied and owned by the |
||
82 | /// returned object on success. |
||
83 | /// \returns An expected GsymReader that contains the object or an error |
||
84 | /// object that indicates reason for failing to read the GSYM. |
||
85 | static llvm::Expected<GsymReader> copyBuffer(StringRef Bytes); |
||
86 | |||
87 | /// Access the GSYM header. |
||
88 | /// \returns A native endian version of the GSYM header. |
||
89 | const Header &getHeader() const; |
||
90 | |||
91 | /// Get the full function info for an address. |
||
92 | /// |
||
93 | /// This should be called when a client will store a copy of the complete |
||
94 | /// FunctionInfo for a given address. For one off lookups, use the lookup() |
||
95 | /// function below. |
||
96 | /// |
||
97 | /// Symbolication server processes might want to parse the entire function |
||
98 | /// info for a given address and cache it if the process stays around to |
||
99 | /// service many symbolication addresses, like for parsing profiling |
||
100 | /// information. |
||
101 | /// |
||
102 | /// \param Addr A virtual address from the orignal object file to lookup. |
||
103 | /// |
||
104 | /// \returns An expected FunctionInfo that contains the function info object |
||
105 | /// or an error object that indicates reason for failing to lookup the |
||
106 | /// address. |
||
107 | llvm::Expected<FunctionInfo> getFunctionInfo(uint64_t Addr) const; |
||
108 | |||
109 | /// Lookup an address in the a GSYM. |
||
110 | /// |
||
111 | /// Lookup just the information needed for a specific address \a Addr. This |
||
112 | /// function is faster that calling getFunctionInfo() as it will only return |
||
113 | /// information that pertains to \a Addr and allows the parsing to skip any |
||
114 | /// extra information encoded for other addresses. For example the line table |
||
115 | /// parsing can stop when a matching LineEntry has been fouhnd, and the |
||
116 | /// InlineInfo can stop parsing early once a match has been found and also |
||
117 | /// skip information that doesn't match. This avoids memory allocations and |
||
118 | /// is much faster for lookups. |
||
119 | /// |
||
120 | /// \param Addr A virtual address from the orignal object file to lookup. |
||
121 | /// \returns An expected LookupResult that contains only the information |
||
122 | /// needed for the current address, or an error object that indicates reason |
||
123 | /// for failing to lookup the address. |
||
124 | llvm::Expected<LookupResult> lookup(uint64_t Addr) const; |
||
125 | |||
126 | /// Get a string from the string table. |
||
127 | /// |
||
128 | /// \param Offset The string table offset for the string to retrieve. |
||
129 | /// \returns The string from the strin table. |
||
130 | StringRef getString(uint32_t Offset) const { return StrTab[Offset]; } |
||
131 | |||
132 | /// Get the a file entry for the suppplied file index. |
||
133 | /// |
||
134 | /// Used to convert any file indexes in the FunctionInfo data back into |
||
135 | /// files. This function can be used for iteration, but is more commonly used |
||
136 | /// for random access when doing lookups. |
||
137 | /// |
||
138 | /// \param Index An index into the file table. |
||
139 | /// \returns An optional FileInfo that will be valid if the file index is |
||
140 | /// valid, or std::nullopt if the file index is out of bounds, |
||
141 | std::optional<FileEntry> getFile(uint32_t Index) const { |
||
142 | if (Index < Files.size()) |
||
143 | return Files[Index]; |
||
144 | return std::nullopt; |
||
145 | } |
||
146 | |||
147 | /// Dump the entire Gsym data contained in this object. |
||
148 | /// |
||
149 | /// \param OS The output stream to dump to. |
||
150 | void dump(raw_ostream &OS); |
||
151 | |||
152 | /// Dump a FunctionInfo object. |
||
153 | /// |
||
154 | /// This function will convert any string table indexes and file indexes |
||
155 | /// into human readable format. |
||
156 | /// |
||
157 | /// \param OS The output stream to dump to. |
||
158 | /// |
||
159 | /// \param FI The object to dump. |
||
160 | void dump(raw_ostream &OS, const FunctionInfo &FI); |
||
161 | |||
162 | /// Dump a LineTable object. |
||
163 | /// |
||
164 | /// This function will convert any string table indexes and file indexes |
||
165 | /// into human readable format. |
||
166 | /// |
||
167 | /// |
||
168 | /// \param OS The output stream to dump to. |
||
169 | /// |
||
170 | /// \param LT The object to dump. |
||
171 | void dump(raw_ostream &OS, const LineTable <); |
||
172 | |||
173 | /// Dump a InlineInfo object. |
||
174 | /// |
||
175 | /// This function will convert any string table indexes and file indexes |
||
176 | /// into human readable format. |
||
177 | /// |
||
178 | /// \param OS The output stream to dump to. |
||
179 | /// |
||
180 | /// \param II The object to dump. |
||
181 | /// |
||
182 | /// \param Indent The indentation as number of spaces. Used for recurive |
||
183 | /// dumping. |
||
184 | void dump(raw_ostream &OS, const InlineInfo &II, uint32_t Indent = 0); |
||
185 | |||
186 | /// Dump a FileEntry object. |
||
187 | /// |
||
188 | /// This function will convert any string table indexes into human readable |
||
189 | /// format. |
||
190 | /// |
||
191 | /// \param OS The output stream to dump to. |
||
192 | /// |
||
193 | /// \param FE The object to dump. |
||
194 | void dump(raw_ostream &OS, std::optional<FileEntry> FE); |
||
195 | |||
196 | /// Get the number of addresses in this Gsym file. |
||
197 | uint32_t getNumAddresses() const { |
||
198 | return Hdr->NumAddresses; |
||
199 | } |
||
200 | |||
201 | /// Gets an address from the address table. |
||
202 | /// |
||
203 | /// Addresses are stored as offsets frrom the gsym::Header::BaseAddress. |
||
204 | /// |
||
205 | /// \param Index A index into the address table. |
||
206 | /// \returns A resolved virtual address for adddress in the address table |
||
207 | /// or std::nullopt if Index is out of bounds. |
||
208 | std::optional<uint64_t> getAddress(size_t Index) const; |
||
209 | |||
210 | protected: |
||
211 | |||
212 | /// Get an appropriate address info offsets array. |
||
213 | /// |
||
214 | /// The address table in the GSYM file is stored as array of 1, 2, 4 or 8 |
||
215 | /// byte offsets from the The gsym::Header::BaseAddress. The table is stored |
||
216 | /// internally as a array of bytes that are in the correct endianness. When |
||
217 | /// we access this table we must get an array that matches those sizes. This |
||
218 | /// templatized helper function is used when accessing address offsets in the |
||
219 | /// AddrOffsets member variable. |
||
220 | /// |
||
221 | /// \returns An ArrayRef of an appropriate address offset size. |
||
222 | template <class T> ArrayRef<T> |
||
223 | getAddrOffsets() const { |
||
224 | return ArrayRef<T>(reinterpret_cast<const T *>(AddrOffsets.data()), |
||
225 | AddrOffsets.size()/sizeof(T)); |
||
226 | } |
||
227 | |||
228 | /// Get an appropriate address from the address table. |
||
229 | /// |
||
230 | /// The address table in the GSYM file is stored as array of 1, 2, 4 or 8 |
||
231 | /// byte address offsets from the The gsym::Header::BaseAddress. The table is |
||
232 | /// stored internally as a array of bytes that are in the correct endianness. |
||
233 | /// In order to extract an address from the address table we must access the |
||
234 | /// address offset using the correct size and then add it to the BaseAddress |
||
235 | /// in the header. |
||
236 | /// |
||
237 | /// \param Index An index into the AddrOffsets array. |
||
238 | /// \returns An virtual address that matches the original object file for the |
||
239 | /// address as the specified index, or std::nullopt if Index is out of bounds. |
||
240 | template <class T> |
||
241 | std::optional<uint64_t> addressForIndex(size_t Index) const { |
||
242 | ArrayRef<T> AIO = getAddrOffsets<T>(); |
||
243 | if (Index < AIO.size()) |
||
244 | return AIO[Index] + Hdr->BaseAddress; |
||
245 | return std::nullopt; |
||
246 | } |
||
247 | /// Lookup an address offset in the AddrOffsets table. |
||
248 | /// |
||
249 | /// Given an address offset, look it up using a binary search of the |
||
250 | /// AddrOffsets table. |
||
251 | /// |
||
252 | /// \param AddrOffset An address offset, that has already been computed by |
||
253 | /// subtracting the gsym::Header::BaseAddress. |
||
254 | /// \returns The matching address offset index. This index will be used to |
||
255 | /// extract the FunctionInfo data's offset from the AddrInfoOffsets array. |
||
256 | template <class T> |
||
257 | std::optional<uint64_t> |
||
258 | getAddressOffsetIndex(const uint64_t AddrOffset) const { |
||
259 | ArrayRef<T> AIO = getAddrOffsets<T>(); |
||
260 | const auto Begin = AIO.begin(); |
||
261 | const auto End = AIO.end(); |
||
262 | auto Iter = std::lower_bound(Begin, End, AddrOffset); |
||
263 | // Watch for addresses that fall between the gsym::Header::BaseAddress and |
||
264 | // the first address offset. |
||
265 | if (Iter == Begin && AddrOffset < *Begin) |
||
266 | return std::nullopt; |
||
267 | if (Iter == End || AddrOffset < *Iter) |
||
268 | --Iter; |
||
269 | return std::distance(Begin, Iter); |
||
270 | } |
||
271 | |||
272 | /// Create a GSYM from a memory buffer. |
||
273 | /// |
||
274 | /// Called by both openFile() and copyBuffer(), this function does all of the |
||
275 | /// work of parsing the GSYM file and returning an error. |
||
276 | /// |
||
277 | /// \param MemBuffer A memory buffer that will transfer ownership into the |
||
278 | /// GsymReader. |
||
279 | /// \returns An expected GsymReader that contains the object or an error |
||
280 | /// object that indicates reason for failing to read the GSYM. |
||
281 | static llvm::Expected<llvm::gsym::GsymReader> |
||
282 | create(std::unique_ptr<MemoryBuffer> &MemBuffer); |
||
283 | |||
284 | |||
285 | /// Given an address, find the address index. |
||
286 | /// |
||
287 | /// Binary search the address table and find the matching address index. |
||
288 | /// |
||
289 | /// \param Addr A virtual address that matches the original object file |
||
290 | /// to lookup. |
||
291 | /// \returns An index into the address table. This index can be used to |
||
292 | /// extract the FunctionInfo data's offset from the AddrInfoOffsets array. |
||
293 | /// Returns an error if the address isn't in the GSYM with details of why. |
||
294 | Expected<uint64_t> getAddressIndex(const uint64_t Addr) const; |
||
295 | |||
296 | /// Given an address index, get the offset for the FunctionInfo. |
||
297 | /// |
||
298 | /// Looking up an address is done by finding the corresponding address |
||
299 | /// index for the address. This index is then used to get the offset of the |
||
300 | /// FunctionInfo data that we will decode using this function. |
||
301 | /// |
||
302 | /// \param Index An index into the address table. |
||
303 | /// \returns An optional GSYM data offset for the offset of the FunctionInfo |
||
304 | /// that needs to be decoded. |
||
305 | std::optional<uint64_t> getAddressInfoOffset(size_t Index) const; |
||
306 | }; |
||
307 | |||
308 | } // namespace gsym |
||
309 | } // namespace llvm |
||
310 | |||
311 | #endif // LLVM_DEBUGINFO_GSYM_GSYMREADER_H |