CISC3130, Spring 2013 April 26

Getting Ready: copy sample /C++ codes into your current directory using command copy_code

1. Study the Makefile a. Note the options –ggdb, -Wall, -o of g++, what are their meanings?

b. Add rules for compiling factorial.cc and cstring.cc to programs named as factorial and cstring respectively

c. Add a target “all” for compiling all three programs d. Modify it so that when called with “clean” target, all three program files will be deleted

2. Many ways to go about :

a. Thinking about what the program is doing and making an educated guess as to what the problem is. b. Printing out variables (using cout statement, or printf in C) c. Adding assert statements in critical points in the program (for example, in the beginning of a function body, to make sure the pre-condition of the function is met) d. Using a debugger: a more interactive approach

For the most tough bugs related to memory issues (bus errors, segmentation faults), one can use gdb to find on which line of code the occurs. Once the line of code in question has been found, it is useful to know about the values in that method, who called the method, and why (specifically) the error is occuring. Using a debugger makes finding all of this information very simple.

3. Loading a program into gdb gdb cstring

gdb commands: • run • help: show help message • backtrace, where: displaying the calling stack o up: go to the caller of current function o down: go back to the function called by the current function

• step: execute the next statement, • break: tell gdb which function to break in o To set a breakpoint at a line: break [filename] lineno o To set a breakpoint at a function: break func1 o To set a breakpoint in a method: break TestClass::testFunc(int)

(use info breakpoints to see all breakpoints Use disable command to disable a breakpoint)

• condition: add condition to a breakpoint, i.e., only break when the condition is met • print: display value of a variable • set x=3: set variable x to 3 • quit: exit gdb

4. Memory related runtime error a. A program's address space The following diagram illustrates the layout of memory space that is used by a (a program running in the system):

Stack is a certain amount of memory given for each process for storing information related to each function calls (i.e., invocations).

Whenever a function call is invoked, a stack frame is created for the function call for storing the local variables of the function, the arguments passed to the functions, as well as the return address (i.e., when returning from the function, which instruction to resume execute). The stack frame is pushed onto the top of the stack upon the function call, and it will be pop off from the stack upon function exits.

When there is a sequence of function calls, such as main() call func1(), which in turns call func2(), which again call func3(), the stack would contain stack frame for all currently active functions: main in the bottom of the stack, then func1, func2, and func3 will be on the top.

• In gdb, you can use command where to display the current calling stack.

The heap, or free store (not a heap, as in heap sort), is a chunk of memory set aside for processes to borrow memory from, dynamically. If you borrow it (using malloc in C, and new in C++), you have to return it (using free and delete respectively). If you don't, you cause memory leaks, which is a serious problem for long-running program as the memory available in the system will decrease overtime, degrading system performance.

In addition to the stack and heap, you get memory for your code and static data (Text as both of them cannot be modified), and for global data (Data).

b. Segmentation fault (based on wikipedia entry) A segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (for example, attempting to write to a read-only location, or to overwrite part of the ).

A few causes of a segmentation fault can be summarized as follows: • attempting to execute a program that does not compile correctly. Note that most will not output a binary given a compile-time error. • a , i.e., when you access an array with an index value less than 0, or larger than the size of the array minus 1 • using uninitialized pointers. • dereferencing NULL pointers. • attempting to access memory the program does not own. • attempting to alter memory the program does not own (storage violation). Generally, segmentation faults occur because: a pointer is either NULL, points to random memory (probably never initialized to anything), or points to memory that has been freed/deallocated/"deleted".

How to fix segmentation fault In order to fix a segmentation fault, you need to figure out which statement causes the problem. The easiest way is to use gdb.

Exercise : debug cstring.c program 1. Use gdb to run the program, and use gdb command where to figure out at which point in the execution of the program the segmentation fault occurs. 2. Sometimes the program breaks due to the fact that you are passing invalid pointer value to a call (such as printf and getchar, etc), which might in turn call other low-level system calls. You will notice this from the printed out by where command. In such case, you want to use command up to go up to the statement in your program, check the variable's value (such as array index, pointer value) at this point for problematic memory access. 3. Fix the problem.

c. Stack overflow Segmentation fault an also be caused by stack overflow. We have seen that stack is used for storing stack frames that are created for function calls, on a last-in- first-out basis. If a program has an indefinite recursive function call, stack is eventually overflown. This sometimes leads to segmentation fault problem, and sometimes leads to stack overflow problem.

Exercise : use gdb to debug factorial.c 1. Set breakpoint in the function, and use display command to see the value of n and &n (the of variable n). Note that by default, the address is displayed as a hexadecimal number (i.e., base 16 numbers). For example:

(int *) 0xbffff450

here 0x denotes it's a base 16 number, and a,b,c,d,e,f are used to represent 10,11,12,13,14,15 respectively. There is an easy conversion between binary and hexadecimal number, therefore the latter is often used for its shorter form.

2. Now rerun the program in gdb, and write down the value of n and &n for the first 6 times that the program stops in the breakpoint.

Do you see any pattern in the value of &n? How large (in number of bytes) is the stack frame for the factorial function?

In this system, does the stack grow up or down in the memory?

3. Fix the program

Optional: debug main.cc (which uses template class in C++).