| # |
| # 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() |
| 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]))) |