Hour 7
Debugging Tools

Programs are easy to write. Writing correct programs is a different story. Locating program bugs can be difficult. When you are working with tools that compile or interpret your programs, debugging tools are built in to the compilation or interpretation process, which makes your life much easier. Although the compiler or interpreter locates syntax errors for you, locating logic errors often takes extra time, and you must be the one to locate such problems before your users locate the problems. For example, when a payroll amount comes out incorrectly due to a bug, you need to locate the problem as quickly as possible.

Although your debugging skills will improve as your programming skills improve, you can help reduce bugs that appear and make debugging simpler by learning to write programs in a way that will make them easier to maintain and update. When you write clear and concise code, you’ll debug your programs more quickly. Many sophisticated debugging tools exist in today’s programming languages. This hour shows you how to access debugging tools available to Python developers so that you can try them yourself.

The highlights of this hour include the following:

Image Looking in the past to the first computer bug

Image Learning the difference between logic errors and syntax errors

Image Seeing the importance of writing clear and concise code

Image Using Python debugging tools

Image Stepping through a program one line at a time

The First Bug

The term bug has an interesting origin. The late U.S. Navy Admiral Grace Hopper was one of the early pioneers of computer hardware and software. She helped to design and write the first COBOL compiler for the military. COBOL (Common Business-Oriented Language) was a language later used by most business programmers of the 1960s and 1970s. Admiral Hopper was working on a military computer system in the early 1950s and, while she was printing a report, the printer stopped working. Admiral Hopper and her coworkers set out to find the problem.

After spending a lot of time and not finding any problems in the program or in the data, Admiral Hopper looked inside the printer and noticed that a moth had lodged itself in a printer’s relay switch, keeping the printer from operating properly. As soon as the bug (get it?) was removed, the printer worked perfectly. The moth did not fare as well, but it did go down in computer history as the first computer bug.

Note

Michael Crichton may have used Admiral Hopper’s true story as the basis for his nail-biting suspense novel Andromeda Strain, a story that had a small printer problem that almost caused disaster.

Accuracy Is Everything

You are now aware that a computer is a machine that cannot deal well with ambiguity. A programmer’s plague is the collection of errors that show up in code. Perhaps as you’ve entered Python programs in the earlier hours, you’ve run across problems that occurred as you mistyped a character or two. Bugs can creep into code. Programmers must ensure that they do not write programs that contain errors, although this is not always as easy as it might seem.

When breaking the programming problem into detailed instructions, programmers often leave things out or code the wrong things. When a program runs, errors may appear because the programmer didn’t plan for a particular event, used an incorrect calculation, or typed a line of code incorrectly.

Tip

Some of the most common bugs you will encounter—and those that are often the most difficult to spot—involve mistyping or omitting punctuation marks, including forgetting that second quotation mark, brace, or parenthesis, or not ending a Python if, while, or for statement with a colon. If your program doesn’t run the way you intended (or if it doesn’t run at all), double-checking all your special characters like these is often a good place to start.

A programmer goes through the debugging process to exterminate the bugs from a program. As a programmer writes a program, he or she often runs the program in its unfinished state (as much as it can be run) to catch as many bugs as possible during the program’s development. Incrementally running the program as more and more of the code is completed helps eliminate bugs from the finished program. Still, the majority of the bugs can be found only after the program is completely written.

Beginning programmers often fail to realize how easy it is for bugs to creep into code. Expect them, and you will not be surprised. Many early programming students have taken a program to an instructor saying, “The computer doesn’t work right,” when in reality, the program has a bug or two. When you begin to write programs, expect to have problems you will have to correct. Nobody writes a perfect program every time.

Depending on the length of a program, the time it takes the programmer (or programmers) to correct problems is often almost as long as the time taken to write the program originally. Some errors are very difficult to find.

There are two categories of computer bugs: syntax errors and logic errors. To learn the difference, take a few moments to find the two errors in the following statement:

There are two errrors in this sentence.

Tip

