adding the memory debugging part
This commit is contained in:
@@ -148,6 +148,272 @@ for (int i = 0; i < rows; i++) {
|
||||
|
||||
- Play this [animation](https://jidongxiao.github.io/CSCI1200-DataStructures/animations/dynamic_memory/two_d_array/index.html) to see what exactly the above code snippet does.
|
||||
|
||||
## 6.5 Exercises
|
||||
## 6.5 Dynamic Allocation: Arrays of Class Objects
|
||||
|
||||
We can dynamically allocate arrays of class objects. The default constructor (the constructor that takes no arguments) must be defined in order to allocate an array of objects.
|
||||
|
||||
```cpp
|
||||
class Foo {
|
||||
public:
|
||||
Foo();
|
||||
double value() const { return a*b; }
|
||||
private:
|
||||
int a;
|
||||
double b;
|
||||
};
|
||||
|
||||
Foo::Foo() {
|
||||
static int counter = 1;
|
||||
a = counter;
|
||||
b = 100.0;
|
||||
counter++;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int n;
|
||||
std::cin >> n;
|
||||
Foo *things = new Foo[n];
|
||||
std::cout << "size of int: " << sizeof(int) << std::endl;
|
||||
std::cout << "size of double: " << sizeof(double) << std::endl;
|
||||
std::cout << "size of foo object: " << sizeof(Foo) << std::endl;
|
||||
for (Foo* i = things; i < things+n; i++)
|
||||
std::cout << "Foo stored at: " << i << " has value " << i->value() << std::endl;
|
||||
delete [] things;
|
||||
}
|
||||
```
|
||||
|
||||
The above program will produce the following output:
|
||||
|
||||
```console
|
||||
size of int: 4
|
||||
size of double: 8
|
||||
size of foo object: 16
|
||||
Foo stored at: 0x104800890 has value 100
|
||||
Foo stored at: 0x1048008a0 has value 200
|
||||
Foo stored at: 0x1048008b0 has value 300
|
||||
Foo stored at: 0x1048008c0 has value 400
|
||||
...
|
||||
```
|
||||
|
||||
## 6.6 Exercises
|
||||
|
||||
- [Leetcode problem 1480: Running Sum of 1d Array](https://leetcode.com/problems/running-sum-of-1d-array/). Solution: [p1480_runningsumofarray.cpp](../../leetcode/p1480_runningsumofarray.cpp)
|
||||
|
||||
## 6.7 Memory Debugging
|
||||
|
||||
In addition to the step-by-step debuggers like gdb, lldb, or the debugger in your IDE, we recommend using a memory
|
||||
debugger like “Dr. Memory” (Windows, Linux, and MacOSX) or “Valgrind” (Linux and MacOSX). These tools can
|
||||
detect the following problems:
|
||||
- Use of uninitialized memory
|
||||
- Reading/writing memory after it has been free’d (NOTE: delete calls free)
|
||||
- Reading/writing off the end of malloc’d blocks (NOTE: new calls malloc)
|
||||
- Reading/writing inappropriate areas on the stack
|
||||
- Memory leaks - where pointers to malloc’d blocks are lost forever
|
||||
- Mismatched use of malloc/new/new [] vs free/delete/delete []
|
||||
- Overlapping src and dst pointers in memcpy() and related functions
|
||||
|
||||
## 6.8 Sample Buggy Program
|
||||
|
||||
Can you see the errors in this program?
|
||||
|
||||
```cpp
|
||||
1 #include <iostream>
|
||||
2
|
||||
3 int main() {
|
||||
4
|
||||
5 int *p = new int;
|
||||
6 if (*p != 10) std::cout << "hi" << std::endl;
|
||||
7
|
||||
8 int *a = new int[3];
|
||||
9 a[3] = 12;
|
||||
10 delete a;
|
||||
11
|
||||
12 }
|
||||
```
|
||||
|
||||
## 6.9 Using Dr. Memory http://www.drmemory.org
|
||||
|
||||
Here’s how Dr. Memory reports the errors in the above program:
|
||||
|
||||
```console
|
||||
~~Dr.M~~ Dr. Memory version 1.8.0
|
||||
~~Dr.M~~
|
||||
~~Dr.M~~ Error #1: UNINITIALIZED READ: reading 4 byte(s)
|
||||
~~Dr.M~~ # 0 main [memory_debugger_test.cpp:6]
|
||||
hi
|
||||
~~Dr.M~~
|
||||
~~Dr.M~~ Error #2: UNADDRESSABLE ACCESS beyond heap bounds: writing 4 byte(s)
|
||||
~~Dr.M~~ # 0 main [memory_debugger_test.cpp:9]
|
||||
~~Dr.M~~ Note: refers to 0 byte(s) beyond last valid byte in prior malloc
|
||||
~~Dr.M~~
|
||||
~~Dr.M~~ Error #3: INVALID HEAP ARGUMENT: allocated with operator new[], freed with operator delete
|
||||
~~Dr.M~~ # 0 replace_operator_delete [/drmemory_package/common/alloc_replace.c:2684]
|
||||
~~Dr.M~~ # 1 main [memory_debugger_test.cpp:10]
|
||||
~~Dr.M~~ Note: memory was allocated here:
|
||||
~~Dr.M~~ Note: # 0 replace_operator_new_array [/drmemory_package/common/alloc_replace.c:2638]
|
||||
~~Dr.M~~ Note: # 1 main [memory_debugger_test.cpp:8]
|
||||
~~Dr.M~~
|
||||
~~Dr.M~~ Error #4: LEAK 4 bytes
|
||||
~~Dr.M~~ # 0 replace_operator_new [/drmemory_package/common/alloc_replace.c:2609]
|
||||
~~Dr.M~~ # 1 main [memory_debugger_test.cpp:5]
|
||||
~~Dr.M~~
|
||||
~~Dr.M~~ ERRORS FOUND:
|
||||
~~Dr.M~~ 1 unique, 1 total unaddressable access(es)
|
||||
~~Dr.M~~ 1 unique, 1 total uninitialized access(es)
|
||||
~~Dr.M~~ 1 unique, 1 total invalid heap argument(s)
|
||||
~~Dr.M~~ 0 unique, 0 total warning(s)
|
||||
~~Dr.M~~ 1 unique, 1 total, 4 byte(s) of leak(s)
|
||||
~~Dr.M~~ 0 unique, 0 total, 0 byte(s) of possible leak(s)
|
||||
~~Dr.M~~ Details: /DrMemory-MacOS-1.8.0-8/drmemory/logs/DrMemory-a.out.7726.000/results.txt
|
||||
```
|
||||
|
||||
And the fixed version:
|
||||
|
||||
```console
|
||||
~~Dr.M~~ Dr. Memory version 1.8.0
|
||||
hi
|
||||
~~Dr.M~~
|
||||
~~Dr.M~~ NO ERRORS FOUND:
|
||||
~~Dr.M~~ 0 unique, 0 total unaddressable access(es)
|
||||
~~Dr.M~~ 0 unique, 0 total uninitialized access(es)
|
||||
~~Dr.M~~ 0 unique, 0 total invalid heap argument(s)
|
||||
~~Dr.M~~ 0 unique, 0 total warning(s)
|
||||
~~Dr.M~~ 0 unique, 0 total, 0 byte(s) of leak(s)
|
||||
~~Dr.M~~ 0 unique, 0 total, 0 byte(s) of possible leak(s)
|
||||
~~Dr.M~~ Details: /DrMemory-MacOS-1.8.0-8/drmemory/logs/DrMemory-a.out.7762.000/results.txt
|
||||
```
|
||||
|
||||
**Note**: Dr. Memory on Windows with the Visual Studio compiler may not report a mismatched free() / delete
|
||||
/ delete [] error (e.g., line 10 of the sample code above). This may happen if optimizations are enabled and the
|
||||
objects stored in the array are simple and do not have their own dynamically-allocated memory that lead to their
|
||||
own indirect memory leaks.
|
||||
|
||||
## 6.10 Using Valgrind http://valgrind.org/
|
||||
|
||||
And this is how Valgrind reports the same errors:
|
||||
|
||||
```console
|
||||
==31226== Memcheck, a memory error detector
|
||||
==31226== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
|
||||
==31226== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
|
||||
==31226== Command: ./a.out
|
||||
==31226==
|
||||
==31226== Conditional jump or move depends on uninitialised value(s)
|
||||
==31226== at 0x40096F: main (memory_debugger_test.cpp:6)
|
||||
==31226==
|
||||
hi
|
||||
==31226== Invalid write of size 4
|
||||
==31226== at 0x4009A3: main (memory_debugger_test.cpp:9)
|
||||
==31226== Address 0x4c3f09c is 0 bytes after a block of size 12 alloc'd
|
||||
==31226== at 0x4A0700A: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
|
||||
==31226== by 0x400996: main (memory_debugger_test.cpp:8)
|
||||
==31226==
|
||||
==31226== Mismatched free() / delete / delete []
|
||||
==31226== at 0x4A07991: operator delete(void*) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
|
||||
==31226== by 0x4009B4: main (memory_debugger_test.cpp:10)
|
||||
==31226== Address 0x4c3f090 is 0 bytes inside a block of size 12 alloc'd
|
||||
==31226== at 0x4A0700A: operator new[](unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
|
||||
==31226== by 0x400996: main (memory_debugger_test.cpp:8)
|
||||
==31226==
|
||||
==31226==
|
||||
==31226== HEAP SUMMARY:
|
||||
==31226== in use at exit: 4 bytes in 1 blocks
|
||||
==31226== total heap usage: 2 allocs, 1 frees, 16 bytes allocated
|
||||
==31226==
|
||||
==31226== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
|
||||
==31226== at 0x4A06965: operator new(unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
|
||||
==31226== by 0x400961: main (memory_debugger_test.cpp:5)
|
||||
==31226==
|
||||
==31226== LEAK SUMMARY:
|
||||
==31226== definitely lost: 4 bytes in 1 blocks
|
||||
==31226== indirectly lost: 0 bytes in 0 blocks
|
||||
==31226== possibly lost: 0 bytes in 0 blocks
|
||||
==31226== still reachable: 0 bytes in 0 blocks
|
||||
==31226== suppressed: 0 bytes in 0 blocks
|
||||
==31226==
|
||||
==31226== For counts of detected and suppressed errors, rerun with: -v
|
||||
==31226== Use --track-origins=yes to see where uninitialised values come from
|
||||
==31226== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 2 from 2)
|
||||
```
|
||||
|
||||
And here’s what it looks like after fixing those bugs:
|
||||
|
||||
```console
|
||||
==31252== Memcheck, a memory error detector
|
||||
==31252== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
|
||||
==31252== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
|
||||
==31252== Command: ./a.out
|
||||
==31252==
|
||||
hi
|
||||
==31252==
|
||||
==31252== HEAP SUMMARY:
|
||||
==31252== in use at exit: 0 bytes in 0 blocks
|
||||
==31252== total heap usage: 2 allocs, 2 frees, 16 bytes allocated
|
||||
==31252==
|
||||
==31252== All heap blocks were freed -- no leaks are possible
|
||||
==31252==
|
||||
==31252== For counts of detected and suppressed errors, rerun with: -v
|
||||
==31252== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
|
||||
```
|
||||
|
||||
## 6.11 How to use a memory debugger
|
||||
|
||||
- Detailed instructions on installation & use of these tools are available here:
|
||||
http://www.cs.rpi.edu/academics/courses/fall23/csci1200/memory_debugging.php
|
||||
- Memory errors (uninitialized memory, out-of-bounds read/write, use after free) may cause seg faults, crashes,
|
||||
or strange output.
|
||||
- Memory leaks on the other hand will never cause incorrect output, but your program will be inefficient and
|
||||
hog system resources. A program with a memory leak may waste so much memory it causes all programs on
|
||||
the system to slow down significantly or it may crash the program or the whole operating system if the system
|
||||
runs out of memory (this takes a while on modern computers with lots of RAM & harddrive space).
|
||||
- For many future homeworks, Submitty will be configured to run your code with Dr. Memory to search for
|
||||
memory problems and present the output with the submission results. For full credit your program must be
|
||||
memory error and memory leak free!
|
||||
- A program that seems to run perfectly fine on one computer may still have significant memory errors. Running
|
||||
a memory debugger will help find issues that might break your homework on another computer or when
|
||||
submitted to the homework server.
|
||||
- **Important Note**: When these tools find a memory leak, they point to the line of code where this memory
|
||||
was allocated. These tools does not understand the program logic and thus obviously cannot tell us where it
|
||||
should have been deleted.
|
||||
- A final note: STL and other 3rd party libraries are highly optimized and sometimes do sneaky but correct and
|
||||
bug-free tricks for efficiency that confuse the memory debugger. For example, because the STL string class
|
||||
uses its own allocator, there may be a warning about memory that is “still reachable” even though you’ve
|
||||
deleted all your dynamically allocated memory. The memory debuggers have automatic suppressions for some
|
||||
of these known “false positives”, so you will see this listed as a “suppressed leak”. So don’t worry if you see
|
||||
those messages.
|
||||
|
||||
## 6.12 Diagramming Memory Exercises
|
||||
|
||||
- Draw a diagram of the heap and stack memory for each segment of code below. Use a “?” to indicate that the
|
||||
value of the memory is uninitialized. Indicate whether there are any errors or memory leaks during execution
|
||||
of this code.
|
||||
|
||||
```cpp
|
||||
class Foo {
|
||||
public:
|
||||
double x;
|
||||
int* y;
|
||||
};
|
||||
Foo a;
|
||||
a.x = 3.14159;
|
||||
Foo *b = new Foo;
|
||||
(*b).y = new int[2];
|
||||
Foo *c = b;
|
||||
a.y = b->y;
|
||||
c->y[1] = 7;
|
||||
b = NULL;
|
||||
```
|
||||
|
||||
```cpp
|
||||
int a[5] = { 10, 11, 12, 13, 14 };
|
||||
int *b = a + 2;
|
||||
*b = 7;
|
||||
int *c = new int[3];
|
||||
c[0] = b[0];
|
||||
c[1] = b[1];
|
||||
c = &(a[3]);
|
||||
```
|
||||
|
||||
Write code to produce this diagram:
|
||||
|
||||
[alt text](memory_exercise.png "memory exercise")
|
||||
|
||||
Reference in New Issue
Block a user