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"""Generates coverage reports using outputs from GCC.
17
18The GenerateCoverageReport() function returns HTML to display the coverage
19at each line of code in a provided source file. Coverage information is
20parsed from .gcno and .gcda file contents and combined with the source file
21to reconstruct a coverage report. GenerateLineCoverageVector() is a helper
22function that produces a vector of line counts and GenerateCoverageHTML()
23uses the vector and source to produce the HTML coverage report.
24"""
25
26import cgi
27import io
28import logging
29import os
30from vts.utils.python.coverage import gcda_parser
31from vts.utils.python.coverage import gcno_parser
32
33GEN_TAG = "/gen/"
34
35def GenerateLineCoverageVector(gcno_file_summary, exclude_paths, coverage_dict):
36    """Process the gcno_file_summary and update the coverage dictionary.
37
38    Create a coverage vector for each source file contained in gcno_file_summary
39    and update the corresponding item in coverage_dict.
40
41    Args:
42        gcno_file_summary: FileSummary object after gcno and gcda files have
43                           been parsed.
44        exclude_paths: a list of paths should be ignored in the coverage report.
45        coverage_dict: a dictionary for each source file and its corresponding
46                       coverage vector.
47    """
48    for ident in gcno_file_summary.functions:
49        func = gcno_file_summary.functions[ident]
50        file_name = func.src_file_name
51        if GEN_TAG in file_name:
52            logging.debug("Skip generated source file %s.", file_name)
53            continue
54        skip_file = False
55        for path in exclude_paths:
56            if file_name.startswith(path):
57                skip_file = True
58                break
59        if skip_file:
60            logging.debug("Skip excluded source file %s.", file_name)
61            continue
62
63        src_lines_counts = coverage_dict[file_name] if file_name in coverage_dict else []
64        for block in func.blocks:
65            for line in block.lines:
66                if line > len(src_lines_counts):
67                    src_lines_counts.extend([-1] *
68                                            (line - len(src_lines_counts)))
69                if src_lines_counts[line - 1] < 0:
70                    src_lines_counts[line - 1] = 0
71                src_lines_counts[line - 1] += block.count
72        coverage_dict[file_name] = src_lines_counts
73
74
75def GetCoverageStats(src_lines_counts):
76    """Returns the coverage stats.
77
78    Args:
79        src_lines_counts: A list of non-negative integers or -1 representing
80                          the number of times the i-th line was executed.
81                          -1 indicates a line that is not executable.
82
83    Returns:
84        integer, the number of lines instrumented for coverage measurement
85        integer, the number of executed or covered lines
86    """
87    total = 0
88    covered = 0
89    if not src_lines_counts or not isinstance(src_lines_counts, list):
90        logging.error("GetCoverageStats: input invalid.")
91        return total, covered
92
93    for line in src_lines_counts:
94        if line < 0:
95            continue
96        total += 1
97        if line > 0:
98            covered += 1
99    return total, covered
100
101