CIS*2750 Lecture 4 - Valgrind and Memory Management PDF

Document Details

ComfortableBowenite2676

Uploaded by ComfortableBowenite2676

University of Guelph

Tags

memory management valgrind c programming computer science

Summary

These lecture notes cover memory management and the use of Valgrind, a memory debugging tool, to identify memory leaks in C programming. The notes provide examples of allocating and modifying memory in functions. The notes emphasize the importance of passing addresses to modify values within functions, outlining potential issues with incorrect pointer manipulation.

Full Transcript

CIS*2750 Lecture 4: memory and valgrind refresher Allocating memory in a function: The rule Rule: to modify a thing in a function, we must pass its address - i.e. a pointer to the thing If we want to modify a value in a function, we must pass it by reference In other words, we must pass...

CIS*2750 Lecture 4: memory and valgrind refresher Allocating memory in a function: The rule Rule: to modify a thing in a function, we must pass its address - i.e. a pointer to the thing If we want to modify a value in a function, we must pass it by reference In other words, we must pass its address, or a pointer to it Otherwise, what is modi ed in the function is the copy of the value For example, if we want to modify the value of an int in a function, we pass a pointer to it - i.e. its address fi Modifying an int in a function main: int n = 2; //We want to change a thing add2(&n); //We pass the address of the thing printf(“%d”,n); //prints 4 add2: void add2(int* val){ *val = *val + 2; //we add 2 to the thing that val points to - //i.e. the value in variable n } Allocating memory in a function If we want to allocate memory to a pointer, we can do it in a function The value of a pointer variable p is a memory address Could be NULL initially - e.g. the value of p is 0 We want to change the value of p to be the address of some block of memory we have allocated We apply the same rule: to modify a thing in a function, we must pass its address to modify a pointer, we must pass its address an address is a pointer the address of pointer is a … double pointer - an address of an address Modifying a pointer in a function main: int arrLen = 10; int* array = NULL; //We want to change a thing allocate(&array, arrLen); //We pass the address of the thing //the value of array is now an address of some freshly allocated memory block allocate: void allocate(int** p, int len){ *p = malloc(sizeof(int)*len); //We have modified the thing that array points - i.e. the value (address) stored in the variable array } Modifying a pointer in a function 05fd 0 array int* array = NULL; allocate(&array, arrLen); We have a create named array that lives at memory address 05fd The value stored at memory address 05fd is whatever NULL happens to be - which is usually the integer value 0 Modifying a pointer in a function 05fd 0 05fd array p int* array = NULL; allocate(&array, arrLen); void allocate(int** p, int len){ *p = malloc(sizeof(int)*arrLen); } We call the function allocate and pass it the address of array Inside allocate: p = 05fd - i.e. the address of array *p = 0 - i.e. the value stored in array Modifying a pointer in a function 05fd f23c 0 05fd array p 80 bytes int* array = NULL; allocate(&array, arrLen); void allocate(int** p, int len){ *p = malloc(sizeof(int)*arrLen); } Inside allocate, we call malloc - which asks the operating system for 10*sizeof(int) bytes of memory - i.e. 80 bytes of a 64-bit system and returns the start address of that block Modifying a pointer in a function 05fd f23c f23c 05fd array p int* array = NULL; allocate(&array, arrLen); void allocate(int** p, len){ *p = malloc(sizeof(int)*arrLen); } We then assign this address to the thing that p points to In other words, we go the address stored inside p - i.e. 05fd and overwrite whatever is stored there with the value returned by malloc so we replace 0 with f230 Modifying a pointer in a function 05fd f23c f23c array int* array = NULL; allocate(&array, arrLen); void allocate(int** p, len){ *p = malloc(sizeof(int)*arrLen); } Control passes back to main() The value in array is now the address of the newly allocated block of memory Why not do this? Let’s see… main: int arrLen = 10; int* array = NULL; allocate(array, arrLen); //Address, shmaddress allocate: void allocate(int* p, len){ //Screw all those extra *’s! p = malloc(sizeof(int)*len); } So far so good 05fd 0 array int* array = NULL; allocate(array, arrLen); We have a create named array that lives at memory address 05fd The value stored at memory address 05fd is whatever NULL happens to be - which is usually the integer value 0 this looks a bit different… 05fd 0 0 array p int* array = NULL; allocate(array, arrLen); void allocate(int* p, len){ p = malloc(sizeof(int)*arrLen); } We call the function allocate and pass it the value of array Inside allocate: p = 0 - i.e. value stored in array the malloc() bit is the same 05fd f23c 0 0 array p 80 bytes int* array = NULL; allocate(array, arrLen); void allocate(int* p, len){ p = malloc(sizeof(int)*arrLen); } Inside allocate, we call malloc - which asks the operating system for 10*sizeof(int) bytes of memory - i.e. 80 bytes of a 64-bit system and returns the start address of that block uh oh… 05fd f23c 0 f23c ccc array p int* array = NULL; allocate(array, arrLen); void allocate(int* p, len){ p = malloc(sizeof(int)*arrLen); } We then assign this address to p In other words, we change the value of p from 0 to f23c The value of array remains 0 - with is NOT what we want! fail! 05fd f23c 0 array int* array = NULL; allocate(array, arrLen); void allocate(int* p, len){ p = malloc(sizeof(int)*arrLen); } Control passes back to main() The value in array remains the same The freshly allocated memory block still exists, but nothing points to it All we got is a brand new memory leak! Moral of the story to modify a thing in a function, we must pass its address - otherwise we’re modifying a copy! Usability note Dereferencing double-pointers is a pain In your Assignment 1, I recommend you do this: VCardErrorCode createCard(char* fileName, Card** newCardObject){ Card* tmp = malloc… //Allocate memory //Fill in tmp with value from the file... //at the end of the file, modify the thing that obj points to *obj = tmp; //return stuff here } Valgrind Valgrind is a memory debugging tool that can check for memory leaks (failing to free dynamically-allocated memory) and diagnose various other memory errors Unix/Linux only. Works on Ubuntu shell on Windows, and macOS up to 10.11 (El Capitan) Run valgrind with a compiled program to check for memory which has been allocated and not freed. e.g. valgrind./myprog You need to compile your program using the -g ag (same as for use with GDB): gcc a1.c -o a1 -g valgrind can print out a lot of information about the memory usage of your program. The most important sections are the HEAP SUMMARY, the LEAK SUMMARY, and the ERROR SUMMARY. Output of a program with no errors $ valgrind./exampleCode1 ==24731== Memcheck, a memory error detector ==24731== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==24731== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info ==24731== Command:./tree1 Reyn-Rozh_parking(0rt_1trk_1seg_400m).gpx ==24731== ==24731== ==24731== HEAP SUMMARY: ==24731== in use at exit: 0 bytes in 0 blocks ==24731== total heap usage: 175 allocs, 175 frees, 132,127 bytes allocated ==24731== ==24731== All heap blocks were freed -- no leaks are possible ==24731== ==24731== For counts of detected and suppressed errors, rerun with: -v ==24731== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) Valgrind and memory leaks Memory blocks are marked by valgrind as one of four types: "de nitely lost" means your program is leaking memory x these leaks "indirectly lost" means your program is leaking memory in a pointer-based structure. (E.g. if the root node of a binary tree is "de nitely lost", all the children will be "indirectly lost".) x these leaks If you x the "de nitely lost" leaks, the "indirectly lost" leaks should go away "possibly lost" means your program is leaking memory, unless you're doing unusual things with pointers that could cause them to point into the middle of an allocated block; see the user manual for some possible causes x these leaks you are unlikely to see these errors http://valgrind.org/docs/manual/faq.html fi fi fi fi fi fi fi Valgrind output program with a memory leak created by a missing free() command $ valgrind./exampleCode2 ==24702== Memcheck, a memory error detector ==24702== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==24702== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info ==24702== Command:./tree1 Reyn-Rozh_parking(0rt_1trk_1seg_400m).gpx ==24702== ==24702== ==24702== HEAP SUMMARY: ==24702== in use at exit: 14,865 bytes in 113 blocks ==24702== total heap usage: 175 allocs, 62 frees, 132,127 bytes allocated ==24702== ==24702== LEAK SUMMARY: ==24702== definitely lost: 176 bytes in 1 blocks ==24702== indirectly lost: 14,689 bytes in 112 blocks ==24702== possibly lost: 0 bytes in 0 blocks ==24702== still reachable: 0 bytes in 0 blocks ==24702== suppressed: 0 bytes in 0 blocks ==24702== Rerun with --leak-check=full to see details of leaked memory ==24702== ==24702== For counts of detected and suppressed errors, rerun with: -v ==24702== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) Valgrind and memory errors In addition to leaks, valgrind can detect memory errors: Using an uninitialized variable Writing into memory that was not allocated etc. http://valgrind.org/docs/manual/faq.html Valgrind output Memory error: your malloc call did not allocate enough memory $ valgrind./StructListDemo ==32322== Memcheck, a memory error detector ==32322== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==32322== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info ==32322== Command:./StructListDemo ==32322== ==32322== Invalid write of size 8 ==32322== at 0x1095D2: main (StructListDemo.c:143) ==32322== Address 0x4a25d10 is 0 bytes inside a block of size 1 alloc'd ==32322== at 0x483577F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==32322== by 0x1095B9: main (StructListDemo.c:142) ==32322==... further errors omitted... ==32322== Invalid read of size 1 ==32322== at 0x4839D20: strcmp (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==32322== by 0x10932D: compareFunc (StructListDemo.c:56) ==32322== by 0x48455AE: deleteDataFromList (LinkedListAPI.c:181) ==32322== by 0x1095F0: main (StructListDemo.c:145) ==32322== Address 0x4a25d11 is 0 bytes after a block of size 1 alloc'd ==32322== at 0x483577F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==32322== by 0x1095B9: main (StructListDemo.c:142) ==32322== ==32322== ==32322== HEAP SUMMARY: ==32322== in use at exit: 0 bytes in 0 blocks ==32322== total heap usage: 33 allocs, 33 frees, 2,049 bytes allocated ==32322== ==32322== All heap blocks were freed -- no leaks are possible ==32322== ==32322== For counts of detected and suppressed errors, rerun with: -v ==32322== ERROR SUMMARY: 20 errors from 3 contexts (suppressed: 0 from 0) Valgrind output Memory error: you did not initialize a variable before use $ valgrind./StructListDemo ==308== Memcheck, a memory error detector ==308== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==308== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info ==308== Command:./StructListDemo ==308== ==308== Conditional jump or move depends on uninitialised value(s) ==308== at 0x48B2029: vfprintf (vfprintf.c:1637) ==308== by 0x48D3320: vsprintf (iovsprintf.c:42) ==308== by 0x48B9773: sprintf (sprintf.c:32) ==308== by 0x1092D2: printFunc (StructListDemo.c:38) ==308== by 0x1094C0: main (StructListDemo.c:104) ==308== ==308== Use of uninitialised value of size 8 ==308== at 0x48ADD1E: _itoa_word (_itoa.c:179) ==308== by 0x48B15F3: vfprintf (vfprintf.c:1637) ==308== by 0x48D3320: vsprintf (iovsprintf.c:42) ==308== by 0x48B9773: sprintf (sprintf.c:32) ==308== by 0x1092D2: printFunc (StructListDemo.c:38) ==308== by 0x1094C0: main (StructListDemo.c:104) ==308==... further errors omitted... ==308== ==308== ==308== HEAP SUMMARY: ==308== in use at exit: 0 bytes in 0 blocks ==308== total heap usage: 33 allocs, 33 frees, 2,052 bytes allocated ==308== ==308== All heap blocks were freed -- no leaks are possible ==308== ==308== For counts of detected and suppressed errors, rerun with: -v ==308== Use --track-origins=yes to see where uninitialised values come fr Memory Reminders All calls to malloc/calloc/realloc require an associated free or a memory leak will be created Some library functions will allocate memory and it is up to you to free that memory if it is returned to your program (e.g. strdup() ) This is also how many of our List functions work as well - e.g. toString() Others will use memory and only free it when you nish using the functions e.g. fopen() and fclose() More information Use the valgrind documentation page: http://valgrind.org/docs/manual/index.html The Quick Start guide is always a good starting point http://valgrind.org/docs/manual/QuickStart.html You can also use the labs and talk to the TA about using valgrind

Use Quizgecko on...
Browser
Browser