Code Coverage for Embedded Target with Eclipse, gcc and gcov


The great thing with open source tools like Eclipse and GNU (gcc, gdb) is that there is a wealth of excellent tools: one thing I had in mind to explore for a while is how to generate code coverage of my embedded application. Yes, GNU and Eclipse comes with code profiling and code coverage tools, all for free! The only downside seems to be that these tools seems to be rarely used for embedded targets. Maybe that knowledge is not widely available? So here is my attempt to change this :-).

Or: How cool is it to see in Eclipse how many times a line in my sources has been executed?

Line Coverage in Eclipse

Line Coverage in Eclipse

And best of all, it does not stop here….

NOTE: if using an actual version of the GNU tools, there might be an issue preventing to collect code coverage as explained in this article. Be sure to read the comments section below and check out https://github.com/reeteshranjan/libgcov-embedded.

Coverage with Eclipse

Coverage with Eclipse

To see how much percentage of my files and functions are covered?

gcov in Eclipse

gcov in Eclipse

Or even to show the data with charts?

Coverage Bar Graph View

Coverage Bar Graph View

Outline

In this tutorial I’m using a Freescale FRDM-K64F board: this board has ARM Cortex-M4F on it, with 1 MByte FLASH and 256 KByte of RAM. The approach used in this tutorial can be used with any embedded target, as long there is enough RAM to store the coverage data on the target. I’m using Eclipse Kepler with the ARM Launchpad GNU tools (q3 2014 release), but with small modifications any Eclipse version or GNU toolchain could be used. To generate the Code Coverage information, I’m using gcov.

Freescale FRDM-K64F Board

Freescale FRDM-K64F Board

Generating Code Coverage Information with gcov

gcov is an open source program which can generate code coverage information. It tells me how often each line of a program is executed. This is important for testing, as that way I can know which parts of my application actually has been executed by the testing procedures. Gcov can be used as well for profiling, but in this post I will use it to generate coverage information only.

The general flow to generate code coverage is:

  1. Instrument code: Compile the application files with a special option. This will add (hidden) code and hooks which records how many times a piece of code is executed.
  2. Generate Instrumentation Information: as part of the previous steps, the compiler generates basic block and line information. This information is stored on the host as *.gcno (Gnu Coverage Notes Object?) files.
  3. Run the application: While the application is running on the target, the instrumented code will record how many the lines or blocks in the application are executed. This information is stored on the target (in RAM).
  4. Dump the recorded information: At application exit (or at any time), the recorded information needs to be stored and sent to the host. By default gcov stores information in files. As a file system might not be alway available, other methods can be used (serial connection, USB, ftp, …) to send and store the information. In this tutorial I show how the debugger can be used for this. The information is stored as *.gcda (Gnu Coverage Data Analysis?) files.
  5. Generate the reports and visualize them with gcov.
General gcov Flow

General gcov Flow

gcc does the instrumentation and provides the library for code coverage, while gcov is the utility to analyze the generated data.

Coverage: Compiler and Linker Options

To generate the *.gcno files, the following option has to be added for each file which should generate coverage information:

-fprofile-arcs -ftest-coverage

💡 There is as well the ‘–coverage’ option (which is a shortcut option) which can be used both for the compiler and linker. But I prefer the ‘full’ options so I know what is behind the options.

 

-fprofile-arcs Compiler Option

The option -fprofile-arcs adds code to the program flow to so execution of source code lines are counted. It does with instrumenting the program flow arcs. From https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html:

-fprofile-arcs Add code so that program flow arcs are instrumented. During execution the program records how many times each branch and call is executed and how many times it is taken or returns. When the compiled program exits it saves this data to a file called auxname.gcda for each source file. The data may be used for profile-directed optimizations (-fbranch-probabilities), or for test coverage analysis (-ftest-coverage). Each object file’s auxname is generated from the name of the output file, if explicitly specified and it is not the final executable, otherwise it is the basename of the source file. In both cases any suffix is removed (e.g. foo.gcda for input file dir/foo.c, or dir/foo.gcda for output file specified as -o dir/foo.o). See Cross-profiling.

