Lecture 6

Nikolaus Huber

Debugging

Outline

  • What is debugging?
  • Systematic debugging
  • Problem shrinking
  • Slicing
  • Tools

What is debugging?

[...] it is the process of "diagnosing the precise nature of a known error and then correcting it."
Software debugging, testing, and verification, B. Hailpern, P. Santhanam. IBM Systems Journal, 2002
  • Given a specification $\Phi$ and a program $P$, not satisfying some $\phi_k \in \Phi$
  • Find a program $P'$ close to $P$ that does satisfy $\phi_k$
  • Original program $P$ is said to contain a bug

Starting point

  • Failure observed during development, testing, ...
  • Failing test case
  • Problem during use of system
    • Should write a test case reproducing the problem
  • Issue reported by someone else
    • First need to reproduce the failure

From failure to fault

  1. Verify the failure, determine correct behaviour
  2. Isolate and minimize (shrink)
  3. Eyeball the code, where could the fault be?
  4. Divise and run experiments to test your hypothesis
  5. Repeat 3 & 4 until you understand what's wrong
  6. Fix the fault/bug and verify the fix
  7. Create a regression test

Problem minimisation

Problem minimisation 1: Inputs

  • We want to get rid of irrelevant complexity
  • Try to simplify the situation
As an analogon from the real world, consider a flight test: an air plane crashes a few seconds after taking off. By repeating the situation over and over again under changed circumstances, we can find out what is relevant and what not. For instance, we may leave away the passenger seats and find that the plane still crashes. [...] Eventually, only the relevant "simplified" skeleton remains, including a test pilot, the wings, the runway, the fuel, and the engines. Each part of this skeleton is relevant for reproducing the crash.
Simplifying and Isolating Failure-Inducing Input, A. Zeller, R. Hildebrandt. IEEE Transactions on SE, 2002

Shrinking

  • Going from failing program input to minimal failing program input
  • Shrink the program input
  • See if problem still occurs
  • If so shrink more
  • If not, backtrack
  • Can be done manually, some clever algorithms do exist

Delta Debugging algorithm

  • Generalization of greedy binary search
  • Basic idea:
    • Divide input into chunks (initially 2)
    • Remove a chunk, does the test still fail?
    • If yes, continue without it
    • If no, increase granularity (*2)
    • Stop when cutting away doesn't help anymore and number of chunks is the length of the input
  • For more info, read the paper on Studium

Example - Array input

Example - Array input

Error whenever 1, 7, 8 are in the array
  • Found a 1-minimal input
    • Removing any element of the array will cause the test to succeed
  • Other types of minima exist
    • Local minimum
    • Global minimum
  • Global minima $\subseteq$ Local minima $\subseteq$ 1-Minima

Problem Minimisation 2: Slicing

  • Central concept of debugging
  • Main idea
    • Given a Program $P$ and some occurance of variable $x$
    • Remove all statements that do not affect $x$
    • End up with a simplified version $P'$ of $P$
    • $P'$ only contains statements important for value of $x$
  • Static Backward Slicing

Slice construction

  • Figure out which statements are control dependencies
  • Need to create a Control Flow Graph (CFG)
    • Statement B is control dependent on A if A influences wether B is executed
  • Also need the data dependencies
    • Statement B is data dependent on A if A writes to a variable that B reads from

Backward Dependency

  • Statement B is directly backwards dependent on A if either
    • B is control dependent on A
    • B is data dependent on A
  • Statement B is backwards dependent on A if B is directly backwards dependent on A in one or multiple steps
    • Transitive closure of "directly backwards dependent"

Example


			Pass = 0;
			Fail = 0;
			Count = 0;
			while (!eof()) {
				TotalMarks=0;
				scanf("%d", Marks);
				if (Marks >= 40) { Pass = Pass + 1; }
				if (Marks < 40) { Fail = Fail + 1; }
				Count = Count + 1;
				TotalMarks = TotalMarks + Marks;
			}
			printf("Out of %d, %d passed and %d failed\n", Count, Pass, Fail);
			average = TotalMarks/Count;
			printf("The average was %d\n", average);
		

Static Backward Slice

Given some occurance of variable x in a statement S, the static backward slice is the set of statements T such that
  1. S in control-dependent on T, or
  2. the occurance of x is data-dependent on T, or
  3. T is a backward dependency of some statement from (1.) or (2.) above

Computing Static Backward Slices

  • By hand
  • Using software tools
    • Frama-C has inbuilt support for slicing

Bug finding / Observation

  • Simple logging
    • printf
    • serial output
    • LEDs
    • ...
  • Logging frameworks
    • log4c
    • Zephyr logging
    • ...
  • Using a debugger

Debuggers

  • Sophisticated tools to inspect and control program execution
  • Most popular for C/C++: gdb
  • Various GUIs and front-ends exist
  • When using an IDE, debugging interface often already integrated

Features

  • Manual stepping
  • Inspection
    • Variables, registers, memory
    • Call stack
    • Devices, peripherals
    • Thread state and data
  • Breakpoints: Pause execution on program location
  • Watchpoints: Pause execution on reading/writing to memory location
  • Tracing

Debugging Setup

Thanks for today!