File: | build/source/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp |
Warning: | line 1504, column 18 Moved-from object 'EC' is moved |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===- DumpOutputStyle.cpp ------------------------------------ *- 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 | #include "DumpOutputStyle.h" | |||
10 | ||||
11 | #include "MinimalSymbolDumper.h" | |||
12 | #include "MinimalTypeDumper.h" | |||
13 | #include "StreamUtil.h" | |||
14 | #include "TypeReferenceTracker.h" | |||
15 | #include "llvm-pdbutil.h" | |||
16 | ||||
17 | #include "llvm/ADT/STLExtras.h" | |||
18 | #include "llvm/DebugInfo/CodeView/CVSymbolVisitor.h" | |||
19 | #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" | |||
20 | #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" | |||
21 | #include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" | |||
22 | #include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" | |||
23 | #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" | |||
24 | #include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" | |||
25 | #include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" | |||
26 | #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" | |||
27 | #include "llvm/DebugInfo/CodeView/DebugSymbolsSubsection.h" | |||
28 | #include "llvm/DebugInfo/CodeView/Formatters.h" | |||
29 | #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" | |||
30 | #include "llvm/DebugInfo/CodeView/Line.h" | |||
31 | #include "llvm/DebugInfo/CodeView/SymbolDeserializer.h" | |||
32 | #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbackPipeline.h" | |||
33 | #include "llvm/DebugInfo/CodeView/SymbolVisitorCallbacks.h" | |||
34 | #include "llvm/DebugInfo/CodeView/TypeHashing.h" | |||
35 | #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" | |||
36 | #include "llvm/DebugInfo/MSF/MappedBlockStream.h" | |||
37 | #include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptor.h" | |||
38 | #include "llvm/DebugInfo/PDB/Native/DbiStream.h" | |||
39 | #include "llvm/DebugInfo/PDB/Native/FormatUtil.h" | |||
40 | #include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" | |||
41 | #include "llvm/DebugInfo/PDB/Native/ISectionContribVisitor.h" | |||
42 | #include "llvm/DebugInfo/PDB/Native/InfoStream.h" | |||
43 | #include "llvm/DebugInfo/PDB/Native/InputFile.h" | |||
44 | #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" | |||
45 | #include "llvm/DebugInfo/PDB/Native/NativeSession.h" | |||
46 | #include "llvm/DebugInfo/PDB/Native/PDBFile.h" | |||
47 | #include "llvm/DebugInfo/PDB/Native/PublicsStream.h" | |||
48 | #include "llvm/DebugInfo/PDB/Native/RawError.h" | |||
49 | #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" | |||
50 | #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" | |||
51 | #include "llvm/DebugInfo/PDB/Native/TpiStream.h" | |||
52 | #include "llvm/Object/COFF.h" | |||
53 | #include "llvm/Support/BinaryStreamReader.h" | |||
54 | #include "llvm/Support/FormatAdapters.h" | |||
55 | #include "llvm/Support/FormatVariadic.h" | |||
56 | ||||
57 | #include <cctype> | |||
58 | ||||
59 | using namespace llvm; | |||
60 | using namespace llvm::codeview; | |||
61 | using namespace llvm::msf; | |||
62 | using namespace llvm::pdb; | |||
63 | ||||
64 | DumpOutputStyle::DumpOutputStyle(InputFile &File) | |||
65 | : File(File), P(2, false, outs(), opts::Filters) { | |||
66 | if (opts::dump::DumpTypeRefStats) | |||
67 | RefTracker.reset(new TypeReferenceTracker(File)); | |||
68 | } | |||
69 | ||||
70 | DumpOutputStyle::~DumpOutputStyle() {} | |||
71 | ||||
72 | PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); } | |||
73 | object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); } | |||
74 | ||||
75 | void DumpOutputStyle::printStreamNotValidForObj() { | |||
76 | AutoIndent Indent(P, 4); | |||
77 | P.formatLine("Dumping this stream is not valid for object files"); | |||
78 | } | |||
79 | ||||
80 | void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) { | |||
81 | AutoIndent Indent(P, 4); | |||
82 | P.formatLine("{0} stream not present", StreamName); | |||
83 | } | |||
84 | ||||
85 | Error DumpOutputStyle::dump() { | |||
86 | // Walk symbols & globals if we are supposed to mark types referenced. | |||
87 | if (opts::dump::DumpTypeRefStats) | |||
88 | RefTracker->mark(); | |||
89 | ||||
90 | if (opts::dump::DumpSummary) { | |||
91 | if (auto EC = dumpFileSummary()) | |||
92 | return EC; | |||
93 | P.NewLine(); | |||
94 | } | |||
95 | ||||
96 | if (opts::dump::DumpStreams) { | |||
97 | if (auto EC = dumpStreamSummary()) | |||
98 | return EC; | |||
99 | P.NewLine(); | |||
100 | } | |||
101 | ||||
102 | if (opts::dump::DumpSymbolStats) { | |||
103 | ExitOnError Err("Unexpected error processing module stats: "); | |||
104 | Err(dumpSymbolStats()); | |||
105 | P.NewLine(); | |||
106 | } | |||
107 | ||||
108 | if (opts::dump::DumpUdtStats) { | |||
109 | if (auto EC = dumpUdtStats()) | |||
110 | return EC; | |||
111 | P.NewLine(); | |||
112 | } | |||
113 | ||||
114 | if (opts::dump::DumpTypeStats || opts::dump::DumpIDStats) { | |||
115 | if (auto EC = dumpTypeStats()) | |||
116 | return EC; | |||
117 | P.NewLine(); | |||
118 | } | |||
119 | ||||
120 | if (opts::dump::DumpNamedStreams) { | |||
121 | if (auto EC = dumpNamedStreams()) | |||
122 | return EC; | |||
123 | P.NewLine(); | |||
124 | } | |||
125 | ||||
126 | if (opts::dump::DumpStringTable || opts::dump::DumpStringTableDetails) { | |||
127 | if (auto EC = dumpStringTable()) | |||
128 | return EC; | |||
129 | P.NewLine(); | |||
130 | } | |||
131 | ||||
132 | if (opts::dump::DumpModules) { | |||
133 | ExitOnError Err("Unexpected error processing modules: "); | |||
134 | Err(dumpModules()); | |||
135 | } | |||
136 | ||||
137 | if (opts::dump::DumpModuleFiles) { | |||
138 | ExitOnError Err("Unexpected error processing files: "); | |||
139 | Err(dumpModuleFiles()); | |||
140 | } | |||
141 | ||||
142 | if (opts::dump::DumpLines) { | |||
143 | ExitOnError Err("Unexpected error processing lines: "); | |||
144 | Err(dumpLines()); | |||
145 | } | |||
146 | ||||
147 | if (opts::dump::DumpInlineeLines) { | |||
148 | ExitOnError Err("Unexpected error processing inlinee lines: "); | |||
149 | Err(dumpInlineeLines()); | |||
150 | } | |||
151 | ||||
152 | if (opts::dump::DumpXmi) { | |||
153 | ExitOnError Err("Unexpected error processing cross module imports: "); | |||
154 | Err(dumpXmi()); | |||
155 | } | |||
156 | ||||
157 | if (opts::dump::DumpXme) { | |||
158 | ExitOnError Err("Unexpected error processing cross module exports: "); | |||
159 | Err(dumpXme()); | |||
160 | } | |||
161 | ||||
162 | if (opts::dump::DumpFpo) { | |||
163 | if (auto EC = dumpFpo()) | |||
164 | return EC; | |||
165 | } | |||
166 | ||||
167 | if (File.isObj()) { | |||
168 | if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || | |||
169 | opts::dump::DumpTypeExtras) | |||
170 | if (auto EC = dumpTypesFromObjectFile()) | |||
171 | return EC; | |||
172 | } else { | |||
173 | if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || | |||
174 | opts::dump::DumpTypeExtras) { | |||
175 | if (auto EC = dumpTpiStream(StreamTPI)) | |||
176 | return EC; | |||
177 | } | |||
178 | ||||
179 | if (opts::dump::DumpIds || !opts::dump::DumpIdIndex.empty() || | |||
180 | opts::dump::DumpIdExtras) { | |||
181 | if (auto EC = dumpTpiStream(StreamIPI)) | |||
182 | return EC; | |||
183 | } | |||
184 | } | |||
185 | ||||
186 | if (opts::dump::DumpGSIRecords) { | |||
187 | if (auto EC = dumpGSIRecords()) | |||
188 | return EC; | |||
189 | } | |||
190 | ||||
191 | if (opts::dump::DumpGlobals) { | |||
192 | if (auto EC = dumpGlobals()) | |||
193 | return EC; | |||
194 | } | |||
195 | ||||
196 | if (opts::dump::DumpPublics) { | |||
197 | if (auto EC = dumpPublics()) | |||
198 | return EC; | |||
199 | } | |||
200 | ||||
201 | if (opts::dump::DumpSymbols) { | |||
202 | ExitOnError Err("Unexpected error processing symbols: "); | |||
203 | Err(File.isPdb() ? dumpModuleSymsForPdb() : dumpModuleSymsForObj()); | |||
204 | } | |||
205 | ||||
206 | if (opts::dump::DumpTypeRefStats) { | |||
207 | if (auto EC = dumpTypeRefStats()) | |||
208 | return EC; | |||
209 | } | |||
210 | ||||
211 | if (opts::dump::DumpSectionHeaders) { | |||
212 | if (auto EC = dumpSectionHeaders()) | |||
213 | return EC; | |||
214 | } | |||
215 | ||||
216 | if (opts::dump::DumpSectionContribs) { | |||
217 | if (auto EC = dumpSectionContribs()) | |||
218 | return EC; | |||
219 | } | |||
220 | ||||
221 | if (opts::dump::DumpSectionMap) { | |||
222 | if (auto EC = dumpSectionMap()) | |||
223 | return EC; | |||
224 | } | |||
225 | ||||
226 | P.NewLine(); | |||
227 | ||||
228 | return Error::success(); | |||
229 | } | |||
230 | ||||
231 | static void printHeader(LinePrinter &P, const Twine &S) { | |||
232 | P.NewLine(); | |||
233 | P.formatLine("{0,=60}", S); | |||
234 | P.formatLine("{0}", fmt_repeat('=', 60)); | |||
235 | } | |||
236 | ||||
237 | Error DumpOutputStyle::dumpFileSummary() { | |||
238 | printHeader(P, "Summary"); | |||
239 | ||||
240 | if (File.isObj()) { | |||
241 | printStreamNotValidForObj(); | |||
242 | return Error::success(); | |||
243 | } | |||
244 | ||||
245 | AutoIndent Indent(P); | |||
246 | ExitOnError Err("Invalid PDB Format: "); | |||
247 | ||||
248 | P.formatLine("Block Size: {0}", getPdb().getBlockSize()); | |||
249 | P.formatLine("Number of blocks: {0}", getPdb().getBlockCount()); | |||
250 | P.formatLine("Number of streams: {0}", getPdb().getNumStreams()); | |||
251 | ||||
252 | auto &PS = Err(getPdb().getPDBInfoStream()); | |||
253 | P.formatLine("Signature: {0}", PS.getSignature()); | |||
254 | P.formatLine("Age: {0}", PS.getAge()); | |||
255 | P.formatLine("GUID: {0}", fmt_guid(PS.getGuid().Guid)); | |||
256 | P.formatLine("Features: {0:x+}", static_cast<uint32_t>(PS.getFeatures())); | |||
257 | P.formatLine("Has Debug Info: {0}", getPdb().hasPDBDbiStream()); | |||
258 | P.formatLine("Has Types: {0}", getPdb().hasPDBTpiStream()); | |||
259 | P.formatLine("Has IDs: {0}", getPdb().hasPDBIpiStream()); | |||
260 | P.formatLine("Has Globals: {0}", getPdb().hasPDBGlobalsStream()); | |||
261 | P.formatLine("Has Publics: {0}", getPdb().hasPDBPublicsStream()); | |||
262 | if (getPdb().hasPDBDbiStream()) { | |||
263 | DbiStream &DBI = Err(getPdb().getPDBDbiStream()); | |||
264 | P.formatLine("Is incrementally linked: {0}", DBI.isIncrementallyLinked()); | |||
265 | P.formatLine("Has conflicting types: {0}", DBI.hasCTypes()); | |||
266 | P.formatLine("Is stripped: {0}", DBI.isStripped()); | |||
267 | } | |||
268 | ||||
269 | return Error::success(); | |||
270 | } | |||
271 | ||||
272 | static StatCollection getSymbolStats(const SymbolGroup &SG, | |||
273 | StatCollection &CumulativeStats) { | |||
274 | StatCollection Stats; | |||
275 | if (SG.getFile().isPdb() && SG.hasDebugStream()) { | |||
276 | // For PDB files, all symbols are packed into one stream. | |||
277 | for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) { | |||
278 | Stats.update(S.kind(), S.length()); | |||
279 | CumulativeStats.update(S.kind(), S.length()); | |||
280 | } | |||
281 | return Stats; | |||
282 | } | |||
283 | ||||
284 | for (const auto &SS : SG.getDebugSubsections()) { | |||
285 | // For object files, all symbols are spread across multiple Symbol | |||
286 | // subsections of a given .debug$S section. | |||
287 | if (SS.kind() != DebugSubsectionKind::Symbols) | |||
288 | continue; | |||
289 | DebugSymbolsSubsectionRef Symbols; | |||
290 | BinaryStreamReader Reader(SS.getRecordData()); | |||
291 | cantFail(Symbols.initialize(Reader)); | |||
292 | for (const auto &S : Symbols) { | |||
293 | Stats.update(S.kind(), S.length()); | |||
294 | CumulativeStats.update(S.kind(), S.length()); | |||
295 | } | |||
296 | } | |||
297 | return Stats; | |||
298 | } | |||
299 | ||||
300 | static StatCollection getChunkStats(const SymbolGroup &SG, | |||
301 | StatCollection &CumulativeStats) { | |||
302 | StatCollection Stats; | |||
303 | for (const auto &Chunk : SG.getDebugSubsections()) { | |||
304 | Stats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength()); | |||
305 | CumulativeStats.update(uint32_t(Chunk.kind()), Chunk.getRecordLength()); | |||
306 | } | |||
307 | return Stats; | |||
308 | } | |||
309 | ||||
310 | static inline std::string formatModuleDetailKind(DebugSubsectionKind K) { | |||
311 | return formatChunkKind(K, false); | |||
312 | } | |||
313 | ||||
314 | static inline std::string formatModuleDetailKind(SymbolKind K) { | |||
315 | return formatSymbolKind(K); | |||
316 | } | |||
317 | ||||
318 | // Get the stats sorted by size, descending. | |||
319 | std::vector<StatCollection::KindAndStat> | |||
320 | StatCollection::getStatsSortedBySize() const { | |||
321 | std::vector<KindAndStat> SortedStats(Individual.begin(), Individual.end()); | |||
322 | llvm::stable_sort(SortedStats, | |||
323 | [](const KindAndStat &LHS, const KindAndStat &RHS) { | |||
324 | return LHS.second.Size > RHS.second.Size; | |||
325 | }); | |||
326 | return SortedStats; | |||
327 | } | |||
328 | ||||
329 | template <typename Kind> | |||
330 | static void printModuleDetailStats(LinePrinter &P, StringRef Label, | |||
331 | const StatCollection &Stats) { | |||
332 | P.NewLine(); | |||
333 | P.formatLine(" {0}", Label); | |||
334 | AutoIndent Indent(P); | |||
335 | P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", "Total", | |||
336 | Stats.Totals.Count, Stats.Totals.Size); | |||
337 | P.formatLine("{0}", fmt_repeat('-', 74)); | |||
338 | ||||
339 | for (const auto &K : Stats.getStatsSortedBySize()) { | |||
340 | std::string KindName = formatModuleDetailKind(Kind(K.first)); | |||
341 | P.formatLine("{0,40}: {1,7} entries ({2,12:N} bytes)", KindName, | |||
342 | K.second.Count, K.second.Size); | |||
343 | } | |||
344 | } | |||
345 | ||||
346 | Error DumpOutputStyle::dumpStreamSummary() { | |||
347 | printHeader(P, "Streams"); | |||
348 | ||||
349 | if (File.isObj()) { | |||
350 | printStreamNotValidForObj(); | |||
351 | return Error::success(); | |||
352 | } | |||
353 | ||||
354 | AutoIndent Indent(P); | |||
355 | ||||
356 | if (StreamPurposes.empty()) | |||
357 | discoverStreamPurposes(getPdb(), StreamPurposes); | |||
358 | ||||
359 | uint32_t StreamCount = getPdb().getNumStreams(); | |||
360 | uint32_t MaxStreamSize = getPdb().getMaxStreamSize(); | |||
361 | ||||
362 | for (uint32_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) { | |||
363 | P.formatLine( | |||
364 | "Stream {0} ({1} bytes): [{2}]", | |||
365 | fmt_align(StreamIdx, AlignStyle::Right, NumDigits(StreamCount)), | |||
366 | fmt_align(getPdb().getStreamByteSize(StreamIdx), AlignStyle::Right, | |||
367 | NumDigits(MaxStreamSize)), | |||
368 | StreamPurposes[StreamIdx].getLongName()); | |||
369 | ||||
370 | if (opts::dump::DumpStreamBlocks) { | |||
371 | auto Blocks = getPdb().getStreamBlockList(StreamIdx); | |||
372 | std::vector<uint32_t> BV(Blocks.begin(), Blocks.end()); | |||
373 | P.formatLine(" {0} Blocks: [{1}]", | |||
374 | fmt_repeat(' ', NumDigits(StreamCount)), | |||
375 | make_range(BV.begin(), BV.end())); | |||
376 | } | |||
377 | } | |||
378 | ||||
379 | return Error::success(); | |||
380 | } | |||
381 | ||||
382 | static Expected<std::pair<std::unique_ptr<MappedBlockStream>, | |||
383 | ArrayRef<llvm::object::coff_section>>> | |||
384 | loadSectionHeaders(PDBFile &File, DbgHeaderType Type) { | |||
385 | if (!File.hasPDBDbiStream()) | |||
386 | return make_error<StringError>( | |||
387 | "Section headers require a DBI Stream, which could not be loaded", | |||
388 | inconvertibleErrorCode()); | |||
389 | ||||
390 | DbiStream &Dbi = cantFail(File.getPDBDbiStream()); | |||
391 | uint32_t SI = Dbi.getDebugStreamIndex(Type); | |||
392 | ||||
393 | if (SI == kInvalidStreamIndex) | |||
394 | return make_error<StringError>( | |||
395 | "PDB does not contain the requested image section header type", | |||
396 | inconvertibleErrorCode()); | |||
397 | ||||
398 | auto Stream = File.createIndexedStream(SI); | |||
399 | if (!Stream) | |||
400 | return make_error<StringError>("Could not load the required stream data", | |||
401 | inconvertibleErrorCode()); | |||
402 | ||||
403 | ArrayRef<object::coff_section> Headers; | |||
404 | if (Stream->getLength() % sizeof(object::coff_section) != 0) | |||
405 | return make_error<StringError>( | |||
406 | "Section header array size is not a multiple of section header size", | |||
407 | inconvertibleErrorCode()); | |||
408 | ||||
409 | uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section); | |||
410 | BinaryStreamReader Reader(*Stream); | |||
411 | cantFail(Reader.readArray(Headers, NumHeaders)); | |||
412 | return std::make_pair(std::move(Stream), Headers); | |||
413 | } | |||
414 | ||||
415 | static Expected<std::vector<std::string>> getSectionNames(PDBFile &File) { | |||
416 | auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr); | |||
417 | if (!ExpectedHeaders) | |||
418 | return ExpectedHeaders.takeError(); | |||
419 | ||||
420 | std::unique_ptr<MappedBlockStream> Stream; | |||
421 | ArrayRef<object::coff_section> Headers; | |||
422 | std::tie(Stream, Headers) = std::move(*ExpectedHeaders); | |||
423 | std::vector<std::string> Names; | |||
424 | for (const auto &H : Headers) | |||
425 | Names.push_back(H.Name); | |||
426 | return Names; | |||
427 | } | |||
428 | ||||
429 | static void dumpSectionContrib(LinePrinter &P, const SectionContrib &SC, | |||
430 | ArrayRef<std::string> SectionNames, | |||
431 | uint32_t FieldWidth) { | |||
432 | std::string NameInsert; | |||
433 | if (SC.ISect > 0 && SC.ISect <= SectionNames.size()) { | |||
434 | StringRef SectionName = SectionNames[SC.ISect - 1]; | |||
435 | NameInsert = formatv("[{0}]", SectionName).str(); | |||
436 | } else | |||
437 | NameInsert = "[???]"; | |||
438 | P.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " | |||
439 | "crc = {4}", | |||
440 | formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size), | |||
441 | fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc), | |||
442 | fmt_align(NameInsert, AlignStyle::Left, FieldWidth + 2)); | |||
443 | AutoIndent Indent(P, FieldWidth + 2); | |||
444 | P.formatLine(" {0}", | |||
445 | formatSectionCharacteristics(P.getIndentLevel() + 6, | |||
446 | SC.Characteristics, 3, " | ")); | |||
447 | } | |||
448 | ||||
449 | static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC, | |||
450 | ArrayRef<std::string> SectionNames, | |||
451 | uint32_t FieldWidth) { | |||
452 | P.formatLine("SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc " | |||
453 | "crc = {4}, coff section = {5}", | |||
454 | formatSegmentOffset(SC.Base.ISect, SC.Base.Off), | |||
455 | fmtle(SC.Base.Size), fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc), | |||
456 | fmtle(SC.Base.RelocCrc), fmtle(SC.ISectCoff)); | |||
457 | P.formatLine(" {0}", | |||
458 | formatSectionCharacteristics(P.getIndentLevel() + 6, | |||
459 | SC.Base.Characteristics, 3, " | ")); | |||
460 | } | |||
461 | ||||
462 | Error DumpOutputStyle::dumpModules() { | |||
463 | printHeader(P, "Modules"); | |||
464 | ||||
465 | if (File.isObj()) { | |||
466 | printStreamNotValidForObj(); | |||
467 | return Error::success(); | |||
468 | } | |||
469 | ||||
470 | if (!getPdb().hasPDBDbiStream()) { | |||
471 | printStreamNotPresent("DBI"); | |||
472 | return Error::success(); | |||
473 | } | |||
474 | ||||
475 | AutoIndent Indent(P); | |||
476 | ||||
477 | Expected<DbiStream &> StreamOrErr = getPdb().getPDBDbiStream(); | |||
478 | if (!StreamOrErr) | |||
479 | return StreamOrErr.takeError(); | |||
480 | DbiStream &Stream = *StreamOrErr; | |||
481 | ||||
482 | const DbiModuleList &Modules = Stream.modules(); | |||
483 | return iterateSymbolGroups( | |||
484 | File, PrintScope{P, 11}, | |||
485 | [&](uint32_t Modi, const SymbolGroup &Strings) -> Error { | |||
486 | auto Desc = Modules.getModuleDescriptor(Modi); | |||
487 | if (opts::dump::DumpSectionContribs) { | |||
488 | auto SectionsOrErr = getSectionNames(getPdb()); | |||
489 | if (!SectionsOrErr) | |||
490 | return SectionsOrErr.takeError(); | |||
491 | ArrayRef<std::string> Sections = *SectionsOrErr; | |||
492 | dumpSectionContrib(P, Desc.getSectionContrib(), Sections, 0); | |||
493 | } | |||
494 | P.formatLine("Obj: `{0}`: ", Desc.getObjFileName()); | |||
495 | P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}", | |||
496 | Desc.getModuleStreamIndex(), Desc.getNumberOfFiles(), | |||
497 | Desc.hasECInfo()); | |||
498 | ||||
499 | auto PdbPathOrErr = Stream.getECName(Desc.getPdbFilePathNameIndex()); | |||
500 | if (!PdbPathOrErr) | |||
501 | return PdbPathOrErr.takeError(); | |||
502 | StringRef PdbFilePath = *PdbPathOrErr; | |||
503 | ||||
504 | auto SrcPathOrErr = Stream.getECName(Desc.getSourceFileNameIndex()); | |||
505 | if (!SrcPathOrErr) | |||
506 | return SrcPathOrErr.takeError(); | |||
507 | StringRef SrcFilePath = *SrcPathOrErr; | |||
508 | ||||
509 | P.formatLine("pdb file ni: {0} `{1}`, src file ni: {2} `{3}`", | |||
510 | Desc.getPdbFilePathNameIndex(), PdbFilePath, | |||
511 | Desc.getSourceFileNameIndex(), SrcFilePath); | |||
512 | return Error::success(); | |||
513 | }); | |||
514 | } | |||
515 | ||||
516 | Error DumpOutputStyle::dumpModuleFiles() { | |||
517 | printHeader(P, "Files"); | |||
518 | ||||
519 | if (File.isObj()) { | |||
520 | printStreamNotValidForObj(); | |||
521 | return Error::success(); | |||
522 | } | |||
523 | ||||
524 | if (!getPdb().hasPDBDbiStream()) { | |||
525 | printStreamNotPresent("DBI"); | |||
526 | return Error::success(); | |||
527 | } | |||
528 | ||||
529 | return iterateSymbolGroups( | |||
530 | File, PrintScope{P, 11}, | |||
531 | [this](uint32_t Modi, const SymbolGroup &Strings) -> Error { | |||
532 | Expected<DbiStream &> StreamOrErr = getPdb().getPDBDbiStream(); | |||
533 | if (!StreamOrErr) | |||
534 | return StreamOrErr.takeError(); | |||
535 | DbiStream &Stream = *StreamOrErr; | |||
536 | ||||
537 | const DbiModuleList &Modules = Stream.modules(); | |||
538 | for (const auto &F : Modules.source_files(Modi)) { | |||
539 | Strings.formatFromFileName(P, F); | |||
540 | } | |||
541 | return Error::success(); | |||
542 | }); | |||
543 | } | |||
544 | ||||
545 | Error DumpOutputStyle::dumpSymbolStats() { | |||
546 | printHeader(P, "Module Stats"); | |||
547 | ||||
548 | if (File.isPdb() && !getPdb().hasPDBDbiStream()) { | |||
549 | printStreamNotPresent("DBI"); | |||
550 | return Error::success(); | |||
551 | } | |||
552 | ||||
553 | StatCollection SymStats; | |||
554 | StatCollection ChunkStats; | |||
555 | PrintScope Scope(P, 2); | |||
556 | ||||
557 | if (Error Err = iterateSymbolGroups( | |||
558 | File, Scope, [&](uint32_t Modi, const SymbolGroup &SG) -> Error { | |||
559 | StatCollection SS = getSymbolStats(SG, SymStats); | |||
560 | StatCollection CS = getChunkStats(SG, ChunkStats); | |||
561 | ||||
562 | if (!SG.getFile().isPdb()) | |||
563 | return Error::success(); | |||
564 | ||||
565 | AutoIndent Indent(P); | |||
566 | auto Modules = cantFail(File.pdb().getPDBDbiStream()).modules(); | |||
567 | uint32_t ModCount = Modules.getModuleCount(); | |||
568 | DbiModuleDescriptor Desc = Modules.getModuleDescriptor(Modi); | |||
569 | uint32_t StreamIdx = Desc.getModuleStreamIndex(); | |||
570 | ||||
571 | if (StreamIdx == kInvalidStreamIndex) { | |||
572 | P.formatLine( | |||
573 | "Mod {0} (debug info not present): [{1}]", | |||
574 | fmt_align(Modi, AlignStyle::Right, NumDigits(ModCount)), | |||
575 | Desc.getModuleName()); | |||
576 | return Error::success(); | |||
577 | } | |||
578 | P.formatLine("Stream {0}, {1} bytes", StreamIdx, | |||
579 | getPdb().getStreamByteSize(StreamIdx)); | |||
580 | ||||
581 | printModuleDetailStats<SymbolKind>(P, "Symbols", SS); | |||
582 | printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", CS); | |||
583 | ||||
584 | return Error::success(); | |||
585 | })) | |||
586 | return Err; | |||
587 | ||||
588 | if (SymStats.Totals.Count > 0) { | |||
589 | P.printLine(" Summary |"); | |||
590 | AutoIndent Indent(P, 4); | |||
591 | printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats); | |||
592 | printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats); | |||
593 | } | |||
594 | ||||
595 | return Error::success(); | |||
596 | } | |||
597 | ||||
598 | Error DumpOutputStyle::dumpTypeStats() { | |||
599 | printHeader(P, "Type Record Stats"); | |||
600 | ||||
601 | // Iterate the types, categorize by kind, accumulate size stats. | |||
602 | StatCollection TypeStats; | |||
603 | LazyRandomTypeCollection &Types = | |||
604 | opts::dump::DumpTypeStats ? File.types() : File.ids(); | |||
605 | for (std::optional<TypeIndex> TI = Types.getFirst(); TI; | |||
606 | TI = Types.getNext(*TI)) { | |||
607 | CVType Type = Types.getType(*TI); | |||
608 | TypeStats.update(uint32_t(Type.kind()), Type.length()); | |||
609 | } | |||
610 | ||||
611 | P.NewLine(); | |||
612 | P.formatLine(" Types"); | |||
613 | AutoIndent Indent(P); | |||
614 | P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", "Total", | |||
615 | TypeStats.Totals.Count, TypeStats.Totals.Size, | |||
616 | (double)TypeStats.Totals.Size / TypeStats.Totals.Count); | |||
617 | P.formatLine("{0}", fmt_repeat('-', 74)); | |||
618 | ||||
619 | for (const auto &K : TypeStats.getStatsSortedBySize()) { | |||
620 | P.formatLine("{0,16}: {1,7} entries ({2,12:N} bytes, {3,7} avg)", | |||
621 | formatTypeLeafKind(TypeLeafKind(K.first)), K.second.Count, | |||
622 | K.second.Size, (double)K.second.Size / K.second.Count); | |||
623 | } | |||
624 | return Error::success(); | |||
625 | } | |||
626 | ||||
627 | static bool isValidNamespaceIdentifier(StringRef S) { | |||
628 | if (S.empty()) | |||
629 | return false; | |||
630 | ||||
631 | if (std::isdigit(S[0])) | |||
632 | return false; | |||
633 | ||||
634 | return llvm::all_of(S, [](char C) { return std::isalnum(C); }); | |||
635 | } | |||
636 | ||||
637 | namespace { | |||
638 | constexpr uint32_t kNoneUdtKind = 0; | |||
639 | constexpr uint32_t kSimpleUdtKind = 1; | |||
640 | constexpr uint32_t kUnknownUdtKind = 2; | |||
641 | } // namespace | |||
642 | ||||
643 | static std::string getUdtStatLabel(uint32_t Kind) { | |||
644 | if (Kind == kNoneUdtKind) | |||
645 | return "<none type>"; | |||
646 | ||||
647 | if (Kind == kSimpleUdtKind) | |||
648 | return "<simple type>"; | |||
649 | ||||
650 | if (Kind == kUnknownUdtKind) | |||
651 | return "<unknown type>"; | |||
652 | ||||
653 | return formatTypeLeafKind(static_cast<TypeLeafKind>(Kind)); | |||
654 | } | |||
655 | ||||
656 | static uint32_t getLongestTypeLeafName(const StatCollection &Stats) { | |||
657 | size_t L = 0; | |||
658 | for (const auto &Stat : Stats.Individual) { | |||
659 | std::string Label = getUdtStatLabel(Stat.first); | |||
660 | L = std::max(L, Label.size()); | |||
661 | } | |||
662 | return static_cast<uint32_t>(L); | |||
663 | } | |||
664 | ||||
665 | Error DumpOutputStyle::dumpUdtStats() { | |||
666 | printHeader(P, "S_UDT Record Stats"); | |||
667 | ||||
668 | if (File.isPdb() && !getPdb().hasPDBGlobalsStream()) { | |||
669 | printStreamNotPresent("Globals"); | |||
670 | return Error::success(); | |||
671 | } | |||
672 | ||||
673 | StatCollection UdtStats; | |||
674 | StatCollection UdtTargetStats; | |||
675 | AutoIndent Indent(P, 4); | |||
676 | ||||
677 | auto &TpiTypes = File.types(); | |||
678 | ||||
679 | StringMap<StatCollection::Stat> NamespacedStats; | |||
680 | ||||
681 | size_t LongestNamespace = 0; | |||
682 | auto HandleOneSymbol = [&](const CVSymbol &Sym) { | |||
683 | if (Sym.kind() != SymbolKind::S_UDT) | |||
684 | return; | |||
685 | UdtStats.update(SymbolKind::S_UDT, Sym.length()); | |||
686 | ||||
687 | UDTSym UDT = cantFail(SymbolDeserializer::deserializeAs<UDTSym>(Sym)); | |||
688 | ||||
689 | uint32_t Kind = 0; | |||
690 | uint32_t RecordSize = 0; | |||
691 | ||||
692 | if (UDT.Type.isNoneType()) | |||
693 | Kind = kNoneUdtKind; | |||
694 | else if (UDT.Type.isSimple()) | |||
695 | Kind = kSimpleUdtKind; | |||
696 | else if (std::optional<CVType> T = TpiTypes.tryGetType(UDT.Type)) { | |||
697 | Kind = T->kind(); | |||
698 | RecordSize = T->length(); | |||
699 | } else | |||
700 | Kind = kUnknownUdtKind; | |||
701 | ||||
702 | UdtTargetStats.update(Kind, RecordSize); | |||
703 | ||||
704 | size_t Pos = UDT.Name.find("::"); | |||
705 | if (Pos == StringRef::npos) | |||
706 | return; | |||
707 | ||||
708 | StringRef Scope = UDT.Name.take_front(Pos); | |||
709 | if (Scope.empty() || !isValidNamespaceIdentifier(Scope)) | |||
710 | return; | |||
711 | ||||
712 | LongestNamespace = std::max(LongestNamespace, Scope.size()); | |||
713 | NamespacedStats[Scope].update(RecordSize); | |||
714 | }; | |||
715 | ||||
716 | P.NewLine(); | |||
717 | ||||
718 | if (File.isPdb()) { | |||
719 | auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream()); | |||
720 | auto ExpGlobals = getPdb().getPDBGlobalsStream(); | |||
721 | if (!ExpGlobals) | |||
722 | return ExpGlobals.takeError(); | |||
723 | ||||
724 | for (uint32_t PubSymOff : ExpGlobals->getGlobalsTable()) { | |||
725 | CVSymbol Sym = SymbolRecords.readRecord(PubSymOff); | |||
726 | HandleOneSymbol(Sym); | |||
727 | } | |||
728 | } else { | |||
729 | for (const auto &Sec : File.symbol_groups()) { | |||
730 | for (const auto &SS : Sec.getDebugSubsections()) { | |||
731 | if (SS.kind() != DebugSubsectionKind::Symbols) | |||
732 | continue; | |||
733 | ||||
734 | DebugSymbolsSubsectionRef Symbols; | |||
735 | BinaryStreamReader Reader(SS.getRecordData()); | |||
736 | cantFail(Symbols.initialize(Reader)); | |||
737 | for (const auto &S : Symbols) | |||
738 | HandleOneSymbol(S); | |||
739 | } | |||
740 | } | |||
741 | } | |||
742 | ||||
743 | LongestNamespace += StringRef(" namespace ''").size(); | |||
744 | size_t LongestTypeLeafKind = getLongestTypeLeafName(UdtTargetStats); | |||
745 | size_t FieldWidth = std::max(LongestNamespace, LongestTypeLeafKind); | |||
746 | ||||
747 | // Compute the max number of digits for count and size fields, including comma | |||
748 | // separators. | |||
749 | StringRef CountHeader("Count"); | |||
750 | StringRef SizeHeader("Size"); | |||
751 | size_t CD = NumDigits(UdtStats.Totals.Count); | |||
752 | CD += (CD - 1) / 3; | |||
753 | CD = std::max(CD, CountHeader.size()); | |||
754 | ||||
755 | size_t SD = NumDigits(UdtStats.Totals.Size); | |||
756 | SD += (SD - 1) / 3; | |||
757 | SD = std::max(SD, SizeHeader.size()); | |||
758 | ||||
759 | uint32_t TableWidth = FieldWidth + 3 + CD + 2 + SD + 1; | |||
760 | ||||
761 | P.formatLine("{0} | {1} {2}", | |||
762 | fmt_align("Record Kind", AlignStyle::Right, FieldWidth), | |||
763 | fmt_align(CountHeader, AlignStyle::Right, CD), | |||
764 | fmt_align(SizeHeader, AlignStyle::Right, SD)); | |||
765 | ||||
766 | P.formatLine("{0}", fmt_repeat('-', TableWidth)); | |||
767 | for (const auto &Stat : UdtTargetStats.getStatsSortedBySize()) { | |||
768 | std::string Label = getUdtStatLabel(Stat.first); | |||
769 | P.formatLine("{0} | {1:N} {2:N}", | |||
770 | fmt_align(Label, AlignStyle::Right, FieldWidth), | |||
771 | fmt_align(Stat.second.Count, AlignStyle::Right, CD), | |||
772 | fmt_align(Stat.second.Size, AlignStyle::Right, SD)); | |||
773 | } | |||
774 | P.formatLine("{0}", fmt_repeat('-', TableWidth)); | |||
775 | P.formatLine("{0} | {1:N} {2:N}", | |||
776 | fmt_align("Total (S_UDT)", AlignStyle::Right, FieldWidth), | |||
777 | fmt_align(UdtStats.Totals.Count, AlignStyle::Right, CD), | |||
778 | fmt_align(UdtStats.Totals.Size, AlignStyle::Right, SD)); | |||
779 | P.formatLine("{0}", fmt_repeat('-', TableWidth)); | |||
780 | struct StrAndStat { | |||
781 | StringRef Key; | |||
782 | StatCollection::Stat Stat; | |||
783 | }; | |||
784 | ||||
785 | // Print namespace stats in descending order of size. | |||
786 | std::vector<StrAndStat> NamespacedStatsSorted; | |||
787 | for (const auto &Stat : NamespacedStats) | |||
788 | NamespacedStatsSorted.push_back({Stat.getKey(), Stat.second}); | |||
789 | llvm::stable_sort(NamespacedStatsSorted, | |||
790 | [](const StrAndStat &L, const StrAndStat &R) { | |||
791 | return L.Stat.Size > R.Stat.Size; | |||
792 | }); | |||
793 | for (const auto &Stat : NamespacedStatsSorted) { | |||
794 | std::string Label = std::string(formatv("namespace '{0}'", Stat.Key)); | |||
795 | P.formatLine("{0} | {1:N} {2:N}", | |||
796 | fmt_align(Label, AlignStyle::Right, FieldWidth), | |||
797 | fmt_align(Stat.Stat.Count, AlignStyle::Right, CD), | |||
798 | fmt_align(Stat.Stat.Size, AlignStyle::Right, SD)); | |||
799 | } | |||
800 | return Error::success(); | |||
801 | } | |||
802 | ||||
803 | static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start, | |||
804 | const LineColumnEntry &E) { | |||
805 | const uint32_t kMaxCharsPerLineNumber = 4; // 4 digit line number | |||
806 | uint32_t MinColumnWidth = kMaxCharsPerLineNumber + 5; | |||
807 | ||||
808 | // Let's try to keep it under 100 characters | |||
809 | constexpr uint32_t kMaxRowLength = 100; | |||
810 | // At least 3 spaces between columns. | |||
811 | uint32_t ColumnsPerRow = kMaxRowLength / (MinColumnWidth + 3); | |||
812 | uint32_t ItemsLeft = E.LineNumbers.size(); | |||
813 | auto LineIter = E.LineNumbers.begin(); | |||
814 | while (ItemsLeft != 0) { | |||
815 | uint32_t RowColumns = std::min(ItemsLeft, ColumnsPerRow); | |||
816 | for (uint32_t I = 0; I < RowColumns; ++I) { | |||
817 | LineInfo Line(LineIter->Flags); | |||
818 | std::string LineStr; | |||
819 | if (Line.isAlwaysStepInto()) | |||
820 | LineStr = "ASI"; | |||
821 | else if (Line.isNeverStepInto()) | |||
822 | LineStr = "NSI"; | |||
823 | else | |||
824 | LineStr = utostr(Line.getStartLine()); | |||
825 | char Statement = Line.isStatement() ? ' ' : '!'; | |||
826 | P.format("{0} {1:X-} {2} ", | |||
827 | fmt_align(LineStr, AlignStyle::Right, kMaxCharsPerLineNumber), | |||
828 | fmt_align(Start + LineIter->Offset, AlignStyle::Right, 8, '0'), | |||
829 | Statement); | |||
830 | ++LineIter; | |||
831 | --ItemsLeft; | |||
832 | } | |||
833 | P.NewLine(); | |||
834 | } | |||
835 | } | |||
836 | ||||
837 | Error DumpOutputStyle::dumpLines() { | |||
838 | printHeader(P, "Lines"); | |||
839 | ||||
840 | if (File.isPdb() && !getPdb().hasPDBDbiStream()) { | |||
841 | printStreamNotPresent("DBI"); | |||
842 | return Error::success(); | |||
843 | } | |||
844 | ||||
845 | uint32_t LastModi = UINT32_MAX(4294967295U); | |||
846 | uint32_t LastNameIndex = UINT32_MAX(4294967295U); | |||
847 | return iterateModuleSubsections<DebugLinesSubsectionRef>( | |||
848 | File, PrintScope{P, 4}, | |||
849 | [this, &LastModi, | |||
850 | &LastNameIndex](uint32_t Modi, const SymbolGroup &Strings, | |||
851 | DebugLinesSubsectionRef &Lines) -> Error { | |||
852 | uint16_t Segment = Lines.header()->RelocSegment; | |||
853 | uint32_t Begin = Lines.header()->RelocOffset; | |||
854 | uint32_t End = Begin + Lines.header()->CodeSize; | |||
855 | for (const auto &Block : Lines) { | |||
856 | if (LastModi != Modi || LastNameIndex != Block.NameIndex) { | |||
857 | LastModi = Modi; | |||
858 | LastNameIndex = Block.NameIndex; | |||
859 | Strings.formatFromChecksumsOffset(P, Block.NameIndex); | |||
860 | } | |||
861 | ||||
862 | AutoIndent Indent(P, 2); | |||
863 | P.formatLine("{0:X-4}:{1:X-8}-{2:X-8}, ", Segment, Begin, End); | |||
864 | uint32_t Count = Block.LineNumbers.size(); | |||
865 | if (Lines.hasColumnInfo()) | |||
866 | P.format("line/column/addr entries = {0}", Count); | |||
867 | else | |||
868 | P.format("line/addr entries = {0}", Count); | |||
869 | ||||
870 | P.NewLine(); | |||
871 | typesetLinesAndColumns(P, Begin, Block); | |||
872 | } | |||
873 | return Error::success(); | |||
874 | }); | |||
875 | } | |||
876 | ||||
877 | Error DumpOutputStyle::dumpInlineeLines() { | |||
878 | printHeader(P, "Inlinee Lines"); | |||
879 | ||||
880 | if (File.isPdb() && !getPdb().hasPDBDbiStream()) { | |||
881 | printStreamNotPresent("DBI"); | |||
882 | return Error::success(); | |||
883 | } | |||
884 | ||||
885 | return iterateModuleSubsections<DebugInlineeLinesSubsectionRef>( | |||
886 | File, PrintScope{P, 2}, | |||
887 | [this](uint32_t Modi, const SymbolGroup &Strings, | |||
888 | DebugInlineeLinesSubsectionRef &Lines) -> Error { | |||
889 | P.formatLine("{0,+8} | {1,+5} | {2}", "Inlinee", "Line", "Source File"); | |||
890 | for (const auto &Entry : Lines) { | |||
891 | P.formatLine("{0,+8} | {1,+5} | ", Entry.Header->Inlinee, | |||
892 | fmtle(Entry.Header->SourceLineNum)); | |||
893 | Strings.formatFromChecksumsOffset(P, Entry.Header->FileID, true); | |||
894 | for (const auto &ExtraFileID : Entry.ExtraFiles) { | |||
895 | P.formatLine(" "); | |||
896 | Strings.formatFromChecksumsOffset(P, ExtraFileID, true); | |||
897 | } | |||
898 | } | |||
899 | P.NewLine(); | |||
900 | return Error::success(); | |||
901 | }); | |||
902 | } | |||
903 | ||||
904 | Error DumpOutputStyle::dumpXmi() { | |||
905 | printHeader(P, "Cross Module Imports"); | |||
906 | ||||
907 | if (File.isPdb() && !getPdb().hasPDBDbiStream()) { | |||
908 | printStreamNotPresent("DBI"); | |||
909 | return Error::success(); | |||
910 | } | |||
911 | ||||
912 | return iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>( | |||
913 | File, PrintScope{P, 2}, | |||
914 | [this](uint32_t Modi, const SymbolGroup &Strings, | |||
915 | DebugCrossModuleImportsSubsectionRef &Imports) -> Error { | |||
916 | P.formatLine("{0,=32} | {1}", "Imported Module", "Type IDs"); | |||
917 | ||||
918 | for (const auto &Xmi : Imports) { | |||
919 | auto ExpectedModule = | |||
920 | Strings.getNameFromStringTable(Xmi.Header->ModuleNameOffset); | |||
921 | StringRef Module; | |||
922 | SmallString<32> ModuleStorage; | |||
923 | if (!ExpectedModule) { | |||
924 | Module = "(unknown module)"; | |||
925 | consumeError(ExpectedModule.takeError()); | |||
926 | } else | |||
927 | Module = *ExpectedModule; | |||
928 | if (Module.size() > 32) { | |||
929 | ModuleStorage = "..."; | |||
930 | ModuleStorage += Module.take_back(32 - 3); | |||
931 | Module = ModuleStorage; | |||
932 | } | |||
933 | std::vector<std::string> TIs; | |||
934 | for (const auto I : Xmi.Imports) | |||
935 | TIs.push_back(std::string(formatv("{0,+10:X+}", fmtle(I)))); | |||
936 | std::string Result = | |||
937 | typesetItemList(TIs, P.getIndentLevel() + 35, 12, " "); | |||
938 | P.formatLine("{0,+32} | {1}", Module, Result); | |||
939 | } | |||
940 | return Error::success(); | |||
941 | }); | |||
942 | } | |||
943 | ||||
944 | Error DumpOutputStyle::dumpXme() { | |||
945 | printHeader(P, "Cross Module Exports"); | |||
946 | ||||
947 | if (File.isPdb() && !getPdb().hasPDBDbiStream()) { | |||
948 | printStreamNotPresent("DBI"); | |||
949 | return Error::success(); | |||
950 | } | |||
951 | ||||
952 | return iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>( | |||
953 | File, PrintScope{P, 2}, | |||
954 | [this](uint32_t Modi, const SymbolGroup &Strings, | |||
955 | DebugCrossModuleExportsSubsectionRef &Exports) -> Error { | |||
956 | P.formatLine("{0,-10} | {1}", "Local ID", "Global ID"); | |||
957 | for (const auto &Export : Exports) { | |||
958 | P.formatLine("{0,+10:X+} | {1}", TypeIndex(Export.Local), | |||
959 | TypeIndex(Export.Global)); | |||
960 | } | |||
961 | return Error::success(); | |||
962 | }); | |||
963 | } | |||
964 | ||||
965 | std::string formatFrameType(object::frame_type FT) { | |||
966 | switch (FT) { | |||
967 | case object::frame_type::Fpo: | |||
968 | return "FPO"; | |||
969 | case object::frame_type::NonFpo: | |||
970 | return "Non-FPO"; | |||
971 | case object::frame_type::Trap: | |||
972 | return "Trap"; | |||
973 | case object::frame_type::Tss: | |||
974 | return "TSS"; | |||
975 | } | |||
976 | return "<unknown>"; | |||
977 | } | |||
978 | ||||
979 | Error DumpOutputStyle::dumpOldFpo(PDBFile &File) { | |||
980 | printHeader(P, "Old FPO Data"); | |||
981 | ||||
982 | ExitOnError Err("Error dumping old fpo data:"); | |||
983 | DbiStream &Dbi = Err(File.getPDBDbiStream()); | |||
984 | ||||
985 | if (!Dbi.hasOldFpoRecords()) { | |||
986 | printStreamNotPresent("FPO"); | |||
987 | return Error::success(); | |||
988 | } | |||
989 | ||||
990 | const FixedStreamArray<object::FpoData>& Records = Dbi.getOldFpoRecords(); | |||
991 | ||||
992 | P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use " | |||
993 | "BP | Has SEH | Frame Type"); | |||
994 | ||||
995 | for (const object::FpoData &FD : Records) { | |||
996 | P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,6} | {5,10} | {6,6} | " | |||
997 | "{7,7} | {8,9}", | |||
998 | uint32_t(FD.Offset), uint32_t(FD.Size), uint32_t(FD.NumLocals), | |||
999 | uint32_t(FD.NumParams), FD.getPrologSize(), | |||
1000 | FD.getNumSavedRegs(), FD.useBP(), FD.hasSEH(), | |||
1001 | formatFrameType(FD.getFP())); | |||
1002 | } | |||
1003 | return Error::success(); | |||
1004 | } | |||
1005 | ||||
1006 | Error DumpOutputStyle::dumpNewFpo(PDBFile &File) { | |||
1007 | printHeader(P, "New FPO Data"); | |||
1008 | ||||
1009 | ExitOnError Err("Error dumping new fpo data:"); | |||
1010 | DbiStream &Dbi = Err(File.getPDBDbiStream()); | |||
1011 | ||||
1012 | if (!Dbi.hasNewFpoRecords()) { | |||
1013 | printStreamNotPresent("New FPO"); | |||
1014 | return Error::success(); | |||
1015 | } | |||
1016 | ||||
1017 | const DebugFrameDataSubsectionRef& FDS = Dbi.getNewFpoRecords(); | |||
1018 | ||||
1019 | P.printLine(" RVA | Code | Locals | Params | Stack | Prolog | Saved Regs " | |||
1020 | "| Has SEH | Has C++EH | Start | Program"); | |||
1021 | for (const FrameData &FD : FDS) { | |||
1022 | bool IsFuncStart = FD.Flags & FrameData::IsFunctionStart; | |||
1023 | bool HasEH = FD.Flags & FrameData::HasEH; | |||
1024 | bool HasSEH = FD.Flags & FrameData::HasSEH; | |||
1025 | ||||
1026 | auto &StringTable = Err(File.getStringTable()); | |||
1027 | ||||
1028 | auto Program = Err(StringTable.getStringForID(FD.FrameFunc)); | |||
1029 | P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,5} | {5,6} | {6,10} | " | |||
1030 | "{7,7} | {8,9} | {9,5} | {10}", | |||
1031 | uint32_t(FD.RvaStart), uint32_t(FD.CodeSize), | |||
1032 | uint32_t(FD.LocalSize), uint32_t(FD.ParamsSize), | |||
1033 | uint32_t(FD.MaxStackSize), uint16_t(FD.PrologSize), | |||
1034 | uint16_t(FD.SavedRegsSize), HasSEH, HasEH, IsFuncStart, | |||
1035 | Program); | |||
1036 | } | |||
1037 | return Error::success(); | |||
1038 | } | |||
1039 | ||||
1040 | Error DumpOutputStyle::dumpFpo() { | |||
1041 | if (!File.isPdb()) { | |||
1042 | printStreamNotValidForObj(); | |||
1043 | return Error::success(); | |||
1044 | } | |||
1045 | ||||
1046 | PDBFile &File = getPdb(); | |||
1047 | if (!File.hasPDBDbiStream()) { | |||
1048 | printStreamNotPresent("DBI"); | |||
1049 | return Error::success(); | |||
1050 | } | |||
1051 | ||||
1052 | if (auto EC = dumpOldFpo(File)) | |||
1053 | return EC; | |||
1054 | if (auto EC = dumpNewFpo(File)) | |||
1055 | return EC; | |||
1056 | return Error::success(); | |||
1057 | } | |||
1058 | ||||
1059 | Error DumpOutputStyle::dumpStringTableFromPdb() { | |||
1060 | AutoIndent Indent(P); | |||
1061 | auto IS = getPdb().getStringTable(); | |||
1062 | if (!IS) { | |||
1063 | P.formatLine("Not present in file"); | |||
1064 | consumeError(IS.takeError()); | |||
1065 | return Error::success(); | |||
1066 | } | |||
1067 | ||||
1068 | if (opts::dump::DumpStringTable) { | |||
1069 | if (IS->name_ids().empty()) | |||
1070 | P.formatLine("Empty"); | |||
1071 | else { | |||
1072 | auto MaxID = | |||
1073 | std::max_element(IS->name_ids().begin(), IS->name_ids().end()); | |||
1074 | uint32_t Digits = NumDigits(*MaxID); | |||
1075 | ||||
1076 | P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits), | |||
1077 | "String"); | |||
1078 | ||||
1079 | std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), | |||
1080 | IS->name_ids().end()); | |||
1081 | llvm::sort(SortedIDs); | |||
1082 | for (uint32_t I : SortedIDs) { | |||
1083 | auto ES = IS->getStringForID(I); | |||
1084 | llvm::SmallString<32> Str; | |||
1085 | if (!ES) { | |||
1086 | consumeError(ES.takeError()); | |||
1087 | Str = "Error reading string"; | |||
1088 | } else if (!ES->empty()) { | |||
1089 | Str.append("'"); | |||
1090 | Str.append(*ES); | |||
1091 | Str.append("'"); | |||
1092 | } | |||
1093 | ||||
1094 | if (!Str.empty()) | |||
1095 | P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), | |||
1096 | Str); | |||
1097 | } | |||
1098 | } | |||
1099 | } | |||
1100 | ||||
1101 | if (opts::dump::DumpStringTableDetails) { | |||
1102 | P.NewLine(); | |||
1103 | { | |||
1104 | P.printLine("String Table Header:"); | |||
1105 | AutoIndent Indent(P); | |||
1106 | P.formatLine("Signature: {0}", IS->getSignature()); | |||
1107 | P.formatLine("Hash Version: {0}", IS->getHashVersion()); | |||
1108 | P.formatLine("Name Buffer Size: {0}", IS->getByteSize()); | |||
1109 | P.NewLine(); | |||
1110 | } | |||
1111 | ||||
1112 | BinaryStreamRef NameBuffer = IS->getStringTable().getBuffer(); | |||
1113 | ArrayRef<uint8_t> Contents; | |||
1114 | cantFail(NameBuffer.readBytes(0, NameBuffer.getLength(), Contents)); | |||
1115 | P.formatBinary("Name Buffer", Contents, 0); | |||
1116 | P.NewLine(); | |||
1117 | { | |||
1118 | P.printLine("Hash Table:"); | |||
1119 | AutoIndent Indent(P); | |||
1120 | P.formatLine("Bucket Count: {0}", IS->name_ids().size()); | |||
1121 | for (const auto &Entry : enumerate(IS->name_ids())) | |||
1122 | P.formatLine("Bucket[{0}] : {1}", Entry.index(), | |||
1123 | uint32_t(Entry.value())); | |||
1124 | P.formatLine("Name Count: {0}", IS->getNameCount()); | |||
1125 | } | |||
1126 | } | |||
1127 | return Error::success(); | |||
1128 | } | |||
1129 | ||||
1130 | Error DumpOutputStyle::dumpStringTableFromObj() { | |||
1131 | return iterateModuleSubsections<DebugStringTableSubsectionRef>( | |||
1132 | File, PrintScope{P, 4}, | |||
1133 | [&](uint32_t Modi, const SymbolGroup &Strings, | |||
1134 | DebugStringTableSubsectionRef &Strings2) -> Error { | |||
1135 | BinaryStreamRef StringTableBuffer = Strings2.getBuffer(); | |||
1136 | BinaryStreamReader Reader(StringTableBuffer); | |||
1137 | while (Reader.bytesRemaining() > 0) { | |||
1138 | StringRef Str; | |||
1139 | uint32_t Offset = Reader.getOffset(); | |||
1140 | cantFail(Reader.readCString(Str)); | |||
1141 | if (Str.empty()) | |||
1142 | continue; | |||
1143 | ||||
1144 | P.formatLine("{0} | {1}", fmt_align(Offset, AlignStyle::Right, 4), | |||
1145 | Str); | |||
1146 | } | |||
1147 | return Error::success(); | |||
1148 | }); | |||
1149 | } | |||
1150 | ||||
1151 | Error DumpOutputStyle::dumpNamedStreams() { | |||
1152 | printHeader(P, "Named Streams"); | |||
1153 | ||||
1154 | if (File.isObj()) { | |||
1155 | printStreamNotValidForObj(); | |||
1156 | return Error::success(); | |||
1157 | } | |||
1158 | ||||
1159 | AutoIndent Indent(P); | |||
1160 | ExitOnError Err("Invalid PDB File: "); | |||
1161 | ||||
1162 | auto &IS = Err(File.pdb().getPDBInfoStream()); | |||
1163 | const NamedStreamMap &NS = IS.getNamedStreams(); | |||
1164 | for (const auto &Entry : NS.entries()) { | |||
1165 | P.printLine(Entry.getKey()); | |||
1166 | AutoIndent Indent2(P, 2); | |||
1167 | P.formatLine("Index: {0}", Entry.getValue()); | |||
1168 | P.formatLine("Size in bytes: {0}", | |||
1169 | File.pdb().getStreamByteSize(Entry.getValue())); | |||
1170 | } | |||
1171 | ||||
1172 | return Error::success(); | |||
1173 | } | |||
1174 | ||||
1175 | Error DumpOutputStyle::dumpStringTable() { | |||
1176 | printHeader(P, "String Table"); | |||
1177 | ||||
1178 | if (File.isPdb()) | |||
1179 | return dumpStringTableFromPdb(); | |||
1180 | ||||
1181 | return dumpStringTableFromObj(); | |||
1182 | } | |||
1183 | ||||
1184 | static void buildDepSet(LazyRandomTypeCollection &Types, | |||
1185 | ArrayRef<TypeIndex> Indices, | |||
1186 | std::map<TypeIndex, CVType> &DepSet) { | |||
1187 | SmallVector<TypeIndex, 4> DepList; | |||
1188 | for (const auto &I : Indices) { | |||
1189 | TypeIndex TI(I); | |||
1190 | if (DepSet.find(TI) != DepSet.end() || TI.isSimple() || TI.isNoneType()) | |||
1191 | continue; | |||
1192 | ||||
1193 | CVType Type = Types.getType(TI); | |||
1194 | DepSet[TI] = Type; | |||
1195 | codeview::discoverTypeIndices(Type, DepList); | |||
1196 | buildDepSet(Types, DepList, DepSet); | |||
1197 | } | |||
1198 | } | |||
1199 | ||||
1200 | static void | |||
1201 | dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, | |||
1202 | TypeReferenceTracker *RefTracker, uint32_t NumTypeRecords, | |||
1203 | uint32_t NumHashBuckets, | |||
1204 | FixedStreamArray<support::ulittle32_t> HashValues, | |||
1205 | TpiStream *Stream, bool Bytes, bool Extras) { | |||
1206 | ||||
1207 | Printer.formatLine("Showing {0:N} records", NumTypeRecords); | |||
1208 | uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords); | |||
1209 | ||||
1210 | MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker, | |||
1211 | NumHashBuckets, HashValues, Stream); | |||
1212 | ||||
1213 | if (auto EC = codeview::visitTypeStream(Types, V)) { | |||
1214 | Printer.formatLine("An error occurred dumping type records: {0}", | |||
1215 | toString(std::move(EC))); | |||
1216 | } | |||
1217 | } | |||
1218 | ||||
1219 | static void dumpPartialTypeStream(LinePrinter &Printer, | |||
1220 | LazyRandomTypeCollection &Types, | |||
1221 | TypeReferenceTracker *RefTracker, | |||
1222 | TpiStream &Stream, ArrayRef<TypeIndex> TiList, | |||
1223 | bool Bytes, bool Extras, bool Deps) { | |||
1224 | uint32_t Width = | |||
1225 | NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); | |||
1226 | ||||
1227 | MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, RefTracker, | |||
1228 | Stream.getNumHashBuckets(), Stream.getHashValues(), | |||
1229 | &Stream); | |||
1230 | ||||
1231 | if (opts::dump::DumpTypeDependents) { | |||
1232 | // If we need to dump all dependents, then iterate each index and find | |||
1233 | // all dependents, adding them to a map ordered by TypeIndex. | |||
1234 | std::map<TypeIndex, CVType> DepSet; | |||
1235 | buildDepSet(Types, TiList, DepSet); | |||
1236 | ||||
1237 | Printer.formatLine( | |||
1238 | "Showing {0:N} records and their dependents ({1:N} records total)", | |||
1239 | TiList.size(), DepSet.size()); | |||
1240 | ||||
1241 | for (auto &Dep : DepSet) { | |||
1242 | if (auto EC = codeview::visitTypeRecord(Dep.second, Dep.first, V)) | |||
1243 | Printer.formatLine("An error occurred dumping type record {0}: {1}", | |||
1244 | Dep.first, toString(std::move(EC))); | |||
1245 | } | |||
1246 | } else { | |||
1247 | Printer.formatLine("Showing {0:N} records.", TiList.size()); | |||
1248 | ||||
1249 | for (const auto &I : TiList) { | |||
1250 | TypeIndex TI(I); | |||
1251 | if (TI.isSimple()) { | |||
1252 | Printer.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Width), | |||
1253 | Types.getTypeName(TI)); | |||
1254 | } else if (std::optional<CVType> Type = Types.tryGetType(TI)) { | |||
1255 | if (auto EC = codeview::visitTypeRecord(*Type, TI, V)) | |||
1256 | Printer.formatLine("An error occurred dumping type record {0}: {1}", | |||
1257 | TI, toString(std::move(EC))); | |||
1258 | } else { | |||
1259 | Printer.formatLine("Type {0} doesn't exist in TPI stream", TI); | |||
1260 | } | |||
1261 | } | |||
1262 | } | |||
1263 | } | |||
1264 | ||||
1265 | Error DumpOutputStyle::dumpTypesFromObjectFile() { | |||
1266 | LazyRandomTypeCollection Types(100); | |||
1267 | ||||
1268 | for (const auto &S : getObj().sections()) { | |||
1269 | Expected<StringRef> NameOrErr = S.getName(); | |||
1270 | if (!NameOrErr) | |||
1271 | return NameOrErr.takeError(); | |||
1272 | StringRef SectionName = *NameOrErr; | |||
1273 | ||||
1274 | // .debug$T is a standard CodeView type section, while .debug$P is the same | |||
1275 | // format but used for MSVC precompiled header object files. | |||
1276 | if (SectionName == ".debug$T") | |||
1277 | printHeader(P, "Types (.debug$T)"); | |||
1278 | else if (SectionName == ".debug$P") | |||
1279 | printHeader(P, "Precompiled Types (.debug$P)"); | |||
1280 | else | |||
1281 | continue; | |||
1282 | ||||
1283 | Expected<StringRef> ContentsOrErr = S.getContents(); | |||
1284 | if (!ContentsOrErr) | |||
1285 | return ContentsOrErr.takeError(); | |||
1286 | ||||
1287 | uint32_t Magic; | |||
1288 | BinaryStreamReader Reader(*ContentsOrErr, llvm::support::little); | |||
1289 | if (auto EC = Reader.readInteger(Magic)) | |||
1290 | return EC; | |||
1291 | if (Magic != COFF::DEBUG_SECTION_MAGIC) | |||
1292 | return make_error<StringError>("Invalid CodeView debug section.", | |||
1293 | inconvertibleErrorCode()); | |||
1294 | ||||
1295 | Types.reset(Reader, 100); | |||
1296 | ||||
1297 | if (opts::dump::DumpTypes) { | |||
1298 | dumpFullTypeStream(P, Types, RefTracker.get(), 0, 0, {}, nullptr, | |||
1299 | opts::dump::DumpTypeData, false); | |||
1300 | } else if (opts::dump::DumpTypeExtras) { | |||
1301 | auto LocalHashes = LocallyHashedType::hashTypeCollection(Types); | |||
1302 | auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types); | |||
1303 | assert(LocalHashes.size() == GlobalHashes.size())(static_cast <bool> (LocalHashes.size() == GlobalHashes .size()) ? void (0) : __assert_fail ("LocalHashes.size() == GlobalHashes.size()" , "llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp", 1303, __extension__ __PRETTY_FUNCTION__)); | |||
1304 | ||||
1305 | P.formatLine("Local / Global hashes:"); | |||
1306 | TypeIndex TI(TypeIndex::FirstNonSimpleIndex); | |||
1307 | for (auto H : zip(LocalHashes, GlobalHashes)) { | |||
1308 | AutoIndent Indent2(P); | |||
1309 | LocallyHashedType &L = std::get<0>(H); | |||
1310 | GloballyHashedType &G = std::get<1>(H); | |||
1311 | ||||
1312 | P.formatLine("TI: {0}, LocalHash: {1:X}, GlobalHash: {2}", TI, L, G); | |||
1313 | ||||
1314 | ++TI; | |||
1315 | } | |||
1316 | P.NewLine(); | |||
1317 | } | |||
1318 | } | |||
1319 | ||||
1320 | return Error::success(); | |||
1321 | } | |||
1322 | ||||
1323 | Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { | |||
1324 | assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI)(static_cast <bool> (StreamIdx == StreamTPI || StreamIdx == StreamIPI) ? void (0) : __assert_fail ("StreamIdx == StreamTPI || StreamIdx == StreamIPI" , "llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp", 1324, __extension__ __PRETTY_FUNCTION__)); | |||
1325 | ||||
1326 | if (StreamIdx == StreamTPI) { | |||
1327 | printHeader(P, "Types (TPI Stream)"); | |||
1328 | } else if (StreamIdx == StreamIPI) { | |||
1329 | printHeader(P, "Types (IPI Stream)"); | |||
1330 | } | |||
1331 | ||||
1332 | assert(!File.isObj())(static_cast <bool> (!File.isObj()) ? void (0) : __assert_fail ("!File.isObj()", "llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp" , 1332, __extension__ __PRETTY_FUNCTION__)); | |||
1333 | ||||
1334 | bool Present = false; | |||
1335 | bool DumpTypes = false; | |||
1336 | bool DumpBytes = false; | |||
1337 | bool DumpExtras = false; | |||
1338 | std::vector<uint32_t> Indices; | |||
1339 | if (StreamIdx == StreamTPI) { | |||
1340 | Present = getPdb().hasPDBTpiStream(); | |||
1341 | DumpTypes = opts::dump::DumpTypes; | |||
1342 | DumpBytes = opts::dump::DumpTypeData; | |||
1343 | DumpExtras = opts::dump::DumpTypeExtras; | |||
1344 | Indices.assign(opts::dump::DumpTypeIndex.begin(), | |||
1345 | opts::dump::DumpTypeIndex.end()); | |||
1346 | } else if (StreamIdx == StreamIPI) { | |||
1347 | Present = getPdb().hasPDBIpiStream(); | |||
1348 | DumpTypes = opts::dump::DumpIds; | |||
1349 | DumpBytes = opts::dump::DumpIdData; | |||
1350 | DumpExtras = opts::dump::DumpIdExtras; | |||
1351 | Indices.assign(opts::dump::DumpIdIndex.begin(), | |||
1352 | opts::dump::DumpIdIndex.end()); | |||
1353 | } | |||
1354 | ||||
1355 | if (!Present) { | |||
1356 | printStreamNotPresent(StreamIdx == StreamTPI ? "TPI" : "IPI"); | |||
1357 | return Error::success(); | |||
1358 | } | |||
1359 | ||||
1360 | AutoIndent Indent(P); | |||
1361 | ExitOnError Err("Unexpected error processing types: "); | |||
1362 | ||||
1363 | auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream() | |||
1364 | : getPdb().getPDBIpiStream()); | |||
1365 | ||||
1366 | auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids(); | |||
1367 | ||||
1368 | // Only emit notes about referenced/unreferenced for types. | |||
1369 | TypeReferenceTracker *MaybeTracker = | |||
1370 | (StreamIdx == StreamTPI) ? RefTracker.get() : nullptr; | |||
1371 | ||||
1372 | // Enable resolving forward decls. | |||
1373 | Stream.buildHashMap(); | |||
1374 | ||||
1375 | if (DumpTypes || !Indices.empty()) { | |||
1376 | if (Indices.empty()) | |||
1377 | dumpFullTypeStream(P, Types, MaybeTracker, Stream.getNumTypeRecords(), | |||
1378 | Stream.getNumHashBuckets(), Stream.getHashValues(), | |||
1379 | &Stream, DumpBytes, DumpExtras); | |||
1380 | else { | |||
1381 | std::vector<TypeIndex> TiList(Indices.begin(), Indices.end()); | |||
1382 | dumpPartialTypeStream(P, Types, MaybeTracker, Stream, TiList, DumpBytes, | |||
1383 | DumpExtras, opts::dump::DumpTypeDependents); | |||
1384 | } | |||
1385 | } | |||
1386 | ||||
1387 | if (DumpExtras) { | |||
1388 | P.NewLine(); | |||
1389 | ||||
1390 | P.formatLine("Header Version: {0}", | |||
1391 | static_cast<uint32_t>(Stream.getTpiVersion())); | |||
1392 | P.formatLine("Hash Stream Index: {0}", Stream.getTypeHashStreamIndex()); | |||
1393 | P.formatLine("Aux Hash Stream Index: {0}", | |||
1394 | Stream.getTypeHashStreamAuxIndex()); | |||
1395 | P.formatLine("Hash Key Size: {0}", Stream.getHashKeySize()); | |||
1396 | P.formatLine("Num Hash Buckets: {0}", Stream.getNumHashBuckets()); | |||
1397 | ||||
1398 | auto IndexOffsets = Stream.getTypeIndexOffsets(); | |||
1399 | P.formatLine("Type Index Offsets:"); | |||
1400 | for (const auto &IO : IndexOffsets) { | |||
1401 | AutoIndent Indent2(P); | |||
1402 | P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset)); | |||
1403 | } | |||
1404 | ||||
1405 | if (getPdb().hasPDBStringTable()) { | |||
1406 | P.NewLine(); | |||
1407 | P.formatLine("Hash Adjusters:"); | |||
1408 | auto &Adjusters = Stream.getHashAdjusters(); | |||
1409 | auto &Strings = Err(getPdb().getStringTable()); | |||
1410 | for (const auto &A : Adjusters) { | |||
1411 | AutoIndent Indent2(P); | |||
1412 | auto ExpectedStr = Strings.getStringForID(A.first); | |||
1413 | TypeIndex TI(A.second); | |||
1414 | if (ExpectedStr) | |||
1415 | P.formatLine("`{0}` -> {1}", *ExpectedStr, TI); | |||
1416 | else { | |||
1417 | P.formatLine("unknown str id ({0}) -> {1}", A.first, TI); | |||
1418 | consumeError(ExpectedStr.takeError()); | |||
1419 | } | |||
1420 | } | |||
1421 | } | |||
1422 | } | |||
1423 | return Error::success(); | |||
1424 | } | |||
1425 | ||||
1426 | Error DumpOutputStyle::dumpModuleSymsForObj() { | |||
1427 | printHeader(P, "Symbols"); | |||
1428 | ||||
1429 | AutoIndent Indent(P); | |||
1430 | ||||
1431 | auto &Types = File.types(); | |||
1432 | ||||
1433 | SymbolVisitorCallbackPipeline Pipeline; | |||
1434 | SymbolDeserializer Deserializer(nullptr, CodeViewContainer::ObjectFile); | |||
1435 | MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Types, Types); | |||
1436 | ||||
1437 | Pipeline.addCallbackToPipeline(Deserializer); | |||
1438 | Pipeline.addCallbackToPipeline(Dumper); | |||
1439 | CVSymbolVisitor Visitor(Pipeline); | |||
1440 | ||||
1441 | return iterateModuleSubsections<DebugSymbolsSubsectionRef>( | |||
1442 | File, PrintScope{P, 2}, | |||
1443 | [&](uint32_t Modi, const SymbolGroup &Strings, | |||
1444 | DebugSymbolsSubsectionRef &Symbols) -> Error { | |||
1445 | Dumper.setSymbolGroup(&Strings); | |||
1446 | for (auto Symbol : Symbols) { | |||
1447 | if (auto EC = Visitor.visitSymbolRecord(Symbol)) { | |||
1448 | return EC; | |||
1449 | } | |||
1450 | } | |||
1451 | return Error::success(); | |||
1452 | }); | |||
1453 | } | |||
1454 | ||||
1455 | Error DumpOutputStyle::dumpModuleSymsForPdb() { | |||
1456 | printHeader(P, "Symbols"); | |||
1457 | ||||
1458 | if (File.isPdb() && !getPdb().hasPDBDbiStream()) { | |||
1459 | printStreamNotPresent("DBI"); | |||
1460 | return Error::success(); | |||
1461 | } | |||
1462 | ||||
1463 | AutoIndent Indent(P); | |||
1464 | ||||
1465 | auto &Ids = File.ids(); | |||
1466 | auto &Types = File.types(); | |||
1467 | ||||
1468 | return iterateSymbolGroups( | |||
1469 | File, PrintScope{P, 2}, | |||
1470 | [&](uint32_t I, const SymbolGroup &Strings) -> Error { | |||
1471 | auto ExpectedModS = getModuleDebugStream(File.pdb(), I); | |||
1472 | if (!ExpectedModS) { | |||
| ||||
1473 | P.formatLine("Error loading module stream {0}. {1}", I, | |||
1474 | toString(ExpectedModS.takeError())); | |||
1475 | return Error::success(); | |||
1476 | } | |||
1477 | ||||
1478 | ModuleDebugStreamRef &ModS = *ExpectedModS; | |||
1479 | ||||
1480 | SymbolVisitorCallbackPipeline Pipeline; | |||
1481 | SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); | |||
1482 | MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Strings, | |||
1483 | Ids, Types); | |||
1484 | ||||
1485 | Pipeline.addCallbackToPipeline(Deserializer); | |||
1486 | Pipeline.addCallbackToPipeline(Dumper); | |||
1487 | CVSymbolVisitor Visitor(Pipeline); | |||
1488 | auto SS = ModS.getSymbolsSubstream(); | |||
1489 | if (opts::Filters.SymbolOffset) { | |||
1490 | CVSymbolVisitor::FilterOptions Filter; | |||
1491 | Filter.SymbolOffset = opts::Filters.SymbolOffset; | |||
1492 | Filter.ParentRecursiveDepth = opts::Filters.ParentRecurseDepth; | |||
1493 | Filter.ChildRecursiveDepth = opts::Filters.ChildrenRecurseDepth; | |||
1494 | if (auto EC = Visitor.visitSymbolStreamFiltered(ModS.getSymbolArray(), | |||
1495 | Filter)) { | |||
1496 | P.formatLine("Error while processing symbol records. {0}", | |||
1497 | toString(std::move(EC))); | |||
1498 | return EC; | |||
1499 | } | |||
1500 | } else if (auto EC = Visitor.visitSymbolStream(ModS.getSymbolArray(), | |||
1501 | SS.Offset)) { | |||
1502 | P.formatLine("Error while processing symbol records. {0}", | |||
1503 | toString(std::move(EC))); | |||
1504 | return EC; | |||
| ||||
1505 | } | |||
1506 | return Error::success(); | |||
1507 | }); | |||
1508 | } | |||
1509 | ||||
1510 | Error DumpOutputStyle::dumpTypeRefStats() { | |||
1511 | printHeader(P, "Type Reference Statistics"); | |||
1512 | AutoIndent Indent(P); | |||
1513 | ||||
1514 | // Sum the byte size of all type records, and the size and count of all | |||
1515 | // referenced records. | |||
1516 | size_t TotalRecs = File.types().size(); | |||
1517 | size_t RefRecs = 0; | |||
1518 | size_t TotalBytes = 0; | |||
1519 | size_t RefBytes = 0; | |||
1520 | auto &Types = File.types(); | |||
1521 | for (std::optional<TypeIndex> TI = Types.getFirst(); TI; | |||
1522 | TI = Types.getNext(*TI)) { | |||
1523 | CVType Type = File.types().getType(*TI); | |||
1524 | TotalBytes += Type.length(); | |||
1525 | if (RefTracker->isTypeReferenced(*TI)) { | |||
1526 | ++RefRecs; | |||
1527 | RefBytes += Type.length(); | |||
1528 | } | |||
1529 | } | |||
1530 | ||||
1531 | P.formatLine("Records referenced: {0:N} / {1:N} {2:P}", RefRecs, TotalRecs, | |||
1532 | (double)RefRecs / TotalRecs); | |||
1533 | P.formatLine("Bytes referenced: {0:N} / {1:N} {2:P}", RefBytes, TotalBytes, | |||
1534 | (double)RefBytes / TotalBytes); | |||
1535 | ||||
1536 | return Error::success(); | |||
1537 | } | |||
1538 | ||||
1539 | Error DumpOutputStyle::dumpGSIRecords() { | |||
1540 | printHeader(P, "GSI Records"); | |||
1541 | ||||
1542 | if (File.isObj()) { | |||
1543 | printStreamNotValidForObj(); | |||
1544 | return Error::success(); | |||
1545 | } | |||
1546 | ||||
1547 | if (!getPdb().hasPDBSymbolStream()) { | |||
1548 | printStreamNotPresent("GSI Common Symbol"); | |||
1549 | return Error::success(); | |||
1550 | } | |||
1551 | ||||
1552 | AutoIndent Indent(P); | |||
1553 | ||||
1554 | auto &Records = cantFail(getPdb().getPDBSymbolStream()); | |||
1555 | auto &Types = File.types(); | |||
1556 | auto &Ids = File.ids(); | |||
1557 | ||||
1558 | P.printLine("Records"); | |||
1559 | SymbolVisitorCallbackPipeline Pipeline; | |||
1560 | SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); | |||
1561 | MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); | |||
1562 | ||||
1563 | Pipeline.addCallbackToPipeline(Deserializer); | |||
1564 | Pipeline.addCallbackToPipeline(Dumper); | |||
1565 | CVSymbolVisitor Visitor(Pipeline); | |||
1566 | ||||
1567 | BinaryStreamRef SymStream = Records.getSymbolArray().getUnderlyingStream(); | |||
1568 | if (auto E = Visitor.visitSymbolStream(Records.getSymbolArray(), 0)) | |||
1569 | return E; | |||
1570 | return Error::success(); | |||
1571 | } | |||
1572 | ||||
1573 | Error DumpOutputStyle::dumpGlobals() { | |||
1574 | printHeader(P, "Global Symbols"); | |||
1575 | ||||
1576 | if (File.isObj()) { | |||
1577 | printStreamNotValidForObj(); | |||
1578 | return Error::success(); | |||
1579 | } | |||
1580 | ||||
1581 | if (!getPdb().hasPDBGlobalsStream()) { | |||
1582 | printStreamNotPresent("Globals"); | |||
1583 | return Error::success(); | |||
1584 | } | |||
1585 | ||||
1586 | AutoIndent Indent(P); | |||
1587 | ExitOnError Err("Error dumping globals stream: "); | |||
1588 | auto &Globals = Err(getPdb().getPDBGlobalsStream()); | |||
1589 | ||||
1590 | if (opts::dump::DumpGlobalNames.empty()) { | |||
1591 | const GSIHashTable &Table = Globals.getGlobalsTable(); | |||
1592 | Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras)); | |||
1593 | } else { | |||
1594 | SymbolStream &SymRecords = cantFail(getPdb().getPDBSymbolStream()); | |||
1595 | auto &Types = File.types(); | |||
1596 | auto &Ids = File.ids(); | |||
1597 | ||||
1598 | SymbolVisitorCallbackPipeline Pipeline; | |||
1599 | SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); | |||
1600 | MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); | |||
1601 | ||||
1602 | Pipeline.addCallbackToPipeline(Deserializer); | |||
1603 | Pipeline.addCallbackToPipeline(Dumper); | |||
1604 | CVSymbolVisitor Visitor(Pipeline); | |||
1605 | ||||
1606 | using ResultEntryType = std::pair<uint32_t, CVSymbol>; | |||
1607 | for (StringRef Name : opts::dump::DumpGlobalNames) { | |||
1608 | AutoIndent Indent(P); | |||
1609 | P.formatLine("Global Name `{0}`", Name); | |||
1610 | std::vector<ResultEntryType> Results = | |||
1611 | Globals.findRecordsByName(Name, SymRecords); | |||
1612 | if (Results.empty()) { | |||
1613 | AutoIndent Indent(P); | |||
1614 | P.printLine("(no matching records found)"); | |||
1615 | continue; | |||
1616 | } | |||
1617 | ||||
1618 | for (ResultEntryType Result : Results) { | |||
1619 | if (auto E = Visitor.visitSymbolRecord(Result.second, Result.first)) | |||
1620 | return E; | |||
1621 | } | |||
1622 | } | |||
1623 | } | |||
1624 | return Error::success(); | |||
1625 | } | |||
1626 | ||||
1627 | Error DumpOutputStyle::dumpPublics() { | |||
1628 | printHeader(P, "Public Symbols"); | |||
1629 | ||||
1630 | if (File.isObj()) { | |||
1631 | printStreamNotValidForObj(); | |||
1632 | return Error::success(); | |||
1633 | } | |||
1634 | ||||
1635 | if (!getPdb().hasPDBPublicsStream()) { | |||
1636 | printStreamNotPresent("Publics"); | |||
1637 | return Error::success(); | |||
1638 | } | |||
1639 | ||||
1640 | AutoIndent Indent(P); | |||
1641 | ExitOnError Err("Error dumping publics stream: "); | |||
1642 | auto &Publics = Err(getPdb().getPDBPublicsStream()); | |||
1643 | ||||
1644 | const GSIHashTable &PublicsTable = Publics.getPublicsTable(); | |||
1645 | if (opts::dump::DumpPublicExtras) { | |||
1646 | P.printLine("Publics Header"); | |||
1647 | AutoIndent Indent(P); | |||
1648 | P.formatLine("sym hash = {0}, thunk table addr = {1}", Publics.getSymHash(), | |||
1649 | formatSegmentOffset(Publics.getThunkTableSection(), | |||
1650 | Publics.getThunkTableOffset())); | |||
1651 | } | |||
1652 | Err(dumpSymbolsFromGSI(PublicsTable, opts::dump::DumpPublicExtras)); | |||
1653 | ||||
1654 | // Skip the rest if we aren't dumping extras. | |||
1655 | if (!opts::dump::DumpPublicExtras) | |||
1656 | return Error::success(); | |||
1657 | ||||
1658 | P.formatLine("Address Map"); | |||
1659 | { | |||
1660 | // These are offsets into the publics stream sorted by secidx:secrel. | |||
1661 | AutoIndent Indent2(P); | |||
1662 | for (uint32_t Addr : Publics.getAddressMap()) | |||
1663 | P.formatLine("off = {0}", Addr); | |||
1664 | } | |||
1665 | ||||
1666 | // The thunk map is optional debug info used for ILT thunks. | |||
1667 | if (!Publics.getThunkMap().empty()) { | |||
1668 | P.formatLine("Thunk Map"); | |||
1669 | AutoIndent Indent2(P); | |||
1670 | for (uint32_t Addr : Publics.getThunkMap()) | |||
1671 | P.formatLine("{0:x8}", Addr); | |||
1672 | } | |||
1673 | ||||
1674 | // The section offsets table appears to be empty when incremental linking | |||
1675 | // isn't in use. | |||
1676 | if (!Publics.getSectionOffsets().empty()) { | |||
1677 | P.formatLine("Section Offsets"); | |||
1678 | AutoIndent Indent2(P); | |||
1679 | for (const SectionOffset &SO : Publics.getSectionOffsets()) | |||
1680 | P.formatLine("{0:x4}:{1:x8}", uint16_t(SO.Isect), uint32_t(SO.Off)); | |||
1681 | } | |||
1682 | ||||
1683 | return Error::success(); | |||
1684 | } | |||
1685 | ||||
1686 | Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table, | |||
1687 | bool HashExtras) { | |||
1688 | auto ExpectedSyms = getPdb().getPDBSymbolStream(); | |||
1689 | if (!ExpectedSyms) | |||
1690 | return ExpectedSyms.takeError(); | |||
1691 | auto &Types = File.types(); | |||
1692 | auto &Ids = File.ids(); | |||
1693 | ||||
1694 | if (HashExtras) { | |||
1695 | P.printLine("GSI Header"); | |||
1696 | AutoIndent Indent(P); | |||
1697 | P.formatLine("sig = {0:X}, hdr = {1:X}, hr size = {2}, num buckets = {3}", | |||
1698 | Table.getVerSignature(), Table.getVerHeader(), | |||
1699 | Table.getHashRecordSize(), Table.getNumBuckets()); | |||
1700 | } | |||
1701 | ||||
1702 | { | |||
1703 | P.printLine("Records"); | |||
1704 | SymbolVisitorCallbackPipeline Pipeline; | |||
1705 | SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); | |||
1706 | MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); | |||
1707 | ||||
1708 | Pipeline.addCallbackToPipeline(Deserializer); | |||
1709 | Pipeline.addCallbackToPipeline(Dumper); | |||
1710 | CVSymbolVisitor Visitor(Pipeline); | |||
1711 | ||||
1712 | ||||
1713 | BinaryStreamRef SymStream = | |||
1714 | ExpectedSyms->getSymbolArray().getUnderlyingStream(); | |||
1715 | for (uint32_t PubSymOff : Table) { | |||
1716 | Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff); | |||
1717 | if (!Sym) | |||
1718 | return Sym.takeError(); | |||
1719 | if (auto E = Visitor.visitSymbolRecord(*Sym, PubSymOff)) | |||
1720 | return E; | |||
1721 | } | |||
1722 | } | |||
1723 | ||||
1724 | // Return early if we aren't dumping public hash table and address map info. | |||
1725 | if (HashExtras) { | |||
1726 | P.formatLine("Hash Entries"); | |||
1727 | { | |||
1728 | AutoIndent Indent2(P); | |||
1729 | for (const PSHashRecord &HR : Table.HashRecords) | |||
1730 | P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off), | |||
1731 | uint32_t(HR.CRef)); | |||
1732 | } | |||
1733 | ||||
1734 | P.formatLine("Hash Buckets"); | |||
1735 | { | |||
1736 | AutoIndent Indent2(P); | |||
1737 | for (uint32_t Hash : Table.HashBuckets) | |||
1738 | P.formatLine("{0:x8}", Hash); | |||
1739 | } | |||
1740 | } | |||
1741 | ||||
1742 | return Error::success(); | |||
1743 | } | |||
1744 | ||||
1745 | static std::string formatSegMapDescriptorFlag(uint32_t IndentLevel, | |||
1746 | OMFSegDescFlags Flags) { | |||
1747 | std::vector<std::string> Opts; | |||
1748 | if (Flags == OMFSegDescFlags::None) | |||
1749 | return "none"; | |||
1750 | ||||
1751 | PUSH_FLAG(OMFSegDescFlags, Read, Flags, "read")if (OMFSegDescFlags::Read == (Flags & OMFSegDescFlags::Read )) Opts.push_back("read");; | |||
1752 | PUSH_FLAG(OMFSegDescFlags, Write, Flags, "write")if (OMFSegDescFlags::Write == (Flags & OMFSegDescFlags::Write )) Opts.push_back("write");; | |||
1753 | PUSH_FLAG(OMFSegDescFlags, Execute, Flags, "execute")if (OMFSegDescFlags::Execute == (Flags & OMFSegDescFlags:: Execute)) Opts.push_back("execute");; | |||
1754 | PUSH_FLAG(OMFSegDescFlags, AddressIs32Bit, Flags, "32 bit addr")if (OMFSegDescFlags::AddressIs32Bit == (Flags & OMFSegDescFlags ::AddressIs32Bit)) Opts.push_back("32 bit addr");; | |||
1755 | PUSH_FLAG(OMFSegDescFlags, IsSelector, Flags, "selector")if (OMFSegDescFlags::IsSelector == (Flags & OMFSegDescFlags ::IsSelector)) Opts.push_back("selector");; | |||
1756 | PUSH_FLAG(OMFSegDescFlags, IsAbsoluteAddress, Flags, "absolute addr")if (OMFSegDescFlags::IsAbsoluteAddress == (Flags & OMFSegDescFlags ::IsAbsoluteAddress)) Opts.push_back("absolute addr");; | |||
1757 | PUSH_FLAG(OMFSegDescFlags, IsGroup, Flags, "group")if (OMFSegDescFlags::IsGroup == (Flags & OMFSegDescFlags:: IsGroup)) Opts.push_back("group");; | |||
1758 | return typesetItemList(Opts, IndentLevel, 4, " | "); | |||
1759 | } | |||
1760 | ||||
1761 | Error DumpOutputStyle::dumpSectionHeaders() { | |||
1762 | dumpSectionHeaders("Section Headers", DbgHeaderType::SectionHdr); | |||
1763 | dumpSectionHeaders("Original Section Headers", DbgHeaderType::SectionHdrOrig); | |||
1764 | return Error::success(); | |||
1765 | } | |||
1766 | ||||
1767 | void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) { | |||
1768 | printHeader(P, Label); | |||
1769 | ||||
1770 | if (File.isObj()) { | |||
1771 | printStreamNotValidForObj(); | |||
1772 | return; | |||
1773 | } | |||
1774 | ||||
1775 | if (!getPdb().hasPDBDbiStream()) { | |||
1776 | printStreamNotPresent("DBI"); | |||
1777 | return; | |||
1778 | } | |||
1779 | ||||
1780 | AutoIndent Indent(P); | |||
1781 | ExitOnError Err("Error dumping section headers: "); | |||
1782 | std::unique_ptr<MappedBlockStream> Stream; | |||
1783 | ArrayRef<object::coff_section> Headers; | |||
1784 | auto ExpectedHeaders = loadSectionHeaders(getPdb(), Type); | |||
1785 | if (!ExpectedHeaders) { | |||
1786 | P.printLine(toString(ExpectedHeaders.takeError())); | |||
1787 | return; | |||
1788 | } | |||
1789 | std::tie(Stream, Headers) = std::move(*ExpectedHeaders); | |||
1790 | ||||
1791 | uint32_t I = 1; | |||
1792 | for (const auto &Header : Headers) { | |||
1793 | P.NewLine(); | |||
1794 | P.formatLine("SECTION HEADER #{0}", I); | |||
1795 | P.formatLine("{0,8} name", Header.Name); | |||
1796 | P.formatLine("{0,8:X-} virtual size", uint32_t(Header.VirtualSize)); | |||
1797 | P.formatLine("{0,8:X-} virtual address", uint32_t(Header.VirtualAddress)); | |||
1798 | P.formatLine("{0,8:X-} size of raw data", uint32_t(Header.SizeOfRawData)); | |||
1799 | P.formatLine("{0,8:X-} file pointer to raw data", | |||
1800 | uint32_t(Header.PointerToRawData)); | |||
1801 | P.formatLine("{0,8:X-} file pointer to relocation table", | |||
1802 | uint32_t(Header.PointerToRelocations)); | |||
1803 | P.formatLine("{0,8:X-} file pointer to line numbers", | |||
1804 | uint32_t(Header.PointerToLinenumbers)); | |||
1805 | P.formatLine("{0,8:X-} number of relocations", | |||
1806 | uint32_t(Header.NumberOfRelocations)); | |||
1807 | P.formatLine("{0,8:X-} number of line numbers", | |||
1808 | uint32_t(Header.NumberOfLinenumbers)); | |||
1809 | P.formatLine("{0,8:X-} flags", uint32_t(Header.Characteristics)); | |||
1810 | AutoIndent IndentMore(P, 9); | |||
1811 | P.formatLine("{0}", formatSectionCharacteristics( | |||
1812 | P.getIndentLevel(), Header.Characteristics, 1, "")); | |||
1813 | ++I; | |||
1814 | } | |||
1815 | } | |||
1816 | ||||
1817 | Error DumpOutputStyle::dumpSectionContribs() { | |||
1818 | printHeader(P, "Section Contributions"); | |||
1819 | ||||
1820 | if (File.isObj()) { | |||
1821 | printStreamNotValidForObj(); | |||
1822 | return Error::success(); | |||
1823 | } | |||
1824 | ||||
1825 | if (!getPdb().hasPDBDbiStream()) { | |||
1826 | printStreamNotPresent("DBI"); | |||
1827 | return Error::success(); | |||
1828 | } | |||
1829 | ||||
1830 | AutoIndent Indent(P); | |||
1831 | ExitOnError Err("Error dumping section contributions: "); | |||
1832 | ||||
1833 | DbiStream &Dbi = Err(getPdb().getPDBDbiStream()); | |||
1834 | ||||
1835 | class Visitor : public ISectionContribVisitor { | |||
1836 | public: | |||
1837 | Visitor(LinePrinter &P, ArrayRef<std::string> Names) : P(P), Names(Names) { | |||
1838 | auto Max = std::max_element( | |||
1839 | Names.begin(), Names.end(), | |||
1840 | [](StringRef S1, StringRef S2) { return S1.size() < S2.size(); }); | |||
1841 | MaxNameLen = (Max == Names.end() ? 0 : Max->size()); | |||
1842 | } | |||
1843 | void visit(const SectionContrib &SC) override { | |||
1844 | dumpSectionContrib(P, SC, Names, MaxNameLen); | |||
1845 | } | |||
1846 | void visit(const SectionContrib2 &SC) override { | |||
1847 | dumpSectionContrib(P, SC, Names, MaxNameLen); | |||
1848 | } | |||
1849 | ||||
1850 | private: | |||
1851 | LinePrinter &P; | |||
1852 | uint32_t MaxNameLen; | |||
1853 | ArrayRef<std::string> Names; | |||
1854 | }; | |||
1855 | ||||
1856 | auto NamesOrErr = getSectionNames(getPdb()); | |||
1857 | if (!NamesOrErr) | |||
1858 | return NamesOrErr.takeError(); | |||
1859 | ArrayRef<std::string> Names = *NamesOrErr; | |||
1860 | Visitor V(P, Names); | |||
1861 | Dbi.visitSectionContributions(V); | |||
1862 | return Error::success(); | |||
1863 | } | |||
1864 | ||||
1865 | Error DumpOutputStyle::dumpSectionMap() { | |||
1866 | printHeader(P, "Section Map"); | |||
1867 | ||||
1868 | if (File.isObj()) { | |||
1869 | printStreamNotValidForObj(); | |||
1870 | return Error::success(); | |||
1871 | } | |||
1872 | ||||
1873 | if (!getPdb().hasPDBDbiStream()) { | |||
1874 | printStreamNotPresent("DBI"); | |||
1875 | return Error::success(); | |||
1876 | } | |||
1877 | ||||
1878 | AutoIndent Indent(P); | |||
1879 | ExitOnError Err("Error dumping section map: "); | |||
1880 | ||||
1881 | DbiStream &Dbi = Err(getPdb().getPDBDbiStream()); | |||
1882 | ||||
1883 | uint32_t I = 0; | |||
1884 | for (auto &M : Dbi.getSectionMap()) { | |||
1885 | P.formatLine( | |||
1886 | "Section {0:4} | ovl = {1}, group = {2}, frame = {3}, name = {4}", I, | |||
1887 | fmtle(M.Ovl), fmtle(M.Group), fmtle(M.Frame), fmtle(M.SecName)); | |||
1888 | P.formatLine(" class = {0}, offset = {1}, size = {2}", | |||
1889 | fmtle(M.ClassName), fmtle(M.Offset), fmtle(M.SecByteLength)); | |||
1890 | P.formatLine(" flags = {0}", | |||
1891 | formatSegMapDescriptorFlag( | |||
1892 | P.getIndentLevel() + 13, | |||
1893 | static_cast<OMFSegDescFlags>(uint16_t(M.Flags)))); | |||
1894 | ++I; | |||
1895 | } | |||
1896 | return Error::success(); | |||
1897 | } |
1 | //===- llvm/Support/Error.h - Recoverable error handling --------*- 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 an API used to report recoverable errors. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef LLVM_SUPPORT_ERROR_H |
14 | #define LLVM_SUPPORT_ERROR_H |
15 | |
16 | #include "llvm-c/Error.h" |
17 | #include "llvm/ADT/SmallVector.h" |
18 | #include "llvm/ADT/StringExtras.h" |
19 | #include "llvm/ADT/Twine.h" |
20 | #include "llvm/Config/abi-breaking.h" |
21 | #include "llvm/Support/AlignOf.h" |
22 | #include "llvm/Support/Compiler.h" |
23 | #include "llvm/Support/Debug.h" |
24 | #include "llvm/Support/ErrorHandling.h" |
25 | #include "llvm/Support/ErrorOr.h" |
26 | #include "llvm/Support/Format.h" |
27 | #include "llvm/Support/raw_ostream.h" |
28 | #include <cassert> |
29 | #include <cstdint> |
30 | #include <cstdlib> |
31 | #include <functional> |
32 | #include <memory> |
33 | #include <new> |
34 | #include <optional> |
35 | #include <string> |
36 | #include <system_error> |
37 | #include <type_traits> |
38 | #include <utility> |
39 | #include <vector> |
40 | |
41 | namespace llvm { |
42 | |
43 | class ErrorSuccess; |
44 | |
45 | /// Base class for error info classes. Do not extend this directly: Extend |
46 | /// the ErrorInfo template subclass instead. |
47 | class ErrorInfoBase { |
48 | public: |
49 | virtual ~ErrorInfoBase() = default; |
50 | |
51 | /// Print an error message to an output stream. |
52 | virtual void log(raw_ostream &OS) const = 0; |
53 | |
54 | /// Return the error message as a string. |
55 | virtual std::string message() const { |
56 | std::string Msg; |
57 | raw_string_ostream OS(Msg); |
58 | log(OS); |
59 | return OS.str(); |
60 | } |
61 | |
62 | /// Convert this error to a std::error_code. |
63 | /// |
64 | /// This is a temporary crutch to enable interaction with code still |
65 | /// using std::error_code. It will be removed in the future. |
66 | virtual std::error_code convertToErrorCode() const = 0; |
67 | |
68 | // Returns the class ID for this type. |
69 | static const void *classID() { return &ID; } |
70 | |
71 | // Returns the class ID for the dynamic type of this ErrorInfoBase instance. |
72 | virtual const void *dynamicClassID() const = 0; |
73 | |
74 | // Check whether this instance is a subclass of the class identified by |
75 | // ClassID. |
76 | virtual bool isA(const void *const ClassID) const { |
77 | return ClassID == classID(); |
78 | } |
79 | |
80 | // Check whether this instance is a subclass of ErrorInfoT. |
81 | template <typename ErrorInfoT> bool isA() const { |
82 | return isA(ErrorInfoT::classID()); |
83 | } |
84 | |
85 | private: |
86 | virtual void anchor(); |
87 | |
88 | static char ID; |
89 | }; |
90 | |
91 | /// Lightweight error class with error context and mandatory checking. |
92 | /// |
93 | /// Instances of this class wrap a ErrorInfoBase pointer. Failure states |
94 | /// are represented by setting the pointer to a ErrorInfoBase subclass |
95 | /// instance containing information describing the failure. Success is |
96 | /// represented by a null pointer value. |
97 | /// |
98 | /// Instances of Error also contains a 'Checked' flag, which must be set |
99 | /// before the destructor is called, otherwise the destructor will trigger a |
100 | /// runtime error. This enforces at runtime the requirement that all Error |
101 | /// instances be checked or returned to the caller. |
102 | /// |
103 | /// There are two ways to set the checked flag, depending on what state the |
104 | /// Error instance is in. For Error instances indicating success, it |
105 | /// is sufficient to invoke the boolean conversion operator. E.g.: |
106 | /// |
107 | /// @code{.cpp} |
108 | /// Error foo(<...>); |
109 | /// |
110 | /// if (auto E = foo(<...>)) |
111 | /// return E; // <- Return E if it is in the error state. |
112 | /// // We have verified that E was in the success state. It can now be safely |
113 | /// // destroyed. |
114 | /// @endcode |
115 | /// |
116 | /// A success value *can not* be dropped. For example, just calling 'foo(<...>)' |
117 | /// without testing the return value will raise a runtime error, even if foo |
118 | /// returns success. |
119 | /// |
120 | /// For Error instances representing failure, you must use either the |
121 | /// handleErrors or handleAllErrors function with a typed handler. E.g.: |
122 | /// |
123 | /// @code{.cpp} |
124 | /// class MyErrorInfo : public ErrorInfo<MyErrorInfo> { |
125 | /// // Custom error info. |
126 | /// }; |
127 | /// |
128 | /// Error foo(<...>) { return make_error<MyErrorInfo>(...); } |
129 | /// |
130 | /// auto E = foo(<...>); // <- foo returns failure with MyErrorInfo. |
131 | /// auto NewE = |
132 | /// handleErrors(E, |
133 | /// [](const MyErrorInfo &M) { |
134 | /// // Deal with the error. |
135 | /// }, |
136 | /// [](std::unique_ptr<OtherError> M) -> Error { |
137 | /// if (canHandle(*M)) { |
138 | /// // handle error. |
139 | /// return Error::success(); |
140 | /// } |
141 | /// // Couldn't handle this error instance. Pass it up the stack. |
142 | /// return Error(std::move(M)); |
143 | /// ); |
144 | /// // Note - we must check or return NewE in case any of the handlers |
145 | /// // returned a new error. |
146 | /// @endcode |
147 | /// |
148 | /// The handleAllErrors function is identical to handleErrors, except |
149 | /// that it has a void return type, and requires all errors to be handled and |
150 | /// no new errors be returned. It prevents errors (assuming they can all be |
151 | /// handled) from having to be bubbled all the way to the top-level. |
152 | /// |
153 | /// *All* Error instances must be checked before destruction, even if |
154 | /// they're moved-assigned or constructed from Success values that have already |
155 | /// been checked. This enforces checking through all levels of the call stack. |
156 | class [[nodiscard]] Error { |
157 | // ErrorList needs to be able to yank ErrorInfoBase pointers out of Errors |
158 | // to add to the error list. It can't rely on handleErrors for this, since |
159 | // handleErrors does not support ErrorList handlers. |
160 | friend class ErrorList; |
161 | |
162 | // handleErrors needs to be able to set the Checked flag. |
163 | template <typename... HandlerTs> |
164 | friend Error handleErrors(Error E, HandlerTs &&... Handlers); |
165 | |
166 | // Expected<T> needs to be able to steal the payload when constructed from an |
167 | // error. |
168 | template <typename T> friend class Expected; |
169 | |
170 | // wrap needs to be able to steal the payload. |
171 | friend LLVMErrorRef wrap(Error); |
172 | |
173 | protected: |
174 | /// Create a success value. Prefer using 'Error::success()' for readability |
175 | Error() { |
176 | setPtr(nullptr); |
177 | setChecked(false); |
178 | } |
179 | |
180 | public: |
181 | /// Create a success value. |
182 | static ErrorSuccess success(); |
183 | |
184 | // Errors are not copy-constructable. |
185 | Error(const Error &Other) = delete; |
186 | |
187 | /// Move-construct an error value. The newly constructed error is considered |
188 | /// unchecked, even if the source error had been checked. The original error |
189 | /// becomes a checked Success value, regardless of its original state. |
190 | Error(Error &&Other) { |
191 | setChecked(true); |
192 | *this = std::move(Other); |
193 | } |
194 | |
195 | /// Create an error value. Prefer using the 'make_error' function, but |
196 | /// this constructor can be useful when "re-throwing" errors from handlers. |
197 | Error(std::unique_ptr<ErrorInfoBase> Payload) { |
198 | setPtr(Payload.release()); |
199 | setChecked(false); |
200 | } |
201 | |
202 | // Errors are not copy-assignable. |
203 | Error &operator=(const Error &Other) = delete; |
204 | |
205 | /// Move-assign an error value. The current error must represent success, you |
206 | /// you cannot overwrite an unhandled error. The current error is then |
207 | /// considered unchecked. The source error becomes a checked success value, |
208 | /// regardless of its original state. |
209 | Error &operator=(Error &&Other) { |
210 | // Don't allow overwriting of unchecked values. |
211 | assertIsChecked(); |
212 | setPtr(Other.getPtr()); |
213 | |
214 | // This Error is unchecked, even if the source error was checked. |
215 | setChecked(false); |
216 | |
217 | // Null out Other's payload and set its checked bit. |
218 | Other.setPtr(nullptr); |
219 | Other.setChecked(true); |
220 | |
221 | return *this; |
222 | } |
223 | |
224 | /// Destroy a Error. Fails with a call to abort() if the error is |
225 | /// unchecked. |
226 | ~Error() { |
227 | assertIsChecked(); |
228 | delete getPtr(); |
229 | } |
230 | |
231 | /// Bool conversion. Returns true if this Error is in a failure state, |
232 | /// and false if it is in an accept state. If the error is in a Success state |
233 | /// it will be considered checked. |
234 | explicit operator bool() { |
235 | setChecked(getPtr() == nullptr); |
236 | return getPtr() != nullptr; |
237 | } |
238 | |
239 | /// Check whether one error is a subclass of another. |
240 | template <typename ErrT> bool isA() const { |
241 | return getPtr() && getPtr()->isA(ErrT::classID()); |
242 | } |
243 | |
244 | /// Returns the dynamic class id of this error, or null if this is a success |
245 | /// value. |
246 | const void* dynamicClassID() const { |
247 | if (!getPtr()) |
248 | return nullptr; |
249 | return getPtr()->dynamicClassID(); |
250 | } |
251 | |
252 | private: |
253 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
254 | // assertIsChecked() happens very frequently, but under normal circumstances |
255 | // is supposed to be a no-op. So we want it to be inlined, but having a bunch |
256 | // of debug prints can cause the function to be too large for inlining. So |
257 | // it's important that we define this function out of line so that it can't be |
258 | // inlined. |
259 | [[noreturn]] void fatalUncheckedError() const; |
260 | #endif |
261 | |
262 | void assertIsChecked() { |
263 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
264 | if (LLVM_UNLIKELY(!getChecked() || getPtr())__builtin_expect((bool)(!getChecked() || getPtr()), false)) |
265 | fatalUncheckedError(); |
266 | #endif |
267 | } |
268 | |
269 | ErrorInfoBase *getPtr() const { |
270 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
271 | return reinterpret_cast<ErrorInfoBase*>( |
272 | reinterpret_cast<uintptr_t>(Payload) & |
273 | ~static_cast<uintptr_t>(0x1)); |
274 | #else |
275 | return Payload; |
276 | #endif |
277 | } |
278 | |
279 | void setPtr(ErrorInfoBase *EI) { |
280 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
281 | Payload = reinterpret_cast<ErrorInfoBase*>( |
282 | (reinterpret_cast<uintptr_t>(EI) & |
283 | ~static_cast<uintptr_t>(0x1)) | |
284 | (reinterpret_cast<uintptr_t>(Payload) & 0x1)); |
285 | #else |
286 | Payload = EI; |
287 | #endif |
288 | } |
289 | |
290 | bool getChecked() const { |
291 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
292 | return (reinterpret_cast<uintptr_t>(Payload) & 0x1) == 0; |
293 | #else |
294 | return true; |
295 | #endif |
296 | } |
297 | |
298 | void setChecked(bool V) { |
299 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
300 | Payload = reinterpret_cast<ErrorInfoBase*>( |
301 | (reinterpret_cast<uintptr_t>(Payload) & |
302 | ~static_cast<uintptr_t>(0x1)) | |
303 | (V ? 0 : 1)); |
304 | #endif |
305 | } |
306 | |
307 | std::unique_ptr<ErrorInfoBase> takePayload() { |
308 | std::unique_ptr<ErrorInfoBase> Tmp(getPtr()); |
309 | setPtr(nullptr); |
310 | setChecked(true); |
311 | return Tmp; |
312 | } |
313 | |
314 | friend raw_ostream &operator<<(raw_ostream &OS, const Error &E) { |
315 | if (auto *P = E.getPtr()) |
316 | P->log(OS); |
317 | else |
318 | OS << "success"; |
319 | return OS; |
320 | } |
321 | |
322 | ErrorInfoBase *Payload = nullptr; |
323 | }; |
324 | |
325 | /// Subclass of Error for the sole purpose of identifying the success path in |
326 | /// the type system. This allows to catch invalid conversion to Expected<T> at |
327 | /// compile time. |
328 | class ErrorSuccess final : public Error {}; |
329 | |
330 | inline ErrorSuccess Error::success() { return ErrorSuccess(); } |
331 | |
332 | /// Make a Error instance representing failure using the given error info |
333 | /// type. |
334 | template <typename ErrT, typename... ArgTs> Error make_error(ArgTs &&... Args) { |
335 | return Error(std::make_unique<ErrT>(std::forward<ArgTs>(Args)...)); |
336 | } |
337 | |
338 | /// Base class for user error types. Users should declare their error types |
339 | /// like: |
340 | /// |
341 | /// class MyError : public ErrorInfo<MyError> { |
342 | /// .... |
343 | /// }; |
344 | /// |
345 | /// This class provides an implementation of the ErrorInfoBase::kind |
346 | /// method, which is used by the Error RTTI system. |
347 | template <typename ThisErrT, typename ParentErrT = ErrorInfoBase> |
348 | class ErrorInfo : public ParentErrT { |
349 | public: |
350 | using ParentErrT::ParentErrT; // inherit constructors |
351 | |
352 | static const void *classID() { return &ThisErrT::ID; } |
353 | |
354 | const void *dynamicClassID() const override { return &ThisErrT::ID; } |
355 | |
356 | bool isA(const void *const ClassID) const override { |
357 | return ClassID == classID() || ParentErrT::isA(ClassID); |
358 | } |
359 | }; |
360 | |
361 | /// Special ErrorInfo subclass representing a list of ErrorInfos. |
362 | /// Instances of this class are constructed by joinError. |
363 | class ErrorList final : public ErrorInfo<ErrorList> { |
364 | // handleErrors needs to be able to iterate the payload list of an |
365 | // ErrorList. |
366 | template <typename... HandlerTs> |
367 | friend Error handleErrors(Error E, HandlerTs &&... Handlers); |
368 | |
369 | // joinErrors is implemented in terms of join. |
370 | friend Error joinErrors(Error, Error); |
371 | |
372 | public: |
373 | void log(raw_ostream &OS) const override { |
374 | OS << "Multiple errors:\n"; |
375 | for (const auto &ErrPayload : Payloads) { |
376 | ErrPayload->log(OS); |
377 | OS << "\n"; |
378 | } |
379 | } |
380 | |
381 | std::error_code convertToErrorCode() const override; |
382 | |
383 | // Used by ErrorInfo::classID. |
384 | static char ID; |
385 | |
386 | private: |
387 | ErrorList(std::unique_ptr<ErrorInfoBase> Payload1, |
388 | std::unique_ptr<ErrorInfoBase> Payload2) { |
389 | assert(!Payload1->isA<ErrorList>() && !Payload2->isA<ErrorList>() &&(static_cast <bool> (!Payload1->isA<ErrorList> () && !Payload2->isA<ErrorList>() && "ErrorList constructor payloads should be singleton errors") ? void (0) : __assert_fail ("!Payload1->isA<ErrorList>() && !Payload2->isA<ErrorList>() && \"ErrorList constructor payloads should be singleton errors\"" , "llvm/include/llvm/Support/Error.h", 390, __extension__ __PRETTY_FUNCTION__ )) |
390 | "ErrorList constructor payloads should be singleton errors")(static_cast <bool> (!Payload1->isA<ErrorList> () && !Payload2->isA<ErrorList>() && "ErrorList constructor payloads should be singleton errors") ? void (0) : __assert_fail ("!Payload1->isA<ErrorList>() && !Payload2->isA<ErrorList>() && \"ErrorList constructor payloads should be singleton errors\"" , "llvm/include/llvm/Support/Error.h", 390, __extension__ __PRETTY_FUNCTION__ )); |
391 | Payloads.push_back(std::move(Payload1)); |
392 | Payloads.push_back(std::move(Payload2)); |
393 | } |
394 | |
395 | static Error join(Error E1, Error E2) { |
396 | if (!E1) |
397 | return E2; |
398 | if (!E2) |
399 | return E1; |
400 | if (E1.isA<ErrorList>()) { |
401 | auto &E1List = static_cast<ErrorList &>(*E1.getPtr()); |
402 | if (E2.isA<ErrorList>()) { |
403 | auto E2Payload = E2.takePayload(); |
404 | auto &E2List = static_cast<ErrorList &>(*E2Payload); |
405 | for (auto &Payload : E2List.Payloads) |
406 | E1List.Payloads.push_back(std::move(Payload)); |
407 | } else |
408 | E1List.Payloads.push_back(E2.takePayload()); |
409 | |
410 | return E1; |
411 | } |
412 | if (E2.isA<ErrorList>()) { |
413 | auto &E2List = static_cast<ErrorList &>(*E2.getPtr()); |
414 | E2List.Payloads.insert(E2List.Payloads.begin(), E1.takePayload()); |
415 | return E2; |
416 | } |
417 | return Error(std::unique_ptr<ErrorList>( |
418 | new ErrorList(E1.takePayload(), E2.takePayload()))); |
419 | } |
420 | |
421 | std::vector<std::unique_ptr<ErrorInfoBase>> Payloads; |
422 | }; |
423 | |
424 | /// Concatenate errors. The resulting Error is unchecked, and contains the |
425 | /// ErrorInfo(s), if any, contained in E1, followed by the |
426 | /// ErrorInfo(s), if any, contained in E2. |
427 | inline Error joinErrors(Error E1, Error E2) { |
428 | return ErrorList::join(std::move(E1), std::move(E2)); |
429 | } |
430 | |
431 | /// Tagged union holding either a T or a Error. |
432 | /// |
433 | /// This class parallels ErrorOr, but replaces error_code with Error. Since |
434 | /// Error cannot be copied, this class replaces getError() with |
435 | /// takeError(). It also adds an bool errorIsA<ErrT>() method for testing the |
436 | /// error class type. |
437 | /// |
438 | /// Example usage of 'Expected<T>' as a function return type: |
439 | /// |
440 | /// @code{.cpp} |
441 | /// Expected<int> myDivide(int A, int B) { |
442 | /// if (B == 0) { |
443 | /// // return an Error |
444 | /// return createStringError(inconvertibleErrorCode(), |
445 | /// "B must not be zero!"); |
446 | /// } |
447 | /// // return an integer |
448 | /// return A / B; |
449 | /// } |
450 | /// @endcode |
451 | /// |
452 | /// Checking the results of to a function returning 'Expected<T>': |
453 | /// @code{.cpp} |
454 | /// if (auto E = Result.takeError()) { |
455 | /// // We must consume the error. Typically one of: |
456 | /// // - return the error to our caller |
457 | /// // - toString(), when logging |
458 | /// // - consumeError(), to silently swallow the error |
459 | /// // - handleErrors(), to distinguish error types |
460 | /// errs() << "Problem with division " << toString(std::move(E)) << "\n"; |
461 | /// return; |
462 | /// } |
463 | /// // use the result |
464 | /// outs() << "The answer is " << *Result << "\n"; |
465 | /// @endcode |
466 | /// |
467 | /// For unit-testing a function returning an 'Expected<T>', see the |
468 | /// 'EXPECT_THAT_EXPECTED' macros in llvm/Testing/Support/Error.h |
469 | |
470 | template <class T> class [[nodiscard]] Expected { |
471 | template <class T1> friend class ExpectedAsOutParameter; |
472 | template <class OtherT> friend class Expected; |
473 | |
474 | static constexpr bool isRef = std::is_reference_v<T>; |
475 | |
476 | using wrap = std::reference_wrapper<std::remove_reference_t<T>>; |
477 | |
478 | using error_type = std::unique_ptr<ErrorInfoBase>; |
479 | |
480 | public: |
481 | using storage_type = std::conditional_t<isRef, wrap, T>; |
482 | using value_type = T; |
483 | |
484 | private: |
485 | using reference = std::remove_reference_t<T> &; |
486 | using const_reference = const std::remove_reference_t<T> &; |
487 | using pointer = std::remove_reference_t<T> *; |
488 | using const_pointer = const std::remove_reference_t<T> *; |
489 | |
490 | public: |
491 | /// Create an Expected<T> error value from the given Error. |
492 | Expected(Error Err) |
493 | : HasError(true) |
494 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
495 | // Expected is unchecked upon construction in Debug builds. |
496 | , Unchecked(true) |
497 | #endif |
498 | { |
499 | assert(Err && "Cannot create Expected<T> from Error success value.")(static_cast <bool> (Err && "Cannot create Expected<T> from Error success value." ) ? void (0) : __assert_fail ("Err && \"Cannot create Expected<T> from Error success value.\"" , "llvm/include/llvm/Support/Error.h", 499, __extension__ __PRETTY_FUNCTION__ )); |
500 | new (getErrorStorage()) error_type(Err.takePayload()); |
501 | } |
502 | |
503 | /// Forbid to convert from Error::success() implicitly, this avoids having |
504 | /// Expected<T> foo() { return Error::success(); } which compiles otherwise |
505 | /// but triggers the assertion above. |
506 | Expected(ErrorSuccess) = delete; |
507 | |
508 | /// Create an Expected<T> success value from the given OtherT value, which |
509 | /// must be convertible to T. |
510 | template <typename OtherT> |
511 | Expected(OtherT &&Val, |
512 | std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) |
513 | : HasError(false) |
514 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
515 | // Expected is unchecked upon construction in Debug builds. |
516 | , |
517 | Unchecked(true) |
518 | #endif |
519 | { |
520 | new (getStorage()) storage_type(std::forward<OtherT>(Val)); |
521 | } |
522 | |
523 | /// Move construct an Expected<T> value. |
524 | Expected(Expected &&Other) { moveConstruct(std::move(Other)); } |
525 | |
526 | /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT |
527 | /// must be convertible to T. |
528 | template <class OtherT> |
529 | Expected(Expected<OtherT> &&Other, |
530 | std::enable_if_t<std::is_convertible_v<OtherT, T>> * = nullptr) { |
531 | moveConstruct(std::move(Other)); |
532 | } |
533 | |
534 | /// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT |
535 | /// isn't convertible to T. |
536 | template <class OtherT> |
537 | explicit Expected( |
538 | Expected<OtherT> &&Other, |
539 | std::enable_if_t<!std::is_convertible_v<OtherT, T>> * = nullptr) { |
540 | moveConstruct(std::move(Other)); |
541 | } |
542 | |
543 | /// Move-assign from another Expected<T>. |
544 | Expected &operator=(Expected &&Other) { |
545 | moveAssign(std::move(Other)); |
546 | return *this; |
547 | } |
548 | |
549 | /// Destroy an Expected<T>. |
550 | ~Expected() { |
551 | assertIsChecked(); |
552 | if (!HasError) |
553 | getStorage()->~storage_type(); |
554 | else |
555 | getErrorStorage()->~error_type(); |
556 | } |
557 | |
558 | /// Return false if there is an error. |
559 | explicit operator bool() { |
560 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
561 | Unchecked = HasError; |
562 | #endif |
563 | return !HasError; |
564 | } |
565 | |
566 | /// Returns a reference to the stored T value. |
567 | reference get() { |
568 | assertIsChecked(); |
569 | return *getStorage(); |
570 | } |
571 | |
572 | /// Returns a const reference to the stored T value. |
573 | const_reference get() const { |
574 | assertIsChecked(); |
575 | return const_cast<Expected<T> *>(this)->get(); |
576 | } |
577 | |
578 | /// Returns \a takeError() after moving the held T (if any) into \p V. |
579 | template <class OtherT> |
580 | Error moveInto( |
581 | OtherT &Value, |
582 | std::enable_if_t<std::is_assignable_v<OtherT &, T &&>> * = nullptr) && { |
583 | if (*this) |
584 | Value = std::move(get()); |
585 | return takeError(); |
586 | } |
587 | |
588 | /// Check that this Expected<T> is an error of type ErrT. |
589 | template <typename ErrT> bool errorIsA() const { |
590 | return HasError && (*getErrorStorage())->template isA<ErrT>(); |
591 | } |
592 | |
593 | /// Take ownership of the stored error. |
594 | /// After calling this the Expected<T> is in an indeterminate state that can |
595 | /// only be safely destructed. No further calls (beside the destructor) should |
596 | /// be made on the Expected<T> value. |
597 | Error takeError() { |
598 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
599 | Unchecked = false; |
600 | #endif |
601 | return HasError ? Error(std::move(*getErrorStorage())) : Error::success(); |
602 | } |
603 | |
604 | /// Returns a pointer to the stored T value. |
605 | pointer operator->() { |
606 | assertIsChecked(); |
607 | return toPointer(getStorage()); |
608 | } |
609 | |
610 | /// Returns a const pointer to the stored T value. |
611 | const_pointer operator->() const { |
612 | assertIsChecked(); |
613 | return toPointer(getStorage()); |
614 | } |
615 | |
616 | /// Returns a reference to the stored T value. |
617 | reference operator*() { |
618 | assertIsChecked(); |
619 | return *getStorage(); |
620 | } |
621 | |
622 | /// Returns a const reference to the stored T value. |
623 | const_reference operator*() const { |
624 | assertIsChecked(); |
625 | return *getStorage(); |
626 | } |
627 | |
628 | private: |
629 | template <class T1> |
630 | static bool compareThisIfSameType(const T1 &a, const T1 &b) { |
631 | return &a == &b; |
632 | } |
633 | |
634 | template <class T1, class T2> |
635 | static bool compareThisIfSameType(const T1 &, const T2 &) { |
636 | return false; |
637 | } |
638 | |
639 | template <class OtherT> void moveConstruct(Expected<OtherT> &&Other) { |
640 | HasError = Other.HasError; |
641 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
642 | Unchecked = true; |
643 | Other.Unchecked = false; |
644 | #endif |
645 | |
646 | if (!HasError) |
647 | new (getStorage()) storage_type(std::move(*Other.getStorage())); |
648 | else |
649 | new (getErrorStorage()) error_type(std::move(*Other.getErrorStorage())); |
650 | } |
651 | |
652 | template <class OtherT> void moveAssign(Expected<OtherT> &&Other) { |
653 | assertIsChecked(); |
654 | |
655 | if (compareThisIfSameType(*this, Other)) |
656 | return; |
657 | |
658 | this->~Expected(); |
659 | new (this) Expected(std::move(Other)); |
660 | } |
661 | |
662 | pointer toPointer(pointer Val) { return Val; } |
663 | |
664 | const_pointer toPointer(const_pointer Val) const { return Val; } |
665 | |
666 | pointer toPointer(wrap *Val) { return &Val->get(); } |
667 | |
668 | const_pointer toPointer(const wrap *Val) const { return &Val->get(); } |
669 | |
670 | storage_type *getStorage() { |
671 | assert(!HasError && "Cannot get value when an error exists!")(static_cast <bool> (!HasError && "Cannot get value when an error exists!" ) ? void (0) : __assert_fail ("!HasError && \"Cannot get value when an error exists!\"" , "llvm/include/llvm/Support/Error.h", 671, __extension__ __PRETTY_FUNCTION__ )); |
672 | return reinterpret_cast<storage_type *>(&TStorage); |
673 | } |
674 | |
675 | const storage_type *getStorage() const { |
676 | assert(!HasError && "Cannot get value when an error exists!")(static_cast <bool> (!HasError && "Cannot get value when an error exists!" ) ? void (0) : __assert_fail ("!HasError && \"Cannot get value when an error exists!\"" , "llvm/include/llvm/Support/Error.h", 676, __extension__ __PRETTY_FUNCTION__ )); |
677 | return reinterpret_cast<const storage_type *>(&TStorage); |
678 | } |
679 | |
680 | error_type *getErrorStorage() { |
681 | assert(HasError && "Cannot get error when a value exists!")(static_cast <bool> (HasError && "Cannot get error when a value exists!" ) ? void (0) : __assert_fail ("HasError && \"Cannot get error when a value exists!\"" , "llvm/include/llvm/Support/Error.h", 681, __extension__ __PRETTY_FUNCTION__ )); |
682 | return reinterpret_cast<error_type *>(&ErrorStorage); |
683 | } |
684 | |
685 | const error_type *getErrorStorage() const { |
686 | assert(HasError && "Cannot get error when a value exists!")(static_cast <bool> (HasError && "Cannot get error when a value exists!" ) ? void (0) : __assert_fail ("HasError && \"Cannot get error when a value exists!\"" , "llvm/include/llvm/Support/Error.h", 686, __extension__ __PRETTY_FUNCTION__ )); |
687 | return reinterpret_cast<const error_type *>(&ErrorStorage); |
688 | } |
689 | |
690 | // Used by ExpectedAsOutParameter to reset the checked flag. |
691 | void setUnchecked() { |
692 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
693 | Unchecked = true; |
694 | #endif |
695 | } |
696 | |
697 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
698 | [[noreturn]] LLVM_ATTRIBUTE_NOINLINE__attribute__((noinline)) void fatalUncheckedExpected() const { |
699 | dbgs() << "Expected<T> must be checked before access or destruction.\n"; |
700 | if (HasError) { |
701 | dbgs() << "Unchecked Expected<T> contained error:\n"; |
702 | (*getErrorStorage())->log(dbgs()); |
703 | } else |
704 | dbgs() << "Expected<T> value was in success state. (Note: Expected<T> " |
705 | "values in success mode must still be checked prior to being " |
706 | "destroyed).\n"; |
707 | abort(); |
708 | } |
709 | #endif |
710 | |
711 | void assertIsChecked() const { |
712 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
713 | if (LLVM_UNLIKELY(Unchecked)__builtin_expect((bool)(Unchecked), false)) |
714 | fatalUncheckedExpected(); |
715 | #endif |
716 | } |
717 | |
718 | union { |
719 | AlignedCharArrayUnion<storage_type> TStorage; |
720 | AlignedCharArrayUnion<error_type> ErrorStorage; |
721 | }; |
722 | bool HasError : 1; |
723 | #if LLVM_ENABLE_ABI_BREAKING_CHECKS1 |
724 | bool Unchecked : 1; |
725 | #endif |
726 | }; |
727 | |
728 | /// Report a serious error, calling any installed error handler. See |
729 | /// ErrorHandling.h. |
730 | [[noreturn]] void report_fatal_error(Error Err, bool gen_crash_diag = true); |
731 | |
732 | /// Report a fatal error if Err is a failure value. |
733 | /// |
734 | /// This function can be used to wrap calls to fallible functions ONLY when it |
735 | /// is known that the Error will always be a success value. E.g. |
736 | /// |
737 | /// @code{.cpp} |
738 | /// // foo only attempts the fallible operation if DoFallibleOperation is |
739 | /// // true. If DoFallibleOperation is false then foo always returns |
740 | /// // Error::success(). |
741 | /// Error foo(bool DoFallibleOperation); |
742 | /// |
743 | /// cantFail(foo(false)); |
744 | /// @endcode |
745 | inline void cantFail(Error Err, const char *Msg = nullptr) { |
746 | if (Err) { |
747 | if (!Msg) |
748 | Msg = "Failure value returned from cantFail wrapped call"; |
749 | #ifndef NDEBUG |
750 | std::string Str; |
751 | raw_string_ostream OS(Str); |
752 | OS << Msg << "\n" << Err; |
753 | Msg = OS.str().c_str(); |
754 | #endif |
755 | llvm_unreachable(Msg)::llvm::llvm_unreachable_internal(Msg, "llvm/include/llvm/Support/Error.h" , 755); |
756 | } |
757 | } |
758 | |
759 | /// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and |
760 | /// returns the contained value. |
761 | /// |
762 | /// This function can be used to wrap calls to fallible functions ONLY when it |
763 | /// is known that the Error will always be a success value. E.g. |
764 | /// |
765 | /// @code{.cpp} |
766 | /// // foo only attempts the fallible operation if DoFallibleOperation is |
767 | /// // true. If DoFallibleOperation is false then foo always returns an int. |
768 | /// Expected<int> foo(bool DoFallibleOperation); |
769 | /// |
770 | /// int X = cantFail(foo(false)); |
771 | /// @endcode |
772 | template <typename T> |
773 | T cantFail(Expected<T> ValOrErr, const char *Msg = nullptr) { |
774 | if (ValOrErr) |
775 | return std::move(*ValOrErr); |
776 | else { |
777 | if (!Msg) |
778 | Msg = "Failure value returned from cantFail wrapped call"; |
779 | #ifndef NDEBUG |
780 | std::string Str; |
781 | raw_string_ostream OS(Str); |
782 | auto E = ValOrErr.takeError(); |
783 | OS << Msg << "\n" << E; |
784 | Msg = OS.str().c_str(); |
785 | #endif |
786 | llvm_unreachable(Msg)::llvm::llvm_unreachable_internal(Msg, "llvm/include/llvm/Support/Error.h" , 786); |
787 | } |
788 | } |
789 | |
790 | /// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and |
791 | /// returns the contained reference. |
792 | /// |
793 | /// This function can be used to wrap calls to fallible functions ONLY when it |
794 | /// is known that the Error will always be a success value. E.g. |
795 | /// |
796 | /// @code{.cpp} |
797 | /// // foo only attempts the fallible operation if DoFallibleOperation is |
798 | /// // true. If DoFallibleOperation is false then foo always returns a Bar&. |
799 | /// Expected<Bar&> foo(bool DoFallibleOperation); |
800 | /// |
801 | /// Bar &X = cantFail(foo(false)); |
802 | /// @endcode |
803 | template <typename T> |
804 | T& cantFail(Expected<T&> ValOrErr, const char *Msg = nullptr) { |
805 | if (ValOrErr) |
806 | return *ValOrErr; |
807 | else { |
808 | if (!Msg) |
809 | Msg = "Failure value returned from cantFail wrapped call"; |
810 | #ifndef NDEBUG |
811 | std::string Str; |
812 | raw_string_ostream OS(Str); |
813 | auto E = ValOrErr.takeError(); |
814 | OS << Msg << "\n" << E; |
815 | Msg = OS.str().c_str(); |
816 | #endif |
817 | llvm_unreachable(Msg)::llvm::llvm_unreachable_internal(Msg, "llvm/include/llvm/Support/Error.h" , 817); |
818 | } |
819 | } |
820 | |
821 | /// Helper for testing applicability of, and applying, handlers for |
822 | /// ErrorInfo types. |
823 | template <typename HandlerT> |
824 | class ErrorHandlerTraits |
825 | : public ErrorHandlerTraits< |
826 | decltype(&std::remove_reference_t<HandlerT>::operator())> {}; |
827 | |
828 | // Specialization functions of the form 'Error (const ErrT&)'. |
829 | template <typename ErrT> class ErrorHandlerTraits<Error (&)(ErrT &)> { |
830 | public: |
831 | static bool appliesTo(const ErrorInfoBase &E) { |
832 | return E.template isA<ErrT>(); |
833 | } |
834 | |
835 | template <typename HandlerT> |
836 | static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { |
837 | assert(appliesTo(*E) && "Applying incorrect handler")(static_cast <bool> (appliesTo(*E) && "Applying incorrect handler" ) ? void (0) : __assert_fail ("appliesTo(*E) && \"Applying incorrect handler\"" , "llvm/include/llvm/Support/Error.h", 837, __extension__ __PRETTY_FUNCTION__ )); |
838 | return H(static_cast<ErrT &>(*E)); |
839 | } |
840 | }; |
841 | |
842 | // Specialization functions of the form 'void (const ErrT&)'. |
843 | template <typename ErrT> class ErrorHandlerTraits<void (&)(ErrT &)> { |
844 | public: |
845 | static bool appliesTo(const ErrorInfoBase &E) { |
846 | return E.template isA<ErrT>(); |
847 | } |
848 | |
849 | template <typename HandlerT> |
850 | static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { |
851 | assert(appliesTo(*E) && "Applying incorrect handler")(static_cast <bool> (appliesTo(*E) && "Applying incorrect handler" ) ? void (0) : __assert_fail ("appliesTo(*E) && \"Applying incorrect handler\"" , "llvm/include/llvm/Support/Error.h", 851, __extension__ __PRETTY_FUNCTION__ )); |
852 | H(static_cast<ErrT &>(*E)); |
853 | return Error::success(); |
854 | } |
855 | }; |
856 | |
857 | /// Specialization for functions of the form 'Error (std::unique_ptr<ErrT>)'. |
858 | template <typename ErrT> |
859 | class ErrorHandlerTraits<Error (&)(std::unique_ptr<ErrT>)> { |
860 | public: |
861 | static bool appliesTo(const ErrorInfoBase &E) { |
862 | return E.template isA<ErrT>(); |
863 | } |
864 | |
865 | template <typename HandlerT> |
866 | static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { |
867 | assert(appliesTo(*E) && "Applying incorrect handler")(static_cast <bool> (appliesTo(*E) && "Applying incorrect handler" ) ? void (0) : __assert_fail ("appliesTo(*E) && \"Applying incorrect handler\"" , "llvm/include/llvm/Support/Error.h", 867, __extension__ __PRETTY_FUNCTION__ )); |
868 | std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release())); |
869 | return H(std::move(SubE)); |
870 | } |
871 | }; |
872 | |
873 | /// Specialization for functions of the form 'void (std::unique_ptr<ErrT>)'. |
874 | template <typename ErrT> |
875 | class ErrorHandlerTraits<void (&)(std::unique_ptr<ErrT>)> { |
876 | public: |
877 | static bool appliesTo(const ErrorInfoBase &E) { |
878 | return E.template isA<ErrT>(); |
879 | } |
880 | |
881 | template <typename HandlerT> |
882 | static Error apply(HandlerT &&H, std::unique_ptr<ErrorInfoBase> E) { |
883 | assert(appliesTo(*E) && "Applying incorrect handler")(static_cast <bool> (appliesTo(*E) && "Applying incorrect handler" ) ? void (0) : __assert_fail ("appliesTo(*E) && \"Applying incorrect handler\"" , "llvm/include/llvm/Support/Error.h", 883, __extension__ __PRETTY_FUNCTION__ )); |
884 | std::unique_ptr<ErrT> SubE(static_cast<ErrT *>(E.release())); |
885 | H(std::move(SubE)); |
886 | return Error::success(); |
887 | } |
888 | }; |
889 | |
890 | // Specialization for member functions of the form 'RetT (const ErrT&)'. |
891 | template <typename C, typename RetT, typename ErrT> |
892 | class ErrorHandlerTraits<RetT (C::*)(ErrT &)> |
893 | : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; |
894 | |
895 | // Specialization for member functions of the form 'RetT (const ErrT&) const'. |
896 | template <typename C, typename RetT, typename ErrT> |
897 | class ErrorHandlerTraits<RetT (C::*)(ErrT &) const> |
898 | : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; |
899 | |
900 | // Specialization for member functions of the form 'RetT (const ErrT&)'. |
901 | template <typename C, typename RetT, typename ErrT> |
902 | class ErrorHandlerTraits<RetT (C::*)(const ErrT &)> |
903 | : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; |
904 | |
905 | // Specialization for member functions of the form 'RetT (const ErrT&) const'. |
906 | template <typename C, typename RetT, typename ErrT> |
907 | class ErrorHandlerTraits<RetT (C::*)(const ErrT &) const> |
908 | : public ErrorHandlerTraits<RetT (&)(ErrT &)> {}; |
909 | |
910 | /// Specialization for member functions of the form |
911 | /// 'RetT (std::unique_ptr<ErrT>)'. |
912 | template <typename C, typename RetT, typename ErrT> |
913 | class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>)> |
914 | : public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {}; |
915 | |
916 | /// Specialization for member functions of the form |
917 | /// 'RetT (std::unique_ptr<ErrT>) const'. |
918 | template <typename C, typename RetT, typename ErrT> |
919 | class ErrorHandlerTraits<RetT (C::*)(std::unique_ptr<ErrT>) const> |
920 | : public ErrorHandlerTraits<RetT (&)(std::unique_ptr<ErrT>)> {}; |
921 | |
922 | inline Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload) { |
923 | return Error(std::move(Payload)); |
924 | } |
925 | |
926 | template <typename HandlerT, typename... HandlerTs> |
927 | Error handleErrorImpl(std::unique_ptr<ErrorInfoBase> Payload, |
928 | HandlerT &&Handler, HandlerTs &&... Handlers) { |
929 | if (ErrorHandlerTraits<HandlerT>::appliesTo(*Payload)) |
930 | return ErrorHandlerTraits<HandlerT>::apply(std::forward<HandlerT>(Handler), |
931 | std::move(Payload)); |
932 | return handleErrorImpl(std::move(Payload), |
933 | std::forward<HandlerTs>(Handlers)...); |
934 | } |
935 | |
936 | /// Pass the ErrorInfo(s) contained in E to their respective handlers. Any |
937 | /// unhandled errors (or Errors returned by handlers) are re-concatenated and |
938 | /// returned. |
939 | /// Because this function returns an error, its result must also be checked |
940 | /// or returned. If you intend to handle all errors use handleAllErrors |
941 | /// (which returns void, and will abort() on unhandled errors) instead. |
942 | template <typename... HandlerTs> |
943 | Error handleErrors(Error E, HandlerTs &&... Hs) { |
944 | if (!E) |
945 | return Error::success(); |
946 | |
947 | std::unique_ptr<ErrorInfoBase> Payload = E.takePayload(); |
948 | |
949 | if (Payload->isA<ErrorList>()) { |
950 | ErrorList &List = static_cast<ErrorList &>(*Payload); |
951 | Error R; |
952 | for (auto &P : List.Payloads) |
953 | R = ErrorList::join( |
954 | std::move(R), |
955 | handleErrorImpl(std::move(P), std::forward<HandlerTs>(Hs)...)); |
956 | return R; |
957 | } |
958 | |
959 | return handleErrorImpl(std::move(Payload), std::forward<HandlerTs>(Hs)...); |
960 | } |
961 | |
962 | /// Behaves the same as handleErrors, except that by contract all errors |
963 | /// *must* be handled by the given handlers (i.e. there must be no remaining |
964 | /// errors after running the handlers, or llvm_unreachable is called). |
965 | template <typename... HandlerTs> |
966 | void handleAllErrors(Error E, HandlerTs &&... Handlers) { |
967 | cantFail(handleErrors(std::move(E), std::forward<HandlerTs>(Handlers)...)); |
968 | } |
969 | |
970 | /// Check that E is a non-error, then drop it. |
971 | /// If E is an error, llvm_unreachable will be called. |
972 | inline void handleAllErrors(Error E) { |
973 | cantFail(std::move(E)); |
974 | } |
975 | |
976 | /// Handle any errors (if present) in an Expected<T>, then try a recovery path. |
977 | /// |
978 | /// If the incoming value is a success value it is returned unmodified. If it |
979 | /// is a failure value then it the contained error is passed to handleErrors. |
980 | /// If handleErrors is able to handle the error then the RecoveryPath functor |
981 | /// is called to supply the final result. If handleErrors is not able to |
982 | /// handle all errors then the unhandled errors are returned. |
983 | /// |
984 | /// This utility enables the follow pattern: |
985 | /// |
986 | /// @code{.cpp} |
987 | /// enum FooStrategy { Aggressive, Conservative }; |
988 | /// Expected<Foo> foo(FooStrategy S); |
989 | /// |
990 | /// auto ResultOrErr = |
991 | /// handleExpected( |
992 | /// foo(Aggressive), |
993 | /// []() { return foo(Conservative); }, |
994 | /// [](AggressiveStrategyError&) { |
995 | /// // Implicitly conusme this - we'll recover by using a conservative |
996 | /// // strategy. |
997 | /// }); |
998 | /// |
999 | /// @endcode |
1000 | template <typename T, typename RecoveryFtor, typename... HandlerTs> |
1001 | Expected<T> handleExpected(Expected<T> ValOrErr, RecoveryFtor &&RecoveryPath, |
1002 | HandlerTs &&... Handlers) { |
1003 | if (ValOrErr) |
1004 | return ValOrErr; |
1005 | |
1006 | if (auto Err = handleErrors(ValOrErr.takeError(), |
1007 | std::forward<HandlerTs>(Handlers)...)) |
1008 | return std::move(Err); |
1009 | |
1010 | return RecoveryPath(); |
1011 | } |
1012 | |
1013 | /// Log all errors (if any) in E to OS. If there are any errors, ErrorBanner |
1014 | /// will be printed before the first one is logged. A newline will be printed |
1015 | /// after each error. |
1016 | /// |
1017 | /// This function is compatible with the helpers from Support/WithColor.h. You |
1018 | /// can pass any of them as the OS. Please consider using them instead of |
1019 | /// including 'error: ' in the ErrorBanner. |
1020 | /// |
1021 | /// This is useful in the base level of your program to allow clean termination |
1022 | /// (allowing clean deallocation of resources, etc.), while reporting error |
1023 | /// information to the user. |
1024 | void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner = {}); |
1025 | |
1026 | /// Write all error messages (if any) in E to a string. The newline character |
1027 | /// is used to separate error messages. |
1028 | inline std::string toString(Error E) { |
1029 | SmallVector<std::string, 2> Errors; |
1030 | handleAllErrors(std::move(E), [&Errors](const ErrorInfoBase &EI) { |
1031 | Errors.push_back(EI.message()); |
1032 | }); |
1033 | return join(Errors.begin(), Errors.end(), "\n"); |
1034 | } |
1035 | |
1036 | /// Consume a Error without doing anything. This method should be used |
1037 | /// only where an error can be considered a reasonable and expected return |
1038 | /// value. |
1039 | /// |
1040 | /// Uses of this method are potentially indicative of design problems: If it's |
1041 | /// legitimate to do nothing while processing an "error", the error-producer |
1042 | /// might be more clearly refactored to return an std::optional<T>. |
1043 | inline void consumeError(Error Err) { |
1044 | handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {}); |
1045 | } |
1046 | |
1047 | /// Convert an Expected to an Optional without doing anything. This method |
1048 | /// should be used only where an error can be considered a reasonable and |
1049 | /// expected return value. |
1050 | /// |
1051 | /// Uses of this method are potentially indicative of problems: perhaps the |
1052 | /// error should be propagated further, or the error-producer should just |
1053 | /// return an Optional in the first place. |
1054 | template <typename T> std::optional<T> expectedToOptional(Expected<T> &&E) { |
1055 | if (E) |
1056 | return std::move(*E); |
1057 | consumeError(E.takeError()); |
1058 | return std::nullopt; |
1059 | } |
1060 | |
1061 | template <typename T> std::optional<T> expectedToStdOptional(Expected<T> &&E) { |
1062 | if (E) |
1063 | return std::move(*E); |
1064 | consumeError(E.takeError()); |
1065 | return std::nullopt; |
1066 | } |
1067 | |
1068 | /// Helper for converting an Error to a bool. |
1069 | /// |
1070 | /// This method returns true if Err is in an error state, or false if it is |
1071 | /// in a success state. Puts Err in a checked state in both cases (unlike |
1072 | /// Error::operator bool(), which only does this for success states). |
1073 | inline bool errorToBool(Error Err) { |
1074 | bool IsError = static_cast<bool>(Err); |
1075 | if (IsError) |
1076 | consumeError(std::move(Err)); |
1077 | return IsError; |
1078 | } |
1079 | |
1080 | /// Helper for Errors used as out-parameters. |
1081 | /// |
1082 | /// This helper is for use with the Error-as-out-parameter idiom, where an error |
1083 | /// is passed to a function or method by reference, rather than being returned. |
1084 | /// In such cases it is helpful to set the checked bit on entry to the function |
1085 | /// so that the error can be written to (unchecked Errors abort on assignment) |
1086 | /// and clear the checked bit on exit so that clients cannot accidentally forget |
1087 | /// to check the result. This helper performs these actions automatically using |
1088 | /// RAII: |
1089 | /// |
1090 | /// @code{.cpp} |
1091 | /// Result foo(Error &Err) { |
1092 | /// ErrorAsOutParameter ErrAsOutParam(&Err); // 'Checked' flag set |
1093 | /// // <body of foo> |
1094 | /// // <- 'Checked' flag auto-cleared when ErrAsOutParam is destructed. |
1095 | /// } |
1096 | /// @endcode |
1097 | /// |
1098 | /// ErrorAsOutParameter takes an Error* rather than Error& so that it can be |
1099 | /// used with optional Errors (Error pointers that are allowed to be null). If |
1100 | /// ErrorAsOutParameter took an Error reference, an instance would have to be |
1101 | /// created inside every condition that verified that Error was non-null. By |
1102 | /// taking an Error pointer we can just create one instance at the top of the |
1103 | /// function. |
1104 | class ErrorAsOutParameter { |
1105 | public: |
1106 | ErrorAsOutParameter(Error *Err) : Err(Err) { |
1107 | // Raise the checked bit if Err is success. |
1108 | if (Err) |
1109 | (void)!!*Err; |
1110 | } |
1111 | |
1112 | ~ErrorAsOutParameter() { |
1113 | // Clear the checked bit. |
1114 | if (Err && !*Err) |
1115 | *Err = Error::success(); |
1116 | } |
1117 | |
1118 | private: |
1119 | Error *Err; |
1120 | }; |
1121 | |
1122 | /// Helper for Expected<T>s used as out-parameters. |
1123 | /// |
1124 | /// See ErrorAsOutParameter. |
1125 | template <typename T> |
1126 | class ExpectedAsOutParameter { |
1127 | public: |
1128 | ExpectedAsOutParameter(Expected<T> *ValOrErr) |
1129 | : ValOrErr(ValOrErr) { |
1130 | if (ValOrErr) |
1131 | (void)!!*ValOrErr; |
1132 | } |
1133 | |
1134 | ~ExpectedAsOutParameter() { |
1135 | if (ValOrErr) |
1136 | ValOrErr->setUnchecked(); |
1137 | } |
1138 | |
1139 | private: |
1140 | Expected<T> *ValOrErr; |
1141 | }; |
1142 | |
1143 | /// This class wraps a std::error_code in a Error. |
1144 | /// |
1145 | /// This is useful if you're writing an interface that returns a Error |
1146 | /// (or Expected) and you want to call code that still returns |
1147 | /// std::error_codes. |
1148 | class ECError : public ErrorInfo<ECError> { |
1149 | friend Error errorCodeToError(std::error_code); |
1150 | |
1151 | void anchor() override; |
1152 | |
1153 | public: |
1154 | void setErrorCode(std::error_code EC) { this->EC = EC; } |
1155 | std::error_code convertToErrorCode() const override { return EC; } |
1156 | void log(raw_ostream &OS) const override { OS << EC.message(); } |
1157 | |
1158 | // Used by ErrorInfo::classID. |
1159 | static char ID; |
1160 | |
1161 | protected: |
1162 | ECError() = default; |
1163 | ECError(std::error_code EC) : EC(EC) {} |
1164 | |
1165 | std::error_code EC; |
1166 | }; |
1167 | |
1168 | /// The value returned by this function can be returned from convertToErrorCode |
1169 | /// for Error values where no sensible translation to std::error_code exists. |
1170 | /// It should only be used in this situation, and should never be used where a |
1171 | /// sensible conversion to std::error_code is available, as attempts to convert |
1172 | /// to/from this error will result in a fatal error. (i.e. it is a programmatic |
1173 | /// error to try to convert such a value). |
1174 | std::error_code inconvertibleErrorCode(); |
1175 | |
1176 | /// Helper for converting an std::error_code to a Error. |
1177 | Error errorCodeToError(std::error_code EC); |
1178 | |
1179 | /// Helper for converting an ECError to a std::error_code. |
1180 | /// |
1181 | /// This method requires that Err be Error() or an ECError, otherwise it |
1182 | /// will trigger a call to abort(). |
1183 | std::error_code errorToErrorCode(Error Err); |
1184 | |
1185 | /// Convert an ErrorOr<T> to an Expected<T>. |
1186 | template <typename T> Expected<T> errorOrToExpected(ErrorOr<T> &&EO) { |
1187 | if (auto EC = EO.getError()) |
1188 | return errorCodeToError(EC); |
1189 | return std::move(*EO); |
1190 | } |
1191 | |
1192 | /// Convert an Expected<T> to an ErrorOr<T>. |
1193 | template <typename T> ErrorOr<T> expectedToErrorOr(Expected<T> &&E) { |
1194 | if (auto Err = E.takeError()) |
1195 | return errorToErrorCode(std::move(Err)); |
1196 | return std::move(*E); |
1197 | } |
1198 | |
1199 | /// This class wraps a string in an Error. |
1200 | /// |
1201 | /// StringError is useful in cases where the client is not expected to be able |
1202 | /// to consume the specific error message programmatically (for example, if the |
1203 | /// error message is to be presented to the user). |
1204 | /// |
1205 | /// StringError can also be used when additional information is to be printed |
1206 | /// along with a error_code message. Depending on the constructor called, this |
1207 | /// class can either display: |
1208 | /// 1. the error_code message (ECError behavior) |
1209 | /// 2. a string |
1210 | /// 3. the error_code message and a string |
1211 | /// |
1212 | /// These behaviors are useful when subtyping is required; for example, when a |
1213 | /// specific library needs an explicit error type. In the example below, |
1214 | /// PDBError is derived from StringError: |
1215 | /// |
1216 | /// @code{.cpp} |
1217 | /// Expected<int> foo() { |
1218 | /// return llvm::make_error<PDBError>(pdb_error_code::dia_failed_loading, |
1219 | /// "Additional information"); |
1220 | /// } |
1221 | /// @endcode |
1222 | /// |
1223 | class StringError : public ErrorInfo<StringError> { |
1224 | public: |
1225 | static char ID; |
1226 | |
1227 | // Prints EC + S and converts to EC |
1228 | StringError(std::error_code EC, const Twine &S = Twine()); |
1229 | |
1230 | // Prints S and converts to EC |
1231 | StringError(const Twine &S, std::error_code EC); |
1232 | |
1233 | void log(raw_ostream &OS) const override; |
1234 | std::error_code convertToErrorCode() const override; |
1235 | |
1236 | const std::string &getMessage() const { return Msg; } |
1237 | |
1238 | private: |
1239 | std::string Msg; |
1240 | std::error_code EC; |
1241 | const bool PrintMsgOnly = false; |
1242 | }; |
1243 | |
1244 | /// Create formatted StringError object. |
1245 | template <typename... Ts> |
1246 | inline Error createStringError(std::error_code EC, char const *Fmt, |
1247 | const Ts &... Vals) { |
1248 | std::string Buffer; |
1249 | raw_string_ostream Stream(Buffer); |
1250 | Stream << format(Fmt, Vals...); |
1251 | return make_error<StringError>(Stream.str(), EC); |
1252 | } |
1253 | |
1254 | Error createStringError(std::error_code EC, char const *Msg); |
1255 | |
1256 | inline Error createStringError(std::error_code EC, const Twine &S) { |
1257 | return createStringError(EC, S.str().c_str()); |
1258 | } |
1259 | |
1260 | template <typename... Ts> |
1261 | inline Error createStringError(std::errc EC, char const *Fmt, |
1262 | const Ts &... Vals) { |
1263 | return createStringError(std::make_error_code(EC), Fmt, Vals...); |
1264 | } |
1265 | |
1266 | /// This class wraps a filename and another Error. |
1267 | /// |
1268 | /// In some cases, an error needs to live along a 'source' name, in order to |
1269 | /// show more detailed information to the user. |
1270 | class FileError final : public ErrorInfo<FileError> { |
1271 | |
1272 | friend Error createFileError(const Twine &, Error); |
1273 | friend Error createFileError(const Twine &, size_t, Error); |
1274 | |
1275 | public: |
1276 | void log(raw_ostream &OS) const override { |
1277 | assert(Err && "Trying to log after takeError().")(static_cast <bool> (Err && "Trying to log after takeError()." ) ? void (0) : __assert_fail ("Err && \"Trying to log after takeError().\"" , "llvm/include/llvm/Support/Error.h", 1277, __extension__ __PRETTY_FUNCTION__ )); |
1278 | OS << "'" << FileName << "': "; |
1279 | if (Line) |
1280 | OS << "line " << *Line << ": "; |
1281 | Err->log(OS); |
1282 | } |
1283 | |
1284 | std::string messageWithoutFileInfo() const { |
1285 | std::string Msg; |
1286 | raw_string_ostream OS(Msg); |
1287 | Err->log(OS); |
1288 | return OS.str(); |
1289 | } |
1290 | |
1291 | StringRef getFileName() const { return FileName; } |
1292 | |
1293 | Error takeError() { return Error(std::move(Err)); } |
1294 | |
1295 | std::error_code convertToErrorCode() const override; |
1296 | |
1297 | // Used by ErrorInfo::classID. |
1298 | static char ID; |
1299 | |
1300 | private: |
1301 | FileError(const Twine &F, std::optional<size_t> LineNum, |
1302 | std::unique_ptr<ErrorInfoBase> E) { |
1303 | assert(E && "Cannot create FileError from Error success value.")(static_cast <bool> (E && "Cannot create FileError from Error success value." ) ? void (0) : __assert_fail ("E && \"Cannot create FileError from Error success value.\"" , "llvm/include/llvm/Support/Error.h", 1303, __extension__ __PRETTY_FUNCTION__ )); |
1304 | FileName = F.str(); |
1305 | Err = std::move(E); |
1306 | Line = std::move(LineNum); |
1307 | } |
1308 | |
1309 | static Error build(const Twine &F, std::optional<size_t> Line, Error E) { |
1310 | std::unique_ptr<ErrorInfoBase> Payload; |
1311 | handleAllErrors(std::move(E), |
1312 | [&](std::unique_ptr<ErrorInfoBase> EIB) -> Error { |
1313 | Payload = std::move(EIB); |
1314 | return Error::success(); |
1315 | }); |
1316 | return Error( |
1317 | std::unique_ptr<FileError>(new FileError(F, Line, std::move(Payload)))); |
1318 | } |
1319 | |
1320 | std::string FileName; |
1321 | std::optional<size_t> Line; |
1322 | std::unique_ptr<ErrorInfoBase> Err; |
1323 | }; |
1324 | |
1325 | /// Concatenate a source file path and/or name with an Error. The resulting |
1326 | /// Error is unchecked. |
1327 | inline Error createFileError(const Twine &F, Error E) { |
1328 | return FileError::build(F, std::optional<size_t>(), std::move(E)); |
1329 | } |
1330 | |
1331 | /// Concatenate a source file path and/or name with line number and an Error. |
1332 | /// The resulting Error is unchecked. |
1333 | inline Error createFileError(const Twine &F, size_t Line, Error E) { |
1334 | return FileError::build(F, std::optional<size_t>(Line), std::move(E)); |
1335 | } |
1336 | |
1337 | /// Concatenate a source file path and/or name with a std::error_code |
1338 | /// to form an Error object. |
1339 | inline Error createFileError(const Twine &F, std::error_code EC) { |
1340 | return createFileError(F, errorCodeToError(EC)); |
1341 | } |
1342 | |
1343 | /// Concatenate a source file path and/or name with line number and |
1344 | /// std::error_code to form an Error object. |
1345 | inline Error createFileError(const Twine &F, size_t Line, std::error_code EC) { |
1346 | return createFileError(F, Line, errorCodeToError(EC)); |
1347 | } |
1348 | |
1349 | Error createFileError(const Twine &F, ErrorSuccess) = delete; |
1350 | |
1351 | /// Helper for check-and-exit error handling. |
1352 | /// |
1353 | /// For tool use only. NOT FOR USE IN LIBRARY CODE. |
1354 | /// |
1355 | class ExitOnError { |
1356 | public: |
1357 | /// Create an error on exit helper. |
1358 | ExitOnError(std::string Banner = "", int DefaultErrorExitCode = 1) |
1359 | : Banner(std::move(Banner)), |
1360 | GetExitCode([=](const Error &) { return DefaultErrorExitCode; }) {} |
1361 | |
1362 | /// Set the banner string for any errors caught by operator(). |
1363 | void setBanner(std::string Banner) { this->Banner = std::move(Banner); } |
1364 | |
1365 | /// Set the exit-code mapper function. |
1366 | void setExitCodeMapper(std::function<int(const Error &)> GetExitCode) { |
1367 | this->GetExitCode = std::move(GetExitCode); |
1368 | } |
1369 | |
1370 | /// Check Err. If it's in a failure state log the error(s) and exit. |
1371 | void operator()(Error Err) const { checkError(std::move(Err)); } |
1372 | |
1373 | /// Check E. If it's in a success state then return the contained value. If |
1374 | /// it's in a failure state log the error(s) and exit. |
1375 | template <typename T> T operator()(Expected<T> &&E) const { |
1376 | checkError(E.takeError()); |
1377 | return std::move(*E); |
1378 | } |
1379 | |
1380 | /// Check E. If it's in a success state then return the contained reference. If |
1381 | /// it's in a failure state log the error(s) and exit. |
1382 | template <typename T> T& operator()(Expected<T&> &&E) const { |
1383 | checkError(E.takeError()); |
1384 | return *E; |
1385 | } |
1386 | |
1387 | private: |
1388 | void checkError(Error Err) const { |
1389 | if (Err) { |
1390 | int ExitCode = GetExitCode(Err); |
1391 | logAllUnhandledErrors(std::move(Err), errs(), Banner); |
1392 | exit(ExitCode); |
1393 | } |
1394 | } |
1395 | |
1396 | std::string Banner; |
1397 | std::function<int(const Error &)> GetExitCode; |
1398 | }; |
1399 | |
1400 | /// Conversion from Error to LLVMErrorRef for C error bindings. |
1401 | inline LLVMErrorRef wrap(Error Err) { |
1402 | return reinterpret_cast<LLVMErrorRef>(Err.takePayload().release()); |
1403 | } |
1404 | |
1405 | /// Conversion from LLVMErrorRef to Error for C error bindings. |
1406 | inline Error unwrap(LLVMErrorRef ErrRef) { |
1407 | return Error(std::unique_ptr<ErrorInfoBase>( |
1408 | reinterpret_cast<ErrorInfoBase *>(ErrRef))); |
1409 | } |
1410 | |
1411 | } // end namespace llvm |
1412 | |
1413 | #endif // LLVM_SUPPORT_ERROR_H |