Movatterモバイル変換


[0]ホーム

URL:


Previous:, Up:gcov—a Test Coverage Program   [Contents][Index]


11.6 Profiling and Test Coverage in Freestanding Environments

In case your application runs in a hosted environment such as GNU/Linux, thenthis section is likely not relevant to you. This section is intended forapplication developers targeting freestanding environments (for exampleembedded systems) with limited resources. In particular, systems or test caseswhich do not support constructors/destructors or the C library file I/O. Inthis section, thetarget system runs your application instrumented forprofiling or test coverage. You develop and analyze your application on thehost system. We now provide an overview how profiling and test coveragecan be obtained in this scenario followed by a tutorial which can be exercisedon the host system. Finally, some system initialization caveats are listed.

11.6.1 Overview

For an application instrumented for profiling or test coverage, the compilergenerates some global data structures which are updated by instrumentation codewhile the application runs. These data structures are called thegcovinformation. Normally, when the application exits, the gcov information isstored to.gcda files. There is one file per translation unitinstrumented for profiling or test coverage. The function__gcov_exit(), which stores the gcov information to a file, is called bya global destructor function for each translation unit instrumented forprofiling or test coverage. It runs at process exit. In a global constructorfunction, the__gcov_init() function is called to register the gcovinformation of a translation unit in a global list. In some situations, thisprocedure does not work. Firstly, if you want to profile the globalconstructor or exit processing of an operating system, the compiler generatedfunctions may conflict with the test objectives. Secondly, you may want totest early parts of the system initialization or abnormal program behaviourwhich do not allow a global constructor or exit processing. Thirdly, you needa filesystem to store the files.

The-fprofile-info-section GCC option enables you to use profiling andtest coverage in freestanding environments. This option disables the use ofglobal constructors and destructors for the gcov information. Instead, apointer to the gcov information is stored in a special linker input section foreach translation unit which is compiled with this option. By default, thesection name is.gcov_info. The gcov information is staticallyinitialized. The pointers to the gcov information from all translation unitsof an executable can be collected by the linker in a contiguous memory block.For the GNU linker, the below linker script output section definition can beused to achieve this:

  .gcov_info      :  {    PROVIDE (__gcov_info_start = .);    KEEP (*(.gcov_info))    PROVIDE (__gcov_info_end = .);  }

The linker will provide two global symbols,__gcov_info_start and__gcov_info_end, which define the start and end of the array of pointersto gcov information blocks, respectively. TheKEEP () directive isrequired to prevent a garbage collection of the pointers. They are notdirectly referenced by anything in the executable. The section may be placedin a read-only memory area.

In order to transfer the profiling and test coverage data from the target tothe host system, the application has to provide a function to produce areliable in order byte stream from the target to the host. The byte stream maybe compressed and encoded using error detection and correction codes to meetapplication-specific requirements. The GCC providedlibgcov targetlibrary provides two functions,__gcov_info_to_gcda() and__gcov_filename_to_gcfn(), to generate a byte stream from a gcovinformation bock. The functions are declared in#include <gcov.h>. Thebyte stream can be deserialized by themerge-stream subcommand of thegcov-tool to create or update.gcda files in the hostfilesystem for the instrumented application.

11.6.2 Tutorial

This tutorial should be exercised on the host system. We will build a programinstrumented for test coverage. The program runs an application and dumps thegcov information tostderr encoded as a printable character stream. Theapplication simply decodes such character streams fromstdin and writesthe decoded character stream tostdout (warning: this is binary data).The decoded character stream is consumed by themerge-streamsubcommand of thegcov-tool to create or update the.gcdafiles.

To get started, create an empty directory. Change into the new directory.Then you will create the following three files in this directory

  1. app.h - a header file included byapp.c andmain.c,
  2. app.c - a source file which contains an example application, and
  3. main.c - a source file which contains the program main function and codeto dump the gcov information.

Firstly, create the header fileapp.h with the following content:

static const unsigned char a = 'a';static inline unsigned char *encode (unsigned char c, unsigned char buf[2]){  buf[0] = c % 16 + a;  buf[1] = (c / 16) % 16 + a;  return buf;}extern void application (void);

Secondly, create the source fileapp.c with the following content:

#include "app.h"#include <stdio.h>/* The application reads a character stream encoded by encode() from stdin,   decodes it, and writes the decoded characters to stdout.  Characters other   than the 16 characters 'a' to 'p' are ignored.  */static int can_decode (unsigned char c){  return (unsigned char)(c - a) < 16;}voidapplication (void){  int first = 1;  int i;  unsigned char c;  while ((i = fgetc (stdin)) != EOF)    {      unsigned char x = (unsigned char)i;      if (can_decode (x))        {          if (first)            c = x - a;          else            fputc (c + 16 * (x - a), stdout);          first = !first;        }      else        first = 1;    }}

Thirdly, create the source filemain.c with the following content:

#include "app.h"#include <gcov.h>#include <stdio.h>#include <stdlib.h>/* The start and end symbols are provided by the linker script.  We use the   array notation to avoid issues with a potential small-data area.  */extern const struct gcov_info *const __gcov_info_start[];extern const struct gcov_info *const __gcov_info_end[];/* This function shall produce a reliable in order byte stream to transfer the   gcov information from the target to the host system.  */static voiddump (const void *d, unsigned n, void *arg){  (void)arg;  const unsigned char *c = d;  unsigned char buf[2];  for (unsigned i = 0; i < n; ++i)    fwrite (encode (c[i], buf), sizeof (buf), 1, stderr);}/* The filename is serialized to a gcfn data stream by the   __gcov_filename_to_gcfn() function.  The gcfn data is used by the   "merge-stream" subcommand of the "gcov-tool" to figure out the filename   associated with the gcov information. */static voidfilename (const char *f, void *arg){  __gcov_filename_to_gcfn (f, dump, arg);}/* The __gcov_info_to_gcda() function may have to allocate memory under   certain conditions.  Simply try it out if it is needed for your application   or not.  */static void *allocate (unsigned length, void *arg){  (void)arg;  return malloc (length);}/* Dump the gcov information of all translation units.  */static voiddump_gcov_info (void){  const struct gcov_info *const *info = __gcov_info_start;  const struct gcov_info *const *end = __gcov_info_end;  /* Obfuscate variable to prevent compiler optimizations.  */  __asm__ ("" : "+r" (info));  while (info != end)  {    void *arg = NULL;    __gcov_info_to_gcda (*info, filename, dump, allocate, arg);    fputc ('\n', stderr);    ++info;  }}/* The main() function just runs the application and then dumps the gcov   information to stderr.  */intmain (void){  application ();  dump_gcov_info ();  return 0;}

If we compileapp.c with test coverage and no extra profiling options,then a global constructor (_sub_I_00100_0 here, it may have a differentname in your environment) and destructor (_sub_D_00100_1) is used toregister and dump the gcov information, respectively. We also see undefinedreferences to__gcov_init and__gcov_exit:

$ gcc --coverage -c app.c$ nm app.o0000000000000000 r a0000000000000030 T application0000000000000000 t can_decode                 U fgetc                 U fputc0000000000000000 b __gcov0.application0000000000000038 b __gcov0.can_decode0000000000000000 d __gcov_.application00000000000000c0 d __gcov_.can_decode                 U __gcov_exit                 U __gcov_init                 U __gcov_merge_add                 U stdin                 U stdout0000000000000161 t _sub_D_00100_10000000000000151 t _sub_I_00100_0

Compileapp.c andmain.c with test coverage and-fprofile-info-section. Now, a read-only pointer size object ispresent in the.gcov_info section and there are no undefined referencesto__gcov_init and__gcov_exit:

$ gcc --coverage -fprofile-info-section -c main.c$ gcc --coverage -fprofile-info-section -c app.c$ objdump -h app.oapp.o:     file format elf64-x86-64Sections:Idx Name          Size      VMA               LMA               File off  Algn  0 .text         00000151  0000000000000000  0000000000000000  00000040  2**0                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE  1 .data         00000100  0000000000000000  0000000000000000  000001a0  2**5                  CONTENTS, ALLOC, LOAD, RELOC, DATA  2 .bss          00000040  0000000000000000  0000000000000000  000002a0  2**5                  ALLOC  3 .rodata       0000003c  0000000000000000  0000000000000000  000002a0  2**3                  CONTENTS, ALLOC, LOAD, READONLY, DATA  4 .gcov_info    00000008  0000000000000000  0000000000000000  000002e0  2**3                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA  5 .comment      0000004e  0000000000000000  0000000000000000  000002e8  2**0                  CONTENTS, READONLY  6 .note.GNU-stack 00000000  0000000000000000  0000000000000000  00000336  2**0                  CONTENTS, READONLY  7 .eh_frame     00000058  0000000000000000  0000000000000000  00000338  2**3                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA

We have to customize the program link procedure so that all the.gcov_info linker input sections are placed in a contiguous memory blockwith a begin and end symbol. Firstly, get the default linker script using thefollowing commands (we assume a GNU linker):

$ ld --verbose | sed '1,/^===/d' | sed '/^===/d' > linkcmds

Secondly, open the filelinkcmds with a text editor and place the linkeroutput section definition from the overview after the.rodata sectiondefinition. Link the program executable using the customized linker script:

$ gcc --coverage main.o app.o -T linkcmds -Wl,-Map,app.map

In the linker map fileapp.map, we see that the linker placed theread-only pointer size objects of our objects filesmain.o andapp.o into a contiguous memory block and provided the symbols__gcov_info_start and__gcov_info_end:

$ grep -C 1 "\.gcov_info" app.map.gcov_info      0x0000000000403ac0       0x10                0x0000000000403ac0                PROVIDE (__gcov_info_start = .) *(.gcov_info) .gcov_info     0x0000000000403ac0        0x8 main.o .gcov_info     0x0000000000403ac8        0x8 app.o                0x0000000000403ad0                PROVIDE (__gcov_info_end = .)

Make sure no.gcda files are present. Run the program with nothing todecode and dumpstderr to the filegcda-0.txt (first run). Runthe program to decodegcda-0.txt and send it to thegcov-toolusing themerge-stream subcommand to create the.gcda files(second run). Rungcov to produce a report forapp.c. We seethat the first run with nothing to decode results in a partially coveredapplication:

$ rm -f app.gcda main.gcda$ echo "" | ./a.out 2>gcda-0.txt$ ./a.out <gcda-0.txt 2>gcda-1.txt | gcov-tool merge-stream$ gcov -bc app.cFile 'app.c'Lines executed:69.23% of 13Branches executed:66.67% of 6Taken at least once:50.00% of 6Calls executed:66.67% of 3Creating 'app.c.gcov'Lines executed:69.23% of 13

Run the program to decodegcda-1.txt and send it to thegcov-tool using themerge-stream subcommand to update the.gcda files. Rungcov to produce a report forapp.c.Since the second run decoded the gcov information of the first run, we have nowa fully covered application:

$ ./a.out <gcda-1.txt 2>gcda-2.txt | gcov-tool merge-stream$ gcov -bc app.cFile 'app.c'Lines executed:100.00% of 13Branches executed:100.00% of 6Taken at least once:100.00% of 6Calls executed:100.00% of 3Creating 'app.c.gcov'Lines executed:100.00% of 13

11.6.3 System Initialization Caveats

The gcov information of a translation unit consists of several global datastructures. For example, the instrumented code may update program flow graphedge counters in a zero-initialized data structure. It is safe to runinstrumented code before the zero-initialized data is cleared to zero. Thecoverage information obtained before the zero-initialized data is cleared tozero is unusable. Dumping the gcov information using__gcov_info_to_gcda() before the zero-initialized data is cleared tozero or the initialized data is loaded, is undefined behaviour. Clearing thezero-initialized data to zero through a function instrumented for profiling ortest coverage is undefined behaviour, since it may produce inconsistent programflow graph edge counters for example.


Previous:Data File Relocation to Support Cross-Profiling, Up:gcov—a Test Coverage Program   [Contents][Index]


[8]ページ先頭

©2009-2026 Movatter.jp