Explain your code to someone else. Another effective technique is to explain your code to someone else. This will often cause you to explain the bug to yourself. Sorry to bother you.
|Published (Last):||10 February 2019|
|PDF File Size:||18.98 Mb|
|ePub File Size:||8.49 Mb|
|Price:||Free* [*Free Regsitration Required]|
Explain your code to someone else. Another effective technique is to explain your code to someone else. This will often cause you to explain the bug to yourself.
Sorry to bother you. One university computer center kept a teddy bear near the help desk. Students with mysterious bugs were required to explain them to the bear before they could speak to a human counselor. What on earth is going on? Make the bug reproducible. The first step is to make sure you can make the bug appear on demand. Spend some time constructing input and parameter settings that reliably cause the problem, then wrap up the recipe so it can be run with a button push or a few keystrokes.
Does some set of conditions make it happen more often than others? If a program provides debugging output, enable it. Simulation programs like the Markov chain program in Chapter 3 should include an option that produces debugging information such as the seed of the random number generator so that output can be reproduced; another option should allow for setting the seed.
Many programs include such options and it is a good idea to include similar facilities in your own programs. Divide and conquer. Can the input that causes the program to fail be made smaller or more focused? Narrow down the possibilities by creating the smallest input where the bug still shows up. What changes make the error go away? Try to find crucial test cases that focus on the error. Each test case should aim at a definitive outcome that confirms or denies a specific hypothesis about what is wrong.
Proceed by binary search. Throw away half the input and see if the output is still wrong; if not, go back to the previous state and discard the other half of the input. The same binary search process can be used on the program text itself: eliminate some part of the program that should have no relationship to the bug and see if the bug is still there. An editor with undo is helpful in reducing big test cases and big programs without losing the bug.
Study the numerology of failures. Sometimes a pattern in the numerology of failing examples gives a clue that focuses the search. We found some spelling mistakes in a newly written section of this book, where occasional letters had simply disappeared. This was mystifying. The text had been created by cutting and pasting from another file, so it seemed possible that something was wrong with the cut or paste commands in the text editor. But where to start looking for the problem? For clues we looked at the data, and noticed that the missing characters seemed uniformly distributed through the text.
We measured the intervals and found that the distance between dropped characters was always bytes, a suspiciously non-random value. A search through the editor source code for numbers near found a couple of candidates. One of those was in new code, so we examined that first, and the bug was easy to spot, a classic off-by-one error where a null byte overwrote the last character in a byte buffer. Studying the patterns of numbers related to the failure pointed us right at the bug.
Elapsed time? A couple of minutes of mystification, five minutes of looking at the data to discover the pattern of missing characters, a minute to search for likely places to fix, and another minute to identify and eliminate the bug. This one would have been hopeless to find with a debugger, since it involved two multiprocess programs, driven by mouse clicks, communicating through a file system.
Display output to localize your search. Display messages in a compact fixed format so they are easy to scan by eye or with programs like the pattern-matching tool grep. A grep-like program is invaluable for searching text.
Chapter 9 includes a simple implementation. Learn to read pointer values and recognize likely and unlikely ones, like zero, negative numbers, odd numbers, and small numbers. If output is potentially voluminous, it might be sufficient to print single-letter outputs like A, B, Write self-checking code. In a different application, you might want check to carry on after printing.
Leave it in the source, commented out or controlled by a debugging option, so that it can be turned on again when the next difficult problem appears. For harder problems, check might evolve to do verification and display of data structures. This approach can be generalized to routines that perform ongoing consistency checks of data structures and other information.
Write a log file. Another tactic is to write a log file containing a fixed-format stream of debugging output. When a crash occurs, the log records what happened just before the crash. Output functions like printf normally buffer their output to print it efficiently; abnormal termination may discard this buffered output. The standard functions setbuf and setvbuf control buffering; setbuf fp, NULL turns off buffering on the stream fp. The standard error streams stderr, cerr, and System.
Draw a picture. Sometimes pictures are more effective than text for testing and debugging. Pictures are especially helpful for understanding data structures, as we saw in Chapter 2, and of course when writing graphics software, but they can be used for all kinds of programs.
Scatter plots display misplaced values more effectively than columns of numbers. A histogram of data reveals anomalies in exam grades, random numbers, bucket sizes in allocators and hash tables, and the like. The following graphs plot, for the C Markov program in Chapter 3, hash chain lengths on the x axis and the number of elements in chains of that length on the y axis. The input data is our standard test, the Book of Psalms 42, words, 22, prefixes.
The first two graphs are for the good hash multipliers of 31 and 37 and the third is for the awful multiplier of In the first two cases, no chain is longer than 15 or 16 elements and most elements are in chains of length 5 or 6.
In the third, the distribution is broader, the longest chain has elements, and there are thousands of elements in chains longer than Use tools. Make good use of the facilities of the environment where you are debugging. For example, a file comparison program like diff compares the outputs from successful and failed debugging runs so you can focus on what has changed. If your debugging output is long, use grep to search it or an editor to examine it.
Resist the temptation to send debugging output to a printer: computers scan voluminous output better than people do. Use shell scripts and other tools to automate the processing of the output from debugging runs. Write trivial programs to test hypotheses or confirm your understanding of how something works.
For instance, is it valid to free a NULL pointer? Besides indicating what has changed recently, they can also identify sections of code that have a long history of frequent modification; these are often a good place for bugs to lurk. Keep records. If the search for a bug goes on for any length of time, you will begin to lose track of what you tried and what you learned.
Occasionally hardware itself goes bad. The floating-point flaw in the Pentium processor that caused certain computations to produce wrong answers was a highly publicized and costly bug in the design of the hardware, but once it had been identified, it was of course reproducible. One of the strangest bugs we ever saw involved a calculator program, long ago on a two-processor system.
The problem was eventually traced to a failure of the floating-point unit in one of the processors. As the calculator program was randomly executed on one processor or the other, answers were either correct or nonsense. Many years ago we used a machine whose internal temperature could be estimated from the number of low-order bits it got wrong in floating-point calculations. One of the circuit cards was loose; as the machine got warmer, the card tilted further out of its socket, and more data bits were disconnected from the backplane.
Last Resorts What do you do if none of this advice helps? Buy the book! Download the sample pages includes Chapter 3 and Index Table of Contents.
The Practice of Programming
Start your free trial Book Description With the same insight and authority that made their book The Unix Programming Environment a classic, Brian Kernighan and Rob Pike have written The Practice of Programming to help make individual programmers more effective and productive. The practice of programming is more than just writing code. Programmers must also assess tradeoffs, choose among design alternatives, debug and test, improve performance, and maintain software written by themselves and others. At the same time, they must be concerned with issues like compatibility, robustness, and reliability, while meeting specifications. The Practice of Programming covers all these topics, and more. It includes chapters on: debugging: finding bugs quickly and methodically testing: guaranteeing that software works correctly and reliably performance: making programs faster and more compact portability: ensuring that programs run everywhere without change design: balancing goals and constraints to decide which algorithms and data structures are best interfaces: using abstraction and information hiding to control the interactions between components style: writing code that works well and is a pleasure to read notation: choosing languages and tools that let the machine do more of the work Kernighan and Pike have distilled years of experience writing programs, teaching, and working with other programmers to create this book.
Practice of Programming, The