1//===- LinePrinter.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 "LinePrinter.h" 10 11#include "llvm-pdbutil.h" 12 13#include "llvm/ADT/STLExtras.h" 14#include "llvm/DebugInfo/MSF/MSFCommon.h" 15#include "llvm/DebugInfo/MSF/MappedBlockStream.h" 16#include "llvm/DebugInfo/PDB/Native/PDBFile.h" 17#include "llvm/DebugInfo/PDB/UDTLayout.h" 18#include "llvm/Support/BinaryStreamReader.h" 19#include "llvm/Support/Format.h" 20#include "llvm/Support/FormatAdapters.h" 21#include "llvm/Support/FormatVariadic.h" 22#include "llvm/Support/Regex.h" 23 24#include <algorithm> 25 26using namespace llvm; 27using namespace llvm::msf; 28using namespace llvm::pdb; 29 30namespace { 31bool IsItemExcluded(llvm::StringRef Item, 32 std::list<llvm::Regex> &IncludeFilters, 33 std::list<llvm::Regex> &ExcludeFilters) { 34 if (Item.empty()) 35 return false; 36 37 auto match_pred = [Item](llvm::Regex &R) { return R.match(Item); }; 38 39 // Include takes priority over exclude. If the user specified include 40 // filters, and none of them include this item, them item is gone. 41 if (!IncludeFilters.empty() && !any_of(IncludeFilters, match_pred)) 42 return true; 43 44 if (any_of(ExcludeFilters, match_pred)) 45 return true; 46 47 return false; 48} 49} 50 51using namespace llvm; 52 53LinePrinter::LinePrinter(int Indent, bool UseColor, llvm::raw_ostream &Stream) 54 : OS(Stream), IndentSpaces(Indent), CurrentIndent(0), UseColor(UseColor) { 55 SetFilters(ExcludeTypeFilters, opts::pretty::ExcludeTypes.begin(), 56 opts::pretty::ExcludeTypes.end()); 57 SetFilters(ExcludeSymbolFilters, opts::pretty::ExcludeSymbols.begin(), 58 opts::pretty::ExcludeSymbols.end()); 59 SetFilters(ExcludeCompilandFilters, opts::pretty::ExcludeCompilands.begin(), 60 opts::pretty::ExcludeCompilands.end()); 61 62 SetFilters(IncludeTypeFilters, opts::pretty::IncludeTypes.begin(), 63 opts::pretty::IncludeTypes.end()); 64 SetFilters(IncludeSymbolFilters, opts::pretty::IncludeSymbols.begin(), 65 opts::pretty::IncludeSymbols.end()); 66 SetFilters(IncludeCompilandFilters, opts::pretty::IncludeCompilands.begin(), 67 opts::pretty::IncludeCompilands.end()); 68} 69 70void LinePrinter::Indent(uint32_t Amount) { 71 if (Amount == 0) 72 Amount = IndentSpaces; 73 CurrentIndent += Amount; 74} 75 76void LinePrinter::Unindent(uint32_t Amount) { 77 if (Amount == 0) 78 Amount = IndentSpaces; 79 CurrentIndent = std::max<int>(0, CurrentIndent - Amount); 80} 81 82void LinePrinter::NewLine() { 83 OS << "\n"; 84 OS.indent(CurrentIndent); 85} 86 87void LinePrinter::print(const Twine &T) { OS << T; } 88 89void LinePrinter::printLine(const Twine &T) { 90 NewLine(); 91 OS << T; 92} 93 94bool LinePrinter::IsClassExcluded(const ClassLayout &Class) { 95 if (IsTypeExcluded(Class.getName(), Class.getSize())) 96 return true; 97 if (Class.deepPaddingSize() < opts::pretty::PaddingThreshold) 98 return true; 99 return false; 100} 101 102void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, 103 uint32_t StartOffset) { 104 NewLine(); 105 OS << Label << " ("; 106 if (!Data.empty()) { 107 OS << "\n"; 108 OS << format_bytes_with_ascii(Data, StartOffset, 32, 4, 109 CurrentIndent + IndentSpaces, true); 110 NewLine(); 111 } 112 OS << ")"; 113} 114 115void LinePrinter::formatBinary(StringRef Label, ArrayRef<uint8_t> Data, 116 uint64_t Base, uint32_t StartOffset) { 117 NewLine(); 118 OS << Label << " ("; 119 if (!Data.empty()) { 120 OS << "\n"; 121 Base += StartOffset; 122 OS << format_bytes_with_ascii(Data, Base, 32, 4, 123 CurrentIndent + IndentSpaces, true); 124 NewLine(); 125 } 126 OS << ")"; 127} 128 129namespace { 130struct Run { 131 Run() = default; 132 explicit Run(uint32_t Block) : Block(Block) {} 133 uint32_t Block = 0; 134 uint32_t ByteLen = 0; 135}; 136} // namespace 137 138static std::vector<Run> computeBlockRuns(uint32_t BlockSize, 139 const msf::MSFStreamLayout &Layout) { 140 std::vector<Run> Runs; 141 if (Layout.Length == 0) 142 return Runs; 143 144 ArrayRef<support::ulittle32_t> Blocks = Layout.Blocks; 145 assert(!Blocks.empty()); 146 uint32_t StreamBytesRemaining = Layout.Length; 147 uint32_t CurrentBlock = Blocks[0]; 148 Runs.emplace_back(CurrentBlock); 149 while (!Blocks.empty()) { 150 Run *CurrentRun = &Runs.back(); 151 uint32_t NextBlock = Blocks.front(); 152 if (NextBlock < CurrentBlock || (NextBlock - CurrentBlock > 1)) { 153 Runs.emplace_back(NextBlock); 154 CurrentRun = &Runs.back(); 155 } 156 uint32_t Used = std::min(BlockSize, StreamBytesRemaining); 157 CurrentRun->ByteLen += Used; 158 StreamBytesRemaining -= Used; 159 CurrentBlock = NextBlock; 160 Blocks = Blocks.drop_front(); 161 } 162 return Runs; 163} 164 165static std::pair<Run, uint32_t> findRun(uint32_t Offset, ArrayRef<Run> Runs) { 166 for (const auto &R : Runs) { 167 if (Offset < R.ByteLen) 168 return std::make_pair(R, Offset); 169 Offset -= R.ByteLen; 170 } 171 llvm_unreachable("Invalid offset!"); 172} 173 174void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File, 175 uint32_t StreamIdx, 176 StringRef StreamPurpose, uint32_t Offset, 177 uint32_t Size) { 178 if (StreamIdx >= File.getNumStreams()) { 179 formatLine("Stream {0}: Not present", StreamIdx); 180 return; 181 } 182 if (Size + Offset > File.getStreamByteSize(StreamIdx)) { 183 formatLine( 184 "Stream {0}: Invalid offset and size, range out of stream bounds", 185 StreamIdx); 186 return; 187 } 188 189 auto S = File.createIndexedStream(StreamIdx); 190 if (!S) { 191 NewLine(); 192 formatLine("Stream {0}: Not present", StreamIdx); 193 return; 194 } 195 196 uint32_t End = 197 (Size == 0) ? S->getLength() : std::min(Offset + Size, S->getLength()); 198 Size = End - Offset; 199 200 formatLine("Stream {0}: {1} (dumping {2:N} / {3:N} bytes)", StreamIdx, 201 StreamPurpose, Size, S->getLength()); 202 AutoIndent Indent(*this); 203 BinaryStreamRef Slice(*S); 204 BinarySubstreamRef Substream; 205 Substream.Offset = Offset; 206 Substream.StreamData = Slice.drop_front(Offset).keep_front(Size); 207 208 auto Layout = File.getStreamLayout(StreamIdx); 209 formatMsfStreamData(Label, File, Layout, Substream); 210} 211 212void LinePrinter::formatMsfStreamData(StringRef Label, PDBFile &File, 213 const msf::MSFStreamLayout &Stream, 214 BinarySubstreamRef Substream) { 215 BinaryStreamReader Reader(Substream.StreamData); 216 217 auto Runs = computeBlockRuns(File.getBlockSize(), Stream); 218 219 NewLine(); 220 OS << Label << " ("; 221 while (Reader.bytesRemaining() > 0) { 222 OS << "\n"; 223 224 Run FoundRun; 225 uint32_t RunOffset; 226 std::tie(FoundRun, RunOffset) = findRun(Substream.Offset, Runs); 227 assert(FoundRun.ByteLen >= RunOffset); 228 uint32_t Len = FoundRun.ByteLen - RunOffset; 229 Len = std::min(Len, Reader.bytesRemaining()); 230 uint64_t Base = FoundRun.Block * File.getBlockSize() + RunOffset; 231 ArrayRef<uint8_t> Data; 232 consumeError(Reader.readBytes(Data, Len)); 233 OS << format_bytes_with_ascii(Data, Base, 32, 4, 234 CurrentIndent + IndentSpaces, true); 235 if (Reader.bytesRemaining() > 0) { 236 NewLine(); 237 OS << formatv(" {0}", 238 fmt_align("<discontinuity>", AlignStyle::Center, 114, '-')); 239 } 240 Substream.Offset += Len; 241 } 242 NewLine(); 243 OS << ")"; 244} 245 246void LinePrinter::formatMsfStreamBlocks( 247 PDBFile &File, const msf::MSFStreamLayout &StreamLayout) { 248 auto Blocks = makeArrayRef(StreamLayout.Blocks); 249 uint32_t L = StreamLayout.Length; 250 251 while (L > 0) { 252 NewLine(); 253 assert(!Blocks.empty()); 254 OS << formatv("Block {0} (\n", uint32_t(Blocks.front())); 255 uint32_t UsedBytes = std::min(L, File.getBlockSize()); 256 ArrayRef<uint8_t> BlockData = 257 cantFail(File.getBlockData(Blocks.front(), File.getBlockSize())); 258 uint64_t BaseOffset = Blocks.front(); 259 BaseOffset *= File.getBlockSize(); 260 OS << format_bytes_with_ascii(BlockData, BaseOffset, 32, 4, 261 CurrentIndent + IndentSpaces, true); 262 NewLine(); 263 OS << ")"; 264 NewLine(); 265 L -= UsedBytes; 266 Blocks = Blocks.drop_front(); 267 } 268} 269 270bool LinePrinter::IsTypeExcluded(llvm::StringRef TypeName, uint32_t Size) { 271 if (IsItemExcluded(TypeName, IncludeTypeFilters, ExcludeTypeFilters)) 272 return true; 273 if (Size < opts::pretty::SizeThreshold) 274 return true; 275 return false; 276} 277 278bool LinePrinter::IsSymbolExcluded(llvm::StringRef SymbolName) { 279 return IsItemExcluded(SymbolName, IncludeSymbolFilters, ExcludeSymbolFilters); 280} 281 282bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) { 283 return IsItemExcluded(CompilandName, IncludeCompilandFilters, 284 ExcludeCompilandFilters); 285} 286 287WithColor::WithColor(LinePrinter &P, PDB_ColorItem C) 288 : OS(P.OS), UseColor(P.hasColor()) { 289 if (UseColor) 290 applyColor(C); 291} 292 293WithColor::~WithColor() { 294 if (UseColor) 295 OS.resetColor(); 296} 297 298void WithColor::applyColor(PDB_ColorItem C) { 299 switch (C) { 300 case PDB_ColorItem::None: 301 OS.resetColor(); 302 return; 303 case PDB_ColorItem::Comment: 304 OS.changeColor(raw_ostream::GREEN, false); 305 return; 306 case PDB_ColorItem::Address: 307 OS.changeColor(raw_ostream::YELLOW, /*bold=*/true); 308 return; 309 case PDB_ColorItem::Keyword: 310 OS.changeColor(raw_ostream::MAGENTA, true); 311 return; 312 case PDB_ColorItem::Register: 313 case PDB_ColorItem::Offset: 314 OS.changeColor(raw_ostream::YELLOW, false); 315 return; 316 case PDB_ColorItem::Type: 317 OS.changeColor(raw_ostream::CYAN, true); 318 return; 319 case PDB_ColorItem::Identifier: 320 OS.changeColor(raw_ostream::CYAN, false); 321 return; 322 case PDB_ColorItem::Path: 323 OS.changeColor(raw_ostream::CYAN, false); 324 return; 325 case PDB_ColorItem::Padding: 326 case PDB_ColorItem::SectionHeader: 327 OS.changeColor(raw_ostream::RED, true); 328 return; 329 case PDB_ColorItem::LiteralValue: 330 OS.changeColor(raw_ostream::GREEN, true); 331 return; 332 } 333} 334