public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/3] gdbserver/linux: access memory while threads are running without pausing
@ 2022-03-30 12:43 Pedro Alves
  2022-03-30 12:43 ` [PATCH 1/3] gdbserver/qXfer::threads, prepare_to_access_memory=>target_pause_all Pedro Alves
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Pedro Alves @ 2022-03-30 12:43 UTC (permalink / raw)
  To: gdb-patches

While working on teaching GDBserver to handle
stepping-over-thread-exit better, I stumbled on the following
scenario:

If you set a conditional breakpoint, and condition evaluation is done
on the target (which it is against gdbserver), GDB uploads the agent
expression for the condition to GDBserver.  When the breakpoint is
hit, GDBserver evaluates the condition, and if false, GDBserver steps
over the breakpoint itself, all without GDB involvement.  That
single-step is done in-line, GDBserver doesn't know how to do
displaced stepping.  That means that all threads are paused
temporarily for the single-step, the breakpoint is removed, and when
the single-step finishes, the breakpoint instruction is inserted
again.

Now, if that breakpoint was set on top of an exit syscall instruction,
then when the single-step over the breakpoint finishes, and GDBserver
tries to re-insert the breakpoint, the thread that was stepping is
gone.  And thus you get something like this:

  ...
  [threads] wait_1: Hit a gdbserver breakpoint.
  [threads] ax_vdebug: gdbserver/ax: About to interpret byte 0x22
  [threads] ax_vdebug: gdbserver/ax: Op const8 -> sp=1, top=0x0
  [threads] ax_vdebug: gdbserver/ax: About to interpret byte 0x27
  [threads] ax_vdebug: gdbserver/ax: At end of expression, sp=1, stack top cache=0x0
  [threads] wait_1: Hit a gdbserver breakpoint.
  [threads] wait_1: proceeding all threads.
  [threads] thread_needs_step_over: Need step over [LWP 864044]? Ignoring, not stopped
  [threads] thread_needs_step_over: Need step over [LWP 864578]? Ignoring, not stopped
  [threads] thread_needs_step_over: Need step over [LWP 864579]? Ignoring, has pending status.
  [threads] get_pc: pc is 0x5555555552a8
  [threads] ax_vdebug: gdbserver/ax: About to interpret byte 0x22
  [threads] ax_vdebug: gdbserver/ax: Op const8 -> sp=1, top=0x0
  [threads] ax_vdebug: gdbserver/ax: About to interpret byte 0x27
  [threads] ax_vdebug: gdbserver/ax: At end of expression, sp=1, stack top cache=0x0
  [threads] thread_needs_step_over: Need step over [LWP 864580]? yes, found breakpoint at 0x5555555552a8
  [threads] proceed_all_lwps: found thread 864580 needing a step-over
  [threads] start_step_over: Starting step-over on LWP 864580.  Stopping all threads
  [threads] stop_all_lwps: enter
  ...
  [threads] stop_all_lwps: exit
  [threads] start_step_over: Done stopping all threads for step-over.
  ...
  [threads] wait_for_event_filtered: waitpid(-1, ...) returned 864580, ERRNO-OK
  [threads] wait_for_event_filtered: waitpid 864580 received 0 (exited)
  [threads] filter_event: 864580 exited
  [threads] finish_step_over: Finished step over.
  [threads] insert_memory_breakpoint: Failed to read shadow memory of breakpoint at 0x5555555552a8 (No such process).
  [threads] reinsert_raw_breakpoint: Failed to reinsert breakpoint at 0x5555555552a8 (-1).
  [threads] reinsert_fast_tracepoint_jumps_at: Could not find fast tracepoint jump at 0x5555555552a8 in list (reinserting).
  [threads] delete_lwp: deleting 864580
  ...

Note the:

  [threads] insert_memory_breakpoint: Failed to read shadow memory of breakpoint at 0x5555555552a8 (No such process).
  [threads] reinsert_raw_breakpoint: Failed to reinsert breakpoint at 0x5555555552a8 (-1).

lines.  GDB fails to reinsert the breakpoint, because the event thread
is gone.  PTRACE_PEEKTEXT/PTRACE_POKETEXT don't work if you pass it a
dead thread.

OTOH, currently, from GDB's perspective, GDBserver/Linux does support
accessing memory while the program is running.  This is important for
e.g., for being able to set breakpoints while the inferior is running,
in non-stop mode.  The way this is implemented today, is that when
handling any remote protocol packet that requires accessing memory,
GDBserver temporarily pauses all threads.  This is done by wrapping
memory accesses with prepare_to_access_memory/done_accessing_memory.
prepare_to_access_memory ultimately calls into the target backend,
which does whatever is necessary to be able to prepare for accessing
memory.  For Linux, that currently translates to pausing everything,
via target_pause_all.  No other port supports non-stop, so these
prepare_to_access_memory/done_accessing_memory calls do nothing for
all other ports.  Now, this memory-access wrapping is only done in
GDB-facing code.  Internally in linux-low.cc, when handling an event,
we tend to know that the current thread is stopped, so we usually can
access memory without pausing everything else.  However, in the
scenario detailed above, the eventing thread has exited.  We would
need to find some other stopped thread to write memory through, and if
none found, stop some.

I guess you see where I'm going with this.  It's so much simpler if we
teach GDBserver to always access memory via /proc/PID/mem.  Then we
don't need to worry about whether the current thread is running, or is
gone.  We just access memory and it Just Works.

So that's what this series does -- it teaches GDBserver to always
access memory via /proc/PID/mem, and then eliminates the whole
prepare_to_access_memory mechanism.

Even without the scenario detailed above, not having to pause threads
all the time just makes sense.

Pedro Alves (3):
  gdbserver/qXfer::threads, prepare_to_access_memory=>target_pause_all
  gdbserver/linux: Access memory even if threads are running
  gdbserver: Eliminate prepare_to_access_memory

 gdbserver/linux-low.cc  | 237 ++++++++++++++--------------------------
 gdbserver/linux-low.h   |  11 +-
 gdbserver/mem-break.cc  | 101 +++--------------
 gdbserver/server.cc     |  81 +++++---------
 gdbserver/target.cc     |  94 ----------------
 gdbserver/target.h      |  21 ----
 gdbserver/tracepoint.cc |  13 +--
 7 files changed, 131 insertions(+), 427 deletions(-)


base-commit: 5321c31bc78379a33f07dc7bef9256d05b942ad7
-- 
2.26.2


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2022-04-04 14:34 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-30 12:43 [PATCH 0/3] gdbserver/linux: access memory while threads are running without pausing Pedro Alves
2022-03-30 12:43 ` [PATCH 1/3] gdbserver/qXfer::threads, prepare_to_access_memory=>target_pause_all Pedro Alves
2022-03-30 12:43 ` [PATCH 2/3] gdbserver/linux: Access memory even if threads are running Pedro Alves
2022-03-30 13:22   ` Lancelot SIX
2022-03-31 11:39     ` Pedro Alves
2022-04-04 14:34       ` Pedro Alves
2022-03-30 12:43 ` [PATCH 3/3] gdbserver: Eliminate prepare_to_access_memory Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).