4.2: Coverage Analysis

What You'll Learn

  • Understand the importance of code coverage in embedded systems development.
  • Identify different types of code coverage (statement, branch, MC/DC).
  • Learn how to configure and interpret coverage analysis tools.
  • Relate coverage requirements to ASIL (Automotive Safety Integrity Level) standards.
  • Implement coverage analysis workflows in CI/CD pipelines.

Code coverage analysis provides quantitative evidence that test cases have exercised the implementation, a fundamental requirement in safety-critical development governed by ISO 26262, IEC 61508, and DO-178C. This chapter explains coverage metrics, tool selection criteria, and practical implementation strategies for achieving compliance-grade coverage measurement.


15.4.1 Coverage Metrics Hierarchy

Coverage metrics progress from simple execution tracking to complex logic verification. Safety standards mandate different levels based on criticality.

Statement Coverage

Definition: Percentage of executable statements executed at least once during testing.

Purpose: Ensures basic code reachability. A statement never executed is untested code.

Example:

void process_sensor(int value) {
    if (value > THRESHOLD) {
        trigger_alarm();        // Statement 1
    }
    log_reading(value);         // Statement 2
}

For 100% statement coverage, tests must execute both trigger_alarm() and log_reading(). Two test cases suffice:

  • Test 1: value = THRESHOLD + 1 (executes both statements)
  • Test 2: value = THRESHOLD - 1 (executes statement 2 only)

Limitation: Does not verify all branches. The false branch of the if-statement remains untested in Test 1.

Branch Coverage (Decision Coverage)

Definition: Percentage of control flow branches (true/false outcomes of decisions) executed during testing.

Purpose: Verifies that both outcomes of every decision point have been tested.

Example (same code):

void process_sensor(int value) {
    if (value > THRESHOLD) {    // Decision with 2 branches
        trigger_alarm();
    }
    log_reading(value);
}

For 100% branch coverage:

  • Test 1: value = THRESHOLD + 1 (true branch)
  • Test 2: value = THRESHOLD - 1 (false branch)

Relationship to Statement Coverage: 100% branch coverage implies 100% statement coverage, but not vice versa.

Modified Condition/Decision Coverage (MC/DC)

Definition: MC/DC requires that each condition in a decision independently affects the decision outcome while holding other conditions constant.

Purpose: Verifies that every boolean condition within complex decisions has been proven to independently control the decision. Required for highest safety integrity levels (ASIL C/D, DAL A/B).

Standards Reference: ISO 26262-6:2018 Section 7.4.7 Table 13 mandates MC/DC for ASIL C and D. DO-178C mandates MC/DC for DAL A and B software.

MC/DC Decision Table Example

Consider a decision with two conditions:

if (pressure > MAX_PRESSURE || temperature > MAX_TEMP) {
    shutdown_system();
}

Decision Table:

Test pressure > MAX_PRESSURE temperature > MAX_TEMP Decision Outcome
1 F F F (no shutdown)
2 T F T (shutdown)
3 F T T (shutdown)

MC/DC Analysis:

  • Condition A independence (pressure > MAX_PRESSURE): Compare Test 1 (F→F) vs. Test 2 (T→T). Condition A changed from F to T, outcome changed from F to T. Condition B held constant (F). ✓ Proven independent.
  • Condition B independence (temperature > MAX_TEMP): Compare Test 1 (F→F) vs. Test 3 (T→T). Condition B changed from F to T, outcome changed from F to T. Condition A held constant (F). ✓ Proven independent.

This test suite achieves MC/DC with 3 tests (minimum for 2-condition OR decision is n+1 = 3).

MC/DC for Complex Decisions

For AND logic with three conditions:

if (speed < MIN_SPEED && fuel > MIN_FUEL && engine_running) {
    engage_boost();
}

Minimum test cases: 4 (n+1 for n=3 conditions). Each test must independently prove one condition affects the outcome.

Key MC/DC Properties:

  • Number of test cases: n+1 to 2ⁿ (where n = number of conditions)
  • Subsumes statement and branch coverage
  • Tool support critical for complex decisions (manual calculation error-prone)

15.4.2 Coverage Requirements by ASIL

ISO 26262-6:2018 Table 13 specifies structural coverage requirements based on ASIL:

ASIL Statement Coverage Branch Coverage MC/DC Coverage
QM Recommended - -
A 100% (++) Recommended (+) -
B 100% (++) 100% (++) Recommended (+)
C 100% (++) 100% (++) 100% (++)
D 100% (++) 100% (++) 100% (++)

Legend: ++ (Highly Recommended), + (Recommended), - (Not Required)

ASPICE Alignment: SUP.2 (Verification) BP5 requires verification of test coverage criteria. SWE.4 (Software Unit Verification) BP4 mandates achieving defined coverage criteria.

IEC 61508 Comparison: IEC 61508-3 Table A.4 recommends MC/DC for SIL 3 and 4, aligning with ISO 26262 ASIL C/D requirements.

DO-178C Comparison: DO-178C Table A-7 mandates MC/DC for DAL A and B, stricter than ISO 26262 (MC/DC starts at ASIL C).


15.4.3 Coverage Tool Comparison Matrix

Selecting the right coverage tool depends on project budget, target platform, standards compliance requirements, and CI/CD integration capabilities.

Tool Cost ASIL/SIL Support MC/DC Support Target Platforms CI Integration Report Formats Traceability
gcov/LCOV Free QM/A No GCC-based (ARM, x86) Excellent HTML, XML, Cobertura Manual
BullseyeCoverage Commercial A/B/C/D Yes ARM, x86, PowerPC Good HTML, XML, JSON Automatic
Testwell CTC++ Commercial A/B/C/D Yes Multi-compiler Good HTML, XML, Text Automatic
VectorCAST Commercial A/B/C/D Yes Embedded (all major) Excellent HTML, XML, PDF Full (reqts->code)
Squish Coco Commercial A/B/C/D Yes GCC, MSVC, IAR, Keil Good HTML, CSV, Cobertura Automatic

Tool Selection Criteria:

  1. For ASIL QM/A projects: gcov/LCOV sufficient. No MC/DC requirement, free tooling adequate.
  2. For ASIL B projects: Commercial tools recommended. MC/DC optional but advisable for complex logic. Consider BullseyeCoverage or Squish Coco for cost-effectiveness.
  3. For ASIL C/D projects: VectorCAST, BullseyeCoverage, or Testwell CTC++ mandatory. MC/DC support non-negotiable. Full traceability to requirements essential for certification.
  4. For DO-178C DAL A/B: VectorCAST preferred (FAA-recognized, extensive aviation heritage). Tool qualification data packages available.

gcov Limitations:

  • No MC/DC support (disqualifies for ASIL C/D primary toolchain)
  • Manual instrumentation required for some embedded targets
  • Limited traceability to requirements
  • Acceptable for ASIL A supplemental coverage verification

Commercial Tool Advantages:

  • Automated MC/DC analysis (essential for complex decisions)
  • Tool qualification packages (DO-330, ISO 26262-8 Clause 11)
  • Requirements-to-code traceability matrices
  • Certification body acceptance (pre-approved by TÜV, FAA, etc.)

15.4.4 Implementation Workflow

Achieving compliance-grade coverage requires systematic workflow integration across development, testing, and CI/CD. The following diagram illustrates the end-to-end coverage analysis workflow, from code instrumentation through test execution, data collection, and report generation with quality gate enforcement.

Coverage Analysis Flow

Step 1: Instrumentation and Compilation

GCC/gcov Example:

# Compile with coverage instrumentation
gcc -fprofile-arcs -ftest-coverage -O0 -g src/sensor.c tests/test_sensor.c -o test_sensor

# Flags explanation:
# -fprofile-arcs: Insert code to count branch execution
# -ftest-coverage: Generate .gcno notes files
# -O0: Disable optimization (preserves 1:1 source-to-binary mapping)
# -g: Include debug symbols for source line mapping

Cross-Compilation for ARM:

arm-none-eabi-gcc -fprofile-arcs -ftest-coverage \
  -mcpu=cortex-m4 -mthumb \
  -specs=nosys.specs \
  src/controller.c tests/test_controller.c \
  -o test_controller.elf

BullseyeCoverage Example:

# Enable BullseyeCoverage instrumentation
export COVFILE=coverage.cov
cov01 -1  # Enable coverage
make clean && make tests
./run_tests
cov01 -0  # Disable coverage

Step 2: Test Execution

Execute the full test suite. Coverage data accumulates in .gcda (gcov) or tool-specific files.

# Run unit tests
./test_sensor
./test_controller

# Coverage data files generated:
# sensor.gcda, controller.gcda (execution counts)

Step 3: Coverage Report Generation

gcov/LCOV:

# Generate gcov coverage data
gcov src/sensor.c

