BullseyeCoverage Up Contents Search

Embedded Systems

This document describes how to port the BullseyeCoverage run-time library to an embedded environment for which a solution is not already provided.

This information does not apply to embedded systems running Linux or other Unix-like systems.

Small Footprint Configuration

For embedded systems, the small footprint run-time library configuration is provided as source code that you may need to tailor to your environment. This configuration uses minimal memory, requires few system functions, and does not require a file system. Data is not collected automatically and some additional steps are required:

  1. Call cov_dumpData to save coverage data from memory
  2. Transfer the resulting data to the host file system
  3. Post-process the data with covpost to transfer measurements into the coverage file

Data Transfer Guidelines

You can implement the i/o functions described below using any mechanism capable of transfering a small amount of plain text from the embedded system to the host system. For example, you can use a serial port, a network connection, or a memory card. A very simple implementation could print the data directly to a debugger console where it is captured and saved to a file.

The maximum total amount of data is usually less than 10% of the size of the coverage file. The amount of data written for each instrumented object file is typically 60-150 bytes, but could be more. The data is plain printable text and should be transferred as-is without any transformation or encoding.

Examples

The BullseyeCoverage/run directory contains small footprint run-time ports for many tools and platforms. Some notable examples are listed below.

SourceDescription
run/libcov-printf.cData output via standard C printf, see comments in file
run/libcov-freeRtosFatSl.cFor FreeRTOS with FAT SL file system
run/libcov-smx.cFor the Micro Digital smxFS file system
run/libcov-uefi.cData output via the UEFI DEBUG macro
run/libcov-memoryDump.cData output via memory dump to host system

Memory Requirements

Layout and Initialization

When covc option --section is enabled, the BSS and CONST sections used for BullseyeCoverage instrumentation data are named .bcovbss and .bcovcon, respectively. This applies to the compilers listed below.

If you use covc option --section and you have a linker definition file that specifies the memory layout, add these sections to the file. The .bcovbss section contains uninitialized data and .bcovcon contains constant data. The run-time writes to .bcovbss very frequently and reads from .bcovcon infrequently. For example, you might locate .bcovbss in fast memory and .bcovcon in slow memory.

BullseyeCoverage does not require BSS sections to be initialized to zeros.

Memory Consumption

Instrumented code size is generally 1.4x the uninstrumented size. The increase varies, depending on the compiler brand, compiler optimization settings, and CPU architecture.

For each object file instrumented, BullseyeCoverage adds a data structure in memory with a size about 50 bytes plus one byte for each probe in that object file. Each probe corresponds to a function entry, condition outcome, decision outcome, switch label and exception handler.

The small footprint run-time library code size is approximately 3KB. The data usage is about 200 bytes. Stack usage is approximately 100 bytes in addition to what the i/o functions use.

Porting Procedure

These steps describe in detail how best to create a BullseyeCoverage run-time implementation and integrate it with your project. Read all these steps before beginning work. Do the work in the order described.

  1. Enable coverage build and verify your build procedure invokes BullseyeCoverage so that your code is instrumented. You should see the banner shown below in your build log each time the compiler is invoked.
    BullseyeCoverage Compile C++, Copyright (c) Bullseye Testing Technology
    

    If you do not see the banner, see Integrating With Your Build Process.

    It is normal to see link errors due to unresolved symbols at this step. After you compile, look at a coverage report. The measurements show all 0%, but you can see what code is instrumented for coverage measurement.

  2. Add run/libcov-userDefined.c to your project. This source is a template for implementing the required functions. It is recommended to add all your code to this file as indicated by the comments. Alternatively, you can use one of the example implementation files in the BullseyeCoverage/run such as libcov-printf.c.

  3. Build your project and do the minimum work required to resolve any compile and link errors. Create stub functions to resolve undefined symbol errors. You should see no build time errors before moving on.

  4. Implement write(2,...) if you have a way to report run-time errors.

  5. Implement the data transfer functionality (open, write, close). Iteratively build and run your instrumented executable to try out your changes. When the covpost program reports no errors and the coverage reports show all expected measurements and you get no run-time errors, your port is complete.

  6. Send us your source code changes. We will inspect your code and verify that it complies with our run-time requirements. If you like, we can incorporate your code into our release distribution so that you do not need to apply patches when you obtain updates. By maintaining your port in our distribution, we can take your run-time requirements into consideration when we make architectural changes in the future.

Required Run-Time Functions

You must provide an implementation for the functions listed below. Usage of these functions is consistent with the Single UNIX Specification (POSIX.1 / IEEE Std 1003.1).

Updated: 2 Aug 2023