Code Debugging with GDB - part 3: Automating the Testing

Abstract

There are some processes nested with complex conditional branches. Now you need to be able to fully cover and test program work correctly.

1
2
3
Sequence 1: Condition1 -> Condition2 -> Condition3 -> Condition4 -> Handler1
Sequence 2: Condition1 -> Condition2 -> Condition3 -> Condition5 -> Handler1
Sequence 3: Condition1 -> Condition2 -> Condition7 -> Condition4 -> Handler1

Note: Each condition is encapsulated into a separate function, and these execution branches execute only one at a time.

Automating Testing

Ideas

  • Do not try to modify the existing code.
  • Construct the conditions required to execute each handler at runtime. For example, to handler called normally requires all its pre-condition to be modified to true.
  • Can do this with GDB.

Using GDB to modify function return values at runtime

Source test

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>

int g_cond1 = 1;
int g_cond2 = 2;
bool g_cond3 = true;
bool g_cond4 = false;

int condition1(){
return g_cond1;
}

int condition2(){
return g_cond2;
}

bool condition3(){
return g_cond3;
}

bool condition4(){
return g_cond4;
}

void handler1(){
std::cout << "handler1() is called" << std::endl;
}

int main(){
if (condition1() == 2 && condition2() == 3 & !condition3() && condition4()) {
handler1();
}
return 0
}

Modify return value in GDB

1
2
3
4
5
6
7
8
9
10
11
ethanol@ethanol:~/Desktop/automating_test$ gdb -q ./main
GEF for linux ready, type `gef' to start, `gef config' to configure
96 commands loaded for GDB 9.2 using Python engine 3.8
Reading symbols from ./main...
gef➤ b condition1
Breakpoint 1 at 0x11a9: file main.cpp, line 8.
gef➤ b condition2
Breakpoint 2 at 0x11b9: file main.cpp, line 12.
gef➤ b condition3
Breakpoint 3 at 0x11c9: file main.cpp, line 16.
gef➤ b condition4
1
2
3
4
5
6
7
8
9
10
11
12
13
gef➤  c
gef➤ return 2
gef➤ c
gef➤ return 3
gef➤ c
gef➤ return false
gef➤ c
gef➤ return true
gef➤ c
Continuing.
handler1() is called
[Inferior 1 (process 20873) exited normally]
gef➤

We can put manually executed commands into a file cmd_test.gdb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
file ./main
start

define test_handler1
b condition1
b condition2
b condition3
b condition4

c
return 2
c
return 3
c
return false
c
return true
c
end
test_handler1
1
gdb -q -x cmd_test.gdb

Tell if the handler is called

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
file ./main
start

define test_handler1
b condition1
b condition2
b condition3
b condition4
b *handler1

c
return 2
c
return 3
c
return false
c
return true
c
set $next_actual = $pc
set $next_expected = &handler1
if $next_actual == $next_expected
echo test_handler1[SUCCESS]\n
else
echo test_handler1[FAILURE]\n
end
c
end
test_handler1

Note: If you want to get the first instruction of a class member function - set $expected =(int) 'test.cpp' ::Test::Handler1.

  • Test: class name.
  • Handler1: member function name.

Other issues

  1. Breakpoint cleanup between test cases - d.
  2. Test case results statistics.
    1. Turn on gdb logging function.
    2. Write scripts that analyze the output logs of gdb and count which test cases passed and which failed.