DEBUGGING TECHNIQUES Aims
To enable
students to: ·
recognise
common errors ·
develop
techniques to find and eliminate bugs ·
practice
error detection and correction Recognise common
errors
Invariably
programs will contain errors. Some source estimate the rate being one for every
ten lines of code - in a project running to hundreds of thousands of lines of
code this represents a serious issue. Errors take
many forms. The hardest to resolve are those generated in the design phase.
Compilers can not pick these up, and frequently neither will developers. These
are usually the realm of product architects who have knowledge of how all the
modules making up a complex program interact. Such problems can include failure
to meet the specified requirements, missing invocations of parts of a program,
etc. Poor source
code layout can produce undesirable behaviour at run time. In code that is not
indented it is frequently difficult to get the start and end of loops to match
up, resulting in the wrong portions of code being repeated. Even code that has
indents can present similar difficulties if they are not used in a consistent
fashion. Many
compilers produce different code depending on the mode they are operating in.
When developing applications, compilers will frequently produce what is termed
‘debug’ programs that contains extra information required by debuggers to allow
portions of the code to be stepped through a line at a time within the
development environment. In such situations compilers will frequently
initialise variables with sensible values, provide padding around arrays to
catch memory overruns, etc. This behaviour can lull developers into a false
sense of security as these extras will be removed when the program is compiled
in ‘release’ mode. For example, the compiler may set integer variables to zero
which will allow the following snippet of code to behave as expected: int iBig = 100; int iSmall; while iSmall <
iBig { iSmall++; } When
compiled in release mode the initialisation of iSmall will not take place, instead the variable will contain
whatever data was previously in that location of memory, it could be a zero or
ten thousand. Likewise,
if the compiler provides buffer zones around arrays the following code will
execute without adverse in debug mode, but may have undesirable consequences
for released code. char szBuffer [5]; strcpy (szBuffer,
“012345”); Compilers
which provide buffer zones around arrays do so to enable debug routines to
detect when a memory overwrite has occurred, but the onus is on developers to
call these check routines - frequently they do not. Develop techniques
to find bugs and eliminate bugs
Several
methods should be employed in bug hunting. First, check
the design of the program to be produced matches the requirements laid down, if
in doubt verify with the customer. Always use
consistent indentation within a program to ensure loops, logical statements and
the such are correctly structured. Verify all
variables that need to be are initialised before use. Truly defensive
programming requires all variables to be provided with initial values. Use any
debug functions provided by the compiler to track down memory overwrites - such
things are often difficult to visibly spot in the code and may not manifest
themselves in the area of the program where the error occurs. Make full
use of the facilities provided by object orientation. Re-use code wherever
possible, the less new code in a project the lower the chance of a bug creeping
in. Object
orientation also allows small, free standing classes to be developed that can be
tested to destruction in isolation. Objects that are known to be bug free can
be combined together into a whole with greater confidence than elements which
are untested. If a bug
occurs, often the easiest way to find it is via the debugger, a tool designed
to allow developers to step through the execution of their programs a line of
source code at a time. Another
technique is termed the ‘binary chop’. This is used to help narrow down the
portion of a program the error occurs in. The process involves a series of
iterations, each time the amount of the program executed is chopped in half and
a note made of whether the error occurred. If an
object oriented approach is taken to programming, each class may contain debug
functions that allow their current state to be recorded. Such facilities allow
their state to be recorded throughout the execution of a program, indicating
what conditions give rise to an error. In very
large and complicated programs code should be included to allow the flow of
execution through a program to be recorded, for example whenever a function is
entered and left its name is logged in a file. As in debug functions within
classes this permits the state of a program prior to its failure to be
determined. Practice error
detection and correction
The
following code will display the numbers between 0 and 10, verify this is the
case. #include
<iostream.h> void main (){ int iCnt; while (iCnt <
100);{iCnt = iCnt + 1;} cout < iCnt;} Add debug
functions to the stack class produced in the exercises for the lecture on
classes that will permit its state to be recorded to a state file. Information
logged should include the current stack depth and an indication of the last
function executed. |
|
||
|
|||
|
Last updated: 11th July 2006. copyright © 2006 Greystoke Systems Ltd. Web address: http://www.gsys.biz/Documents/Services/Tuition/CityAndGuilds/FurtherWork/Debugging.htm |