OLD | NEW |
(Empty) | |
| 1 //===-- NaClBitcodeAnalyzer.cpp - Bitcode Analyzer ------------------------===// |
| 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 #define DEBUG_TYPE "nacl-bitcode-analyzer" |
| 11 |
| 12 #include "llvm/ADT/STLExtras.h" |
| 13 #include "llvm/ADT/Twine.h" |
| 14 #include "llvm/Bitcode/NaCl/NaClAnalyzerBlockDist.h" |
| 15 #include "llvm/Bitcode/NaCl/NaClBitcodeAnalyzer.h" |
| 16 #include "llvm/Bitcode/NaCl/NaClBitcodeHeader.h" |
| 17 #include "llvm/Bitcode/NaCl/NaClBitcodeParser.h" |
| 18 #include "llvm/Bitcode/NaCl/NaClBitstreamReader.h" |
| 19 #include "llvm/Bitcode/NaCl/NaClLLVMBitCodes.h" |
| 20 #include "llvm/Bitcode/NaCl/NaClReaderWriter.h" |
| 21 #include "llvm/Support/Debug.h" |
| 22 #include "llvm/Support/Format.h" |
| 23 #include "llvm/Support/MemoryBuffer.h" |
| 24 #include "llvm/Support/raw_ostream.h" |
| 25 #include <algorithm> |
| 26 #include <map> |
| 27 #include <system_error> |
| 28 |
| 29 // TODO(kschimpf): Separate out into two bitcode parsers, one for |
| 30 // dumping records, and one for collecting distribution stats for |
| 31 // printing. This should simplify the code. |
| 32 |
| 33 /// Error - All bitcode analysis errors go through this function, making this a |
| 34 /// good place to breakpoint if debugging. |
| 35 static bool Error(const llvm::Twine &Err) { |
| 36 llvm::errs() << Err << "\n"; |
| 37 return true; |
| 38 } |
| 39 |
| 40 namespace llvm { |
| 41 |
| 42 // Parses all bitcode blocks, and collects distribution of records in |
| 43 // each block. Also dumps bitcode structure if specified (via global |
| 44 // variables). |
| 45 class PNaClBitcodeAnalyzerParser : public NaClBitcodeParser { |
| 46 public: |
| 47 PNaClBitcodeAnalyzerParser(NaClBitstreamCursor &Cursor, |
| 48 raw_ostream &OS, |
| 49 const AnalysisDumpOptions &DumpOptions, |
| 50 NaClBitcodeDist *Dist) |
| 51 : NaClBitcodeParser(Cursor), |
| 52 IndentLevel(0), |
| 53 OS(OS), |
| 54 DumpOptions(DumpOptions), |
| 55 Dist(Dist), |
| 56 AbbrevListener(this) |
| 57 { |
| 58 SetListener(&AbbrevListener); |
| 59 } |
| 60 |
| 61 virtual ~PNaClBitcodeAnalyzerParser() {} |
| 62 |
| 63 virtual bool Error(const std::string &Message) { |
| 64 // Use local error routine so that all errors are treated uniformly. |
| 65 return ::Error(Message); |
| 66 } |
| 67 |
| 68 virtual bool ParseBlock(unsigned BlockID); |
| 69 |
| 70 // Returns the string defining the indentation to use with respect |
| 71 // to the current indent level. |
| 72 const std::string &GetIndentation() { |
| 73 size_t Size = IndentationCache.size(); |
| 74 if (IndentLevel >= Size) { |
| 75 IndentationCache.resize(IndentLevel+1); |
| 76 for (size_t i = Size; i <= IndentLevel; ++i) { |
| 77 IndentationCache[i] = std::string(i*2, ' '); |
| 78 } |
| 79 } |
| 80 return IndentationCache[IndentLevel]; |
| 81 } |
| 82 |
| 83 // Keeps track of current indentation level based on block nesting. |
| 84 unsigned IndentLevel; |
| 85 // The output stream to print to. |
| 86 raw_ostream &OS; |
| 87 // The dump options to use. |
| 88 const AnalysisDumpOptions &DumpOptions; |
| 89 // The bitcode distribution map (if defined) to update. |
| 90 NaClBitcodeDist *Dist; |
| 91 |
| 92 private: |
| 93 // The set of cached, indentation strings. Used for indenting |
| 94 // records when dumping. |
| 95 std::vector<std::string> IndentationCache; |
| 96 // Listener used to get abbreviations as they are read. |
| 97 NaClBitcodeParserListener AbbrevListener; |
| 98 }; |
| 99 |
| 100 // Parses a bitcode block, and collects distribution of records in that block. |
| 101 // Also dumps bitcode structure if specified (via global variables). |
| 102 class PNaClBitcodeAnalyzerBlockParser : public NaClBitcodeParser { |
| 103 public: |
| 104 // Parses top-level block. |
| 105 PNaClBitcodeAnalyzerBlockParser( |
| 106 unsigned BlockID, |
| 107 PNaClBitcodeAnalyzerParser *Parser) |
| 108 : NaClBitcodeParser(BlockID, Parser) { |
| 109 Initialize(BlockID, Parser); |
| 110 } |
| 111 |
| 112 virtual ~PNaClBitcodeAnalyzerBlockParser() { |
| 113 if (Context->Dist) Context->Dist->AddBlock(GetBlock()); |
| 114 } |
| 115 |
| 116 // ***************************************************** |
| 117 // This subsection Defines an XML generator for the dump. |
| 118 // Tag. TagName, Attribute |
| 119 // ***************************************************** |
| 120 |
| 121 private: |
| 122 // The tag name for an element. |
| 123 std::string TagName; |
| 124 // The number of attributes associated with a tag. |
| 125 unsigned NumTagAttributes; |
| 126 // The number of (indexed attribute) operands associated with a tag. |
| 127 unsigned NumTagOperands; |
| 128 |
| 129 protected: |
| 130 |
| 131 /// Initializes internal data used to emit an XML tag. |
| 132 void InitializeEmitTag() { |
| 133 TagName.clear(); |
| 134 NumTagAttributes = 0; |
| 135 NumTagOperands = 0; |
| 136 } |
| 137 |
| 138 /// Emits the start of an XML start tag. |
| 139 void EmitBeginStartTag() { |
| 140 InitializeEmitTag(); |
| 141 Context->OS << Indent << "<"; |
| 142 } |
| 143 |
| 144 /// Emit the start of an XML end tag. |
| 145 void EmitBeginEndTag() { |
| 146 InitializeEmitTag(); |
| 147 Context->OS << Indent << "</"; |
| 148 } |
| 149 |
| 150 /// Emits the end of an empty-element XML tag. |
| 151 void EmitEndTag() { |
| 152 Context->OS << "/>\n"; |
| 153 } |
| 154 |
| 155 /// Emits the End of a start/end tag for an XML element. |
| 156 void EmitEndElementTag() { |
| 157 Context->OS << ">\n"; |
| 158 } |
| 159 |
| 160 /// Emits the tag name for an XML tag. |
| 161 void EmitTagName(const std::string &ElmtName) { |
| 162 TagName = ElmtName; |
| 163 Context->OS << ElmtName; |
| 164 } |
| 165 |
| 166 /// Emits the "name=" portion of an XML tag attribute. |
| 167 raw_ostream &EmitAttributePrefix(const std::string &AttributeName) { |
| 168 WrapOperandsLine(); |
| 169 Context->OS << " " << AttributeName << "="; |
| 170 ++NumTagAttributes; |
| 171 return Context->OS; |
| 172 } |
| 173 |
| 174 /// Emits a string-valued XML attribute of an XML tag. |
| 175 void EmitStringAttribute(const char *AttributeName, const std::string &Str) { |
| 176 EmitAttributePrefix(AttributeName) << "'" << Str << "'"; |
| 177 } |
| 178 |
| 179 /// Emits a string-valued XML attribute of an XML tag. |
| 180 void EmitStringAttribute(const char *AttributeName, const char*Str) { |
| 181 std::string StrStr(Str); |
| 182 EmitStringAttribute(AttributeName, StrStr); |
| 183 } |
| 184 |
| 185 /// Emits an unsigned integer-valued XML attribute of an XML tag. |
| 186 void EmitAttribute(const char *AttributeName, uint64_t Value) { |
| 187 EmitAttributePrefix(AttributeName) << Value; |
| 188 } |
| 189 |
| 190 /// Emits the "opN=" portion of an XML tag (indexable) operand attribute. |
| 191 raw_ostream &EmitOperandPrefix() { |
| 192 std::string OpName; |
| 193 raw_string_ostream OpNameStrm(OpName); |
| 194 OpNameStrm << "op" << NumTagOperands; |
| 195 ++NumTagOperands; |
| 196 return EmitAttributePrefix(OpNameStrm.str()); |
| 197 } |
| 198 |
| 199 /// Adds line wrap if more than "OpsPerLine" XML tag attributes are |
| 200 /// emitted on the current line. |
| 201 void WrapOperandsLine() { |
| 202 if (Context->DumpOptions.OpsPerLine) { |
| 203 if (NumTagAttributes && |
| 204 (NumTagAttributes % Context->DumpOptions.OpsPerLine) == 0) { |
| 205 raw_ostream &OS = Context->OS; |
| 206 // Last operand crossed width boundary, add newline and indent. |
| 207 OS << "\n" << Indent << " "; |
| 208 for (unsigned J = 0, SZ = TagName.size(); J < SZ; ++J) |
| 209 OS << " "; |
| 210 } |
| 211 } |
| 212 } |
| 213 |
| 214 // ******************************************************** |
| 215 // This section defines how to parse the block and generate |
| 216 // the corresponding XML. |
| 217 // ******************************************************** |
| 218 |
| 219 // Parses nested blocks. |
| 220 PNaClBitcodeAnalyzerBlockParser( |
| 221 unsigned BlockID, |
| 222 PNaClBitcodeAnalyzerBlockParser *EnclosingBlock) |
| 223 : NaClBitcodeParser(BlockID, EnclosingBlock), |
| 224 Context(EnclosingBlock->Context) { |
| 225 Initialize(BlockID, EnclosingBlock->Context); |
| 226 } |
| 227 |
| 228 // Initialize data associated with a block. |
| 229 void Initialize(unsigned BlockID, PNaClBitcodeAnalyzerParser *Parser) { |
| 230 InitializeEmitTag(); |
| 231 Context = Parser; |
| 232 if (Context->DumpOptions.DumpRecords) { |
| 233 Indent = Parser->GetIndentation(); |
| 234 } |
| 235 NumWords = 0; |
| 236 } |
| 237 |
| 238 // Increment the indentation level for dumping. |
| 239 void IncrementIndent() { |
| 240 Context->IndentLevel++; |
| 241 Indent = Context->GetIndentation(); |
| 242 } |
| 243 |
| 244 // Increment the indentation level for dumping. |
| 245 void DecrementIndent() { |
| 246 Context->IndentLevel--; |
| 247 Indent = Context->GetIndentation(); |
| 248 } |
| 249 |
| 250 virtual bool Error(const std::string &Message) { |
| 251 // Use local error routine so that all errors are treated uniformly. |
| 252 return ::Error(Message); |
| 253 } |
| 254 |
| 255 // Called once the block has been entered by the bitstream reader. |
| 256 // Argument NumWords is set to the number of words in the |
| 257 // corresponding block. |
| 258 virtual void EnterBlock(unsigned NumberWords) { |
| 259 NumWords = NumberWords; |
| 260 if (Context->DumpOptions.DumpRecords) { |
| 261 unsigned BlockID = GetBlockID(); |
| 262 EmitBeginStartTag(); |
| 263 EmitEnterBlockTagName(BlockID); |
| 264 if (Context->DumpOptions.DumpDetails) { |
| 265 EmitAttribute("NumWords", NumWords); |
| 266 EmitAttribute("BlockCodeSize", Record.GetCursor().getAbbrevIDWidth()); |
| 267 } |
| 268 if (!Context->DumpOptions.DumpDetails && |
| 269 naclbitc::BLOCKINFO_BLOCK_ID == GetBlockID()) { |
| 270 EmitEndTag(); |
| 271 } else { |
| 272 EmitEndElementTag(); |
| 273 IncrementIndent(); |
| 274 } |
| 275 } |
| 276 } |
| 277 |
| 278 // Called when the corresponding EndBlock of the block being parsed |
| 279 // is found. |
| 280 virtual void ExitBlock() { |
| 281 if (Context->DumpOptions.DumpRecords) { |
| 282 if (!Context->DumpOptions.DumpDetails && |
| 283 naclbitc::BLOCKINFO_BLOCK_ID == GetBlockID()) |
| 284 return; |
| 285 DecrementIndent(); |
| 286 EmitBeginEndTag(); |
| 287 EmitExitBlockTagName(Record.GetBlockID()); |
| 288 EmitEndElementTag(); |
| 289 } |
| 290 } |
| 291 |
| 292 virtual void SetBID() { |
| 293 if (!(Context->DumpOptions.DumpRecords && |
| 294 Context->DumpOptions.DumpDetails)) { |
| 295 return; |
| 296 } |
| 297 EmitBeginStartTag(); |
| 298 EmitCodeTagName(naclbitc::BLOCKINFO_CODE_SETBID, |
| 299 naclbitc::BLOCKINFO_BLOCK_ID); |
| 300 EmitStringAttribute("block", |
| 301 NaClBitcodeBlockDist::GetName(Record.GetValues()[0])); |
| 302 EmitEndTag(); |
| 303 } |
| 304 |
| 305 virtual void ProcessAbbreviation(unsigned BlockID, |
| 306 NaClBitCodeAbbrev *Abbrev, |
| 307 bool IsLocal) { |
| 308 if (Context->DumpOptions.DumpDetails) { |
| 309 EmitAbbreviation(Abbrev); |
| 310 } |
| 311 } |
| 312 |
| 313 // Process the last read record in the block. |
| 314 virtual void ProcessRecord() { |
| 315 // Increment the # occurrences of this code. |
| 316 if (Context->Dist) Context->Dist->AddRecord(Record); |
| 317 |
| 318 if (Context->DumpOptions.DumpRecords) { |
| 319 EmitBeginStartTag(); |
| 320 EmitCodeTagName(Record.GetCode(), GetBlockID(), Record.GetEntryID()); |
| 321 const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
| 322 for (unsigned i = 0, e = Values.size(); i != e; ++i) { |
| 323 EmitOperandPrefix() << (int64_t)Values[i]; |
| 324 } |
| 325 EmitEndTag(); |
| 326 } |
| 327 } |
| 328 |
| 329 virtual bool ParseBlock(unsigned BlockID) { |
| 330 PNaClBitcodeAnalyzerBlockParser Parser(BlockID, this); |
| 331 return Parser.ParseThisBlock(); |
| 332 } |
| 333 |
| 334 private: |
| 335 /// Defines the indent level of the block being parsed. |
| 336 std::string Indent; |
| 337 /// Defines the number of (32-bit) words the block occupies in |
| 338 /// the bitstream. |
| 339 unsigned NumWords; |
| 340 /// Refers to global parsing context. |
| 341 PNaClBitcodeAnalyzerParser *Context; |
| 342 |
| 343 protected: |
| 344 |
| 345 /// Emit the given abbreviation as an XML tag. |
| 346 void EmitAbbreviation(const NaClBitCodeAbbrev *Abbrev) { |
| 347 EmitBeginStartTag(); |
| 348 EmitTagName("DEFINE_ABBREV"); |
| 349 if (Context->DumpOptions.DumpDetails) { |
| 350 EmitStringAttribute("abbrev", "DEFINE_ABBREV"); |
| 351 } |
| 352 for (unsigned I = 0, IEnd = Abbrev->getNumOperandInfos(); I != IEnd; ++I) { |
| 353 EmitAbbreviationOp(Abbrev->getOperandInfo(I)); |
| 354 } |
| 355 EmitEndTag(); |
| 356 } |
| 357 |
| 358 /// Emit the given abbreviation operand as an XML tag attribute. |
| 359 void EmitAbbreviationOp(const NaClBitCodeAbbrevOp &Op) { |
| 360 EmitOperandPrefix() |
| 361 << "'" << NaClBitCodeAbbrevOp::getEncodingName(Op.getEncoding()); |
| 362 if (Op.hasValue()) { |
| 363 Context->OS << "(" << Op.getValue() << ")"; |
| 364 } |
| 365 Context->OS << "'"; |
| 366 } |
| 367 |
| 368 /// Emits the symbolic name of the record code as the XML tag name. |
| 369 void EmitCodeTagName( |
| 370 unsigned CodeID, unsigned BlockID, |
| 371 unsigned AbbreviationID = naclbitc::UNABBREV_RECORD) { |
| 372 EmitTagName(NaClBitcodeCodeDist::GetCodeName(CodeID, BlockID)); |
| 373 if (Context->DumpOptions.DumpDetails) { |
| 374 if (AbbreviationID == naclbitc::UNABBREV_RECORD) { |
| 375 EmitStringAttribute("abbrev", "UNABBREVIATED"); |
| 376 } else { |
| 377 EmitAttribute("abbrev", AbbreviationID); |
| 378 } |
| 379 } |
| 380 } |
| 381 |
| 382 /// Emits the symbolic name of the block as the XML tag name. |
| 383 void EmitEnterBlockTagName(unsigned BlockID) { |
| 384 EmitTagName(NaClBitcodeBlockDist::GetName(BlockID)); |
| 385 if (Context->DumpOptions.DumpDetails) |
| 386 EmitStringAttribute("abbrev", "ENTER_SUBBLOCK"); |
| 387 } |
| 388 |
| 389 /// Emits the symbolic name of the block as the the XML tag name. |
| 390 void EmitExitBlockTagName(unsigned BlockID) { |
| 391 EmitTagName(NaClBitcodeBlockDist::GetName(BlockID)); |
| 392 if (Context->DumpOptions.DumpDetails) |
| 393 EmitStringAttribute("abbrev", "END_BLOCK"); |
| 394 } |
| 395 }; |
| 396 |
| 397 bool PNaClBitcodeAnalyzerParser::ParseBlock(unsigned BlockID) { |
| 398 PNaClBitcodeAnalyzerBlockParser Parser(BlockID, this); |
| 399 return Parser.ParseThisBlock(); |
| 400 } |
| 401 |
| 402 static void PrintSize(uint64_t Bits, raw_ostream &OS) { |
| 403 OS << format("%lub/%.2fB/%luW", (unsigned long)Bits, |
| 404 (double)Bits/8, (unsigned long)(Bits/32)); |
| 405 } |
| 406 |
| 407 int AnalyzeBitcodeInBuffer(const std::unique_ptr<MemoryBuffer> &Buf, |
| 408 raw_ostream &OS, |
| 409 const AnalysisDumpOptions &DumpOptions) { |
| 410 DEBUG(dbgs() << "-> AnalyzeBitcodeInBuffer\n"); |
| 411 |
| 412 if (Buf->getBufferSize() & 3) |
| 413 return Error("Bitcode stream should be a multiple of 4 bytes in length"); |
| 414 |
| 415 const unsigned char *BufPtr = (const unsigned char *)Buf->getBufferStart(); |
| 416 const unsigned char *EndBufPtr = BufPtr + Buf->getBufferSize(); |
| 417 |
| 418 NaClBitcodeHeader Header; |
| 419 if (Header.Read(BufPtr, EndBufPtr)) |
| 420 return Error("Invalid PNaCl bitcode header"); |
| 421 |
| 422 if (!Header.IsSupported()) |
| 423 errs() << "Warning: " << Header.Unsupported() << "\n"; |
| 424 |
| 425 if (!Header.IsReadable()) |
| 426 Error("Bitcode file is not readable"); |
| 427 |
| 428 NaClBitstreamReader StreamFile(BufPtr, EndBufPtr); |
| 429 NaClBitstreamCursor Stream(StreamFile); |
| 430 |
| 431 unsigned NumTopBlocks = 0; |
| 432 |
| 433 // Print out header information. |
| 434 for (size_t i = 0, limit = Header.NumberFields(); i < limit; ++i) { |
| 435 OS << Header.GetField(i)->Contents() << "\n"; |
| 436 } |
| 437 if (Header.NumberFields()) OS << "\n"; |
| 438 |
| 439 // Parse the top-level structure. We only allow blocks at the top-level. |
| 440 NaClAnalyzerBlockDistElement DistSentinel(0, DumpOptions.OrderBlocksByID); |
| 441 NaClAnalyzerBlockDist Dist(DistSentinel); |
| 442 PNaClBitcodeAnalyzerParser Parser(Stream, OS, DumpOptions, &Dist); |
| 443 while (!Stream.AtEndOfStream()) { |
| 444 ++NumTopBlocks; |
| 445 if (Parser.Parse()) return 1; |
| 446 } |
| 447 |
| 448 if (DumpOptions.DumpRecords) return 0; |
| 449 |
| 450 uint64_t BufferSizeBits = (EndBufPtr-BufPtr)*CHAR_BIT; |
| 451 // Print a summary |
| 452 OS << "Total size: "; |
| 453 PrintSize(BufferSizeBits, OS); |
| 454 OS << "\n"; |
| 455 OS << "# Toplevel Blocks: " << NumTopBlocks << "\n"; |
| 456 OS << "\n"; |
| 457 |
| 458 if (Parser.Dist) Parser.Dist->Print(OS); |
| 459 |
| 460 DEBUG(dbgs() << "<- AnalyzeBitcode\n"); |
| 461 return 0; |
| 462 } |
| 463 |
| 464 int AnalyzeBitcodeInFile(const StringRef &InputFilename, raw_ostream &OS, |
| 465 const AnalysisDumpOptions &DumpOptions) { |
| 466 ErrorOr<std::unique_ptr<MemoryBuffer>> ErrOrFile = |
| 467 MemoryBuffer::getFileOrSTDIN(InputFilename); |
| 468 if (std::error_code EC = ErrOrFile.getError()) |
| 469 return Error(Twine("Error reading '") + InputFilename + "': " + |
| 470 EC.message()); |
| 471 |
| 472 return AnalyzeBitcodeInBuffer(ErrOrFile.get(), OS, DumpOptions); |
| 473 } |
| 474 |
| 475 } // namespace llvm |
OLD | NEW |