You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
8.0 KiB

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