# Aggregate with LCOV
lcov --capture --directory . --output-file coverage.info
lcov --remove coverage.info '/usr/*' 'tests/*' --output-file coverage_filtered.info

# Generate HTML report
genhtml coverage_filtered.info --output-directory reports/coverage

BullseyeCoverage:

# Generate HTML report with MC/DC
covhtml -f coverage.cov -o reports/coverage

# Export to XML for CI
covxml -f coverage.cov -o coverage.xml

Step 4: CI/CD Integration

GitLab CI Example

# .gitlab-ci.yml
coverage-analysis:
  stage: test
  image: gcc:12
  before_script:
    - apt-get update && apt-get install -y lcov
  script:
    # Build with coverage
    - gcc -fprofile-arcs -ftest-coverage -O0 src/*.c tests/*.c -o test_suite
    # Run tests
    - ./test_suite
    # Generate coverage report
    - lcov --capture --directory . --output-file coverage.info
    - lcov --remove coverage.info '/usr/*' 'tests/*' --output-file coverage.info
    - lcov --list coverage.info
  coverage: '/lines\.*: (\d+\.\d+)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
    paths:
      - coverage.info
      - reports/coverage/
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request"'

GitHub Actions Example

# .github/workflows/coverage.yml
name: Coverage Analysis
on: [pull_request]

jobs:
  coverage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install dependencies
        run: sudo apt-get install -y lcov

      - name: Build with coverage
        run: |
          gcc -fprofile-arcs -ftest-coverage -O0 src/*.c tests/*.c -o test_suite

      - name: Run tests
        run: ./test_suite

      - name: Generate coverage
        run: |
          lcov --capture --directory . --output-file coverage.info
          lcov --remove coverage.info '/usr/*' 'tests/*' --output-file coverage.info

      - name: Upload to Codecov
        uses: codecov/codecov-action@v3
        with:
          files: coverage.info
          fail_ci_if_error: true

      - name: Enforce thresholds
        run: |
          COVERAGE=$(lcov --summary coverage.info | grep lines | awk '{print $2}' | sed 's/%//')
          if (( $(echo "$COVERAGE < 100" | bc -l) )); then
            echo "Coverage $COVERAGE% below 100% threshold"
            exit 1
          fi

Step 5: Trend Tracking and Reporting

Coverage Dashboard Example (using Codecov or SonarQube):

## Coverage Trends

| Metric    | Current | Previous | Trend |
|-----------|---------|----------|-------|
| Statement | 98.7%   | 97.2%    | ↑     |
| Branch    | 96.3%   | 95.8%    | ↑     |
| MC/DC     | 94.1%   | 92.5%    | ↑     |

Automated Alerts:

  • Coverage drops below threshold → Block merge request
  • Coverage gaps in new code → Notify developer
  • MC/DC coverage incomplete → Escalate to safety manager

15.4.5 Coverage Exclusions and Justifications

Not all code requires coverage. Safety standards permit exclusions with rigorous justification and documentation.

Acceptable Exclusion Categories

1. Test Harness Code

#ifndef COVERAGE_EXCLUDE_TEST_HARNESS
void test_setup() {
    // Test initialization code
    // Not part of production binary
}
#endif

Justification: Test infrastructure not deployed to production. No safety impact.

2. Unreachable Defensive Code

// Hardware guarantees temperature sensor range -40°C to 125°C
if (temperature < -100 || temperature > 200) {
    // Defensive check: physically impossible given sensor specs
    trigger_error();  // EXCLUDE: Unreachable by design
}

Justification: Hardware constraints prevent execution. Documented in safety analysis.

3. Third-Party Libraries (with qualification evidence)

// libsafety.a from TÜV-certified supplier (Cert ID: TUV-12345)
#include <libsafety.h>  // EXCLUDE: Pre-qualified component

Justification: Component qualified independently. Supplier-provided coverage evidence. Reference certification ID in traceability matrix.

4. Auto-Generated Code (with tool qualification)

// Generated by Simulink Coder (Tool ID: TC-SIMULINK-2024)
// EXCLUDE: Tool-qualified per ISO 26262-8
void model_step() {
    // Auto-generated state machine
}

Justification: Code generator qualified as TCL-3 tool per ISO 26262-8 Clause 11. Tool validation evidence in project quality records.

Unacceptable Exclusion Examples

1. Complex Logic Avoidance

if (condition_A && condition_B && condition_C && condition_D) {
    critical_safety_function();  // ❌ Cannot exclude: Safety-critical
}

Rationale: Complexity does not justify exclusion. Use commercial MC/DC tool or decompose logic.

2. "Too Hard to Test" Rationale

void handle_rare_error() {
    // ❌ Cannot exclude: Error handling must be verified
}

Rationale: Safety-critical error paths must be tested. Use fault injection or simulation.

Documentation Requirements for Exclusions

ISO 26262-8 Clause 12 requires exclusion documentation in the Safety Case:

Coverage Exclusion Justification Template:

## Coverage Exclusion Justification

**File**: src/sensor_manager.c
**Lines**: 245-257
**Function**: defensive_range_check()

**Exclusion Category**: Unreachable defensive code
**Rationale**: Temperature sensor hardware specification (Bosch SHT31, Datasheet v2.3)
guarantees operating range -40°C to 125°C. Code path checking values outside
-100°C to 200°C is physically unreachable.

**Safety Impact**: None. Primary range check (0-100°C operational) fully covered.

**Evidence**:
- Hardware specification: docs/datasheets/SHT31_v2.3.pdf
- Safety analysis: FMEA Item ID SA-TEMP-003
- Design review: DR-2024-11-15

**Approval**: J. Schmidt (Safety Manager), 2024-12-01
**Audit Trail**: QMS-2024-456

ASPICE Work Product: SUP.2 WP 13-07 (Verification Results) must include exclusion justifications.

Coverage Exclusion in CI/CD

# Coverage configuration with exclusions
coverage:
  exclusions:
    # Test infrastructure
    - 'tests/**/*'
    - 'test_harness/**/*'

    # Third-party qualified libraries
    - 'lib/tuv_certified/**/*'  # Cert: TUV-12345

    # Auto-generated code (tool-qualified)
    - 'generated/simulink/**/*'  # Tool: TC-SIMULINK-2024

  require_justification: true
  justification_file: docs/safety/coverage_exclusions.md

