summaryrefslogtreecommitdiff
path: root/devdocs/gcc~13/freestanding-environments.html
blob: 9d423f270ea192ab5c129be9734bf64d9900ab9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<div class="section-level-extent" id="Freestanding-Environments"> <div class="nav-panel"> <p> Previous: <a href="cross-profiling" accesskey="p" rel="prev">Data File Relocation to Support Cross-Profiling</a>, Up: <a href="gcov" accesskey="u" rel="up"><code class="command">gcov</code>—a Test Coverage Program</a> [<a href="index#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="indices" title="Index" rel="index">Index</a>]</p> </div>  <h1 class="section" id="Profiling-and-Test-Coverage-in-Freestanding-Environments"><span>10.6 Profiling and Test Coverage in Freestanding Environments<a class="copiable-link" href="#Profiling-and-Test-Coverage-in-Freestanding-Environments"> ¶</a></span></h1> <p>In case your application runs in a hosted environment such as GNU/Linux, then this section is likely not relevant to you. This section is intended for application developers targeting freestanding environments (for example embedded systems) with limited resources. In particular, systems or test cases which do not support constructors/destructors or the C library file I/O. In this section, the <em class="dfn">target system</em> runs your application instrumented for profiling or test coverage. You develop and analyze your application on the <em class="dfn">host system</em>. We now provide an overview how profiling and test coverage can be obtained in this scenario followed by a tutorial which can be exercised on the host system. Finally, some system initialization caveats are listed. </p> <ul class="mini-toc"> <li><a href="#Overview" accesskey="1">Overview</a></li> <li><a href="#Tutorial" accesskey="2">Tutorial</a></li> <li><a href="#System-Initialization-Caveats" accesskey="3">System Initialization Caveats</a></li> </ul> <div class="subsection-level-extent" id="Overview"> <h2 class="subsection"><span>10.6.1 Overview<a class="copiable-link" href="#Overview"> ¶</a></span></h2> <p>For an application instrumented for profiling or test coverage, the compiler generates some global data structures which are updated by instrumentation code while the application runs. These data structures are called the <em class="dfn">gcov information</em>. Normally, when the application exits, the gcov information is stored to <samp class="file">.gcda</samp> files. There is one file per translation unit instrumented for profiling or test coverage. The function <code class="code">__gcov_exit()</code>, which stores the gcov information to a file, is called by a global destructor function for each translation unit instrumented for profiling or test coverage. It runs at process exit. In a global constructor function, the <code class="code">__gcov_init()</code> function is called to register the gcov information of a translation unit in a global list. In some situations, this procedure does not work. Firstly, if you want to profile the global constructor or exit processing of an operating system, the compiler generated functions may conflict with the test objectives. Secondly, you may want to test early parts of the system initialization or abnormal program behaviour which do not allow a global constructor or exit processing. Thirdly, you need a filesystem to store the files. </p> <p>The <samp class="option">-fprofile-info-section</samp> GCC option enables you to use profiling and test coverage in freestanding environments. This option disables the use of global constructors and destructors for the gcov information. Instead, a pointer to the gcov information is stored in a special linker input section for each translation unit which is compiled with this option. By default, the section name is <code class="code">.gcov_info</code>. The gcov information is statically initialized. The pointers to the gcov information from all translation units of 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 be used to achieve this: </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">.gcov_info      :
{
  PROVIDE (__gcov_info_start = .);
  KEEP (*(.gcov_info))
  PROVIDE (__gcov_info_end = .);
}</pre>
</div> <p>The linker will provide two global symbols, <code class="code">__gcov_info_start</code> and <code class="code">__gcov_info_end</code>, which define the start and end of the array of pointers to gcov information blocks, respectively. The <code class="code">KEEP ()</code> directive is required to prevent a garbage collection of the pointers. They are not directly referenced by anything in the executable. The section may be placed in a read-only memory area. </p> <p>In order to transfer the profiling and test coverage data from the target to the host system, the application has to provide a function to produce a reliable in order byte stream from the target to the host. The byte stream may be compressed and encoded using error detection and correction codes to meet application-specific requirements. The GCC provided <samp class="file">libgcov</samp> target library provides two functions, <code class="code">__gcov_info_to_gcda()</code> and <code class="code">__gcov_filename_to_gcfn()</code>, to generate a byte stream from a gcov information bock. The functions are declared in <code class="code">#include &lt;gcov.h&gt;</code>. The byte stream can be deserialized by the <code class="command">merge-stream</code> subcommand of the <code class="command">gcov-tool</code> to create or update <samp class="file">.gcda</samp> files in the host filesystem for the instrumented application. </p> </div> <div class="subsection-level-extent" id="Tutorial"> <h2 class="subsection"><span>10.6.2 Tutorial<a class="copiable-link" href="#Tutorial"> ¶</a></span></h2> <p>This tutorial should be exercised on the host system. We will build a program instrumented for test coverage. The program runs an application and dumps the gcov information to <samp class="file">stderr</samp> encoded as a printable character stream. The application simply decodes such character streams from <samp class="file">stdin</samp> and writes the decoded character stream to <samp class="file">stdout</samp> (warning: this is binary data). The decoded character stream is consumed by the <code class="command">merge-stream</code> subcommand of the <code class="command">gcov-tool</code> to create or update the <samp class="file">.gcda</samp> files. </p> <p>To get started, create an empty directory. Change into the new directory. Then you will create the following three files in this directory </p> <ol class="enumerate"> <li> <samp class="file">app.h</samp> - a header file included by <samp class="file">app.c</samp> and <samp class="file">main.c</samp>, </li>
<li> <samp class="file">app.c</samp> - a source file which contains an example application, and </li>
<li> <samp class="file">main.c</samp> - a source file which contains the program main function and code to dump the gcov information. </li>
</ol> <p>Firstly, create the header file <samp class="file">app.h</samp> with the following content: </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">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);</pre>
</div> <p>Secondly, create the source file <samp class="file">app.c</samp> with the following content: </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">#include "app.h"

#include &lt;stdio.h&gt;

/* 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) &lt; 16;
}

void
application (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;
    }
}</pre>
</div> <p>Thirdly, create the source file <samp class="file">main.c</samp> with the following content: </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">#include "app.h"

#include &lt;gcov.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

/* 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 void
dump (const void *d, unsigned n, void *arg)
{
  (void)arg;
  const unsigned char *c = d;
  unsigned char buf[2];

  for (unsigned i = 0; i &lt; 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 void
filename (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 void
dump_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.  */

int
main (void)
{
  application ();
  dump_gcov_info ();
  return 0;
}</pre>
</div> <p>If we compile <samp class="file">app.c</samp> with test coverage and no extra profiling options, then a global constructor (<code class="code">_sub_I_00100_0</code> here, it may have a different name in your environment) and destructor (<code class="code">_sub_D_00100_1</code>) is used to register and dump the gcov information, respectively. We also see undefined references to <code class="code">__gcov_init</code> and <code class="code">__gcov_exit</code>: </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">$ gcc --coverage -c app.c
$ nm app.o
0000000000000000 r a
0000000000000030 T application
0000000000000000 t can_decode
                 U fgetc
                 U fputc
0000000000000000 b __gcov0.application
0000000000000038 b __gcov0.can_decode
0000000000000000 d __gcov_.application
00000000000000c0 d __gcov_.can_decode
                 U __gcov_exit
                 U __gcov_init
                 U __gcov_merge_add
                 U stdin
                 U stdout
0000000000000161 t _sub_D_00100_1
0000000000000151 t _sub_I_00100_0</pre>
</div> <p>Compile <samp class="file">app.c</samp> and <samp class="file">main.c</samp> with test coverage and <samp class="option">-fprofile-info-section</samp>. Now, a read-only pointer size object is present in the <code class="code">.gcov_info</code> section and there are no undefined references to <code class="code">__gcov_init</code> and <code class="code">__gcov_exit</code>: </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">$ gcc --coverage -fprofile-info-section -c main.c
$ gcc --coverage -fprofile-info-section -c app.c
$ objdump -h app.o

app.o:     file format elf64-x86-64

Sections:
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</pre>
</div> <p>We have to customize the program link procedure so that all the <code class="code">.gcov_info</code> linker input sections are placed in a contiguous memory block with a begin and end symbol. Firstly, get the default linker script using the following commands (we assume a GNU linker): </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">$ ld --verbose | sed '1,/^===/d' | sed '/^===/d' &gt; linkcmds</pre>
</div> <p>Secondly, open the file <samp class="file">linkcmds</samp> with a text editor and place the linker output section definition from the overview after the <code class="code">.rodata</code> section definition. Link the program executable using the customized linker script: </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">$ gcc --coverage main.o app.o -T linkcmds -Wl,-Map,app.map</pre>
</div> <p>In the linker map file <samp class="file">app.map</samp>, we see that the linker placed the read-only pointer size objects of our objects files <samp class="file">main.o</samp> and <samp class="file">app.o</samp> into a contiguous memory block and provided the symbols <code class="code">__gcov_info_start</code> and <code class="code">__gcov_info_end</code>: </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">$ 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 = .)</pre>
</div> <p>Make sure no <samp class="file">.gcda</samp> files are present. Run the program with nothing to decode and dump <samp class="file">stderr</samp> to the file <samp class="file">gcda-0.txt</samp> (first run). Run the program to decode <samp class="file">gcda-0.txt</samp> and send it to the <code class="command">gcov-tool</code> using the <code class="command">merge-stream</code> subcommand to create the <samp class="file">.gcda</samp> files (second run). Run <code class="command">gcov</code> to produce a report for <samp class="file">app.c</samp>. We see that the first run with nothing to decode results in a partially covered application: </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">$ rm -f app.gcda main.gcda
$ echo "" | ./a.out 2&gt;gcda-0.txt
$ ./a.out &lt;gcda-0.txt 2&gt;gcda-1.txt | gcov-tool merge-stream
$ gcov -bc app.c
File 'app.c'
Lines executed:69.23% of 13
Branches executed:66.67% of 6
Taken at least once:50.00% of 6
Calls executed:66.67% of 3
Creating 'app.c.gcov'

Lines executed:69.23% of 13</pre>
</div> <p>Run the program to decode <samp class="file">gcda-1.txt</samp> and send it to the <code class="command">gcov-tool</code> using the <code class="command">merge-stream</code> subcommand to update the <samp class="file">.gcda</samp> files. Run <code class="command">gcov</code> to produce a report for <samp class="file">app.c</samp>. Since the second run decoded the gcov information of the first run, we have now a fully covered application: </p> <div class="example smallexample"> <pre class="example-preformatted" data-language="cpp">$ ./a.out &lt;gcda-1.txt 2&gt;gcda-2.txt | gcov-tool merge-stream
$ gcov -bc app.c
File 'app.c'
Lines executed:100.00% of 13
Branches executed:100.00% of 6
Taken at least once:100.00% of 6
Calls executed:100.00% of 3
Creating 'app.c.gcov'

Lines executed:100.00% of 13</pre>
</div> </div> <div class="subsection-level-extent" id="System-Initialization-Caveats"> <h2 class="subsection"><span>10.6.3 System Initialization Caveats<a class="copiable-link" href="#System-Initialization-Caveats"> ¶</a></span></h2> <p>The gcov information of a translation unit consists of several global data structures. For example, the instrumented code may update program flow graph edge counters in a zero-initialized data structure. It is safe to run instrumented code before the zero-initialized data is cleared to zero. The coverage information obtained before the zero-initialized data is cleared to zero is unusable. Dumping the gcov information using <code class="code">__gcov_info_to_gcda()</code> before the zero-initialized data is cleared to zero or the initialized data is loaded, is undefined behaviour. Clearing the zero-initialized data to zero through a function instrumented for profiling or test coverage is undefined behaviour, since it may produce inconsistent program flow graph edge counters for example. </p> </div> </div>  <div class="nav-panel"> <p> Previous: <a href="cross-profiling">Data File Relocation to Support Cross-Profiling</a>, Up: <a href="gcov"><code class="command">gcov</code>—a Test Coverage Program</a> [<a href="index#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="indices" title="Index" rel="index">Index</a>]</p> </div><div class="_attribution">
  <p class="_attribution-p">
    &copy; Free Software Foundation<br>Licensed under the GNU Free Documentation License, Version 1.3.<br>
    <a href="https://gcc.gnu.org/onlinedocs/gcc-13.1.0/gcc/Freestanding-Environments.html" class="_attribution-link">https://gcc.gnu.org/onlinedocs/gcc-13.1.0/gcc/Freestanding-Environments.html</a>
  </p>
</div>