From: Simon Marchi <simark@simark.ca>
To: Pedro Alves <pedro@palves.net>,
Simon Marchi <simon.marchi@efficios.com>,
gdb-patches@sourceware.org
Subject: Re: [PATCH 09/12] gdb: move displaced stepping logic to gdbarch, allow starting concurrent displaced steps
Date: Mon, 30 Nov 2020 15:26:55 -0500 [thread overview]
Message-ID: <da72a806-696f-e526-53b5-81ef9cca4188@simark.ca> (raw)
In-Reply-To: <598ee38c-b1d7-63d8-b8d0-36093e06f51f@palves.net>
On 2020-11-26 9:24 a.m., Pedro Alves wrote:
> The function still throws errors anyway, e.g. a bit above:
>
> int status = target_read_memory (m_addr, m_saved_copy.data (), len);
> if (status != 0)
> throw_error (MEMORY_ERROR,
> _("Error accessing memory address %s (%s) for "
> "displaced-stepping scratch space."),
> paddress (arch, m_addr), safe_strerror (status));
>
> That's then caught by displaced_step_prepare, which results in
> the "disabling displaced stepping" warning, and GDB disabling
> displaced stepping for the inferior.
>
> Also, NOT_SUPPORTED_ERROR is handled the same way. See:
>
> commit 16b4184277c4ad5b4a20278060fd3f6259d1ed49
> Author: Pedro Alves <palves@redhat.com>
> AuthorDate: Tue Mar 15 16:33:04 2016 +0000
>
> Fix PR gdb/19676: Disable displaced stepping if /proc not mounted
>
> Any other error is just propagated out, likely all the way to
> the prompt:
>
> static displaced_step_prepare_status
> displaced_step_prepare (thread_info *thread)
> {
> ...
> catch (const gdb_exception_error &ex)
> {
> struct displaced_step_inferior_state *displaced_state;
>
> if (ex.error != MEMORY_ERROR
> && ex.error != NOT_SUPPORTED_ERROR)
> throw;
>
>> Would it make sense to remove the _ERROR status code and report all
>> errors using exceptions then?
>
> I don't think so, because there are 3 kinds of "errors":
>
> #1 - MEMORY and NOT_SUPPORTED errors result in disabling displaced stepping
> for the inferior permanently.
>
> #2 - other errors/exceptions propagate outwards.
>
> #3 - "prepare" returning "Can't do displace step for this instruction" for
> some reason. Like the backend not supporting displaced stepping
> that particular instruction, or the breakpoint address falling
> within the scratch pad. I.e., these cases:
>
> ...
> if (breakpoint_in_range_p (aspace, m_addr, len))
> {
> /* There's a breakpoint set in the scratch pad location range
> (which is usually around the entry point). We'd either
> install it before resuming, which would overwrite/corrupt the
> scratch pad, or if it was already inserted, this displaced
> step would overwrite it. The latter is OK in the sense that
> we already assume that no thread is going to execute the code
> in the scratch pad range (after initial startup) anyway, but
> the former is unacceptable. Simply punt and fallback to
> stepping over this breakpoint in-line. */
> displaced_debug_printf ("breakpoint set in scratch pad. "
> "Stepping over breakpoint in-line instead.");
>
> return DISPLACED_STEP_PREPARE_STATUS_ERROR;
> }
> ...
> m_copy_insn_closure = gdbarch_displaced_step_copy_insn (arch,
> m_original_pc,
> m_addr,
> regcache);
> if (m_copy_insn_closure == nullptr)
> {
> /* The architecture doesn't know how or want to displaced step
> this instruction or instruction sequence. Fallback to
> stepping over the breakpoint in-line. */
> return DISPLACED_STEP_PREPARE_STATUS_ERROR;
> }
>
> The "failed to write pc" try/catch case seems debatable, but I'd
> lean on it being case #2.
>
> I think that case #3 calls for renaming DISPLACED_STEP_PREPARE_STATUS_ERROR
> to something else to avoid confusion. Note that in master, -1 wasn't
> documented in terms of error:
>
> ...
> Returns 1 if preparing was successful -- this thread is going to be
> stepped now; 0 if displaced stepping this thread got queued; or -1
> if this instruction can't be displaced stepped. */
>
> static int
> displaced_step_prepare_throw (thread_info *tp)
Ok, thanks for this explanation. I didn't understand well the different
error modes (can't displaced-step this particular instruction vs some
other unexpected error) and how they were handled.
I'll rename DISPLACED_STEP_PREPARE_STATUS_ERROR to
DISPLACED_STEP_PREPARE_STATUS_CANT. It fits well with the comments in
the code where it is used, that all say variants of "can't displaced
step this instruction, fall back on in-line stepping".
>>>> - return false;
>>>> + /* If there are threads left in the THREADS_TO_STEP list, but we have
>>>> + detected that we can't start anything more, put back these threads
>>>> + in the global list. */
>>>
>>> Do we also need to do this if an exception happens to escape the function?
>>> We might end up pretty bonkers anyhow if that happens, though...
>>
>> Yes, I realized that when playing with this code again yesterday. A
>> scope_exit would help for this I think.
>>
>> But I'm also wondering if we should enqueue back the thread TP that was
>> dequeued, that caused an error. This one has already been dequeued from
>> THREADS_TO_STEP. For example, if thread_still_needs_step_over throws
>> (it reads the PC, so that could happen), should we put back the thread
>> in the global chain? If we do, and the same error happens over and
>> over, the thread will never leave the step over chain and be the first
>> in line, preventing any other displaced step to happen.
>>
>> If, on the other hand, the error was just an intermittent one and we
>> don't put it back in the queue, the next time we'll resume the thread
>> we'll realize it needs a step over and enqueue it again. So it sounds
>> less risky to me to just not enqueue back the thread on error. I'm
>> really not sure, I find it difficult to reason about all the possible
>> cases.
>
> Yeah, not enqueuing sounds OK. And indeed, error handling in all of
> infrun is very hard to reason about, and likely to end up with things
> messed up, e.g., with a thread marked as running when it is really
> stopped.
Ok, did that.
>> Also, re-reading that code, I noticed that we only return "true" when an
>> inline step over is started, or if a displaced step on an all-stop
>> target was started. That's the case even before the patch. I suppose
>> that's on purpose, because the caller wants to know whether it can
>> resume more stuff or not?
>
> Yes. If it returns true, then the caller shouldn't resume anything
> and go straight to waiting. ISTR having some trouble coming up with
> the right conditions at the callers. Maybe it could be simplified.
> I no longer remember why start_step_over returns false here:
>
> /* Don't start a new step-over if we already have an in-line
> step-over operation ongoing. */
> if (step_over_info_valid_p ())
> return false;
>
> Off hand, it seems like that should return true. But it may
> be that the intention was to only return true if anything
> that was stopped before was started. I'd need to experiment
> to know better now. :-/
Ok, I'll leave it as-is for now and treat is as a separete issue.
>>>> @@ -5293,25 +5226,23 @@ handle_inferior_event (struct execution_control_state *ecs)
>>>> {
>>>> struct regcache *regcache = get_thread_regcache (ecs->event_thread);
>>>> struct gdbarch *gdbarch = regcache->arch ();
>>>> + inferior *parent_inf = find_inferior_ptid (ecs->target, ecs->ptid);
>>>> +
>>>> + if (ecs->ws.kind == TARGET_WAITKIND_FORKED)
>>>> + {
>>>> + /* Restore in the child process any displaced stepping buffers that
>>>> + were in use at the time of the fork. */
>>>> + gdbarch_displaced_step_restore_all_in_ptid
>>>> + (gdbarch, parent_inf, ecs->ws.value.related_pid);
>>>> + }
>>>
>>> Why was this moved out of the displaced_step_in_progress_thread check
>>> below?
>>
>> If:
>>
>> 1. thread 1 is doing a displaced step
>> 2. thread 2 does a fork, not letting time for thread 1 to complete
>>
>> event_thread is thread 2, and it is not doing a displaced step, so we
>> don't enter the
>>
>> if (displaced_step_in_progress_thread (ecs->event_thread))
>>
>> But we still want to restore the bytes in the child used as the
>> displaced stepping buffer for thread 1. Does that make sense? Is it
>> possible that the current code is not correct in that regard?
>
> Indeed, looks like it. Good catch. How about fixing that
> in a separate patch, then, with its own commit log?
Will do. I didn't do a separate patch at first because I initially
thought: "now that we can do multiple displaced steps in parallel, it
can be that the other displaced steps does a fork!". I didn't think
right away of the case where it's a non-displaced-stepping thread who
does a fork.
Simon
next prev parent reply other threads:[~2020-11-30 20:27 UTC|newest]
Thread overview: 44+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-11-10 21:46 [PATCH 00/12] Concurrent displaced stepping Simon Marchi via Gdb-patches
2020-11-10 21:46 ` [PATCH 01/12] gdb: add inferior_execd observable Simon Marchi via Gdb-patches
2020-11-25 1:28 ` Pedro Alves
2020-11-10 21:46 ` [PATCH 02/12] gdb: clear inferior displaced stepping state on exec Simon Marchi via Gdb-patches
2020-11-25 1:28 ` Pedro Alves
2020-12-01 4:27 ` Simon Marchi
2020-11-10 21:46 ` [PATCH 03/12] gdb: rename things related to step over chains Simon Marchi via Gdb-patches
2020-11-25 1:28 ` Pedro Alves
2020-11-25 13:16 ` Simon Marchi via Gdb-patches
2020-11-10 21:46 ` [PATCH 04/12] gdb: rename displaced_step_closure to displaced_step_copy_insn_closure Simon Marchi via Gdb-patches
2020-11-25 1:29 ` Pedro Alves
2020-11-10 21:46 ` [PATCH 05/12] gdb: rename displaced_step_fixup to displaced_step_finish Simon Marchi via Gdb-patches
2020-11-25 1:29 ` Pedro Alves
2020-11-10 21:46 ` [PATCH 06/12] gdb: introduce status enum for displaced step prepare/finish Simon Marchi via Gdb-patches
2020-11-11 23:36 ` Andrew Burgess
2020-11-25 13:17 ` Simon Marchi via Gdb-patches
2020-11-25 1:30 ` Pedro Alves
2020-11-25 13:20 ` Simon Marchi via Gdb-patches
2020-11-10 21:46 ` [PATCH 07/12] gdb: pass inferior to get_linux_inferior_data Simon Marchi via Gdb-patches
2020-11-25 1:30 ` Pedro Alves
2020-11-10 21:46 ` [PATCH 08/12] gdb: move displaced stepping types to displaced-stepping.{h, c} Simon Marchi via Gdb-patches
2020-11-25 1:30 ` Pedro Alves
2020-11-10 21:46 ` [PATCH 09/12] gdb: move displaced stepping logic to gdbarch, allow starting concurrent displaced steps Simon Marchi via Gdb-patches
2020-11-25 1:40 ` Pedro Alves
2020-11-25 19:29 ` Simon Marchi via Gdb-patches
2020-11-25 19:35 ` Simon Marchi
2020-11-26 14:25 ` Pedro Alves
2020-11-30 19:13 ` Simon Marchi via Gdb-patches
2020-11-26 14:24 ` Pedro Alves
2020-11-30 20:26 ` Simon Marchi [this message]
2020-11-10 21:46 ` [PATCH 10/12] gdb: change linux gdbarch data from post to pre-init Simon Marchi via Gdb-patches
2020-11-25 1:41 ` Pedro Alves
2020-11-10 21:46 ` [PATCH 11/12] gdb: make displaced stepping implementation capable of managing multiple buffers Simon Marchi via Gdb-patches
2020-11-25 1:41 ` Pedro Alves
2020-11-30 18:58 ` Simon Marchi
2020-11-30 19:01 ` Simon Marchi
2020-11-10 21:46 ` [PATCH 12/12] gdb: use two displaced step buffers on amd64/Linux Simon Marchi via Gdb-patches
2020-11-25 1:42 ` Pedro Alves
2020-11-25 6:26 ` Simon Marchi
2020-11-25 20:07 ` Simon Marchi
2020-11-25 20:56 ` Simon Marchi
2020-11-26 21:43 ` Simon Marchi
2020-11-26 22:34 ` Simon Marchi
2020-11-28 18:56 ` Pedro Alves
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=da72a806-696f-e526-53b5-81ef9cca4188@simark.ca \
--to=simark@simark.ca \
--cc=gdb-patches@sourceware.org \
--cc=pedro@palves.net \
--cc=simon.marchi@efficios.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox