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