I am implementing a priority queue with linked list in C, however I am getting memory leaks when I print out my pop
operations. I have another memory leak and I am trying to find it too.
As a side note, I am using heapusage
by d99kris
instead of Valgrind
.
This is the heap summary when I use printf
:
HEAP SUMMARY:
in use at exit: 4112 bytes in 2 blocks
total heap usage: 10 allocs, 17 frees, 4536 bytes allocated
peak heap usage: 4256 bytes allocated
16 bytes in 1 block(s) are lost, originally allocated at:
LEAK SUMMARY:
definitely lost: 4112 bytes in 2 blocks
This is the heap summary without printf
:
HEAP SUMMARY:
in use at exit: 16 bytes in 1 blocks
total heap usage: 9 allocs, 10 frees, 440 bytes allocated
peak heap usage: 256 bytes allocated
LEAK SUMMARY:
definitely lost: 16 bytes in 1 blocks
My pop
function:
void *prio_q_pop(struct prio_q *q) {
q->size--;
struct elem *temp = q->first;
(q->first) = (q->first)->next;
void *asd = temp->datei;
free(temp);
return asd;
}
And my main
function where I call printf
struct prio_q *queue;
char *s;
int i;
queue = prio_q_create();
push(queue, "Bye World", 0);
for (i = 0; i < 5; i++) {
s = prio_q_pop(queue);
//printf("%s\n", s);
}
s = prio_q_front(queue);
//printf("%s\n", s);
reason
Problem is not caused by my code, It is the memory checker. The following program leaks 1 block with a heap usage of 2 allocs and 4 frees.
#include <stdio.h>
int main() {
printf("omer");
return 0;
}
This is a false positive. If anything, the problem is that heapusage does not have good enough documentation. I recommend using a better leak checker like the leak sanitizer or Valgrind.
I created a file test.c
.
#include <stdio.h>
int main(int argc, char **argv) {
puts("Hello, world!");
}
With leak sanitizer, no errors.
$ cc -fsanitize=leak -g test.c $ ./a.out Hello, world!
With address sanitizer, no errors.
$ cc -fsanitize=address -g test.c $ ./a.out Hello, world!
With Valgrind, no errors.
$ cc -g test.c $ valgrind ./a.out ==189174== Memcheck, a memory error detector ==189174== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==189174== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==189174== Command: ./a.out ==189174== Hello, world! ==189174== ==189174== HEAP SUMMARY: ==189174== in use at exit: 0 bytes in 0 blocks ==189174== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated ==189174== ==189174== All heap blocks were freed -- no leaks are possible ==189174== ==189174== For counts of detected and suppressed errors, rerun with: -v ==189174== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
With heapusage, a leak!
$ cc -g test.c $ ./heapusage ./a.out Hello, world! ==189005== Heapusage - https://github.com/d99kris/heapusage ==189005== ==189005== HEAP SUMMARY: ==189005== in use at exit: 1024 bytes in 1 blocks ==189005== total heap usage: 1 allocs, 0 frees, 1024 bytes allocated ==189005== peak heap usage: 1024 bytes allocated ==189005== ==189005== 1024 bytes in 1 block(s) are lost, originally allocated at: ==189005== at 0x00007f99f0de56a7: malloc + 49 ==189005== at 0x00007f99f0a96a32: _IO_file_doallocate + 114 ==189005== at 0x00007f99f0aa4a46: _IO_doallocbuf + 70 ==189005== at 0x00007f99f0aa3da8: _IO_file_overflow + 472 ==189005== at 0x00007f99f0aa2e86: _IO_file_xsputn + 182 ==189005== at 0x00007f99f0a99033: _IO_puts + 211 ==189005== at 0x000055f667ee7655: ==189005== at 0x00007f99f0a502b1: __libc_start_main + 241 ==189005== at 0x000055f667ee755a: ==189005== ==189005== LEAK SUMMARY: ==189005== definitely lost: 1024 bytes in 1 blocks ==189005==
Heapusage works by hooking malloc and free (and doesn't scan memory for pointers). Heapusage doesn't explain the advantages or disadvantages of this approach fully in the documentation. One advantage is that it's fast, but a disadvantage is that it's not precise.
In particular, I would call out heapusage as giving incorrect messages: the words "definitely lost" don't apply here!
If you want better error messages, use one of the tools recommended above: leak sanitizer or Valgrind (memcheck).
In general, I would also like to remind people that false positives are a fact of life with tools like these. Whether a program is "Valgrind clean" is a different question from whether the program is correct.
Unlike Valgrind, heapusage
does not track memory allocated by the C library for its own purposes. printf
indirectly causes this as the stream stdout
is line buffered to the terminal and fully buffered to a file. The stream buffer is allocated (by printf
or any other output function) only when you actually produce output.
You can try and work around this limitation by making stdout
unbuffered at the start of your main
function. Try this for example:
#include <stdio.h>
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
printf("omer\n");
return 0;
}
If the above code still shows a leak, try this alternative:
#include <stdio.h>
int main() {
char buf[BUFSIZ];
setvbuf(stdout, buf, _IONBF, BUFSIZ);
printf("omer\n");
fclose(stdout);
return 0;
}
Notice also that reading input from stdin
will allocate a buffer for the input stream too. Any other stream opened by the program should have been closed prior to leaving the main()
function. Closing a stream releases any memory allocated for it behind the scenes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With