binutils-gdb/gdb/gdbserver/target.c
Simon Marchi 9c80ecd646 gdbserver: use std::list for all_threads
Remove the usage of inferior_list for the all_threads list in
gdbserver.  The entry field in thread_info is removed, and replaced by a
simple ptid field.

I added some functions to iterate (for_each_thread) and find threads
(find_thread).  However, changing all the users of find_inferior & co to
use these new functions would have made the patch way too big.  So I
opted instead to make find_inferior & co some shims, so that the
existing code only needs to be updated minimally.  We can then update
the existing code to use the new functions incrementally (I've started
to do the work, but I'll post it afterwards, see [1] if you want a peek).

This patch has been built-tested on all relevant platforms, except
lynx.  I also regtested using the native-gdbserver and
native-extended-gdbserver boards on x86.

[1] https://github.com/simark/binutils-gdb/commits/kill-inferior-list-entry

gdb/gdbserver/ChangeLog:

	* inferiors.h: (struct inferior_list): Remove.
	(struct inferior_list_entry); Remove.
	(add_inferior_to_list, clear_inferior_list, one_inferior_p,
	A_I_NEXT, ALL_INFERIORS_TYPE, ALL_INFERIORS, remove_inferior,
	get_first_inferior): Remove.
	(for_each_inferior, for_each_inferior_with_data, find_inferior,
	find_inferior_id, find_inferior_in_random): Change signature.
	* inferiors.c (all_threads): Change type to
	std::list<thread_info *>.
	(get_thread): Remove macro.
	(find_inferior, find_inferior_id): Change signature, implement
	using find_thread.
	(find_inferior_in_random): Change signature, implement using
	find_thread_in_random.
	(for_each_inferior, for_each_inferior_with_data): Change
	signature, implement using for_each_thread.
	(add_inferior_to_list, remove_inferior): Remove.
	(add_thread, get_first_thread, thread_of_pid,
	find_any_thread_of_pid, free_one_thread, remove_thread): Update.
	(get_first_inferior, one_inferior_p, clear_inferior_list):
	Remove.
	(clear_inferiors, get_thread_process): Update.
	* gdbthread.h: Include <list>.
	(struct thread_info) <entry>: Remove field.
	<id>: New field.
	(all_threads): Change type to std::list<thread_info *>.
	(get_first_inferior): Add doc.
	(find_thread, for_each_thread, find_thread_in_random): New
	functions.
	(current_ptid, pid_of, ptid_of, lwpid_of): Update.
	* linux-arm-low.c (update_registers_callback): Update.
	* linux-low.c (second_thread_of_pid_p): Update.
	(kill_one_lwp_callback, linux_detach_lwp_callback,
	delete_lwp_callback, status_pending_p_callback, same_lwp,
	find_lwp_pid, num_lwps, iterate_over_lwps_filter,
	iterate_over_lwps, not_stopped_callback,
	resume_stopped_resumed_lwps, count_events_callback,
	select_singlestep_lwp_callback, select_event_lwp_callback,
	unsuspend_one_lwp, linux_wait_1, send_sigstop_callback,
	suspend_and_send_sigstop_callback, wait_for_sigstop,
	stuck_in_jump_pad_callback, move_out_of_jump_pad_callback,
	lwp_running, linux_set_resume_request, resume_status_pending_p,
	need_step_over_p, start_step_over, linux_resume_one_thread,
	proceed_one_lwp, unsuspend_and_proceed_one_lwp,
	reset_lwp_ptrace_options_callback): Update.
	* linux-mips-low.c (update_watch_registers_callback): Update.
	* regcache.c (regcache_invalidate_one, regcache_invalidate):
	Update.
	(free_register_cache_thread_one): Remove.
	(regcache_release): Update.
	* server.c (handle_btrace_enable_bts, handle_btrace_enable_pt,
	handle_qxfer_threads_worker): Update.
	(handle_query): Update, use list iterator.
	(visit_actioned_threads, handle_pending_status,
	queue_stop_reply_callback, gdb_wants_all_threads_stopped,
	clear_pending_status_callback, set_pending_status_callback,
	find_status_pending_thread_callback, handle_status,
	process_serial_event): Update.
	* target.c (thread_search_callback): Update.
	* thread-db.c (thread_db_get_tls_address): Update.
	* tracepoint.c (tracepoint_finished_step, tracepoint_was_hit):
	Update.
	* win32-i386-low.c (update_debug_registers_callback): Update.
	* win32-low.c (delete_thread_info, child_delete_thread,
	continue_one_thread, suspend_one_thread,
	get_child_debug_event): Adjust.
2017-10-14 09:11:12 -04:00

432 lines
9.9 KiB
C

/* Target operations for the remote server for GDB.
Copyright (C) 2002-2017 Free Software Foundation, Inc.
Contributed by MontaVista Software.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "server.h"
#include "tracepoint.h"
struct target_ops *the_target;
int
set_desired_thread ()
{
thread_info *found = find_thread_ptid (general_thread);
current_thread = found;
return (current_thread != NULL);
}
/* Structure used to look up a thread to use as current when accessing
memory. */
struct thread_search
{
/* The PTID of the current general thread. This is an input
parameter. */
ptid_t current_gen_ptid;
/* The first thread found. */
struct thread_info *first;
/* The first stopped thread found. */
struct thread_info *stopped;
/* The current general thread, if found. */
struct thread_info *current;
};
/* Callback for find_inferior. Search for a thread to use as current
when accessing memory. */
static int
thread_search_callback (thread_info *thread, void *args)
{
struct thread_search *s = (struct thread_search *) args;
if (thread->id.pid () == ptid_get_pid (s->current_gen_ptid)
&& mythread_alive (ptid_of (thread)))
{
if (s->stopped == NULL
&& the_target->thread_stopped != NULL
&& thread_stopped (thread))
s->stopped = thread;
if (s->first == NULL)
s->first = thread;
if (s->current == NULL && s->current_gen_ptid == thread->id)
s->current = thread;
}
return 0;
}
/* The thread that was current before prepare_to_access_memory was
called. done_accessing_memory uses this to restore the previous
selected thread. */
static ptid_t prev_general_thread;
/* See target.h. */
int
prepare_to_access_memory (void)
{
struct thread_search search;
struct thread_info *thread;
memset (&search, 0, sizeof (search));
search.current_gen_ptid = general_thread;
prev_general_thread = general_thread;
if (the_target->prepare_to_access_memory != NULL)
{
int res;
res = the_target->prepare_to_access_memory ();
if (res != 0)
return res;
}
find_inferior (&all_threads, thread_search_callback, &search);
/* Prefer a stopped thread. If none is found, try the current
thread. Otherwise, take the first thread in the process. If
none is found, undo the effects of
target->prepare_to_access_memory() and return error. */
if (search.stopped != NULL)
thread = search.stopped;
else if (search.current != NULL)
thread = search.current;
else if (search.first != NULL)
thread = search.first;
else
{
done_accessing_memory ();
return 1;
}
current_thread = thread;
general_thread = ptid_of (thread);
return 0;
}
/* See target.h. */
void
done_accessing_memory (void)
{
if (the_target->done_accessing_memory != NULL)
the_target->done_accessing_memory ();
/* Restore the previous selected thread. */
general_thread = prev_general_thread;
switch_to_thread (general_thread);
}
int
read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
{
int res;
res = (*the_target->read_memory) (memaddr, myaddr, len);
check_mem_read (memaddr, myaddr, len);
return res;
}
/* See target/target.h. */
int
target_read_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len)
{
return read_inferior_memory (memaddr, myaddr, len);
}
/* See target/target.h. */
int
target_read_uint32 (CORE_ADDR memaddr, uint32_t *result)
{
return read_inferior_memory (memaddr, (gdb_byte *) result, sizeof (*result));
}
int
write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
int len)
{
/* Lacking cleanups, there is some potential for a memory leak if the
write fails and we go through error(). Make sure that no more than
one buffer is ever pending by making BUFFER static. */
static unsigned char *buffer = 0;
int res;
if (buffer != NULL)
free (buffer);
buffer = (unsigned char *) xmalloc (len);
memcpy (buffer, myaddr, len);
check_mem_write (memaddr, buffer, myaddr, len);
res = (*the_target->write_memory) (memaddr, buffer, len);
free (buffer);
buffer = NULL;
return res;
}
/* See target/target.h. */
int
target_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, ssize_t len)
{
return write_inferior_memory (memaddr, myaddr, len);
}
ptid_t
mywait (ptid_t ptid, struct target_waitstatus *ourstatus, int options,
int connected_wait)
{
ptid_t ret;
if (connected_wait)
server_waiting = 1;
ret = target_wait (ptid, ourstatus, options);
/* We don't expose _LOADED events to gdbserver core. See the
`dlls_changed' global. */
if (ourstatus->kind == TARGET_WAITKIND_LOADED)
ourstatus->kind = TARGET_WAITKIND_STOPPED;
/* If GDB is connected through TCP/serial, then GDBserver will most
probably be running on its own terminal/console, so it's nice to
print there why is GDBserver exiting. If however, GDB is
connected through stdio, then there's no need to spam the GDB
console with this -- the user will already see the exit through
regular GDB output, in that same terminal. */
if (!remote_connection_is_stdio ())
{
if (ourstatus->kind == TARGET_WAITKIND_EXITED)
fprintf (stderr,
"\nChild exited with status %d\n", ourstatus->value.integer);
else if (ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n",
gdb_signal_to_host (ourstatus->value.sig),
gdb_signal_to_name (ourstatus->value.sig));
}
if (connected_wait)
server_waiting = 0;
return ret;
}
/* See target/target.h. */
void
target_stop_and_wait (ptid_t ptid)
{
struct target_waitstatus status;
int was_non_stop = non_stop;
struct thread_resume resume_info;
resume_info.thread = ptid;
resume_info.kind = resume_stop;
resume_info.sig = GDB_SIGNAL_0;
(*the_target->resume) (&resume_info, 1);
non_stop = 1;
mywait (ptid, &status, 0, 0);
non_stop = was_non_stop;
}
/* See target/target.h. */
ptid_t
target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
{
return (*the_target->wait) (ptid, status, options);
}
/* See target/target.h. */
void
target_mourn_inferior (ptid_t ptid)
{
(*the_target->mourn) (find_process_pid (ptid_get_pid (ptid)));
}
/* See target/target.h. */
void
target_continue_no_signal (ptid_t ptid)
{
struct thread_resume resume_info;
resume_info.thread = ptid;
resume_info.kind = resume_continue;
resume_info.sig = GDB_SIGNAL_0;
(*the_target->resume) (&resume_info, 1);
}
/* See target/target.h. */
void
target_continue (ptid_t ptid, enum gdb_signal signal)
{
struct thread_resume resume_info;
resume_info.thread = ptid;
resume_info.kind = resume_continue;
resume_info.sig = gdb_signal_to_host (signal);
(*the_target->resume) (&resume_info, 1);
}
/* See target/target.h. */
int
target_supports_multi_process (void)
{
return (the_target->supports_multi_process != NULL ?
(*the_target->supports_multi_process) () : 0);
}
int
start_non_stop (int nonstop)
{
if (the_target->start_non_stop == NULL)
{
if (nonstop)
return -1;
else
return 0;
}
return (*the_target->start_non_stop) (nonstop);
}
void
set_target_ops (struct target_ops *target)
{
the_target = XNEW (struct target_ops);
memcpy (the_target, target, sizeof (*the_target));
}
/* Convert pid to printable format. */
const char *
target_pid_to_str (ptid_t ptid)
{
static char buf[80];
if (ptid_equal (ptid, minus_one_ptid))
xsnprintf (buf, sizeof (buf), "<all threads>");
else if (ptid_equal (ptid, null_ptid))
xsnprintf (buf, sizeof (buf), "<null thread>");
else if (ptid_get_tid (ptid) != 0)
xsnprintf (buf, sizeof (buf), "Thread %d.0x%lx",
ptid_get_pid (ptid), ptid_get_tid (ptid));
else if (ptid_get_lwp (ptid) != 0)
xsnprintf (buf, sizeof (buf), "LWP %d.%ld",
ptid_get_pid (ptid), ptid_get_lwp (ptid));
else
xsnprintf (buf, sizeof (buf), "Process %d",
ptid_get_pid (ptid));
return buf;
}
int
kill_inferior (int pid)
{
gdb_agent_about_to_close (pid);
return (*the_target->kill) (pid);
}
/* Target can do hardware single step. */
int
target_can_do_hardware_single_step (void)
{
return 1;
}
/* Default implementation for breakpoint_kind_for_pc.
The default behavior for targets that don't implement breakpoint_kind_for_pc
is to use the size of a breakpoint as the kind. */
int
default_breakpoint_kind_from_pc (CORE_ADDR *pcptr)
{
int size = 0;
gdb_assert (the_target->sw_breakpoint_from_kind != NULL);
(*the_target->sw_breakpoint_from_kind) (0, &size);
return size;
}
/* Define it. */
enum target_terminal::terminal_state target_terminal::terminal_state
= target_terminal::terminal_is_ours;
/* See target/target.h. */
void
target_terminal::init ()
{
/* Placeholder needed because of fork_inferior. Not necessary on
GDBserver. */
}
/* See target/target.h. */
void
target_terminal::inferior ()
{
/* Placeholder needed because of fork_inferior. Not necessary on
GDBserver. */
}
/* See target/target.h. */
void
target_terminal::ours ()
{
/* Placeholder needed because of fork_inferior. Not necessary on
GDBserver. */
}
/* See target/target.h. */
void
target_terminal::ours_for_output (void)
{
/* Placeholder. */
}
/* See target/target.h. */
void
target_terminal::info (const char *arg, int from_tty)
{
/* Placeholder. */
}