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
Lin 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 alternateLthat 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 valuesVof local variables in the call stack that resulted inLbeing reached. This includes all variables that were referenced in conditional statements that resulted inLbeing executed. - For each of these values
V:- If
Vcorrect 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
valuewas computed. - Find all lines
Lin the program that can setvalue - Reproduce
failure, finding last execution of a lineLthat 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 reproducefailuremore than once to find the last execution. - Is the last
Lto 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
value2used by the lastLto execute incorrect? If so, returnlocalizeWrongValue(failure, value2). - Failed to find defect. Return nothing.
strategy localizeMissingOutput(failure)
- Find the line of code
Lthat would have produced the output you expected. - Do
diagnoseUnexecutedLine(failure, L)
strategy diagnoseUnexecutedLine(failure, line)
- Find all conditional statements
conditionsthat would have causedlineto execute. These may be an if-statements, switch-statements, or other conditional statements that would have prevented the line from executing. - For each line
Linconditions:- Set a breakpoint on line
- Reproduce the failure to see if
Lexecuted - If
Lexecuted, did it execute correctly? If so, move on to the nextLinconditions. - If it didn't execute correctly, identify the value
Vthat 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")