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