GCOV(1)
๐ GCOV(1) ๐
๐ This page is from the GNU manual, so it's the official gcov tea ๐
โจ NAME โจ
gcov - coverage testing tool ๐ฆ It's a whole coverage testing tool, bestie.
๐ SYNOPSIS ๐
gcov [-v|--version] [-h|--help]
[-a|--all-blocks]
[-b|--branch-probabilities]
[-c|--branch-counts]
[-d|--display-progress]
[-f|--function-summaries]
[-i|--json-format]
[-j|--human-readable]
[-k|--use-colors]
[-l|--long-file-names]
[-m|--demangled-names]
[-n|--no-output]
[-o|--object-directory directory|file]
[-p|--preserve-paths]
[-q|--use-hotness-colors]
[-r|--relative-only]
[-s|--source-prefix directory]
[-t|--stdout]
[-u|--unconditional-branches]
[-x|--hash-filenames]
files
๐ฆ DESCRIPTION ๐ฆ
Okay so gcov is a test coverage program ๐ง You use it with GCC to analyze your programs so they run more efficiently and faster, and to find the parts of your program nobody has tested yet. It's also a profiling tool, so you can figure out where your optimization efforts will actually slay. You can pair it with the other profiling tool, gprof, to see which parts of your code eat up the most computing time. โจ
Profiling tools help you analyze your code's performance ๐ With a profiler like gcov or gprof, you can find out basic performance stats, like:
- how often each line of code runs
- which lines of code actually get executed
- how much computing time each section of code uses
Once you know these things about how your code works when compiled, you can look at each module and see which ones need optimizing. gcov helps you figure out where to put your optimization energy ๐
Software devs also use coverage testing alongside testsuites, to make sure software is actually good enough to release ๐ Testsuites check that a program works as expected. A coverage program checks how much of the program the testsuite actually exercises. Devs can then see what kinds of test cases to add to the testsuites, for better testing and a better final product ๐ซถ
Compile without optimization if you plan to use gcov ๐ Optimization can combine some lines of code into one function, which may not give you enough info to find the hot spots' where your code is using a ton of computer time. Also, gcov` collects stats by line (at the lowest resolution), so it works best with a style that puts only one statement on each line. If you use complicated macros that expand to loops or other control structures, the stats are less helpful, because they only report on the line where the macro call appears. If your complex macros act like functions, swap them for inline functions to fix this ๐
gcov creates a logfile called sourcefile.gcov that shows how many times each line of a source file sourcefile.c has executed ๐ You can use these logfiles with gprof to fine-tune your program's performance. gprof gives timing info you can use alongside what you get from gcov.
gcov only works on code compiled with GCC.
โ ๏ธ It is not compatible with any other profiling or test coverage mechanism, so don't try to mix it with those.
๐ OPTIONS ๐
-a,--all-blocksโ Writes individual execution counts for every basic block ๐งฉ Normallygcovonly outputs counts for the main blocks of a line. With this option, you can tell if blocks inside a single line aren't being executed.-b,--branch-probabilitiesโ Writes branch frequencies to the output file and branch summary info to standard output ๐ฟ This lets you see how often each branch in your program was taken. Unconditional branches won't show up unless you pass-u.-c,--branch-countsโ Writes branch frequencies as the number of branches taken, instead of the percentage of branches taken ๐ข-d,--display-progressโ Shows the progress on standard output โณ-f,--function-summariesโ Outputs summaries for each function, on top of the file-level summary ๐-h,--helpโ Shows help about usinggcov(on standard output) and exits without doing any further processing ๐โโ๏ธ-i,--json-formatโ Outputs the gcov file in an easy-to-parse JSON intermediate format ๐งพ It doesn't need source code to generate. The JSON file is compressed with the gzip algorithm and has the.gcov.json.gzextension.
Here's the structure of the JSON, bestie ๐
{
"current_working_directory": <current_working_directory>,
"data_file": <data_file>,
"format_version": <format_version>,
"gcc_version": <gcc_version>
"files": [<file>]
}
The root element fields mean:
current_working_directoryโ the working directory where a compilation unit was compileddata_fileโ name of the data file (GCDA)format_versionโ semantic version of the formatgcc_versionโ version of the GCC compiler
๐ธ File element
Each file has this form:
{
"file": <file_name>,
"functions": [<function>],
"lines": [<line>]
}
The file element fields mean:
file_nameโ name of the source file
๐งฉ Function element
Each function has this form:
{
"blocks": <blocks>,
"blocks_executed": <blocks_executed>,
"demangled_name": "<demangled_name>,
"end_column": <end_column>,
"end_line": <end_line>,
"execution_count": <execution_count>,
"name": <name>,
"start_column": <start_column>
"start_line": <start_line>
}
The function element fields mean:
blocksโ number of blocks that are in the functionblocks_executedโ number of executed blocks of the functiondemangled_nameโ demangled name of the functionend_columnโ column in the source file where the function endsend_lineโ line in the source file where the function endsexecution_countโ number of executions of the functionnameโ name of the functionstart_columnโ column in the source file where the function beginsstart_lineโ line in the source file where the function begins
๐ Line numbers and column numbers start counting from 1. In the current implementation,start_lineandstart_columndo not include any template parameters or the leading return type, but that's likely to be fixed in the future.
๐งต Line element
Each line has this form:
{
"branches": [<branch>],
"count": <count>,
"line_number": <line_number>,
"unexecuted_block": <unexecuted_block>
"function_name": <function_name>,
}
Branches only show up with the -b option. The line element fields mean:
countโ number of executions of the lineline_numberโ line numberunexecuted_blockโ flag for whether the line contains an unexecuted block (not all statements on the line are executed)function_nameโ name of a function this line belongs to (for a line with inlined statements, it can be unset)
๐ฟ Branch element
Each branch has this form:
{
"count": <count>,
"fallthrough": <fallthrough>,
"throw": <throw>
}
The branch element fields mean:
countโ number of executions of the branchfallthroughโtruewhen the branch is a fall through branchthrowโtruewhen the branch is an exceptional branch-j,--human-readableโ Writes counts in a human-readable format, like24.6k๐-k,--use-colorsโ Colors lines of code that have zero coverage ๐ We use red for non-exceptional lines and cyan for exceptional ones. The same colors are used for basic blocks with-a.-l,--long-file-namesโ Creates long file names for included source files ๐งท For example, if the headerx.hhas code and was included ina.c, runninggcovona.cmakes a file calleda.c##x.h.gcovinstead ofx.h.gcov. This is great whenx.his included in multiple source files and you want to see each one's contribution. If you use-p, both the including and included file names will be complete path names.-m,--demangled-namesโ Displays demangled function names in the output. The default is to show mangled function names ๐ฎ-n,--no-outputโ Doesn't create thegcovoutput file ๐ โโ๏ธ-o directory|file,--object-directory directory,--object-file fileโ Tellsgcoveither the directory containing the gcov data files, or the object path name ๐ The.gcnoand.gcdadata files are searched for using this option. If you give a directory, the data files live in that directory and are named after the input file name, without its extension. If you give a file, the data files are named after that file, without its extension.-p,--preserve-pathsโ Keeps complete path info in the names of generated.gcovfiles ๐บ๏ธ Without this option, only the filename component is used. With it, all directories are used:/characters get translated to#characters,.directory components are removed, and unremoveable..components are renamed to^. This is super useful if your source files live in several different directories.-q,--use-hotness-colorsโ Emits perf-like colored output for hot lines ๐ฅ A legend for the color scale gets printed at the very beginning of the output file.-r,--relative-onlyโ Only outputs info about source files with a relative pathname (after source prefix elision) ๐ท Absolute paths are usually system header files, and coverage of any inline functions in those is normally uninteresting.-s directory,--source-prefix directoryโ okay so this one lets you chop a prefix off the source file names when gcov makes the output coverage files ๐ . It's super useful when you build in a separate directory and don't want that path showing up in your output file names ๐. Heads up: gcov checks this prefix before it decides whether the source file is absolute โจ.-t,--stdoutโ skip the output files entirely and just send everything to standard output ๐ฆ.-u,--unconditional-branchesโ when branch probabilities are showing, this also includes the ones for unconditional branches. Those are normally not that interesting tbh, so they're not usually the main event ๐ฅฑ.-v,--versionโ prints the gcov version number to standard output and then bounces right away, with no further processing ๐ซถ.-w,--verboseโ prints verbose info about basic blocks and arcs ๐ง .-x,--hash-filenamesโ this one's for when your file paths get way too long ๐ญ. With--preserve-paths, gcov uses the full pathname of the source files to make an output filename, and those can get long enough to overflow filesystem limits. This option makes names likesource-file##md5.gcovinstead, wheresource-fileis the final part of the filename and themd5bit is calculated from the full mangled name that would've been used otherwise. It's the alternative to--preserve-pathson systems that have a filesystem limit ๐.
Okay so gcov needs to be run from the same current directory you were in when you invoked the compiler, or it won't be able to find your source files ๐บ๏ธ. It drops files called mangledname.gcov into the current directory, and each one holds the coverage info for the source file it matches. You get one .gcov file for every source (or header) file that had code compiled into your data files. The mangledname part is usually just the source file name, but it can get more complicated if you use the -l or -p options, so go check those out for the deets ๐.
If you invoke gcov with multiple input files, the contributions from each input file get summed up together ๐งฎ. Usually you'd pass it the same list of files you used for the final link of your executable.
The .gcov files have :-separated fields sitting right next to your program's source code. The format goes like this:
<execution_count>:<line_number>:<source line text>
Extra block info can follow each line if you ask for it with a command line option ๐. The execution_count is - for lines that have no code at all. Unexecuted lines get marked with ##### or =====, and which one you get depends on whether they can be reached by non-exceptional paths (#####) or only exceptional paths like C++ exception handlers (=====) ๐. If you use the -a option, unexecuted blocks get marked with $$$$$ or %%%%% instead, depending on whether a basic block is reachable via non-exceptional paths ($$$$$) or exceptional paths (%%%%%) ๐ซ . Executed basic blocks that have a statement with a zero execution_count end with a * character, and they're colored magenta when you use the -k option ๐ธ. Just a heads up, this functionality isn't supported in Ada.
Note that GCC can totally delete the bodies of functions it doesn't need, like when they've been inlined everywhere ๐๏ธ. Those functions get marked with -, which can be super confusing. If you want to keep them around so gcov can show their execution_count, use the -fkeep-inline-functions and -fkeep-static-functions options ๐ง.
Some lines at the start have a line_number of zero. These preamble lines look like this:
-:0:<tag>:<value>
The ordering and number of these preamble lines will be augmented as gcov development progresses, so do NOT rely on them staying the same. Use the tag to find a particular preamble line instead ๐จ.
The additional block info looks like this:
<tag> <information>
The information is human readable, but it's designed to be simple enough for machines to parse too ๐ค.
When printing percentages, 0% and 100% only show up when the values are exactly 0% and 100%. Anything that would normally get rounded to those gets printed as the nearest non-boundary value instead ๐
.
When you're using gcov, you have to compile your program first with a special GCC option, --coverage. This tells the compiler to generate the extra info gcov needs (basically a flow graph of the program) and to add extra code to the object files that collects the extra profiling info gcov wants ๐. Those extra files get placed in the directory where the object file lives ๐.
Running the program makes profile output get generated. For each source file compiled with -fprofile-arcs, a matching .gcda file gets placed in the object file directory ๐.
Now if you run gcov with your program's source file names as arguments, you'll get a listing of the code along with how often each line ran โจ. For example, if your program is called tmp.cpp, this is what you'd see when you use the basic gcov facility:
$ g++ --coverage tmp.cpp
$ a.out
$ gcov tmp.cpp -m
File 'tmp.cpp'
Lines executed:92.86% of 14
Creating 'tmp.cpp.gcov'
The file tmp.cpp.gcov has the gcov output in it. Here's a sample ๐:
-: 0:Source:tmp.cpp
-: 0:Working directory:/home/gcc/testcase
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:template<class T>
-: 4:class Foo
-: 5:{
-: 6: public:
1*: 7: Foo(): b (1000) {}
------------------
Foo<char>::Foo():
#####: 7: Foo(): b (1000) {}
------------------
Foo<int>::Foo():
1: 7: Foo(): b (1000) {}
------------------
2*: 8: void inc () { b++; }
------------------
Foo<char>::inc():
#####: 8: void inc () { b++; }
------------------
Foo<int>::inc():
2: 8: void inc () { b++; }
------------------
-: 9:
-: 10: private:
-: 11: int b;
-: 12:};
-: 13:
-: 14:template class Foo<int>;
-: 15:template class Foo<char>;
-: 16:
-: 17:int
1: 18:main (void)
-: 19:{
-: 20: int i, total;
1: 21: Foo<int> counter;
-: 22:
1: 23: counter.inc();
1: 24: counter.inc();
1: 25: total = 0;
-: 26:
11: 27: for (i = 0; i < 10; i++)
10: 28: total += i;
-: 29:
1*: 30: int v = total > 100 ? 1 : 2;
-: 31:
1: 32: if (total != 45)
#####: 33: printf ("Failure\n");
-: 34: else
1: 35: printf ("Success\n");
1: 36: return 0;
-: 37:}
Note that line 7 shows up in the report multiple times ๐. The first occurrence shows the total number of executions of the line, and the next two belong to the instances of the Foo class constructors. You can also see that line 30 has some unexecuted basic blocks, which is why its execution count has an asterisk ๐ฅ.
When you use the -a option, you get individual block counts, and the output looks like this:
-: 0:Source:tmp.cpp
-: 0:Working directory:/home/gcc/testcase
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:template<class T>
-: 4:class Foo
-: 5:{
-: 6: public:
1*: 7: Foo(): b (1000) {}
------------------
Foo<char>::Foo():
#####: 7: Foo(): b (1000) {}
------------------
Foo<int>::Foo():
1: 7: Foo(): b (1000) {}
------------------
2*: 8: void inc () { b++; }
------------------
Foo<char>::inc():
#####: 8: void inc () { b++; }
------------------
Foo<int>::inc():
2: 8: void inc () { b++; }
------------------
-: 9:
-: 10: private:
-: 11: int b;
-: 12:};
-: 13:
-: 14:template class Foo<int>;
-: 15:template class Foo<char>;
-: 16:
-: 17:int
1: 18:main (void)
-: 19:{
-: 20: int i, total;
1: 21: Foo<int> counter;
1: 21-block 0
-: 22:
1: 23: counter.inc();
1: 23-block 0
1: 24: counter.inc();
1: 24-block 0
1: 25: total = 0;
-: 26:
11: 27: for (i = 0; i < 10; i++)
1: 27-block 0
11: 27-block 1
10: 28: total += i;
10: 28-block 0
-: 29:
1*: 30: int v = total > 100 ? 1 : 2;
1: 30-block 0
%%%%%: 30-block 1
1: 30-block 2
-: 31:
1: 32: if (total != 45)
1: 32-block 0
#####: 33: printf ("Failure\n");
%%%%%: 33-block 0
-: 34: else
1: 35: printf ("Success\n");
1: 35-block 0
1: 36: return 0;
1: 36-block 0
-: 37:}
Okay bestie, so in this mode ๐ฆ every basic block gets squished onto just one line: the last line of the block. A multi-line block only adds to the execution count of that last line, and the other lines won't count as containing code unless an earlier block ends on them. The total count for a line is shown, and the lines after it show the counts for each individual block that ends there. And if you pass the -b option, you'll also get each block's branch and call counts after it ๐ง
Because of how GCC instruments calls, you can sometimes see a call count after a line with no individual blocks at all. Look at line 33 below. It has a block that never ran ๐
When you use -b, your output looks like this ๐
-: 0:Source:tmp.cpp
-: 0:Working directory:/home/gcc/testcase
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:template<class T>
-: 4:class Foo
-: 5:{
-: 6: public:
1*: 7: Foo(): b (1000) {}
------------------
Foo<char>::Foo():
function Foo<char>::Foo() called 0 returned 0% blocks executed 0%
#####: 7: Foo(): b (1000) {}
------------------
Foo<int>::Foo():
function Foo<int>::Foo() called 1 returned 100% blocks executed 100%
1: 7: Foo(): b (1000) {}
------------------
2*: 8: void inc () { b++; }
------------------
Foo<char>::inc():
function Foo<char>::inc() called 0 returned 0% blocks executed 0%
#####: 8: void inc () { b++; }
------------------
Foo<int>::inc():
function Foo<int>::inc() called 2 returned 100% blocks executed 100%
2: 8: void inc () { b++; }
------------------
-: 9:
-: 10: private:
-: 11: int b;
-: 12:};
-: 13:
-: 14:template class Foo<int>;
-: 15:template class Foo<char>;
-: 16:
-: 17:int
function main called 1 returned 100% blocks executed 81%
1: 18:main (void)
-: 19:{
-: 20: int i, total;
1: 21: Foo<int> counter;
call 0 returned 100%
branch 1 taken 100% (fallthrough)
branch 2 taken 0% (throw)
-: 22:
1: 23: counter.inc();
call 0 returned 100%
branch 1 taken 100% (fallthrough)
branch 2 taken 0% (throw)
1: 24: counter.inc();
call 0 returned 100%
branch 1 taken 100% (fallthrough)
branch 2 taken 0% (throw)
1: 25: total = 0;
-: 26:
11: 27: for (i = 0; i < 10; i++)
branch 0 taken 91% (fallthrough)
branch 1 taken 9%
10: 28: total += i;
-: 29:
1*: 30: int v = total > 100 ? 1 : 2;
branch 0 taken 0% (fallthrough)
branch 1 taken 100%
-: 31:
1: 32: if (total != 45)
branch 0 taken 0% (fallthrough)
branch 1 taken 100%
#####: 33: printf ("Failure\n");
call 0 never executed
branch 1 never executed
branch 2 never executed
-: 34: else
1: 35: printf ("Success\n");
call 0 returned 100%
branch 1 taken 100% (fallthrough)
branch 2 taken 0% (throw)
1: 36: return 0;
-: 37:}
For each function, gcov prints a line showing how many times the function was called, how many times it returned, and what percentage of the function's blocks got executed. Easy peasy ๐
For each basic block, a line is printed after the last line of that block, describing the branch or call that ends the block. A single source line can have several branches and calls listed if several basic blocks end on it. In that case, each one gets a number. There's no simple way to map those numbers back to source constructs, but in general, the lowest numbered branch or call lines up with the leftmost construct on the source line ๐งฉ
For a branch, if it ran at least once, you get a percentage: the number of times the branch was taken divided by the number of times it was executed. Otherwise, you get the message never executed ๐
For a call, if it ran at least once, you get a percentage: the number of times the call returned divided by the number of times it was executed. This is usually 100%, but it can be lower for functions that call exit or longjmp, since those may not return every time they get called ๐
The execution counts are cumulative, which is lowkey iconic. If the example program runs again without you deleting the .gcda file, the counts for each source line get added to the results from the previous run(s). This can be super useful for collecting data across a whole test verification suite, or for building more accurate long-term info over a huge number of program runs ๐
The data in the .gcda files is saved right before the program exits. For each source file compiled with -fprofile-arcs, the profiling code first tries to read in an existing .gcda file. If that file doesn't match the executable (different number of basic block counts), it ignores the contents. Then it adds in the new execution counts and finally writes everything back to the file ๐พ
โจ Using gcov with GCC Optimization โจ
If you plan to use gcov to help optimize your code, you must first compile your program with a special GCC option, --coverage. Other than that, you can use any other GCC options! But if you want to prove that every single line in your program was executed, don't compile with optimization at the same time. Some machines can squish simple code lines together by combining them with other lines. For example, code like this:
if (a != b)
c = 1;
else
c = 0;
can be compiled into one instruction on some machines. In that case, there's no way for gcov to work out separate execution counts for each line, because there isn't separate code for each line. So if you compiled with optimization, the gcov output looks like this:
100: 12:if (a != b)
100: 13: c = 1;
100: 14:else
100: 15: c = 0;
That output says this block of code, combined by optimization, ran 100 times. In one sense that's correct, because there was only one instruction representing all four of those lines. But it doesn't tell you how many times the result was 0 and how many times it was 1 ๐ค
Inlineable functions can create unexpected line counts. Line counts are shown for the source code of the inlineable function, but what you see depends on where the function gets inlined, or whether it isn't inlined at all ๐
If the function isn't inlined, the compiler has to emit an out-of-line copy of it in every object file that needs it. If fileA.o and fileB.o both contain out-of-line bodies of a particular inlineable function, they'll both contain coverage counts for that function too. When fileA.o and fileB.o get linked together, the linker will, on many systems, pick one of those out-of-line bodies for all calls to that function and remove or ignore the other. Unfortunately, it won't remove the coverage counters for the unused function body. So when instrumented, all but one use of that function will show zero counts ๐ญ
If the function is inlined in several places, the block structure at each location might not be the same. For instance, a condition might be calculable at compile time in some instances. Because the coverage of all the uses of the inline function gets shown for the same source lines, the line counts themselves might look inconsistent ๐คทโโ๏ธ
Long-running applications can use the __gcov_reset and __gcov_dump facilities to limit profile collection to the part of the program you care about. Calling __gcov_reset(void) clears all profile counters to zero, and calling __gcov_dump(void) dumps the profile information collected up to that point into .gcda output files. Instrumented applications use a static destructor with priority 99 to invoke __gcov_dump. That means __gcov_dump runs after all user defined static destructors, as well as handlers registered with atexit ๐ธ
If an executable loads a dynamic shared object through dlopen, you need -Wl,--dynamic-list-data to dump all of the profile data. Don't skip this one, bestie!
The profiling run-time library reports various errors related to profile manipulation and profile saving. These get printed to standard error output, or to the file named by GCOV_ERROR_FILE if that environment variable is used. If you want to bail out immediately after an error happens, set the GCOV_EXIT_AT_ERROR environment variable. That can help you track down profile clashes, which lead to a misleading profile ๐
โจ SEE ALSO โจ
Go check out gpl(7), gfdl(7), fsf-funding(7), gcc(1), and the Info entry for gcc ๐
โจ COPYRIGHT โจ
Copyright (c) 1996-2020 Free Software Foundation, Inc.
You're allowed to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation. The Invariant Sections are "GNU General Public License" and "Funding Free Software", the Front-Cover texts are (a) (see below), and the Back-Cover Texts are (b) (see below). A copy of the license is included in the gfdl(7) man page ๐ฆ
(a) The FSF's Front-Cover Text is:
A GNU Manual
(b) The FSF's Back-Cover Text is:
You have freedom to copy and modify this GNU Manual, like GNU software. Copies published by the Free Software Foundation raise funds for GNU development.
And that's the whole page, bestie! ๐ Source and version: gcc-10.5.0, dated 2023-07-07, GCOV(1) ๐ Love you, mwah ๐๐
๐ฆ where this page comes from
- ubuntu noble: gcc-10-mipsel-linux-gnu 10.5.0-4ubuntu2cross1
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms