0% found this document useful (0 votes)
2 views49 pages

08_Debugging1

This document is a lecture on debugging techniques and strategies, emphasizing the importance of understanding error messages, thinking before writing code, and using systematic approaches like 'divide and conquer' to identify bugs. It outlines the goals of debugging, differentiates between testing and debugging, and provides a structured agenda for learning effective debugging practices. The document also includes quotes from notable figures about the challenges of debugging and highlights the significance of internal tests in the debugging process.

Uploaded by

findustries00
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views49 pages

08_Debugging1

This document is a lecture on debugging techniques and strategies, emphasizing the importance of understanding error messages, thinking before writing code, and using systematic approaches like 'divide and conquer' to identify bugs. It outlines the goals of debugging, differentiates between testing and debugging, and provides a structured agenda for learning effective debugging practices. The document also includes quotes from notable figures about the challenges of debugging and highlights the significance of internal tests in the debugging process.

Uploaded by

findustries00
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Debugging  

(Part  1)
Aarti  Gupta

The material for this lecture is drawn, in part, from


The Practice of Programming (Kernighan & Pike) Chapter 5
1
For  Your  Amusement
“When  debugging,  novices  insert  corrective  code;;  experts  
remove  defective  code.”
-­-­ Richard  Pattis
“If  debugging  is  the  act  of  removing  errors  from  code,  what's  
programming?”
-­-­ Tom  Gilb
“Debugging  is  twice  as  hard  as  writing  the  code  in  the  first  
place.  Therefore,  if  you  write  the  code  as  cleverly  as  
possible,  you  are,  by  definition,  not  smart  enough  to  debug  
it.”
-­-­ Brian  Kernighan
2
For  Your  Amusement

The  first  computer  bug


(found  in  the  Harvard  Mark  II  computer) 3
“Programming  in  the  Large” Steps
Design  &  Implement
• Program  &  programming  style    (done)
• Common  data  structures  and  algorithms
• Modularity
• Building  techniques  &  tools    (done)

Test
• Testing  techniques    (done)

Debug
• Debugging  techniques  &  tools    <-- we  are  here

Maintain
• Performance  improvement  techniques  &  tools

4
Goals  of  this  Lecture

Help  you  learn  about:


• Strategies  and  tools  for  debugging  your  code

Why?
• Debugging  large  programs  can  be  difficult
• A  power  programmer  knows  a  wide  variety  of  debugging  strategies
• A  power  programmer  knows  about  tools that  facilitate  debugging
• Debuggers
• Version  control  systems

5
Testing  vs.  Debugging

Testing
• What  should  I  do  to  try  to  break my  program?

Debugging
• What  should  I  do  to  try  to  fix my  program?

6
Agenda
(1)  Understand   error  messages
(2)  Think  before  writing
(3)  Look  for  familiar  bugs
(4)  Divide  and  conquer
(5)  Add  more  internal  tests
(6)  Display  output
(7)  Use  a  debugger
(8)  Focus  on  recent  changes

7
Understand  Error  Messages
Debugging  at  build-­time   is  easier  than  debugging  at  run-­
time,  if  and  only  if  you…
Understand  the  error  messages!

#include <stdioo.h>
/* Print "hello, world" to stdout and What  are  the  
return 0.
int main(void)
errors?    (No  
{ printf("hello, world\n"); fair  looking  at  
return 0; the  next  slide!)
}

8
Understand  Error  Messages

#include <stdioo.h>
/* Print "hello, world" to stdout and
return 0. Which  tool  
int main(void) (preprocessor,  
{ printf("hello, world\n"); compiler,  or  
return 0;
linker)  reports  
}
the  error(s)?

$ gcc217 hello.c -o hello


hello.c:1:20: error: stdioo.h: No such file or
directory
hello.c:2:1: error: unterminated comment
hello.c:7: warning: ISO C forbids an empty
translation unit

9
Understand  Error  Messages

#include <stdio.h>
/* Print "hello, world" to stdout and
return 0. */ What  are  the  
int main(void) errors?    (No  
{ printf("hello, world\n") fair  looking  at  
return 0;
} the  next  slide!)

