forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			334 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			334 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- LinePrinter.cpp ------------------------------------------*- C++ -*-===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "LinePrinter.h"
 | |
| 
 | |
| #include "llvm-pdbutil.h"
 | |
| 
 | |
| #include "llvm/ADT/STLExtras.h"
 | |
| #include "llvm/DebugInfo/MSF/MSFCommon.h"
 | |
| #include "llvm/DebugInfo/MSF/MappedBlockStream.h"
 | |
| #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
 | |
| #include "llvm/DebugInfo/PDB/UDTLayout.h"
 | |
| #include "llvm/Support/BinaryStreamReader.h"
 | |
| #include "llvm/Support/Format.h"
 | |
| #include "llvm/Support/FormatAdapters.h"
 | |
| #include "llvm/Support/FormatVariadic.h"
 | |
| #include "llvm/Support/Regex.h"
 | |
| 
 | |
| #include <algorithm>
 | |
| 
 | |
| using namespace llvm;
 | |
| using namespace llvm::msf;
 | |
| using namespace llvm::pdb;
 | |
| 
 | |
| namespace {
 | |
| bool IsItemExcluded(llvm::StringRef Item,
 | |
|                     std::list<llvm::Regex> &IncludeFilters,
 | |
|                     std::list<llvm::Regex> &ExcludeFilters) {
 | |
|   if (Item.empty())
 | |
|     return false;
 | |
| 
 | |
|   auto match_pred = [Item](llvm::Regex &R) { return R.match(Item); };
 | |
| 
 | |
|   // Include takes priority over exclude.  If the user specified include
 | |
|   // filters, and none of them include this item, them item is gone.
 | |
|   if (!IncludeFilters.empty() && !any_of(IncludeFilters, match_pred))
 | |
|     return true;
 | |
| 
 | |
|   if (any_of(ExcludeFilters, match_pred))
 | |
|     return true;
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| }
 | |
| 
 | |
| using namespace llvm;
 | |
| 
 | |
| LinePrinter::LinePrinter(int Indent, bool UseColor, llvm::raw_ostream &Stream)
 | |
|     : OS(Stream), IndentSpaces(Indent), CurrentIndent(0), UseColor(UseColor) {
 | |
|   SetFilters(ExcludeTypeFilters, opts::pretty::ExcludeTypes.begin(),
 | |
|              opts::pretty::ExcludeTypes.end());
 | |
|   SetFilters(ExcludeSymbolFilters, opts::pretty::ExcludeSymbols.begin(),
 | |
|              opts::pretty::ExcludeSymbols.end());
 | |
|   SetFilters(ExcludeCompilandFilters, opts::pretty::ExcludeCompilands.begin(),
 | |
|              opts::pretty::ExcludeCompilands.end());
 | |
| 
 | |
|   SetFilters(IncludeTypeFilters, opts::pretty::IncludeTypes.begin(),
 | |
|              opts::pretty::IncludeTypes.end());
 | |
|   SetFilters(IncludeSymbolFilters, opts::pretty::IncludeSymbols.begin(),
 | |
|              opts::pretty::IncludeSymbols.end());
 | |
|   SetFilters(IncludeCompilandFilters, opts::pretty::IncludeCompilands.begin(),
 | |
|              opts::pretty::IncludeCompilands.end());
 | |
| }
 | |
| 
 | |
| void LinePrinter::Indent(uint32_t Amount) {
 | |
|   if (Amount == 0)
 | |
|     Amount = IndentSpaces;
 | |
|   CurrentIndent += Amount;
 | |
| }
 | |
| 
 | |
| void LinePrinter::Unindent(uint32_t Amount) {
 | |
|   if (Amount == 0)
 | |
|     Amount = IndentSpaces;
 | |
|   CurrentIndent = std::max<int>(0, CurrentIndent - Amount);
 | |
| }
 | |
| 
 | |
| void LinePrinter::NewLine() {
 | |
|   OS << "\n";
 | |
|   OS.indent(CurrentIndent);
 | |
| }
 | |
| 
 | |
| void LinePrinter::print(const Twine &T) { OS << T; }
 | |
| 
 | |
| void LinePrinter::printLine(const Twine &T) {
 | |
|   NewLine();
 | |
|   OS << T;
 | |
| }
 | |
| 
 | |
| bool LinePrinter::IsClassExcluded(const ClassLayout &Class) {
 | |
|   if (IsTypeExcluded(Class.getName(), Class.getSize()))
 | |
|     return true;
 | |
|   if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold)
 | |
|     return true;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
 | |
|                                uint32_t StartOffset) {
 | |
|   NewLine();
 | |
|   OS << Label << " (";
 | |
|   if (!Data.empty()) {
 | |
|     OS << "\n";
 | |
|     OS << format_bytes_with_ascii(Data, StartOffset, 32, 4,
 | |
|                                   CurrentIndent + IndentSpaces, true);
 | |
|     NewLine();
 | |
|   }
 | |
|   OS << ")";
 | |
| }
 | |
| 
 | |
