Programming Language Concepts Using C and C++/Dynamic Memory Management

Mathematicians, when asked to define a concept, do not tend to base their answers on limitations of daily life. A set is simply a collection of distinct elements and does not impose an implied ordering among them. An empty set is as “real” as a set with three or five elements. One can add an indefinite number of elements to a set. As a matter of fact, one can speak of infinite sets.

Unfortunately, finite nature of computers does not afford computer scientists the luxury of making such wild claims. You just cannot assume your data structures to have infinite sizes. Even worse, concerns about time and/ or space performance generally force you to put on the straitjacket of static data structures: your data structures cannot have a size greater than some predefined value. But, what if you make a poor forecast? Simple: your program crashes; you rebuild your program with a different value and cross your fingers, hoping it will not happen again. Or, in a self-defeating manner, your program allocates memory that’s never used.

Picture is not so grim, though. You have an alternative: dynamic memory management. Using dynamic memory management functions, one can grow and shrink the size of data structures dynamically as needed. These functions do their job by affecting a region of memory called the heap or free store.

Growing a data structure is done by means of a function such as malloc or new. These functions basically demand memory from a part of the language runtime, called memory allocator. If it can satisfy the request memory allocator returns a pointer (or handle) to the allocated region. Otherwise, a special value (such as  or  ) or an exception object is returned, indicating failure to fulfill the allocation demand.

Shrinking the data structure is more interesting. While some programming languages provide functions to explicitly release memory, some take responsibility and do it on behalf of the programmer. The latter group is said to have [automatic] garbage collection ([automatic] GC). Memory allocators of the programming languages belonging in the first group expect programmers to take initiative by calling functions such as  or. This is bad news for sloppy programmers. Not releasing unused memory is equal to wasting memory and will eventually give rise to crashes; as more and more memory is allocated and none is reclaimed, the pool of usable memory is shrunk and the allocator finds itself in a situation where it cannot satisfy an allocation request, which inevitably leads to the aforementioned result. Such unused memory is referred to as garbage. Converse of this, early releasing of memory, is equally disastrous. You are now faced with the danger of using a pointer that does not point to valid data. Such a pointer is called a dangling pointer.

So, why do people- at least some- keep using languages without garbage collection? Or, cannot they simply incorporate this facility into such a language as C? Answer to the first question is performance. Executing as a background thread (or process), garbage collector will take over either when all other threads are in a wait state or the program is out of memory and next allocation demand can only be satisfied by some garbage that is yet to be reclaimed. This is a time-consuming process and won’t complete in a few machine cycles; as part of its execution, garbage collector will sweep through the entire heap memory and mark unused regions as garbage. This means invocation of the garbage collector will give rise to substantial drops in performance. On the other hand, explicit de-allocation by means of functions such as  or   enable the programmer to return memory as it becomes unused. Although it is still an option there is no designated phase in the program where cycles are exclusively spent for returning unused memory in a wholesale fashion. This is depicted in the figure below.



As for the second question, the answer is negative. Being able to refer to the same memory region through different pointers, liberal use of casting in C makes it impossible for the allocator to figure out the contents of the memory. What if this region initially had some other pointer in it and now seen as a collection of raw bytes? So tracking down the pointer through memory to find unused regions and therefore garbage collection becomes impossible.

Module
In this example, we provide an implementation for character strings as an opaque type. On top of the previous presentation (Object-Based Programming), two levels of pointers in the representation of this type present us with the chance to emphasize correct allocation and de-allocation orders.

Interface
String.h

Yet another forward declaration and its companion ! The former is used to forewarn the compiler about the existence of the record type that we will be working on and the latter provides a handle on an instance of such a record type. A type manipulated this way is called an opaque type; access to its instances is mediated by a pointer.

Observe that all formal parameters provided in the prototypes are '[constant] pointers to ', not ' '. The reasoning for this is as follows: as a natural consequence of the information hiding principle, we should keep the record type representation as an implementation detail: the user of a facility need not concern herself about a detailed answer to the question "how" (implementation); she had better concentrate on finding an answer to the question "what" (interface). This can be accomplished by giving her things that do not change; things that may change should be made part of the implementation, not the interface. And size of pointer to a record type remains the same although size of the record type itself may change.

Implementation
String.c

In addition to the string [of characters] our representation provides the length of the string as a member field. By doing this we trade space for time: when asked the length of a string, instead of going through all the characters till we see the terminating  character, we return the value of this member field. In other words, we prefer computation in space to computation in time.

In addition to the static data area, where data that remain alive throughout the program execution reside, and the runtime stack, where variables local to blocks live; we are provided with a pool of memory we can use during program execution. This memory region is called the program’s free store or heap. Unlike the other two areas, this area is managed by the programmer by means of dynamic memory management functions.

One aspect of free store memory is that it is unnamed. Objects allocated on the free store are manipulated indirectly through pointers. A second aspect of the free store is that the allocated memory is un-initialized.

Allocation of memory at run time is referred to as dynamic memory allocation; memory allocated by means of a function such as malloc is said to be allocated dynamically. However, this does not mean that storage for the pointer itself is allocated dynamically. It might well be the case that it is allocated statically.

Definition: An object’s lifetime–that period of time during program execution when storage is bound to the object–is referred to as an object’s extent.

Variables defined at file scope are spoken of as having static extent. Storage is allocated before program start-up and remains bound to the variable throughout program execution. Variables defined at local scope are spoken of as having local extent. Storage is allocated on the run-time stack on entry to the local scope; on exit, this storage is freed. A static local variable on the other hand exhibits static extent.

Objects allocated on the free store are spoken of as having dynamic extent. Storage allocated through the use of functions like  in C remains bound to an object until explicitly deallocated by the programmer.

returns a generic pointer,  or the subscripting operators. In order to use this pointer to productive ends we must cast the returned value to some particular pointer type. Assuming  can satisfy the request, local variable   contains an address value that is to be interpreted to indicate the starting address of a   object. Otherwise, it contains a polymorphic value,, that signifies the failure of.

In case there may not be enough memory left in the heap,  will be returned by the   function. We should let the user know about it.

Upon successful allocation of memory for the metadata, which is a combination of the pointer to the string contents and its length, we must allocate room for the string contents. We do this by using the  function for a second time. Eventually, if everything goes right, we have the figure given below.

Next loop and the two lines following it are used to copy a string of characters into another memory region. We first, in the for loop, copy as many characters as the length of the string and then append the terminating  character. Finally, now that we have moved the pointer in the process and it points to the  character at the end of the string, we get it to point to the beginning of it using pointer arithmetic on the last line.

Note that we could have done this using the standard string function. Along with, we are given an assortment of string functions. In addition to these, since a string of characters is a memory region ending with the sentinel value of ‘\0’, we can deal with such an entity using the appropriate memory function. A partial list of such functions is provided below.

It is now time to release the area of memory pointed to by  and return it to the free store for re-use. In C, this can be done through a couple of functions, one of which is.

Our destructor is admittedly very simple. It turns out that heap memory is the only resource used by the  data type. It may not be the case for other data types. They may hold handles to resources like files, semaphores, and so on.

Given the definition of, we have the following memory layout:

is what is called a handle. The use of a pointer, in other words indirection, is needed to isolate the user from probable changes in the representation. Even when the implementer changes the underlying representation, the user will not be affected. Because she does not have direct access to the representation. All she has is a ‘handle on a  object’, not the   object itself.

The region pointed to by a pointer should be deallocated only when everything pointed to by the pointers in the region have been deallocated. That’s why we should first deallocate  and then. One thing to keep in mind: it is the region that is pointed by the pointer that gets freed, not the pointer itself!

By the time control reaches the return statement, we get the picture below. Shaded regions indicate memory returned to the allocator; they may be re-used in subsequent allocation requests. For this reason, it is not wise trying to use released memory.

Observe the signature of the destructor: The sole argument is of type, not. This modification is made to ensure that assignment of  to the handle of the deleted object is permanent. The reason why we make this assignment in the destructor is to relieve user(s) of having to do it after the call to the destructor, which is&mdash;since there are many users who are concerned about what is provided by the module and one implementer who should be concerned about how the module is implemented&mdash;certainly more error prone.