10
Understand  Error  Messages

#include <stdio.h>
/* Print "hello, world" to stdout and
return 0. */
Which  tool  
int main(void) (preprocessor,  
{ printf("hello, world\n") compiler,  or  
return 0; linker)  reports  
}
the  error?

$ gcc217 hello.c -o hello


hello.c: In function 'main':
hello.c:6: error: expected ';' before 'return'

11
Understand  Error  Messages

#include <stdio.h>
/* Print "hello, world" to stdout and
return 0. */ What  are  the  
int main(void) errors?    (No  
{ prinf("hello, world\n"); fair  looking  at  
return 0;
} the  next  slide!)

12
Understand  Error  Messages

#include <stdio.h>
/* Print "hello, world" to stdout and Which  tool  
return 0. */ (preprocessor,  
int main(void)
{ prinf("hello, world\n")
compiler,  or  
return 0; linker)  reports  
} the  error?

$ gcc217 hello.c -o hello


hello.c: In function 'main':
hello.c:5: warning: implicit declaration of function
'prinf'
/tmp/ccLSPMTR.o: In function `main':
hello.c:(.text+0x1a): undefined reference to `prinf'
collect2: ld returned 1 exit status

13
Understand  Error  Messages

#include <stdio.h>
#include <stdlib.h>
enum StateType What  are  the  
{ STATE_REGULAR, errors?    (No  
STATE_INWORD fair  looking  at  
}
int main(void) the  next  slide!)
{ printf("just hanging around\n");
return EXIT_SUCCESS;
}

14
Understand  Error  Messages

#include <stdio.h>
#include <stdlib.h>
enum StateType
{ STATE_REGULAR, What  does  
STATE_INWORD this  error  
} message  even  
int main(void)
{ printf("just hanging around\n"); mean?
return EXIT_SUCCESS;
}

$ gcc217 hello.c -o hello


hello.c:7: error: two or more data types in declaration specifiers
hello.c:7: warning: return type of 'main' is not 'int'

15
Understand  Error  Messages
Caveats  concerning  error  messages
• Line  #  in  error  message  may  be  approximate
• Error  message  may  seem  nonsensical
• Compiler  may  not  report  the  real  error

Tips  for  eliminating  error  messages


• Clarity  facilitates  debugging
• Make  sure  code  is  indented  properly
• Look  for  missing  semicolons
• At  ends  of  structure  type  definitions
• At  ends  of  function  declarations
• Work  incrementally
• Start  at  first  error  message
• Fix,  rebuild,  repeat

16
Agenda
(1)  Understand  error  messages
(2)  Think  before  writing
(3)  Look  for  familiar  bugs
(4)  Divide  and  conquer
(5)  Add  more  internal  tests
(6)  Display  output
(7)  Use  a  debugger
(8)  Focus  on  recent  changes

17
Think  Before  Writing
Inappropriate  changes  could  make  matters  worse,  so…

Think  before  changing  your  code


• Explain  the  code  to:
• Yourself
• Someone  else
• A  Teddy  bear?
• Do  experiments
• But  make  sure  they’re  disciplined

18
Agenda
(1)  Understand  error  messages
(2)  Think  before  writing
(3)  Look  for  common   bugs
(4)  Divide  and  conquer
(5)  Add  more  internal  tests
(6)  Display  output
(7)  Use  a  debugger
(8)  Focus  on  recent  changes

19
Look  for  Common  Bugs
Some  of  our  favorites:
int i;

switch (i) scanf("%d", i);
{ case 0:
… char c;
break; …
case 1: c = getchar();

case 2:
… while (c = getchar() != EOF)
} …

if (i = 5)

What  are  
the  
if (5 < i < 10) if (i & j)
… … errors?

20
Look  for  Common  Bugs
Some  of  our  favorites:

for (i = 0; i < 10; i++)


{ for (j = 0; j < 10; i++)
{ ...
}
}
What  are  
for (i = 0; i < 10; i++) the  
{ for (j = 10; j >= 0; j++) errors?
{ ...
}
}

21
Look  for  Common  Bugs
Some  of  our  favorites:

{ int i;

i = 5; What  value  is  
if (something) written  if  this  
{ int i;

statement  is  
i = 6; present?    Absent?

}

printf("%d\n", i);

}

22
Agenda
(1)  Understand  error  messages
(2)  Think  before  writing
(3)  Look  for  common  bugs
(4)  Divide  and  conquer
(5)  Add  more  internal  tests
(6)  Display  output
(7)  Use  a  debugger
(8)  Focus  on  recent  changes

23
Divide  and  Conquer
Divide  and  conquer:  To  debug  a  program…

• Incrementally  find  smallest  input  file  that  illustrates  the  bug

• Approach  1:    Remove  input


• Start  with  file
• Incrementally  remove  lines
until  bug  disappears
• Examine  most-­recently-­removed  lines

• Approach  2:    Add  input


• Start  with  small  subset  of  file
• Incrementally  add  lines
until  bug  appears
• Examine  most-­recently-­added  lines
24
Divide  and  Conquer
Divide  and  conquer:    To  debug  a  module…

• Incrementally  find  smallest  client  code  subset  that  illustrates  the  


bug

• Approach  1:    Remove  code


• Start  with  test  client
• Incrementally  remove  lines  of  code  until  bug  disappears
• Examine  most-­recently-­removed  lines

• Approach  2:    Add  code


• Start  with  minimal  client
• Incrementally  add  lines  of  test  client  until  bug  appears
• Examine  most-­recently-­added  lines

25
Agenda
(1)  Understand  error  messages
(2)  Think  before  writing
(3)  Look  for  common  bugs
(4)  Divide  and  conquer
(5)  Add  more  internal  tests
(6)  Display  output
(7)  Use  a  debugger
(8)  Focus  on  recent  changes

26
Add  More  Internal  Tests

(5)  Add  more  internal  tests

• Internal  tests  help  find  bugs  (see  “Testing” lecture)

• Internal  test  also  can  help  eliminate  bugs


• Validating  parameters  &  checking  invariants
can  eliminate  some  functions  from  the  bug  hunt

27
Agenda
(1)  Understand  error  messages
(2)  Think  before  writing
(3)  Look  for  common  bugs
(4)  Divide  and  conquer
(5)  Add  more  internal  tests
(6)  Display  output
(7)  Use  a  debugger
(8)  Focus  on  recent  changes

28
Display  Output
Write  values  of  important  variables  at  critical  spots

• Poor: stdout is  buffered;;  


printf("%d", keyvariable); program  may  crash  
before  output  appears

• Maybe  better: Printing  '\n' flushes  


the  stdout buffer,  but  
printf("%d\n", keyvariable);
not  if  stdout is  
redirected  to  a  file
• Better:
Call  fflush() to  flush  
printf("%d", keyvariable); stdout buffer  
fflush(stdout);
explicitly
29
Display  Output
Write  debugging  
output  to  stderr;;  
debugging  output  
• Maybe  even  better:
can  be  separated  
fprintf(stderr, "%d", keyvariable); from  normal  output  
via  redirection

• Maybe  better  still: Bonus:    stderr is  


unbuffered
FILE *fp = fopen("logfile", "w");

fprintf(fp, "%d", keyvariable); Write  to  a  log  file
fflush(fp);

30
Agenda
(1)  Understand  error  messages
(2)  Think  before  writing
(3)  Look  for  common  bugs
(4)  Divide  and  conquer
(5)  Add  more  internal  tests
(6)  Display  output
(7)  Use  a  debugger
(8)  Focus  on  recent  changes

