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