Our representation has two levels: one for the information about the underlying string and one for the string itself. This means we need to issue two separate memory allocation commands, one for each level. So, at this point in the program we have the memory layout given above. There are random values in the  and   fields, which are probably left over from previous uses of the areas allocated to them.

By the time control reaches this point we will have assigned the accurate value to the  field and allocated enough memory to hold the concatenation of the two arguments. Note that content pointed to by  is random. This doesn’t mean that they cannot be used though. Application of  function on   will still yield legitimate values, depending on where the first ‘/0’ appears in memory.

Finally we are over with copying the first argument into the resultant. Well, almost! Each time we copy a character from  into , we advance the pointer so that it points to the next character to be copied. By the time we reach the end of the source , both pointers will be greater than their original contents by as much as the length of the source string. As far as  is concerned this is OK since a second string is yet to be appended to it. But the original value of  is to be restored, which is done by the following compound assignment statement.

We could have done away with this readjustment had we used a temporary pointer to point to the same location as  and copy the source   using this temporary pointer. The code for this is as follows:

Having copied the argument s into the resultant   we now return to the caller. Note that we return a pointer to some object that resides in heap. This gives us the opportunity to share the same object among function calls, something that we wouldn’t have had had we chosen to use an object that resides in the run-time stack: An object in the run-time stack is accessible only in the function it is created and those functions that are directly and indirectly called from this function.

This may lead us to think that returning a pointer to some object in the static data region or the frame of main function is a safe course. As far as lifetime of the object is concerned that’s right. But we now face the limitation imposed by the static nature of the allocation: Objects allocated in these regions cannot dynamically change in size. Size of an object created in the static data region is fixed at compile-time while an object created in the run-time stack must have its size fixed at the point its definition is elaborated. This implies the only way of implementing dynamic data structures is through the use of heap memory. ,

Backward search can be formulated in terms of forward search. That’s exactly what we do here. We make a forward search of the reversed substring in the reversed string and return a value depending on the result of this search.

The following is what we call a user-defined conversion function: it converts from  to. Unlike C++, where the compiler implicitly calls such functions, this conversion function must be explicitly invoked by the programmer.

Test Program
String_Test.c

For printing the contents of a  variable, we convert it to a C-style character string and pass this to the   function. But we have to be careful about avoiding garbage creation. If we pass the return value, a pointer to, of   to   without storing it in a variable, it will be lost by the time   returns. This will turn the memory region pointed to by this pointer into garbage. Something we don’t want! For this reason, we first assign the return value to some temporary variable and then send it to the  function. As soon as  returns, region pointed to by the temporary variable is freed using.

Debugging a Program with GDB
A program compiled and linked with no errors does not necessarily mean that everything is okay. A logical error may have crept into the program and our executable will possibly produce wrong results. In such cases, we should go back to the source code and try to fix the glitch. The problem with this approach is the difficulty of deciding where to begin the search for the malicious bug. What if we are working on a large project involving the interaction of hundreds of functions and the logical error in question rears its head far away from its origin? We had better find an easier way!

A debugger is the answer to our worries. It lets us walk through the problematic software, modify the code on the fly, and pinpoint the potential trouble spots. What follows is an introduction to the GNU Debugger, GDB.

Compiling Source Programs for Debugging
In order to debug a program, you need to generate debugging information when you compile it. This debugging information is stored in the object file; it contains meta-data to describe such things as the type of each variable and function, the correspondence between source line numbers and addresses in the executable code.

To request insertion of debugging information into the object code, you must specify the ‘-g’ option when you run the compiler. According to this, String_Test.c would be run by the following commands:


 * String_Test↵
 * String_Test↵
 * String_Test↵

[Before moving ahead it should be stressed that&mdash;since certain optimizations involve code elimination/modification, which means the source code you debug and what the debugger shows may not match each other&mdash;enabling optimization together with the debugging option is not a good idea. Therefore, it is well-advised that you first turn off optimization (and turn on the debugging switch) and then, when you are convinced the code is throughly tested and it is time to hit the market-place, turn optimization on.]

In case something may go wrong in the program, issue the command given on the next line.



This command will put you in the GDB environment and wait for you to issue GDB commands. You can also debug a crashed process and try to figure out what went wrong by passing the core dump of the process as an extra argument. One can also monitor a running process. So, the  command can be summarized as follows:



As can be seen from the above specification,  can be issued without any arguments. In such a case, you have to supply the argument values by means of GDB commands.


 * &equiv;
 * GNU gdb 5.0
 * Copyright 2000 Free Software Foundation. Inc.
 * (gdb) file String_Test.exe↵
 * Copyright 2000 Free Software Foundation. Inc.
 * (gdb) file String_Test.exe↵
 * (gdb) file String_Test.exe↵

Similarly, a core dump can be supplied with the core-file command and GDB can attach itself to a process using the attach command.

Major GDB Commands
A command is a single line of input with no limit to its length. It starts with a command name and is followed by arguments whose meaning depends on the command name.

GDB command names may always be truncated if that abbreviation is unambiguous. A time-saver for frequently used commands, an abbreviation with no ambiguous interpretation will surely execute the intended command.

Entering a blank line as input to GDB (typing just RET) generally means to repeat the previous command. # is used to start a single line comment; anything from # to the end of the line is treated as a comment and therefore discarded.

Getting Help from GDB
With so many commands in GDB, one may have difficulty remembering a particular one. In such cases you may ask GDB for information on its commands. All you need to know is the <tt>help</tt> command, which comes in three flavors:


 * h elp
 * Displays a short list of command classes.
 * h elp command_class
 * Displays a list of commands found in a particular command class.
 * h elp command
 * Displays a short paragraph on how to use a specific command.


 * ap ropos reg_exp
 * Searches through all the GDB commands and their documentation for the regular expression specified in reg_exp. For example,


 * (gdb) apropos local var*↵
 * backtrace -- Print backtrace of all stack frames
 * bt -- Print backtrace of all stack frames
 * info locals -- Local variables of current stack frame
 * info locals -- Local variables of current stack frame
 * where -- Print backtrace of all stack frames


 * compl ete command_prefix
 * Lists all possible commands that start with command_prefix.

GDB can fill in the rest of a word in a command for you, if there is only one possibility; it can also show you what the possibilities are for the next word in a command, at any time. In order to get gdb to do command completion you must press the TAB key. In case there is no ambiguity gdb will fill in the word and wait for you to finish the command. Otherwise, it will sound a bell telling you about the possibilities. At this point you can either supply more characters and try again, or just press TAB a second time. Pressing TAB a second time displays all command names starting with the prefix you have entered, which is actually what is offered by the complete command. For example,


 * (gdb) h TAB TAB
 * handle hbreak  help
 * (gdb) he TAB &rarr; (gdb) help

Stopping Execution
Unless you tell otherwise, once a program starts it runs to completion. For a program being debugged this sort of behavior is not very helpful. We should be able to stop at certain points to check program state, iterate through the program statements, and probably modify code by means of interfering with the control flow of the program.

Stopping at certain points of the program can be achieved by setting breakpoints. In GDB, this is done by the <tt> b reak</tt> command. <tt>break</tt> can be used in many different ways.


 * b reak (func_name | filename:func_name)
 * Sets a breakpoint at entry to the function named func_name [of the file named filename].
 * b reak (line_num | filename:line_num)
 * Sets a breakpoint at line line_num [of the file named filename].
 * b reak *address
 * Sets a breakpoint at address address. * prefix is used to signal that what comes next is to be interpreted as an address value.
 * b reak (+ | -) offset
 * Using the argument value as an offset, this command sets a breakpoint relative to the current source line.
 * b reak
 * Sets a breakpoint at the next instruction to be executed. Equivalent to <tt> b reak +0</tt>.
 * b reak location if condition
 * Sets a breakpoint at a location, which can be provided in different ways as described above. GDB will take control of the program (that is, the program will stop) only if condition evaluates to a non-zero value. Note that the unconditional versions of break can be expressed using a conditional that always evaluates to a nonzero value. That is, previous break commands are equivalent to <tt> b reak</tt> location <tt> if </tt> n, where n is a non-zero value.

Above group of alternative usages can be compactly defined as


 * b reak [location] [ if condition]

where [ and ] are used to express zero or one instance of the enclosed syntactic units.


 * tb reak location [ if condition]
 * Sets a temporary breakpoint, which will be deleted after the first time the program stops at location specified as the argument. Typically, one would put such a breakpoint at the entry point to a program.


 * hb reak location [ if condition]
 * Sets a hardware-assisted breakpoint. Due to the lack of hardware support, GDB may not be able to set this type of breakpoints. Even when it is supported, the number of registers used for this purpose may not be enough to meet your demands. In such a case, you have to delete or disable an unused breakpoint and reuse it for the new one.


 * th break location [ if condition]
 * Sets a temporary, hardware-assisted breakpoint.


 * rb reak reg_exp
 * Sets breakpoints on all functions matching the regular expression reg_exp. Keep in mind that there is an implicit ".*" leading and trailing the regular expression you supply. So, issued while debugging String_Test.exe,


 * rb reak .*Cont.*↵ and  rb reak Cont↵


 * are equivalent and will both set breakpoints at  and.

A watchpoint is a special breakpoint that stops your program when the value of an expression changes. This relieves you from the burden of predicting places where such changes may occur. It comes in three flavors:


 * wa tch expr
 * Sets a watchpoint that will break whenever expr is written into by the program and its value changes.


 * rw atch expr
 * Sets a watchpoint that will break whenever expr is read by the program.


 * aw atch expr
 * Sets a watchpoint that will break whenever expr is accessed (read or written into) by the program.

A catchpoint is another special breakpoint that stops your program when a certain kind of event occurs, such as the throwing of a C++ exception or the loading of a library.

An unconditional breakpoint, be that a breakpoint, watchpoint, or catchpoint, can be turned into a conditional one by means of the <tt>condition</tt> command.


 * cond ition breakpoint_num [expression]
 * Adds the expression as a condition for the breakpoint specified by breakpoint_num. If there is no expression part, then any condition set for the breakpoint is removed. That is, it becomes an ordinary unconditional breakpoint.

Here, breakpoint number is an index used to refer to a particular breakpoint. It can be found out by issuing the <tt> i nfo b reakpoints</tt> command.

One can give any breakpoint a series of commands to execute when the program stops due to that breakpoint.


 * comm ands [breakpoint_num]
 * command1
 * command2
 * commandn
 * end
 * Missing breakpoint number will cause the commands to be attached to the last breakpoint set (not encountered!). Following <tt> comm ands</tt> and breakpoint number with <tt> end </tt> can easily accomplish removal of a command list from a breakpoint.
 * Missing breakpoint number will cause the commands to be attached to the last breakpoint set (not encountered!). Following <tt> comm ands</tt> and breakpoint number with <tt> end </tt> can easily accomplish removal of a command list from a breakpoint.

Effect of <tt>commands</tt> can be partially obtained by <tt>display</tt> command, which adds its argument expression to a so-called automatic display list. All items in this list are printed each time the program is stopped.

Having figured out the problem in our program we may want to remove a breakpoint. This can be done by using <tt>clear</tt> and <tt>delete</tt> commands.


 * cl ear
 * Removes breakpoints set at the instruction about to be executed.
 * cl ear (function | filename:function)
 * Removes the breakpoint set at the entry of the function passed as the argument.
 * cl ear (line_num | filename:line_num)
 * Removes any breakpoint set at or within the code of the specified line.


 * d elete [ b reakpoints] [list_of_breakpoints]
 * Removes the breakpoints passed as arguments. With no argument, it deletes all the breakpoints.

Instead of removing a breakpoint we may choose to ignore or disable it. Such a breakpoint will be present but not effective, awaiting its revival.


 * ig nore breakpoint_num ignore_count
 * Causes the breakpoint referred to by breakpoint_num to be bypassed ignore_count times.


 * dis able [ b reakpoints] [list_of_breakpoints]
 * Causes the given breakpoints to be ignored till a relevant enable command. If no list is supplied, all breakpoints are disabled.


 * en able [ b reakpoints] [list_of_breakpoints]
 * Activates the previously disabled list of breakpoints.
 * en able [ b reakpoints] once list_of_breakpoints
 * Activates the given list of breakpoints for only once.
 * en able [ b reakpoints] delete list_of_breakpoints
 * Activates the given list of breakpoints to work once, and then die. GDB will delete any breakpoint in the list as soon as the program stops there.

