Gcov and GPRO

Source: Internet
Author: User
Tags gcov gtk
Zach Frey translation Roland

Optimizing the correct code is easier than debugging the optimized code.
-- Yves Deville

Maybe you don't know what the purpose of gcov in the GCC tool set is, maybe you need to adjust the new project at hand, or your users require that the software you release have a certain degree of test coverage, you don't know how to do this. This document introduces the basic concepts of Coverage measurement and performance measurement in conjunction with GNU tools (gcov and GPROF.
Coverage measurement is a record of the Code Execution path. Coverage can be divided into various granularities. The largest coverage is the coverage of the function, which measures the called functions, the statement overwrites, the row of the measured statements executed, and the branch overwrites, the logical conditions in the measurement branch statement have been met. Coverage measurement usually refers to statement coverage and branch coverage. gcov is a standard GNU Coverage measurement tool that requires GCC to run.
Even if you have the latest and fastest CPU and memory, snail ‑running software can still offset the benefits of Moore's Law and cheap hardware. Unfortunately, the software will consume all the resources, not to mention those systems with limited resources, such as PDAs and embedded systems. Even new hardware cannot solve their performance problems.
The performance measurement test allows you to measure which code consumes most of the running time. Just like opening a window, it allows you to observe the actual behavior of the program running, and let you know that part is a hot spots that can be optimized ). Generally, a simple code review or take it for granted that the Code should run in this way and cannot discover these hot spots, so performance measurement tools (Profiler) are essential.
Performance Measurements cover a superset of measurements, but they have different purposes. Overwrite measurements let you know that the Code is not running, while performance measurements let you know that the Code consumes most of the time. The performance measurement tool can measure the number of times a function is called, and the number of times a row of code or logic Branch runs. The call graph of the called function and the time consumed by each part of the program. GPROF is a standard GNU performance measurement tool that also requires GCC.
Coverage measurement: Why is it so troublesome?
Although coverage measurement is required in some industries, such as the aviation industry, it seems that this technology is rarely used outside of these industries. This is because coverage measurement is a little less direct than other debugging technologies such as memory leak detection and rewrite detection. Coverage testing is just a test, and it cannot automatically locate bugs or improve code quality.
The coverage test shows the degree of perfection of your test. If you do not perform a test or do not perform a systematic test, do not talk about the measurement coverage. Furthermore, if you do not have any testing standards, the automated testing suite (such as dejarnu) is simply thankless and error-prone to collect Coverage measurement data, and it is difficult for you to effectively explain your measurement data.
Even if it looks like a Comprehensive Test Set is actually not perfect, in our first project using Coverage measurement, after running the proud standard regression test set, the row-code coverage rate is only 63%. If you have never done this kind of thing, you may think: "Only 63%, your test does not seem very good ", in fact, we are very happy to reach this high rate. Our system contains a large number of error handling programs that handle system faults. Some of these errors cannot be triggered by our test set, such as insufficient memory, file descriptor used up. I also know that the average coverage of the newly developed test set in the industry is close to 50%, so we are glad that we can do so well.
Assuming that the number is reduced to 50%, the average full test set only executes 50% of the Code to be tested. If you do not perform coverage tests, you do not know how you are doing-there is no standard to tell you how to do better, it is difficult to optimize the code that has not been measured.
Knowing how much your code has been executed through the test set is just the beginning, not the end. Once you understand this, you need to carefully observe the coverage report, find the code that has not been executed, and prepare to add new tests.
Use gcov for coverage test
To use gcov, you must use GCC as the compiler. In addition, you need to add the compilation option-fprofile-arcs-ftest-coverage during compilation, which will enable GCC to insert some code when creating the target file, the code will record the information required by gcov and save it to the hard disk.
To use gcov, you must use GCC as the compiler. In addition, you need to add the compilation option-fprofile-arcs-ftest-coverage during compilation, which will enable GCC to insert some code when creating the target file, the code will record the information required by gcov and save it to the hard disk.
According to the GCC manual, the target file contains the absolute path of the. Da file. Therefore, after creating (build), you cannot move the source file and then collect coverage test data. In this way, the error message can't open output file filename is generated during data storage. Gcov only displays the results of one file at a time and does not provide summary information.
It is easy to create a gcov report. I use glib (the basic library of GTK + and gnome, www.gtk.org) as an example. First, download glib from the GTK + official website or its image. I use glib-2.2.1, which is the latest version when I write an article.
To measure the coverage, set the environment variable cflags to-fprofile-arcs-ftest-coverage to run./configure; Make; make check. All software released following the GNU recommendation can run like this.
After make check is complete, you can use gcov to create a coverage report. The most basic report we get is the statement coverage report.
[Zfrey @ warthog Glib] $ gcov garray. c
68.37% of 215 source lines executed in file garray. c
Creating garray. C. gcov.
In addition to the output statistical summary, gcov also creates a list of annotated source files for each executable statement. This list shows the number of times it is executed or identifies that it is not running at all.
Table 1 Annotated gcov file fragments