31
Use  a  Debugger

Use  a  debugger

• Alternative  to  displaying  output

32
The  GDB  Debugger

GNU  Debugger
• Part  of  the  GNU  development  environment
• Integrated  with  Emacs  editor
• Allows  user  to:
• Run  program
• Set  breakpoints
• Step  through  code  one  line  at  a  time
• Examine  values  of  variables  during  run
• Etc.

For  details  see  precept  tutorial,  precept  reference  sheet,  


Appendix  1
33
Agenda
(1)  Understand  error  messages
(2)  Think  before  writing
(3)  Look  for  common  bugs
(4)  Divide  and  conquer
(5)  Add  more  internal  tests
(6)  Display  output
(7)  Use  a  debugger
(8)  Focus  on  recent  changes

34
Focus  on  Recent  Changes
Focus  on  recent  changes

• Corollary:    Debug  now,  not  later

Difficult: Easier:

(1)  Compose  entire  program (1)  Compose  a  little


(2)  Test  entire  program (2)  Test  a  little
(3)  Debug  entire  program (3)  Debug  a  little
(4)  Compose  a  little
(5)  Test  a  little
(6)  Debug  a  little

35
Focus  on  Recent  Changes
Focus  on  recent  change  (cont.)

• Corollary:    Maintain  old  versions

Difficult: Easier:

(1)  Change  code (1)  Backup  current  version


(2)  Note  new  bug (2)  Change  code
(3)  Try  to  remember  what (3)  Note  new  bug
changed  since  last   (4)  Compare  code  with  
version last  version  to
determine  what  changed

36
Maintaining  Old  Versions
To  maintain  old  versions…
Approach  1:    Manually  copy  project  directory


$ mkdir myproject
$ cd myproject

Create project files here.

$ cd ..
$ cp –r myproject myprojectDateTime
$ cd myproject

Continue creating project files here.


37
Maintaining  Old  Versions

Approach  2:    Use  the  Revision  Control  System  (RCS)


• A  simple  version  control  system
• Provided  with  many  Linux  distributions
• Available  on  CourseLab
• Allows  programmer  to:
• Check-­in  source  code  files  from  working  copy  to  repository
• RCS  saves  old  versions
• Check-­out source  code  files  from  repository  to  working  copy
• Can  retrieve  old  versions
• Appropriate  for  one-­developer  projects

Not  required  for  COS  217,  but  good  to  know!


See  Appendix  2  for  details
38
Maintaining  Old  Versions

Approach  3:    Use  CVS,  Subversion,  Git,  …


• High-­powered  version  control  systems
• Appropriate  for  multi-­developer  projects
• Allow  repositories  to  be  shared

Beyond  our  scope,  but  good  to  know!

39
Summary

General  debugging  strategies  and  tools:


(1)  Understand  error  messages
(2)  Think  before  writing
(3)  Look  for  common  bugs
(4)  Divide  and  conquer
(5)  Add  more  internal  tests
(6)  Display  output
(7)  Use  a  debugger
• Use  GDB!!!
(8)  Focus  on  recent  changes
• Consider  using  RCS,  etc.

40
Appendix  1:  Using  GDB
An  example  program Euclid’s  algorithm;;
File  testintmath.c: Don’t  be  concerned
with  details
#include <stdio.h>

int gcd(int i, int j) int main(void)
{ int temp; { int iGcd;
while (j != 0) int iLcm;
{ temp = i % j; iGcd = gcd(8, 12);
i = j; iLcm = lcm(8, 12);
j = temp; printf("%d %d\n", iGcd, iLcm);
} return 0;
return i; }
}
The  program  is  correct
int lcm(int i, int j)
{ return (i / gcd(i, j)) * j;
} But  let’s  pretend  it  has  a  
… runtime  error  in  gcd()…
41
Appendix  1:  Using  GDB

General  GDB  strategy:

• Execute  the  program  to  the  point  of  interest


