mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
Compare commits
10 Commits
efbd9add96
...
users/palv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a99047e605 | ||
|
|
1ad36a4b89 | ||
|
|
84c63c819e | ||
|
|
6f07d888c0 | ||
|
|
419e460add | ||
|
|
367aff81be | ||
|
|
85dae34612 | ||
|
|
f7bf4732bb | ||
|
|
ce502e47c0 | ||
|
|
14b98a10ca |
@@ -448,14 +448,10 @@ find_gnu_ifunc_target_type (CORE_ADDR resolver_funaddr)
|
||||
struct frame_info *
|
||||
block_innermost_frame (const struct block *block)
|
||||
{
|
||||
struct frame_info *frame;
|
||||
|
||||
if (block == NULL)
|
||||
return NULL;
|
||||
|
||||
frame = get_selected_frame_if_set ();
|
||||
if (frame == NULL)
|
||||
frame = get_current_frame ();
|
||||
frame_info *frame = get_selected_frame (NULL);
|
||||
while (frame != NULL)
|
||||
{
|
||||
const struct block *frame_block = get_frame_block (frame, NULL);
|
||||
|
||||
@@ -377,7 +377,6 @@ dwarf2_tailcall_sniffer_first (struct frame_info *this_frame,
|
||||
get_frame_address_in_block will decrease it by 1 in such case. */
|
||||
this_pc = get_frame_address_in_block (this_frame);
|
||||
|
||||
/* Catch any unwinding errors. */
|
||||
try
|
||||
{
|
||||
int sp_regnum;
|
||||
@@ -439,7 +438,22 @@ dwarf2_tailcall_sniffer_first (struct frame_info *this_frame,
|
||||
{
|
||||
if (entry_values_debug)
|
||||
exception_print (gdb_stdout, except);
|
||||
return;
|
||||
|
||||
switch (except.error)
|
||||
{
|
||||
case NO_ENTRY_VALUE_ERROR:
|
||||
/* Thrown by call_site_find_chain. */
|
||||
case MEMORY_ERROR:
|
||||
case OPTIMIZED_OUT_ERROR:
|
||||
case NOT_AVAILABLE_ERROR:
|
||||
/* These can normally happen when we try to access an
|
||||
optimized out or unavailable register, either in a
|
||||
physical register or spilled to memory. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Let unexpected errors propagate. */
|
||||
throw;
|
||||
}
|
||||
|
||||
/* Ambiguous unwind or unambiguous unwind verified as matching. */
|
||||
|
||||
99
gdb/frame.c
99
gdb/frame.c
@@ -297,17 +297,15 @@ frame_stash_invalidate (void)
|
||||
/* See frame.h */
|
||||
scoped_restore_selected_frame::scoped_restore_selected_frame ()
|
||||
{
|
||||
m_fid = get_frame_id (get_selected_frame (NULL));
|
||||
m_lang = current_language->la_language;
|
||||
save_selected_frame (&m_fid, &m_level);
|
||||
}
|
||||
|
||||
/* See frame.h */
|
||||
scoped_restore_selected_frame::~scoped_restore_selected_frame ()
|
||||
{
|
||||
frame_info *frame = frame_find_by_id (m_fid);
|
||||
if (frame == NULL)
|
||||
warning (_("Unable to restore previously selected frame."));
|
||||
else
|
||||
select_frame (frame);
|
||||
restore_selected_frame (m_fid, m_level);
|
||||
set_language (m_lang);
|
||||
}
|
||||
|
||||
/* Flag to control debugging. */
|
||||
@@ -1641,10 +1639,51 @@ get_current_frame (void)
|
||||
}
|
||||
|
||||
/* The "selected" stack frame is used by default for local and arg
|
||||
access. May be zero, for no selected frame. */
|
||||
access. */
|
||||
|
||||
/* The "single source of truth" for the selected frame is the
|
||||
SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL pair. Frame IDs can be
|
||||
saved/restored across reinitializing the frame cache, while
|
||||
frame_info pointers can't (frame_info objects are invalidated). If
|
||||
we know the corresponding frame_info object, it is cached in
|
||||
SELECTED_FRAME. If SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL are
|
||||
null_ptid / -1, and the target has stack and is stopped, the
|
||||
selected frame is the current (innermost) frame, otherwise there's
|
||||
no selected frame. */
|
||||
static frame_id selected_frame_id = null_frame_id;
|
||||
static int selected_frame_level = -1;
|
||||
|
||||
/* The cached frame_info object pointing to the selected frame.
|
||||
Looked up on demand by get_selected_frame. */
|
||||
static struct frame_info *selected_frame;
|
||||
|
||||
/* See frame.h. */
|
||||
|
||||
void
|
||||
save_selected_frame (frame_id *frame_id, int *frame_level)
|
||||
noexcept
|
||||
{
|
||||
*frame_id = selected_frame_id;
|
||||
*frame_level = selected_frame_level;
|
||||
}
|
||||
|
||||
/* See frame.h. */
|
||||
|
||||
void
|
||||
restore_selected_frame (frame_id a_frame_id, int frame_level)
|
||||
noexcept
|
||||
{
|
||||
/* save_selected_frame never returns level == 0, so we shouldn't see
|
||||
it here either. */
|
||||
gdb_assert (frame_level != 0);
|
||||
|
||||
selected_frame_id = a_frame_id;
|
||||
selected_frame_level = frame_level;
|
||||
|
||||
/* Will be looked up latter by get_seleted_frame. */
|
||||
selected_frame = nullptr;
|
||||
}
|
||||
|
||||
int
|
||||
has_stack_frames (void)
|
||||
{
|
||||
@@ -1682,24 +1721,14 @@ get_selected_frame (const char *message)
|
||||
{
|
||||
if (message != NULL && !has_stack_frames ())
|
||||
error (("%s"), message);
|
||||
/* Hey! Don't trust this. It should really be re-finding the
|
||||
last selected frame of the currently selected thread. This,
|
||||
though, is better than nothing. */
|
||||
select_frame (get_current_frame ());
|
||||
|
||||
lookup_selected_frame (selected_frame_id, selected_frame_level);
|
||||
}
|
||||
/* There is always a frame. */
|
||||
gdb_assert (selected_frame != NULL);
|
||||
return selected_frame;
|
||||
}
|
||||
|
||||
/* If there is a selected frame, return it. Otherwise, return NULL. */
|
||||
|
||||
struct frame_info *
|
||||
get_selected_frame_if_set (void)
|
||||
{
|
||||
return selected_frame;
|
||||
}
|
||||
|
||||
/* This is a variant of get_selected_frame() which can be called when
|
||||
the inferior does not have a frame; in that case it will return
|
||||
NULL instead of calling error(). */
|
||||
@@ -1712,12 +1741,42 @@ deprecated_safe_get_selected_frame (void)
|
||||
return get_selected_frame (NULL);
|
||||
}
|
||||
|
||||
/* Select frame FI (or NULL - to invalidate the current frame). */
|
||||
/* Select frame FI (or NULL - to invalidate the selected frame). */
|
||||
|
||||
void
|
||||
select_frame (struct frame_info *fi)
|
||||
{
|
||||
selected_frame = fi;
|
||||
selected_frame_level = frame_relative_level (fi);
|
||||
if (selected_frame_level == 0)
|
||||
{
|
||||
/* Treat the current frame especially -- we want to always
|
||||
save/restore it without warning, even if the frame ID changes
|
||||
(see lookup_selected_frame). E.g.:
|
||||
|
||||
// The current frame is selected, the target had just stopped.
|
||||
{
|
||||
scoped_restore_selected_frame restore_frame;
|
||||
some_operation_that_changes_the_stack ();
|
||||
}
|
||||
// scoped_restore_selected_frame's dtor runs, but the
|
||||
// original frame_id can't be found. No matter whether it
|
||||
// is found or not, we still end up with the now-current
|
||||
// frame selected. Warning in lookup_selected_frame in this
|
||||
// case seems pointless.
|
||||
|
||||
Also get_frame_id may access the target's registers/memory,
|
||||
and thus skipping get_frame_id optimizes the common case.
|
||||
|
||||
Saving the selected frame this way makes get_selected_frame
|
||||
and restore_current_frame return/re-select whatever frame is
|
||||
the innermost (current) then. */
|
||||
selected_frame_level = -1;
|
||||
selected_frame_id = null_frame_id;
|
||||
}
|
||||
else
|
||||
selected_frame_id = get_frame_id (fi);
|
||||
|
||||
/* NOTE: cagney/2002-05-04: FI can be NULL. This occurs when the
|
||||
frame is being invalidated. */
|
||||
|
||||
|
||||
38
gdb/frame.h
38
gdb/frame.h
@@ -181,8 +181,14 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
/* The ID of the previously selected frame. */
|
||||
/* The ID and level of the previously selected frame. */
|
||||
struct frame_id m_fid;
|
||||
int m_level;
|
||||
|
||||
/* Save/restore the language as well, because selecting a frame
|
||||
changes the current language to the frame's language if "set
|
||||
language auto". */
|
||||
enum language m_lang;
|
||||
};
|
||||
|
||||
/* Methods for constructing and comparing Frame IDs. */
|
||||
@@ -329,13 +335,33 @@ extern void reinit_frame_cache (void);
|
||||
and then return that thread's previously selected frame. */
|
||||
extern struct frame_info *get_selected_frame (const char *message);
|
||||
|
||||
/* If there is a selected frame, return it. Otherwise, return NULL. */
|
||||
extern struct frame_info *get_selected_frame_if_set (void);
|
||||
|
||||
/* Select a specific frame. NULL, apparently implies re-select the
|
||||
inner most frame. */
|
||||
/* Select a specific frame. NULL implies re-select the inner most
|
||||
frame. */
|
||||
extern void select_frame (struct frame_info *);
|
||||
|
||||
/* Save the frame ID and frame level of the selected frame in FRAME_ID
|
||||
and FRAME_LEVEL, to be restored later with restore_selected_frame.
|
||||
This is preferred over getting the same info out of
|
||||
get_selected_frame directly because this function does not create
|
||||
the selected-frame's frame_info object if it hasn't been created
|
||||
yet, and thus doesn't throw. */
|
||||
extern void save_selected_frame (frame_id *frame_id, int *frame_level)
|
||||
noexcept;
|
||||
|
||||
/* Restore selected frame as saved with save_selected_frame. Does not
|
||||
try to find the corresponding frame_info object. Instead the next
|
||||
call to get_selected_frame will look it up and cache the result.
|
||||
This function does not throw, it is designed to be safe to called
|
||||
from the destructors of RAII types. */
|
||||
extern void restore_selected_frame (frame_id frame_id, int frame_level)
|
||||
noexcept;
|
||||
|
||||
/* Lookup the frame_info object for the selected frame FRAME_ID /
|
||||
FRAME_LEVEL and cache the result. If FRAME_LEVEL > 0 and the
|
||||
originally selected frame isn't found, warn and select the
|
||||
innermost (current) frame. */
|
||||
extern void lookup_selected_frame (frame_id frame_id, int frame_level);
|
||||
|
||||
/* Given a FRAME, return the next (more inner, younger) or previous
|
||||
(more outer, older) frame. */
|
||||
extern struct frame_info *get_prev_frame (struct frame_info *);
|
||||
|
||||
@@ -667,6 +667,10 @@ private:
|
||||
frame_id m_selected_frame_id;
|
||||
int m_selected_frame_level;
|
||||
bool m_was_stopped;
|
||||
/* Save/restore the language as well, because selecting a frame
|
||||
changes the current language to the frame's language if "set
|
||||
language auto". */
|
||||
enum language m_lang;
|
||||
};
|
||||
|
||||
/* Returns a pointer into the thread_info corresponding to
|
||||
|
||||
116
gdb/infrun.c
116
gdb/infrun.c
@@ -3601,23 +3601,9 @@ do_target_wait_1 (inferior *inf, ptid_t ptid,
|
||||
return event_ptid;
|
||||
}
|
||||
|
||||
/* Returns true if INF has any resumed thread with a status
|
||||
pending. */
|
||||
|
||||
static bool
|
||||
threads_are_resumed_pending_p (inferior *inf)
|
||||
{
|
||||
for (thread_info *tp : inf->non_exited_threads ())
|
||||
if (tp->resumed
|
||||
&& tp->suspend.waitstatus_pending_p)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Wrapper for target_wait that first checks whether threads have
|
||||
pending statuses to report before actually asking the target for
|
||||
more events. Polls for events from all inferiors/targets. */
|
||||
more events. Polls for events from all inferiors/targets. */
|
||||
|
||||
static bool
|
||||
do_target_wait (ptid_t wait_ptid, execution_control_state *ecs, int options)
|
||||
@@ -3625,20 +3611,18 @@ do_target_wait (ptid_t wait_ptid, execution_control_state *ecs, int options)
|
||||
int num_inferiors = 0;
|
||||
int random_selector;
|
||||
|
||||
/* For fairness, we pick the first inferior/target to poll at
|
||||
random, and then continue polling the rest of the inferior list
|
||||
starting from that one in a circular fashion until the whole list
|
||||
is polled once. */
|
||||
/* For fairness, we pick the first inferior/target to poll at random
|
||||
out of all inferiors that may report events, and then continue
|
||||
polling the rest of the inferior list starting from that one in a
|
||||
circular fashion until the whole list is polled once. */
|
||||
|
||||
auto inferior_matches = [&wait_ptid] (inferior *inf)
|
||||
{
|
||||
return (inf->process_target () != NULL
|
||||
&& (threads_are_executing (inf->process_target ())
|
||||
|| threads_are_resumed_pending_p (inf))
|
||||
&& ptid_t (inf->pid).matches (wait_ptid));
|
||||
};
|
||||
|
||||
/* First see how many resumed inferiors we have. */
|
||||
/* First see how many matching inferiors we have. */
|
||||
for (inferior *inf : all_inferiors ())
|
||||
if (inferior_matches (inf))
|
||||
num_inferiors++;
|
||||
@@ -3649,7 +3633,7 @@ do_target_wait (ptid_t wait_ptid, execution_control_state *ecs, int options)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Now randomly pick an inferior out of those that were resumed. */
|
||||
/* Now randomly pick an inferior out of those that matched. */
|
||||
random_selector = (int)
|
||||
((num_inferiors * (double) rand ()) / (RAND_MAX + 1.0));
|
||||
|
||||
@@ -3658,7 +3642,7 @@ do_target_wait (ptid_t wait_ptid, execution_control_state *ecs, int options)
|
||||
"infrun: Found %d inferiors, starting at #%d\n",
|
||||
num_inferiors, random_selector);
|
||||
|
||||
/* Select the Nth inferior that was resumed. */
|
||||
/* Select the Nth inferior that matched. */
|
||||
|
||||
inferior *selected = nullptr;
|
||||
|
||||
@@ -3670,7 +3654,7 @@ do_target_wait (ptid_t wait_ptid, execution_control_state *ecs, int options)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now poll for events out of each of the resumed inferior's
|
||||
/* Now poll for events out of each of the matching inferior's
|
||||
targets, starting from the selected one. */
|
||||
|
||||
auto do_wait = [&] (inferior *inf)
|
||||
@@ -3680,8 +3664,8 @@ do_target_wait (ptid_t wait_ptid, execution_control_state *ecs, int options)
|
||||
return (ecs->ws.kind != TARGET_WAITKIND_IGNORE);
|
||||
};
|
||||
|
||||
/* Needed in all-stop+target-non-stop mode, because we end up here
|
||||
spuriously after the target is all stopped and we've already
|
||||
/* Needed in 'all-stop + target-non-stop' mode, because we end up
|
||||
here spuriously after the target is all stopped and we've already
|
||||
reported the stop to the user, polling for events. */
|
||||
scoped_restore_current_thread restore_thread;
|
||||
|
||||
@@ -5068,23 +5052,77 @@ handle_no_resumed (struct execution_control_state *ecs)
|
||||
have resumed threads _now_. In the example above, this removes
|
||||
thread 3 from the thread list. If thread 2 was re-resumed, we
|
||||
ignore this event. If we find no thread resumed, then we cancel
|
||||
the synchronous command show "no unwaited-for " to the user. */
|
||||
update_thread_list ();
|
||||
the synchronous command and show "no unwaited-for " to the
|
||||
user. */
|
||||
|
||||
for (thread_info *thread : all_non_exited_threads (ecs->target))
|
||||
inferior *curr_inf = current_inferior ();
|
||||
|
||||
scoped_restore_current_thread restore_thread;
|
||||
|
||||
for (auto *target : all_non_exited_process_targets ())
|
||||
{
|
||||
if (thread->executing
|
||||
|| thread->suspend.waitstatus_pending_p)
|
||||
switch_to_target_no_thread (target);
|
||||
update_thread_list ();
|
||||
}
|
||||
|
||||
/* If:
|
||||
|
||||
- the current target has no thread executing, and
|
||||
- the current inferior is native, and
|
||||
- the current inferior is the one which has the terminal, and
|
||||
- we did nothing,
|
||||
|
||||
then a Ctrl-C from this point on would remain stuck in the
|
||||
kernel, until a thread resumes and dequeues it. That would
|
||||
result in the GDB CLI not reacting to Ctrl-C, not able to
|
||||
interrupt the program. To address this, if the current inferior
|
||||
no longer has any thread executing, we give the terminal to some
|
||||
other inferior that has at least one thread executing. */
|
||||
bool swap_terminal = true;
|
||||
|
||||
/* Whether to ignore this TARGET_WAITKIND_NO_RESUMED event, or
|
||||
whether to report it to the user. */
|
||||
bool ignore_event = false;
|
||||
|
||||
for (thread_info *thread : all_non_exited_threads ())
|
||||
{
|
||||
if (swap_terminal && thread->executing)
|
||||
{
|
||||
/* There were no unwaited-for children left in the target at
|
||||
some point, but there are now. Just ignore. */
|
||||
if (thread->inf != curr_inf)
|
||||
{
|
||||
target_terminal::ours ();
|
||||
|
||||
switch_to_thread (thread);
|
||||
target_terminal::inferior ();
|
||||
}
|
||||
swap_terminal = false;
|
||||
}
|
||||
|
||||
if (!ignore_event
|
||||
&& (thread->executing
|
||||
|| thread->suspend.waitstatus_pending_p))
|
||||
{
|
||||
/* Either there were no unwaited-for children left in the
|
||||
target at some point, but there are now, or some target
|
||||
other than the eventing one has unwaited-for children
|
||||
left. Just ignore. */
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"infrun: TARGET_WAITKIND_NO_RESUMED "
|
||||
"(ignoring: found resumed)\n");
|
||||
prepare_to_wait (ecs);
|
||||
return 1;
|
||||
|
||||
ignore_event = true;
|
||||
}
|
||||
|
||||
if (ignore_event && !swap_terminal)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ignore_event)
|
||||
{
|
||||
switch_to_inferior_no_thread (curr_inf);
|
||||
prepare_to_wait (ecs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Go ahead and report the event. */
|
||||
@@ -8116,7 +8154,11 @@ prepare_to_wait (struct execution_control_state *ecs)
|
||||
|
||||
ecs->wait_some_more = 1;
|
||||
|
||||
if (!target_is_async_p ())
|
||||
/* If the target can't async, emulate it by marking the infrun event
|
||||
handler such that as soon as we get back to the event-loop, we
|
||||
immediately end up in fetch_inferior_event again calling
|
||||
target_wait. */
|
||||
if (!target_can_async_p ())
|
||||
mark_infrun_async_event_handler ();
|
||||
}
|
||||
|
||||
|
||||
15
gdb/remote.c
15
gdb/remote.c
@@ -5605,7 +5605,7 @@ remote_target::open_1 (const char *name, int from_tty, int extended_p)
|
||||
|
||||
/* Register extra event sources in the event loop. */
|
||||
rs->remote_async_inferior_event_token
|
||||
= create_async_event_handler (remote_async_inferior_event_handler, NULL);
|
||||
= create_async_event_handler (remote_async_inferior_event_handler, remote);
|
||||
rs->notif_state = remote_notif_state_allocate (remote);
|
||||
|
||||
/* Reset the target state; these things will be queried either by
|
||||
@@ -14164,6 +14164,19 @@ static void
|
||||
remote_async_inferior_event_handler (gdb_client_data data)
|
||||
{
|
||||
inferior_event_handler (INF_REG_EVENT);
|
||||
|
||||
remote_target *remote = (remote_target *) data;
|
||||
remote_state *rs = remote->get_remote_state ();
|
||||
|
||||
/* inferior_event_handler may have consumed an event pending on the
|
||||
infrun side without calling target_wait on the REMOTE target, or
|
||||
may have pulled an event out of a different target. Keep trying
|
||||
for this remote target as long it still has either pending events
|
||||
or unacknowledged notifications. */
|
||||
|
||||
if (rs->notif_state->pending_event[notif_client_stop.id] != NULL
|
||||
|| !rs->stop_reply_queue.empty ())
|
||||
mark_async_event_handler (rs->remote_async_inferior_event_token);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -1836,9 +1836,9 @@ trailing_outermost_frame (int count)
|
||||
static void
|
||||
select_frame_command_core (struct frame_info *fi, bool ignored)
|
||||
{
|
||||
struct frame_info *prev_frame = get_selected_frame_if_set ();
|
||||
frame_info *prev_frame = get_selected_frame (NULL);
|
||||
select_frame (fi);
|
||||
if (get_selected_frame_if_set () != prev_frame)
|
||||
if (get_selected_frame (NULL) != prev_frame)
|
||||
gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
|
||||
}
|
||||
|
||||
@@ -1857,10 +1857,9 @@ select_frame_for_mi (struct frame_info *fi)
|
||||
static void
|
||||
frame_command_core (struct frame_info *fi, bool ignored)
|
||||
{
|
||||
struct frame_info *prev_frame = get_selected_frame_if_set ();
|
||||
|
||||
frame_info *prev_frame = get_selected_frame (nullptr);
|
||||
select_frame (fi);
|
||||
if (get_selected_frame_if_set () != prev_frame)
|
||||
if (get_selected_frame (nullptr) != prev_frame)
|
||||
gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
|
||||
else
|
||||
print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
|
||||
|
||||
@@ -3274,7 +3274,7 @@ target_pass_ctrlc (void)
|
||||
if (proc_target == NULL)
|
||||
continue;
|
||||
|
||||
for (thread_info *thr : inf->threads ())
|
||||
for (thread_info *thr : inf->non_exited_threads ())
|
||||
{
|
||||
/* A thread can be THREAD_STOPPED and executing, while
|
||||
running an infcall. */
|
||||
|
||||
@@ -26,12 +26,14 @@
|
||||
|
||||
static pthread_barrier_t barrier;
|
||||
|
||||
volatile int exit_thread;
|
||||
|
||||
static void *
|
||||
thread_start (void *arg)
|
||||
{
|
||||
pthread_barrier_wait (&barrier);
|
||||
|
||||
while (1)
|
||||
while (!exit_thread)
|
||||
sleep (1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -439,6 +439,77 @@ proc test_info_inferiors {multi_process} {
|
||||
}
|
||||
}
|
||||
|
||||
# Test that when there's a foreground execution command in progress, a
|
||||
# TARGET_WAITKIND_NO_RESUMED for a particular target is ignored when
|
||||
# other targets are still resumed.
|
||||
|
||||
proc test_no_resumed {} {
|
||||
proc test_no_resumed_infs {inf_A inf_B} {
|
||||
global gdb_prompt
|
||||
|
||||
if {![setup "off"]} {
|
||||
untested "setup failed"
|
||||
return
|
||||
}
|
||||
|
||||
gdb_test "thread $inf_A.2" "Switching to thread $inf_A\.2 .*" \
|
||||
"select thread of target A"
|
||||
|
||||
gdb_test_no_output "set scheduler-locking on"
|
||||
|
||||
gdb_test_multiple "continue &" "" {
|
||||
-re "Continuing.*$gdb_prompt " {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
gdb_test "thread $inf_B.2" "Switching to thread $inf_B\.2 .*" \
|
||||
"select thread of target B"
|
||||
gdb_test "p exit_thread = 1" " = 1" \
|
||||
"set the thread to exit on resumption"
|
||||
|
||||
# Wait 3 seconds. If we see any response from GDB, such as
|
||||
# "No unwaited-for children left." it's a bug.
|
||||
gdb_test_multiple "continue" "continue" {
|
||||
-timeout 3
|
||||
timeout {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
# Now stop the program (all targets).
|
||||
send_gdb "\003"
|
||||
gdb_test_multiple "" "send_gdb control C" {
|
||||
-re "received signal SIGINT.*$gdb_prompt $" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
|
||||
gdb_test_multiple "info threads" "all threads stopped" {
|
||||
-re "\\\(running\\\).*$gdb_prompt $" {
|
||||
fail $gdb_test_name
|
||||
}
|
||||
-re "$gdb_prompt $" {
|
||||
pass $gdb_test_name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# inferior 1 -> native
|
||||
# inferior 2 -> extended-remote 1
|
||||
# inferior 5 -> extended-remote 2
|
||||
set inferiors {1 2 5}
|
||||
foreach_with_prefix inf_A $inferiors {
|
||||
foreach_with_prefix inf_B $inferiors {
|
||||
if {$inf_A == $inf_B} {
|
||||
continue
|
||||
}
|
||||
test_no_resumed_infs $inf_A $inf_B
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Make a core file with two threads upfront. Several tests load the
|
||||
# same core file.
|
||||
prepare_core
|
||||
@@ -467,4 +538,9 @@ with_test_prefix "info-inferiors" {
|
||||
}
|
||||
}
|
||||
|
||||
# Test TARGET_WAITKIND_NO_RESUMED handling with multiple targets.
|
||||
with_test_prefix "no-resumed" {
|
||||
test_no_resumed
|
||||
}
|
||||
|
||||
cleanup_gdbservers
|
||||
|
||||
72
gdb/thread.c
72
gdb/thread.c
@@ -1325,20 +1325,25 @@ switch_to_thread (process_stratum_target *proc_target, ptid_t ptid)
|
||||
switch_to_thread (thr);
|
||||
}
|
||||
|
||||
static void
|
||||
restore_selected_frame (struct frame_id a_frame_id, int frame_level)
|
||||
/* See frame.h. */
|
||||
|
||||
void
|
||||
lookup_selected_frame (struct frame_id a_frame_id, int frame_level)
|
||||
{
|
||||
struct frame_info *frame = NULL;
|
||||
int count;
|
||||
|
||||
/* This means there was no selected frame. */
|
||||
/* This either means there was no selected frame, or the selected
|
||||
frame was the innermost (the current frame). */
|
||||
if (frame_level == -1)
|
||||
{
|
||||
select_frame (NULL);
|
||||
select_frame (get_current_frame ());
|
||||
return;
|
||||
}
|
||||
|
||||
gdb_assert (frame_level >= 0);
|
||||
/* select_frame never saves 0 in SELECTED_FRAME_LEVEL, so we
|
||||
shouldn't see it here. */
|
||||
gdb_assert (frame_level > 0);
|
||||
|
||||
/* Restore by level first, check if the frame id is the same as
|
||||
expected. If that fails, try restoring by frame id. If that
|
||||
@@ -1409,22 +1414,14 @@ scoped_restore_current_thread::restore ()
|
||||
&& target_has_stack
|
||||
&& target_has_memory)
|
||||
restore_selected_frame (m_selected_frame_id, m_selected_frame_level);
|
||||
|
||||
set_language (m_lang);
|
||||
}
|
||||
|
||||
scoped_restore_current_thread::~scoped_restore_current_thread ()
|
||||
{
|
||||
if (!m_dont_restore)
|
||||
{
|
||||
try
|
||||
{
|
||||
restore ();
|
||||
}
|
||||
catch (const gdb_exception &ex)
|
||||
{
|
||||
/* We're in a dtor, there's really nothing else we can do
|
||||
but swallow the exception. */
|
||||
}
|
||||
}
|
||||
restore ();
|
||||
|
||||
if (m_thread != NULL)
|
||||
m_thread->decref ();
|
||||
@@ -1433,46 +1430,21 @@ scoped_restore_current_thread::~scoped_restore_current_thread ()
|
||||
|
||||
scoped_restore_current_thread::scoped_restore_current_thread ()
|
||||
{
|
||||
m_thread = NULL;
|
||||
m_inf = current_inferior ();
|
||||
m_inf->incref ();
|
||||
|
||||
m_lang = current_language->la_language;
|
||||
|
||||
if (inferior_ptid != null_ptid)
|
||||
{
|
||||
thread_info *tp = inferior_thread ();
|
||||
struct frame_info *frame;
|
||||
m_thread = inferior_thread ();
|
||||
m_thread->incref ();
|
||||
|
||||
m_was_stopped = tp->state == THREAD_STOPPED;
|
||||
if (m_was_stopped
|
||||
&& target_has_registers
|
||||
&& target_has_stack
|
||||
&& target_has_memory)
|
||||
{
|
||||
/* When processing internal events, there might not be a
|
||||
selected frame. If we naively call get_selected_frame
|
||||
here, then we can end up reading debuginfo for the
|
||||
current frame, but we don't generally need the debuginfo
|
||||
at this point. */
|
||||
frame = get_selected_frame_if_set ();
|
||||
}
|
||||
else
|
||||
frame = NULL;
|
||||
|
||||
try
|
||||
{
|
||||
m_selected_frame_id = get_frame_id (frame);
|
||||
m_selected_frame_level = frame_relative_level (frame);
|
||||
}
|
||||
catch (const gdb_exception_error &ex)
|
||||
{
|
||||
m_selected_frame_id = null_frame_id;
|
||||
m_selected_frame_level = -1;
|
||||
}
|
||||
|
||||
tp->incref ();
|
||||
m_thread = tp;
|
||||
m_was_stopped = m_thread->state == THREAD_STOPPED;
|
||||
save_selected_frame (&m_selected_frame_id, &m_selected_frame_level);
|
||||
}
|
||||
|
||||
m_inf->incref ();
|
||||
else
|
||||
m_thread = NULL;
|
||||
}
|
||||
|
||||
/* See gdbthread.h. */
|
||||
|
||||
13
gdb/value.c
13
gdb/value.c
@@ -1412,7 +1412,18 @@ value_optimized_out (struct value *value)
|
||||
}
|
||||
catch (const gdb_exception_error &ex)
|
||||
{
|
||||
/* Fall back to checking value->optimized_out. */
|
||||
switch (ex.error)
|
||||
{
|
||||
case MEMORY_ERROR:
|
||||
case OPTIMIZED_OUT_ERROR:
|
||||
case NOT_AVAILABLE_ERROR:
|
||||
/* These can normally happen when we try to access an
|
||||
optimized out or unavailable register, either in a
|
||||
physical register or spilled to memory. */
|
||||
break;
|
||||
default:
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user