1 //===- BytesOutputStyle.cpp ----------------------------------- *- C++ --*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "BytesOutputStyle.h" 11 12 #include "FormatUtil.h" 13 #include "StreamUtil.h" 14 #include "llvm-pdbutil.h" 15 16 #include "llvm/DebugInfo/CodeView/Formatters.h" 17 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.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::DumpStreamData.empty()) { 124 dumpStreamBytes(); 125 P.NewLine(); 126 } 127 128 if (opts::bytes::NameMap) { 129 dumpNameMap(); 130 P.NewLine(); 131 } 132 133 if (opts::bytes::SectionContributions) { 134 dumpSectionContributions(); 135 P.NewLine(); 136 } 137 138 if (opts::bytes::SectionMap) { 139 dumpSectionMap(); 140 P.NewLine(); 141 } 142 143 if (opts::bytes::ModuleInfos) { 144 dumpModuleInfos(); 145 P.NewLine(); 146 } 147 148 if (opts::bytes::FileInfo) { 149 dumpFileInfo(); 150 P.NewLine(); 151 } 152 153 if (opts::bytes::TypeServerMap) { 154 dumpTypeServerMap(); 155 P.NewLine(); 156 } 157 158 if (opts::bytes::ECData) { 159 dumpECData(); 160 P.NewLine(); 161 } 162 163 if (!opts::bytes::TypeIndex.empty()) { 164 dumpTypeIndex(StreamTPI, opts::bytes::TypeIndex); 165 P.NewLine(); 166 } 167 168 if (!opts::bytes::IdIndex.empty()) { 169 dumpTypeIndex(StreamIPI, opts::bytes::IdIndex); 170 P.NewLine(); 171 } 172 173 if (opts::bytes::ModuleSyms) { 174 dumpModuleSyms(); 175 P.NewLine(); 176 } 177 178 if (opts::bytes::ModuleC11) { 179 dumpModuleC11(); 180 P.NewLine(); 181 } 182 183 if (opts::bytes::ModuleC13) { 184 dumpModuleC13(); 185 P.NewLine(); 186 } 187 188 return Error::success(); 189 } 190 191 void BytesOutputStyle::dumpNameMap() { 192 printHeader(P, "Named Stream Map"); 193 194 AutoIndent Indent(P); 195 196 auto &InfoS = Err(File.getPDBInfoStream()); 197 BinarySubstreamRef NS = InfoS.getNamedStreamsBuffer(); 198 auto Layout = File.getStreamLayout(StreamPDB); 199 P.formatMsfStreamData("Named Stream Map", File, Layout, NS); 200 } 201 202 void BytesOutputStyle::dumpBlockRanges(uint32_t Min, uint32_t Max) { 203 printHeader(P, "MSF Blocks"); 204 205 AutoIndent Indent(P); 206 for (uint32_t I = Min; I <= Max; ++I) { 207 uint64_t Base = I; 208 Base *= File.getBlockSize(); 209 210 auto ExpectedData = File.getBlockData(I, File.getBlockSize()); 211 if (!ExpectedData) { 212 P.formatLine("Could not get block {0}. Reason = {1}", I, 213 toString(ExpectedData.takeError())); 214 continue; 215 } 216 std::string Label = formatv("Block {0}", I).str(); 217 P.formatBinary(Label, *ExpectedData, Base, 0); 218 } 219 } 220 221 void BytesOutputStyle::dumpSectionContributions() { 222 printHeader(P, "Section Contributions"); 223 224 AutoIndent Indent(P); 225 226 auto &DbiS = Err(File.getPDBDbiStream()); 227 BinarySubstreamRef NS = DbiS.getSectionContributionData(); 228 auto Layout = File.getStreamLayout(StreamDBI); 229 P.formatMsfStreamData("Section Contributions", File, Layout, NS); 230 } 231 232 void BytesOutputStyle::dumpSectionMap() { 233 printHeader(P, "Section Map"); 234 235 AutoIndent Indent(P); 236 237 auto &DbiS = Err(File.getPDBDbiStream()); 238 BinarySubstreamRef NS = DbiS.getSecMapSubstreamData(); 239 auto Layout = File.getStreamLayout(StreamDBI); 240 P.formatMsfStreamData("Section Map", File, Layout, NS); 241 } 242 243 void BytesOutputStyle::dumpModuleInfos() { 244 printHeader(P, "Module Infos"); 245 246 AutoIndent Indent(P); 247 248 auto &DbiS = Err(File.getPDBDbiStream()); 249 BinarySubstreamRef NS = DbiS.getModiSubstreamData(); 250 auto Layout = File.getStreamLayout(StreamDBI); 251 P.formatMsfStreamData("Module Infos", File, Layout, NS); 252 } 253 254 void BytesOutputStyle::dumpFileInfo() { 255 printHeader(P, "File Info"); 256 257 AutoIndent Indent(P); 258 259 auto &DbiS = Err(File.getPDBDbiStream()); 260 BinarySubstreamRef NS = DbiS.getFileInfoSubstreamData(); 261 auto Layout = File.getStreamLayout(StreamDBI); 262 P.formatMsfStreamData("File Info", File, Layout, NS); 263 } 264 265 void BytesOutputStyle::dumpTypeServerMap() { 266 printHeader(P, "Type Server Map"); 267 268 AutoIndent Indent(P); 269 270 auto &DbiS = Err(File.getPDBDbiStream()); 271 BinarySubstreamRef NS = DbiS.getTypeServerMapSubstreamData(); 272 auto Layout = File.getStreamLayout(StreamDBI); 273 P.formatMsfStreamData("Type Server Map", File, Layout, NS); 274 } 275 276 void BytesOutputStyle::dumpECData() { 277 printHeader(P, "Edit and Continue Data"); 278 279 AutoIndent Indent(P); 280 281 auto &DbiS = Err(File.getPDBDbiStream()); 282 BinarySubstreamRef NS = DbiS.getECSubstreamData(); 283 auto Layout = File.getStreamLayout(StreamDBI); 284 P.formatMsfStreamData("Edit and Continue Data", File, Layout, NS); 285 } 286 287 void BytesOutputStyle::dumpTypeIndex(uint32_t StreamIdx, 288 ArrayRef<uint32_t> Indices) { 289 assert(StreamIdx == StreamTPI || StreamIdx == StreamIPI); 290 assert(!Indices.empty()); 291 292 bool IsTpi = (StreamIdx == StreamTPI); 293 294 StringRef Label = IsTpi ? "Type (TPI) Records" : "Index (IPI) Records"; 295 printHeader(P, Label); 296 auto &Stream = Err(IsTpi ? File.getPDBTpiStream() : File.getPDBIpiStream()); 297 298 AutoIndent Indent(P); 299 300 auto Substream = Stream.getTypeRecordsSubstream(); 301 auto &Types = Err(initializeTypes(StreamIdx)); 302 auto Layout = File.getStreamLayout(StreamIdx); 303 for (const auto &Id : Indices) { 304 TypeIndex TI(Id); 305 if (TI.toArrayIndex() >= Types.capacity()) { 306 P.formatLine("Error: TypeIndex {0} does not exist", TI); 307 continue; 308 } 309 310 auto Type = Types.getType(TI); 311 uint32_t Offset = Types.getOffsetOfType(TI); 312 auto OneType = Substream.slice(Offset, Type.length()); 313 P.formatMsfStreamData(formatv("Type {0}", TI).str(), File, Layout, OneType); 314 } 315 } 316 317 template <typename CallbackT> 318 static void iterateOneModule(PDBFile &File, LinePrinter &P, 319 const DbiModuleList &Modules, uint32_t I, 320 uint32_t Digits, uint32_t IndentLevel, 321 CallbackT Callback) { 322 if (I >= Modules.getModuleCount()) { 323 P.formatLine("Mod {0:4} | Invalid module index ", 324 fmt_align(I, AlignStyle::Right, std::max(Digits, 4U))); 325 return; 326 } 327 328 auto Modi = Modules.getModuleDescriptor(I); 329 P.formatLine("Mod {0:4} | `{1}`: ", 330 fmt_align(I, AlignStyle::Right, std::max(Digits, 4U)), 331 Modi.getModuleName()); 332 333 uint16_t ModiStream = Modi.getModuleStreamIndex(); 334 AutoIndent Indent2(P, IndentLevel); 335 if (ModiStream == kInvalidStreamIndex) 336 return; 337 338 auto ModStreamData = MappedBlockStream::createIndexedStream( 339 File.getMsfLayout(), File.getMsfBuffer(), ModiStream, 340 File.getAllocator()); 341 ModuleDebugStreamRef ModStream(Modi, std::move(ModStreamData)); 342 if (auto EC = ModStream.reload()) { 343 P.formatLine("Could not parse debug information."); 344 return; 345 } 346 auto Layout = File.getStreamLayout(ModiStream); 347 Callback(I, ModStream, Layout); 348 } 349 350 template <typename CallbackT> 351 static void iterateModules(PDBFile &File, LinePrinter &P, uint32_t IndentLevel, 352 CallbackT Callback) { 353 AutoIndent Indent(P); 354 if (!File.hasPDBDbiStream()) { 355 P.formatLine("DBI Stream not present"); 356 return; 357 } 358 359 ExitOnError Err("Unexpected error processing modules"); 360 361 auto &Stream = Err(File.getPDBDbiStream()); 362 363 const DbiModuleList &Modules = Stream.modules(); 364 365 if (opts::bytes::ModuleIndex.getNumOccurrences() > 0) { 366 iterateOneModule(File, P, Modules, opts::bytes::ModuleIndex, 1, IndentLevel, 367 Callback); 368 } else { 369 uint32_t Count = Modules.getModuleCount(); 370 uint32_t Digits = NumDigits(Count); 371 for (uint32_t I = 0; I < Count; ++I) { 372 iterateOneModule(File, P, Modules, I, Digits, IndentLevel, Callback); 373 } 374 } 375 } 376 377 void BytesOutputStyle::dumpModuleSyms() { 378 printHeader(P, "Module Symbols"); 379 380 AutoIndent Indent(P); 381 382 iterateModules(File, P, 2, 383 [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, 384 const MSFStreamLayout &Layout) { 385 auto Symbols = Stream.getSymbolsSubstream(); 386 P.formatMsfStreamData("Symbols", File, Layout, Symbols); 387 }); 388 } 389 390 void BytesOutputStyle::dumpModuleC11() { 391 printHeader(P, "C11 Debug Chunks"); 392 393 AutoIndent Indent(P); 394 395 iterateModules(File, P, 2, 396 [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, 397 const MSFStreamLayout &Layout) { 398 auto Chunks = Stream.getC11LinesSubstream(); 399 P.formatMsfStreamData("C11 Debug Chunks", File, Layout, 400 Chunks); 401 }); 402 } 403 404 static std::string formatChunkKind(DebugSubsectionKind Kind) { 405 switch (Kind) { 406 RETURN_CASE(DebugSubsectionKind, None, "none"); 407 RETURN_CASE(DebugSubsectionKind, Symbols, "symbols"); 408 RETURN_CASE(DebugSubsectionKind, Lines, "lines"); 409 RETURN_CASE(DebugSubsectionKind, StringTable, "strings"); 410 RETURN_CASE(DebugSubsectionKind, FileChecksums, "checksums"); 411 RETURN_CASE(DebugSubsectionKind, FrameData, "frames"); 412 RETURN_CASE(DebugSubsectionKind, InlineeLines, "inlinee lines"); 413 RETURN_CASE(DebugSubsectionKind, CrossScopeImports, "xmi"); 414 RETURN_CASE(DebugSubsectionKind, CrossScopeExports, "xme"); 415 RETURN_CASE(DebugSubsectionKind, ILLines, "il lines"); 416 RETURN_CASE(DebugSubsectionKind, FuncMDTokenMap, "func md token map"); 417 RETURN_CASE(DebugSubsectionKind, TypeMDTokenMap, "type md token map"); 418 RETURN_CASE(DebugSubsectionKind, MergedAssemblyInput, 419 "merged assembly input"); 420 RETURN_CASE(DebugSubsectionKind, CoffSymbolRVA, "coff symbol rva"); 421 } 422 return formatUnknownEnum(Kind); 423 } 424 425 void BytesOutputStyle::dumpModuleC13() { 426 printHeader(P, "Debug Chunks"); 427 428 AutoIndent Indent(P); 429 430 iterateModules( 431 File, P, 2, 432 [this](uint32_t Modi, const ModuleDebugStreamRef &Stream, 433 const MSFStreamLayout &Layout) { 434 auto Chunks = Stream.getC13LinesSubstream(); 435 if (opts::bytes::SplitChunks) { 436 for (const auto &SS : Stream.subsections()) { 437 BinarySubstreamRef ThisChunk; 438 std::tie(ThisChunk, Chunks) = Chunks.split(SS.getRecordLength()); 439 P.formatMsfStreamData(formatChunkKind(SS.kind()), File, Layout, 440 ThisChunk); 441 } 442 } else { 443 P.formatMsfStreamData("Debug Chunks", File, Layout, Chunks); 444 } 445 }); 446 } 447 448 void BytesOutputStyle::dumpByteRanges(uint32_t Min, uint32_t Max) { 449 printHeader(P, "MSF Bytes"); 450 451 AutoIndent Indent(P); 452 453 BinaryStreamReader Reader(File.getMsfBuffer()); 454 ArrayRef<uint8_t> Data; 455 consumeError(Reader.skip(Min)); 456 uint32_t Size = Max - Min + 1; 457 auto EC = Reader.readBytes(Data, Size); 458 assert(!EC); 459 consumeError(std::move(EC)); 460 P.formatBinary("Bytes", Data, Min); 461 } 462 463 Expected<codeview::LazyRandomTypeCollection &> 464 BytesOutputStyle::initializeTypes(uint32_t StreamIdx) { 465 auto &TypeCollection = (StreamIdx == StreamTPI) ? TpiTypes : IpiTypes; 466 if (TypeCollection) 467 return *TypeCollection; 468 469 auto Tpi = (StreamIdx == StreamTPI) ? File.getPDBTpiStream() 470 : File.getPDBIpiStream(); 471 if (!Tpi) 472 return Tpi.takeError(); 473 474 auto &Types = Tpi->typeArray(); 475 uint32_t Count = Tpi->getNumTypeRecords(); 476 auto Offsets = Tpi->getTypeIndexOffsets(); 477 TypeCollection = 478 llvm::make_unique<LazyRandomTypeCollection>(Types, Count, Offsets); 479 480 return *TypeCollection; 481 } 482 483 void BytesOutputStyle::dumpStreamBytes() { 484 if (StreamPurposes.empty()) 485 discoverStreamPurposes(File, StreamPurposes); 486 487 printHeader(P, "Stream Data"); 488 ExitOnError Err("Unexpected error reading stream data"); 489 490 auto Specs = parseStreamSpecs(P); 491 492 for (const auto &Spec : Specs) { 493 AutoIndent Indent(P); 494 if (Spec.SI >= StreamPurposes.size()) { 495 P.formatLine("Stream {0}: Not present", Spec.SI); 496 continue; 497 } 498 P.formatMsfStreamData("Data", File, Spec.SI, StreamPurposes[Spec.SI], 499 Spec.Begin, Spec.Size); 500 } 501 } 502