If you are not familiar with compiler technology or graph theory: An ‘Arc‘ (alternatively ‘edge’ or ‘branch’) is a directed link between a pair ‘Basic Blocks‘. A Basic is a sequence of code which has no branching in it (it is executed in a single sequence). For example if you have the following code:

k = 0;
if (i==10) {
  i += j;
  j++;
} else {
  foo();
}
bar();

Then this consists of the following four basic blocks:

Basic Blocks

Basic Blocks

The ‘Arcs’ are the directed edges (arrows) of the control flow. It is important to understand that not every line of the source gets instrumented, but only the arcs: This means that the instrumentation overhead (code size and data) depends how ‘complicated’ the program flow is, and not how many lines the source file has.

However, there is an important aspect to know about gcov: it provides ‘condition coverage‘ if a full expression evaluates to TRUE or FALSE. Consider the following case:

if (i==0 || j>=20) {

In other words: I get coverage how many times the ‘if’ has been executed, but *not* how many times ‘i==0’ or ‘j>=20’ (which would be ‘decision coverage‘, which is not provided here). See http://www.bullseye.com/coverage.html for all the details.

-ftest-coverage Compiler Option

The second option for the compiler is -ftest-coverage (from https://gcc.gnu.org/onlinedocs/gcc-3.4.5/gcc/Debugging-Options.html):

-ftest-coverage Produce a notes file that the gcov code-coverage utility (see gcov—a Test Coverage Program) can use to show program coverage. Each source file’s note file is called auxname.gcno. Refer to the -fprofile-arcs option above for a description of auxname and instructions on how to generate test coverage data. Coverage data will match the source files more closely, if you do not optimize.

So this option generates the *.gcno file for each source file I decided to instrument:

gcno file generated

gcno file generated

This file is needed later to visualize the data with gcov. More about this later.

Adding Compiler Options

So with this knowledge, I need to add

-fprofile-arcs -ftest-coverage

as compiler option to every file I want to profile. It is not necessary profile the full application: to save ROM and RAM and resources, I can add this option only to the files needed. Actually as a starter, I recommend to instrument a single source file only at the beginning. For this I select the properties (context menu) of my file Test.c I add the options in ‘other compiler flags’:

Coverage Added to Compilation File

Coverage Added to Compilation File

-fprofile-arcs Linker Option

Profiling not only needs a compiler option: I need to tell the linker that it needs to link with the profiler library. For this I add

-fprofile-arcs

to the linker options:

-fprofile-arcs Linker Option

-fprofile-arcs Linker Option

Coverage Stubs

Depending on your library settings, you might now get a lot of unresolved symbol linker errors. This is because by default the profiling library assumes to write the profiling information to a file system. However, most file systems do *not* have a file system. To overcome this, I add a stubs for all the needed functions. I have them added with a file to my project (see latest version of that file on GitHub):

/*
 * coverage_stubs.c
 *
 *  These stubs are needed to generate coverage from an embedded target.
 */
#include <stdio.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include "UTIL1.h"
#include "coverage_stubs.h"

/* prototype */
void gcov_exit(void);

/* call the coverage initializers if not done by startup code */
void static_init(void) {
  void (**p)(void);
  extern uint32_t __init_array_start, __init_array_end; /* linker defined symbols, array of function pointers */
  uint32_t beg = (uint32_t)&__init_array_start;
  uint32_t end = (uint32_t)&__init_array_end;

  while(beg<end) {
    p = (void(**)(void))beg; /* get function pointer */
    (*p)(); /* call constructor */
    beg += sizeof(p); /* next pointer */
  }
}

void _exit(int status) {
  (void) status;
  gcov_exit();
  for(;;){} /* does not return */
}

static const unsigned char *fileName; /* file name used for _open() */

int _write(int file, char *ptr, int len) {
  static unsigned char gdb_cmd[128]; /* command line which can be used for gdb */
  (void)file;
  /* construct gdb command string */
  UTIL1_strcpy(gdb_cmd, sizeof(gdb_cmd), (unsigned char*)"dump binary memory ");
  UTIL1_strcat(gdb_cmd, sizeof(gdb_cmd), fileName);
  UTIL1_strcat(gdb_cmd, sizeof(gdb_cmd), (unsigned char*)" 0x");
  UTIL1_strcatNum32Hex(gdb_cmd, sizeof(gdb_cmd), (uint32_t)ptr);
  UTIL1_strcat(gdb_cmd, sizeof(gdb_cmd), (unsigned char*)" 0x");
  UTIL1_strcatNum32Hex(gdb_cmd, sizeof(gdb_cmd), (uint32_t)(ptr+len));
  return len; /* on success, return number of bytes written */
}

int _open (const char *ptr, int mode) {
  (void)mode;
  fileName = (const unsigned char*)ptr; /* store file name for _write() */
  return 0; /* success */
}

int _close(int file) {
  (void) file;
  return 0; /* success closing file */
}

int _fstat(int file, struct stat *st) {
  (void)file;
  (void)st;
  st->st_mode = S_IFCHR;
  return 0;
}

int _getpid(void) {
  return 1;
}

int _isatty(int file) {
  switch (file) {
  case STDOUT_FILENO:
  case STDERR_FILENO:
  case STDIN_FILENO:
    return 1;
  default:
    errno = EBADF;
    return 0;
  }
}

int _kill(int pid, int sig) {
  (void)pid;
  (void)sig;
  errno = EINVAL;
  return (-1);
}

int _lseek(int file, int ptr, int dir) {
  (void)file;
  (void)ptr;
  (void)dir;
  return 0; /* return offset in file */
}

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wreturn-type"
__attribute__((naked)) static unsigned int get_stackpointer(void) {
  __asm volatile (
    "mrs r0, msp   \r\n"
    "bx lr         \r\n"
  );
}
#pragma GCC diagnostic pop

void *_sbrk(int incr) {
  extern char __HeapLimit; /* Defined by the linker */
  static char *heap_end = 0;
  char *prev_heap_end;
  char *stack;

  if (heap_end==0) {
    heap_end = &__HeapLimit;
  }
  prev_heap_end = heap_end;
  stack = (char*)get_stackpointer();

  if (heap_end+incr > stack) {
    _write (STDERR_FILENO, "Heap and stack collision\n", 25);
    errno = ENOMEM;
    return  (void *)-1;
  }
  heap_end += incr;
  return (void *)prev_heap_end;
}

int _read(int file, char *ptr, int len) {
  (void)file;
  (void)ptr;
  (void)len;
  return 0; /* zero means end of file */
}

💡 In this code I’m using the UTIL1 (Utility) Processor Expert component, available on SourceForge. If you do not want/need this, you can remove the lines with UTIL1.

Coverage Stubs File in Project

Coverage Stubs File in Project

Coverage Constructors

There is one important thing to mention: the coverage data structures need to be initialized, similar to constructors for C++. Depending on your startup code, this might *not* be done automatically. Check your linker .map file for some _GLOBAL__ symbols:

 .text._GLOBAL__sub_I_65535_0_TEST_Test
                0x0000395c       0x10 ./Sources/Test.o

Such a symbol should exist for every source file which has been instrumented with coverage information. These are functions which need to be called as part of the startup code. Set a breakpoint in your code at the given address to check if it gets called. If not, you need to call it yourself.

❗ Typically I use the linker option ‘-nostartfiles’), and I have my startup code. In that case, these constructors are not called by default, so I need to do myself. See http://stackoverflow.com/questions/6343348/global-constructor-call-not-in-init-array-section

In my linker file I have this:

  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } > m_text

