Skip to content
Snippets Groups Projects
Forked from TheAlternative / courses
350 commits behind, 25 commits ahead of the upstream repository.
pres.md 19.95 KiB

\begin{center} {\Large \textbf{Debugging on Linux}} \end{center}

Print Debugging

\begin{center} Most often, debugging is done via print... \linebreak

...But that scales poorly \end{center}

GDB

\begin{center} A Debugger (such as GDB) can help here \end{center}

GDB

What is GDB?

GDB is a tool to inspect your program while it is running. It allows:

  • to stop program execution at any point
  • step through the program
  • print the value of variables
  • modify variables

Usage

Before running a program in GDB, it needs to be compiled with debug symbol.

  • Available compiler flags flags are -g, -g3, -ggdb3
  • -ggdb3 gives the most debug information

The program can then be run under gdb: gdb ./a.out

An Example...

#include<stdio.h>
#include<stdbool.h>
#include<tgmath.h>

bool is_prime(int number) { /* snip */ }

int
main(int argc, char** argv) {
  int number;
  scanf("%d", &number);
  if (is_prime(number))
	printf("%d: prime\n", number);
  else
	printf("%d: not prime\n", number);
  return 0;
}

An Example...

$ gcc -ggdb3 simple.c -lm
$ gdb ./a.out
GNU gdb (GDB) 8.3.1
Copyright (C) 2019 Free Software Foundation, Inc.
...snip...
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...
(gdb)

The Start Command

start
starts program execution and stops it at the beginning of main
(gdb) start
Temporary breakpoint 1 at 0x11c7: file simple.c, line 16.
Starting program: /home/dcm/misc/debugging-on-linux/code/gdb-examples/a.out

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdf18) at simple.c:16
16	main(int argc, char** argv) {
(gdb)

The Step Command

step
executes the next line. If there's a function, it will step into it.
(gdb) start
Temporary breakpoint 1 at 0x11c7: file simple.c, line 16.
Starting program: /home/dcm/misc/debugging-on-linux/code/gdb-examples/a.out

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdf18) at simple.c:16
16	main(int argc, char** argv) {
(gdb) step
18	  scanf("%d", &number);
(gdb)

The Next Command

next
executes the next line. If there's a function, DO NOT step into it.
(gdb) start
Temporary breakpoint 1 at 0x11c7: file simple.c, line 16.
Starting program: /home/dcm/misc/debugging-on-linux/code/gdb-examples/a.out

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffdf18) at simple.c:16
16	main(int argc, char** argv) {
(gdb) step
18	  scanf("%d", &number);
(gdb) next
1234567
19	  if (is_prime(number))
(gdb)

The Print Command

print
prints the contents of variables. It also allows calling functions.
(gdb) step
18	  scanf("%d", &number);
(gdb) next
1234567
19	  if (is_prime(number))
(gdb) print number
$1 = 1234567
(gdb) print is_prime(number)
$2 = false
(gdb)

The List Command

list
lists the 10 lines of source code surrounding the current one
(gdb) next
1234567
19	  if (is_prime(number))
(gdb) list
14
15	int
16	main(int argc, char** argv) {
17	  int number;
18	  scanf("%d", &number);
19	  if (is_prime(number))
20		printf("%d: prime\n", number);
21	  else
22		printf("%d: not prime\n", number);
23	  return 0;
(gdb)

The Break Command

break
adds a break point, which will stop program execution when reached
(gdb) list
14
15	int
16	main(int argc, char** argv) {
17	  int number;
18	  scanf("%d", &number);
19	  if (is_prime(number))
20		printf("%d: prime\n", number);
21	  else
22		printf("%d: not prime\n", number);
23	  return 0;
(gdb) break 22
Breakpoint 2 at 0x555555555214: file simple.c, line 22.
(gdb)

The Continue Command

continue
continues program execution until either a breakpoint is hit or the programs exits
(gdb) break 22
Breakpoint 2 at 0x555555555214: file simple.c, line 22.
(gdb) continue
Continuing.

Breakpoint 2, main (argc=1, argv=0x7fffffffdf08) at simple.c:22
22		printf("%d: not prime\n", number);
(gdb) 

The Quit Command

quit
quits GDB, terminating the program
(gdb) continue
Continuing.

Breakpoint 2, main (argc=1, argv=0x7fffffffdf08) at simple.c:22
22		printf("%d: not prime\n", number);
(gdb) quit
A debugging session is active.

	Inferior 1 [process 7431] will be killed.

Quit anyway? (y or n) y
$

Text User Interface

GDB can also be run with a TUI: gdb -tui ./a.out

Text User Interface

\begin{center} \includegraphics[width=0.75\textwidth]{img/gdb-tui.png} \end{center}

Valgrind

Quick Warning

The following is slide code and output, therefore all source code and program output is heavily shortend.

Valgrind

  • Collection of usefull debugging tools
    • Memcheck
    • Helgrind
  • Works on any executable
    • Doesn't require recompilation
    • Output gets more readable with debug symbols!

How to use Valgrind?

  • (optionally) Recompile the program with debug symbols
  • Choose a tool
  • Run the program under valgrind: valgrind --tool=$TOOL ./a.out

Memcheck

Memcheck

  • finds common memory errors
    • Use after free
    • Use of uninitialised values
    • Memory leaks
    • ...and many more
  • Usage: valgrind [--leak-check=full] ./a.out

Use After Free

Use After Free
Usage of a pointer after it has been free()'d

Use After Free

#include<stdlib.h>

int
main(int argc, char **argv) {
  int *ip;
  ip = malloc(sizeof(int));
  free(ip);
  *ip = 3;
  return 0;
}

Use After Free

==4474== Memcheck, a memory error detector
..snip..
==4474== Invalid write of size 4
==4474==    at 0x109176: main (uaf.c:8)
==4474==  Address 0x4a86040 is 0 bytes inside a block of size 4 free'd
==4474==    at 0x48399AB: free (vg_replace_malloc.c:540)
==4474==    by 0x109171: main (uaf.c:7)
==4474==  Block was alloc'd at
==4474==    at 0x483877F: malloc (vg_replace_malloc.c:309)
==4474==    by 0x109161: main (uaf.c:6)
==4474==
..snip..

Uninitialized Values

Unitialized Values
Use of a variable or memory before it has been initialized

Uninitialized Values

#include<stdio.h>

int
main(int argc, char **argv) {
  int i;
  if (i)
	printf("Hui\n");
  else
	printf("Pfui\n");
  return 0;
}

Uninitialized Values

==7096== Memcheck, a memory error detector
..snip..
==7096== Conditional jump or move depends on uninitialised value(s)
==7096==    at 0x10914C: main (ui.c:6)
==7096==
Pfui
==7096==
..snip..

Memory Leaks

Memory Leak
A piece of memory is malloc()'d, but never free()'d

Memory Leaks

#include<stdlib.h>

int
main(int argc, char **argv) {
  int *p;
  p = malloc(sizeof(int));
  return 0;
}

Memory Leaks

==7219== Memcheck, a memory error detector
..snip..
==7219== HEAP SUMMARY:
==7219==     in use at exit: 4 bytes in 1 blocks
==7219==   total heap usage: 1 allocs, 0 frees, 4 bytes allocated
==7219==
==7219== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==7219==    at 0x483877F: malloc (vg_replace_malloc.c:309)
==7219==    by 0x109151: main (ms.c:6)
==7219==
==7219== LEAK SUMMARY:
==7219==    definitely lost: 4 bytes in 1 blocks
==7219==    indirectly lost: 0 bytes in 0 blocks
==7219==      possibly lost: 0 bytes in 0 blocks
==7219==    still reachable: 0 bytes in 0 blocks
==7219==         suppressed: 0 bytes in 0 blocks
..snip..

Helgrind

Helgrind

  • finds common threading problems
    • Potential lock order inversions
    • Race conditions
  • Has problems with lock-free data structures/algorithms
  • Usage: valgrind --tool=helgrind ./a.out

Race Condition

Race Condition
Several Threads try to modify the same variable at the same time yielding unexpected results