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() # dummy value 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