ExplainOutputStyle.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. //===- ExplainOutputStyle.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. #include "ExplainOutputStyle.h"
  9. #include "StreamUtil.h"
  10. #include "llvm-pdbutil.h"
  11. #include "llvm/DebugInfo/CodeView/Formatters.h"
  12. #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
  13. #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
  14. #include "llvm/DebugInfo/PDB/Native/DbiStream.h"
  15. #include "llvm/DebugInfo/PDB/Native/FormatUtil.h"
  16. #include "llvm/DebugInfo/PDB/Native/InfoStream.h"
  17. #include "llvm/DebugInfo/PDB/Native/InputFile.h"
  18. #include "llvm/DebugInfo/PDB/Native/NativeSession.h"
  19. #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
  20. #include "llvm/DebugInfo/PDB/Native/RawTypes.h"
  21. #include "llvm/Object/COFF.h"
  22. #include "llvm/Support/BinaryByteStream.h"
  23. #include "llvm/Support/BinaryStreamArray.h"
  24. #include "llvm/Support/Error.h"
  25. using namespace llvm;
  26. using namespace llvm::codeview;
  27. using namespace llvm::msf;
  28. using namespace llvm::pdb;
  29. ExplainOutputStyle::ExplainOutputStyle(InputFile &File, uint64_t FileOffset)
  30. : File(File), FileOffset(FileOffset), P(2, false, outs(), opts::Filters) {}
  31. Error ExplainOutputStyle::dump() {
  32. P.formatLine("Explaining file offset {0} of file '{1}'.", FileOffset,
  33. File.getFilePath());
  34. if (File.isPdb())
  35. return explainPdbFile();
  36. return explainBinaryFile();
  37. }
  38. Error ExplainOutputStyle::explainPdbFile() {
  39. bool IsAllocated = explainPdbBlockStatus();
  40. if (!IsAllocated)
  41. return Error::success();
  42. AutoIndent Indent(P);
  43. if (isPdbSuperBlock())
  44. explainPdbSuperBlockOffset();
  45. else if (isPdbFpmBlock())
  46. explainPdbFpmBlockOffset();
  47. else if (isPdbBlockMapBlock())
  48. explainPdbBlockMapOffset();
  49. else if (isPdbStreamDirectoryBlock())
  50. explainPdbStreamDirectoryOffset();
  51. else if (auto Index = getPdbBlockStreamIndex())
  52. explainPdbStreamOffset(*Index);
  53. else
  54. explainPdbUnknownBlock();
  55. return Error::success();
  56. }
  57. Error ExplainOutputStyle::explainBinaryFile() {
  58. std::unique_ptr<BinaryByteStream> Stream =
  59. std::make_unique<BinaryByteStream>(File.unknown().getBuffer(),
  60. llvm::support::little);
  61. switch (opts::explain::InputType) {
  62. case opts::explain::InputFileType::DBIStream: {
  63. DbiStream Dbi(std::move(Stream));
  64. if (auto EC = Dbi.reload(nullptr))
  65. return EC;
  66. explainStreamOffset(Dbi, FileOffset);
  67. break;
  68. }
  69. case opts::explain::InputFileType::PDBStream: {
  70. InfoStream Info(std::move(Stream));
  71. if (auto EC = Info.reload())
  72. return EC;
  73. explainStreamOffset(Info, FileOffset);
  74. break;
  75. }
  76. default:
  77. llvm_unreachable("Invalid input file type!");
  78. }
  79. return Error::success();
  80. }
  81. uint32_t ExplainOutputStyle::pdbBlockIndex() const {
  82. return FileOffset / File.pdb().getBlockSize();
  83. }
  84. uint32_t ExplainOutputStyle::pdbBlockOffset() const {
  85. uint64_t BlockStart = pdbBlockIndex() * File.pdb().getBlockSize();
  86. assert(FileOffset >= BlockStart);
  87. return FileOffset - BlockStart;
  88. }
  89. bool ExplainOutputStyle::isPdbSuperBlock() const {
  90. return pdbBlockIndex() == 0;
  91. }
  92. bool ExplainOutputStyle::isPdbFpm1() const {
  93. return ((pdbBlockIndex() - 1) % File.pdb().getBlockSize() == 0);
  94. }
  95. bool ExplainOutputStyle::isPdbFpm2() const {
  96. return ((pdbBlockIndex() - 2) % File.pdb().getBlockSize() == 0);
  97. }
  98. bool ExplainOutputStyle::isPdbFpmBlock() const {
  99. return isPdbFpm1() || isPdbFpm2();
  100. }
  101. bool ExplainOutputStyle::isPdbBlockMapBlock() const {
  102. return pdbBlockIndex() == File.pdb().getBlockMapIndex();
  103. }
  104. bool ExplainOutputStyle::isPdbStreamDirectoryBlock() const {
  105. const auto &Layout = File.pdb().getMsfLayout();
  106. return llvm::is_contained(Layout.DirectoryBlocks, pdbBlockIndex());
  107. }
  108. std::optional<uint32_t> ExplainOutputStyle::getPdbBlockStreamIndex() const {
  109. const auto &Layout = File.pdb().getMsfLayout();
  110. for (const auto &Entry : enumerate(Layout.StreamMap)) {
  111. if (!llvm::is_contained(Entry.value(), pdbBlockIndex()))
  112. continue;
  113. return Entry.index();
  114. }
  115. return std::nullopt;
  116. }
  117. bool ExplainOutputStyle::explainPdbBlockStatus() {
  118. if (FileOffset >= File.pdb().getFileSize()) {
  119. P.formatLine("Address {0} is not in the file (file size = {1}).",
  120. FileOffset, File.pdb().getFileSize());
  121. return false;
  122. }
  123. P.formatLine("Block:Offset = {2:X-}:{1:X-4}.", FileOffset, pdbBlockOffset(),
  124. pdbBlockIndex());
  125. bool IsFree = File.pdb().getMsfLayout().FreePageMap[pdbBlockIndex()];
  126. P.formatLine("Address is in block {0} ({1}allocated).", pdbBlockIndex(),
  127. IsFree ? "un" : "");
  128. return !IsFree;
  129. }
  130. #define endof(Class, Field) (offsetof(Class, Field) + sizeof(Class::Field))
  131. void ExplainOutputStyle::explainPdbSuperBlockOffset() {
  132. P.formatLine("This corresponds to offset {0} of the MSF super block, ",
  133. pdbBlockOffset());
  134. if (pdbBlockOffset() < endof(SuperBlock, MagicBytes))
  135. P.printLine("which is part of the MSF file magic.");
  136. else if (pdbBlockOffset() < endof(SuperBlock, BlockSize)) {
  137. P.printLine("which contains the block size of the file.");
  138. P.formatLine("The current value is {0}.",
  139. uint32_t(File.pdb().getMsfLayout().SB->BlockSize));
  140. } else if (pdbBlockOffset() < endof(SuperBlock, FreeBlockMapBlock)) {
  141. P.printLine("which contains the index of the FPM block (e.g. 1 or 2).");
  142. P.formatLine("The current value is {0}.",
  143. uint32_t(File.pdb().getMsfLayout().SB->FreeBlockMapBlock));
  144. } else if (pdbBlockOffset() < endof(SuperBlock, NumBlocks)) {
  145. P.printLine("which contains the number of blocks in the file.");
  146. P.formatLine("The current value is {0}.",
  147. uint32_t(File.pdb().getMsfLayout().SB->NumBlocks));
  148. } else if (pdbBlockOffset() < endof(SuperBlock, NumDirectoryBytes)) {
  149. P.printLine("which contains the number of bytes in the stream directory.");
  150. P.formatLine("The current value is {0}.",
  151. uint32_t(File.pdb().getMsfLayout().SB->NumDirectoryBytes));
  152. } else if (pdbBlockOffset() < endof(SuperBlock, Unknown1)) {
  153. P.printLine("whose purpose is unknown.");
  154. P.formatLine("The current value is {0}.",
  155. uint32_t(File.pdb().getMsfLayout().SB->Unknown1));
  156. } else if (pdbBlockOffset() < endof(SuperBlock, BlockMapAddr)) {
  157. P.printLine("which contains the file offset of the block map.");
  158. P.formatLine("The current value is {0}.",
  159. uint32_t(File.pdb().getMsfLayout().SB->BlockMapAddr));
  160. } else {
  161. assert(pdbBlockOffset() > sizeof(SuperBlock));
  162. P.printLine(
  163. "which is outside the range of valid data for the super block.");
  164. }
  165. }
  166. static std::string toBinaryString(uint8_t Byte) {
  167. char Result[9] = {0};
  168. for (int I = 0; I < 8; ++I) {
  169. char C = (Byte & 1) ? '1' : '0';
  170. Result[I] = C;
  171. Byte >>= 1;
  172. }
  173. return std::string(Result);
  174. }
  175. void ExplainOutputStyle::explainPdbFpmBlockOffset() {
  176. const MSFLayout &Layout = File.pdb().getMsfLayout();
  177. uint32_t MainFpm = Layout.mainFpmBlock();
  178. uint32_t AltFpm = Layout.alternateFpmBlock();
  179. assert(isPdbFpmBlock());
  180. uint32_t Fpm = isPdbFpm1() ? 1 : 2;
  181. uint32_t FpmChunk = pdbBlockIndex() / File.pdb().getBlockSize();
  182. assert((Fpm == MainFpm) || (Fpm == AltFpm));
  183. (void)AltFpm;
  184. bool IsMain = (Fpm == MainFpm);
  185. P.formatLine("Address is in FPM{0} ({1} FPM)", Fpm, IsMain ? "Main" : "Alt");
  186. uint32_t DescribedBlockStart =
  187. 8 * (FpmChunk * File.pdb().getBlockSize() + pdbBlockOffset());
  188. if (DescribedBlockStart > File.pdb().getBlockCount()) {
  189. P.printLine("Address is in extraneous FPM space.");
  190. return;
  191. }
  192. P.formatLine("Address describes the allocation status of blocks [{0},{1})",
  193. DescribedBlockStart, DescribedBlockStart + 8);
  194. ArrayRef<uint8_t> Bytes;
  195. cantFail(File.pdb().getMsfBuffer().readBytes(FileOffset, 1, Bytes));
  196. P.formatLine("Status = {0} (Note: 0 = allocated, 1 = free)",
  197. toBinaryString(Bytes[0]));
  198. }
  199. void ExplainOutputStyle::explainPdbBlockMapOffset() {
  200. uint64_t BlockMapOffset = File.pdb().getBlockMapOffset();
  201. uint32_t OffsetInBlock = FileOffset - BlockMapOffset;
  202. P.formatLine("Address is at offset {0} of the directory block list",
  203. OffsetInBlock);
  204. }
  205. static uint32_t getOffsetInStream(ArrayRef<support::ulittle32_t> StreamBlocks,
  206. uint64_t FileOffset, uint32_t BlockSize) {
  207. uint32_t BlockIndex = FileOffset / BlockSize;
  208. uint32_t OffsetInBlock = FileOffset - BlockIndex * BlockSize;
  209. auto Iter = llvm::find(StreamBlocks, BlockIndex);
  210. assert(Iter != StreamBlocks.end());
  211. uint32_t StreamBlockIndex = std::distance(StreamBlocks.begin(), Iter);
  212. return StreamBlockIndex * BlockSize + OffsetInBlock;
  213. }
  214. void ExplainOutputStyle::explainPdbStreamOffset(uint32_t Stream) {
  215. SmallVector<StreamInfo, 12> Streams;
  216. discoverStreamPurposes(File.pdb(), Streams);
  217. assert(Stream <= Streams.size());
  218. const StreamInfo &S = Streams[Stream];
  219. const auto &Layout = File.pdb().getStreamLayout(Stream);
  220. uint32_t StreamOff =
  221. getOffsetInStream(Layout.Blocks, FileOffset, File.pdb().getBlockSize());
  222. P.formatLine("Address is at offset {0}/{1} of Stream {2} ({3}){4}.",
  223. StreamOff, Layout.Length, Stream, S.getLongName(),
  224. (StreamOff > Layout.Length) ? " in unused space" : "");
  225. switch (S.getPurpose()) {
  226. case StreamPurpose::DBI: {
  227. DbiStream &Dbi = cantFail(File.pdb().getPDBDbiStream());
  228. explainStreamOffset(Dbi, StreamOff);
  229. break;
  230. }
  231. case StreamPurpose::PDB: {
  232. InfoStream &Info = cantFail(File.pdb().getPDBInfoStream());
  233. explainStreamOffset(Info, StreamOff);
  234. break;
  235. }
  236. case StreamPurpose::IPI:
  237. case StreamPurpose::TPI:
  238. case StreamPurpose::ModuleStream:
  239. case StreamPurpose::NamedStream:
  240. default:
  241. break;
  242. }
  243. }
  244. void ExplainOutputStyle::explainPdbStreamDirectoryOffset() {
  245. auto DirectoryBlocks = File.pdb().getDirectoryBlockArray();
  246. const auto &Layout = File.pdb().getMsfLayout();
  247. uint32_t StreamOff =
  248. getOffsetInStream(DirectoryBlocks, FileOffset, File.pdb().getBlockSize());
  249. P.formatLine("Address is at offset {0}/{1} of Stream Directory{2}.",
  250. StreamOff, uint32_t(Layout.SB->NumDirectoryBytes),
  251. uint32_t(StreamOff > Layout.SB->NumDirectoryBytes)
  252. ? " in unused space"
  253. : "");
  254. }
  255. void ExplainOutputStyle::explainPdbUnknownBlock() {
  256. P.formatLine("Address has unknown purpose.");
  257. }
  258. template <typename T>
  259. static void printStructField(LinePrinter &P, StringRef Label, T Value) {
  260. P.formatLine("which contains {0}.", Label);
  261. P.formatLine("The current value is {0}.", Value);
  262. }
  263. static void explainDbiHeaderOffset(LinePrinter &P, DbiStream &Dbi,
  264. uint32_t Offset) {
  265. const DbiStreamHeader *Header = Dbi.getHeader();
  266. assert(Header != nullptr);
  267. if (Offset < endof(DbiStreamHeader, VersionSignature))
  268. printStructField(P, "the DBI Stream Version Signature",
  269. int32_t(Header->VersionSignature));
  270. else if (Offset < endof(DbiStreamHeader, VersionHeader))
  271. printStructField(P, "the DBI Stream Version Header",
  272. uint32_t(Header->VersionHeader));
  273. else if (Offset < endof(DbiStreamHeader, Age))
  274. printStructField(P, "the age of the DBI Stream", uint32_t(Header->Age));
  275. else if (Offset < endof(DbiStreamHeader, GlobalSymbolStreamIndex))
  276. printStructField(P, "the index of the Global Symbol Stream",
  277. uint16_t(Header->GlobalSymbolStreamIndex));
  278. else if (Offset < endof(DbiStreamHeader, BuildNumber))
  279. printStructField(P, "the build number", uint16_t(Header->BuildNumber));
  280. else if (Offset < endof(DbiStreamHeader, PublicSymbolStreamIndex))
  281. printStructField(P, "the index of the Public Symbol Stream",
  282. uint16_t(Header->PublicSymbolStreamIndex));
  283. else if (Offset < endof(DbiStreamHeader, PdbDllVersion))
  284. printStructField(P, "the version of mspdb.dll",
  285. uint16_t(Header->PdbDllVersion));
  286. else if (Offset < endof(DbiStreamHeader, SymRecordStreamIndex))
  287. printStructField(P, "the index of the Symbol Record Stream",
  288. uint16_t(Header->SymRecordStreamIndex));
  289. else if (Offset < endof(DbiStreamHeader, PdbDllRbld))
  290. printStructField(P, "the rbld of mspdb.dll", uint16_t(Header->PdbDllRbld));
  291. else if (Offset < endof(DbiStreamHeader, ModiSubstreamSize))
  292. printStructField(P, "the size of the Module Info Substream",
  293. int32_t(Header->ModiSubstreamSize));
  294. else if (Offset < endof(DbiStreamHeader, SecContrSubstreamSize))
  295. printStructField(P, "the size of the Section Contribution Substream",
  296. int32_t(Header->SecContrSubstreamSize));
  297. else if (Offset < endof(DbiStreamHeader, SectionMapSize))
  298. printStructField(P, "the size of the Section Map Substream",
  299. int32_t(Header->SectionMapSize));
  300. else if (Offset < endof(DbiStreamHeader, FileInfoSize))
  301. printStructField(P, "the size of the File Info Substream",
  302. int32_t(Header->FileInfoSize));
  303. else if (Offset < endof(DbiStreamHeader, TypeServerSize))
  304. printStructField(P, "the size of the Type Server Map",
  305. int32_t(Header->TypeServerSize));
  306. else if (Offset < endof(DbiStreamHeader, MFCTypeServerIndex))
  307. printStructField(P, "the index of the MFC Type Server stream",
  308. uint32_t(Header->MFCTypeServerIndex));
  309. else if (Offset < endof(DbiStreamHeader, OptionalDbgHdrSize))
  310. printStructField(P, "the size of the Optional Debug Stream array",
  311. int32_t(Header->OptionalDbgHdrSize));
  312. else if (Offset < endof(DbiStreamHeader, ECSubstreamSize))
  313. printStructField(P, "the size of the Edit & Continue Substream",
  314. int32_t(Header->ECSubstreamSize));
  315. else if (Offset < endof(DbiStreamHeader, Flags))
  316. printStructField(P, "the DBI Stream flags", uint16_t(Header->Flags));
  317. else if (Offset < endof(DbiStreamHeader, MachineType))
  318. printStructField(P, "the machine type", uint16_t(Header->MachineType));
  319. else if (Offset < endof(DbiStreamHeader, Reserved))
  320. printStructField(P, "reserved data", uint32_t(Header->Reserved));
  321. }
  322. static void explainDbiModiSubstreamOffset(LinePrinter &P, DbiStream &Dbi,
  323. uint32_t Offset) {
  324. VarStreamArray<DbiModuleDescriptor> ModuleDescriptors;
  325. BinaryStreamRef ModiSubstreamData = Dbi.getModiSubstreamData().StreamData;
  326. BinaryStreamReader Reader(ModiSubstreamData);
  327. cantFail(Reader.readArray(ModuleDescriptors, ModiSubstreamData.getLength()));
  328. auto Prev = ModuleDescriptors.begin();
  329. assert(Prev.offset() == 0);
  330. auto Current = Prev;
  331. uint32_t Index = 0;
  332. while (true) {
  333. Prev = Current;
  334. ++Current;
  335. if (Current == ModuleDescriptors.end() || Offset < Current.offset())
  336. break;
  337. ++Index;
  338. }
  339. const DbiModuleDescriptor &Descriptor = *Prev;
  340. P.formatLine("which contains the descriptor for module {0} ({1}).", Index,
  341. Descriptor.getModuleName());
  342. }
  343. template <typename T>
  344. static void dontExplain(LinePrinter &Printer, T &Stream, uint32_t Offset) {}
  345. template <typename T, typename SubstreamRangeT>
  346. static void explainSubstreamOffset(LinePrinter &P, uint32_t OffsetInStream,
  347. T &Stream,
  348. const SubstreamRangeT &Substreams) {
  349. uint32_t SubOffset = OffsetInStream;
  350. for (const auto &Entry : Substreams) {
  351. if (Entry.Size <= 0)
  352. continue;
  353. uint32_t S = static_cast<uint32_t>(Entry.Size);
  354. if (SubOffset < S) {
  355. P.formatLine("address is at offset {0}/{1} of the {2}.", SubOffset, S,
  356. Entry.Label);
  357. Entry.Explain(P, Stream, SubOffset);
  358. return;
  359. }
  360. SubOffset -= S;
  361. }
  362. }
  363. void ExplainOutputStyle::explainStreamOffset(DbiStream &Dbi,
  364. uint32_t OffsetInStream) {
  365. P.printLine("Within the DBI stream:");
  366. AutoIndent Indent(P);
  367. const DbiStreamHeader *Header = Dbi.getHeader();
  368. assert(Header != nullptr);
  369. struct SubstreamInfo {
  370. int32_t Size;
  371. StringRef Label;
  372. void (*Explain)(LinePrinter &, DbiStream &, uint32_t);
  373. } Substreams[] = {
  374. {sizeof(DbiStreamHeader), "DBI Stream Header", explainDbiHeaderOffset},
  375. {int32_t(Header->ModiSubstreamSize), "Module Info Substream",
  376. explainDbiModiSubstreamOffset},
  377. {int32_t(Header->SecContrSubstreamSize), "Section Contribution Substream",
  378. dontExplain<DbiStream>},
  379. {int32_t(Header->SectionMapSize), "Section Map", dontExplain<DbiStream>},
  380. {int32_t(Header->FileInfoSize), "File Info Substream",
  381. dontExplain<DbiStream>},
  382. {int32_t(Header->TypeServerSize), "Type Server Map Substream",
  383. dontExplain<DbiStream>},
  384. {int32_t(Header->ECSubstreamSize), "Edit & Continue Substream",
  385. dontExplain<DbiStream>},
  386. {int32_t(Header->OptionalDbgHdrSize), "Optional Debug Stream Array",
  387. dontExplain<DbiStream>},
  388. };
  389. explainSubstreamOffset(P, OffsetInStream, Dbi, Substreams);
  390. }
  391. static void explainPdbStreamHeaderOffset(LinePrinter &P, InfoStream &Info,
  392. uint32_t Offset) {
  393. const InfoStreamHeader *Header = Info.getHeader();
  394. assert(Header != nullptr);
  395. if (Offset < endof(InfoStreamHeader, Version))
  396. printStructField(P, "the PDB Stream Version Signature",
  397. uint32_t(Header->Version));
  398. else if (Offset < endof(InfoStreamHeader, Signature))
  399. printStructField(P, "the signature of the PDB Stream",
  400. uint32_t(Header->Signature));
  401. else if (Offset < endof(InfoStreamHeader, Age))
  402. printStructField(P, "the age of the PDB", uint32_t(Header->Age));
  403. else if (Offset < endof(InfoStreamHeader, Guid))
  404. printStructField(P, "the guid of the PDB", fmt_guid(Header->Guid.Guid));
  405. }
  406. void ExplainOutputStyle::explainStreamOffset(InfoStream &Info,
  407. uint32_t OffsetInStream) {
  408. P.printLine("Within the PDB stream:");
  409. AutoIndent Indent(P);
  410. struct SubstreamInfo {
  411. uint32_t Size;
  412. StringRef Label;
  413. void (*Explain)(LinePrinter &, InfoStream &, uint32_t);
  414. } Substreams[] = {{sizeof(InfoStreamHeader), "PDB Stream Header",
  415. explainPdbStreamHeaderOffset},
  416. {Info.getNamedStreamMapByteSize(), "Named Stream Map",
  417. dontExplain<InfoStream>},
  418. {Info.getStreamSize(), "PDB Feature Signatures",
  419. dontExplain<InfoStream>}};
  420. explainSubstreamOffset(P, OffsetInStream, Info, Substreams);
  421. }