Search code examples
cmemory-leakspthreadsvalgrind

Why does valgrind report different results (no leaks are possible / still reachable) just by running the program multiple times?


While debugging a random segmentation fault, I ran valgrind, running the same program multiple times I get different results. Here is a simplified version of the program (I did not get a a random segmentation fault on this simplified version):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

pthread_t* tid;
pthread_t prodId;
int numberThreads = 0;

void *worker(void *arg);
void *producer(void *arg);

int main(int argc, char** argv) {
    numberThreads = atoi((char *) argv[1]);
    tid = (pthread_t*) malloc(sizeof (pthread_t) * numberThreads);
    pthread_create(&prodId, NULL, producer, &prodId);
    pthread_join(prodId, NULL);
    free(tid);
    return (EXIT_SUCCESS);
}

void *producer(void *arg) {
    for (int x = 0; x < numberThreads; x++)
        pthread_create(&(tid[x]), NULL, worker, &(tid[x]));
    for (int x = 0; x < numberThreads; x++)
        pthread_join(tid[x], NULL);
    pthread_exit(NULL);
}

void *worker(void *arg) {
    pthread_exit(NULL);
}

Here is the output of running the program a few times consecutively:

$ valgrind --leak-check=full --leak-check=full --show-leak-kinds=all  ./memleek 1
==3044== Memcheck, a memory error detector
==3044== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3044== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==3044== Command: ./memleek 1
==3044== 
==3044== 
==3044== HEAP SUMMARY:
==3044==     in use at exit: 0 bytes in 0 blocks
==3044==   total heap usage: 8 allocs, 8 frees, 2,222 bytes allocated
==3044== 
==3044== All heap blocks were freed -- no leaks are possible
==3044== 
==3044== For counts of detected and suppressed errors, rerun with: -v
==3044== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$ 
$ 
$ valgrind --leak-check=full --leak-check=full --show-leak-kinds=all  ./memleek 2
==3047== Memcheck, a memory error detector
==3047== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3047== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==3047== Command: ./memleek 2
==3047== 
==3047== 
==3047== HEAP SUMMARY:
==3047==     in use at exit: 0 bytes in 0 blocks
==3047==   total heap usage: 9 allocs, 9 frees, 2,502 bytes allocated
==3047== 
==3047== All heap blocks were freed -- no leaks are possible
==3047== 
==3047== For counts of detected and suppressed errors, rerun with: -v
==3047== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$
$
$ valgrind --leak-check=full --leak-check=full --show-leak-kinds=all  ./memleek 2
==3051== Memcheck, a memory error detector
==3051== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3051== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==3051== Command: ./memleek 2
==3051== 
==3051== 
==3051== HEAP SUMMARY:
==3051==     in use at exit: 0 bytes in 0 blocks
==3051==   total heap usage: 9 allocs, 9 frees, 2,502 bytes allocated
==3051== 
==3051== All heap blocks were freed -- no leaks are possible
==3051== 
==3051== For counts of detected and suppressed errors, rerun with: -v
==3051== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$
$
$ valgrind --leak-check=full --leak-check=full --show-leak-kinds=all  ./memleek 2
==3055== Memcheck, a memory error detector
==3055== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==3055== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==3055== Command: ./memleek 2
==3055== 
==3055== 
==3055== HEAP SUMMARY:
==3055==     in use at exit: 1,614 bytes in 4 blocks
==3055==   total heap usage: 9 allocs, 5 frees, 2,502 bytes allocated
==3055== 
==3055== 36 bytes in 1 blocks are still reachable in loss record 1 of 4
==3055==    at 0x4C2BBAF: malloc (vg_replace_malloc.c:299)
==3055==    by 0x401AE89: strdup (strdup.c:42)
==3055==    by 0x4016676: _dl_load_cache_lookup (dl-cache.c:311)
==3055==    by 0x4008A87: _dl_map_object (dl-load.c:2336)
==3055==    by 0x4013B13: dl_open_worker (dl-open.c:237)
==3055==    by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055==    by 0x4013608: _dl_open (dl-open.c:660)
==3055==    by 0x517431C: do_dlopen (dl-libc.c:87)
==3055==    by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055==    by 0x51743AE: dlerror_run (dl-libc.c:46)
==3055==    by 0x5174421: __libc_dlopen_mode (dl-libc.c:163)
==3055==    by 0x4E4966A: pthread_cancel_init (unwind-forcedunwind.c:52)
==3055== 
==3055== 36 bytes in 1 blocks are still reachable in loss record 2 of 4
==3055==    at 0x4C2BBAF: malloc (vg_replace_malloc.c:299)
==3055==    by 0x400B4D3: _dl_new_object (dl-object.c:165)
==3055==    by 0x400587C: _dl_map_object_from_fd (dl-load.c:1000)
==3055==    by 0x400874B: _dl_map_object (dl-load.c:2470)
==3055==    by 0x4013B13: dl_open_worker (dl-open.c:237)
==3055==    by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055==    by 0x4013608: _dl_open (dl-open.c:660)
==3055==    by 0x517431C: do_dlopen (dl-libc.c:87)
==3055==    by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055==    by 0x51743AE: dlerror_run (dl-libc.c:46)
==3055==    by 0x5174421: __libc_dlopen_mode (dl-libc.c:163)
==3055==    by 0x4E4966A: pthread_cancel_init (unwind-forcedunwind.c:52)
==3055== 
==3055== 360 bytes in 1 blocks are still reachable in loss record 3 of 4
==3055==    at 0x4C2DBC5: calloc (vg_replace_malloc.c:711)
==3055==    by 0x4010FBD: _dl_check_map_versions (dl-version.c:293)
==3055==    by 0x4014076: dl_open_worker (dl-open.c:286)
==3055==    by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055==    by 0x4013608: _dl_open (dl-open.c:660)
==3055==    by 0x517431C: do_dlopen (dl-libc.c:87)
==3055==    by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055==    by 0x51743AE: dlerror_run (dl-libc.c:46)
==3055==    by 0x5174421: __libc_dlopen_mode (dl-libc.c:163)
==3055==    by 0x4E4966A: pthread_cancel_init (unwind-forcedunwind.c:52)
==3055==    by 0x4E49853: _Unwind_ForcedUnwind (unwind-forcedunwind.c:126)
==3055==    by 0x4E47D7F: __pthread_unwind (unwind.c:121)
==3055== 
==3055== 1,182 bytes in 1 blocks are still reachable in loss record 4 of 4
==3055==    at 0x4C2DBC5: calloc (vg_replace_malloc.c:711)
==3055==    by 0x400B215: _dl_new_object (dl-object.c:75)
==3055==    by 0x400587C: _dl_map_object_from_fd (dl-load.c:1000)
==3055==    by 0x400874B: _dl_map_object (dl-load.c:2470)
==3055==    by 0x4013B13: dl_open_worker (dl-open.c:237)
==3055==    by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055==    by 0x4013608: _dl_open (dl-open.c:660)
==3055==    by 0x517431C: do_dlopen (dl-libc.c:87)
==3055==    by 0x400F643: _dl_catch_error (dl-error.c:187)
==3055==    by 0x51743AE: dlerror_run (dl-libc.c:46)
==3055==    by 0x5174421: __libc_dlopen_mode (dl-libc.c:163)
==3055==    by 0x4E4966A: pthread_cancel_init (unwind-forcedunwind.c:52)
==3055== 
==3055== LEAK SUMMARY:
==3055==    definitely lost: 0 bytes in 0 blocks
==3055==    indirectly lost: 0 bytes in 0 blocks
==3055==      possibly lost: 0 bytes in 0 blocks
==3055==    still reachable: 1,614 bytes in 4 blocks
==3055==         suppressed: 0 bytes in 0 blocks
==3055== 
==3055== For counts of detected and suppressed errors, rerun with: -v
==3055== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
$ 

I noticed that if I run 4 workers, by passing 4 as the first argument, it constantly says

still reachable: 1,614 bytes in 4 blocks

Considering that according to this, the "still reachable" category within Valgrind's leak report refers to allocations that fit only the first definition of "memory leak". These blocks were not freed, but they could have been freed (if the programmer had wanted to) because the program still was keeping track of pointers to those memory blocks.

Well, I want to free these blocks. Can anyone show me how? Thank you!


Solution

  • I tried running your program and I saw the same behavior. I also found that removing one row, the pthread_exit in the workers, removed the valgrind warning about still reachable memory.

    I started to suspect glibc, and this was confirmed by a similar SO question/answer: glibc oddity with Valgrind