# # Copyright (C) 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # """Parses the contents of a GCDA file generated by the GCC compiler. The parse() function updates a summary object, which was created by the GCNO parser, and includes coverage information along arcs and at code blocks. Typical usage example: parse(file_name, file_summary) """ import struct import sys from vts.utils.python.coverage import parser from vts.utils.python.coverage import gcno_parser class GCDAParser(parser.GcovStreamParserUtil): """Parser object class stores stateful information for parsing GCDA files. Stores the file stream and a FileSummary object as it is updated. Attributes: checksum: The checksum (int) of the file file_summary: The FileSummary object describing the source file format: Character denoting the endianness of the file stream: File stream object for a GCDA file """ MAGIC = 0x67636461 TAG_FUNCTION = 0x01000000 TAG_COUNTER = 0x01a10000 TAG_OBJECT = 0xa1000000 TAG_PROGRAM = 0xa3000000 def __init__(self, stream): """Inits the parser with the input stream and default values. The byte order is set by default to little endian and the summary file must be provided from the output of the GCNOparser. Args: stream: An input binary file stream to a .gcno file """ self._file_summary = None super(GCDAParser, self).__init__(stream, self.MAGIC) @property def file_summary(self): """Gets the FileSummary object where coverage data is stored. Returns: A FileSummary object. """ return self._file_summary @file_summary.setter def file_summary(self, file_summary): """Sets the FileSummary object in which to store coverage data. Args: file_summary: A FileSummary object from a processed gcno file """ self._file_summary = file_summary def Parse(self, file_summary): """Runs the parser on the file opened in the stream attribute. Reads coverage information from the GCDA file stream and resolves block and edge weights. Returns: FileSummary object representing the coverage for functions, blocks, arcs, and lines in the opened GCNO file. Raises: parser.FileFormatError: invalid file format or invalid counts. """ self.file_summary = file_summary func = None while True: tag = str() try: while True: tag = self.ReadInt() if (tag == self.TAG_FUNCTION or tag == self.TAG_COUNTER or tag == self.TAG_OBJECT or tag == self.TAG_PROGRAM): break length = self.ReadInt() except parser.FileFormatError: return self.file_summary # end of file reached if tag == self.TAG_FUNCTION: func = self.ReadFunction(length) elif tag == self.TAG_COUNTER: self.ReadCounts(func) if not func.Resolve(): raise parser.FileFormatError( "Corrupt file: Counts could not be resolved.") elif tag == self.TAG_OBJECT: pass elif tag == self.TAG_PROGRAM: self.ReadInt() # checksum for i in range(length - 1): self.ReadInt() def ReadFunction(self, length): """Reads a function header from the stream. Reads information about a function from the gcda file stream and returns the function. Args: func: the function for which coverage information will be read. Raises: parser.FileFormatError: Corrupt file. """ ident = self.ReadInt() func = self.file_summary.functions[ident] checksum = self.ReadInt() words_read = 3 if int(self.version[1]) > 4: self.ReadInt() words_read = 4 if words_read < length: gcda_name = self.ReadString() return func def ReadCounts(self, func): """Reads arc counts from the stream. Reads counts from the gcda file stream for arcs that are not fake and are not in the tree. Updates their counts and marks them as having resolved counts. Args: func: FunctionSummary for which arc counts will be read. """ for block in func.blocks: for arc in block.exit_arcs: if not arc.fake and not arc.on_tree: count = self.ReadInt64() arc.count = count arc.resolved = True def ParseGcdaFile(file_name, file_summary): """Parses the .gcno file specified by the input. Reads the .gcno file specified and parses the information describing basic blocks, functions, and arcs. Args: file_name: A string file path to a .gcno file file_summary: The summary from a parsed gcno file Returns: A summary object containing information about the coverage for each block in each function. """ with open(file_name, 'rb') as stream: return GCDAParser(stream).Parse(file_summary) if __name__ == '__main__': if len(sys.argv) != 2: print('usage: gcda_parser.py [gcda file name] [gcno file name]') else: file_summary = gcno_parser.ParseGcnoFile(sys.argv[2]) print(str(ParseGcdaFile(sys.argv[1], file_summary)))