Retrieving Code Coverage from ARM Cortex-M Microcontrollers

Introduction

In a previous post, we saw how to Measure Code Coverage on ARM Cortex-M Microcontrollers using Squish Coco. In this post, we’ll take a closer look at how to retrieve the code coverage data from these microcontrollers using GDB. We recommend reading the previous post before proceeding with this one.

This a simple approach and is convenient for those situations where you don’t have RS232 or some other connections to the host computer.

Atmel Studio Setup

Since we are going to use GDB, let’s first check whether it is configured properly in Atmel Studio. Click on the menu Tools > Options and under the Debugger options select GDB Settings. Make sure that the ARM Architecture is selected and that the path to the GDB executable is valid.

Sample Application

For the demonstration of the code coverage data retrieval we’ll use the sample application GccApplication1.zip. This is the same application which is provided in the blog post mentioned above. A simple application, it tests the function abs(x) which returns the absolute value of the specified integer argument, and intentionally it tests only one negative value of the input argument so that we can see the code which is not covered by the test.

#include "sam.h"

static int abs(int x) {           // simple function which we want to test
    if (x <= 0) {
        return x;
    }
    return -x;
}

static int testAbs() {            // simple test case
    return abs(-1) == 1 ? 1 : 0;
}

#ifdef __COVERAGESCANNER__        // Coco IO functions

// Code coverage of the rest of the code is not that important for this example
#pragma CoverageScanner(cov-off)

char coverage[256];               // execution report

static int csfputs(const char *s, void *stream) {
    char **buffer_p = (char**)(stream);
    do {
        **buffer_p = *s;
        (*buffer_p)++;
        s++;
    } while (*s);
    return 1;
}

static void *csfopenappend(const char *path) {
    int i;
    static char * pp = 0 ;
    for (i = 0 ; i < sizeof( coverage )/sizeof( coverage[0] ); i++)
        coverage[i] = '\n';
    pp = coverage ;
    return (void*)( & pp );
}

static int csfclose(void *fp) {
    return 1;
}

static void saveCov() {
    __coveragescanner_set_custom_io(0, csfputs, csfopenappend,
0, 0, csfclose, 0);
    __coveragescanner_save();
}

#endif

int main(void) {
    SystemInit();  // initialize the SAM system

    testAbs();     // execute test case

#ifdef __COVERAGESCANNER__
    saveCov();
#endif

    while (1) {}   // no exit - this is an MCU
}

The Coco Custom IO functions which are used here are practically the same as those in the post Retrieve Code Coverage Data via a Remote Debugger Connection. They are just a bit more densely formatted in order to save some space when presented on the screen.

Using GDB Console

After you compile the example, set a breakpoint at line 62 (the line with the while loop), deploy the application and start debugging. Once the debugger stops at the breakpoint, open the GDB Console in Atmel Studio (menu View > Other Windows > GDB Console).

Let’s first print the contents of the variable coverage to the console window:

p coverage

After this, change the GDB working directory to the directory of your project, where Squish Coco generated the .csmes file (in our case GccApplication1.elf.csmes). Do this by issuing the following command:

cd "C:\\Users\\autotest\\Documents\\Atmel Studio\\7.0\\GccApplication1\\Debug"

This will make dumping of the coverage variable into a file more convenient. By default (in the current version of Atmel Studio) the GDB working directory is %LOCALAPPDATA%\VirtualStore\Program Files (x86)\Atmel\Studio\7.0\atbackend and that might be a bit hard to find. Also Coco’s CoverageBrowser will not automatically offer the dump file for import if the file is put there.

Let’s now dump the value of the variable coverage into a file. To do so, issue the following command in GDB Console:

dump value GccApplication1.elf.csexe coverage

This will “dump” the value of the variable coverage into the file GccApplication1.elf.csexe which will be created in the directory C:\Users\autotest\Documents\Atmel Studio\7.0\GccApplication1\Debug.

Viewing the Results

Once you get the coverage data file (GccApplication1.elf.csexe) you can import it into the CoverageBrowser.

For this, open the file GccApplication1.elf.csmes (note the file has a different extension than GccApplication1.elf.csexe) from the CoverageBrowser.  You can find this .csmes file in the Debug folder of the project build. In our case, that is C:\Users\autotest\Documents\Atmel Studio\7.0\GccApplication1\Debug. After that, click the menu File > Load Execution Report in CoverageBrowser and select GccApplication1.elf.csmes file.

Comments