15.4.6 Interpreting Coverage Reports

Sample Coverage Report (LCOV HTML)

File: src/brake_controller.c
Lines executed: 287 / 290 (98.97%)
Branches executed: 154 / 158 (97.47%)

Uncovered Lines:
  Line 145: emergency_shutdown()  // Never executed
  Line 212: fallback_mode()       // Never executed
  Line 278: log_critical_error()  // Never executed

Analysis:

  1. 98.97% statement coverage: Excellent, but not compliant for ASIL B/C/D (requires 100%).
  2. Uncovered lines: All error handling paths. Critical gap.
  3. Action: Write fault injection tests to trigger emergency_shutdown(), fallback_mode(), and log_critical_error().

MC/DC Report Example (BullseyeCoverage)

Function: evaluate_braking_conditions()
Decision at Line 89: (speed > 50 && brake_pressed) || emergency_stop

Condition Coverage:
  speed > 50:         100% (T: Test 1,2  F: Test 3,4)
  brake_pressed:      100% (T: Test 1,3  F: Test 2,4)
  emergency_stop:     100% (T: Test 5    F: Test 1,2,3,4)

MC/DC Coverage: 100%
Independence Pairs:
  speed > 50:        Test 3 (F) vs Test 1 (T) [brake_pressed=T, emergency_stop=F]
  brake_pressed:     Test 2 (F) vs Test 1 (T) [speed>50=T, emergency_stop=F]
  emergency_stop:    Test 1 (F) vs Test 5 (T) [speed>50=T, brake_pressed=T]

Interpretation: All three conditions independently proven to affect the decision. Full MC/DC compliance achieved.


15.4.7 Tool Configuration Examples

GCC/gcov Configuration

# Coverage Analysis Configuration
coverage:
  tool: gcov
  version: 12.2.0

  compiler_flags:
    - -fprofile-arcs
    - -ftest-coverage
    - -O0                    # No optimization (preserves line mapping)
    - -g                     # Debug symbols
    - -fno-inline            # Prevent function inlining

  targets:
    statement: 100%
    branch: 100%
    # MC/DC: Not supported by gcov

  reporting:
    formats:
      - html                 # Visual report via genhtml
      - cobertura           # XML for CI integration
      - lcov                # Standardized format
    output: reports/coverage/

  ci_integration:
    fail_threshold:
      statement: 100
      branch: 100
    trend_tracking: enabled
    badge_generation: true

  exclusions:
    patterns:
      - 'src/generated/*'
      - 'src/third_party/*'
      - 'tests/*'
      - '*.test.c'
    justification_required: true

BullseyeCoverage Configuration

<!-- BullseyeCoverage configuration: .covignore -->
<CoverageSettings>
  <Instrumentation>
    <Mode>Function+Decision+Condition+MCDC</Mode>
    <OptimizationLevel>0</OptimizationLevel>
  </Instrumentation>

  <Exclusions>
    <Pattern>tests/*</Pattern>
    <Pattern>generated/simulink/*</Pattern>
    <Pattern>third_party/*</Pattern>
  </Exclusions>

  <Reporting>
    <Formats>
      <HTML>reports/coverage/index.html</HTML>
      <XML>reports/coverage/coverage.xml</XML>
      <CSV>reports/coverage/summary.csv</CSV>
    </Formats>
    <MCDCDetail>Full</MCDCDetail>
  </Reporting>

  <Thresholds>
    <Function>100</Function>
    <Decision>100</Decision>
    <Condition>100</Condition>
    <MCDC>100</MCDC>
  </Thresholds>
</CoverageSettings>

VectorCAST Configuration

; VectorCAST environment configuration
[ENVIRONMENT]
COMPILER=GCC_ARM
TESTABLE_SOURCE_DIR=src/
TEST_SCRIPT_DIR=tests/
COVERAGE_TYPE=STATEMENT+BRANCH+MCDC

[COVERAGE_GOALS]
STATEMENT_COVERAGE=100
BRANCH_COVERAGE=100
MCDC_COVERAGE=100

[EXCLUSIONS]
EXCLUDE_PATTERN=generated/*
EXCLUDE_PATTERN=third_party/*
EXCLUDE_FUNCTION=test_*

[REPORTING]
REPORT_FORMAT=HTML,XML,PDF
EXPORT_REQUIREMENTS_TRACEABILITY=TRUE
CERTIFICATION_PACKAGE=ISO26262

[TOOL_QUALIFICATION]
STANDARD=ISO26262-8
TCL=TCL-1
QUALIFICATION_PACKAGE=vectorcast_tqp_iso26262.pdf

Summary

Coverage analysis provides objective evidence that test cases have exercised the implementation, a mandatory requirement for safety-critical systems. The progression from statement coverage (basic execution tracking) to branch coverage (decision outcome verification) to MC/DC coverage (condition independence proof) aligns with increasing safety integrity levels.

ISO 26262 mandates 100% statement coverage for ASIL A and above, 100% branch coverage for ASIL B and above, and 100% MC/DC coverage for ASIL C/D. Tool selection must align with these requirements: gcov/LCOV suffices for ASIL QM/A, while commercial tools (BullseyeCoverage, VectorCAST, Testwell CTC++) are mandatory for ASIL C/D due to MC/DC support and certification heritage.

Practical implementation requires systematic workflow integration: instrumented compilation, test execution, report generation, CI/CD enforcement, and trend tracking. Coverage exclusions are permitted for test harnesses, unreachable defensive code, pre-qualified third-party libraries, and tool-generated code, but require rigorous justification documented in the Safety Case per ISO 26262-8 Clause 12.

MC/DC coverage, the most stringent metric, proves that each boolean condition in a decision independently affects the outcome. For a decision with n conditions, MC/DC requires n+1 to 2ⁿ test cases. Manual MC/DC calculation for complex decisions is error-prone; commercial tool automation is essential for ASIL C/D compliance.

Interpreting coverage reports requires identifying gaps (uncovered lines/branches), analyzing safety impact (error paths vs. nominal paths), writing targeted tests to close gaps, and documenting justified exclusions. Full traceability from requirements to test cases to coverage results is mandatory for certification body acceptance (ASPICE SUP.2, ISO 26262-8).

References

Standards:

  • ISO 26262-6:2018, Clause 7.4.7 and Table 13: Software unit verification - structural coverage requirements by ASIL
  • ISO 26262-8:2018, Clause 11: Software tool qualification and confidence levels (TCL)
  • ISO 26262-8:2018, Clause 12: Safety case - coverage exclusion justification requirements
  • IEC 61508-3:2010, Table A.4: Software structural coverage recommendations by SIL
  • DO-178C, Table A-7: Structural coverage objectives for software verification (DAL A/B/C/D)
  • DO-330: Software Tool Qualification Considerations (tool qualification for coverage analyzers)
  • ASPICE 4.0 SUP.2: Verification Strategy - BP5 (verification coverage criteria)
  • ASPICE 4.0 SWE.4: Software Unit Verification - BP4 (achieving defined coverage)

Tool Documentation:

MC/DC Resources: