aboutsummaryrefslogtreecommitdiff
path: root/gdb/target.c
diff options
context:
space:
mode:
authorSimon Marchi <simon.marchi@polymtl.ca>2020-10-13 12:01:19 -0400
committerSimon Marchi <simon.marchi@polymtl.ca>2020-10-13 12:01:19 -0400
commitd3a071228e8f7cf9da017f2d0d6c28468a652795 (patch)
tree34b01cde0d8577fed1503dd76cfb772b92de66e7 /gdb/target.c
parentb07993f695bdca0153ee7b9123be9348f6aad4a6 (diff)
downloadgdb-d3a071228e8f7cf9da017f2d0d6c28468a652795.zip
gdb-d3a071228e8f7cf9da017f2d0d6c28468a652795.tar.gz
gdb-d3a071228e8f7cf9da017f2d0d6c28468a652795.tar.bz2
gdb: don't pass TARGET_WNOHANG to targets that can't async (PR 26642)
Debugging with "maintenance set target-async off" on Linux has been broken since 5b6d1e4fa4f ("Multi-target support"). The issue is easy to reproduce: $ ./gdb -q --data-directory=data-directory -nx ./test Reading symbols from ./test... (gdb) maintenance set target-async off (gdb) start Temporary breakpoint 1 at 0x1151: file test.c, line 5. Starting program: /home/simark/build/binutils-gdb/gdb/test ... and it hangs there. The difference between pre-5b6d1e4fa4f and 5b6d1e4fa4f is that fetch_inferior_event now calls target_wait with TARGET_WNOHANG for non-async-capable targets, whereas it didn't before. For non-async-capable targets, this is how it's expected to work when resuming execution: 1. we call resume 2. the infrun async handler is marked in prepare_to_wait, to immediately wake up the event loop when we get back to it 3. fetch_inferior_event calls the target's wait method without TARGET_WNOHANG, effectively blocking until the target has something to report However, since we call the target's wait method with TARGET_WNOHANG, this happens: 1. we call resume 2. the infrun async handler is marked in prepare_to_wait, to immediately wake up the event loop when we get back to it 3. fetch_inferior_event calls the target's wait method with TARGET_WNOHANG, the target has nothing to report yet 4. we go back to blocking on the event loop 5. SIGCHLD finally arrives, but the event loop is not woken up, because we are not in async mode. Normally, we should have been stuck in waitpid the SIGCHLD would have unblocked us. We end up in this situation because these two necessary conditions are met: 1. GDB uses the TARGET_WNOHANG option with a target that can't do async. I don't think this makes sense. I mean, it's technically possible, the doc for TARGET_WNOHANG is: /* Return immediately if there's no event already queued. If this options is not requested, target_wait blocks waiting for an event. */ TARGET_WNOHANG = 1, ... which isn't in itself necessarily incompatible with synchronous targets. It could be possible for a target to support non-blocking polls, while not having a way to asynchronously wake up the event loop, which is also necessary to support async. But as of today, we don't expect GDB and sync targets to work this way. 2. The linux-nat target, even in the mode where it emulates a synchronous target (with "maintenance set target-async off") respects TARGET_WNOHANG. Other non-async targets, such as windows_nat_target, simply don't check / support TARGET_WNOHANG, so their wait method is always blocking. Fix the first issue by avoiding using TARGET_WNOHANG on non-async targets, in do_target_wait_1. Add an assert in target_wait to verify it doesn't happen. The new test gdb.base/maint-target-async-off.exp is a simple test that just tries running to main and then to the end of the program, with "maintenance set target-async off". gdb/ChangeLog: PR gdb/26642 * infrun.c (do_target_wait_1): Clear TARGET_WNOHANG if the target can't do async. * target.c (target_wait): Assert that we don't pass TARGET_WNOHANG to a target that can't async. gdb/testsuite/ChangeLog: PR gdb/26642 * gdb.base/maint-target-async-off.c: New test. * gdb.base/maint-target-async-off.exp: New test. Change-Id: I69ad3a14598863d21338a8c4e78700a58ce7ad86
Diffstat (limited to 'gdb/target.c')
-rw-r--r--gdb/target.c7
1 files changed, 6 insertions, 1 deletions
diff --git a/gdb/target.c b/gdb/target.c
index 531858a..a111ea3 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -1992,7 +1992,12 @@ ptid_t
target_wait (ptid_t ptid, struct target_waitstatus *status,
target_wait_flags options)
{
- return current_top_target ()->wait (ptid, status, options);
+ target_ops *target = current_top_target ();
+
+ if (!target->can_async_p ())
+ gdb_assert ((options & TARGET_WNOHANG) == 0);
+
+ return target->wait (ptid, status, options);
}
/* See target.h. */