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