From f72ab94853f7b901852f1ceb67b602c14118b82c Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Thu, 9 Feb 2012 18:37:20 +0100 Subject: [PATCH] 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. --- ports/run/gdb_monitor.run | 141 ++++++++++++++++++++++++++++- ports/src/test/gdb_monitor/main.cc | 52 +++++------ 2 files changed, 163 insertions(+), 30 deletions(-) diff --git a/ports/run/gdb_monitor.run b/ports/run/gdb_monitor.run index bc331c7c6..05104298d 100644 --- a/ports/run/gdb_monitor.run +++ b/ports/run/gdb_monitor.run @@ -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 : diff --git a/ports/src/test/gdb_monitor/main.cc b/ports/src/test/gdb_monitor/main.cc index e5c9fb59b..0d1fef3db 100644 --- a/ports/src/test/gdb_monitor/main.cc +++ b/ports/src/test/gdb_monitor/main.cc @@ -12,7 +12,6 @@ */ /* Genode includes */ -#include #include #include #include @@ -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; }