# # 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 GCNO file generated by the GCC compiler. The parse() function returns a FileSummary object, which contains descriptions of all functions in the parsed .gcno file. Each FunctionSummary object describes the code blocks within each function, the line numbers associated within each block, and the arcs exiting/entering each block. Typical usage example: summary = parse(file_name) """ import math import struct import sys from vts.utils.python.coverage import arc_summary from vts.utils.python.coverage import block_summary from vts.utils.python.coverage import file_summary from vts.utils.python.coverage import function_summary from vts.utils.python.coverage import parser class GCNOParser(parser.GcovStreamParserUtil): """Parser object class stores stateful information for parsing GCNO file. Stores the file stream and summary object as it is updated. Attributes: checksum: The checksum (int) of the file file_summary: The FileSummary object describing the GCNO file format: Character denoting the endianness of the file parsed: True if the content has been parsed, False otherwise stream: File stream object for a GCNO file version: The (integer) version of the GCNO file """ MAGIC = 0x67636e6f TAG_FUNCTION = 0x01000000 TAG_BLOCKS = 0x01410000 TAG_ARCS = 0x01430000 TAG_LINES = 0x01450000 BYTES_IN_WORD = 4 HEADER_LENGTH = 3 # number of words in a section header 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 is instantiated with an empty FileSummary object. Args: stream: An input binary file stream to a .gcno file """ super(GCNOParser, self).__init__(stream, self.MAGIC) self.file_summary = file_summary.FileSummary() self.parsed = False def Parse(self): """Runs the parser on the file opened in the stream attribute. Reads the binary file and extracts functions, blocks, arcs, and lines. Information is stored the summary attribute. Returns: FileSummary object representing the functions, blocks, arcs, and lines in the opened GCNO file. Raises: parser.FileFormatError: invalid file format. """ if self.parsed: return self.file_summary func = None while True: tag = str() try: while True: tag = self.ReadInt() if (tag == self.TAG_FUNCTION or tag == self.TAG_BLOCKS or tag == self.TAG_ARCS or tag == self.TAG_LINES): break length = self.ReadInt() except parser.FileFormatError: if not func: raise parser.FileFormatError("Invalid file.") self.file_summary.functions[func.ident] = func self.parsed = True return self.file_summary # end of file reached if tag == self.TAG_FUNCTION: if func: self.file_summary.functions[func.ident] = func func = self.ReadFunction() elif tag == self.TAG_BLOCKS: self.ReadBlocks(length, func) elif tag == self.TAG_ARCS: self.ReadArcs(length, func) elif tag == self.TAG_LINES: self.ReadLines(length, func) def ReadFunction(self): """Reads and returns a function from the stream. Reads information about a function from the gcno file stream and returns a summary object. Returns: FunctionSummary object containing the function name, source file, and first line number. Raises: parser.FileFormatError: Function could not be read. """ ident = self.ReadInt() self.ReadInt() # line number checksum if int(self.version[1]) > 4: self.ReadInt() # configuration checksum name = self.ReadString() source_file_name = self.ReadString() first_line_number = self.ReadInt() return function_summary.FunctionSummary(ident, name, source_file_name, first_line_number) def ReadBlocks(self, length, func): """Reads the basic block information from the stream. Reads information about the basic blocks from the gcno file stream and updates the specified function. Args: length: number of blocks to read func: FunctionSummary object for the blocks' parent function Raises: parser.FileFormatError: Blocks could not be read. Corrupt file. """ blocks = [] for _ in range(length): block_flag = self.ReadInt() block = block_summary.BlockSummary(len(blocks), block_flag) blocks.append(block) func.blocks.extend(blocks) def ReadArcs(self, length, func): """Reads the arcs from the stream. Parses the arcs from the gcno file and updates the input function summary with arc information. Args: length: represents the number of bytes to read func: FunctionSummary object for the arcs' parent fuction Raises: parser.FileFormatError: Arcs could not be read. Corrupt file. """ src_block_index = self.ReadInt() src_block = func.blocks[src_block_index] n_arcs = (length - 1) / 2 arcs = [] for _ in range(n_arcs): dst_block_index = self.ReadInt() dst_block = func.blocks[dst_block_index] flag = self.ReadInt() arc = arc_summary.ArcSummary(src_block, dst_block, flag) src_block.exit_arcs.append(arc) dst_block.entry_arcs.append(arc) def ReadLines(self, length, func): """Reads the line information from the stream. Parses the lines from the gcno file and updates the input function summary with line information. Args: length: represents the number of bytes to read func: FunctionSummary object for the lines' parent fuction Raises: parser.FileFormatError: Lines could not be read. Corrupt file. """ block_number = self.ReadInt() self.ReadInt() # dummy value lines = [] src = self.ReadString() # source file name src_length = int(math.ceil(len(src) * 1.0 / self.BYTES_IN_WORD)) + 1 for i in range(length - src_length - self.HEADER_LENGTH): line = self.ReadInt() if line: lines.append(line) func.blocks[block_number].lines = lines def ParseGcnoFile(file_name): """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 Returns: A FileSummary object containing information about all of the fuctions, blocks, and arcs in the .gcno file. """ with open(file_name, 'rb') as stream: return GCNOParser(stream).Parse() if __name__ == '__main__': if len(sys.argv) < 3 or sys.argv[1] != '-f': print('usage: gcno_parser.py -f [file name]') else: print(str(ParseGcnoFile(sys.argv[2])))