| 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(String: 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(Range&: IncludeFilters, P: match_pred)) |
| 44 | return true; |
| 45 | |
| 46 | if (any_of(Range&: ExcludeFilters, P: 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(List&: ExcludeTypeFilters, Begin: Filters.ExcludeTypes.begin(), |
| 60 | End: Filters.ExcludeTypes.end()); |
| 61 | SetFilters(List&: ExcludeSymbolFilters, Begin: Filters.ExcludeSymbols.begin(), |
| 62 | End: Filters.ExcludeSymbols.end()); |
| 63 | SetFilters(List&: ExcludeCompilandFilters, Begin: Filters.ExcludeCompilands.begin(), |
| 64 | End: Filters.ExcludeCompilands.end()); |
| 65 | |
| 66 | SetFilters(List&: IncludeTypeFilters, Begin: Filters.IncludeTypes.begin(), |
| 67 | End: Filters.IncludeTypes.end()); |
| 68 | SetFilters(List&: IncludeSymbolFilters, Begin: Filters.IncludeSymbols.begin(), |
| 69 | End: Filters.IncludeSymbols.end()); |
| 70 | SetFilters(List&: IncludeCompilandFilters, Begin: Filters.IncludeCompilands.begin(), |
| 71 | End: 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>(a: 0, b: CurrentIndent - Amount); |
| 84 | } |
| 85 | |
| 86 | void LinePrinter::NewLine() { |
| 87 | OS << "\n" ; |
| 88 | OS.indent(NumSpaces: 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(TypeName: Class.getName(), Size: 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(Bytes: Data, FirstByteOffset: StartOffset, NumPerLine: 32, ByteGroupSize: 4, |
| 113 | IndentLevel: CurrentIndent + IndentSpaces, Upper: 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(Bytes: Data, FirstByteOffset: Base, NumPerLine: 32, ByteGroupSize: 4, |
| 127 | IndentLevel: CurrentIndent + IndentSpaces, Upper: 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(args&: 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(args&: NextBlock); |
| 158 | CurrentRun = &Runs.back(); |
| 159 | } |
| 160 | uint64_t Used = |
| 161 | std::min(a: static_cast<uint64_t>(BlockSize), b: 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(x: R, y&: 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(Fmt: "Stream {0}: Not present" , Items&: StreamIdx); |
| 185 | return; |
| 186 | } |
| 187 | if (Size + Offset > File.getStreamByteSize(StreamIndex: StreamIdx)) { |
| 188 | formatLine( |
| 189 | Fmt: "Stream {0}: Invalid offset and size, range out of stream bounds" , |
| 190 | Items&: StreamIdx); |
| 191 | return; |
| 192 | } |
| 193 | |
| 194 | auto S = File.createIndexedStream(SN: StreamIdx); |
| 195 | if (!S) { |
| 196 | NewLine(); |
| 197 | formatLine(Fmt: "Stream {0}: Not present" , Items&: StreamIdx); |
| 198 | return; |
| 199 | } |
| 200 | |
| 201 | uint64_t End = |
| 202 | (Size == 0) ? S->getLength() : std::min(a: Offset + Size, b: S->getLength()); |
| 203 | Size = End - Offset; |
| 204 | |
| 205 | formatLine(Fmt: "Stream {0}: {1} (dumping {2:N} / {3:N} bytes)" , Items&: StreamIdx, |
| 206 | Items&: StreamPurpose, Items&: Size, Items: S->getLength()); |
| 207 | AutoIndent Indent(*this); |
| 208 | BinaryStreamRef Slice(*S); |
| 209 | BinarySubstreamRef Substream; |
| 210 | Substream.Offset = Offset; |
| 211 | Substream.StreamData = Slice.drop_front(N: Offset).keep_front(N: Size); |
| 212 | |
| 213 | auto Layout = File.getStreamLayout(StreamIdx); |
| 214 | formatMsfStreamData(Label, File, Stream: 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(BlockSize: File.getBlockSize(), Layout: 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(args&: FoundRun, args&: RunOffset) = findRun(Offset: Substream.Offset, Runs); |
| 232 | assert(FoundRun.ByteLen >= RunOffset); |
| 233 | uint64_t Len = FoundRun.ByteLen - RunOffset; |
| 234 | Len = std::min(a: Len, b: Reader.bytesRemaining()); |
| 235 | uint64_t Base = FoundRun.Block * File.getBlockSize() + RunOffset; |
| 236 | ArrayRef<uint8_t> Data; |
| 237 | consumeError(Err: Reader.readBytes(Buffer&: Data, Size: Len)); |
| 238 | OS << format_bytes_with_ascii(Bytes: Data, FirstByteOffset: Base, NumPerLine: 32, ByteGroupSize: 4, |
| 239 | IndentLevel: CurrentIndent + IndentSpaces, Upper: true); |
| 240 | if (Reader.bytesRemaining() > 0) { |
| 241 | NewLine(); |
| 242 | OS << formatv(Fmt: " {0}" , |
| 243 | Vals: fmt_align(Item: "<discontinuity>" , Where: AlignStyle::Center, Amount: 114, Fill: '-')); |
| 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(Fmt: "Block {0} (\n" , Vals: uint32_t(Blocks.front())); |
| 260 | uint64_t UsedBytes = |
| 261 | std::min(a: L, b: static_cast<uint64_t>(File.getBlockSize())); |
| 262 | ArrayRef<uint8_t> BlockData = |
| 263 | cantFail(ValOrErr: File.getBlockData(BlockIndex: Blocks.front(), NumBytes: File.getBlockSize())); |
| 264 | uint64_t BaseOffset = Blocks.front(); |
| 265 | BaseOffset *= File.getBlockSize(); |
| 266 | OS << format_bytes_with_ascii(Bytes: BlockData, FirstByteOffset: BaseOffset, NumPerLine: 32, ByteGroupSize: 4, |
| 267 | IndentLevel: CurrentIndent + IndentSpaces, Upper: 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(Item: TypeName, IncludeFilters&: IncludeTypeFilters, ExcludeFilters&: 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(Item: SymbolName, IncludeFilters&: IncludeSymbolFilters, ExcludeFilters&: ExcludeSymbolFilters); |
| 286 | } |
| 287 | |
| 288 | bool LinePrinter::IsCompilandExcluded(llvm::StringRef CompilandName) { |
| 289 | return IsItemExcluded(Item: CompilandName, IncludeFilters&: IncludeCompilandFilters, |
| 290 | ExcludeFilters&: 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(Color: raw_ostream::GREEN, Bold: false); |
| 311 | return; |
| 312 | case PDB_ColorItem::Address: |
| 313 | OS.changeColor(Color: raw_ostream::YELLOW, /*bold=*/Bold: true); |
| 314 | return; |
| 315 | case PDB_ColorItem::Keyword: |
| 316 | OS.changeColor(Color: raw_ostream::MAGENTA, Bold: true); |
| 317 | return; |
| 318 | case PDB_ColorItem::Register: |
| 319 | case PDB_ColorItem::Offset: |
| 320 | OS.changeColor(Color: raw_ostream::YELLOW, Bold: false); |
| 321 | return; |
| 322 | case PDB_ColorItem::Type: |
| 323 | OS.changeColor(Color: raw_ostream::CYAN, Bold: true); |
| 324 | return; |
| 325 | case PDB_ColorItem::Identifier: |
| 326 | OS.changeColor(Color: raw_ostream::CYAN, Bold: false); |
| 327 | return; |
| 328 | case PDB_ColorItem::Path: |
| 329 | OS.changeColor(Color: raw_ostream::CYAN, Bold: false); |
| 330 | return; |
| 331 | case PDB_ColorItem::Padding: |
| 332 | case PDB_ColorItem::SectionHeader: |
| 333 | OS.changeColor(Color: raw_ostream::RED, Bold: true); |
| 334 | return; |
| 335 | case PDB_ColorItem::LiteralValue: |
| 336 | OS.changeColor(Color: raw_ostream::GREEN, Bold: true); |
| 337 | return; |
| 338 | } |
| 339 | } |
| 340 | |