1 //===- BytesOutputStyle.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 "BytesOutputStyle.h" 10 11 #include "FormatUtil.h" 12 #include "StreamUtil.h" 13 #include "llvm-pdbutil.h" 14 15 #include "llvm/DebugInfo/CodeView/Formatters.h" 16 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" 17 #include "llvm/DebugInfo/MSF/MSFCommon.h" 18 #include "llvm/DebugInfo/MSF/MappedBlockStream.h" 19 #include "llvm/DebugInfo/PDB/Native/DbiStream.h" 20 #include "llvm/DebugInfo/PDB/Native/InfoStream.h" 21 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" 22 #include "llvm/DebugInfo/PDB/Native/PDBFile.h" 23 #include "llvm/DebugInfo/PDB/Native/RawError.h" 24 #include "llvm/DebugInfo/PDB/Native/TpiStream.h" 25 #include "llvm/Support/BinaryStreamReader.h" 26 #include "llvm/Support/FormatAdapters.h" 27 #include "llvm/Support/FormatVariadic.h" 28 29 using namespace llvm; 30 using namespace llvm::codeview; 31 using namespace llvm::msf; 32 using namespace llvm::pdb; 33 34 namespace { 35 struct StreamSpec { 36 uint32_t SI = 0; 37 uint32_t Begin = 0; 38 uint32_t Size = 0; 39 }; 40 } // namespace 41 42 static Expected<StreamSpec> parseStreamSpec(StringRef Str) { 43 StreamSpec Result; 44 if (Str.consumeInteger(0, Result.SI)) 45 return make_error<RawError>(raw_error_code::invalid_format, 46 "Invalid Stream Specification"); 47 if (Str.consume_front(":")) { 48 if (Str.consumeInteger(0, Result.Begin)) 49 return make_error<RawError>(raw_error_code::invalid_format, 50 "Invalid Stream Specification"); 51 } 52 if (Str.consume_front("@")) { 53 if (Str.consumeInteger(0, Result.Size)) 54 return make_error<RawError>(raw_error_code::invalid_format, 55 "Invalid Stream Specification"); 56 } 57 58 if (!Str.empty()) 59 return make_error<RawError>(raw_error_code::invalid_format, 60 "Invalid Stream Specification"); 61 return Result; 62 } 63 64 static SmallVector<StreamSpec, 2> parseStreamSpecs(LinePrinter &P) { 65 SmallVector<StreamSpec, 2> Result; 66 67 for (auto &Str : opts::bytes::DumpStreamData) { 68 auto ESS = parseStreamSpec(Str); 69 if (!ESS) { 70 P.formatLine("Error parsing stream spec {0}: {1}", Str, 71 toString(ESS.takeError())); 72 continue; 73 } 74 Result.push_back(*ESS); 75 } 76 return Result; 77 } 78 79 static void printHeader(LinePrinter &P, const Twine &S) { 80 P.NewLine(); 81 P.formatLine("{0,=60}", S); 82 P.formatLine("{0}", fmt_repeat('=', 60)); 83 } 84 85 BytesOutputStyle::BytesOutputStyle(PDBFile &File) 86 : File(File), P(2, false, outs()) {} 87 88 Error BytesOutputStyle::dump() { 89 90 if (opts::bytes::DumpBlockRange.hasValue()) { 91 auto &R = *opts::bytes::DumpBlockRange; 92 uint32_t Max = R.Max.getValueOr(R.Min); 93 94 if (Max < R.Min) 95 return make_error<StringError>( 96 "Invalid block range specified. Max < Min", 97 inconvertibleErrorCode()); 98 if (Max >= File.getBlockCount()) 99 return make_error<StringError>( 100 "Invalid block range specified. Requested block out of bounds", 101 inconvertibleErrorCode()); 102 103 dumpBlockRanges(R.Min, Max); 104 P.NewLine(); 105 } 106 107 if (opts::bytes::DumpByteRange.hasValue()) { 108 auto &R = *opts::bytes::DumpByteRange; 109 uint32_t Max = R.Max.getValueOr(File.getFileSize()); 110 111 if (Max < R.Min) 112 return make_error<StringError>("Invalid byte range specified. Max < Min", 113 inconvertibleErrorCode()); 114 if (Max >= File.getFileSize()) 115 return make_error<StringError>( 116 "Invalid byte range specified. Requested byte larger than file size", 117 inconvertibleErrorCode()); 118 119 dumpByteRanges(R.Min, Max); 120 P.NewLine(); 121 } 122 123 if (opts::bytes::Fpm) { 124 dumpFpm(); 125 P.NewLine(); 126 } 127 128 if (!opts::bytes::DumpStreamData.empty()) { 129 dumpStreamBytes(); 130 P.NewLine(); 131 } 132 133 if (opts::bytes::NameMap) { 134 dumpNameMap(); 135 P.NewLine(); 136 } 137 138 if (opts::bytes::SectionContributions) { 139 dumpSectionContributions(); 140 P.NewLine(); 141 } 142 143 if (opts::bytes::SectionMap) { 144 dumpSectionMap(); 145 P.NewLine(); 146 } 147 148 if (opts::bytes::ModuleInfos) { 149 dumpModuleInfos(); 150 P.NewLine(); 151 } 152 153 if (opts::bytes::FileInfo) { 154 dumpFileInfo(); 155 P.NewLine(); 156 } 157 158 if (opts::bytes::TypeServerMap) { 159 dumpTypeServerMap(); 160 P.NewLine(); 161 } 162 163 if (opts::bytes::ECData) { 164 dumpECData(); 165 P.NewLine(); 166 } 167 168 if (!opts::bytes::TypeIndex.empty()) { 169 dumpTypeIndex(StreamTPI, opts::bytes::TypeIndex); 170 P.NewLine(); 171 } 172 173 if (!opts::bytes::IdIndex.empty()) { 174 dumpTypeIndex(StreamIPI, opts::bytes::IdIndex); 175 P.NewLine(); 176 } 177 178 if (opts::bytes::ModuleSyms) { 179 dumpModuleSyms(); 180 P.NewLine(); 181 } 182 183 if (opts::bytes::ModuleC11) { 184 dumpModuleC11(); 185 P.NewLine(); 186 } 187 188 if (opts::bytes::ModuleC13) { 189 dumpModuleC13(); 190 P.NewLine(); 191 } 192 193 return Error::success(); 194 } 195 196 void BytesOutputStyle::dumpNameMap() { 197 printHeader(P, "Named Stream Map"); 198 199 AutoIndent Indent(P); 200 201 auto &InfoS = Err(File.getPDBInfoStream()); 202 BinarySubstreamRef NS = InfoS.getNamedStreamsBuffer(); 203 auto Layout = File.getStreamLayout(StreamPDB); 204 P.formatMsfStreamData("Named Stream Map", File, Layout, NS); 205 } 206 207 void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) { 208 printHeader(P, "MSF Blocks"); 209 210 AutoIndent Indent(P); 211 for (uint32_t I = Min; I <= Max; ++I) { 212 uint64_t Base = I; 213 Base *= File.getBlockSize(); 214 215 auto ExpectedData = File.getBlockData(I, File.getBlockSize()); 216 if (!ExpectedData) { 217 P.formatLine("Could not get block {0}. Reason = {1}", I, 218 toString(ExpectedData.takeError())); 219 continue; 220 } 221 std::string Label = formatv("Block {0}", I).str(); 222 P.formatBinary(Label, *ExpectedData, Base, 0); 223 } 224 } 225 226 void BytesOutputStyle::dumpSectionContributions() { 227 printHeader(P, "Section Contributions"); 228 229 AutoIndent Indent(P); 230 231 auto &DbiS = Err(File.getPDBDbiStream()); 232 BinarySubstreamRef NS = DbiS.getSectionContributionData(); 233 auto Layout = File.getStreamLayout(StreamDBI); 234 P.formatMsfStreamData("Section Contributions", File, Layout, NS); 235 } 236 237 void BytesOutputStyle::dumpSectionMap() { 238 printHeader(P, "Section Map"); 239 240 AutoIndent Indent(P); 241 242 auto &DbiS = Err(File.getPDBDbiStream()); 243 BinarySubstreamRef NS = DbiS.getSecMapSubstreamData(); 244 auto Layout = File.getStreamLayout(StreamDBI); 245 P.formatMsfStreamData("Section Map", File, Layout, NS); 246 } 247 248 void BytesOutputStyle::dumpModuleInfos() { 249 printHeader(P, "Module Infos"); 250 251 AutoIndent Indent(P); 252 253 auto &DbiS = Err(File.getPDBDbiStream()); 254 BinarySubstreamRef NS = DbiS.getModiSubstreamData(); 255 auto Layout = File.getStreamLayout(StreamDBI); 256 P.formatMsfStreamData("Module Infos", File, Layout, NS); 257 } 258 259 void BytesOutputStyle::dumpFileInfo() { 260 printHeader(P, "File Info"); 261 262 AutoIndent Indent(P); 263 264 auto &DbiS = Err(File.getPDBDbiStream()); 265 BinarySubstreamRef NS = DbiS.getFileInfoSubstreamData(); 266 auto Layout = File.getStreamLayout(StreamDBI); 267 P.formatMsfStreamData("File Info", File, Layout, NS); 268 } 269 270 void BytesOutputStyle::dumpTypeServerMap() { 271 printHeader(P, "Type Server Map"); 272 273 AutoIndent Indent(P); 274 275 auto &DbiS = Err(File.getPDBDbiStream()); 276 BinarySubstreamRef NS = DbiS.getTypeServerMapSubstreamData(); 277 auto Layout = File.getStreamLayout(StreamDBI); 278 P.formatMsfStreamData("Type Server Map", File, Layout, NS); 279 } 280 281 void BytesOutputStyle::dumpECData() { 282 printHeader(P, "Edit and Continue Data"); 283 284 AutoIndent Indent(P); 285 286 auto &DbiS = Err(File.getPDBDbiStream()); 287 BinarySubstreamRef NS = DbiS.getECSubstreamData(); 288 auto Layout = File.getStreamLayout(StreamDBI); 289 P.formatMsfStreamData("Edit and Continue Data", File, Layout, NS); 290 } 291 292 void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx, 293 ArrayRef<uint32_t> Indices) { 294 assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); 295 assert(!Indices.empty()); 296 297 bool IsTpi = (StreamIdx == StreamTPI); 298 299 StringRef Label = IsTpi ? "Type (TPI) Records" : "Index (IPI) Records"; 300 printHeader(P, Label); 301 auto &Stream = Err(IsTpi ? File.getPDBTpiStream() : File.getPDBIpiStream()); 302 303 AutoIndent Indent(P); 304 305 auto Substream = Stream.getTypeRecordsSubstream(); 306 auto &Types = Err(initializeTypes(StreamIdx)); 307 auto Layout = File.getStreamLayout(StreamIdx); 308 for (const auto &Id : Indices) { 309 TypeIndex TI(Id); 310 if (TI.toArrayIndex() >= Types.capacity()) { 311 P.formatLine("Error: TypeIndex {0} does not exist", TI); 312 continue; 313 } 314 315 auto Type = Types.getType(TI); 316 uint32_t Offset = Types.getOffsetOfType(TI); 317 auto OneType = Substream.slice(Offset, Type.length()); 318 P.formatMsfStreamData(formatv("Type {0}", TI).str(), File, Layout, OneType); 319 } 320 } 321 322 template <typename CallbackT> 323 static void iterateOneModule(PDBFile &File, LinePrinter &P, 324 const DbiModuleList &Modules, uint32_t I, 325 uint32_t Digits, uint32_t IndentLevel, 326 CallbackT Callback) { 327 if (I >= Modules.getModuleCount()) { 328 P.formatLine("Mod {0:4} | Invalid module index ", 329 fmt_align(I, AlignStyle::Right, std::max(Digits, 4U))); 330 return; 331 } 332 333 auto Modi = Modules.getModuleDescriptor(I); 334 P.formatLine("Mod {0:4} | `{1}`: ", 335 fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)), 336 Modi.getModuleName()); 337 338 uint16_t ModiStream = Modi.getModuleStreamIndex(); 339 AutoIndent Indent2(P, IndentLevel); 340 if (ModiStream == kInvalidStreamIndex) 341 return; 342 343 auto ModStreamData = File.createIndexedStream(ModiStream); 344 ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData)); 345 if (auto EC = ModStream.reload()) { 346 P.formatLine("Could not parse debug information."); 347 return; 348 } 349 auto Layout = File.getStreamLayout(ModiStream); 350 Callback(I, ModStream, Layout); 351 } 352 353 template <typename CallbackT> 354 static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel, 355 CallbackT Callback) { 356 AutoIndent Indent(P); 357 if (!File.hasPDBDbiStream()) { 358 P.formatLine("DBI Stream not present"); 359 return; 360 } 361 362 ExitOnError Err("Unexpected error processing modules"); 363 364 auto &Stream = Err(File.getPDBDbiStream()); 365 366 const DbiModuleList &Modules = Stream.modules(); 367 368 if (opts::bytes::ModuleIndex.getNumOccurrences() > 0) { 369 iterateOneModule(File, P, Modules, opts::bytes::ModuleIndex, 1, IndentLevel, 370 Callback); 371 } else { 372 uint32_t Count = Modules.getModuleCount(); 373 uint32_t Digits = NumDigits(Count); 374 for (uint32_t I = 0; I < Count; ++I) { 375 iterateOneModule(File, P, Modules, I, Digits, IndentLevel, Callback); 376 } 377 } 378 } 379 380 void BytesOutputStyle::dumpModuleSyms() { 381 printHeader(P, "Module Symbols"); 382 383 AutoIndent Indent(P); 384 385 iterateModules(File, P, 2, 386 [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, 387 const MSFStreamLayout &Layout) { 388 auto Symbols = Stream.getSymbolsSubstream(); 389 P.formatMsfStreamData("Symbols", File, Layout, Symbols); 390 }); 391 } 392 393 void BytesOutputStyle::dumpModuleC11() { 394 printHeader(P, "C11 Debug Chunks"); 395 396 AutoIndent Indent(P); 397 398 iterateModules(File, P, 2, 399 [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, 400 const MSFStreamLayout &Layout) { 401 auto Chunks = Stream.getC11LinesSubstream(); 402 P.formatMsfStreamData("C11 Debug Chunks", File, Layout, 403 Chunks); 404 }); 405 } 406 407 void BytesOutputStyle::dumpModuleC13() { 408 printHeader(P, "Debug Chunks"); 409 410 AutoIndent Indent(P); 411 412 iterateModules( 413 File, P, 2, 414 [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, 415 const MSFStreamLayout &Layout) { 416 auto Chunks = Stream.getC13LinesSubstream(); 417 if (opts::bytes::SplitChunks) { 418 for (const auto &SS : Stream.subsections()) { 419 BinarySubstreamRef ThisChunk; 420 std::tie(ThisChunk, Chunks) = Chunks.split(SS.getRecordLength()); 421 P.formatMsfStreamData(formatChunkKind(SS.kind()), File, Layout, 422 ThisChunk); 423 } 424 } else { 425 P.formatMsfStreamData("Debug Chunks", File, Layout, Chunks); 426 } 427 }); 428 } 429 430 void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) { 431 printHeader(P, "MSF Bytes"); 432 433 AutoIndent Indent(P); 434 435 BinaryStreamReader Reader(File.getMsfBuffer()); 436 ArrayRef<uint8_t> Data; 437 consumeError(Reader.skip(Min)); 438 uint32_t Size = Max - Min + 1; 439 auto EC = Reader.readBytes(Data, Size); 440 assert(!EC); 441 consumeError(std::move(EC)); 442 P.formatBinary("Bytes", Data, Min); 443 } 444 445 Expected<codeview::LazyRandomTypeCollection &> 446 BytesOutputStyle::initializeTypes(uint32_t StreamIdx) { 447 auto &TypeCollection = (StreamIdx == StreamTPI) ? TpiTypes : IpiTypes; 448 if (TypeCollection) 449 return *TypeCollection; 450 451 auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream() 452 : File.getPDBIpiStream(); 453 if (!Tpi) 454 return Tpi.takeError(); 455 456 auto &Types = Tpi->typeArray(); 457 uint32_t Count = Tpi->getNumTypeRecords(); 458 auto Offsets = Tpi->getTypeIndexOffsets(); 459 TypeCollection = 460 std::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets); 461 462 return *TypeCollection; 463 } 464 465 void BytesOutputStyle::dumpFpm() { 466 printHeader(P, "Free Page Map"); 467 468 msf::MSFStreamLayout FpmLayout = File.getFpmStreamLayout(); 469 P.formatMsfStreamBlocks(File, FpmLayout); 470 } 471 472 void BytesOutputStyle::dumpStreamBytes() { 473 if (StreamPurposes.empty()) 474 discoverStreamPurposes(File, StreamPurposes); 475 476 printHeader(P, "Stream Data"); 477 ExitOnError Err("Unexpected error reading stream data"); 478 479 auto Specs = parseStreamSpecs(P); 480 481 for (const auto &Spec : Specs) { 482 AutoIndent Indent(P); 483 if (Spec.SI >= StreamPurposes.size()) { 484 P.formatLine("Stream {0}: Not present", Spec.SI); 485 continue; 486 } 487 P.formatMsfStreamData("Data", File, Spec.SI, 488 StreamPurposes[Spec.SI].getShortName(), Spec.Begin, 489 Spec.Size); 490 } 491 } 492