Resuming Execution
Once a program has been stopped, it can be resumed in different ways. Following is a list of such commands:


 * n ext [no_of_repetitions]
 * Continues to the next source line. If the current line to be executed contains a function call, it is executed in one single step without going into it. In other words, it never increases the depth of the run-time stack. Supplied argument tells GDB the number of times to execute the <tt> n ext</tt> command.


 * s tep [no_of_repetitions]
 * Continues to the next source line. Unlike <tt> n ext</tt>, if the current line to be executed contains a function call, a new stack frame will be inserted and control will flow into the very first executable statement in the called function.


 * n ext i [no_of_repetitions]
 * Executes the next machine instruction and returns to the debugger. If the next instruction is a function call, it executes the entire function and then returns.


 * s tep i [no_of_repetitions]
 * Executes the next machine instruction and returns to the debugger.


 * c ontinue [ignore_counts]
 * Continues running the program up to the next breakpoint. When passed an argument, it is taken to mean the number of times the debugger will ignore the breakpoint set at the most recently encountered location for ignore_count – 1 times. So, continue is equivalent to <tt> c ontinue</tt> 1.


 * u ntil [location]
 * When passed an argument, <tt> u ntil</tt> continues running the program either until the specified location or end of the current function is reached. When used without an argument, location is assumed be the current line. It is very useful for avoiding stepping through a loop.


 * fin ish
 * Continues running the current function to completion.


 * ret urn [ret_value]
 * Returns immediately to the caller without executing the remaining statements in the callee&mdash;that is, the current function. If it’s a value-returning function, ret_value is interpreted as the return value of the callee.


 * cal l expr
 * Evaluates the expression passed as its only argument without changing the current location. Note that side-effects due to evaluation is permanent.


 * j ump (line_no | *address)
 * Resumes execution at line or address specified in the argument.

The diagram below shows the effect of these commands. A dashed line is used to indicate a change in state where effect of the transition is achieved by visiting all the intermediate states. For example, “finishing f1” means executing the remaining statements in f1 and then returning, which is suggested by the dashed line. “returning from f2”, however, is shown by a straight line, meaning it skips all statements in f2 and returns control to.



Tracing a Running Program
There are times when the very act of stopping the program may affect the correctness of the program or may degrade the quality of service. An example to the former is a real-time program, such as those found in embedded systems; an example to the latter is a server that should stay up 24 hours a day, 7 days a week. In such cases we may choose to use the <tt> tr ace</tt> and <tt> col lect</tt> commands found in GDB. Using these and a few other commands, we can specify locations in the program and cause certain actions, such as data collection, to take place at these locations. Such locations are called tracepoints. Data collected at the tracepoints are later inspected to see what goes wrong within the program.


 * tr ace location
 * Sets a tracepoint at the specified location. Setting a tracepoint or changing its commands doesn’t take effect until the next <tt> tstar t</tt> command.


 * tstar t
 * Starts the trace experiment and begins collecting data.


 * tstat us
 * Displays the status of the current trace data collection.


 * i nfo tr acepoints [tracepoint_num]
 * Displays information about a particular tracepoint. Without an argument it displays information about all tracepoints defined so far. Among other information items, it provides the system assigned number of each tracepoint.


 * ac tions [tracepoint_num]
 * Defines a list of actions to be taken when the tracepoint numbered tracepoint_num is hit. With no arguments, tracepoint most recently defined will be used.


 * There are commands special to an <tt> ac tion</tt> block. These are <tt> col lect</tt> and <tt> w hile- s tepping</tt>.


 * col lect expr [, expression_list]
 * Collects values of the given expression(s) when the tracepoint is hit. In addition to expressions formed according to the syntax rules of the source language, you can use one of the following.


 * $regs     Collects all registers.
 * $args     Collects all function arguments.
 * $locals   Collects all local variables.


 * while-stepping n
 * Performs n single-step traces after the tracepoint, collecting new data at each step.

