mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-26 13:59:57 +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
|
# \date 2011-05-24
|
||||||
#
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Only Genode/Fiasco.OC supports all the tested features at this time
|
||||||
|
#
|
||||||
|
|
||||||
|
assert_spec foc
|
||||||
|
|
||||||
#
|
#
|
||||||
# Build
|
# 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
|
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 :
|
# vi: set ft=tcl :
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* Genode includes */
|
/* Genode includes */
|
||||||
#include <base/printf.h>
|
|
||||||
#include <base/sleep.h>
|
#include <base/sleep.h>
|
||||||
#include <base/thread.h>
|
#include <base/thread.h>
|
||||||
#include <timer_session/connection.h>
|
#include <timer_session/connection.h>
|
||||||
@ -24,56 +23,55 @@ class Test_thread : public Genode::Thread<2*4096>
|
|||||||
{
|
{
|
||||||
public:
|
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;
|
static Timer::Connection timer;
|
||||||
|
timer.msleep(500);
|
||||||
|
|
||||||
while (1) {
|
*(int *)0 = 42;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void entry()
|
void entry() /* set a breakpoint here to test the 'info threads' command */
|
||||||
{
|
{
|
||||||
func3();
|
func();
|
||||||
|
|
||||||
Genode::sleep_forever();
|
Genode::sleep_forever();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* this function returns a value to make itself appear in the stack trace when building with -O2 */
|
||||||
static void func2()
|
int func2()
|
||||||
{
|
{
|
||||||
static Timer::Connection timer;
|
/* set the first breakpoint here to test the 'backtrace' command for a
|
||||||
while(1) {
|
* thread which is not in a syscall */
|
||||||
PDBG("GDB monitor test is running...");
|
puts("in func2()\n");
|
||||||
timer.msleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
func2();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
Test_thread test_thread;
|
Test_thread test_thread;
|
||||||
test_thread.start();
|
|
||||||
|
|
||||||
func1();
|
func1();
|
||||||
|
|
||||||
|
test_thread.start();
|
||||||
|
|
||||||
|
Genode::sleep_forever();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user