Showing posts with label Stack. Show all posts
Showing posts with label Stack. Show all posts

Saturday, August 16, 2008

Memory Layout of a C Program - Stack Wise



high high --------------
        |               |
        | Arguments and |
        |  environment  |
        |   variables   |
        |               |
        |---------------|
        |     Stack     |<--|--
        |(grow downward)|   |
        |               |   |User
        |               |   |Stack
        |               |   |Frame
        |               |   |
        | (grow upward) |   |( Mind the Gap )
        |      Heap     |<--|--
        |---------------|
        |      BSS      |<-- uninitialized static data(block started by symbol)
         |               |      long  sum[1000];
        |---------------|
        |      Data     |<-- initilized static data(int   maxcount = 99)
        |---------------|
        |      Code     |<-- text segment machine instructions

low
Stack : where automatic variables are stored, along with information that is
saved each time a function is called. Each time a function is called, the
address of where to return to and certain information about the caller's
environment, such as some of the machine registers, are saved on the stack. The
newly called function then allocates room on the stack for its automatic and
temporary variables. This is how recursive functions in C can work. Each time a
recursive function calls itself, a new stack frame is used, so one set of
variables doesn't interfere with the variables from another instance of the
function.
Text Segment: The text segment contains the actual code to be executed. It's
usually sharable, so multiple instances of a program can share the text segment
to lower memory requirements. This segment is usually marked read-only so a
program can't modify its own instructions.
Initialized Data Segment: This segment contains global variables which are
initialized by the programmer.
Uninitialized Data Segment: Also named "bss" (block started by symbol) which
was an operator used by an old assembler. This segment contains uninitialized
global variables. All variables in this segment are initialized to 0 or NULL
pointers before the program begins to execute.
The stack: The stack is a collection of stack frames which will be described in
the next section. When a new frame needs to be added (as a result of a newly
called function), the stack grows downward.
Every time a function is called, an area of memory is set aside, called a stack frame,
for the new function call. This area of memory holds some crucial information, like:
1. Storage space for all the automatic variables for the newly called function.
2. The line number of the calling function to return to when the called function
returns.
3. The arguments, or parameters, of the called function.
The heap: Most dynamic memory, whether requested via C's malloc() and friends
or C++'s new is doled out to the program from the heap. The C library also gets
dynamic memory for its own personal workspace from the heap as well. As more
memory is requested "on the fly", the heap grows upward.

Sunday, June 29, 2008

Debugging with GDB ( Stack and frames )

A debugger lets you pause a program, examine and change variables, and step through code. Spend a few hours to learn one so you can avoid dozens of hours of frustration in the future.

Getting Started: Starting and Stopping

  • gcc -g myprogram.c
    • Compiles myprogram.c with the debugging option (-g). You still get an a.out, but it contains debugging information that lets you use variables and function names inside GDB, rather than raw memory locations (not fun).
  • gdb a.out
    • Opens GDB with file a.out, but does not run the program. You’ll see a prompt (gdb) - all examples are from this prompt.
  • r
  • r arg1 arg2
  • r <>
    • Three ways to run “a.out”, loaded previously. You can run it directly (r), pass arguments (r arg1 arg2), or feed in a file. You will usually set breakpoints before running.
  • help
  • h breakpoints
    • List help topics (help) or get help on a specific topic (h breakpoints). GDB is well-documented.
  • q - Quit GDB

Stepping Through Code

Stepping lets you trace the path of your program, and zero in on the code that is crashing or returning invalid input.

  • l
  • l 50
  • l myfunction
    • List 10 lines of source code for current line (l), a specific line (l 50), or for a function (l myfunction).
  • next
    • Run program until next line, then pause. If the current line is a function, execute the entire function, then pause. Next is good for walking through your code quickly.
  • step
    • Run the next instruction, not line. If the current instructions is setting a variable, it is the same as next. If it’s a function, it will jump into the function, execute the first statement, then pause. Step is good for diving into the details of your code.
  • finish
    • Finish executing the current function, then pause (also called step out). Useful if you accidentally stepped into a function.

Breakpoints and Watchpoints

Breakpoints are one of the keys to debugging. They pause (break) a program when it reaches a certain location. You can examine and change variables, then resume execution. This is helpful when seeing why certain inputs fail, or testing inputs.

  • break 45
  • break myfunction
    • Set a breakpoint at line 45, or at myfunction. The program will pause when it reaches the breakpoint.
  • watch x == 3
    • Set a watchpoint, which pauses the program when a condition changes (when x == 3 changes). Watchpoints are great for certain inputs (myPtr != NULL) without having to break on every function call.
  • continue
    • Resume execution after being paused by a breakpoint/watchpoint. The program will continue until it hits the next breakpoint/watchpoint.
  • delete N
    • Delete breakpoint N (breakpoints are numbered when created).

Setting Variables and Calling Functions

Viewing and changing variables at run-time is a huge part of debugging. Try giving functions invalid inputs or running other test cases to find the root of problems. Typically, you will view/set variables when the program is paused.

  • print x
    • Print current value of variable x. Being able to use the original variable names is why the (-g) flag is needed; programs compiled regularly have this information removed.
  • set x = 3
  • set x = y
    • Set x to a set value (3) or to another variable (y)
  • call myfunction()
  • call myotherfunction(x)
  • call strlen(mystring)
    • Call user-defined or system functions. This is extremely useful, but beware calling buggy functions.
  • display x
  • undisplay x
    • Constantly display value of variable x, which is shown after every step or pause. Useful if you are constantly checking for a certain value. Use undisplay to remove the constant display.

Backtrace and Changing Frames

The stack is a list of the current function calls - it shows you where you are in the program. A frame stores the details of a single function call, such as the arguments.

bt

Backtrace, aka print the current function stack to show where you are in the current program. If main calls function a(), which calls b(), which calls c(), the backtrace is

<= current location
b
a
main
  • up
  • down
    • Move to the next frame up or down in the function stack. If you are in c, you can move to b or a to examine local variables.
  • return
    • Return from current function.

Crashes and Core Dumps

A “core dump” is a snapshot of memory at the instant the program crashed, typically saved in a file called “core”. GDB can read the core dump and give you the line number of the crash, the arguments that were passed, and more. This is very helpful, but remember to compile with (-g) or the core dump will be difficult to debug.

  • gdb myprogram core
    • Debug myprogram with “core” as the core dump file.
  • bt
    • Print the backtrace (function stack) at the point of the crash. Examine variables using the techniques above.
This is a quick guide, more information here: