BullseyeCoverage
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.
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:
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.
The BullseyeCoverage/run
directory contains small footprint run-time ports for many tools and platforms.
Some notable examples are listed below.
Source | Description |
---|---|
run/libcov-printf.c | Data output via standard C printf , see comments in file |
run/libcov-freeRtosFatSl.c | For FreeRTOS with FAT SL file system |
run/libcov-smx.c | For the Micro Digital smxFS file system |
run/libcov-uefi.c | Data output via the UEFI DEBUG macro |
run/libcov-memoryDump.c | Data output via memory dump to host system |
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.
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.
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.
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.
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
.
write(2,
...)
if you have a way to report run-time errors.
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.
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).
pid_t getpid(void);
The getpid
function is called to generate unique filenames for saving coverage.
If there is no possibility of multiple instrumented programs running at one time,
this function may return the constant value 1.
If there are multiple programs, getpid
should return unique values to prevent overwriting data files.
A definition of pid_t
is not required.
getpid
may return any integral type.
int open(const char *path, int oflag, ...);
The open
function is called with the arguments below.
open("BullseyeCoverage.data-pid", O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR)
The function must not return the file descriptor value 2, which is reserved for error reporting.
Only one file is opened at a time.
The file name component pid is a textual representation of the value returned by getpid
.
ssize_t write(int fildes, const void *buf, size_t nbyte);
Calls to the write
function pass a buffer that ends with one new-line.
The buffer is followed by a nul character.
That is, buf[nbyte-1] == '\n'
and buf[nbyte] == '\0'
.
The nbyte argument is not more than 128.
All data passed to write
consists of printable ASCII characters (0x20-0x7e) and the new-line (0x10).
The run-time reports errors by calling write
with file descriptor 2.
int close(int fildes);
The return value is not used.
cov_atomic_tryLock
,
cov_atomic_unlock
The run-time uses these functions to synchronize multiple threads or interrupt handlers.
They are defined in run/libcov-atomic.h
.
This source already supports many compiler/CPU combinations.
Updated: 2 Aug 2023
Copyright © Bullseye Testing Technology. All Rights Reserved.