This means that there is a list of constructor function pointers put together between __init_array_start and __init_array_end. So all what I need is to iterate through this array and call the function pointers:

/* call the coverage initializers if not done by startup code */
void static_init(void) {
  void (**p)(void);
  extern uint32_t __init_array_start, __init_array_end; /* linker defined symbols, array of function pointers */
  uint32_t beg = (uint32_t)&__init_array_start;
  uint32_t end = (uint32_t)&__init_array_end;

  while(beg<end) {
    p = (void(**)(void))beg; /* get function pointer */
    (*p)(); /* call constructor */
    beg += sizeof(p); /* next pointer */
  }
}

So I need to call this function as one of the first things inside main().

Heap Management

The other aspect of the coverage library is the heap usage. At the time of dumping the data, it uses malloc() to allocate heap memory. As typically my applications do not use malloc(), I still need to provide a heap for the profiler. Therefore I provide a custom sbrk() implementation in my coverage_stubs.c:

void *_sbrk(int incr) {
  extern char __HeapLimit; /* Defined by the linker */
  static char *heap_end = 0;
  char *prev_heap_end;
  char *stack;

  if (heap_end==0) {
    heap_end = &__HeapLimit;
  }
  prev_heap_end = heap_end;
  stack = (char*)get_stackpointer();

  if (heap_end+incr > stack) {
    _write (STDERR_FILENO, "Heap and stack collision\n", 25);
    errno = ENOMEM;
    return  (void *)-1;
  }
  heap_end += incr;
  return (void *)prev_heap_end;
}

❗ It might be that several kBytes of heap are needed. So if you are running in a memory constraint system, be sure that you have enough RAM available.

The above implementation assumes that I have space between my heap end and the stack area.

❗ If your memory mapping/linker file is different, of course you will need to change that _sbrk() implementation.

Compiling and Building

Now the application should compile and link without errors.Check that the .gcno files are generated:

💡 You might need to refresh the folder in Eclipse.

.gcno files generated

.gcno files generated

In the next steps I’m showing how to get the coverage data as *.gcda files to the host using gdb.

Using Debugger to get the Coverage Data

The coverage data gets dumped when _exit() gets called by the application. Alternatively I could call gcov_exit() or __gcov_flush() any time. What it then does is

  1. Open the *.gcda file with _open() for every instrumented source file.
  2. Write the data to the file with _write().

So I can set a breakpoint in the debugger to both _open() and _write() and have all the data I need 🙂

With _open() I get the file name, and I store it in a global pointer so I can reference it in _write():

static const unsigned char *fileName; /* file name used for _open() */

int _open (const char *ptr, int mode) {
  (void)mode;
  fileName = (const unsigned char*)ptr; /* store file name for _write() */
  return 0;
}

In _write() I get a pointer to the data and the length of the data. Here I can dump the data to a file using the gdb command:

dump binary memory <file> <hexStartAddr> <hexEndAddr>

I could use a calculator to calculate the memory dump range, but it is much easier if I let the program generate the command line for gdb :-):

int _write(int file, char *ptr, int len) {
  static unsigned char gdb_cmd[128]; /* command line which can be used for gdb */
  (void)file;
  /* construct gdb command string */
  UTIL1_strcpy(gdb_cmd, sizeof(gdb_cmd), (unsigned char*)"dump binary memory ");
  UTIL1_strcat(gdb_cmd, sizeof(gdb_cmd), fileName);
  UTIL1_strcat(gdb_cmd, sizeof(gdb_cmd), (unsigned char*)" 0x");
  UTIL1_strcatNum32Hex(gdb_cmd, sizeof(gdb_cmd), (uint32_t)ptr);
  UTIL1_strcat(gdb_cmd, sizeof(gdb_cmd), (unsigned char*)" 0x");
  UTIL1_strcatNum32Hex(gdb_cmd, sizeof(gdb_cmd), (uint32_t)(ptr+len));
  return 0;
}

That way I can copy the string in the gdb debugger:

