mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 21:57:55 +00:00
GDB monitor test for automatic testing
The following features are tested (currently on Fiasco.OC only): - breakpoint in 'main()' - breakpoint in a shared library function - stack trace when not in a syscall - thread info - single stepping - handling of segmention fault exception - stack trace when in a syscall This patch fixes #105.
This commit is contained in:
parent
8b7252cdde
commit
f72ab94853
@ -5,6 +5,12 @@
|
||||
# \date 2011-05-24
|
||||
#
|
||||
|
||||
#
|
||||
# Only Genode/Fiasco.OC supports all the tested features at this time
|
||||
#
|
||||
|
||||
assert_spec foc
|
||||
|
||||
#
|
||||
# Build
|
||||
#
|
||||
@ -92,10 +98,139 @@ append qemu_args " -chardev socket,id=uart,port=$local_port,host=localhost,serve
|
||||
|
||||
run_genode_until {.*Remote debugging using /dev/terminal.*} 30
|
||||
|
||||
puts "GDB monitor is up, starting GDB in a new terminal"
|
||||
puts "GDB monitor is up, starting GDB"
|
||||
|
||||
exec [terminal] -e "[gdb] bin/test-gdb_monitor -ex \"target remote localhost:$local_port\"" &
|
||||
# sequence of GDB commands to execute at startup
|
||||
set gdb_cmds ""
|
||||
append gdb_cmds {-ex "target remote localhost:$local_port" }
|
||||
|
||||
interact
|
||||
#
|
||||
# The test breaks into the 'main()' function of the dynamically linked test
|
||||
# application by using the following gdb command sequence. It's important that
|
||||
# the 'main()' breakpoint gets set before the 'sharedlibrary' command is
|
||||
# executed. Otherwise the breakpoint would get set in ld.lib.so's main()
|
||||
# function.
|
||||
#
|
||||
|
||||
# don't ask for y/n when loading a new symbol file
|
||||
append gdb_cmds {-ex "set interactive-mode off" }
|
||||
|
||||
# load the symbols of ld.lib.so
|
||||
append gdb_cmds {-ex "symbol-file bin/ld.lib.so" }
|
||||
|
||||
# set a breakpoint in the 'call_main()' function
|
||||
append gdb_cmds {-ex "b call_main" }
|
||||
|
||||
# continue execution until the breakpoint triggers
|
||||
append gdb_cmds {-ex "c" }
|
||||
|
||||
# delete the 'call_main()' breakpoint
|
||||
append gdb_cmds {-ex "delete 1" }
|
||||
|
||||
# load the symbols of the test application
|
||||
append gdb_cmds {-ex "symbol-file bin/test-gdb_monitor" }
|
||||
|
||||
# set a breakpoint in the application's 'main()' function
|
||||
append gdb_cmds {-ex "b main" }
|
||||
|
||||
# load the symbols of loaded shared libraries
|
||||
append gdb_cmds {-ex "sharedlibrary" }
|
||||
|
||||
# continue execution until the breakpoint triggers
|
||||
append gdb_cmds {-ex "c" }
|
||||
|
||||
# delete the 'main()' breakpoint
|
||||
append gdb_cmds {-ex "delete 2" }
|
||||
|
||||
#
|
||||
# Test commands
|
||||
#
|
||||
|
||||
# test: breakpoint in shared library
|
||||
append gdb_cmds {-ex "b puts" }
|
||||
append gdb_cmds {-ex "c" }
|
||||
|
||||
# test: stack trace when not in syscall
|
||||
append gdb_cmds {-ex "bt" }
|
||||
|
||||
# test: thread info
|
||||
append gdb_cmds {-ex "b Test_thread::entry()" }
|
||||
append gdb_cmds {-ex "c" }
|
||||
append gdb_cmds {-ex "info threads" }
|
||||
|
||||
# test: single stepping
|
||||
append gdb_cmds {-ex "step" }
|
||||
|
||||
# test: catch segmentation fault
|
||||
append gdb_cmds {-ex "c" }
|
||||
|
||||
# test: stack trace when in syscall
|
||||
append gdb_cmds {-ex "thread 1" }
|
||||
append gdb_cmds {-ex "bt" }
|
||||
|
||||
# quit
|
||||
append gdb_cmds {-ex "q" }
|
||||
|
||||
# run GDB and redirect stderr to stdio to get the relevant output into the expect buffer
|
||||
eval spawn [gdb] bin/test-gdb_monitor -batch $gdb_cmds 2&>1
|
||||
|
||||
set timeout 60
|
||||
expect {
|
||||
timeout { puts stderr "Error: Test execution timed out"; exit -2 }
|
||||
}
|
||||
|
||||
set gdb_output $expect_out(buffer)
|
||||
|
||||
#
|
||||
# Evaluate the test results
|
||||
#
|
||||
|
||||
if {![regexp {Breakpoint 2, main ()} $gdb_output]} {
|
||||
puts stderr "Error: Breakpoint in main() did not trigger"
|
||||
exit -1
|
||||
}
|
||||
|
||||
if {![regexp {Breakpoint 3, puts ()} $gdb_output]} {
|
||||
puts "Error: Breakpoint in shared library did not trigger"
|
||||
exit -1
|
||||
}
|
||||
|
||||
if {![regexp {#0 puts} $gdb_output] ||
|
||||
![regexp {in func2 ()} $gdb_output] ||
|
||||
![regexp {in func1 ()} $gdb_output] ||
|
||||
![regexp {in main ()} $gdb_output]} {
|
||||
puts stderr "Error: Stack trace when not in syscall is not as expected"
|
||||
exit -1
|
||||
}
|
||||
|
||||
if {![regexp {Breakpoint 4, Test_thread::entry()} $gdb_output]} {
|
||||
puts stderr "Error: Breakpoint in test thread did not trigger"
|
||||
exit -1
|
||||
}
|
||||
|
||||
if {![regexp {\* 2 Thread 2 Test_thread::entry} $gdb_output] ||
|
||||
![regexp { 1 Thread 1} $gdb_output]} {
|
||||
puts stderr "Error: Thread info is not as expected"
|
||||
exit -1
|
||||
}
|
||||
|
||||
if {![regexp {40 func()} $gdb_output]} {
|
||||
puts stderr "Error: Single stepping didn't result in the expected output"
|
||||
exit -1
|
||||
}
|
||||
|
||||
if {![regexp {Program received signal SIGSEGV, Segmentation fault.} $gdb_output]} {
|
||||
puts stderr "Error: Segmentation fault exception was not catched"
|
||||
exit -1
|
||||
}
|
||||
|
||||
if {![regexp {Genode::Ipc_istream::_wait} $gdb_output] ||
|
||||
![regexp {Genode::Ipc_server::_wait} $gdb_output] ||
|
||||
![regexp {Genode::sleep_forever ()} $gdb_output]} {
|
||||
puts stderr "Error: Stack trace when in syscall is not as expected"
|
||||
exit -1
|
||||
}
|
||||
|
||||
puts "Test succeeded"
|
||||
|
||||
# vi: set ft=tcl :
|
||||
|
@ -12,7 +12,6 @@
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/printf.h>
|
||||
#include <base/sleep.h>
|
||||
#include <base/thread.h>
|
||||
#include <timer_session/connection.h>
|
||||
@ -24,56 +23,55 @@ class Test_thread : public Genode::Thread<2*4096>
|
||||
{
|
||||
public:
|
||||
|
||||
void func3()
|
||||
void func()
|
||||
{
|
||||
/*
|
||||
* make sure that the main thread is sleeping in
|
||||
* Genode::sleep_forever() when the segfault happens
|
||||
*/
|
||||
static Timer::Connection timer;
|
||||
timer.msleep(500);
|
||||
|
||||
while (1) {
|
||||
enum { ROUNDS = 2 };
|
||||
|
||||
for (int cnt = 0; cnt < ROUNDS; ++cnt) {
|
||||
/* call libc printf function */
|
||||
printf("Test thread is running, round %d of %d\n", cnt + 1, ROUNDS);
|
||||
timer.msleep(1000);
|
||||
}
|
||||
|
||||
*(int *)0 = 42;
|
||||
}
|
||||
|
||||
Genode::sleep_forever();
|
||||
*(int *)0 = 42;
|
||||
}
|
||||
|
||||
void entry()
|
||||
void entry() /* set a breakpoint here to test the 'info threads' command */
|
||||
{
|
||||
func3();
|
||||
func();
|
||||
|
||||
Genode::sleep_forever();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void func2()
|
||||
/* this function returns a value to make itself appear in the stack trace when building with -O2 */
|
||||
int func2()
|
||||
{
|
||||
static Timer::Connection timer;
|
||||
while(1) {
|
||||
PDBG("GDB monitor test is running...");
|
||||
timer.msleep(1000);
|
||||
}
|
||||
/* set the first breakpoint here to test the 'backtrace' command for a
|
||||
* thread which is not in a syscall */
|
||||
puts("in func2()\n");
|
||||
|
||||
Genode::sleep_forever();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void func1()
|
||||
|
||||
/* this function returns a value to make itself appear in the stack trace when building with -O2 */
|
||||
int func1()
|
||||
{
|
||||
func2();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Test_thread test_thread;
|
||||
test_thread.start();
|
||||
|
||||
func1();
|
||||
|
||||
test_thread.start();
|
||||
|
||||
Genode::sleep_forever();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user