genode/repos/ports/run/gdb_monitor.run
Norman Feske 0de54cddaa gdb_monitor: hide exceptions during create_thread
This patch is a workaround for the missing implementation of
'Pd_session::transfer_quota' interface by the GDB monitor's PD service.
The missing implementation becomes problematic with the changes of #3750
that enabled the cap-quota accounting for core's CPU service.

In regular scenarios without the GDB monitor, the client of
'Cpu_session::create_thread' deals with Out_of_caps or Out_of_ram by
upgrading the CPU session's cap and RAM quotas. This, in turn, results
in a sequence of 'transfer_quota' operations at the parent.

Since GDB monitor implements a custom PD service, these 'transfer_quota'
calls try to transfer quota between sessions provided by core and those
provided by the GDB monitor. This does of course not work. To fix this
issue, the GDB monitor needs a major overhaul. This patch side-steps
the problem by handing Out_of_caps and Out_of_ram from the debuging
target.
2020-05-18 10:16:15 +02:00

309 lines
6.7 KiB
Tcl

#
# \brief Test for using the GDB monitor
# \author Christian Prochaska
# \author Norman Feske
# \date 2011-05-24
#
#
# Only the NOVA platform supports most of the tested features at this time.
#
if {![have_include "power_on/qemu"] ||
!([have_spec nova])} {
puts "Run script is only supported for NOVA in Qemu"; exit 0
}
#
# Build
#
set build_components {
core init timer
drivers/uart
app/gdb_monitor
test/gdb_monitor
}
lappend build_components "lib/gdbserver_platform-$::env(KERNEL)"
build $build_components
create_boot_directory
#
# Generate config
#
set config {
<config verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="2M"/>
<provides> <service name="Timer"/> </provides>
</start>
<start name="pc_uart_drv">
<resource name="RAM" quantum="2M"/>
<provides>
<service name="Terminal"/>
<service name="Uart"/>
</provides>
<config>
<policy label_prefix="gdb_monitor" uart="1"/>
</config>
</start>
<start name="gdb_monitor" caps="200">
<resource name="RAM" quantum="9M"/>
<config>
<target name="test-gdb_monitor">
<config>
<vfs> <dir name="dev"> <log/> </dir> </vfs>
<libc stdout="/dev/log" stderr="/dev/log"/>
</config>
</target>
<preserve name="RAM" quantum="5M"/>
<vfs> <dir name="dev"> <log/> <terminal/> </dir> </vfs>
<libc stdout="/dev/log" stderr="/dev/log"/>
</config>
</start>
</config>
}
install_config $config
#
# Boot modules
#
# evaluated by the run tool
proc binary_name_gdbserver_platform_lib_so { } {
return "gdbserver_platform-$::env(KERNEL).lib.so"
}
# generic modules
set boot_modules {
core init timer
ld.lib.so libc.lib.so vfs.lib.so libm.lib.so libc_pipe.lib.so
pc_uart_drv posix.lib.so stdcxx.lib.so
gdb_monitor gdbserver_platform.lib.so test-gdb_monitor
}
build_boot_image $boot_modules
#
# Execute test case
#
#
set local_port 5555
# qemu config
append qemu_args " -display none "
# connect comport 0 to stdio
append qemu_args " -serial stdio "
# connect comport 1 with TCP port $local_port
append qemu_args " -serial chardev:uart "
append qemu_args " -chardev socket,id=uart,port=$local_port,host=localhost,server,nowait,ipv4 "
run_genode_until {.*\[init -> gdb_monitor\] Remote debugging using /dev/terminal.*} 30
set genode_id [output_spawn_id]
puts "GDB monitor is up, starting GDB"
source ${genode_dir}/repos/ports/run/gdb_monitor.inc
# GDB loads symbols from 'debug/ld.lib.so'
if { [have_spec nova] } {
exec ln -sf ld-nova.lib.so debug/ld.lib.so
}
set gdb_target_binary "test-gdb_monitor"
# sequence of GDB commands to execute at startup
set gdb_cmds ""
append gdb_cmds {-ex "target remote localhost:$local_port" }
append gdb_cmds [gdb_initial_breakpoint_cmds $gdb_target_binary]
# run GDB
eval spawn [gdb] debug/ld.lib.so -n $gdb_cmds
set gdb_id [list $spawn_id $genode_id]
puts ""
puts "----- test: breakpoint in 'main()' -----"
puts ""
run_genode_until {\(gdb\)} 60 $gdb_id
send "b main\n"
run_genode_until {\(gdb\)} 20 $gdb_id
send "c\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {Breakpoint 2, main ()} $output]} {
puts stderr "*** Error: Breakpoint in main() did not trigger"
exit -1
}
send "delete 2\n"
run_genode_until {\(gdb\)} 20 $gdb_id
puts "\n"
puts "----- test: breakpoint in shared library -----"
puts ""
send "b puts\n"
run_genode_until {\(gdb\)} 20 $gdb_id
send "c\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {Breakpoint 3, puts ()} $output]} {
puts "*** Error: Breakpoint in shared library did not trigger"
exit -1
}
puts "\n"
puts "----- test: stack trace when not in syscall -----"
puts ""
send "bt\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {#0 puts} $output] ||
![regexp {in func2()} $output] ||
![regexp {in func1()} $output] ||
![regexp {in main ()} $output]} {
puts stderr "*** Error: Stack trace when not in syscall is not as expected"
exit -1
}
puts "\n"
puts "----- test: modification of a variable value -----"
puts ""
send "print test_var\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {\$1 = 1} $output]} {
puts stderr "*** Error: first 'print test_var' command didn't result in the expected output"
exit -1
}
send "set var test_var=2\n"
run_genode_until {\(gdb\)} 20 $gdb_id
send "print test_var\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {\$2 = 2} $output]} {
puts stderr "*** Error: second 'print test_var' command didn't result in the expected output"
exit -1
}
puts "\n"
puts "----- test: 'call' command -----"
puts ""
send "call test_var_func()\n"
run_genode_until {\(gdb\)} 60 $gdb_id
if {![regexp {\$3 = 3} $output]} {
puts stderr "*** Error: 'call' command didn't result in the expected output"
exit -1
}
puts "\n"
puts "----- test: thread info -----"
puts ""
send "b test_thread_start\n"
run_genode_until {\(gdb\)} 20 $gdb_id
send "c\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {Breakpoint 4, test_thread_start} $output]} {
puts stderr "*** Error: Breakpoint in test thread did not trigger"
exit -1
}
send "info threads\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {\* 4 Thread 4 test_thread_start} $output] ||
![regexp { 3 Thread 3} $output] ||
![regexp { 2 Thread 2} $output] ||
![regexp { 1 Thread 1} $output]} {
puts stderr "*** Error: Thread info is not as expected"
exit -1
}
puts "\n"
puts "----- test: step into function -----"
puts ""
send "step\n"
run_genode_until {\(gdb\)} 30 $gdb_id
send "thread 2\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {test_thread_step} $output]} {
puts stderr "*** Error: Step into function didn't result in the expected output"
exit -1
}
puts "\n"
puts "----- test: catching a segmentation fault -----"
puts ""
send "c\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {Thread 4 received signal SIGSEGV, Segmentation fault.} $output]} {
puts stderr "*** Error: Segmentation fault exception was not caught"
exit -1
}
# does not work well on ARM yet
if {![have_spec arm]} {
puts "\n"
puts "----- test: stack trace when in syscall -----"
puts ""
send "thread 2\n"
run_genode_until {\(gdb\)} 20 $gdb_id
send "bt\n"
run_genode_until {\(gdb\)} 20 $gdb_id
if {![regexp {Genode::Cancelable_lock::lock} $output] ||
![regexp {Genode::Signal_receiver::block_for_signal} $output] ||
![regexp {Genode::Entrypoint::_wait_and_dispatch_one_io_signal} $output] ||
![regexp {Libc::Kernel::run} $output] } {
puts stderr "*** Error: Stack trace when in syscall is not as expected"
exit -1
}
}
puts ""
# vi: set ft=tcl :