1#
2# Copyright (C) 2016 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16"""Parses the contents of a GCNO file generated by the GCC compiler.
17
18The parse() function returns a FileSummary object, which
19contains descriptions of all functions in the parsed .gcno file. Each
20FunctionSummary object describes the code blocks within each function,
21the line numbers associated within each block, and the arcs exiting/entering
22each block.
23
24
25    Typical usage example:
26
27    summary = parse(file_name)
28"""
29
30import math
31import struct
32import sys
33
34from vts.utils.python.coverage import arc_summary
35from vts.utils.python.coverage import block_summary
36from vts.utils.python.coverage import file_summary
37from vts.utils.python.coverage import function_summary
38from vts.utils.python.coverage import parser
39
40
41class GCNOParser(parser.GcovStreamParserUtil):
42    """Parser object class stores stateful information for parsing GCNO file.
43
44    Stores the file stream and summary object as it is updated.
45
46    Attributes:
47        checksum: The checksum (int) of the file
48        file_summary: The FileSummary object describing the GCNO file
49        format: Character denoting the endianness of the file
50        parsed: True if the content has been parsed, False otherwise
51        stream: File stream object for a GCNO file
52        version: The (integer) version of the GCNO file
53    """
54
55    MAGIC = 0x67636e6f
56    TAG_FUNCTION = 0x01000000
57    TAG_BLOCKS = 0x01410000
58    TAG_ARCS = 0x01430000
59    TAG_LINES = 0x01450000
60    BYTES_IN_WORD = 4
61    HEADER_LENGTH = 3  #  number of words in a section header
62
63    def __init__(self, stream):
64        """Inits the parser with the input stream and default values.
65
66        The byte order is set by default to little endian and the summary file
67        is instantiated with an empty FileSummary object.
68
69        Args:
70            stream: An input binary file stream to a .gcno file
71        """
72        super(GCNOParser, self).__init__(stream, self.MAGIC)
73        self.file_summary = file_summary.FileSummary()
74        self.parsed = False
75
76    def Parse(self):
77        """Runs the parser on the file opened in the stream attribute.
78
79        Reads the binary file and extracts functions, blocks, arcs, and
80        lines. Information is stored the summary attribute.
81
82        Returns:
83            FileSummary object representing the functions, blocks, arcs,
84            and lines in the opened GCNO file.
85
86        Raises:
87            parser.FileFormatError: invalid file format.
88        """
89        if self.parsed:
90            return self.file_summary
91
92        func = None
93
94        while True:
95            tag = str()
96
97            try:
98                while True:
99                    tag = self.ReadInt()
100                    if (tag == self.TAG_FUNCTION or tag == self.TAG_BLOCKS or
101                            tag == self.TAG_ARCS or tag == self.TAG_LINES):
102                        break
103                length = self.ReadInt()
104            except parser.FileFormatError:
105                if not func:
106                    raise parser.FileFormatError("Invalid file.")
107                self.file_summary.functions[func.ident] = func
108                self.parsed = True
109                return self.file_summary  #  end of file reached
110
111            if tag == self.TAG_FUNCTION:
112                if func:
113                    self.file_summary.functions[func.ident] = func
114                func = self.ReadFunction()
115
116            elif tag == self.TAG_BLOCKS:
117                self.ReadBlocks(length, func)
118
119            elif tag == self.TAG_ARCS:
120                self.ReadArcs(length, func)
121
122            elif tag == self.TAG_LINES:
123                self.ReadLines(length, func)
124
125    def ReadFunction(self):
126        """Reads and returns a function from the stream.
127
128        Reads information about a function from the gcno file stream and
129        returns a summary object.
130
131        Returns:
132            FunctionSummary object containing the function name, source file,
133            and first line number.
134
135        Raises:
136            parser.FileFormatError: Function could not be read.
137        """
138        ident = self.ReadInt()
139        self.ReadInt()  #  line number checksum
140        if int(self.version[1]) > 4:
141            self.ReadInt()  #  configuration checksum
142        name = self.ReadString()
143        source_file_name = self.ReadString()
144        first_line_number = self.ReadInt()
145        return function_summary.FunctionSummary(ident, name, source_file_name,
146                                                first_line_number)
147
148    def ReadBlocks(self, length, func):
149        """Reads the basic block information from the stream.
150
151        Reads information about the basic blocks from the gcno file
152        stream and updates the specified function.
153
154        Args:
155            length: number of blocks to read
156            func: FunctionSummary object for the blocks' parent function
157
158        Raises:
159            parser.FileFormatError: Blocks could not be read. Corrupt file.
160        """
161
162        blocks = []
163        for _ in range(length):
164            block_flag = self.ReadInt()
165            block = block_summary.BlockSummary(len(blocks), block_flag)
166            blocks.append(block)
167        func.blocks.extend(blocks)
168
169    def ReadArcs(self, length, func):
170        """Reads the arcs from the stream.
171
172        Parses the arcs from the gcno file and updates the input
173        function summary with arc information.
174
175        Args:
176            length: represents the number of bytes to read
177            func: FunctionSummary object for the arcs' parent fuction
178
179        Raises:
180            parser.FileFormatError: Arcs could not be read. Corrupt file.
181        """
182
183        src_block_index = self.ReadInt()
184        src_block = func.blocks[src_block_index]
185        n_arcs = (length - 1) / 2
186        arcs = []
187        for _ in range(n_arcs):
188            dst_block_index = self.ReadInt()
189            dst_block = func.blocks[dst_block_index]
190            flag = self.ReadInt()
191            arc = arc_summary.ArcSummary(src_block, dst_block, flag)
192            src_block.exit_arcs.append(arc)
193            dst_block.entry_arcs.append(arc)
194
195    def ReadLines(self, length, func):
196        """Reads the line information from the stream.
197
198        Parses the lines from the gcno file and updates the input
199        function summary with line information.
200
201        Args:
202            length: represents the number of bytes to read
203            func: FunctionSummary object for the lines' parent fuction
204
205        Raises:
206            parser.FileFormatError: Lines could not be read. Corrupt file.
207        """
208
209        block_number = self.ReadInt()
210        self.ReadInt()
211        lines = []
212        src = self.ReadString()  #  source file name
213        src_length = int(math.ceil(len(src) * 1.0 / self.BYTES_IN_WORD)) + 1
214        for i in range(length - src_length - self.HEADER_LENGTH):
215            line = self.ReadInt()
216            if line:
217                lines.append(line)
218        func.blocks[block_number].lines = lines
219
220
221def ParseGcnoFile(file_name):
222    """Parses the .gcno file specified by the input.
223
224    Reads the .gcno file specified and parses the information describing
225    basic blocks, functions, and arcs.
226
227    Args:
228        file_name: A string file path to a .gcno file
229
230    Returns:
231        A FileSummary object containing information about all of the
232        fuctions, blocks, and arcs in the .gcno file.
233    """
234
235    with open(file_name, 'rb') as stream:
236        return GCNOParser(stream).Parse()
237
238if __name__ == '__main__':
239    if len(sys.argv) < 3 or sys.argv[1] != '-f':
240        print('usage: gcno_parser.py -f [file name]')
241    else:
242        print(str(ParseGcnoFile(sys.argv[2])))
243