aboutsummaryrefslogtreecommitdiff
path: root/gcc/doc
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2022-04-08 17:16:09 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2022-04-28 20:46:50 +0200
commit9ae8b993cd362e8aea4f65580aaf1453120207f2 (patch)
treeee4c80acfdcf7e25dd35ee176e8e6f8e394020ae /gcc/doc
parent92475ea84f69ad2bb92dbecd6b5c33be174c4777 (diff)
downloadgcc-9ae8b993cd362e8aea4f65580aaf1453120207f2.zip
gcc-9ae8b993cd362e8aea4f65580aaf1453120207f2.tar.gz
gcc-9ae8b993cd362e8aea4f65580aaf1453120207f2.tar.bz2
gcov: Add section for freestanding environments
gcc/ * doc/gcov.texi (Profiling and Test Coverage in Freestanding Environments): New section.
Diffstat (limited to 'gcc/doc')
-rw-r--r--gcc/doc/gcov.texi389
1 files changed, 389 insertions, 0 deletions
diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index fc39da0..a1f7d26 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -41,6 +41,8 @@ test code coverage in your programs.
* Gcov and Optimization:: Using gcov with GCC optimization.
* Gcov Data Files:: The files used by gcov.
* Cross-profiling:: Data file relocation.
+* Freestanding Environments:: How to use profiling and test
+ coverage in freestanding environments.
@end menu
@node Gcov Intro
@@ -971,3 +973,390 @@ setting will name the data file @file{/target/run/build/foo.gcda}.
You must move the data files to the expected directory tree in order to
use them for profile directed optimizations (@option{-fprofile-use}), or to
use the @command{gcov} tool.
+
+@node Freestanding Environments
+@section Profiling and Test Coverage in Freestanding Environments
+
+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 @dfn{target system} runs your application instrumented for
+profiling or test coverage. You develop and analyze your application on the
+@dfn{host system}. 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.
+
+@subsection Overview
+
+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 @dfn{gcov
+information}. Normally, when the application exits, the gcov information is
+stored to @file{.gcda} files. There is one file per translation unit
+instrumented for profiling or test coverage. The function
+@code{__gcov_exit()}, 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{__gcov_init()} 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.
+
+The @option{-fprofile-info-section} 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{.gcov_info}. 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:
+
+@smallexample
+ .gcov_info :
+ @{
+ PROVIDE (__gcov_info_start = .);
+ KEEP (*(.gcov_info))
+ PROVIDE (__gcov_info_end = .);
+ @}
+@end smallexample
+
+The linker will provide two global symbols, @code{__gcov_info_start} and
+@code{__gcov_info_end}, which define the start and end of the array of pointers
+to gcov information blocks, respectively. The @code{KEEP ()} 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.
+
+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 @file{libgcov} target
+library provides two functions, @code{__gcov_info_to_gcda()} and
+@code{__gcov_filename_to_gcfn()}, to generate a byte stream from a gcov
+information bock. The functions are declared in @code{#include <gcov.h>}. The
+byte stream can be deserialized by the @command{merge-stream} subcommand of the
+@command{gcov-tool} to create or update @file{.gcda} files in the host
+filesystem for the instrumented application.
+
+@subsection Tutorial
+
+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 @file{stderr} encoded as a printable character stream. The
+application simply decodes such character streams from @file{stdin} and writes
+the decoded character stream to @file{stdout} (warning: this is binary data).
+The decoded character stream is consumed by the @command{merge-stream}
+subcommand of the @command{gcov-tool} to create or update the @file{.gcda}
+files.
+
+To get started, create an empty directory. Change into the new directory.
+Then you will create the following three files in this directory
+
+@enumerate
+@item
+@file{app.h} - a header file included by @file{app.c} and @file{main.c},
+
+@item
+@file{app.c} - a source file which contains an example application, and
+
+@item
+@file{main.c} - a source file which contains the program main function and code
+to dump the gcov information.
+@end enumerate
+
+Firstly, create the header file @file{app.h} with the following content:
+
+@smallexample
+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);
+@end smallexample
+
+Secondly, create the source file @file{app.c} with the following content:
+
+@smallexample
+#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;
+@}
+
+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;
+ @}
+@}
+@end smallexample
+
+Thirdly, create the source file @file{main.c} with the following content:
+
+@smallexample
+#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 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 < 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;
+@}
+@end smallexample
+
+If we compile @file{app.c} with test coverage and no extra profiling options,
+then a global constructor (@code{_sub_I_00100_0} here, it may have a different
+name in your environment) and destructor (@code{_sub_D_00100_1}) is used to
+register and dump the gcov information, respectively. We also see undefined
+references to @code{__gcov_init} and @code{__gcov_exit}:
+
+@smallexample
+$ 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
+@end smallexample
+
+Compile @file{app.c} and @file{main.c} with test coverage and
+@option{-fprofile-info-section}. Now, a read-only pointer size object is
+present in the @code{.gcov_info} section and there are no undefined references
+to @code{__gcov_init} and @code{__gcov_exit}:
+
+@smallexample
+$ 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
+@end smallexample
+
+We have to customize the program link procedure so that all the
+@code{.gcov_info} 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):
+
+@smallexample
+$ ld --verbose | sed '1,/^===/d' | sed '/^===/d' > linkcmds
+@end smallexample
+
+Secondly, open the file @file{linkcmds} with a text editor and place the linker
+output section definition from the overview after the @code{.rodata} section
+definition. Link the program executable using the customized linker script:
+
+@smallexample
+$ gcc --coverage main.o app.o -T linkcmds -Wl,-Map,app.map
+@end smallexample
+
+In the linker map file @file{app.map}, we see that the linker placed the
+read-only pointer size objects of our objects files @file{main.o} and
+@file{app.o} into a contiguous memory block and provided the symbols
+@code{__gcov_info_start} and @code{__gcov_info_end}:
+
+@smallexample
+$ 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 = .)
+@end smallexample
+
+Make sure no @file{.gcda} files are present. Run the program with nothing to
+decode and dump @file{stderr} to the file @file{gcda-0.txt} (first run). Run
+the program to decode @file{gcda-0.txt} and send it to the @command{gcov-tool}
+using the @command{merge-stream} subcommand to create the @file{.gcda} files
+(second run). Run @command{gcov} to produce a report for @file{app.c}. We see
+that the first run with nothing to decode results in a partially covered
+application:
+
+@smallexample
+$ 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.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
+@end smallexample
+
+Run the program to decode @file{gcda-1.txt} and send it to the
+@command{gcov-tool} using the @command{merge-stream} subcommand to update the
+@file{.gcda} files. Run @command{gcov} to produce a report for @file{app.c}.
+Since the second run decoded the gcov information of the first run, we have now
+a fully covered application:
+
+@smallexample
+$ ./a.out <gcda-1.txt 2>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
+@end smallexample
+
+@subsection System Initialization Caveats
+
+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{__gcov_info_to_gcda()} 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.