• Use  breakpoints  and  stepping  to  do  that

• Examine  the  values  of  variables  at  that  point

42
Appendix  1:  Using  GDB
Typical  steps  for  using  GDB:

(a)  Build  with  –g


gcc217 –g testintmath.c –o testintmath
• Adds  extra  information  to  executable  file  that  GDB  uses
(b)  Run  Emacs,  with  no  arguments
emacs
(c)  Run  GDB  on  executable  file  from  within  Emacs
<Esc key> x gdb <Enter key> testintmath <Enter key>
(d)  Set  breakpoints,  as  desired
break main
• GDB  sets  a  breakpoint  at  the  first  executable  line  of  main()
break gcd
• GDB  sets  a  breakpoint  at  the  first  executable  line  of  gcd()
43
Appendix  1:  Using  GDB
Typical  steps  for  using  GDB  (cont.):
(e)  Run  the  program
run
• GDB  stops  at  the  breakpoint  in  main()
• Emacs  opens  window  showing  source  code
• Emacs  highlights  line  that  is  to  be  executed  next
continue
• GDB  stops  at  the  breakpoint  in  gcd()
• Emacs  highlights  line  that  is  to  be  executed  next
(f)  Step  through  the  program,  as  desired
step (repeatedly)
• GDB  executes  the  next  line  (repeatedly)

• Note:    When  next  line  is  a  call  of  one  of  your  functions:
• step command  steps  into the  function
• next command  steps  over the  function,  that  is,  executes  the  next  line  
without  stepping  into  the  function
44
Appendix  1:  Using  GDB
Typical  steps  for  using  GDB  (cont.):

(g)  Examine  variables,  as  desired


print i
print j
print temp
• GDB  prints  the  value  of  each  variable
(h)  Examine  the  function  call  stack,  if  desired
where
• GBB  prints  the  function  call  stack
• Useful  for  diagnosing  crash  in  large  program
(i)  Exit  gdb
quit
(j)  Exit  Emacs
<Ctrl-x key> <Ctrl-c key>
45
Appendix  1:  Using  GDB

GDB  can  do  much  more:


• Handle  command-­line  arguments
run arg1 arg2
• Handle  redirection  of  stdin,  stdout,  stderr
run < somefile > someotherfile
• Print  values  of  expressions
• Break  conditionally
• Etc.

46
Appendix  2:  Using  RCS
Typical  steps  for  using  RCS:
(a)  Create  project  directory,  as  usual
mkdir helloproj
cd helloproj
(b)  Create  RCS  directory  in  project  directory
mkdir RCS
• RCS  will  store  its  repository  in  that  directory
(c)  Create  source  code  files  in  project  directory
emacs hello.c …
(d)  Check  in
ci hello.c
• Adds  file  to  RCS  repository
• Deletes  local  copy  (don’t  panic!)
• Can  provide  description  of  file  (1st time)
• Can  provide  log  message,  typically  describing  changes
47
Appendix  2:  Using  RCS
Typical  steps  for  using  RCS  (cont.):
(e)  Check  out  most  recent  version  for  reading
co hello.c
• Copies  file  from  repository  to  project  directory
• File  in  project  directory  has  read-­only permissions
(f)  Check  out  most  recent  version  for  reading/writing
co –l hello.c
• Copies  file  from  repository  to  project  directory
• File  in  project  directory  has  read/write permissions
(g)  List  versions  in  repository
rlog hello.c
• Shows  versions  of  file,  by  number  (1.1,  1.2,  etc.),  with  
descriptions
(h)  Check  out  a  specified  version
co –l –rversionnumber hello.c

48
Appendix  2:  Using  RCS

RCS  can  do  much  more:


• Merge  versions  of  files
• Maintain  distinct  development  branches
• Place  descriptions  in  code  as  comments
• Assign  symbolic  names  to  versions
• Etc.

Recommendation:    Use  RCS


• ci and  co can  become  automatic!

49

You might also like