Self-Study 4, CPSC 331, Fall 2010

home page -  news -  syllabus -  schedule -  assignments -  tutorials -  tests -  java -  references -  Mike Jacobson


Self-Study 1 -  Self-Study 2 -  Self-Study 3 -  Self-Study 4 -  Self-Study 5 -  Self-Study 6 -  Self-Study 7

 Debugging Software

About This Exercise

You will be expected to test the programs that you write in this course and to do your best to correct any problems that you discover during testing. A debugger can be quite helpful when you are trying to do this.

In this exercise — which should be worked through during either the third or fourth week of classes — we will step through the process of using the debugger jdb to examine a program that uses one of the “Fibonacci number” calculators developed in recent exercises. Since it involves the software that was introduced there, this exercise can be considered to be a continuation of the previous self-study exercise.

Problems To Be Solved

  1. If you have not already done so, please read through the information about jdb for CPSC 331 students that is available as part of this course web site.

  2. In the next few questions we will use jdb to examine some of the programs that we have developed as part of the Fibonacci1 package.

    In order to get as much information as possible using a debugger we will need to compile our program over again (in a slightly different way).

    1. Download the programs SFibonacci.java and Fibonacci.java (that were part of the Fibonacci1 package) over again, if you do not still have them from the previous exercise — these files have not been changed.

    2. Compile these programs with the –g flag set, that is, execute the following commands:

      javac -g SFibonacci.java javac -g Fibonacci.java

    3. Make sure that the resulting class files are where they are supposed to be: They should be located in a subdirectory called Fibonacci1 of one of the directories on your CLASSPATH. (Please create this directory and move the files into it if you need to.)

  3. This next step is a little “Java test.”

    We will use a very short main program that calls both the “slow” and “efficient” Fibonacci number calculators. Download and examine two versions of this program — FibTrial1.java and FibTrial2.java. One (but only one) of these programs can be used to compute and print out the fifth Fibonacci number using both of our Fibonacci number calculators.

    1. After examining SFibonacci.java and Fibonacci.java try to figure out which of the two “Trial” programs cannot be used (and why).

    2. When you think that you know which of these two programs can be used, try to compile them both — with the –g flag set, once again.

      If you’ve put the SFibonacci.class and Fibonacci.class files where they belong then you should be able to compile one of these “Trial” programs (but not the other).

      You will be ready to continue once you have successfully compiled one of these two programs.

  4. Here is the solution for the “Java test” you’ve just taken.

    Recall that we have only been using the slow Fibonacci number calculator, provided by SFibonacci.java, for testing — it is too slow to be useful for much more than that.

    Under these circumstances, the only methods that need access to this slow Fibonacci number calculator are other methods that are also part of the Fibonacci1 package. Consequently the signature for this “slow” method is

    protected static long fib (int i)

    FibTrial1.java is not part of this package! Consequently it cannot use the slow Fibonacci number calculator — as the compiler errors you read when you try to compile this program should suggest.

    On the other hand, FibTrial2.java is part of the Fibonacci1 package — and you should be able to compile this.

    Once you have managed to do so, make sure that the file FibTrial2.class is located where it should be (namely, in the same directory as SFibonacci.class and Fibonacci.class).

  5. One last bit of preparatory work will be helpful: Print out the file Fibonacci.java and add the line numbers. for the listing of the method fib You should find that the assertion near the bottom of the method’s loop begins at line number 58.

    It will probably be helpful to refer to this listing as you carry out the next few steps.

  6. We are finally ready to start using the debugger!

    1. Execute the command

      jdb Fibonacci1.FibTrial2

      You should see a message saying that jdb is starting up, and a prompt for a command.

    2. We should set some breakpoints before we do much else. Enter the following two commands, in order to tell jdb that it should stop and wait for instructions every time one of the Fibonacci number methods is entered.

      stop in Fibonacci1.Fibonacci.fib stop in Fibonacci1.SFibonacci.fib

      After each command, you will be informed that jdb is deferring setting up the breakpoint until “after the class is loaded.”

    3. Execute the command

      run

      You will probably see a couple of lines that mention exceptions, the information that the virtual machine has started along with the first part of the first part of output generated by the FibTrial2 program and, finally, the following message:

      Breakpoint hit: "thread=main", Fibonacci1.Fibonacci.fib(), line=31 bci=0

      This is telling us that the first breakpoint has been reached — the program is about to start the execution of the Fibonacci1.Fibonacci.fib method. It also tells us that this method begins at line 31 of the source code.

    4. Execute the command

      step

      repeatedly — causing another instruction in the program to be executed each time — until the following message (including line number 55) has been displayed:

      Step completed: "thread=main", Fibonacci1.Fibonacci.fib(), line=52 bci=57

      At this point we have entered the method’s loop for the first time. To see that this is the case, examine the source code (and locate line number 52 in it) and execute the commands

      print j print previous print current

      to confirm that these have the expected initial values (j = 1, previous=0, and current=1) at this point.

      Set a breakpoint so that we can see what is happening each time the execution of the loop body begins:

      stop at Fibonacci1.Fibonacci:52

      While we’re at it, let’s set one more breakpoint:

      stop at Fibonacci1.Fibonacci:55

      This sets a breakpoint at the assertion in the code — the place where we can inspect the values of j, previous and current at the end of the loop (but before j has been incremented again).

    5. Now continue to move through the program using the command

      cont

      which runs the program until the next breakpoint has been reached — bringing us to the end of the first execution of the loop body. Print the values of the variables j, previous and current once again.

      Continue to use cont to move through the program, printing the values of the above variables while this first method’s loop is being executed. Compare these values to the values that are shown in the source code’s documentation — specifically, the loop invariant — and the assertion at the bottom of the loop.

    6. Continue to use cont in order to proceed through the execution of this program.

      You will eventually see that the “slow” Fibonacci calculator is being called — more of the program’s output will be displayed, and the message

      Breakpoint hit: "thread=main", Fibonacci1.SFibonacci.fib(), line=34 bci=0

      Once that has happened you should print the value of the variable i to see what the input is for this method each time it is called.

      By doing this you will see (once again) why this method is so slow when its input is an integer that is larger than 40.

    We have now seen one use of a debugger that is different from its main purpose, but still of interest: We can use a debugger as a learning tool, in order to understand the behaviour of a program that we need to know more about.

  7. Consider the problem of sorting an input integer array: Entries of the array are moved shuffled in order to produce an array whose entried are arranged in nondecreasing order.

    A sorting algorithm is now available as part of the class MySort.java.

    The loop invariants for the loops in this sorting algorithm are quit complicated! Unfortunately they are needed in order to prove correctness of this algorithm, so it worthwhile to make sure you understand them.

    1. Write a “driver” program that initializes an integer array A with length 10 and entries

      5, 4, 6, 3, 7, 2, 8, 1, 9, 0

      and then sorts the array using the MySort.sort method and prints out the results.

    2. Use the debugger to check the behaviour of the outer loop.

      Note that, when a breakpoint is set at a given line of code, the debugger stops just before the first statement on this line is executed. Consequently you should set a breakpoint at the line containing the statement

      int j = i-1;

      at the beginning of the outer loop’s loop body in order to be able to check that the loop invariant for this loop is satisfied.

      Use the commands “cont,” “print i” and “dump A” repeatedly in order to see how the outer loop modifies the array and to confirm that the outer loop invariant really is a “loop invariant” for this loop.

      You should check that the “loop variant” has the properties that it is supposed to, as well. You can execute the command

      print A.length−i

      inside the debugger in order to see the value of this function.

    3. Now use the debugger again to check the behaviour of the inner loop. Along with the breakpoint you set when carrying out the previous part of this exercise you will need to set a breakpoint somewhere inside the inner loop body (you should know where, by now). Inspect the values of i, j, and the array A and evaluate the loop variant in order to check that the program is doing what its documentation claims.

  8. In the eleventh problem on the tutorial exercise on software testing, you were asked to test and debug a program that decides whether the entries of a given integer array are distinct. In particular, you were asked to execute the program by hand in order to try to find errors in it. In a previous problem on the same exercise you were asked to design black box tests for any method solving the problem being described and, of course, these test cases should have been some (but not necessarily all) of the tests that you used.

    This algorithm is now available as part of the class CheckDistinctness.java.

    Write a simple driver program that initializes the array, calls the method, and then prints a message to say whether the method returned the correct answer.

    Then compile, test, and debug the program. If you set a breakpoint at the line in which A[i] is compared to A[j] in the code, then, by printing i and j, you will be able to see precisely which array entries are being compared. Setting breakpoints at the lines containing return statements will help you to discover why the method returns whatever it does.

    If you solved the previous problems involving this program (as well as the problem it solves) on the tutorial exercise on software testing, then you know of several test cases that should be used to test this software. Modifying your driver as needed, use each of these test cases to test this program.

    Four errors have been deliberately included in this program. If you did not find (at least) this number of errors then your testing is probably not as complete as it should be!

    Note: While the code in the given program is incorrect its documentation is not! That is, the “loop invariants” and “loop variants” do say what should happen if the algorithm used to write this software is implemented correctly.

    And, now, finally, we are at a point where a claim, made earier on this course, should be repeated: Testing and debugging is much easier (indeed, they are only really feasible at all) if you know what the software you are working with is supposed to do. This is a significant part of the reason why correctness of algorithms was discussed in this course, before testing.


Last updated:
http://www.cpsc.ucalgary.ca/~jacobs/cpsc331/F10/tutorials/self-study4.html