主要内容

Calculate Coverage of Data Couplings and Control Couplings (DCCC) in C/C++ Code

R2026b

Data coupling and control coupling refer to dependencies and interactions between software components related to data flow and control flow.

  • An example of a data coupling is a use of a global variable to pass data between components

  • An example of a control coupling is one component calling a function provided by another component

The DO-178C standard requires you to measure the coverage of data couplings and control couplings (DCCC) during integration testing. This requirement is similar to how coverage of statements, decisions, and conditions must be measured when testing individual components. The DCCC analysis is required because achieving full statement, decision, and condition coverage does not imply full data coupling or control coupling coverage. This reflects the fact that having each individual component behave as designed does not imply that they also interact as designed.

The polyspace-test -dccc command generates data coupling and control coupling (DCCC) coverage data by combining:

  • Polyspace® Code Prover™ static analysis results (that contain information about data and control coupling between functions in your source code)

  • Polyspace Test™ per-test coverage data

The analysis identifies cross-component interactions in your code, determines whether your tests cover them, and produces a .dccc file that contains the DCCC coverage results. To learn about how to run this analysis, see polyspace-test -dccc. To learn about how Polyspace defines components, data coupling, and control coupling, read this topic.

Components

A component is a collection of functions in your source code. Before running DCCC analysis, you must partition the collection of all functions in your source code into disjoint components. To do this, create a component definition file in TOML format that provides the following information for each Component table element:

  • Name — Name of the component, specified as a string.

  • Spec — A string array of glob patterns. A file is assigned to the first component whose pattern matches the file path. Place specific patterns before general ones.

This specification assigns all functions defined in a source or header file to the component to which that file belongs. If you do not provide this TOML file, or if a subset of your source files do not match any of the glob patterns, Polyspace automatically creates a separate component for each uncategorized source file.

This is an example of a component definition file:

[[Component]]
Name = "Engine"
Spec = [ "**/engine.c", "**/engine.h" ]

[[Component]]
Name = "Controller"
Spec = [ "**/controller.c" ]

This file defines two components Engine and Controller. The ** wildcard recursively matches folders at any level in your file system. For more information on the supported patterns for file selection, see Select Files for Polyspace Project Using Pattern Matching.

Control Couplings

A control coupling is a function call whose call site and callee belong to different components. A test that exercises this function call covers the associated control coupling.

A call that happens via a function pointer or a virtual function (in C++) is referred to as an indirect call. If a test exercises an indirect function call, Polyspace reports the associated control coupling as potentially covered because the software cannot fully confirm the specific call target at run time. For such results, you must manually verify if the test actually exercises the intended call target at run time.

For example, consider an Engine component that defines the start_engine and stop_engine functions:

// COMPONENT: Engine 
// FILE: engine.c
#include "engine.h"

rpm_t rpm = {0, 0};
int pressure = 0;

void start_engine(int initial_rpm, int initial_pressure) { 
    rpm.value = initial_rpm; /* Data coupling: Write 'rpm.value' */
    rpm.threshold = RPM_THRESHOLD;
    pressure = initial_pressure;
}

void stop_engine(void) {
    rpm.value = 0;
    pressure = 0;
}

Next, consider a Controller component that contains a call to start_engine via a function pointer and a direct call to stop_engine:

// COMPONENT: Controller 
// FILE: controller.c
#include "engine.h"

void run_controller(int initial_rpm, int initial_pressure) {
    void (*start_engine_fnptr)(int, int) = start_engine;
    start_engine_fnptr(initial_rpm, initial_pressure); /* Control coupling: Indirect call */

    rpm.value = rpm.value + pressure * 100; /* Data coupling: Read 'rpm.value' */
    
    if (rpm.value > rpm.threshold) {
        stop_engine(); /* Control coupling: Direct call */
    }
}

Suppose that your test test_controller exercises run_controller(600, 5), and therefore exercises both function calls. The DCCC report indicates that the indirect call is "potentially covered" and the direct call is "covered".

Control coupling coverage report showing an indirect call as potentially covered and a direct call as covered.

Data Couplings

Software components usually exchange data via shared memory locations. One component writes to a shared memory location and another component reads this location after the previous write operation (but before any subsequent rewrite operation).

For the purposes of calculating data coupling, Polyspace defines a shared memory location as a pair (G,F) where G is a global variable and F is a field associated with the global variable. For non-aggregate types, F is identical to G. For aggregate types like structs, F can be any field or data member of G. For example:

  • A global variable pressure of int type corresponds to a single shared memory location.

  • A global variable rpm of type struct{int value; int threshold;} corresponds to two shared memory locations rpm.value and rpm.threshold.

Polyspace defines a data coupling as a pair of memory operations that consist of:

  • A write operation on a shared memory location in one component

  • A read operation on the same memory location by another component

A test that exercises both of these operations is considered to cover this data coupling.

Here is an example data couplings coverage report for the code and test described in the previous section:

Data coupling coverage report showing all data couplings as covered.

Limitations of Data Couplings Coverage Report

When calculating coverage of data couplings, the analysis does not verify the data flow at run time. Therefore, you might need to independently verify:

  • Whether a test that is reported to cover a data coupling exercises the write operation before the associated read operation.

  • Whether the report contains any write-read pairs that do not transfer data between the components, and therefore, should be ignored. For example, if a global variable is written multiple times in one component before it is read in another component, only the final write operation transfers data to the subsequent read operation.

See Also

Topics