简体   繁体   中英

libgcov fork and exec hooks

My man page for gcc claims about the --coverage option:

Also "fork" calls are detected and correctly handled (double counting will not happen).

And I notice my /usr/lib/gcc/x86_64-linux-gnu/5.4.0/libgcov.a contains symbols __gcov_fork , __gcov_execl , and other __gcov_exec* variants. Looking up the definitions of these functions online, it looks like they would dump and clear coverage output to avoid duplicating or losing the data.

But this doesn't seem to be working for me:

gcov_test$ rm *.gcno *.gcda
gcov_test$ cat gcov_test.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void) {
    puts("Before loop");
    for (int i=0; i<5; ++i)
        printf("i=%d\n", i);
    puts("After loop");
    pid_t child1 = fork();
    if (child1<0) {
        perror("fork 1");
        exit(1);
    } else if (child1==0) {
        printf("In child 1: %d\n", (int)getpid());
        execl("/bin/true", "/bin/true", (char*)NULL);
        perror("execl");
        exit(1);
    }
    printf("Parent spawned child 1: %d\n", (int)child1);
    pid_t child2 = fork();
    if (child2<0)
    {
        perror("fork 2");
        exit(1);
    } else if (child2==0) {
        printf("In child 2: %d\n", (int)getpid());
    } else {
        printf("Parent spawned child 2: %d\n", (int)child2);
        if (waitpid(child1, NULL, 0)<0)
            perror("waitpid 1");
        if (waitpid(child2, NULL, 0)<0)
            perror("waitpid 2");
        puts("Parent done");
    }
    return 0;
}

gcov_test$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

gcov_test$ gcc -c -std=c11 -Wall --coverage gcov_test.c 
gcov_test$ gcc --coverage gcov_test.o -o gcov_test
gcov_test$ ./gcov_test
Before loop
i=0
i=1
i=2
i=3
i=4
After loop
Parent spawned child 1: 31569
Parent spawned child 2: 31570
In child 2: 31570
In child 1: 31569
Parent done
gcov_test$ gcov gcov_test.c
File 'gcov_test.c'
Lines executed:64.29% of 28
Creating 'gcov_test.c.gcov'

gcov_test$ cat gcov_test.c.gcov
        -:    0:Source:gcov_test.c
        -:    0:Graph:gcov_test.gcno
        -:    0:Data:gcov_test.gcda
        -:    0:Runs:2
        -:    0:Programs:1
        -:    1:#include <stdlib.h>
        -:    2:#include <stdio.h>
        -:    3:#include <unistd.h>
        -:    4:#include <sys/types.h>
        -:    5:#include <sys/wait.h>
        -:    6:
        2:    7:int main(void) {
        2:    8:    puts("Before loop");
       12:    9:    for (int i=0; i<5; ++i)
       10:   10:        printf("i=%d\n", i);
        2:   11:    puts("After loop");
        2:   12:    pid_t child1 = fork();
        2:   13:    if (child1<0) {
    #####:   14:        perror("fork 1");
    #####:   15:        exit(1);
        2:   16:    } else if (child1==0) {
    #####:   17:        printf("In child 1: %d\n", (int)getpid());
    #####:   18:        execl("/bin/true", "/bin/true", (char*)NULL);
    #####:   19:        perror("execl");
    #####:   20:        exit(1);
        -:   21:    }
        2:   22:    printf("Parent spawned child 1: %d\n", (int)child1);
        2:   23:    pid_t child2 = fork();
        2:   24:    if (child2<0)
        -:   25:    {
    #####:   26:        perror("fork 2");
    #####:   27:        exit(1);
        2:   28:    } else if (child2==0) {
        1:   29:        printf("In child 2: %d\n", (int)getpid());
        -:   30:    } else {
        1:   31:        printf("Parent spawned child 2: %d\n", (int)child2);
        1:   32:        if (waitpid(child1, NULL, 0)<0)
    #####:   33:            perror("waitpid 1");
        1:   34:        if (waitpid(child2, NULL, 0)<0)
    #####:   35:            perror("waitpid 2");
        1:   36:        puts("Parent done");
        -:   37:    }
        2:   38:    return 0;
        -:   39:}
        -:   40:
gcov_test$ 

It looks to me like the "child 1" process never wrote its coverage results to file, since in particular the "In child 1" line executed but is not shown as covered. And all the lines before the second fork seem to report doubled coverage, so it seems the coverage results were not reset at calling fork as the man page claimed.

Is there something else I need to do to enable those libgcov hooks? I'm not supposed to actually replace the syscalls with the actual hook names just when compiling in coverage mode, am I?

Solution: replace c11 with gnu11 .

-std=c11 means pure C11 (without GNU extensions). -std=gnu11 also enables GNU extensions. I can't explain the connection between -std= and --coverage (probably -std= affects built-in function usage in general, and __gcov_fork is one of them), but simply changing the standard to gnu11 seems to solve the problem, ie, both line 17 and 29 now have execution count equal to 1. (I tried it both on GCC 5.4.0 and a recent trunk revision).

PS I suggest you to file a bug report. Even if this behavior is intended, the compiler should at least warn us about potential problems.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM