When we say "debug", we are usually referring to many different activities, including reproducing a failure, localizing the root causes of a failure, and patching an implementation to prevent those causes. There are many strategies for each of these activities. Below is a strategy for the fault localization part of debugging.
Below is a algorithm you can follow manually. If you follow it reliably, it should result in successful localization of a defect. Since it's something a person executes, I've written the strategy in a loosely formal pseudocode. While you execute the strategy, keep track of any variables you need to track in a text editor or paper, and keep track of which step and function you're on, just like a computer does when it executes a program.
strategy localizeFailure(failure)
- Reproduce the failure by finding a sequence of inputs that produces the failure reliably.
- To the extent that you can, write down the inputs for later reference.
- If the failure is output that shouldn't have occurred, return
localizeWrongOutput(failure)
. Otherwise, if the failure is output that should have occurred, but didn't returnlocalizeMissingOutput(failure)
strategy localizeWrongOutput(failure)
- Find the line of code
L
in the application that most directly produced the incorrect output. For example, if it was console output, it's a print statement; if it's user interface output, it's whatever component was responsible for rendering the output. If you don't know how to find this line, one strategy is to find a unique feature in the output such as a string constant and do a global search in the code for that string. - Execute the program to line
L
(using a breakpoint or a time-travel debugger). - Reproduce
failure
. If the program does not executeL
, return to step 1 and find an alternateL
that does. If there is no suchL
, then something else is creating the problem. - With the program halted on
L
, inspect the state of all values in memory and all valuesV
of local variables in the call stack that resulted inL
being reached. This includes all variables that were referenced in conditional statements that resulted inL
being executed. - For each of these values
V
:- If
V
correct in this context, move on to the nextV
. - Otherwise, return
localizeWrongValue(failure, V)
- If
- If none of the values were wrong, then one of the inputs to the program was not handled correctly. Identify which input was unexpected and devise a way to handle it correctly.
strategy localizeWrongValue(failure, value)
- The goal of this strategy is to find where
value
was computed. - Find all lines
L
in the program that can setvalue
- Reproduce
failure
, finding last execution of a lineL
that occurred beforefailure
(using breakpoints or a time-travel debugger). If your debugger supports reverse execution, this is a matter of stepping backwards. If not, you may have to reproducefailure
more than once to find the last execution. - Is the last
L
to execute incorrect? Reflect on the intended behavior of the line and whether, as implemented, it achieves this behavior. If it's incorrect, you've found the bug! ReturnL
. - If the line is correct, is a value
value2
used by the lastL
to execute incorrect? If so, returnlocalizeWrongValue(failure, value2)
. - Failed to find defect. Return nothing.
strategy localizeMissingOutput(failure)
- Find the line of code
L
that would have produced the output you expected. - Do
diagnoseUnexecutedLine(failure, L)
strategy diagnoseUnexecutedLine(failure, line)
- Find all conditional statements
conditions
that would have causedline
to execute. These may be an if-statements, switch-statements, or other conditional statements that would have prevented the line from executing. - For each line
L
inconditions
:- Set a breakpoint on line
- Reproduce the failure to see if
L
executed - If
L
executed, did it execute correctly? If so, move on to the nextL
inconditions
. - If it didn't execute correctly, identify the value
V
that caused it to execute incorrectly - Return `localizeWrongValue(failure, V)
Visit Python Tutor. Use the program to compute 1+1 and notice that the output is "Invalid input" instead of 2. Use the fault localization strategy above to find out why.
''' Program make a simple calculator that can add, subtract, multiply and divide using functions '''
# This function adds two numbers
def add(x, y):
return x + y
# This function subtracts two numbers
def subtract(x, y):
return x - y
# This function multiplies two numbers
def multiply(x, y):
return x * y
# This function divides two numbers
def divide(x, y):
return x / y
print("Select operation.")
print("1.Add")
print("2.Subtract")
print("3.Multiply")
print("4.Divide")
# Take input from the user
choice = input("Choose operation:")
num1 = int(input("Enter first number: "))
num2 = int(input("Enter second number: "))
if choice == '1':
print(num1,"+",num2,"=", add(num1,num2))
elif choice == '2':
print(num1,"-",num2,"=", subtract(num1,num2))
elif choice == '3':
print(num1,"*",num2,"=", multiply(num1,num2))
elif choice == '4':
print(num1,"/",num2,"=", divide(num1,num2))
else:
print("Invalid input")