Found only one problem? Need a clue? Not yet; look again for two mistakes before going further.

The first error is obvious. The word errrors is misspelled; it should be spelled errors. The second problem is much more difficult to find. The second problem with the statement is that the entire premise of the statement is incorrect. There is only one error in the statement, and that error is the misspelled word errrors. Therefore, the logic of the statement itself is in error.

This problem demonstrates the difference between a syntax error and a logic error. The syntax error is much easier to find. As you learned in Hour 1, “Hands-On Programming,” syntax errors often appear as misspelled programming language commands and grammatical problems with the way you use the programming language. Logic errors occur when your program is syntactically correct, but you told it to do something that is not what should really be done.

Compilers and interpreters locate your program’s syntax errors when you try to compile your program. This is another reason syntax errors are easier to spot than logic errors: Your computer tells you where they are. When a computer runs into a syntax error, it halts and refuses to analyze the program further until you correct the syntax error. Listing 7.1 shows a Python program that does not work because of a syntax error. It’s one of the first programs you wrote, one to compute the area of a circle. However, a character has been removed from the original code to create the error. Figure 7.1 shows the how the code runs.

LISTING 7.1 Even a simple program can have an error in it

# Filename AreaHalf2.py
# program that calculates and prints the area
# of a circle and half circle

pi = 3.14159 # mathematical value of PI
radius = 3 # radius of the circle

# calculate the area of the whole circle
area = pi * radius * radius

print("The area of a circle with a radius of 3 is ", area)
pint("The area of a half circle is ", area/2)
images

FIGURE 7.1
Running a Python program in the Jupyter Notebook gives you clear information on what went wrong with the program.

If you review the output, you’ll see how helpful an IDE can be when running code. The program prints the first statement in the program because no errors have occurred yet. The typo was introduced in the last line, and the Jupyter Notebook reprints the offending line of code, points it out with a handy arrow, and then gives an explanation of the problem.

Some of the time, it can be a little difficult to translate what went wrong based on the message received, but this one is very clear: “Pint” is not defined. The error in the code is that print is misspelled, and Python has no idea what you are trying to do with the term pint.

You could actually get a different error with this same code. Imagine that you had defined a string variable named pint to be equal to the message My favorite beer. If you misspelled print as pint in this case, you would still get an error, but it would be a different one, as reflected in Figure 7.2.

images

FIGURE 7.2
The error message has changed! Now pint is defined, but it can’t do what you are trying to make it do.

These are just two of the possible errors that could arise from a single typo. In the next lesson, you will learn how to create your own custom functions. If you had created a function named pint (perhaps one that takes a number in ounces and returns how many pints that amount would be), your program would still probably generate an error, but the message would be different.

Look back at the original program shown in Figure 7.1, and you’ll see another advantage of using an IDE. Most integrated development environments use color-coding of terms and phrases to help you differentiate between items. In the program from Figure 7.1, the Jupyter Notebook uses five different colors. The term print (a defined function in Python) is in green. Comments (the pound sign and anything after them) are in blue. Operators like the multiplication and equal signs are in purple. Strings are in red. Everything else is in black. If you follow along when writing your program, you can use these colors to get context clues that something may not work as expected. When originally writing the program, if you saw that what you expected to be the second print statement is in a different color from the first, you might catch your spelling error before you even tried to run the program. And catching errors as soon as possible is vitally important.

Suppose you’re writing a program to print invoices for your company’s accounts receivable department. Because of an error, the computer prints all the invoices with a balance due of –$1,000. In other words, according to the invoice, every customer has a $1,000 credit. Your computer did its job, acting out your program’s instructions. The program obviously contained no syntax errors because it ran without stopping. The logic errors, however, kept it from working properly.

Extensive testing is critical. A programmer wants to get all the errors out so the program will work correctly when the user finally uses it. The larger the program, the more difficult this is. Exterminating program bugs is just part of the daily job programmers tackle.

Note

There is a third kind of error, called a runtime error. Runtime errors are almost always caused by logic mistakes because the programmer failed to predict and therefore handle a potential problem. Runtime errors can occur if a program attempts to write to a disk without first checking to ensure that the disk is mounted. A runtime error can occur, for instance, if the program divides by zero (division by zero is undefined mathematically). The more you program, the more you will learn about heading off potential runtime errors that can occur.

Some errors will not show up until you use the program. Consider the simple code in Listing 7.2.

LISTING 7.2 Taking two user-entered variables and dividing them by each other

a = int(input("What value would you like to assign a? "))
n = int(input("What value would you like to assign n? "))
print("n divided by a is ", n/a)
print("a divided by n is ", a/n)

It’s a pretty straightforward program without any visible bugs, right? You can even run it to check for bugs, as demonstrated in Figure 7.3, where the user enters 1 and 3 at the two prompts.

images

FIGURE 7.3
The division worked exactly as expected, giving you the two answers you figured you would see.

But what if a user enters 3 and 0 at the two prompts? Now you get an error, and again the IDE shares information with you that is going to help you debug the program and get to the root of the issue (see Figure 7.4).

images

FIGURE 7.4
You can’t divide by 0, so the program can’t give you an answer.

It can get even worse. What if the user enters 20 and x for the two prompts (see Figure 7.5)?

images

FIGURE 7.5
Entering a non-number when a number is expected can make the program extremely cranky.

This brings up an important consideration when debugging your programs: You must also have users test the program, almost to the point of trying to break it. This is what is going to happen to your programs in the real world, and most of the time it isn’t even malicious. What if someone were running the code in Listing 7.2 and typed out the word for a specific number (such as one instead of 1)? You would get the same error message in Figure 7.5 because Python was expecting a number and got a string.

Testing and debugging your programs generally takes at least as much time as writing the original code. But don’t get down; it is only with this level of commitment to writing bulletproof code that you can create top-notch applications.

Write Clear Programs

As you write programs, keep in mind the future maintenance that will be required. Sometimes, a program might be in use for a long time before a bug appears because a certain combination of factors must occur to bring the bug to light. Perhaps an unexpected data value appears, such as a negative value in an age field that should never be negative. Perhaps an operating system update causes a program to stop working.

Not only might bugs appear down the road, but programs often need to be modified as the needs of the program change. When you write your programs, write clear and concise code. Every time you write a new line of code, consider whether a comment is needed to explain that line or to introduce a new section. Add extra whitespace characters to your program.

Consider the C program shown in Listing 7.3. The program is a valid, legal, C program that works on many C compilers that you might use. However, the program is horribly written. The program works and contains no syntax or logic errors, but the program is virtually impossible to maintain easily. The program is rather short, but even advanced C programmers will have to hesitate to decipher the code if they are to make changes to it.

LISTING 7.3 This C program contains no errors but is extremely difficult to maintain

/* Filename: CFIRST.C Initial C program that
demonstrates the C comments and shows a few variables
   and their declarations */
#include <stdio.h>
main() {int i,j;/* These 3 lines declare 4 variables */
char c;float x;i=4;/* i and j are assigned integer values */
j=i+7;c='A';/* All character constants are enclosed in
single quotes */x=9.087;/* x requires a floating-point
value since it was declared as a floating-point variable */x=
x*4.5;/*Change what was in x with a formula */
/* Sends the values of the variables to the screen */printf("%d %d %c %f",i,
j,c,x);return 0;/* End programs and functions this way*/}

Whether you know C or not, you must admit that Listing 7.3 is bunched together and difficult to read. By adding whitespace and extra lines, the program automatically becomes easier to understand even for non–C programmers, as Listing 7.4 demonstrates.

Tip

You’ll find that much of C programming is clear to you, thanks to your Python experience. If you want to learn C, you still have to learn about some of the ways C works, including some of the lines in Listings 7.3 and 7.4, but you’ll certainly have a head start.

LISTING 7.4 This C program with better spacing is far clearer and easier to understand