Once enough data has been collected we may either stop the experiment, not the program, explicitly or get GDB to stop on its own using the passcount of each tracepoint.


 * tsto p
 * Ends the trace experiment and stops collecting data.


 * pas scount [n [tracepoint_num]]
 * Sets the passcount of a tracepoint. Passcount is the maximum number of hits a tracepoint can take. So, setting the passcount of a tracepoint to n will stop the trace experiment at or before the nth time that particular tracepoint is hit. How many times the other tracepoints may have been hit and whatever their passcounts may be, the very first tracepoint to be hit as many times as its passcount will stop the trace experiment. Missing tracepoint number assumes the most recently defined tracepoint. With no passcount value, the trace experiment will go on till it is stopped explicitly by the user.

Data collected each time a tracepoint is hit is called a snapshot. These snapshots are consecutively numbered from zero and go into a buffer so that you can examine them later.


 * tf ind snaphot_num
 * Retrieves the trace snapshot numbered snapshot_num. Argument can be specified in a number of ways as given below:


 * start
 * Finds the first snapshot in the buffer. It’s a synonym for 0.
 * none
 * Stops debugging snapshots and resume live debugging.
 * end
 * Same as none.
 * ENTER
 * Finds the next snapshot in the buffer.
 * Finds the previous snapshot in the buffer.
 * tracepoint_num
 * Finds the next snapshot associated with tracepoint_num.
 * pc addr
 * Finds the next snapshot whose pc (program counter) value is addr.
 * outside addr1, addr2
 * Finds the next snapshot whose pc is outside the given range.
 * line [filename:]n
 * Finds the next snapshot associated with a certain source line.
 * Finds the next snapshot associated with a certain source line.

A trace experiment typically involves the following steps.


 * 1) Set a tracepoint using the <tt> tr ace</tt> command.
 * 2) Using <tt> ac tions</tt>, attach a list of actions to the tracepoint.
 * 3) Set a passcount for the tracepoint, if needed.
 * 4) Repeat 1-3 for all other data collection points.
 * 5) Start the trace experiment by using <tt>tstart</tt>.
 * 6) Use <tt> tsto p</tt> to stop the trace experiment or wait for a tracepoint to stop it (trace experiment) by reaching its (tracepoint’s) passcount.
 * 7) Use <tt> tf ind</tt> to inspect values collected during the experiment.

Examining the Program
Each time the program being debugged stops gdb prints the line to be executed. This may not be sufficient to give you an idea about the context. In such a case you need to get a listing of the program lines around the current line. You can do this by using the <tt> l ist</tt> command.


 * l ist [line]
 * When passed an argument, <tt> l ist</tt> prints lines centered on the line specified by the argument. This value can be specified by giving a line number [in a particular file], function name [in a specific file], an address, or an offset. With no argument, <tt> l ist</tt> prints lines following the last lines printed. Passing + as argument has the same effect with passing no argument. Passing - as argument causes listing of the lines coming just before the lines last printed.


 * l ist first_line, last_line
 * When passed two arguments, <tt> l ist</tt> prints source lines between the first and second arguments. If either of the arguments is missing, with ‘,’ still present, this missing value is figured out by the other argument.

In addition to listing parts of the source file you may want to see the value of a variable or evaluate an expression.


 * p rint [/format] expression
 * Evaluates expression and prints the result using format. Format specification can be any one of the following:


 * d   Interpret the value as an integer and print in signed decimal.
 * u	Interpret the value as an integer and print in unsigned decimal.
 * x	Interpret the value as an integer and print in hexadecimal.
 * o	Interpret the value as an integer and print in octal.
 * t	Interpret the value as an integer and print in binary.
 * c	Interpret the value as an integer and print it as a character constant.
 * a   Treat the value as an address and print.
 * f   Print as a floating value.


 * A missing format specification will have GDB use an appropriate type. <tt> ins pect</tt> is a synonym of this command.


 * Expressions can be formed according to the syntax rules of the source language. We can extend this syntax using certain operators. <tt>@</tt> treats parts of memory as an array; <tt>::</tt> allows you to specify the scope of an identifier. For example, given that  is of  ,


 * <tt> p rint *sum_all::seq@10</tt>↵


 * sees the area of memory pointed to by, which is local to a function named  , as an array of size 10 and prints this array.

Each time an expression is evaluated and printed it is also saved in what is called value history. Values found in this history can be referred to by prefixing its order of printing by the <tt>$</tt> sign. First value to be printed is <tt>$1</tt>; second value is <tt>$2</tt> and so on. A single <tt>$</tt> refers to the most recently printed value, and <tt>$$</tt> refers to the value before that. <tt>$$</tt>n refers to the nth value from the end. So, <tt>$$0</tt> is equivalent to <tt>$</tt>. One thing to keep in mind: value history holds values of expressions, not expressions themselves. That is, given that <tt>*name</tt> holds "Mete" as its value, what gets stored after printing <tt>*name</tt> in the value history is "Mete", not "*name".

Values in the value history can be inspected using the <tt> va lues</tt> subcommand of <tt> sho w</tt>.


 * sho w va lues [ n | + ]
 * Prints ten entries of value history centered on n. If no argument is passed, it prints the last ten values. When supplied a + as the argument, this command prints ten history values just after the values last printed.

If you do not want to record the result of the evaluation in the value history, you should use the <tt> x </tt> commands.


 * x [[/repeat_count format unit] address]
 * Examines memory starting at address, independently of the underlying real data type. In addition to those used in <tt> p rint</tt>, format can be s (for strings) or i (for machine instructions). repeat_count and unit are used to determine how much memory to display: repeat_count * unit bytes. repeat_count is a decimal value while unit can be one of the following:


 * b   Byte.
 * h   Halfword (two bytes).
 * w   Word (four bytes).
 * g   Giant words (eight bytes).


 * Note that any one of the parameters might be missing. Format defaults to x initially and changes each time you use either <tt> x </tt> or <tt> p rint</tt>. Repetition count defaults to 1. Unit size specified for a particular value is accepted as a default next time same format is used.

When your program stops, you may want to know where it stopped and how it got there. This requires information about the run-time stack, which are provided by the following commands. Note that these commands do not change the control flow of the program. That is; issuing <tt> n ext</tt> after a combination of the following commands will still continue execution from the most recently hit breakpoint. In other words, none of the following commands modifies the instruction pointer.


 * f rame [frame_no | address]
 * Moves you from one stack frame to another and prints information about the stack frame you select. Stack frame can be selected by passing either the address or number of the frame. frame_no is a non-negative number, where the currently executing frame is 0, its caller is 1, and so on.


 * b ack t race [[(- | +)] number_of_frames]
 * Prints a summary of how your program got where it is. When used with no argument, this command prints a backtrace of the entire stack; with a positive number, it prints the innermost (most recent) n stack frames; with a negative value passed as an argument, it prints the least recent n stack frames. A synonym for this command is <tt> i nfo s tack</tt>.


 * up [n]
 * Moves n frames up the stack. That is, it moves toward less recently created frames. Default value for n is 1.


 * do wn [n]
 * Moves n frames down the stack. In other words, it moves toward more recent stack frames.

In addition to these, we may get further information through the use of <tt> i nfo</tt> subcommands.


 * i nfo f rame [address]
 * Prints a description of the frame without selecting it.


 * i nfo arg s
 * Prints the arguments of the selected frame.


 * i nfo loc als
 * Prints the local variables of the selected frame.


 * i nfo va riables [reg_exp]
 * Displays all globals and static variable names used in the program. When passed an argument, it displays all such information items matching the regular expression.


 * i nfo sc ope (function_name | *address | line_num)
 * Lists the variables local to a scope. This listing includes the address of arguments and local variables relative to frame base.

Debuggers with Graphical User Interface
If you are not in for the extra flexibility of the [incomplete!] command list presented in the previous section, you may want to try one of the following debuggers, which basically put a graphical interface on top of GDB.

In addition to these, you can try GNU Emacs, XEmacs, Eclipse, KDevelop, NetBeans and MS Visual Studio, which also offer integrated debugging support. One final thing you need to keep in mind: not all debuggers/environments support all debugging formats. For instance, an executable containing GDB debugging information will not make any sense to MS Visual Studio.