| void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data,
 | |
|                                uint64_t Base, uint32_t StartOffset) {
 | |
|   NewLine();
 | |
|   OS << Label << " (";
 | |
|   if (!Data.empty()) {
 | |
|     OS << "\n";
 | |
|     Base += StartOffset;
 | |
|     OS << format_bytes_with_ascii(Data, Base, 32, 4,
 | |
|                                   CurrentIndent + IndentSpaces, true);
 | |
|     NewLine();
 | |
|   }
 | |
|   OS << ")";
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| struct Run {
 | |
|   Run() = default;
 | |
|   explicit Run(uint32_t Block) : Block(Block) {}
 | |
|   uint32_t Block = 0;
 | |
|   uint32_t ByteLen = 0;
 | |
| };
 | |
| } // namespace
 | |
| 
 | |
| static std::vector<Run> computeBlockRuns(uint32_t BlockSize,
 | |
|                                          const msf::MSFStreamLayout &Layout) {
 | |
|   std::vector<Run> Runs;
 | |
|   if (Layout.Length == 0)
 | |
|     return Runs;
 | |
| 
 | |
|   ArrayRef<support::ulittle32_t> Blocks = Layout.Blocks;
 | |
|   assert(!Blocks.empty());
 | |
|   uint32_t StreamBytesRemaining = Layout.Length;
 | |
|   uint32_t CurrentBlock = Blocks[0];
 | |
|   Runs.emplace_back(CurrentBlock);
 | |
|   while (!Blocks.empty()) {
 | |
|     Run *CurrentRun = &Runs.back();
 | |
|     uint32_t NextBlock = Blocks.front();
 | |
|     if (NextBlock < CurrentBlock || (NextBlock - CurrentBlock > 1)) {
 | |
|       Runs.emplace_back(NextBlock);
 | |
|       CurrentRun = &Runs.back();
 | |
|     }
 | |
|     uint32_t Used = std::min(BlockSize, StreamBytesRemaining);
 | |
|     CurrentRun->ByteLen += Used;
 | |
|     StreamBytesRemaining -= Used;
 | |
|     CurrentBlock = NextBlock;
 | |
|     Blocks = Blocks.drop_front();
 | |
|   }
 | |
|   return Runs;
 | |
| }
 | |
| 
 | |
| static std::pair<Run, uint32_t> findRun(uint32_t Offset, ArrayRef<Run> Runs) {
 | |
|   for (const auto &R : Runs) {
 | |
|     if (Offset < R.ByteLen)
 | |
|       return std::make_pair(R, Offset);
 | |
|     Offset -= R.ByteLen;
 | |
|   }
 | |
|   llvm_unreachable("Invalid offset!");
 | |
| }
 | |
| 
 | |
| void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File,
 | |
|                                       uint32_t StreamIdx,
 | |
|                                       StringRef StreamPurpose, uint32_t Offset,
 | |
|                                       uint32_t Size) {
 | |
|   if (StreamIdx >= File.getNumStreams()) {
 | |
|     formatLine("Stream {0}: Not present", StreamIdx);
 | |
|     return;
 | |
|   }
 | |
|   if (Size + Offset > File.getStreamByteSize(StreamIdx)) {
 | |
|     formatLine(
 | |
|         "Stream {0}: Invalid offset and size, range out of stream bounds",
 | |
|         StreamIdx);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto S = File.createIndexedStream(StreamIdx);
 | |
|   if (!S) {
 | |
|     NewLine();
 | |
|     formatLine("Stream {0}: Not present", StreamIdx);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   uint32_t End =
 | |
|       (Size == 0) ? S->getLength() : std::min(Offset + Size, S->getLength());
 | |
|   Size = End - Offset;
 | |
| 
 | |
|   formatLine("Stream {0}: {1} (dumping {2:N} / {3:N} bytes)", StreamIdx,
 | |
|              StreamPurpose, Size, S->getLength());
 | |
|   AutoIndent Indent(*this);
 | |
|   BinaryStreamRef Slice(*S);
 | |
|   BinarySubstreamRef Substream;
 | |
|   Substream.Offset = Offset;
 | |
|   Substream.StreamData = Slice.drop_front(Offset).keep_front(Size);
 | |
| 
 | |
|   auto Layout = File.getStreamLayout(StreamIdx);
 | |
|   formatMsfStreamData(Label, File, Layout, Substream);
 | |
| }
 | |
| 
 | |
| void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File,
 | |
|                                       const msf::MSFStreamLayout &Stream,
 | |
|                                       BinarySubstreamRef Substream) {
 | |
|   BinaryStreamReader Reader(Substream.StreamData);
 | |
| 
 | |
|   auto Runs = computeBlockRuns(File.getBlockSize(), Stream);
 | |
| 
 | |
|   NewLine();
 | |
|   OS << Label << " (";
 | |
|   while (Reader.bytesRemaining() > 0) {
 | |
|     OS << "\n";
 | |
| 
 | |
|     Run FoundRun;
 | |
|     uint32_t RunOffset;
 | |
|     std::tie(FoundRun, RunOffset) = findRun(Substream.Offset, Runs);
 | |
|     assert(FoundRun.ByteLen >= RunOffset);
 | |
|     uint32_t Len = FoundRun.ByteLen - RunOffset;
 | |
|     Len = std::min(Len, Reader.bytesRemaining());
 | |
|     uint64_t Base = FoundRun.Block * File.getBlockSize() + RunOffset;
 | |
|     ArrayRef<uint8_t> Data;
 | |
|     consumeError(Reader.readBytes(Data, Len));
 | |
|     OS << format_bytes_with_ascii(Data, Base, 32, 4,
 | |
|                                   CurrentIndent + IndentSpaces, true);
 | |
|     if (Reader.bytesRemaining() > 0) {
 | |
|       NewLine();
 | |
|       OS << formatv("  {0}",
 | |
|                     fmt_align("<discontinuity>", AlignStyle::Center, 114, '-'));
 | |
|     }
 | |
|     Substream.Offset += Len;
 | |
|   }
 | |
|   NewLine();
 | |
|   OS << ")";
 | |
| }
 | |
| 
 | |
| void LinePrinter::formatMsfStreamBlocks(
 | |
|     PDBFile &File, const msf::MSFStreamLayout &StreamLayout) {
 | |
|   auto Blocks = makeArrayRef(StreamLayout.Blocks);
 | |
|   uint32_t L = StreamLayout.Length;
 | |
| 
 | |
|   while (L > 0) {
 | |
|     NewLine();
 | |
|     assert(!Blocks.empty());
 | |
|     OS << formatv("Block {0} (\n", uint32_t(Blocks.front()));
 | |
|     uint32_t UsedBytes = std::min(L, File.getBlockSize());
 | |
|     ArrayRef<uint8_t> BlockData =
 | |
|         cantFail(File.getBlockData(Blocks.front(), File.getBlockSize()));
 | |
|     uint64_t BaseOffset = Blocks.front();
 | |
|     BaseOffset *= File.getBlockSize();
 | |
|     OS << format_bytes_with_ascii(BlockData, BaseOffset, 32, 4,
 | |
|                                   CurrentIndent + IndentSpaces, true);
 | |
|     NewLine();
 | |
|     OS << ")";
 | |
|     NewLine();
 | |
|     L -= UsedBytes;
 | |
|     Blocks = Blocks.drop_front();
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size) {
 | |
|   if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters))
 | |
|     return true;
 | |
|   if (Size < opts::pretty::SizeThreshold)
 | |
|     return true;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) {
 | |
|   return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters);
 | |
| }
 | |
| 
 | |
| bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) {
 | |
|   return IsItemExcluded(CompilandName, IncludeCompilandFilters,
 | |
|                         ExcludeCompilandFilters);
 | |
| }
 | |
| 
 | |
| WithColor::WithColor(LinePrinter &P, PDB_ColorItem C)
 | |
|     : OS(P.OS), UseColor(P.hasColor()) {
 | |
|   if (UseColor)
 | |
|     applyColor(C);
 | |
| }
 | |
| 
 | |
| WithColor::~WithColor() {
 | |
|   if (UseColor)
 | |
|     OS.resetColor();
 | |
| }
 | |
| 
 | |
| void WithColor::applyColor(PDB_ColorItem C) {
 | |
|   switch (C) {
 | |
|   case PDB_ColorItem::None:
 | |
|     OS.resetColor();
 | |
|     return;
 | |
|   case PDB_ColorItem::Comment:
 | |
|     OS.changeColor(raw_ostream::GREEN, false);
 | |
|     return;
 | |
|   case PDB_ColorItem::Address:
 | |
|     OS.changeColor(raw_ostream::YELLOW, /*bold=*/true);
 | |
|     return;
 | |
|   case PDB_ColorItem::Keyword:
 | |
|     OS.changeColor(raw_ostream::MAGENTA, true);
 | |
|     return;
 | |
|   case PDB_ColorItem::Register:
 | |
|   case PDB_ColorItem::Offset:
 | |
|     OS.changeColor(raw_ostream::YELLOW, false);
 | |
|     return;
 | |
|   case PDB_ColorItem::Type:
 | |
|     OS.changeColor(raw_ostream::CYAN, true);
 | |
|     return;
 | |
|   case PDB_ColorItem::Identifier:
 | |
|     OS.changeColor(raw_ostream::CYAN, false);
 | |
|     return;
 | |
|   case PDB_ColorItem::Path:
 | |
|     OS.changeColor(raw_ostream::CYAN, false);
 | |
|     return;
 | |
|   case PDB_ColorItem::Padding:
 | |
|   case PDB_ColorItem::SectionHeader:
 | |
|     OS.changeColor(raw_ostream::RED, true);
 | |
|     return;
 | |
|   case PDB_ColorItem::LiteralValue:
 | |
|     OS.changeColor(raw_ostream::GREEN, true);
 | |
|     return;
 | |
|   }
 | |
| }
 |