Generated GDB Memory Dump Command

Generated GDB Memory Dump Command

 

That command gets pasted and executed in the gdb console:

gdb command line

gdb command line

After execution of the program, the *.gcda file gets created (refresh might be necessary to show it up):

gcda file created

gcda file created

Repeat this for all instrumented files as necessary.

Showing Coverage Information

To show the coverage information, I need the *.gcda, the *.gcno plus the .elf file.

💡 Use Refresh if not all files are shown in the Project Explorer view

Files Ready to Show Coverage Information

Files Ready to Show Coverage Information

Then double-click on the gcda file to show coverage results:

Double Click on gcda File

Double Click on gcda File

Press OK, and it opens the gcov view. Double click on file in that view to show the details:

gcov Views

gcov Views

Use the chart icon to create a chart view:

Chart view

Chart view

Bargraph View

Bar Graph View

Video of Steps to Create and Use Coverage

The following video summarizes the steps needed:

Data and Code Overhead

Instrumenting code to generate coverage information means that it is an intrusive method: it impacts the application execution speed, and needs extra RAM and ROM. How much heavily depends on the complexity of the control flow and on the number of arcs. Higher compiler optimizations would reduce the code size footprint, however optimizations are not recommended for coverage sessions, as this might make the job of the coverage much harder.

I made a quick comparison using my test application. I used the ‘size’ GNU command (see “Printing Code Size Information in Eclipse”).

Without coverage enabled, the application footprint is:

arm-none-eabi-size --format=berkeley "FRDM-K64F_Coverage.elf"
   text       data        bss        dec        hex    filename
   6360       1112       5248      12720       31b0    FRDM-K64F_Coverage.elf

With coverage enabled only for Test.c gave:

arm-none-eabi-size --format=berkeley "FRDM-K64F_Coverage.elf"
   text       data        bss        dec        hex    filename
  39564       2376       9640      51580       c97c    FRDM-K64F_Coverage.elf

Adding main.c to generate coverage gives:

arm-none-eabi-size --format=berkeley "FRDM-K64F_Coverage.elf"
   text       data        bss        dec        hex    filename
  39772       2468       9700      51940       cae4    FRDM-K64F_Coverage.elf

So indeed there is some initial add-up because of the coverage library, but afterwards adding more source files does not add up much.

Summary

It took me a while and reading many articles and papers to get code coverage implemented for an embedded target. Clearly, code coverage is easier if I have a file system and plenty of resources available. But I’m now able to retrieve coverage information from a rather small embedded system using the debugger to dump the data to the host. It is not practical for large sets of files, but at least a starting point :-).

I have committed my Eclipse Kepler/Launchpad project I used in this tutorial on GitHub.

Ideas I have in my mind:

  • Instead using the debugger/gdb, use FatFS and SD card to store the data
  • Exploring how to use profiling
  • Combining multiple coverage runs

Happy Covering 🙂

Links:

Advertisements

46 thoughts on “Code Coverage for Embedded Target with Eclipse, gcc and gcov

  1. Hi Erich

    A useful article on the important topic of code quality and testing. However, to me, this article is mostly about how to run the tool, and I think it would be valuable if you could write a companion piece at a higher level. Perhaps you could extend your example to explain: (1) How to test bar(), (2) why TEST_Test() will test bar(), (3) how to run TEST_Test() – your video shows it being run in a for(;;) loop, (3) why the code coverage tool provides insight into the quality of the code and the test coverage, and (4) provide an example of a bug or weakness that this process can reveal.

    I don’t know whether you cover formal quality assurance methodology with your students? I do know that it is a real subject that must be addressed in even simple embedded systems, that they and your readers (like me!) will encounter frequently in the real world. For example, European and US regulators both mandate the use of IEC 62304 “Medical Device – Software Life Cycle Processes” which requires the design of a test process and the documenting of the results. It would be great if you could follow this article with others that cover GNU tools that can be used. A perfect article would take a real-world requirement of a standard and show a real-world GNU tool being used to address the requirement.

    A serious related question is the extent to which PE code, including FRTOS, can be used in medical devices, and other safety-critical systems. Discuss!

    Finally, a couple of good links:
    http://www.emdt.co.uk/article/developing-medical-device-software-iso-62304
    http://medicaldesign.com/prototyping/industry-viewpoint-device-makers-can-take-cots-only-clear-soup

    Thanks as always for your invaluable articles. – Charles

    Like

    • Hi Charles,
      yes, this article is about running the tools, not about the higher level motivation for coverage, tests and to meet any compliances (e.g. medical). I don’t cover that in my course because this is a very large topic on its own, and this topic is covered with other courses we offer at the university (we just have started a new department around medical technologies).
      As for PE code, FRTOS and any other sources or libraries: of course they can be used in any kind of applications, but it depends on the given regulations. E.g. if the regulation says that only ADA can be used, then it will be hard to find a FreeRTOS written in ADA, or that certain things like inline assembly are not allowed. But for all these kind of things the usual solution is to deal with it as an exception, and carefully document and making things compliant.

      Erich

      Like

  2. Pingback: Code Coverage with gcov, launchpad tools and Eclipse Kinetis Design Studio V3.0.0 | MCU on Eclipse

  3. Hi Erich
    First of all great tutorial about gcov on embedded system for code coverage.
    Before asking my questions to you I would like to inform you about my project details…

    Myself currently working with LPC54102 microcontroller and target was to test entire peripheral APIs. That part is almost done and we put Jenkins as CI tools for testing incremental release of LPC54012 APIs lib. Now the remaining part is the code coverage for that we use gcov.
    As we all know that *.gcda files are created inside target board and its always make BIG question mark!!! Your tutorial is the only way I just finding out over google. So, what I does at my end, port your entire coverage_stubs.c file and related changes in my project.

    Now I suffered from linker errors(about start and end init_array) in my LPCXpresso IDE(modified eclipse). My knowledge is very limited about linker script and section called .init_array. What can I do and get context about *.gcda files back to host.

    Thanks in advance 🙂

    Like

      • Compiler Error message seems like this :-Undefined referance at __init_array_start, __init_array_end symbol. But I am already defined this two symbol inside linker script within .init_array section.

        Like

      • Something like this?
        .init_array :
        {
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array*))
        PROVIDE_HIDDEN (__init_array_end = .);
        } > m_text

        Like

      • the other thought I have: you are not linking the proper libraries? These symbols are used with the standard libraries for the initialization code during the startup.

        Like

      • Actually In my LPC linker script there is no any section called .init_array but after viewing your code I came across static_init function and realized that it is necessary for me to copy that part in my code as well as .init_array section symbol addition in my LPC linker script. I am using Redlib(nohost) lib currently for building my code in LPCXpresso IDE. Do you think that I’m missing something in startup code??

        Like

      • Yes, I think you are missing something with/in the startup code. Gcov as I have used it depends on semihosting, so you need to configure your project that it is using/working with semihosting, including terminal and file I/O.

        Liked by 1 person

  4. Hi Erich,

    Great work!

    I have a system that has FatFS, an SD card, threadx and codesourcery toolchain for ARM.

    I have already added the -fprofile-arcs -ftest-coverage options and I see one .gcno file being generated. No warnings, errors or unresolved symbol linker errors are generated during the build.

    Then, I load the binary to the SD card. Reset the system, it works as usual, but, I don’t see the .gcda files.

    I’m guessing I have to add some coverage stubs to the mix. Is that correct? How would I go about doing that?

    Thanks a lot,

    Joe

    Like

    • Hi Joe,
      Are you using an I/O library which is able to write to the SD card/FatFS? What you need is that fopen()/fwrite/etc are using the SD card and file system. This will not be the default (usually). So you need to ensure (or implement) that your standard I/O library is taking advantage of the SD card. I hope that makes sense?

      Like

      • I checked and fopen, fseek et al are available in the system. Could it just be I need to do the linker initialuzation of the counters?

        Thanks

        Like

      • Hi Joe,
        that the functions are there does not say anything. Depending on your library, they could be ’empty’ and not doing anyting. Are you sure you have a full semihosting implementation available?

        Like

  5. Hi! Thanks for the post. I am working on getting coverage for a Raspberry Pi from Eclipse, following your guide. The thing is I get to the point where I get this error:

    Error: selected processor does not support requested special purpose register — `mrs r0,msp’
    make: *** [src/coverage_stubs.o] Error 1

    Do you know how can I solve it? Or do you have any guidance for getting coverage for the RPi?

    Thanks!

    Like

    • The Raspberry Pi is using a different ARM core. I used ARM-M4/M0+, while the Raspberry Pi is an ARM11. The assembly syntax is different, so you would need to change the assembly code to ARM11 assembly code.

      Like

      • Got it. I think the error is because the instruction MRS is not supported on ARMv6. Do you know any similar instruction when moving the SP to r0?

        Like

  6. Hi Eric,

    it’s a very useful articles!

    i am trying to use the TWRK64F120M to test the code coverage.
    After i look into the articles, i have some questions:
    1. how do you know the memory and the memory size to save the coverage data?
    2. the *.gcda open and the gdb_cmd build are done manually? because i didn’t found the call flow of the _open() and _write().

    Like

    • Hi Meili,
      first of, there is a newer this subject here: https://mcuoneclipse.com/2015/05/31/code-coverage-with-gcov-launchpad-tools-and-eclipse-kinetis-design-studio-v3-0-0/
      That approach is still using a low level approach, but give you some insights how things are working internally.

      Related to that, you might have a look at profiling here: https://mcuoneclipse.com/2015/08/23/tutorial-using-gnu-profiling-gprof-with-arm-cortex-m/
      The above article is using semihosting. I belive I have done coverage with semihosting too, but not sure if I ever published an article about this, would have to check.

      about your questions:
      1) The size of memory is based on how many ‘arcs’ are present in the code. See the article about profiling. I usually do some trial-and-error size estimation.
      2) the *.gcda files are created as part of the _exit() library call. _exit() will then call _open() and _write().

      I hope this helps,
      Erich

      Like

      • yes, i have looked into the article and the code in the Github in the link
        https://mcuoneclipse.com/2015/05/31/code-coverage-with-gcov-launchpad-tools-and-eclipse-kinetis-design-studio-v3-0-0/.
        i tried to use the code coverage without profiling (set option -ftest-coverage only).
        in the coverage_stubs.c, i saw the exit() call the gcov_exit() as:
        void gcov_exit(void);

        void _exit(int status) {
        (void) status;
        gcov_exit();
        for(;;){} /* does not return */
        }

        so from above code, i didn’t see the call flow of open() and write().

        Like

      • Hi Meili,
        it is burried behind gcov_exit(). open() and write() will be called from the library. You can see this if you set a breakpoint on open() and write().
        Erich

        Like

      • Hi Erich,

        i tried the full option set -fprofile-arcs -ftest-coverage, the open/write will be called by gcov_exit. but if set the compiler option with -ftest-coverage, the open/write with never be called by gcov_exit().

        Like

      • Hi Meili,
        did you check in the debugger if maybe the program is running into an assertion or crashes somewhere? It would be interesting to know where it goes after gcov_exit(). Beside of that: do you have semihosting turned on/enabled? If so, it will try to use the semihosting open/write. If you are using P&E semihosting: their fopen/fwrite is not implemented, so you would have to use the Segger semihosting.

        Like

  7. HI Erich,
    Thanks for the amazing article.

    While I tried this with the libgcov.a from the arm toolchain path directly, I could n’t call gcov_exit() in my test application coz, the symbol was HIDDEN in libgcov.a.
    How can I get over this?

    Like

  8. Hi Erich,
    Yes, I was using the libgcov.a from ARM (launchpad) embedded toolchain.
    Now, letting linker to find the correct library, gives the following error.
    arm-none-eabi-ld: error: required section ‘.got’ not found in the linker script
    arm-none-eabi-ld: final link failed: Invalid operation

    But I see .got data put in .text section.
    .text : {
    _stext = ABSOLUTE(.);
    KEEP(*(.text.startup))
    *(.text .text.*)
    *(.fixup)
    *(.gnu.warning)
    *(.rodata .rodata.*)
    *(.gnu.linkonce.t.*)
    *(.glue_7)
    *(.glue_7t)
    *(.got)
    *(.gcc_except_table)
    *(.gnu.linkonce.r.*)
    _etext = ABSOLUTE(.);
    } > SRAM

    should I create a exclusive .got section as below. If so, will the following do?

    .got : {
    *(.got*)
    } > SRAM

    .rel.plt : {
    *(.rel.plt*)
    } > SRAM

    Coz, adding above sections built the binary but it didn’t boot up.

    Like

  9. Hi Erich,

    I followed all the steps and using the same test code on windows machine but I’m getting the value of gdb_cmd as :
    dump binary memory D:\\code_coverage_test\\test1\\Debug/Sources/test.gcda 0x200030BB 0x200030BC
    That is ONLY 1 byte and hence code coverage is 0%.
    But interestingly, when I try the same code on Ubuntu Machine, I’m getting the desired code coverage.
    Can you guide what the problem might be?

    Thanks!

    Like

      • Hey Erich!

        Using newlib-nano library on my windows machine, I’m able to get more than one byte in buffer and I’m able to create gcda files for all my source code files but when I try opening the gcda file(s) in KDS, the code coverage is still 0%.

        Could you please suggest how to solve this?

        Thanks
        Raja

        Like

      • Hey Erich,

        Thanks for the pointer. Earlier I had only renamed gcov and addr2line which didnt work but after renaming strings, c++filt and nm, I’m getting code coverage!

        Thanks a lot for your help! 🙂

        Like

      • Erich,
        Also on double clicking on file in that view, I’m unable to see the executed lines in green and not executed ones in red colors. I’ve checked the related settings in KDS, but still nothing works. Do you had to enable any other option or any other change?

        Like

      • Hi Raja,
        either the code has not been executed at all?
        Or that somehow Eclipse finds other GNU tools than the one matching the ones in KDS?
        I did not had to set anything else in KDS, but I had to make sure that really all the correct binary tools were present.
        Have you tried the command line tools to verify that coverage is indeed present in your data files? To single out problems in Eclipse.

        Like

  10. Thanks for this post. Most of things worked fine except that with current version of gnu arm toolchain, which is 5-2016-q3-update as of now (https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q3-update), the function __gcov_flush just hangs. It was used because gcov_exit is made static in this version.

    I made a port of gcov_exit starting from the gcc source for this version, and on using this directly rather than calling the gcc library’s version, I found the bug that was causing the hang. Basically, there is one gcov_info struct node per source file, and at 3 places in the flow of gcov_exit/__gcov_flush a circular list of these nodes is accessed as a non-circular singly linked list! So basically the loop never ends causing the hang. My attempt of calling __gcov_flush got stuck at the 1st of these 3 hangs.

    Not sure how such a big bug was introduced much later to this article being published and things working. I have published my port at https://github.com/reeteshranjan/libgcov-embedded for everybody’s use. You’ll definitely need this if you are in October/November 2016 or later till the bug in gcc code is fixed.

    Like

    • Many, many thanks for that information! This explains why one student reported to me that with the newer GNU toolchain coverage did not work. Now everything makes sense. Many thanks again for sharing!

      Like

  11. Pingback: Code coverage using Eclipse, GNU ARM toolchain, and Gcov for embedded systems | Technical Info Articles

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s