/*  Filename: CFIRST.C
    Initial C program that demonstrates the C comments
    and shows a few variables and their declarations */
#include <stdio.h>

main()
{
   int i, j;              /* These 3 lines declare 4 variables */
   char c;
   float x;

   i = 4;              /* i and j are assigned integer values */
   j = i + 7;
   c = 'A';           /* All character constants are
                         enclosed in single quotes */
    x = 9.087;     /* x requires a floating-point value since it
                      was declared as a floating-point variable */
    x = x * 4.5;    /* Change what was in x with a formula */

 /* Sends the values of the variables to the screen */
   printf("%d %d %c %f", i, j, c, x);

   return 0; /* End programs and functions with return */
}

Additional Debugging Techniques

No matter how you program Python (or any other language you choose), you are going to find robust debugging tools and add-ons. It’s not possible to cover all of them in an introductory chapter like this, so instead, it makes sense to cover some of the techniques that are universal across almost all debugging tools and modules.

The following is a partial list of some of the features you’ll find in most of today’s debugging systems:

Image You can analyze variables at runtime and view those variables in a window separate from the output.

Image You can change contents of variables during the execution of the program so that the rest of the program acts as though the code had assigned those values.

Image You can set breakpoints throughout the program so that the program runs at normal speed until your preset breakpoint is reached. At that time, you can single-step through the rest of the code.

Image You can set watch variables that halt the program’s execution when the watch variables receive a specific value or range of values.

Image You can skip statements that you don’t want to execute during debugging.

In addition to the usual debugging features, many tools allow you to retrace your steps through a program. When you need to see exactly which parts of a program have executed up to a breakpoint, you can look at the call stack. The call stack appears in a dialog box and shows all procedures that have executed in the program to that point. If you double-click a procedure name, the code window opens to that procedure, and all variable values are still intact so that you can see what values have been computed.

Be sure to explore the development tools available to you for your programming language and development environment of choice. They are numerous!

Summary

This hour showed how you can use debugging tools to locate problems in your programs. You used the Python IDE to learn skills related to debugging, and those same skills will transfer rapidly to other languages and tools. Learning how to debug pays dividends when you need to track bugs. Although the debugging tools cannot locate specific logic bugs on their own, they make locating logic bugs easier for you to do.

Q&A

Q. How can single-stepping help me debug a program when it takes so long to step through a large application?

A. By executing a program one line at a time, you can analyze variable and control values at your pace. Remember that you don’t have to single-step through every statement, but only the statements that you want to analyze. After you view values, you can run the rest of the program or set a breakpoint later in the code and run the program at normal speed to that set breakpoint. Single-stepping helps you ensure that data values are correct, and it also enables you to monitor the execution of a program to make sure that the program’s statements execute in the order that you intended.

Q. Can I debug a compiled program?

A. Unfortunately, the debugging tools only work with source code. The source code is needed to display variable names and locate program statements. As a good programmer, you will keep a copy of all source code you compile into user’s programs so you can locate and fix bugs that you or the user find later.

Workshop

The quiz questions are provided for your further understanding.

Quiz

1. True or false: The first bug in a computer program was literally a bug.

2. What is one of the most common causes of bugs?

3. What are the two main categories of bugs?

4. What are the differences between the two main categories of bugs?

5. What does single-stepping mean?

6. What are watch variables?

7. If your program compiles without errors and executes without stopping at a runtime error, are you assured that the program is bug free?

Answers

1. True (a moth, to be precise)

2. Mistyping a bit of punctuation in your code, such as a semicolon

3. The two types are syntax errors and logic errors.

4. A logic error is a mistake in program logic, whereas a syntax error is a mistake in program grammar or spelling.

5. To single-step means to walk through a program’s execution one statement at a time, analyzing the results of each statement.

6. Watch variables are variables you set up to monitor during a program’s debugging session.

7. Just because a program compiles and runs does not mean that you have fully debugged it. Sometimes, an error appears long after the program has been in use because a special set of circumstances occur together.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset