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 GCDA file generated by the GCC compiler. 17 18The parse() function updates a summary object, which was created by 19the GCNO parser, and includes coverage information along arcs and at 20code blocks. 21 22 23 Typical usage example: 24 25 parse(file_name, file_summary) 26""" 27 28import struct 29import sys 30 31from vts.utils.python.coverage import parser 32from vts.utils.python.coverage import gcno_parser 33 34class GCDAParser(parser.GcovStreamParserUtil): 35 """Parser object class stores stateful information for parsing GCDA files. 36 37 Stores the file stream and a FileSummary object as it is updated. 38 39 Attributes: 40 checksum: The checksum (int) of the file 41 file_summary: The FileSummary object describing the source file 42 format: Character denoting the endianness of the file 43 stream: File stream object for a GCDA file 44 """ 45 46 MAGIC = 0x67636461 47 TAG_FUNCTION = 0x01000000 48 TAG_COUNTER = 0x01a10000 49 TAG_OBJECT = 0xa1000000 50 TAG_PROGRAM = 0xa3000000 51 52 def __init__(self, stream): 53 """Inits the parser with the input stream and default values. 54 55 The byte order is set by default to little endian and the summary file 56 must be provided from the output of the GCNOparser. 57 58 Args: 59 stream: An input binary file stream to a .gcno file 60 """ 61 self._file_summary = None 62 super(GCDAParser, self).__init__(stream, self.MAGIC) 63 64 @property 65 def file_summary(self): 66 """Gets the FileSummary object where coverage data is stored. 67 68 Returns: 69 A FileSummary object. 70 """ 71 return self._file_summary 72 73 @file_summary.setter 74 def file_summary(self, file_summary): 75 """Sets the FileSummary object in which to store coverage data. 76 77 Args: 78 file_summary: A FileSummary object from a processed gcno file 79 """ 80 self._file_summary = file_summary 81 82 def Parse(self, file_summary): 83 """Runs the parser on the file opened in the stream attribute. 84 85 Reads coverage information from the GCDA file stream and resolves 86 block and edge weights. 87 88 Returns: 89 FileSummary object representing the coverage for functions, blocks, 90 arcs, and lines in the opened GCNO file. 91 92 Raises: 93 parser.FileFormatError: invalid file format or invalid counts. 94 """ 95 self.file_summary = file_summary 96 func = None 97 98 while True: 99 tag = str() 100 101 try: 102 while True: 103 tag = self.ReadInt() 104 if (tag == self.TAG_FUNCTION or tag == self.TAG_COUNTER or 105 tag == self.TAG_OBJECT or tag == self.TAG_PROGRAM): 106 break 107 length = self.ReadInt() 108 except parser.FileFormatError: 109 return self.file_summary # end of file reached 110 111 if tag == self.TAG_FUNCTION: 112 func = self.ReadFunction(length) 113 elif tag == self.TAG_COUNTER: 114 self.ReadCounts(func) 115 if not func.Resolve(): 116 raise parser.FileFormatError( 117 "Corrupt file: Counts could not be resolved.") 118 elif tag == self.TAG_OBJECT: 119 pass 120 elif tag == self.TAG_PROGRAM: 121 self.ReadInt() # checksum 122 for i in range(length - 1): 123 self.ReadInt() 124 125 def ReadFunction(self, length): 126 """Reads a function header from the stream. 127 128 Reads information about a function from the gcda file stream and 129 returns the function. 130 131 Args: 132 func: the function for which coverage information will be read. 133 134 Raises: 135 parser.FileFormatError: Corrupt file. 136 """ 137 ident = self.ReadInt() 138 func = self.file_summary.functions[ident] 139 checksum = self.ReadInt() 140 words_read = 3 141 if int(self.version[1]) > 4: 142 self.ReadInt() 143 words_read = 4 144 145 if words_read < length: 146 gcda_name = self.ReadString() 147 148 return func 149 150 def ReadCounts(self, func): 151 """Reads arc counts from the stream. 152 153 Reads counts from the gcda file stream for arcs that are not 154 fake and are not in the tree. Updates their counts and marks them 155 as having resolved counts. 156 157 Args: 158 func: FunctionSummary for which arc counts will be read. 159 """ 160 for block in func.blocks: 161 for arc in block.exit_arcs: 162 if not arc.fake and not arc.on_tree: 163 count = self.ReadInt64() 164 arc.count = count 165 arc.resolved = True 166 167 168def ParseGcdaFile(file_name, file_summary): 169 """Parses the .gcno file specified by the input. 170 171 Reads the .gcno file specified and parses the information describing 172 basic blocks, functions, and arcs. 173 174 Args: 175 file_name: A string file path to a .gcno file 176 file_summary: The summary from a parsed gcno file 177 178 Returns: 179 A summary object containing information about the coverage for each 180 block in each function. 181 """ 182 183 with open(file_name, 'rb') as stream: 184 return GCDAParser(stream).Parse(file_summary) 185 186 187if __name__ == '__main__': 188 if len(sys.argv) != 2: 189 print('usage: gcda_parser.py [gcda file name] [gcno file name]') 190 else: 191 file_summary = gcno_parser.ParseGcnoFile(sys.argv[2]) 192 print(str(ParseGcdaFile(sys.argv[1], file_summary))) 193