The last remaining bug to slay in my spell-checker for CS50 — after making sure my hash function was implemented properly — is a memory issue. The program works, finds the correct number of misspelled words. But the CS50 bot that checks submissions isn’t going to give a full score yet.
detects memory-management problems, and is aimed primarily at C and C++ programs. When a program is run under Memcheck's supervision, all reads and writes of memory are checked, and calls to malloc/new/free/delete are intercepted.
I am using the CS50 IDE, where Valgrind is already installed. So directly out of the box: I can add
valgrind to the beginning of the command to run my program with Memcheck.
- Quick Start Guide: Interpreting Memcheck's output
- Manual: Explanation of error messages from Memcheck
Braindump of the debugging 🐛
There is a lot of output, but the parts that look relevant are:
Invalid read of size 8 Address 0x4b9a550 is 48 bytes inside a block of size 56 free'd HEAP SUMMARY: in use at exit: 472 bytes in 1 blocks total heap usage: 12 allocs, 11 frees, 10,552 bytes allocated ERROR SUMMARY: 7 errors from 1 contexts
Not sure I am reading this correctly, but… 8 bytes? Does this sounds like some sort of off-by-one error? 🤔 That was the reduced test case with tiny dictionary and short text. What will the output be with the large dictionary and longer text…?
Invalid read of size 8 Address 0x5d11af0 is 48 bytes inside a block of size 56 free'd HEAP SUMMARY: in use at exit: 472 bytes in 1 blocks total heap usage: 143,096 allocs, 143,095 frees, 8,023,256 bytes allocated
Still a single alloc more than blocks free'd. Hm. My theory is that there is something off in the details of how I am traversing the linked lists and calling
free() on the nodes.
- Is my
forloop looping 1 too few or 1 too many times? No.
- Also played around with replacing a
whileloop with a
do...whileand hey, got a segmentation fault. Which was fun to see in the Memcheck output like this:
Address 0x30 is not stack'd, malloc'd or (recently) free'd Process terminating with default action of signal 11 (SIGSEGV) Access not within mapped region at address 0x30
Time to check if everything is working as it should with the
cursor variable that moves the pointer to the next node in the linked list. I’ve got a
tmp which is what I call
free() on, so I don’t think I’m loosing the rest of the linked list after the first node. But ah, I was moving the pointer and free-ing in the wrong order. Fixed! And voilà:
HEAP SUMMARY: in use at exit: 472 bytes in 1 blocks total heap usage: 12 allocs, 11 frees, 10,552 bytes allocated LEAK SUMMARY: definitely lost: 0 bytes in 0 blocks indirectly lost: 0 bytes in 0 blocks possibly lost: 0 bytes in 0 blocks still reachable: 472 bytes in 1 blocks suppressed: 0 bytes in 0 blocks ERROR SUMMARY: 0 errors from 0 contexts
So 0 errors, but still an issue. Beginning to feel confident-ish in my unload function, so maybe the bug is in my load function? That I am alloc'ing more that I should? Hm. No.
Noooooooooooo… 🤦🏻♀️ Or rather… yay? Because now I know exactly what is still reachable. Lets just say that I will never ever again forget to
All heap blocks were freed -- no leaks are possible
Spell Checker Submitted 🍾
What I Learnt Today 🎓
- How to use Valgrind and a bit about reading the output
- Always remeber to close files!
- Not all memory issues are memory leaks