
This folder is part of the C/C++ Programming Tools lectures, themselves
part of the Programming III course.

This is the debugging example in lecture 2.

  (C) Duncan C. White, 2014
      Pedro A.M. Mediano, 2015

  Finding the problem
  ==================================================================

  - Make and run the .c file in this directory. It will probably segfault.
    This problem is tricky to solve with just printf's, so let's use a
    debugger.

  - Recompile with '-g' flag and run again (yes, it still segfaults).
    Now run the program inside gdb:

    $ gdb -tui ./eg

  - Use 'r' to run the program until it crashes. Use 'where'/'bt' to find
    out where you are and 'frame' to select where in the call chain you
    want to be.

  - Use 'whatis' to check everything is the type it should be. Use 'p'
    to print the value of each variable. Variables p and str are ok, but
    q shows weird behaviour:

    (gdb) p q
    $3 = 0x657265 <error: Cannot access memory at address 0x657265>

  - Use the '&' operator to print the memory address of the variables.
    Observe that C has allocated the three variables consecutively.

    $4 = (char **) 0x601058 <p>
    (gdb) p &str
    $5 = (char (*)[8]) 0x601060 <str>
    (gdb) p &q
    $6 = (char **) 0x601068 <q>

  - Use 'x' to see the actual bytes at some particular address.

    (gdb) x/12c &str
    0x601060 <str>: 104 'h' 101 'e' 108 'l' 108 'l' 111 'o' 32 ' '  116 't' 104 'h'
    0x601068 <q>:   101 'e' 114 'r' 101 'e' 0 '\000'

    Aha! str tried to grow larger than it could and it got into q's space.
    We mistakenly wrote something on q, making it point to some arbitrary
    address in memory.

    
  Finding the origin of the problem
  ==================================================================

  - Now we know what's wrong, but in order to fix it we must know how did it
    go wrong in the first place.

  - Type 'break main' to stop execution when the program enters main().
    Run again.

  - Use 'n' or 's' to step through the program, monitoring the variables.
    Check the value of p, str, q after some critical points (e.g. append).

  - Alternatively you can 'watch' q, so that execution stops whenever q is
    modified.

    (gdb) watch q
    Hardware watchpoint 2: q
    (gdb) c
    Continuing.
    Hardware watchpoint 2: q

    Old value = 0x0
    New value = 0x607265 <error: Cannot access memory at address 0x607265>

  - Use 'bt' to check that q was modified in the line append(str, "there").
    q definitely shouldn't be modified in there. strcat did the damage.


  Fixing the problem
  ==================================================================

  - In append(), you should check there's enough space left in str.
    Something like this could work:

    char* append(char *str, char *app, int maxlen) {
      if ( (strlen(str) + strlen(app)) < maxlen ) {
        strcat( str, app );
        return str;
      } else {
        return NULL;	/* indicates failure */
      }
    }
 
  - Now you can check for errors back in main(). For example, using
    assertions (remember to include assert.h):

    assert(append(str, "there", STRSIZE) != NULL);

    Or with your own error code:

    if (!append(str, "there", STRSIZE) != NULL)) {
      printf("Error when appending to str.\n");
      exit(EXIT_FAILURE);
    }

