Index: lib/Bitcode/NaCl/Analysis/NaClBitcodeAnalyzer.cpp |
diff --git a/lib/Bitcode/NaCl/Analysis/NaClBitcodeAnalyzer.cpp b/lib/Bitcode/NaCl/Analysis/NaClBitcodeAnalyzer.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..09c507822a4374a8fb97f4fc899b3d277fda8b69 |
--- /dev/null |
+++ b/lib/Bitcode/NaCl/Analysis/NaClBitcodeAnalyzer.cpp |
@@ -0,0 +1,475 @@ |
+//===-- NaClBitcodeAnalyzer.cpp - Bitcode Analyzer ------------------------===// |
+// |
+// The LLVM Compiler Infrastructure |
+// |
+// This file is distributed under the University of Illinois Open Source |
+// License. See LICENSE.TXT for details. |
+// |
+//===----------------------------------------------------------------------===// |
+ |
+#define DEBUG_TYPE "nacl-bitcode-analyzer" |
+ |
+#include "llvm/ADT/STLExtras.h" |
+#include "llvm/ADT/Twine.h" |
+#include "llvm/Bitcode/NaCl/NaClAnalyzerBlockDist.h" |
+#include "llvm/Bitcode/NaCl/NaClBitcodeAnalyzer.h" |
+#include "llvm/Bitcode/NaCl/NaClBitcodeHeader.h" |
+#include "llvm/Bitcode/NaCl/NaClBitcodeParser.h" |
+#include "llvm/Bitcode/NaCl/NaClBitstreamReader.h" |
+#include "llvm/Bitcode/NaCl/NaClLLVMBitCodes.h" |
+#include "llvm/Bitcode/NaCl/NaClReaderWriter.h" |
+#include "llvm/Support/Debug.h" |
+#include "llvm/Support/Format.h" |
+#include "llvm/Support/MemoryBuffer.h" |
+#include "llvm/Support/raw_ostream.h" |
+#include <algorithm> |
+#include <map> |
+#include <system_error> |
+ |
+// TODO(kschimpf): Separate out into two bitcode parsers, one for |
+// dumping records, and one for collecting distribution stats for |
+// printing. This should simplify the code. |
+ |
+/// Error - All bitcode analysis errors go through this function, making this a |
+/// good place to breakpoint if debugging. |
+static bool Error(const llvm::Twine &Err) { |
+ llvm::errs() << Err << "\n"; |
+ return true; |
+} |
+ |
+namespace llvm { |
+ |
+// Parses all bitcode blocks, and collects distribution of records in |
+// each block. Also dumps bitcode structure if specified (via global |
+// variables). |
+class PNaClBitcodeAnalyzerParser : public NaClBitcodeParser { |
+public: |
+ PNaClBitcodeAnalyzerParser(NaClBitstreamCursor &Cursor, |
+ raw_ostream &OS, |
+ const AnalysisDumpOptions &DumpOptions, |
+ NaClBitcodeDist *Dist) |
+ : NaClBitcodeParser(Cursor), |
+ IndentLevel(0), |
+ OS(OS), |
+ DumpOptions(DumpOptions), |
+ Dist(Dist), |
+ AbbrevListener(this) |
+ { |
+ SetListener(&AbbrevListener); |
+ } |
+ |
+ virtual ~PNaClBitcodeAnalyzerParser() {} |
+ |
+ virtual bool Error(const std::string &Message) { |
+ // Use local error routine so that all errors are treated uniformly. |
+ return ::Error(Message); |
+ } |
+ |
+ virtual bool ParseBlock(unsigned BlockID); |
+ |
+ // Returns the string defining the indentation to use with respect |
+ // to the current indent level. |
+ const std::string &GetIndentation() { |
+ size_t Size = IndentationCache.size(); |
+ if (IndentLevel >= Size) { |
+ IndentationCache.resize(IndentLevel+1); |
+ for (size_t i = Size; i <= IndentLevel; ++i) { |
+ IndentationCache[i] = std::string(i*2, ' '); |
+ } |
+ } |
+ return IndentationCache[IndentLevel]; |
+ } |
+ |
+ // Keeps track of current indentation level based on block nesting. |
+ unsigned IndentLevel; |
+ // The output stream to print to. |
+ raw_ostream &OS; |
+ // The dump options to use. |
+ const AnalysisDumpOptions &DumpOptions; |
+ // The bitcode distribution map (if defined) to update. |
+ NaClBitcodeDist *Dist; |
+ |
+private: |
+ // The set of cached, indentation strings. Used for indenting |
+ // records when dumping. |
+ std::vector<std::string> IndentationCache; |
+ // Listener used to get abbreviations as they are read. |
+ NaClBitcodeParserListener AbbrevListener; |
+}; |
+ |
+// Parses a bitcode block, and collects distribution of records in that block. |
+// Also dumps bitcode structure if specified (via global variables). |
+class PNaClBitcodeAnalyzerBlockParser : public NaClBitcodeParser { |
+public: |
+ // Parses top-level block. |
+ PNaClBitcodeAnalyzerBlockParser( |
+ unsigned BlockID, |
+ PNaClBitcodeAnalyzerParser *Parser) |
+ : NaClBitcodeParser(BlockID, Parser) { |
+ Initialize(BlockID, Parser); |
+ } |
+ |
+ virtual ~PNaClBitcodeAnalyzerBlockParser() { |
+ if (Context->Dist) Context->Dist->AddBlock(GetBlock()); |
+ } |
+ |
+ // ***************************************************** |
+ // This subsection Defines an XML generator for the dump. |
+ // Tag. TagName, Attribute |
+ // ***************************************************** |
+ |
+private: |
+ // The tag name for an element. |
+ std::string TagName; |
+ // The number of attributes associated with a tag. |
+ unsigned NumTagAttributes; |
+ // The number of (indexed attribute) operands associated with a tag. |
+ unsigned NumTagOperands; |
+ |
+protected: |
+ |
+ /// Initializes internal data used to emit an XML tag. |
+ void InitializeEmitTag() { |
+ TagName.clear(); |
+ NumTagAttributes = 0; |
+ NumTagOperands = 0; |
+ } |
+ |
+ /// Emits the start of an XML start tag. |
+ void EmitBeginStartTag() { |
+ InitializeEmitTag(); |
+ Context->OS << Indent << "<"; |
+ } |
+ |
+ /// Emit the start of an XML end tag. |
+ void EmitBeginEndTag() { |
+ InitializeEmitTag(); |
+ Context->OS << Indent << "</"; |
+ } |
+ |
+ /// Emits the end of an empty-element XML tag. |
+ void EmitEndTag() { |
+ Context->OS << "/>\n"; |
+ } |
+ |
+ /// Emits the End of a start/end tag for an XML element. |
+ void EmitEndElementTag() { |
+ Context->OS << ">\n"; |
+ } |
+ |
+ /// Emits the tag name for an XML tag. |
+ void EmitTagName(const std::string &ElmtName) { |
+ TagName = ElmtName; |
+ Context->OS << ElmtName; |
+ } |
+ |
+ /// Emits the "name=" portion of an XML tag attribute. |
+ raw_ostream &EmitAttributePrefix(const std::string &AttributeName) { |
+ WrapOperandsLine(); |
+ Context->OS << " " << AttributeName << "="; |
+ ++NumTagAttributes; |
+ return Context->OS; |
+ } |
+ |
+ /// Emits a string-valued XML attribute of an XML tag. |
+ void EmitStringAttribute(const char *AttributeName, const std::string &Str) { |
+ EmitAttributePrefix(AttributeName) << "'" << Str << "'"; |
+ } |
+ |
+ /// Emits a string-valued XML attribute of an XML tag. |
+ void EmitStringAttribute(const char *AttributeName, const char*Str) { |
+ std::string StrStr(Str); |
+ EmitStringAttribute(AttributeName, StrStr); |
+ } |
+ |
+ /// Emits an unsigned integer-valued XML attribute of an XML tag. |
+ void EmitAttribute(const char *AttributeName, uint64_t Value) { |
+ EmitAttributePrefix(AttributeName) << Value; |
+ } |
+ |
+ /// Emits the "opN=" portion of an XML tag (indexable) operand attribute. |
+ raw_ostream &EmitOperandPrefix() { |
+ std::string OpName; |
+ raw_string_ostream OpNameStrm(OpName); |
+ OpNameStrm << "op" << NumTagOperands; |
+ ++NumTagOperands; |
+ return EmitAttributePrefix(OpNameStrm.str()); |
+ } |
+ |
+ /// Adds line wrap if more than "OpsPerLine" XML tag attributes are |
+ /// emitted on the current line. |
+ void WrapOperandsLine() { |
+ if (Context->DumpOptions.OpsPerLine) { |
+ if (NumTagAttributes && |
+ (NumTagAttributes % Context->DumpOptions.OpsPerLine) == 0) { |
+ raw_ostream &OS = Context->OS; |
+ // Last operand crossed width boundary, add newline and indent. |
+ OS << "\n" << Indent << " "; |
+ for (unsigned J = 0, SZ = TagName.size(); J < SZ; ++J) |
+ OS << " "; |
+ } |
+ } |
+ } |
+ |
+ // ******************************************************** |
+ // This section defines how to parse the block and generate |
+ // the corresponding XML. |
+ // ******************************************************** |
+ |
+ // Parses nested blocks. |
+ PNaClBitcodeAnalyzerBlockParser( |
+ unsigned BlockID, |
+ PNaClBitcodeAnalyzerBlockParser *EnclosingBlock) |
+ : NaClBitcodeParser(BlockID, EnclosingBlock), |
+ Context(EnclosingBlock->Context) { |
+ Initialize(BlockID, EnclosingBlock->Context); |
+ } |
+ |
+ // Initialize data associated with a block. |
+ void Initialize(unsigned BlockID, PNaClBitcodeAnalyzerParser *Parser) { |
+ InitializeEmitTag(); |
+ Context = Parser; |
+ if (Context->DumpOptions.DumpRecords) { |
+ Indent = Parser->GetIndentation(); |
+ } |
+ NumWords = 0; |
+ } |
+ |
+ // Increment the indentation level for dumping. |
+ void IncrementIndent() { |
+ Context->IndentLevel++; |
+ Indent = Context->GetIndentation(); |
+ } |
+ |
+ // Increment the indentation level for dumping. |
+ void DecrementIndent() { |
+ Context->IndentLevel--; |
+ Indent = Context->GetIndentation(); |
+ } |
+ |
+ virtual bool Error(const std::string &Message) { |
+ // Use local error routine so that all errors are treated uniformly. |
+ return ::Error(Message); |
+ } |
+ |
+ // Called once the block has been entered by the bitstream reader. |
+ // Argument NumWords is set to the number of words in the |
+ // corresponding block. |
+ virtual void EnterBlock(unsigned NumberWords) { |
+ NumWords = NumberWords; |
+ if (Context->DumpOptions.DumpRecords) { |
+ unsigned BlockID = GetBlockID(); |
+ EmitBeginStartTag(); |
+ EmitEnterBlockTagName(BlockID); |
+ if (Context->DumpOptions.DumpDetails) { |
+ EmitAttribute("NumWords", NumWords); |
+ EmitAttribute("BlockCodeSize", Record.GetCursor().getAbbrevIDWidth()); |
+ } |
+ if (!Context->DumpOptions.DumpDetails && |
+ naclbitc::BLOCKINFO_BLOCK_ID == GetBlockID()) { |
+ EmitEndTag(); |
+ } else { |
+ EmitEndElementTag(); |
+ IncrementIndent(); |
+ } |
+ } |
+ } |
+ |
+ // Called when the corresponding EndBlock of the block being parsed |
+ // is found. |
+ virtual void ExitBlock() { |
+ if (Context->DumpOptions.DumpRecords) { |
+ if (!Context->DumpOptions.DumpDetails && |
+ naclbitc::BLOCKINFO_BLOCK_ID == GetBlockID()) |
+ return; |
+ DecrementIndent(); |
+ EmitBeginEndTag(); |
+ EmitExitBlockTagName(Record.GetBlockID()); |
+ EmitEndElementTag(); |
+ } |
+ } |
+ |
+ virtual void SetBID() { |
+ if (!(Context->DumpOptions.DumpRecords && |
+ Context->DumpOptions.DumpDetails)) { |
+ return; |
+ } |
+ EmitBeginStartTag(); |
+ EmitCodeTagName(naclbitc::BLOCKINFO_CODE_SETBID, |
+ naclbitc::BLOCKINFO_BLOCK_ID); |
+ EmitStringAttribute("block", |
+ NaClBitcodeBlockDist::GetName(Record.GetValues()[0])); |
+ EmitEndTag(); |
+ } |
+ |
+ virtual void ProcessAbbreviation(unsigned BlockID, |
+ NaClBitCodeAbbrev *Abbrev, |
+ bool IsLocal) { |
+ if (Context->DumpOptions.DumpDetails) { |
+ EmitAbbreviation(Abbrev); |
+ } |
+ } |
+ |
+ // Process the last read record in the block. |
+ virtual void ProcessRecord() { |
+ // Increment the # occurrences of this code. |
+ if (Context->Dist) Context->Dist->AddRecord(Record); |
+ |
+ if (Context->DumpOptions.DumpRecords) { |
+ EmitBeginStartTag(); |
+ EmitCodeTagName(Record.GetCode(), GetBlockID(), Record.GetEntryID()); |
+ const NaClBitcodeRecord::RecordVector &Values = Record.GetValues(); |
+ for (unsigned i = 0, e = Values.size(); i != e; ++i) { |
+ EmitOperandPrefix() << (int64_t)Values[i]; |
+ } |
+ EmitEndTag(); |
+ } |
+ } |
+ |
+ virtual bool ParseBlock(unsigned BlockID) { |
+ PNaClBitcodeAnalyzerBlockParser Parser(BlockID, this); |
+ return Parser.ParseThisBlock(); |
+ } |
+ |
+private: |
+ /// Defines the indent level of the block being parsed. |
+ std::string Indent; |
+ /// Defines the number of (32-bit) words the block occupies in |
+ /// the bitstream. |
+ unsigned NumWords; |
+ /// Refers to global parsing context. |
+ PNaClBitcodeAnalyzerParser *Context; |
+ |
+protected: |
+ |
+ /// Emit the given abbreviation as an XML tag. |
+ void EmitAbbreviation(const NaClBitCodeAbbrev *Abbrev) { |
+ EmitBeginStartTag(); |
+ EmitTagName("DEFINE_ABBREV"); |
+ if (Context->DumpOptions.DumpDetails) { |
+ EmitStringAttribute("abbrev", "DEFINE_ABBREV"); |
+ } |
+ for (unsigned I = 0, IEnd = Abbrev->getNumOperandInfos(); I != IEnd; ++I) { |
+ EmitAbbreviationOp(Abbrev->getOperandInfo(I)); |
+ } |
+ EmitEndTag(); |
+ } |
+ |
+ /// Emit the given abbreviation operand as an XML tag attribute. |
+ void EmitAbbreviationOp(const NaClBitCodeAbbrevOp &Op) { |
+ EmitOperandPrefix() |
+ << "'" << NaClBitCodeAbbrevOp::getEncodingName(Op.getEncoding()); |
+ if (Op.hasValue()) { |
+ Context->OS << "(" << Op.getValue() << ")"; |
+ } |
+ Context->OS << "'"; |
+ } |
+ |
+ /// Emits the symbolic name of the record code as the XML tag name. |
+ void EmitCodeTagName( |
+ unsigned CodeID, unsigned BlockID, |
+ unsigned AbbreviationID = naclbitc::UNABBREV_RECORD) { |
+ EmitTagName(NaClBitcodeCodeDist::GetCodeName(CodeID, BlockID)); |
+ if (Context->DumpOptions.DumpDetails) { |
+ if (AbbreviationID == naclbitc::UNABBREV_RECORD) { |
+ EmitStringAttribute("abbrev", "UNABBREVIATED"); |
+ } else { |
+ EmitAttribute("abbrev", AbbreviationID); |
+ } |
+ } |
+ } |
+ |
+ /// Emits the symbolic name of the block as the XML tag name. |
+ void EmitEnterBlockTagName(unsigned BlockID) { |
+ EmitTagName(NaClBitcodeBlockDist::GetName(BlockID)); |
+ if (Context->DumpOptions.DumpDetails) |
+ EmitStringAttribute("abbrev", "ENTER_SUBBLOCK"); |
+ } |
+ |
+ /// Emits the symbolic name of the block as the the XML tag name. |
+ void EmitExitBlockTagName(unsigned BlockID) { |
+ EmitTagName(NaClBitcodeBlockDist::GetName(BlockID)); |
+ if (Context->DumpOptions.DumpDetails) |
+ EmitStringAttribute("abbrev", "END_BLOCK"); |
+ } |
+}; |
+ |
+bool PNaClBitcodeAnalyzerParser::ParseBlock(unsigned BlockID) { |
+ PNaClBitcodeAnalyzerBlockParser Parser(BlockID, this); |
+ return Parser.ParseThisBlock(); |
+} |
+ |
+static void PrintSize(uint64_t Bits, raw_ostream &OS) { |
+ OS << format("%lub/%.2fB/%luW", (unsigned long)Bits, |
+ (double)Bits/8, (unsigned long)(Bits/32)); |
+} |
+ |
+int AnalyzeBitcodeInBuffer(const std::unique_ptr<MemoryBuffer> &Buf, |
+ raw_ostream &OS, |
+ const AnalysisDumpOptions &DumpOptions) { |
+ DEBUG(dbgs() << "-> AnalyzeBitcodeInBuffer\n"); |
+ |
+ if (Buf->getBufferSize() & 3) |
+ return Error("Bitcode stream should be a multiple of 4 bytes in length"); |
+ |
+ const unsigned char *BufPtr = (const unsigned char *)Buf->getBufferStart(); |
+ const unsigned char *EndBufPtr = BufPtr + Buf->getBufferSize(); |
+ |
+ NaClBitcodeHeader Header; |
+ if (Header.Read(BufPtr, EndBufPtr)) |
+ return Error("Invalid PNaCl bitcode header"); |
+ |
+ if (!Header.IsSupported()) |
+ errs() << "Warning: " << Header.Unsupported() << "\n"; |
+ |
+ if (!Header.IsReadable()) |
+ Error("Bitcode file is not readable"); |
+ |
+ NaClBitstreamReader StreamFile(BufPtr, EndBufPtr); |
+ NaClBitstreamCursor Stream(StreamFile); |
+ |
+ unsigned NumTopBlocks = 0; |
+ |
+ // Print out header information. |
+ for (size_t i = 0, limit = Header.NumberFields(); i < limit; ++i) { |
+ OS << Header.GetField(i)->Contents() << "\n"; |
+ } |
+ if (Header.NumberFields()) OS << "\n"; |
+ |
+ // Parse the top-level structure. We only allow blocks at the top-level. |
+ NaClAnalyzerBlockDistElement DistSentinel(0, DumpOptions.OrderBlocksByID); |
+ NaClAnalyzerBlockDist Dist(DistSentinel); |
+ PNaClBitcodeAnalyzerParser Parser(Stream, OS, DumpOptions, &Dist); |
+ while (!Stream.AtEndOfStream()) { |
+ ++NumTopBlocks; |
+ if (Parser.Parse()) return 1; |
+ } |
+ |
+ if (DumpOptions.DumpRecords) return 0; |
+ |
+ uint64_t BufferSizeBits = (EndBufPtr-BufPtr)*CHAR_BIT; |
+ // Print a summary |
+ OS << "Total size: "; |
+ PrintSize(BufferSizeBits, OS); |
+ OS << "\n"; |
+ OS << "# Toplevel Blocks: " << NumTopBlocks << "\n"; |
+ OS << "\n"; |
+ |
+ if (Parser.Dist) Parser.Dist->Print(OS); |
+ |
+ DEBUG(dbgs() << "<- AnalyzeBitcode\n"); |
+ return 0; |
+} |
+ |
+int AnalyzeBitcodeInFile(const StringRef &InputFilename, raw_ostream &OS, |
+ const AnalysisDumpOptions &DumpOptions) { |
+ ErrorOr<std::unique_ptr<MemoryBuffer>> ErrOrFile = |
+ MemoryBuffer::getFileOrSTDIN(InputFilename); |
+ if (std::error_code EC = ErrOrFile.getError()) |
+ return Error(Twine("Error reading '") + InputFilename + "': " + |
+ EC.message()); |
+ |
+ return AnalyzeBitcodeInBuffer(ErrOrFile.get(), OS, DumpOptions); |
+} |
+ |
+} // namespace llvm |