普通文本  |  243行  |  8.04 KB

#
# 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: GCNOparser.py -f [file name]')
    else:
        print(str(parse(sys.argv[2])))