Code:
Listing 1. excerpt of a gcov annotated source listing

Garray * g_array_sized_new
(Gboolean zero_terminated,
Gboolean clear,
Guint elt_size,
Guint reserved_size)
40 {
40 grealarray * array;

40 g_lock (array_mem_chunk );
40 if (! Array_mem_chunk)
14 array_mem_chunk = g_mem_chunk_new
("Array mem chunk ",
Sizeof (grealarray ),
1024, g_alloc_and_free );

40 array = g_chunk_new (grealarray,
Array_mem_chunk );


If you want to view the branch coverage, use the-B option.

Code:
[Zfrey @ warthog Glib] $ gcov-B garray. c
68.37% of 215 source lines executed in file garray. c
59.02% of 122 branches executed in file garray. c
46.72% of 122 branches taken at least once in file garray. c
45.35% of 86 CILS executed in file garray. c
Creating garray. C. gcov.


The commented source file now contains the branch data.
Table 2. Comment the file segment of the branch data

Code:
Garray * g_array_sized_new (
Gboolean zero_terminated,
Gboolean clear,
Guint elt_size,
Guint reserved_size)
40 {
40 grealarray * array;

40 g_lock (array_mem_chunk );
Branch 0 taken = 40%
Branch 1 taken = 58%
Call 2 returns = 100%
Branch 3 taken = 100%
Call 4 returns = 100%
40 if (! Array_mem_chunk)
Branch 0 taken = 65%
14 array_mem_chunk = g_mem_chunk_new (
"Array mem chunk ",
Call 0 returns = 100%
Sizeof (grealarray ),
1024, g_alloc_and_free );
1024, g_alloc_and_free );

40 array = g_chunk_new (grealarray,
Array_mem_chunk );
Call 0 returns = 100%


To view the function-level coverage of the source file, use the-F option, which can be used with the-B Option to obtain the branch coverage in the unit of function. Unfortunately, gcov does not provide the ability to summarize multiple file reports.
Ggcov: GTK + Gui
Ggcov is a gcov graphics front-end based on GTK +. The author is Greg banks (www.alphalink.com. au /~ GNB/ggcov) I downloaded the latest version (0.1.1), and there is no problem with compilation and installation. Use./configure; Make; make install for installation. The prefix is/usr/local.
Go to the source file directory and run ggcov. ggcov searches for coverage measurement data under the Directory and creates a summary (1). GNU gcov does not have this capability.

Figure 1. ggcov Summary Report
Select the file button to activate the drop-down list. The drop-down list contains all the files in the directory that contain the coverage information. Select a file to obtain the statistics for the file (figure 2 ).


Figure 2. ggcov file report
Select the function button to activate the drop-down list, which contains the function names of all files containing the Coverage Information in this directory. Select a function to obtain the statistics for the function (Figure 3 ).

Figure 3. ggcov function report
If you still feel unsatisfied, try the range button, which allows you to select the code for a specific range of rows in a file, and create a summary for the function in this range only. In addition to all summary, all summaries have a view button. If you click it, a new window is generated, showing the comments of the Code in the selected file.
All in all, I found that ggcov provides simple and effective functions. Ggcov can also display the call diagram. However, on Red Hat 8.0, glib will cause ggcov segfault. I contacted Greg banks and said that the calling graph function is not complete yet. He hopes to solve these problems in future versions.
Use GPROF for performance measurement
Like gcov, GPROF also needs to use specific options during program compilation so that GCC can insert appropriate commands in your code. To use GPROF, you must use the-PG option. By default, this will analyze each function executed. If you want to analyze each line, you must use the-G option at the same time. GCC inserts debugging information so that GPROF can generate row reports.
When the program runs, the function call and time consumption are stored in the memory. When the program exits, the information is written to the gmon. Out file. GPROF uses this file to generate performance measurement reports. GPROF only saves data using one file. Unlike Coverage measurement, GPROF saves data from different source files in different files. Gmon. out is not necessarily stored in the same directory of the source file, but in the current directory when the program exits. Therefore, pay attention to the chdir () call in the program.
By default, the measurement data accumulates the number of executions of each function and the function it calls. A sampling process collects data during running hours. This program checks the data every sampling period, record the currently executed functions. GPROF explains how long this sample has taken to report each piece of code.
Because this is a statistical sampling, all statistical errors. For more information about error calculation, see the GPROF manual. In short, the running time of the GPROF report may not be very accurate. In addition, if a function has a very short execution time, it may miss the sampling and display the execution time as 0.0. This is not a problem in performance analysis. If the running time of a function or code segment is insufficient for sampling, we may not need to spend time optimizing it.
However, if you want to use GPROF to display the statement and branch coverage, the statistical error becomes a problem.
To accurately count how many times each line of code actually runs, you must use the-A option during compilation to count the basic block. This will add instructions for calculating the execution of each code segment in the target file, which also enables GPROF to precisely display how many times each code line has been executed, even if the sampling result is 0 times.
Using the-s option of GPROF, you can combine multiple gmon. out files to create analysis reports that run multiple times. It can use any number of gmon. out files to create a new gmon. Sum comprehensive data file. If gmon. sum is used as the input file, you can add Performance Analysis Data incrementally.
I originally hoped to use glib as an example of GPROF, but the behavior of gcov and GPROF is very different, so this idea is very difficult to implement. Each time you run make check, the glib test program runs once, so the gmon. out is overwritten by the next test. In addition, the test program is actually a shell script. To run the program, it sets quite complex environment variables, which are stored in a hidden directory. The lesson learned from this is that gcov is a suitable tool for glib coverage testing.
So I created a simple program to calculate the number of Fibonacci from zero to 42. The initial value of the Fibonacci sequence is 0, 1, and the following number is the sum of the first two numbers (0, 1, 2, 3, 5, 8, 13, 21 ,...), A good introduction to the number of Fibonacci and related mathematical concepts is provided at www. MCS. surrey. AC. UK/personal/R. knott/Fibonacci/fib.html: Select 42, not because I am a fan of Douglas Adams, but because the 32 with the symbol of 47 is an integer, therefore, a smaller number is selected.
The computation of the Fibonacci series is a classic example of recursive functions, because the number of Fibonacci is the sum of the first two. The simplest method is to create a function int Fibonacci (int n ), it calls its own N-1 and N-2 and returns the result of summation (table 3 ).
Table 3. fib. c

Code:
# Include <stdio. h>

Int maid (int n );

Int main (INT argc, char ** argv)
{
Int fib;
Int N;

For (n = 0; n <= 42; n ++ ){
FIB = maid (N );
Printf ("Maid (% d) = % d \ n", N, FIB );
}

Return 0;
}

Int Fibonacci (int n)
{
Int fib;
If (n <= 0 ){
FIB = 0;
}
Else if (n = 1 ){
FIB = 1;
}
Else {
FIB = maid (n-1) + maid (n-2 );
}

Return fib;
}


Below is the output of the Fibonacci program

Code:
[Zfrey @ warthog Prof] $ time./fib
Maid (0) = 0
Maid (1) = 1
Maid (2) = 1
Maid (3) = 2
Maid (4) = 3
...
Maid (40) = 102334155
Maid (41) = 165580141
Maid (42) = 267914296

Real 3m12. 580 s
User 3m10. 566 s
Sys 0m0. 127 S
[Zfrey @ warthog Prof] $


Now let's take a look at the performance analysis data:

Code:
[Zfrey @ warthog Prof] $ GPROF-B./fib gmon. Out
Flat profile:

Each sample counts as 0.00195312 seconds.

% Cumulative Self total
Time seconds cils ms/call name
95.13 16.34 16.34 43 380.02 380.02 Fibonacci
4.87 17.18 0.84 main

Call Graph

Index % time self children called name

[1] 100.0 0.84 16.34 main [1]
16.34 0.00 43/43 Fibonacci [2]
-----------------------------------------------
2269806252 Fibonacci [2]
16.34 0.00 43/43 main [1]
[2] 95.1 16.34 0.00 43 + 2269806252 Fibonacci [2]
2269806252 Fibonacci [2]
-----------------------------------------------

Index by function name

[2] Fibonacci [1] Main


Obviously, the bottleneck of this program lies in the function itself. This is even more obvious if you read the code. Performance analysis tells us that although the number of recursive calls in the Code does not seem to be a lot, although the fiber Naci itself has been directly called only 43 times by main (), there are about 0.23 billion recursive calls.
Fortunately, there are other methods. For more information about the number of fiber Naci, we can find the formula for directly using N to calculate the number of fiber Naci:
F (n) = round (dolphin/SQRT (5 ))
I don't want to explain the derivation of this mathematical formula. The reason is simple and I don't understand it myself. With this improved algorithm, we have rewritten the program and analyzed it with gcov (list 4 ):
Table 4 new Fibonacci Functions

Code:
# Define Phi 1.6180339887498948

Int Fibonacci (int n)
{
Return (INT) RINT (POW (PHI, N)/SQRT (5 ));
}


Let's take a look at GPROF's results.

Code:
Call Graph

Granularity: each sample hit covers 4 byte (s) No time propagated

Index % time self children called name
0.00 0.00 43/43 main [8]
[1] 0.0 0.00 0.00 43 Fibonacci [1]
-----------------------------------------------

Index by function name

[1] Fibonacci


Our program is so fast that the sampling program cannot calculate its running time. On the one hand, this result is good, but on the other hand, we cannot continue to improve this program.
The standard method provided by the GPROF manual is to run the program multiple times and then summarize the results, as shown in the following code:

Code:
[Zfrey @ warthog Prof] $ mkdir runfib2; CD runfib2
[Zfrey @ warthog runfib2] $ for I in 'seq 1 100 ';\
Do ../fiber 2>/dev/NULL; MV gmon. Out gmon. Out. $ I; done
[Zfrey @ warthog runfib2] $ GPROF-S ../fig. Out .*


However, the result is still 0. Obviously, another method is needed now. I created the file fib3.c, which has two changes. First, I executed 1000 cycles between 0 and 42 to increase the running time. Second, I added a local variable to the Fibonacci function to split the calculation into one row for row analysis. This time we have enough running time, And GPROF generates the following report:
Table 5. fibre 3.c

Code:
# Include <stdio. h>
# Include <math. h>

Int maid (int n );

Int main (INT argc, char ** argv)
{
Int fib;
Int N;
Int I;

For (I = 0; I <1000; I ++ ){
For (n = 0; n <= 42; n ++ ){
FIB = maid (N );
Printf ("Maid (% d) = % d \ n", N, FIB );
}
}

Return 0;
}

# Define Phi 1.6180339887498948

Int Fibonacci (int n)
{
Double temp;

Temp = POW (PHI, N );
Temp = temp/SQRT (5 );
Return (INT) RINT (temp );
}


Before you start the report, I created a summary of multiple running reports as before.
Now let's take a look at the line report.

Code:
[Zfrey @ warthog runfib3] $ GPROF-B-l-P ../fig. Sum
Flat profile:

Each sample counts as 0.00195312 seconds.
% Cumulative Self total
Time seconds cils ps/call name
38.25 1.12 1.12 fig (FIG: 31)
27.97 1.94 0.82 fig (FIG: 30)
14.49 2.36 0.42 fig (FIG: 29)
5.91 2.53 0.17 main (FIG: 16)
4.64 2.67 0.14 main (FIG: 15)
3.14 2.76 0.09 fig (FIG: 32)
1.87 2.82 0.05 main (FIG: 14)
1.54 2.86 0.04 main (FIG: 14)
0.83 2.89 0.02 43000000 567.77 567.77 fig (FIG: 26)
0.77 2.91 0.02 main (FIG: 21)
0.53 2.92 0.02 main (FIG: 13)
0.07 2.93 0.00 main (FIG: 20)


31 rows consume the maximum running time, followed by 30 rows. There is no way for 31 rows, because printf () is a necessary operation. But for the 30 rows, we noticed that we had to calculate the square root of 5 every time. Because this result will not change, the next optimization is to replace it with a constant. This reduces the execution time from 136 Ms to 22 Ms.
Kprof-gprof kde Gui
Kprof is a KDE-based GPROF data display program. You can download it from kprof.sourceforge.net and install it using standard./configure; Make; make install.
After running kprof, open the measurement execution file or text file that contains the GPROF report. If you open the execution file, kprof assumes that the data is saved in the gmon. out file.
Figure 4 shows the running result of Fib displayed by kprof. The arrows in the ring indicate that Fibonacci is a recursive call.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.