diff options
author | Pedro Alves <palves@redhat.com> | 2008-11-25 20:47:19 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2008-11-25 20:47:19 +0000 |
commit | fb173436b5a44b9045548084e0bae32f97be2b79 (patch) | |
tree | 32cb9663093471638dae2895617a63bdb4383e25 | |
parent | 9ae191253c464c1c3d3e1bc1ea5f06cd89eeea8e (diff) | |
download | gdb-fb173436b5a44b9045548084e0bae32f97be2b79.zip gdb-fb173436b5a44b9045548084e0bae32f97be2b79.tar.gz gdb-fb173436b5a44b9045548084e0bae32f97be2b79.tar.bz2 |
2008-11-25 Pedro Alves <pedro@codesourcery.com>
2008-10-13 Pedro Alves <pedro@codesourcery.com>
* server.c (discard_pending_stop_replies): Initialize prev.
2008-10-12 Pedro Alves <pedro@codesourcery.com>
* linux-low.c (linux_wait_1): Cancel breakpoint hits in threads
we're not reporting.
(cancel_breakpoints_callback): New.
2008-10-12 Pedro Alves <pedro@codesourcery.com>
* server.c (queue_stop_reply_callback, handle_status): Pass
TARGET_SIGNAL_TRAP, not TARGET_SIGNAL_0.
* linux-low.c (ptid_is_pid): Delete.
(linux_wait_for_event): In non-stop, don't set the current
inferior arbitrarily.
(linux_wait): Flush the event pipe before polling for an event.
(wait_for_sigstop): In non-stop, don't set the current
inferior arbitrarily.
(linux_resume_one_lwp): Small cleanup.
(cancel_breakpoint): New.
(linux_resume_one_thread): Use it. Still report SIGTRAPs.
(regsets_store_inferior_registers): Plug leak.
(sigchld_handler): Don't use fprintf here.
* mem-break.c (breakpoint_at): New.
* inferiors.c (ptid_is_pid): New.
* mem-break.h (breakpoint_at): Declare.
* server.c (discard_queued_stop_replies): Add `pid' argument.
Handle it.
(send_next_stop_reply): Cleanup.
(attach_inferior): Don't wait here in non-stop mode.
(handle_v_attach): Don't queue stop replies here.
(handle_v_kill): Discard queued stop replies of the inferior we
just killed.
(queue_stop_reply_callback): Add `arg' argument. Handle it.
(handle_status): Adjust.
(kill_inferior_callback): Discard queued stop replies of the
inferior we just killed.
(detach_or_kill_inferior_callback): Discard queued stop replies of
the inferior we just killed or detached from.
(process_serial_event): Cleanup. Discard queued stop replies of
the inferior we just detached. Don't write 0 bytes to the inferior.
(handle_serial_event): Debug output.
* server.h (ptid_is_pid): Declare.
* remote-utils.c (prepare_resume_reply): Avoid reading registers
and memory from a thread that is gone.
2008-09-26 Pedro Alves <pedro@codesourcery.com>
* server.h (struct sym_cache, struct process_info_private):
Forward declare.
(struct process_info): Add symbol_cache, all_symbols_looked_up and
private fields.
(current_process): Declare.
* remote-utils.c (struct sym_cache) <name>: Remove constness.
(symbol_cache): Delete.
(free_sym_cache, clear_symbol_cache): New.
(look_up_one_symbol): Adjust, to per-process symbol cache.
* inferiors.c (current_process): New.
* linux-low.h: Include "gdb_proc_service.h".
(struct process_info_private): Define.
* linux-low.c (thread_db_active): Delete.
(linux_add_process): New.
(handle_extended_wait, linux_create_inferior, linux_attach): Use
it.
(linux_wait_for_event, linux_look_up_symbols): Adjust.
(initialize_low): Don't clear the global thread_db_active.
* thread-db.c (proc_handle, thread_agent): Delete.
(fixup_proc_handle): New.
(thread_db_err_str, thread_db_enable_reporting): Use it. Adjust.
(thread_db_find_new_threads): Look for the current inferior
thread, not the first thread in the list. Use fixup_proc_handle.
Adjust.
(thread_db_get_tls_address): Use fixup_proc_handle.
(thread_db_init): Likewise. Adjust.
2008-09-26 Pedro Alves <pedro@codesourcery.com>
* mem-break.c (get_breakpoint_list): Add `create' argument. Only
create the list if create is set.
(remove_breakpoint_list): New.
(set_breakpoint_at, find_breakpoint_at, check_mem_read)
(check_mem_write, delete_all_breakpoints): Adjust.
2008-09-26 Pedro Alves <pedro@codesourcery.com>
* server.c (gdbserver_usage): Describe --remote-debug option.
(main): Handle --remote-debug switch.
2008-09-19 Pedro Alves <pedro@codesourcery.com>
Non-stop mode support.
* linux-low.c (linux_event_pipe): New int array for pipe.
(target_is_async_p): New.
(handle_extended_wait): Use my_waitpid.
(linux_kill_one_lwp): Stop the LWP if it is not stopped already.
Use my_waitpid, not linux_wait_for_event. Remove the lwp and the
corresponding thread from the lists.
(linux_kill_one_process, linux_kill): Delete.
(linux_kill_1): Rename back to ...
(linux_kill) ... this. Stop the LWP if it is not stopped already.
Use my_waitpid, not linux_wait_for_event. Remove the lwp and the
corresponding thread from the lists.
(linux_detach_one_lwp): Make sure the LWP is stopped. Adjust to
new linux_wait_for_event interface.
(linux_detach_one_process, linux_detach): Delete.
(linux_detach_1): Rename back to ...
(linux_detach): This.
(linux_join): Add PID argument. Use my_waitpid instead of
waitpid.
(status_pending_p): Ignore suspended threads.
(my_waitpid): Emulate __WALL.
(linux_wait_for_lwp): Add 'options' argument. Handle it. Use
my_wait. If requesting an event from a specific process, leave
events in other processes pending.
(resume_stopped_lwps): New.
(linux_wait_for_event): Delete.
(linux_wait_for_event_1): Rename back to ...
(linux_wait_for_event): ... this. Change interface: add wstat and
options arguments, return -1 on error, 0 otherwise. Adjust. In
all-stop, resume stopped lwps if there was no pending status.
Don't return immediatelly if a pending status was found ---
continue handling it instead. Don't loop, only handle one event.
(linux_wait): Rename to ...
(linux_wait_1): ... this. Add target_options argument. Handle
it. Don't loop --- only handle one event. Ignore the continue
thread in non_stop mode. Adjust to new resume_kind interface.
Only stop all threads in all-stop mode.
(async_file_flush, async_file_mark): New.
(linux_wait): New.
(wait_for_sigstop): Set stopping_threads here. Use
linux_wait_for_lwp instead of linux_wait_for_event. Adjust.
(stop_all_lwps): Don't set stopping_threads here.
(resume_ptr): Delete.
(struct resume_info): New.
(linux_set_resume_request): Add arg argument. Adjust to take a
struct remove_info instead of the global resume_ptr. Accept
pid,-1 to apply to all threads.
(linux_continue_one_thread, linux_queue_one_thread): Merge both
and create ...
(linux_resume_one_thread): ... this. New. Handle rk_stop.
(resume_status_pending_p): Assume no resume info to mean do
nothing. Ignore suspended LWPs.
(linux_resume): Add n argument. Adjust. In non-stop mode, don't
look for a pending status over all threads.
(linux_read_offsets): Minor cleanup.
(sigchld_handler, linux_async, linux_start_non_stop): New.
(linux_target_ops): Register linux_async and linux_start_non_stop.
(initialize_low): Register sigchld_handler as SIGCHLD handler.
* utils.c (internal_verror, internal_error_file_line): New.
* Makefile.in (SFILES): Add event-loop.c.
(OBS): Add event-loop.o.
(event-loop.o): New rule.
* linux-low.h (struct lwp_info) <suspended>: New flag.
* thread-db.c (thread_db_create_event): Make sure thread_db reads
from the current inferior.
(thread_db_get_tls_address): Comment.
* server.c (thread_from_wait, old_thread_from_wait, attached):
Delete.
(non_stop): New global.
(own_buf, mem_buf): New globals.
(struct vstop_notif): New.
(notif_queue): New.
(queue_stop_reply, push_event, discard_queued_stop_replies)
(send_next_stop_reply): New.
(start_inferior): Adjust to new thread_resume interface. Adjust
to new mywait interface.
(attach_inferior): Adjust.
(handle_general_set): Handle QNonStop.
(handle_query): Pass 'QNonStop+'.
(handle_v_cont): Handle vCont;t. Don't enable/disable async io in
non-stop mode. In non-stop return OK, and don't wait for the
target.
(handle_v_attach): In non-stop, return OK, and queue events for
all threads.
(handle_v_run): In non-stop, set the general thread here.
(handle_v_stopped): New.
(handle_v_requests): Report support for 't'. Handle 'vStopped'.
(proceed): Add comment. Adjust. In non-stop, don't
enable/disable async io; write 'OK', and don't wait for the
target.
(queue_stop_reply_callback, handle_status): New.
(kill_inferior_callback, detach_or_kill_inferior_callback)
(join_inferiors_callback): New.
(main): In --debug mode, also enable remote debug. Don't pass -1
to kill_inferior or detach_inferior; instead, iterate over all
processes killing or detaching them. Adjust to use the even-loop.
(process_serial_event): New, factored out of main. If the
connection closed, remove all sources from the event loop.
Iterate over all inferiors joining them. Use handle_status.
Don't print inferior exit notices here. In non-stop, defer
exiting until GDB read all queued events.
(handle_serial_event, handle_target_event): New.
* server.h (FOR_EACH_INFERIOR): New.
(thread_from_wait, old_thread_from_wait): Remove.
(non_stop): Declare.
(gdb_client_data, handler_func): New typedefs.
(delete_file_handler, add_file_handler, start_event_loop)
(handle_serial_event, handle_target_event, push_event)
(putpkt_notif, internal_error_file_line): Declare.
(internal_error): Define.
* target.c (mywait): Add `options' argument. Print inferior exit
notices here.
(start_non_stop): New.
* event-loop.c: New.
* remote-utils.c (remote_open): Register remote_desc in the event
loop, with handle_serial_event as callback.
(remote_close): Remove remote_desc from the event loop.
(hex_or_minus_one): New.
(read_ptid, read_ptid): Use it.
(putpkt_binary): Rename to ...
(putpkt_binary_1): ... this. Add `notif' argument. Handle
pushing a remote protocol notification.
(putpkt_binary): New as wrapper around putpkt_binary_1.
(putpkt_notif): New.
(getpkt): Debug output.
(prepare_resume_reply): Remove dead code. In non-stop, don't set
the general thread here.
* target.h (enum resume_kind): New.
(struct thread_resume) <leave_stopped, step>: Delete.
(struct thread_resume) <kind>: New field.
(TARGET_WNOHANG): Define.
(struct target_ops) <kill, detach>: Adjust comments.
(struct target_ops) <join>: Add `pid' argument.
(struct target_ops) <resume>: Add `n' argument.
(struct target_ops) <wait>: Add `options' argument.
(struct target_ops) <async, start_non_stop>: New fields.
(join_inferior): Add `pid' argument.
(target_async): New.
(start_non_stop): Declare.
(mywait): Add options argument.
2008-09-15 Pedro Alves <pedro@codesourcery.com>
* linux-low.c (new_inferior): Mention in the comment that all
inferiors should have the same architecture for now.
(linux_create_inferior, linux_attach): Only set new_inferior if
this is the first process.
2008-08-29 Pedro Alves <pedro@codesourcery.com>
* linux-low.c (ptid_is_pid): Move higher.
(linux_wait_for_lwp): Remove dead code.
(linux_wait_for_event): Rename to ...
(linux_wait_for_event_1): ... this.
(linux_wait_for_event): New.
(ptid_same_pid): Delete.
(linux_set_resume_request): Clearify.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
* linux-low.c (handle_extended_wait, linux_attach_lwp)
(linux_attach): Minor cleanups.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
* linux-low.c (linux_thread_alive): Return false for a listed
thread known to be dead.
(linux_wait_for_event): Don't set the dead flag here.
(wait_for_sigstop): Store ptid before waiting for the event.
Minor cleanup.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
* inferiors.c (find_inferior): Allow deleting the current iterated
inferior.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
* linux-low.c (linux_pid_to_exec_file): Move higher.
(linux_enable_event_reporting): Enable PTRACE_O_TRACEEXEC.
(handle_extended_wait): Handle PTRACE_EVENT_EXEC.
* remote-utils.c (prepare_resume_reply): Set the general thread to
the last thread that had an event in TARGET_WAITKIND_FORKED and
TARGET_WAITKIND_VFORKED. Handle TARGET_WAITKIND_EXECD.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
* server.c (handle_query): Pass "QExecFile:PID;" back in the reply.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
* linux-low.c (linux_supports_tracefork_flag): Move higher.
(linux_enable_event_reporting): New.
(handle_extended_wait): Change return type to int. Handle
PTRACE_EVENT_FORK and PTRACE_EVENT_VFORK.
(add_lwp): Set waitstatus.kind to TARGET_WAITKIND_IGNORE.
(linux_attach_lwp): Use linux_enable_event_reporting.
(linux_wait_for_event): Don't keep waiting if the extended wait
status should be reported to gdb.
(linux_wait): Use linux_enable_event_reporting. If waitstatus
holds a processed event, return it instead.
* remote-utils.c (prepare_resume_reply): Handle
TARGET_WAITKIND_FORKED and TARGET_WAITKIND_VFORKED.
* linux-low.h (struct lwp_info) <waitstatus>: New member.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
* target.h (struct target_ops) <pid_to_exec_file>: New member.
* server.c (handle_query): Handle qExecFile.
* linux-low.c (linux_pid_to_exec_file): New.
(linux_target_ops): Register it.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
* linux-low.c (is_lwpid): New.
(linux_kill_one_lwp, linux_kill_1, linux_detach_one_lwp): Adjust.
(status_pending_p): Check if we're interested in this lwp.
(linux_wait_for_lwp): Change signature: return an lwp_info*, and
take a ptid instead of an lwp_info**.
(linux_wait_for_event): Take a ptid instead of a thread_info
pointer. Adjust.
(wait_for_sigstop): Adjust. If a whole process died, keep the
exit status pending.
(ptid_is_pid, ptid_same_pid): New.
(linux_set_resume_request): Allow resuming all threads of a process.
(resume_status_pending_p): Check for dead lwps.
* linux-low.h (struct lwp_info) <dead>: New field.
* server.c (start_inferior): Only resume and wait for events from
the inferior we're creating.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
Decouple target code from remote protocol.
* linux-low.c (linux_wait): Change prototype. Adjust.
* server.c (last_status, last_ptid): New.
(start_inferior): Remove "statusptr" argument. Adjust.
(attach_inferior, handle_v_cont, handle_v_attach, handle_v_run)
(handle_v_kill, handle_v_requests): Remove "status" and "signal"
parameters. Adjust.
(myresume): Rename to ...
(proceed): ... this. Remove "statusp" parameter. Adjust.
(main): Remove "status" local. Adjust.
* target.c (mywait): Change prototype. Adjust.
* target.h (enum target_waitkind): New.
(struct target_waitstatus): New.
(struct target_ops) <wait>: Change prototype.
(mywait): Adjust.
* remote-utils.c: Include "target.h".
(prepare_resume_reply): Change prototype to take a ptid and a
target_waitstatus. Adjust.
* server.h (prepare_resume_reply): Adjust prototype.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
* inferiors.c (all_processes): New.
(add_process): New.
* linux-low.c (linux_create_inferior): Add process.
(linux_attach_lwp): Add "initial" parameter, and use it instead of
relying on having only one thread in the global list.
(linux_attach): Add process.
(struct counter): New.
(check_if_last_thread_of_pid, is_last_thread_of_process): New.
(linux_kill_one_lwp): Add ARGS parameter. Change return type to
int. Check if we're interested in this lwp. Use
is_last_thread_of_process.
(linux_kill): Rename to ...
(linux_kill_1): ... this. Kill lwps of the requested only.
(linux_kill_one_process): New.
(linux_kill): New.
(linux_detach_one_lwp): Add ARGS parameter. Change return type to
int. Check if we're interested in this lwp. Remove the lwp from
both the lwp list and the thread list.
(any_thread_of, linux_detach_1, linux_detach_one_process): New.
(linux_detach): Reimplement.
(linux_wait_for_event): Use is_last_thread_of_process.
(linux_wait): Likewise. On process exit, don't clear all inferiors.
Implement multi-process extensions.
* mem-break.c (breakpoints): Delete.
(struct breakpoint_list): New.
(all_breakpoints): New.
(get_breakpoint_list): New.
(set_breakpoint_at, delete_breakpoint, find_breakpoint_at)
(check_mem_read, check_mem_write, delete_all_breakpoints): Use it.
* server.h (struct process_info): New.
(all_processes): Declare.
(add_process): Declare.
* linux-low.h (linux_attach_lwp): Add "initial" parameter.
* thread-db.c (maybe_attach_thread): Adjust.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
* linux-low.c, linux-low.h, proc-service.c, thread-db.c: Rename
`struct process_info' to `struct lwp_info', and adjust throughout.
2008-08-28 Pedro Alves <pedro@codesourcery.com>
Implement remote protocol multi-process extensions.
* inferiors.c (null_ptid, minus_one_ptid): New.
(ptid_build, pid_to_ptid, ptid_get_pid, ptid_get_lwp)
(ptid_get_tid, ptid_equal): New functions.
(add_thread): Drop gdb_id argument. Retype thread_id argument to
ptid_t. Adjust.
(thread_id_to_gdb_id): Adjust.
(thread_to_gdb_id): Change return type to ptid_t. Adjust.
(gdb_id_to_thread): Rename to ...
(find_thread_pid): ... this. Adjust.
(gdb_id_to_thread_id): Change return type to ptid_t. Adjust.
(find_inferior_id): Change id argument type to ptid_t. Adjust.
(loaded_dll, add_pid_to_list, pull_pid_from_list): Adjust.
(initialize_inferiors): New.
* remote-utils.c (hexchars): New.
(ishex, unpack_varlen_hex, write_ptid, read_ptid): New.
(prepare_resume_reply): Adjust.
* server.c (cont_thread, general_thread, step_thread)
(thread_from_wait, old_thread_from_wait): Change type to ptid_t.
(multi_process): New.
(start_inferior): Adjust.
(handle_query): Adjust. Report multiprocess extensions support.
(handle_v_cont): Adjust.
(handle_v_kill): New.
(handle_v_requests): Handle vKill.
(myresume): Adjust.
(first_thread_of): New.
(main): Call initialize_inferiors. If bailing out, kill all
inferiors. Handle multi-process detach. Handle multi-process H
and T.
* server.h (ULONGEST): New typedef.
(struct ptid): New struct.
(ptid_t): New typedef.
(minus_one_ptid, null_ptid): New.
(ptid_t ptid_build, pid_to_ptid, ptid_get_pid, ptid_get_lwp)
(ptid_get_tid, ptid_equal): New.
(struct inferior_list_entry) <id>: Change type to ptid_t.
(add_thread, thread_id_to_gdb_id, thread_to_gdb_id)
(gdb_id_to_thread_id): Adjust prototypes.
(find_thread_pid): Declare.
(find_inferior_id): Adjust prototype.
(cont_thread, general_thread, step_thread, thread_from_wait)
(old_thread_from_wait): Adjust type to ptid_t.
(multi_process): Declare.
(read_ptid, write_ptid): Declare.
* linux-low.c (pid_of): Adjust.
(lwpid_of): New.
(inferior_lwpid): New.
(handle_extended_wait): Adjust.
(add_process): Change pid argument to a ptid. Adjust.
(linux_create_inferior): Adjust.
(linux_attach_lwp): Adjust. Clear new_inferior on error. If
creating a new inferior, don't rely on inferior_pid, instead use
the lwpid as pid.
(linux_attach): Set new_inferior earlier. Adjust.
(linux_kill): Change return type to int. Adjust.
(linux_detach): Add pid parameter.
(linux_thread_alive): Change lwpid paremeter type to ptid.
Adjust.
(same_lwp, find_lwp_pid): New.
(linux_wait_for_process): Adjust.
(linux_wait_for_process, linux_wait_for_event, send_sigstop)
(wait_for_sigstop, linux_resume_one_process,
(linux_resume_one_process, linux_set_resume_request)
(linux_continue_one_thread, linux_queue_one_thread)
(fetch_register, usr_store_inferior_registers)
(regsets_fetch_inferior_registers)
(regsets_store_inferior_registers, linux_read_memory)
(linux_write_memory, linux_request_interrupt, linux_read_auxv):
Adjust.
* linux-low.h (get_process_thread): Adjust.
(struct process_info) <lwpid>: Remove.
(find_lwp_pid): Declare.
* target.c (set_desired_inferior): Adjust.
(target_pid_to_str): New.
* target.h (struct thread_resume) <thread>: Change type to ptid_t.
(struct target_ops) <kill>: Change return type to int, and take an
int as parameter.
(struct target_ops) <detach>: Take an int as parameter.
(struct target_ops) <thread_alive>: Change pid argument type to
ptid_t.
(kill_inferior, detach_inferior): Add PID argument.
* thread-db.c (thread_db_create_event): Adjust.
(find_one_thread): Change argument to a ptid. Adjust.
(maybe_attach_thread, thread_db_get_tls_address, thread_db_init):
Adjust.
* proc-service.c (ps_lgetregs): Adjust.
-rw-r--r-- | gdb/gdbserver/ChangeLog | 512 | ||||
-rw-r--r-- | gdb/gdbserver/Makefile.in | 3 | ||||
-rw-r--r-- | gdb/gdbserver/inferiors.c | 163 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.c | 2002 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.h | 55 | ||||
-rw-r--r-- | gdb/gdbserver/mem-break.c | 117 | ||||
-rw-r--r-- | gdb/gdbserver/mem-break.h | 4 | ||||
-rw-r--r-- | gdb/gdbserver/proc-service.c | 9 | ||||
-rw-r--r-- | gdb/gdbserver/regcache.c | 10 | ||||
-rw-r--r-- | gdb/gdbserver/remote-utils.c | 412 | ||||
-rw-r--r-- | gdb/gdbserver/server.c | 957 | ||||
-rw-r--r-- | gdb/gdbserver/server.h | 159 | ||||
-rw-r--r-- | gdb/gdbserver/target.c | 76 | ||||
-rw-r--r-- | gdb/gdbserver/target.h | 169 | ||||
-rw-r--r-- | gdb/gdbserver/thread-db.c | 148 | ||||
-rw-r--r-- | gdb/gdbserver/utils.c | 22 |
16 files changed, 3827 insertions, 991 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 6928840..33ccb4d 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,515 @@ +2008-11-25 Pedro Alves <pedro@codesourcery.com> + + 2008-10-13 Pedro Alves <pedro@codesourcery.com> + + * server.c (discard_pending_stop_replies): Initialize prev. + + 2008-10-12 Pedro Alves <pedro@codesourcery.com> + + * linux-low.c (linux_wait_1): Cancel breakpoint hits in threads + we're not reporting. + (cancel_breakpoints_callback): New. + + 2008-10-12 Pedro Alves <pedro@codesourcery.com> + + * server.c (queue_stop_reply_callback, handle_status): Pass + TARGET_SIGNAL_TRAP, not TARGET_SIGNAL_0. + + * linux-low.c (ptid_is_pid): Delete. + (linux_wait_for_event): In non-stop, don't set the current + inferior arbitrarily. + (linux_wait): Flush the event pipe before polling for an event. + (wait_for_sigstop): In non-stop, don't set the current + inferior arbitrarily. + (linux_resume_one_lwp): Small cleanup. + (cancel_breakpoint): New. + (linux_resume_one_thread): Use it. Still report SIGTRAPs. + (regsets_store_inferior_registers): Plug leak. + (sigchld_handler): Don't use fprintf here. + + * mem-break.c (breakpoint_at): New. + * inferiors.c (ptid_is_pid): New. + * mem-break.h (breakpoint_at): Declare. + * server.c (discard_queued_stop_replies): Add `pid' argument. + Handle it. + (send_next_stop_reply): Cleanup. + (attach_inferior): Don't wait here in non-stop mode. + (handle_v_attach): Don't queue stop replies here. + (handle_v_kill): Discard queued stop replies of the inferior we + just killed. + (queue_stop_reply_callback): Add `arg' argument. Handle it. + (handle_status): Adjust. + (kill_inferior_callback): Discard queued stop replies of the + inferior we just killed. + (detach_or_kill_inferior_callback): Discard queued stop replies of + the inferior we just killed or detached from. + (process_serial_event): Cleanup. Discard queued stop replies of + the inferior we just detached. Don't write 0 bytes to the inferior. + (handle_serial_event): Debug output. + * server.h (ptid_is_pid): Declare. + + * remote-utils.c (prepare_resume_reply): Avoid reading registers + and memory from a thread that is gone. + + 2008-09-26 Pedro Alves <pedro@codesourcery.com> + + * server.h (struct sym_cache, struct process_info_private): + Forward declare. + (struct process_info): Add symbol_cache, all_symbols_looked_up and + private fields. + (current_process): Declare. + * remote-utils.c (struct sym_cache) <name>: Remove constness. + (symbol_cache): Delete. + (free_sym_cache, clear_symbol_cache): New. + (look_up_one_symbol): Adjust, to per-process symbol cache. + * inferiors.c (current_process): New. + + * linux-low.h: Include "gdb_proc_service.h". + (struct process_info_private): Define. + * linux-low.c (thread_db_active): Delete. + (linux_add_process): New. + (handle_extended_wait, linux_create_inferior, linux_attach): Use + it. + (linux_wait_for_event, linux_look_up_symbols): Adjust. + (initialize_low): Don't clear the global thread_db_active. + * thread-db.c (proc_handle, thread_agent): Delete. + (fixup_proc_handle): New. + (thread_db_err_str, thread_db_enable_reporting): Use it. Adjust. + (thread_db_find_new_threads): Look for the current inferior + thread, not the first thread in the list. Use fixup_proc_handle. + Adjust. + (thread_db_get_tls_address): Use fixup_proc_handle. + (thread_db_init): Likewise. Adjust. + + 2008-09-26 Pedro Alves <pedro@codesourcery.com> + + * mem-break.c (get_breakpoint_list): Add `create' argument. Only + create the list if create is set. + (remove_breakpoint_list): New. + (set_breakpoint_at, find_breakpoint_at, check_mem_read) + (check_mem_write, delete_all_breakpoints): Adjust. + + 2008-09-26 Pedro Alves <pedro@codesourcery.com> + + * server.c (gdbserver_usage): Describe --remote-debug option. + (main): Handle --remote-debug switch. + + 2008-09-19 Pedro Alves <pedro@codesourcery.com> + + Non-stop mode support. + + * linux-low.c (linux_event_pipe): New int array for pipe. + (target_is_async_p): New. + (handle_extended_wait): Use my_waitpid. + (linux_kill_one_lwp): Stop the LWP if it is not stopped already. + Use my_waitpid, not linux_wait_for_event. Remove the lwp and the + corresponding thread from the lists. + (linux_kill_one_process, linux_kill): Delete. + (linux_kill_1): Rename back to ... + (linux_kill) ... this. Stop the LWP if it is not stopped already. + Use my_waitpid, not linux_wait_for_event. Remove the lwp and the + corresponding thread from the lists. + (linux_detach_one_lwp): Make sure the LWP is stopped. Adjust to + new linux_wait_for_event interface. + (linux_detach_one_process, linux_detach): Delete. + (linux_detach_1): Rename back to ... + (linux_detach): This. + (linux_join): Add PID argument. Use my_waitpid instead of + waitpid. + (status_pending_p): Ignore suspended threads. + (my_waitpid): Emulate __WALL. + (linux_wait_for_lwp): Add 'options' argument. Handle it. Use + my_wait. If requesting an event from a specific process, leave + events in other processes pending. + (resume_stopped_lwps): New. + (linux_wait_for_event): Delete. + (linux_wait_for_event_1): Rename back to ... + (linux_wait_for_event): ... this. Change interface: add wstat and + options arguments, return -1 on error, 0 otherwise. Adjust. In + all-stop, resume stopped lwps if there was no pending status. + Don't return immediatelly if a pending status was found --- + continue handling it instead. Don't loop, only handle one event. + (linux_wait): Rename to ... + (linux_wait_1): ... this. Add target_options argument. Handle + it. Don't loop --- only handle one event. Ignore the continue + thread in non_stop mode. Adjust to new resume_kind interface. + Only stop all threads in all-stop mode. + (async_file_flush, async_file_mark): New. + (linux_wait): New. + (wait_for_sigstop): Set stopping_threads here. Use + linux_wait_for_lwp instead of linux_wait_for_event. Adjust. + (stop_all_lwps): Don't set stopping_threads here. + (resume_ptr): Delete. + (struct resume_info): New. + (linux_set_resume_request): Add arg argument. Adjust to take a + struct remove_info instead of the global resume_ptr. Accept + pid,-1 to apply to all threads. + (linux_continue_one_thread, linux_queue_one_thread): Merge both + and create ... + (linux_resume_one_thread): ... this. New. Handle rk_stop. + (resume_status_pending_p): Assume no resume info to mean do + nothing. Ignore suspended LWPs. + (linux_resume): Add n argument. Adjust. In non-stop mode, don't + look for a pending status over all threads. + (linux_read_offsets): Minor cleanup. + (sigchld_handler, linux_async, linux_start_non_stop): New. + (linux_target_ops): Register linux_async and linux_start_non_stop. + (initialize_low): Register sigchld_handler as SIGCHLD handler. + + * utils.c (internal_verror, internal_error_file_line): New. + + * Makefile.in (SFILES): Add event-loop.c. + (OBS): Add event-loop.o. + (event-loop.o): New rule. + + * linux-low.h (struct lwp_info) <suspended>: New flag. + + * thread-db.c (thread_db_create_event): Make sure thread_db reads + from the current inferior. + (thread_db_get_tls_address): Comment. + + * server.c (thread_from_wait, old_thread_from_wait, attached): + Delete. + (non_stop): New global. + (own_buf, mem_buf): New globals. + (struct vstop_notif): New. + (notif_queue): New. + (queue_stop_reply, push_event, discard_queued_stop_replies) + (send_next_stop_reply): New. + (start_inferior): Adjust to new thread_resume interface. Adjust + to new mywait interface. + (attach_inferior): Adjust. + (handle_general_set): Handle QNonStop. + (handle_query): Pass 'QNonStop+'. + (handle_v_cont): Handle vCont;t. Don't enable/disable async io in + non-stop mode. In non-stop return OK, and don't wait for the + target. + (handle_v_attach): In non-stop, return OK, and queue events for + all threads. + (handle_v_run): In non-stop, set the general thread here. + (handle_v_stopped): New. + (handle_v_requests): Report support for 't'. Handle 'vStopped'. + (proceed): Add comment. Adjust. In non-stop, don't + enable/disable async io; write 'OK', and don't wait for the + target. + (queue_stop_reply_callback, handle_status): New. + (kill_inferior_callback, detach_or_kill_inferior_callback) + (join_inferiors_callback): New. + (main): In --debug mode, also enable remote debug. Don't pass -1 + to kill_inferior or detach_inferior; instead, iterate over all + processes killing or detaching them. Adjust to use the even-loop. + (process_serial_event): New, factored out of main. If the + connection closed, remove all sources from the event loop. + Iterate over all inferiors joining them. Use handle_status. + Don't print inferior exit notices here. In non-stop, defer + exiting until GDB read all queued events. + (handle_serial_event, handle_target_event): New. + + * server.h (FOR_EACH_INFERIOR): New. + (thread_from_wait, old_thread_from_wait): Remove. + (non_stop): Declare. + (gdb_client_data, handler_func): New typedefs. + (delete_file_handler, add_file_handler, start_event_loop) + (handle_serial_event, handle_target_event, push_event) + (putpkt_notif, internal_error_file_line): Declare. + (internal_error): Define. + + * target.c (mywait): Add `options' argument. Print inferior exit + notices here. + (start_non_stop): New. + + * event-loop.c: New. + + * remote-utils.c (remote_open): Register remote_desc in the event + loop, with handle_serial_event as callback. + (remote_close): Remove remote_desc from the event loop. + (hex_or_minus_one): New. + (read_ptid, read_ptid): Use it. + (putpkt_binary): Rename to ... + (putpkt_binary_1): ... this. Add `notif' argument. Handle + pushing a remote protocol notification. + (putpkt_binary): New as wrapper around putpkt_binary_1. + (putpkt_notif): New. + (getpkt): Debug output. + (prepare_resume_reply): Remove dead code. In non-stop, don't set + the general thread here. + + * target.h (enum resume_kind): New. + (struct thread_resume) <leave_stopped, step>: Delete. + (struct thread_resume) <kind>: New field. + (TARGET_WNOHANG): Define. + (struct target_ops) <kill, detach>: Adjust comments. + (struct target_ops) <join>: Add `pid' argument. + (struct target_ops) <resume>: Add `n' argument. + (struct target_ops) <wait>: Add `options' argument. + (struct target_ops) <async, start_non_stop>: New fields. + (join_inferior): Add `pid' argument. + (target_async): New. + (start_non_stop): Declare. + (mywait): Add options argument. + + 2008-09-15 Pedro Alves <pedro@codesourcery.com> + + * linux-low.c (new_inferior): Mention in the comment that all + inferiors should have the same architecture for now. + (linux_create_inferior, linux_attach): Only set new_inferior if + this is the first process. + + 2008-08-29 Pedro Alves <pedro@codesourcery.com> + + * linux-low.c (ptid_is_pid): Move higher. + (linux_wait_for_lwp): Remove dead code. + (linux_wait_for_event): Rename to ... + (linux_wait_for_event_1): ... this. + (linux_wait_for_event): New. + (ptid_same_pid): Delete. + (linux_set_resume_request): Clearify. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + * linux-low.c (handle_extended_wait, linux_attach_lwp) + (linux_attach): Minor cleanups. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + * linux-low.c (linux_thread_alive): Return false for a listed + thread known to be dead. + (linux_wait_for_event): Don't set the dead flag here. + (wait_for_sigstop): Store ptid before waiting for the event. + Minor cleanup. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + * inferiors.c (find_inferior): Allow deleting the current iterated + inferior. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + * linux-low.c (linux_pid_to_exec_file): Move higher. + (linux_enable_event_reporting): Enable PTRACE_O_TRACEEXEC. + (handle_extended_wait): Handle PTRACE_EVENT_EXEC. + + * remote-utils.c (prepare_resume_reply): Set the general thread to + the last thread that had an event in TARGET_WAITKIND_FORKED and + TARGET_WAITKIND_VFORKED. Handle TARGET_WAITKIND_EXECD. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + * server.c (handle_query): Pass "QExecFile:PID;" back in the reply. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + * linux-low.c (linux_supports_tracefork_flag): Move higher. + (linux_enable_event_reporting): New. + (handle_extended_wait): Change return type to int. Handle + PTRACE_EVENT_FORK and PTRACE_EVENT_VFORK. + (add_lwp): Set waitstatus.kind to TARGET_WAITKIND_IGNORE. + (linux_attach_lwp): Use linux_enable_event_reporting. + (linux_wait_for_event): Don't keep waiting if the extended wait + status should be reported to gdb. + (linux_wait): Use linux_enable_event_reporting. If waitstatus + holds a processed event, return it instead. + * remote-utils.c (prepare_resume_reply): Handle + TARGET_WAITKIND_FORKED and TARGET_WAITKIND_VFORKED. + * linux-low.h (struct lwp_info) <waitstatus>: New member. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + * target.h (struct target_ops) <pid_to_exec_file>: New member. + * server.c (handle_query): Handle qExecFile. + * linux-low.c (linux_pid_to_exec_file): New. + (linux_target_ops): Register it. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + * linux-low.c (is_lwpid): New. + (linux_kill_one_lwp, linux_kill_1, linux_detach_one_lwp): Adjust. + (status_pending_p): Check if we're interested in this lwp. + (linux_wait_for_lwp): Change signature: return an lwp_info*, and + take a ptid instead of an lwp_info**. + (linux_wait_for_event): Take a ptid instead of a thread_info + pointer. Adjust. + (wait_for_sigstop): Adjust. If a whole process died, keep the + exit status pending. + (ptid_is_pid, ptid_same_pid): New. + (linux_set_resume_request): Allow resuming all threads of a process. + (resume_status_pending_p): Check for dead lwps. + + * linux-low.h (struct lwp_info) <dead>: New field. + + * server.c (start_inferior): Only resume and wait for events from + the inferior we're creating. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + Decouple target code from remote protocol. + + * linux-low.c (linux_wait): Change prototype. Adjust. + * server.c (last_status, last_ptid): New. + (start_inferior): Remove "statusptr" argument. Adjust. + (attach_inferior, handle_v_cont, handle_v_attach, handle_v_run) + (handle_v_kill, handle_v_requests): Remove "status" and "signal" + parameters. Adjust. + (myresume): Rename to ... + (proceed): ... this. Remove "statusp" parameter. Adjust. + (main): Remove "status" local. Adjust. + * target.c (mywait): Change prototype. Adjust. + * target.h (enum target_waitkind): New. + (struct target_waitstatus): New. + (struct target_ops) <wait>: Change prototype. + (mywait): Adjust. + * remote-utils.c: Include "target.h". + (prepare_resume_reply): Change prototype to take a ptid and a + target_waitstatus. Adjust. + * server.h (prepare_resume_reply): Adjust prototype. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + * inferiors.c (all_processes): New. + (add_process): New. + + * linux-low.c (linux_create_inferior): Add process. + (linux_attach_lwp): Add "initial" parameter, and use it instead of + relying on having only one thread in the global list. + (linux_attach): Add process. + (struct counter): New. + (check_if_last_thread_of_pid, is_last_thread_of_process): New. + (linux_kill_one_lwp): Add ARGS parameter. Change return type to + int. Check if we're interested in this lwp. Use + is_last_thread_of_process. + (linux_kill): Rename to ... + (linux_kill_1): ... this. Kill lwps of the requested only. + (linux_kill_one_process): New. + (linux_kill): New. + (linux_detach_one_lwp): Add ARGS parameter. Change return type to + int. Check if we're interested in this lwp. Remove the lwp from + both the lwp list and the thread list. + (any_thread_of, linux_detach_1, linux_detach_one_process): New. + (linux_detach): Reimplement. + (linux_wait_for_event): Use is_last_thread_of_process. + (linux_wait): Likewise. On process exit, don't clear all inferiors. + Implement multi-process extensions. + + * mem-break.c (breakpoints): Delete. + (struct breakpoint_list): New. + (all_breakpoints): New. + (get_breakpoint_list): New. + (set_breakpoint_at, delete_breakpoint, find_breakpoint_at) + (check_mem_read, check_mem_write, delete_all_breakpoints): Use it. + + * server.h (struct process_info): New. + (all_processes): Declare. + (add_process): Declare. + + * linux-low.h (linux_attach_lwp): Add "initial" parameter. + + * thread-db.c (maybe_attach_thread): Adjust. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + * linux-low.c, linux-low.h, proc-service.c, thread-db.c: Rename + `struct process_info' to `struct lwp_info', and adjust throughout. + + 2008-08-28 Pedro Alves <pedro@codesourcery.com> + + Implement remote protocol multi-process extensions. + + * inferiors.c (null_ptid, minus_one_ptid): New. + (ptid_build, pid_to_ptid, ptid_get_pid, ptid_get_lwp) + (ptid_get_tid, ptid_equal): New functions. + (add_thread): Drop gdb_id argument. Retype thread_id argument to + ptid_t. Adjust. + (thread_id_to_gdb_id): Adjust. + (thread_to_gdb_id): Change return type to ptid_t. Adjust. + (gdb_id_to_thread): Rename to ... + (find_thread_pid): ... this. Adjust. + (gdb_id_to_thread_id): Change return type to ptid_t. Adjust. + (find_inferior_id): Change id argument type to ptid_t. Adjust. + (loaded_dll, add_pid_to_list, pull_pid_from_list): Adjust. + (initialize_inferiors): New. + + * remote-utils.c (hexchars): New. + (ishex, unpack_varlen_hex, write_ptid, read_ptid): New. + (prepare_resume_reply): Adjust. + + * server.c (cont_thread, general_thread, step_thread) + (thread_from_wait, old_thread_from_wait): Change type to ptid_t. + (multi_process): New. + (start_inferior): Adjust. + (handle_query): Adjust. Report multiprocess extensions support. + (handle_v_cont): Adjust. + (handle_v_kill): New. + (handle_v_requests): Handle vKill. + (myresume): Adjust. + (first_thread_of): New. + (main): Call initialize_inferiors. If bailing out, kill all + inferiors. Handle multi-process detach. Handle multi-process H + and T. + + * server.h (ULONGEST): New typedef. + (struct ptid): New struct. + (ptid_t): New typedef. + (minus_one_ptid, null_ptid): New. + (ptid_t ptid_build, pid_to_ptid, ptid_get_pid, ptid_get_lwp) + (ptid_get_tid, ptid_equal): New. + (struct inferior_list_entry) <id>: Change type to ptid_t. + (add_thread, thread_id_to_gdb_id, thread_to_gdb_id) + (gdb_id_to_thread_id): Adjust prototypes. + (find_thread_pid): Declare. + (find_inferior_id): Adjust prototype. + (cont_thread, general_thread, step_thread, thread_from_wait) + (old_thread_from_wait): Adjust type to ptid_t. + (multi_process): Declare. + (read_ptid, write_ptid): Declare. + + * linux-low.c (pid_of): Adjust. + (lwpid_of): New. + (inferior_lwpid): New. + (handle_extended_wait): Adjust. + (add_process): Change pid argument to a ptid. Adjust. + (linux_create_inferior): Adjust. + (linux_attach_lwp): Adjust. Clear new_inferior on error. If + creating a new inferior, don't rely on inferior_pid, instead use + the lwpid as pid. + (linux_attach): Set new_inferior earlier. Adjust. + (linux_kill): Change return type to int. Adjust. + (linux_detach): Add pid parameter. + (linux_thread_alive): Change lwpid paremeter type to ptid. + Adjust. + (same_lwp, find_lwp_pid): New. + (linux_wait_for_process): Adjust. + (linux_wait_for_process, linux_wait_for_event, send_sigstop) + (wait_for_sigstop, linux_resume_one_process, + (linux_resume_one_process, linux_set_resume_request) + (linux_continue_one_thread, linux_queue_one_thread) + (fetch_register, usr_store_inferior_registers) + (regsets_fetch_inferior_registers) + (regsets_store_inferior_registers, linux_read_memory) + (linux_write_memory, linux_request_interrupt, linux_read_auxv): + Adjust. + + * linux-low.h (get_process_thread): Adjust. + (struct process_info) <lwpid>: Remove. + (find_lwp_pid): Declare. + + * target.c (set_desired_inferior): Adjust. + (target_pid_to_str): New. + + * target.h (struct thread_resume) <thread>: Change type to ptid_t. + (struct target_ops) <kill>: Change return type to int, and take an + int as parameter. + (struct target_ops) <detach>: Take an int as parameter. + (struct target_ops) <thread_alive>: Change pid argument type to + ptid_t. + (kill_inferior, detach_inferior): Add PID argument. + + * thread-db.c (thread_db_create_event): Adjust. + (find_one_thread): Change argument to a ptid. Adjust. + (maybe_attach_thread, thread_db_get_tls_address, thread_db_init): + Adjust. + + * proc-service.c (ps_lgetregs): Adjust. + 2008-11-19 Bob Wilson <bob.wilson@acm.org> * xtensa-xtregs.c (XTENSA_ELF_XTREG_SIZE): Change to 4. diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index 641ebe2..882ce93 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -133,7 +133,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPARAM} ${POSSLIBS} OBS = inferiors.o regcache.o remote-utils.o server.o signals.o target.o \ utils.o version.o \ - mem-break.o hostio.o \ + mem-break.o hostio.o event-loop.o \ $(XML_BUILTIN) \ $(DEPFILES) $(LIBOBJS) GDBREPLAY_OBS = gdbreplay.o version.o @@ -283,6 +283,7 @@ server.o: server.c $(server_h) target.o: target.c $(server_h) thread-db.o: thread-db.c $(server_h) $(gdb_proc_service_h) utils.o: utils.c $(server_h) +event-loop.o: event-loop.c $(server_h) gdbreplay.o: gdbreplay.c config.h signals.o: ../signals/signals.c $(server_h) diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c index f35cfa0..5fb6d80 100644 --- a/gdb/gdbserver/inferiors.c +++ b/gdb/gdbserver/inferiors.c @@ -30,12 +30,88 @@ struct thread_info unsigned int gdb_id; }; +struct inferior_list all_processes; struct inferior_list all_threads; struct inferior_list all_dlls; int dlls_changed; struct thread_info *current_inferior; + +/* Oft used ptids */ +ptid_t null_ptid; +ptid_t minus_one_ptid; + +/* Create a ptid given the necessary PID, LWP, and TID components. */ + +ptid_t +ptid_build (int pid, long lwp, long tid) +{ + ptid_t ptid; + + ptid.pid = pid; + ptid.lwp = lwp; + ptid.tid = tid; + return ptid; +} + +/* Create a ptid from just a pid. */ + +ptid_t +pid_to_ptid (int pid) +{ + return ptid_build (pid, 0, 0); +} + +/* Fetch the pid (process id) component from a ptid. */ + +int +ptid_get_pid (ptid_t ptid) +{ + return ptid.pid; +} + +/* Fetch the lwp (lightweight process) component from a ptid. */ + +long +ptid_get_lwp (ptid_t ptid) +{ + return ptid.lwp; +} + +/* Fetch the tid (thread id) component from a ptid. */ + +long +ptid_get_tid (ptid_t ptid) +{ + return ptid.tid; +} + +/* ptid_equal() is used to test equality of two ptids. */ + +int +ptid_equal (ptid_t ptid1, ptid_t ptid2) +{ + return (ptid1.pid == ptid2.pid + && ptid1.lwp == ptid2.lwp + && ptid1.tid == ptid2.tid); +} + +/* Return true if this ptid represents a process. */ + +int +ptid_is_pid (ptid_t ptid) +{ + if (ptid_equal (minus_one_ptid, ptid)) + return 0; + if (ptid_equal (null_ptid, ptid)) + return 0; + + return (ptid_get_pid (ptid) != 0 + && ptid_get_lwp (ptid) == 0 + && ptid_get_tid (ptid) == 0); +} + #define get_thread(inf) ((struct thread_info *)(inf)) #define get_dll(inf) ((struct dll_info *)(inf)) @@ -93,7 +169,7 @@ remove_inferior (struct inferior_list *list, } void -add_thread (unsigned long thread_id, void *target_data, unsigned int gdb_id) +add_thread (ptid_t thread_id, void *target_data) { struct thread_info *new_thread = malloc (sizeof (*new_thread)); @@ -101,47 +177,45 @@ add_thread (unsigned long thread_id, void *target_data, unsigned int gdb_id) new_thread->entry.id = thread_id; - add_inferior_to_list (&all_threads, & new_thread->entry); + add_inferior_to_list (&all_threads, &new_thread->entry); if (current_inferior == NULL) current_inferior = new_thread; new_thread->target_data = target_data; set_inferior_regcache_data (new_thread, new_register_cache ()); - new_thread->gdb_id = gdb_id; } -unsigned int -thread_id_to_gdb_id (unsigned long thread_id) +ptid_t +thread_id_to_gdb_id (ptid_t thread_id) { struct inferior_list_entry *inf = all_threads.head; while (inf != NULL) { - struct thread_info *thread = get_thread (inf); - if (inf->id == thread_id) - return thread->gdb_id; + if (ptid_equal (inf->id, thread_id)) + return thread_id; inf = inf->next; } - return 0; + return null_ptid; } -unsigned int +ptid_t thread_to_gdb_id (struct thread_info *thread) { - return thread->gdb_id; + return thread->entry.id; } struct thread_info * -gdb_id_to_thread (unsigned int gdb_id) +find_thread_pid (ptid_t ptid) { struct inferior_list_entry *inf = all_threads.head; while (inf != NULL) { struct thread_info *thread = get_thread (inf); - if (thread->gdb_id == gdb_id) + if (ptid_equal (thread->entry.id, ptid)) return thread; inf = inf->next; } @@ -149,12 +223,12 @@ gdb_id_to_thread (unsigned int gdb_id) return NULL; } -unsigned long -gdb_id_to_thread_id (unsigned int gdb_id) +ptid_t +gdb_id_to_thread_id (ptid_t gdb_id) { - struct thread_info *thread = gdb_id_to_thread (gdb_id); + struct thread_info *thread = find_thread_pid (gdb_id); - return thread ? thread->entry.id : 0; + return thread ? thread->entry.id : null_ptid; } static void @@ -180,22 +254,25 @@ find_inferior (struct inferior_list *list, while (inf != NULL) { + struct inferior_list_entry *next; + + next = inf->next; if ((*func) (inf, arg)) return inf; - inf = inf->next; + inf = next; } return NULL; } struct inferior_list_entry * -find_inferior_id (struct inferior_list *list, unsigned long id) +find_inferior_id (struct inferior_list *list, ptid_t id) { struct inferior_list_entry *inf = list->head; while (inf != NULL) { - if (inf->id == id) + if (ptid_equal (inf->id, id)) return inf; inf = inf->next; } @@ -264,7 +341,7 @@ loaded_dll (const char *name, CORE_ADDR base_addr) struct dll_info *new_dll = malloc (sizeof (*new_dll)); memset (new_dll, 0, sizeof (*new_dll)); - new_dll->entry.id = -1; + new_dll->entry.id = minus_one_ptid; new_dll->name = strdup (name); new_dll->base_addr = base_addr; @@ -315,7 +392,7 @@ add_pid_to_list (struct inferior_list *list, unsigned long pid) struct inferior_list_entry *new_entry; new_entry = malloc (sizeof (struct inferior_list_entry)); - new_entry->id = pid; + new_entry->id = pid_to_ptid (pid); add_inferior_to_list (list, new_entry); } @@ -324,7 +401,7 @@ pull_pid_from_list (struct inferior_list *list, unsigned long pid) { struct inferior_list_entry *new_entry; - new_entry = find_inferior_id (list, pid); + new_entry = find_inferior_id (list, pid_to_ptid (pid)); if (new_entry == NULL) return 0; else @@ -334,3 +411,43 @@ pull_pid_from_list (struct inferior_list *list, unsigned long pid) return 1; } } + +struct process_info * +add_process (int pid, int attached) +{ + struct process_info *process; + + process = (struct process_info *) malloc (sizeof (*process)); + memset (process, 0, sizeof (*process)); + + process->head.id = pid_to_ptid (pid); + process->attached = attached; + + add_inferior_to_list (&all_processes, &process->head); + + return process; +} + +struct process_info * +current_process (void) +{ + struct process_info *process; + int pid; + + if (current_inferior == NULL) + internal_error ("No current_inferior\n"); + + pid = ptid_get_pid (current_inferior->entry.id); + process = (struct process_info *) find_inferior_id (&all_processes, + pid_to_ptid (pid)); + + return process; +} + + +void +initialize_inferiors (void) +{ + null_ptid = ptid_build (0, 0, 0); + minus_one_ptid = ptid_build (-1, 0, 0); +} diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 4766cc9..c303e44 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -82,16 +82,19 @@ #endif #endif -/* ``all_threads'' is keyed by the LWP ID, which we use as the GDB protocol - representation of the thread ID. +/* ``all_processes'' is keyed by the "overall process ID", which + GNU/Linux calls tgid, "thread group ID". - ``all_processes'' is keyed by the process ID - which on Linux is (presently) - the same as the LWP ID. */ + ``all_threads'' is keyed by the LWP ID, which we use as the GDB + protocol representation of the thread ID. -struct inferior_list all_processes; + ``all_lwps'' is keyed by the LWP ID - on Linux the LWP IDs are + global (presently). */ -/* A list of all unknown processes which receive stop signals. Some other - process will presumably claim each of these as forked children +struct inferior_list all_lwps; + +/* A list of all unknown lwps which receive stop signals. Some other + lwp will presumably claim each of these as forked children momentarily. */ struct inferior_list stopped_pids; @@ -101,22 +104,25 @@ int stopping_threads; /* FIXME make into a target method? */ int using_threads = 1; -static int thread_db_active; static int must_set_ptrace_flags; -/* This flag is true iff we've just created or attached to a new inferior - but it has not stopped yet. As soon as it does, we need to call the - low target's arch_setup callback. */ +/* This flag is true iff we've just created or attached to our first + inferior but it has not stopped yet. As soon as it does, we need + to call the low target's arch_setup callback. Doing this only on + the first inferior avoids reinializing the architecture on every + inferior, and avoids messing with the register caches of the + already running inferiors. NOTE: this assumes all inferiors under + control of gdbserver have the same architecture. */ static int new_inferior; -static void linux_resume_one_process (struct inferior_list_entry *entry, - int step, int signal, siginfo_t *info); -static void linux_resume (struct thread_resume *resume_info); -static void stop_all_processes (void); -static int linux_wait_for_event (struct thread_info *child); -static int check_removed_breakpoint (struct process_info *event_child); -static void *add_process (unsigned long pid); +static void linux_resume_one_lwp (struct inferior_list_entry *entry, + int step, int signal, siginfo_t *info); +static void linux_resume (struct thread_resume *resume_info, size_t n); +static void stop_all_lwps (void); +static int linux_wait_for_event (ptid_t ptid, int *wstat, int options); +static int check_removed_breakpoint (struct lwp_info *event_child); +static void *add_lwp (ptid_t ptid); struct pending_signals { @@ -133,23 +139,124 @@ static char *disabled_regsets; static int num_regsets; #endif -#define pid_of(proc) ((proc)->head.id) +#define pid_of(proc) ptid_get_pid ((proc)->head.id) +#define lwpid_of(proc) ptid_get_lwp ((proc)->head.id) + +#define is_lwpid(ptid) (ptid_get_lwp (ptid) != 0) /* FIXME: Delete eventually. */ -#define inferior_pid (pid_of (get_thread_process (current_inferior))) +#define inferior_pid (pid_of (get_thread_lwp (current_inferior))) +#define inferior_lwpid (lwpid_of (get_thread_lwp (current_inferior))) + +/* The read/write ends of the pipe registered as waitable file in the + event loop. */ +static int linux_event_pipe[2] = { -1, -1 }; + +/* True if we're currently in async mode. */ +#define target_is_async_p() (linux_event_pipe[0] != -1) + +static int my_waitpid (int pid, int *status, int flags); +static void send_sigstop (struct inferior_list_entry *entry); +static void wait_for_sigstop (struct inferior_list_entry *entry); +static int cancel_breakpoints_callback (struct inferior_list_entry *entry, + void *data); + +/* Add a process to the common process list, and set its private + data. */ + +static struct process_info * +linux_add_process (int pid, int attached) +{ + struct process_info *proc; + + proc = add_process (pid, attached); + proc->private = calloc (1, sizeof (*proc->private)); + + return proc; +} + +/* Read the tgid of an lwp from `/proc/PID/status'. */ + +static int +tgid_of_pid (int pid) +{ + FILE *status_file; + char buf[100]; + int tgid = 0; + + snprintf (buf, sizeof (buf), "/proc/%d/status", pid); + status_file = fopen (buf, "r"); + if (status_file != NULL) + { + while (fgets (buf, sizeof (buf), status_file)) + { + if (strncmp (buf, "Tgid:", 5) == 0) + { + tgid = strtol (buf + 5, NULL, 0); + break; + } + } + fclose (status_file); + } + + return tgid; +} + +static char * +linux_pid_to_exec_file (int pid) +{ + static char path[MAXPATHLEN]; + char procexe[MAXPATHLEN]; + + sprintf (procexe, "/proc/%d/exe", pid); + if (readlink (procexe, path, MAXPATHLEN) > 0) + return path; + else + return NULL; +} + +static int linux_supports_tracefork_flag; static void -handle_extended_wait (struct process_info *event_child, int wstat) +linux_enable_event_reporting (int pid) +{ + int options; + + options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEEXEC | PTRACE_O_TRACECLONE; + +#if 0 + /* TODO: We need to model this in the remote protocol. */ + if (linux_supports_tracevforkdone (pid)) + options |= PTRACE_O_TRACEVFORKDONE; +#endif + + /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support + read-only process state. */ + ptrace (PTRACE_SETOPTIONS, pid, 0, options); +} + +/* Handle a GNU/Linux extended wait response. If we see a clone + event, we need to add the new LWP to our list (and not report the + trap to higher layers). This function returns non-zero if the + event should be ignored and we should wait again. */ + +static int +handle_extended_wait (struct lwp_info *event_child, int wstat) { int event = wstat >> 16; - struct process_info *new_process; + struct lwp_info *new_lwp; + struct target_waitstatus *ourstatus = &event_child->waitstatus; - if (event == PTRACE_EVENT_CLONE) + if (event == PTRACE_EVENT_FORK + || event == PTRACE_EVENT_VFORK + || event == PTRACE_EVENT_CLONE) { + ptid_t ptid; unsigned long new_pid; int ret, status = W_STOPCODE (SIGSTOP); - ptrace (PTRACE_GETEVENTMSG, inferior_pid, 0, &new_pid); + ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_child), 0, &new_pid); /* If we haven't already seen the new PID stop, wait for it now. */ if (! pull_pid_from_list (&stopped_pids, new_pid)) @@ -157,9 +264,7 @@ handle_extended_wait (struct process_info *event_child, int wstat) /* The new child has a pending SIGSTOP. We can't affect it until it hits the SIGSTOP, but we're already attached. */ - do { - ret = waitpid (new_pid, &status, __WALL); - } while (ret == -1 && errno == EINTR); + ret = my_waitpid (new_pid, &status, __WALL); if (ret == -1) perror_with_name ("waiting for new child"); @@ -169,54 +274,103 @@ handle_extended_wait (struct process_info *event_child, int wstat) warning ("wait returned unexpected status 0x%x", status); } - ptrace (PTRACE_SETOPTIONS, new_pid, 0, PTRACE_O_TRACECLONE); + ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0); - new_process = (struct process_info *) add_process (new_pid); - add_thread (new_pid, new_process, new_pid); - new_thread_notify (thread_id_to_gdb_id (new_process->lwpid)); + linux_enable_event_reporting (new_pid); - /* Normally we will get the pending SIGSTOP. But in some cases - we might get another signal delivered to the group first. - If we do, be sure not to lose it. */ - if (WSTOPSIG (status) == SIGSTOP) + if (event == PTRACE_EVENT_FORK + || event == PTRACE_EVENT_VFORK) { - if (stopping_threads) - new_process->stopped = 1; + int is_fork = (event == PTRACE_EVENT_FORK); + + if (is_fork) + ourstatus->kind = TARGET_WAITKIND_FORKED; else - ptrace (PTRACE_CONT, new_pid, 0, 0); + ourstatus->kind = TARGET_WAITKIND_VFORKED; + + if (debug_threads) + { + if (is_fork) + fprintf (stderr, "PTRACE_EVENT_FORK pid=(%ld), newpid=(%ld)", + lwpid_of (event_child), new_pid); + else + fprintf (stderr, "PTRACE_EVENT_VFORK pid=(%ld), newpid=(%ld)", + lwpid_of (event_child), new_pid); + } + + linux_add_process (new_pid, 1); + ptid = ptid_build (new_pid, new_pid, 0); + new_lwp = (struct lwp_info *) add_lwp (ptid); + new_lwp->stopped = 1; + add_thread (ptid, new_lwp); + + event_child->stopped = 1; + return 0; } else { - new_process->stop_expected = 1; - if (stopping_threads) + ptid = ptid_build (pid_of (event_child), new_pid, 0); + new_lwp = (struct lwp_info *) add_lwp (ptid); + add_thread (ptid, new_lwp); +#if 0 + new_thread_notify (thread_id_to_gdb_id (new_lwp->lwpid)); +#endif + + /* Normally we will get the pending SIGSTOP. But in some + cases we might get another signal delivered to the group + first. If we do, be sure not to lose it. */ + if (WSTOPSIG (status) == SIGSTOP) { - new_process->stopped = 1; - new_process->status_pending_p = 1; - new_process->status_pending = status; + if (stopping_threads) + new_lwp->stopped = 1; + else + ptrace (PTRACE_CONT, new_pid, 0, 0); } else - /* Pass the signal on. This is what GDB does - except - shouldn't we really report it instead? */ - ptrace (PTRACE_CONT, new_pid, 0, WSTOPSIG (status)); - } + { + new_lwp->stop_expected = 1; + if (stopping_threads) + { + new_lwp->stopped = 1; + new_lwp->status_pending_p = 1; + new_lwp->status_pending = status; + } + else + /* Pass the signal on. This is what GDB does - except + shouldn't we really report it instead? */ + ptrace (PTRACE_CONT, new_pid, 0, WSTOPSIG (status)); + } - /* Always resume the current thread. If we are stopping - threads, it will have a pending SIGSTOP; we may as well - collect it now. */ - linux_resume_one_process (&event_child->head, + /* Always resume the current thread. If we are stopping + threads, it will have a pending SIGSTOP; we may as well + collect it now. */ + linux_resume_one_lwp (&event_child->head, event_child->stepping, 0, NULL); + return 1; + } } + else if (event == PTRACE_EVENT_EXEC) + { + fprintf (stderr, "got PTRACE_EVENT_EXEC\n"); + + ourstatus->kind = TARGET_WAITKIND_EXECD; + ourstatus->value.execd_pathname + = strdup (linux_pid_to_exec_file (lwpid_of (event_child))); + return 0; + } + + error ("unknown ptrace event %d", event); } -/* This function should only be called if the process got a SIGTRAP. +/* This function should only be called if the lwp got a SIGTRAP. The SIGTRAP could mean several things. On i386, where decr_pc_after_break is non-zero: - If we were single-stepping this process using PTRACE_SINGLESTEP, + If we were single-stepping this lwp using PTRACE_SINGLESTEP, we will get only the one SIGTRAP (even if the instruction we stepped over was a breakpoint). The value of $eip will be the next instruction. - If we continue the process using PTRACE_CONT, we will get a + If we continue the lwp using PTRACE_CONT, we will get a SIGTRAP when we hit a breakpoint. The value of $eip will be the instruction after the breakpoint (i.e. needs to be decremented). If we report the SIGTRAP to GDB, we must also @@ -235,26 +389,27 @@ get_stop_pc (void) { CORE_ADDR stop_pc = (*the_low_target.get_pc) (); - if (get_thread_process (current_inferior)->stepping) + if (get_thread_lwp (current_inferior)->stepping) return stop_pc; else return stop_pc - the_low_target.decr_pc_after_break; } static void * -add_process (unsigned long pid) +add_lwp (ptid_t ptid) { - struct process_info *process; + struct lwp_info *lwp; + + lwp = (struct lwp_info *) malloc (sizeof (*lwp)); + memset (lwp, 0, sizeof (*lwp)); - process = (struct process_info *) malloc (sizeof (*process)); - memset (process, 0, sizeof (*process)); + lwp->head.id = ptid; - process->head.id = pid; - process->lwpid = pid; + lwp->waitstatus.kind = TARGET_WAITKIND_IGNORE; - add_inferior_to_list (&all_processes, &process->head); + add_inferior_to_list (&all_lwps, &lwp->head); - return process; + return lwp; } /* Start an inferior process and returns its pid. @@ -263,8 +418,9 @@ add_process (unsigned long pid) static int linux_create_inferior (char *program, char **allargs) { - void *new_process; + void *new_lwp; int pid; + ptid_t ptid; #if defined(__UCLIBC__) && defined(HAS_NOMMU) pid = vfork (); @@ -292,10 +448,16 @@ linux_create_inferior (char *program, char **allargs) _exit (0177); } - new_process = add_process (pid); - add_thread (pid, new_process, pid); + /* If this the first process? If so, then set the arch. */ + if (all_processes.head == NULL) + new_inferior = 1; + + linux_add_process (pid, 0); + + ptid = ptid_build (pid, pid, 0); + new_lwp = add_lwp (ptid); + add_thread (ptid, new_lwp); must_set_ptrace_flags = 1; - new_inferior = 1; return pid; } @@ -303,165 +465,314 @@ linux_create_inferior (char *program, char **allargs) /* Attach to an inferior process. */ void -linux_attach_lwp (unsigned long pid) +linux_attach_lwp (unsigned long pid, int initial) { - struct process_info *new_process; + ptid_t ptid; + struct lwp_info *new_lwp; if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0) { - if (all_threads.head != NULL) + if (!initial) { /* If we fail to attach to an LWP, just warn. */ - fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid, + fprintf (stderr, "Cannot attach to lwp %ld: %s (%d)\n", pid, strerror (errno), errno); fflush (stderr); return; } else - /* If we fail to attach to a process, report an error. */ - error ("Cannot attach to process %ld: %s (%d)\n", pid, - strerror (errno), errno); + { + new_inferior = 0; + + /* If we fail to attach to a process, report an error. */ + error ("Cannot attach to lwp %ld: %s (%d)\n", pid, + strerror (errno), errno); + } } - ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACECLONE); + linux_enable_event_reporting (pid); - new_process = (struct process_info *) add_process (pid); - add_thread (pid, new_process, pid); - new_thread_notify (thread_id_to_gdb_id (new_process->lwpid)); + if (initial) + /* NOTE/FIXME: This lwp might have not been the tgid. */ + ptid = ptid_build (pid, pid, 0); + else + ptid = ptid_build (inferior_pid, pid, 0); + + new_lwp = (struct lwp_info *) add_lwp (ptid); + add_thread (ptid, new_lwp); + +#if 0 + new_thread_notify (thread_id_to_gdb_id (new_lwp->lwpid)); +#endif /* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH - brings it to a halt. We should ignore that SIGSTOP and resume the process + brings it to a halt. We should ignore that SIGSTOP and resume the lwp (unless this is the first process, in which case the flag will be cleared in linux_attach). On the other hand, if we are currently trying to stop all threads, we should treat the new thread as if we had sent it a SIGSTOP. This works - because we are guaranteed that add_process added us to the end of the + because we are guaranteed that add_lwp added us to the end of the list, and so the new thread has not yet reached wait_for_sigstop (but will). */ if (! stopping_threads) - new_process->stop_expected = 1; + new_lwp->stop_expected = 1; } int linux_attach (unsigned long pid) { - struct process_info *process; + struct lwp_info *lwp; + int tgid = tgid_of_pid (pid); + + /* Maybe we could be smarter about this. */ + if (tgid != 0 && tgid != pid) + warning ("%s is a cloned process", + target_pid_to_str (pid_to_ptid (pid))); - linux_attach_lwp (pid); + linux_attach_lwp (pid, 1); + + /* If this the first process? If so, then set the arch. */ + if (all_processes.head == NULL) + new_inferior = 1; + + linux_add_process (pid, 1); /* Don't ignore the initial SIGSTOP if we just attached to this process. It will be collected by wait shortly. */ - process = (struct process_info *) find_inferior_id (&all_processes, pid); - process->stop_expected = 0; + lwp = (struct lwp_info *) find_inferior_id (&all_lwps, + ptid_build (pid, pid, 0)); + lwp->stop_expected = 0; + + return 0; +} + +struct counter +{ + int pid; + int count; +}; + +static int +check_if_last_thread_of_pid (struct inferior_list_entry *entry, void *args) +{ + struct counter *counter = args; - new_inferior = 1; + if (ptid_get_pid (entry->id) == counter->pid) + { + counter->count++; + + if (counter->count > 1) + /* OK, we know it's not the last, we can stop counting. */ + return 1; + } return 0; } -/* Kill the inferior process. Make us have no inferior. */ +static int +is_last_thread_of_process (struct thread_info *thread) +{ + ptid_t ptid = ((struct inferior_list_entry *)thread)->id; + int pid = ptid_get_pid (ptid); + struct counter counter = { pid , 0 }; -static void -linux_kill_one_process (struct inferior_list_entry *entry) + return (find_inferior (&all_threads, + check_if_last_thread_of_pid, &counter) == NULL); +} + +/* Kill the inferior lwp. */ + +static int +linux_kill_one_lwp (struct inferior_list_entry *entry, void *args) { struct thread_info *thread = (struct thread_info *) entry; - struct process_info *process = get_thread_process (thread); + struct lwp_info *lwp = get_thread_lwp (thread); int wstat; + int pid = * (int *) args; + + if (ptid_get_pid (entry->id) != pid) + return 0; /* We avoid killing the first thread here, because of a Linux kernel (at least 2.6.0-test7 through 2.6.8-rc4) bug; if we kill the parent before the children get a chance to be reaped, it will remain a zombie forever. */ - if (entry == all_threads.head) - return; + + if (pid_of (lwp) == lwpid_of (lwp)) + { + if (debug_threads) + fprintf (stderr, "lkop: is last of process %s\n", + target_pid_to_str (entry->id)); + return 0; + } + + /* If we're killing a running inferior, make sure it is stopped + first, as PTRACE_KILL will not work otherwise. */ + if (!lwp->stopped) + send_sigstop (&lwp->head); do { - ptrace (PTRACE_KILL, pid_of (process), 0, 0); + ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0); /* Make sure it died. The loop is most likely unnecessary. */ - wstat = linux_wait_for_event (thread); - } while (WIFSTOPPED (wstat)); + pid = my_waitpid (lwpid_of (lwp), &wstat, __WALL); + } while (pid == lwpid_of (lwp)); + + remove_inferior (&all_lwps, &lwp->head); + remove_thread (thread); + + return 0; } -static void -linux_kill (void) +static int +linux_kill (int pid) { - struct thread_info *thread = (struct thread_info *) all_threads.head; struct process_info *process; + struct lwp_info *lwp; + struct thread_info *thread; int wstat; + int lwpid; - if (thread == NULL) - return; + process = + (struct process_info *) find_inferior_id (&all_processes, pid_to_ptid (pid)); - for_each_inferior (&all_threads, linux_kill_one_process); + if (process == NULL) + return -1; + + find_inferior (&all_threads, linux_kill_one_lwp, &pid); - /* See the comment in linux_kill_one_process. We did not kill the first + /* See the comment in linux_kill_one_lwp. We did not kill the first thread in the list, so do so now. */ - process = get_thread_process (thread); + lwp = find_lwp_pid (pid_to_ptid (pid)); + thread = get_lwp_thread (lwp); + + if (debug_threads) + fprintf (stderr, "lk_1: killing lwp %s, for pid: %d\n", + target_pid_to_str (lwp->head.id), pid); + + /* If we're killing a running inferior, make sure it is stopped + first, as PTRACE_KILL will not work otherwise. */ + if (!lwp->stopped) + send_sigstop (&lwp->head); + do { - ptrace (PTRACE_KILL, pid_of (process), 0, 0); + ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0); /* Make sure it died. The loop is most likely unnecessary. */ - wstat = linux_wait_for_event (thread); - } while (WIFSTOPPED (wstat)); + lwpid = my_waitpid (lwpid_of (lwp), &wstat, __WALL); + } while (lwpid == lwpid_of (lwp)); - clear_inferiors (); - free (all_processes.head); - all_processes.head = all_processes.tail = NULL; + remove_inferior (&all_lwps, &lwp->head); + remove_thread (thread); + remove_inferior (&all_processes, &process->head); + + return 0; } -static void -linux_detach_one_process (struct inferior_list_entry *entry) +static int +linux_detach_one_lwp (struct inferior_list_entry *entry, void *args) { struct thread_info *thread = (struct thread_info *) entry; - struct process_info *process = get_thread_process (thread); + struct lwp_info *lp = get_thread_lwp (thread); + int pid = * (int *) args; + + if (ptid_get_pid (entry->id) != pid) + return 0; - /* Make sure the process isn't stopped at a breakpoint that's + /* If we're detaching from a running inferior, make sure it is + stopped first, as PTRACE_DETACH will not work otherwise. */ + if (!lp->stopped) + { + send_sigstop (&lp->head); + wait_for_sigstop (&lp->head); + } + + /* Make sure the LWP isn't stopped at a breakpoint that's no longer there. */ - check_removed_breakpoint (process); + check_removed_breakpoint (lp); - /* If this process is stopped but is expecting a SIGSTOP, then make + /* If this lwp is stopped but is expecting a SIGSTOP, then make sure we take care of that now. This isn't absolutely guaranteed to collect the SIGSTOP, but is fairly likely to. */ - if (process->stop_expected) + if (lp->stop_expected) { + int wstat; /* Clear stop_expected, so that the SIGSTOP will be reported. */ - process->stop_expected = 0; - if (process->stopped) - linux_resume_one_process (&process->head, 0, 0, NULL); - linux_wait_for_event (thread); + lp->stop_expected = 0; + if (lp->stopped) + linux_resume_one_lwp (&lp->head, 0, 0, NULL); + linux_wait_for_event (lp->head.id, &wstat, __WALL); } - /* Flush any pending changes to the process's registers. */ + /* Flush any pending changes to the lwp's registers. */ regcache_invalidate_one ((struct inferior_list_entry *) - get_process_thread (process)); + get_lwp_thread (lp)); /* Finally, let it resume. */ - ptrace (PTRACE_DETACH, pid_of (process), 0, 0); + ptrace (PTRACE_DETACH, lwpid_of (lp), 0, 0); + + /* Get rid of it. */ + remove_inferior (&all_lwps, &lp->head); + remove_thread (thread); + + return 0; +} + +static int +any_thread_of (struct inferior_list_entry *entry, void *args) +{ + int *pid_p = args; + + if (ptid_get_pid (entry->id) == *pid_p) + return 1; + + return 0; } static int -linux_detach (void) +linux_detach (int pid) { + struct process_info *process; + + process = + (struct process_info *) find_inferior_id (&all_processes, + pid_to_ptid (pid)); + + if (process == NULL) + return -1; + + current_inferior = + (struct thread_info *) find_inferior (&all_threads, any_thread_of, &pid); + delete_all_breakpoints (); - for_each_inferior (&all_threads, linux_detach_one_process); - clear_inferiors (); - free (all_processes.head); - all_processes.head = all_processes.tail = NULL; + find_inferior (&all_threads, linux_detach_one_lwp, &pid); + + remove_inferior (&all_processes, &process->head); return 0; } static void -linux_join (void) +linux_join (int pid) { - extern unsigned long signal_pid; int status, ret; + struct process_info *process; + process = (struct process_info *) find_inferior_id (&all_processes, + pid_to_ptid (pid)); + + if (!process) + return; /* weird */ + + /* If we are attached, then we can exit. Otherwise, we need to hang + around doing nothing, until the child is gone. */ + if (process->attached) + return; + do { - ret = waitpid (signal_pid, &status, 0); + ret = my_waitpid (pid, &status, 0); if (WIFEXITED (status) || WIFSIGNALED (status)) break; } while (ret != -1 || errno != ECHILD); @@ -469,19 +780,24 @@ linux_join (void) /* Return nonzero if the given thread is still alive. */ static int -linux_thread_alive (unsigned long lwpid) +linux_thread_alive (ptid_t ptid) { - if (find_inferior_id (&all_threads, lwpid) != NULL) - return 1; + struct lwp_info *lwp = find_lwp_pid (ptid); + + /* We assume we always know if a thread exits. If a whole process + exited but we still haven't been able to report it to GDB, we'll + hold on to the last lwp of the dead process. */ + if (lwp != NULL) + return !lwp->dead; else return 0; } -/* Return nonzero if this process stopped at a breakpoint which +/* Return nonzero if this lwp stopped at a breakpoint which no longer appears to be inserted. Also adjust the PC appropriately to resume where the breakpoint used to be. */ static int -check_removed_breakpoint (struct process_info *event_child) +check_removed_breakpoint (struct lwp_info *event_child) { CORE_ADDR stop_pc; struct thread_info *saved_inferior; @@ -490,11 +806,11 @@ check_removed_breakpoint (struct process_info *event_child) return 0; if (debug_threads) - fprintf (stderr, "Checking for breakpoint in process %ld.\n", - event_child->lwpid); + fprintf (stderr, "Checking for breakpoint in lwp %ld.\n", + lwpid_of (event_child)); saved_inferior = current_inferior; - current_inferior = get_process_thread (event_child); + current_inferior = get_lwp_thread (event_child); stop_pc = get_stop_pc (); @@ -539,89 +855,213 @@ check_removed_breakpoint (struct process_info *event_child) return 1; } -/* Return 1 if this process has an interesting status pending. This function - may silently resume an inferior process. */ +/* Return 1 if this lwp has an interesting status pending. This function + may silently resume an inferior lwp. */ static int -status_pending_p (struct inferior_list_entry *entry, void *dummy) +status_pending_p (struct inferior_list_entry *entry, void *arg) { - struct process_info *process = (struct process_info *) entry; + struct lwp_info *lwp = (struct lwp_info *) entry; + ptid_t ptid = * (ptid_t *) arg; - if (process->status_pending_p) - if (check_removed_breakpoint (process)) + /* Check if we're only interested in events from a specific process + or its lwps. */ + if (!ptid_equal (minus_one_ptid, ptid) + && ptid_get_pid (ptid) != ptid_get_pid (lwp->head.id)) + return 0; + + if (lwp->status_pending_p && !lwp->suspended) + if (check_removed_breakpoint (lwp)) { /* This thread was stopped at a breakpoint, and the breakpoint is now gone. We were told to continue (or step...) all threads, so GDB isn't trying to single-step past this breakpoint. So instead of reporting the old SIGTRAP, pretend we got to the breakpoint just after it was removed instead of just - before; resume the process. */ - linux_resume_one_process (&process->head, 0, 0, NULL); + before; resume the lwp. */ + linux_resume_one_lwp (&lwp->head, 0, 0, NULL); return 0; } - return process->status_pending_p; + return (lwp->status_pending_p && !lwp->suspended); } -static void -linux_wait_for_process (struct process_info **childp, int *wstatp) +static int +same_lwp (struct inferior_list_entry *entry, void *data) { - int ret; - int to_wait_for = -1; + ptid_t ptid = *(ptid_t *) data; + int lwp; - if (*childp != NULL) - to_wait_for = (*childp)->lwpid; + if (ptid_get_lwp (ptid) != 0) + lwp = ptid_get_lwp (ptid); + else + lwp = ptid_get_pid (ptid); -retry: - while (1) + if (ptid_get_lwp (entry->id) == lwp) + return 1; + + return 0; +} + +struct lwp_info * +find_lwp_pid (ptid_t ptid) +{ + return (struct lwp_info*) find_inferior (&all_lwps, same_lwp, &ptid); +} + +/* Wrapper function for waitpid which handles EINTR, and emulates + __WALL for systems where that is not available. */ + +static int +my_waitpid (int pid, int *status, int flags) +{ + int ret, out_errno; + + if (debug_threads) + fprintf (stderr, "my_waitpid (%d, 0x%x)\n", pid, flags); + + if (flags & __WALL) { - ret = waitpid (to_wait_for, wstatp, WNOHANG); + sigset_t mask; + sigset_t org_mask; + sigset_t suspend_mask; - if (ret == -1) - { - if (errno != ECHILD) - perror_with_name ("waitpid"); - } - else if (ret > 0) - break; + int org_flags = flags; + int wnohang = (flags & WNOHANG) != 0; + flags &= ~__WALL; + flags |= WNOHANG; + flags &= ~__WCLONE; + + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); - ret = waitpid (to_wait_for, wstatp, WNOHANG | __WCLONE); + sigprocmask (SIG_BLOCK, &mask, &org_mask); - if (ret == -1) + /* Any signal unblocks the sigsuspend below. */ + sigemptyset (&suspend_mask); + + while (1) { - if (errno != ECHILD) - perror_with_name ("waitpid (WCLONE)"); + do + { + errno = 0; + ret = waitpid (pid, status, flags); + } + while (ret == -1 && errno == EINTR); + out_errno = errno; + + if (ret == -1) + { + if (out_errno != ECHILD) + break; + + /* We know there's no child of this flavour, recurse + with the right type. */ + org_flags &= ~__WALL; + org_flags |= (flags & __WCLONE) ? 0 : __WCLONE; + ret = my_waitpid (pid, status, org_flags); + out_errno = errno; + break; + } + else if (ret > 0) + break; + + if (flags & __WCLONE) + { + if (wnohang) + break; + else + { + if (debug_threads) + fprintf (stderr, "blocking\n"); + + /* Block waiting for any signal. Ideally, we'd wait for + SIGCHLD and LinuxThreads' signals, but this avoids + knowing which signals those are. */ + sigsuspend (&suspend_mask); + } + } + + flags ^= __WCLONE; } - else if (ret > 0) - break; - usleep (1000); + sigprocmask (SIG_SETMASK, &org_mask, NULL); + } + else + { + do + ret = waitpid (pid, status, flags); + while (ret == -1 && errno == EINTR); + out_errno = errno; } + if (debug_threads) + fprintf (stderr, "my_waitpid (%d, 0x%x): status(%x), %d\n", + pid, flags, status ? *status : -1, ret); + + errno = out_errno; + return ret; +} + +static struct lwp_info * +linux_wait_for_lwp (ptid_t ptid, int *wstatp, int options) +{ + int ret; + int to_wait_for = -1; + struct lwp_info *child = NULL; + + if (debug_threads) + fprintf (stderr, "linux_wait_for_lwp: %s\n", target_pid_to_str (ptid)); + + if (ptid_equal (ptid, minus_one_ptid)) + to_wait_for = -1; /* any child */ + else if (is_lwpid (ptid)) + to_wait_for = ptid_get_lwp (ptid); /* this lwp only */ + else + to_wait_for = -1; /* a specific tgid, but this + is not possible. We wait + for everything, and cache + what we don't need now. */ + + options |= __WALL; + +retry: + + ret = my_waitpid (to_wait_for, wstatp, options); + if (ret == 0 || (ret == -1 && errno == ECHILD && (options & WNOHANG))) + return NULL; + else if (ret == -1) + perror_with_name ("waitpid"); + if (debug_threads && (!WIFSTOPPED (*wstatp) || (WSTOPSIG (*wstatp) != 32 && WSTOPSIG (*wstatp) != 33))) fprintf (stderr, "Got an event from %d (%x)\n", ret, *wstatp); - if (to_wait_for == -1) - *childp = (struct process_info *) find_inferior_id (&all_processes, ret); + child = find_lwp_pid (pid_to_ptid (ret)); - /* If we didn't find a process, one of two things presumably happened: - - A process we started and then detached from has exited. Ignore it. - - A process we are controlling has forked and the new child's stop + /* If we didn't find a lwp, one of two things presumably happened: + - A lwp we started and then detached from has exited. Ignore it. + - A lwp we are controlling has forked and the new child's stop was reported to us by the kernel. Save its PID. */ - if (*childp == NULL && WIFSTOPPED (*wstatp)) + if (child == NULL && WIFSTOPPED (*wstatp)) { add_pid_to_list (&stopped_pids, ret); + if (options & WNOHANG) + return NULL; + goto retry; + } + else if (child == NULL) + { + if (options & WNOHANG) + return NULL; goto retry; } - else if (*childp == NULL) - goto retry; - (*childp)->stopped = 1; - (*childp)->pending_is_breakpoint = 0; + child->stopped = 1; + child->pending_is_breakpoint = 0; - (*childp)->last_status = *wstatp; + child->last_status = *wstatp; /* Architecture-specific setup after inferior is running. This needs to happen after we have attached to the inferior @@ -640,117 +1080,184 @@ retry: && WIFSTOPPED (*wstatp)) { current_inferior = (struct thread_info *) - find_inferior_id (&all_threads, (*childp)->lwpid); + find_inferior_id (&all_threads, child->head.id); /* For testing only; i386_stop_pc prints out a diagnostic. */ if (the_low_target.get_pc != NULL) get_stop_pc (); } + + if (ptid_is_pid (ptid)) + { + if (pid_of (child) != ptid_get_pid (ptid)) + { + if (debug_threads) + fprintf (stderr, "LWP %ld got an event %06x, leaving pending.\n", + lwpid_of (child), *wstatp); + child->status_pending_p = 1; + child->status_pending = *wstatp; + if ((options & WNOHANG) == 0) + goto retry; + + child = NULL; + } + } + + return child; } static int -linux_wait_for_event (struct thread_info *child) +resume_stopped_lwps (struct inferior_list_entry *entry, void *arg) +{ + ptid_t ptid = * (ptid_t *) arg; + struct lwp_info *lwp = (struct lwp_info *) entry; + + if ((ptid_equal (ptid, minus_one_ptid) + || (ptid_is_pid (ptid) + && ptid_get_pid (ptid) == pid_of (lwp))) + && lwp->stopped + && !lwp->suspended) + { + if (debug_threads) + fprintf (stderr, "resuming stopped LWP %ld\n", lwpid_of (lwp)); + linux_resume_one_lwp (&lwp->head, 0, 0, NULL); + } + + return 0; +} + + +static int +linux_wait_for_event (ptid_t ptid, int *wstat, int options) { CORE_ADDR stop_pc; - struct process_info *event_child; - int wstat; + struct lwp_info *event_child = NULL; int bp_status; + struct lwp_info *requested_child = NULL; - /* Check for a process with a pending status. */ + /* Check for a lwp with a pending status. */ /* It is possible that the user changed the pending task's registers since it stopped. We correctly handle the change of PC if we hit a breakpoint (in check_removed_breakpoint); signals should be reported anyway. */ - if (child == NULL) + + if (ptid_equal (ptid, minus_one_ptid) + || ptid_equal (pid_to_ptid (ptid_get_pid (ptid)), ptid)) { - event_child = (struct process_info *) - find_inferior (&all_processes, status_pending_p, NULL); + event_child = (struct lwp_info *) + find_inferior (&all_lwps, status_pending_p, &ptid); if (debug_threads && event_child) - fprintf (stderr, "Got a pending child %ld\n", event_child->lwpid); + fprintf (stderr, "Got a pending child %ld\n", lwpid_of (event_child)); + + /* If we just handled a pending status, and the event was not + interesting to report to GDB, we will reach here again. This + time, we may find that there are no more interesting pending + statuses to handle, but, the previous iteration left all + threads stopped. Resume them now. + + Don't do this in non-stop mode, as that would resume threads + silently behind GDB's back --- e.g., just after starting a + new inferior, we could get here due to a spurious + target_wait(..., TARGET_WNOHANG) call. In that case, we + don't want to resume all stopped threads. In all-stop, there + should be no wnohang calls, so we always want to resume + threads. */ + if (!non_stop && !event_child) + find_inferior (&all_lwps, resume_stopped_lwps, &ptid); } else { - event_child = get_thread_process (child); - if (event_child->status_pending_p - && check_removed_breakpoint (event_child)) - event_child = NULL; + requested_child = find_lwp_pid (ptid); + if (requested_child->status_pending_p + && !check_removed_breakpoint (requested_child)) + event_child = requested_child; } if (event_child != NULL) { - if (event_child->status_pending_p) - { - if (debug_threads) - fprintf (stderr, "Got an event from pending child %ld (%04x)\n", - event_child->lwpid, event_child->status_pending); - wstat = event_child->status_pending; - event_child->status_pending_p = 0; - event_child->status_pending = 0; - current_inferior = get_process_thread (event_child); - return wstat; - } + if (debug_threads) + fprintf (stderr, "Got an event from pending child %ld (%04x)\n", + ptid_get_lwp (event_child->head.id), event_child->status_pending); + *wstat = event_child->status_pending; + event_child->status_pending_p = 0; + event_child->status_pending = 0; } - /* We only enter this loop if no process has a pending wait status. Thus - any action taken in response to a wait status inside this loop is - responding as soon as we detect the status, not after any pending - events. */ - while (1) - { - if (child == NULL) - event_child = NULL; - else - event_child = get_thread_process (child); + /* If no lwp of interested had a pending wait status, wait for + one. */ + if (!event_child) + event_child = linux_wait_for_lwp (ptid, wstat, options); - linux_wait_for_process (&event_child, &wstat); + { + if ((options & WNOHANG) && event_child == NULL) + return -1; if (event_child == NULL) error ("event from unknown child"); - current_inferior = (struct thread_info *) - find_inferior_id (&all_threads, event_child->lwpid); + current_inferior = get_lwp_thread (event_child); /* Check for thread exit. */ - if (! WIFSTOPPED (wstat)) + if (! WIFSTOPPED (*wstat)) { if (debug_threads) - fprintf (stderr, "LWP %ld exiting\n", event_child->head.id); + fprintf (stderr, "LWP %ld exiting\n", + ptid_get_lwp (event_child->head.id)); /* If the last thread is exiting, just return. */ - if (all_threads.head == all_threads.tail) - return wstat; - + if (is_last_thread_of_process (current_inferior)) + { + if (debug_threads) + fprintf (stderr, "LWP %ld is last lwp of process\n", + ptid_get_lwp (event_child->head.id)); + return 0; + } +#if 0 dead_thread_notify (thread_id_to_gdb_id (event_child->lwpid)); +#endif - remove_inferior (&all_processes, &event_child->head); + remove_inferior (&all_lwps, &event_child->head); free (event_child); remove_thread (current_inferior); - current_inferior = (struct thread_info *) all_threads.head; + + if (!non_stop) + { + current_inferior = (struct thread_info *) all_threads.head; + if (debug_threads) + fprintf (stderr, "Current inferior is now %ld\n", + lwpid_of (get_thread_lwp (current_inferior))); + } + else + { + current_inferior = NULL; + if (debug_threads) + fprintf (stderr, "Current inferior is now <NULL>\n"); + } /* If we were waiting for this particular child to do something... well, it did something. */ - if (child != NULL) - return wstat; + if (requested_child != NULL) + return 0; /* Wait for a more interesting event. */ - continue; + return -1; } - if (WIFSTOPPED (wstat) - && WSTOPSIG (wstat) == SIGSTOP + if (WIFSTOPPED (*wstat) + && WSTOPSIG (*wstat) == SIGSTOP && event_child->stop_expected) { if (debug_threads) fprintf (stderr, "Expected stop.\n"); event_child->stop_expected = 0; - linux_resume_one_process (&event_child->head, - event_child->stepping, 0, NULL); - continue; + linux_resume_one_lwp (&event_child->head, + event_child->stepping, 0, NULL); + return -1; } - if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP - && wstat >> 16 != 0) + if (WIFSTOPPED (*wstat) && WSTOPSIG (*wstat) == SIGTRAP + && *wstat >> 16 != 0) { - handle_extended_wait (event_child, wstat); - continue; + if (handle_extended_wait (event_child, *wstat)) + return -1; } /* If GDB is not interested in this signal, don't stop other @@ -762,47 +1269,49 @@ linux_wait_for_event (struct thread_info *child) special handling to skip the signal handler. */ /* FIXME drow/2002-06-09: Get signal numbers from the inferior's thread library? */ - if (WIFSTOPPED (wstat) + if (WIFSTOPPED (*wstat) && !event_child->stepping && ( #ifdef USE_THREAD_DB - (thread_db_active && (WSTOPSIG (wstat) == __SIGRTMIN - || WSTOPSIG (wstat) == __SIGRTMIN + 1)) + (current_process ()->private->thread_db_active + && (WSTOPSIG (*wstat) == __SIGRTMIN + || WSTOPSIG (*wstat) == __SIGRTMIN + 1)) || #endif - (pass_signals[target_signal_from_host (WSTOPSIG (wstat))] - && (WSTOPSIG (wstat) != SIGSTOP || !stopping_threads)))) + (pass_signals[target_signal_from_host (WSTOPSIG (*wstat))] + && (WSTOPSIG (*wstat) != SIGSTOP || !stopping_threads)))) { siginfo_t info, *info_p; if (debug_threads) fprintf (stderr, "Ignored signal %d for LWP %ld.\n", - WSTOPSIG (wstat), event_child->head.id); + WSTOPSIG (*wstat), ptid_get_lwp (event_child->head.id)); - if (ptrace (PTRACE_GETSIGINFO, event_child->lwpid, 0, &info) == 0) + if (ptrace (PTRACE_GETSIGINFO, ptid_get_lwp (event_child->head.id), 0, &info) == 0) info_p = &info; else info_p = NULL; - linux_resume_one_process (&event_child->head, - event_child->stepping, - WSTOPSIG (wstat), info_p); - continue; + + linux_resume_one_lwp (&event_child->head, + event_child->stepping, + WSTOPSIG (*wstat), info_p); + return -1; } /* If this event was not handled above, and is not a SIGTRAP, report it. */ - if (!WIFSTOPPED (wstat) || WSTOPSIG (wstat) != SIGTRAP) - return wstat; + if (!WIFSTOPPED (*wstat) || WSTOPSIG (*wstat) != SIGTRAP) + return 0; /* If this target does not support breakpoints, we simply report the SIGTRAP; it's of no concern to us. */ if (the_low_target.get_pc == NULL) - return wstat; + return 0; stop_pc = get_stop_pc (); /* bp_reinsert will only be set if we were single-stepping. - Notice that we will resume the process after hitting + Notice that we will resume the lwp after hitting a gdbserver breakpoint; single-stepping to/over one is not supported (yet). */ if (event_child->bp_reinsert != 0) @@ -813,8 +1322,8 @@ linux_wait_for_event (struct thread_info *child) event_child->bp_reinsert = 0; /* Clear the single-stepping flag and SIGTRAP as we resume. */ - linux_resume_one_process (&event_child->head, 0, 0, NULL); - continue; + linux_resume_one_lwp (&event_child->head, 0, 0, NULL); + return -1; } bp_status = check_breakpoints (stop_pc); @@ -847,24 +1356,29 @@ linux_wait_for_event (struct thread_info *child) Otherwise, call the target function to figure out where we need our temporary breakpoint, create it, and continue executing this - process. */ + lwp. */ + + /* NOTE: we're lifting breakpoints in non-stop mode. This + is currently only used for thread event breakpoints, so + it isn't that bad as long as we have PTRACE_EVENT_CLONE + events. */ if (bp_status == 2) /* No need to reinsert. */ - linux_resume_one_process (&event_child->head, 0, 0, NULL); + linux_resume_one_lwp (&event_child->head, 0, 0, NULL); else if (the_low_target.breakpoint_reinsert_addr == NULL) { event_child->bp_reinsert = stop_pc; uninsert_breakpoint (stop_pc); - linux_resume_one_process (&event_child->head, 1, 0, NULL); + linux_resume_one_lwp (&event_child->head, 1, 0, NULL); } else { reinsert_breakpoint_by_bp (stop_pc, (*the_low_target.breakpoint_reinsert_addr) ()); - linux_resume_one_process (&event_child->head, 0, 0, NULL); + linux_resume_one_lwp (&event_child->head, 0, 0, NULL); } - continue; + return -1; } if (debug_threads) @@ -882,11 +1396,11 @@ linux_wait_for_event (struct thread_info *child) if (event_child->stepping) { event_child->stepping = 0; - return wstat; + return 0; } /* A SIGTRAP that we can't explain. It may have been a breakpoint. - Check if it is a breakpoint, and if so mark the process information + Check if it is a breakpoint, and if so mark the lwp information accordingly. This will handle both the necessary fiddling with the PC on decr_pc_after_break targets and suppressing extra threads hitting a breakpoint if two hit it at once and then GDB removes it @@ -899,7 +1413,7 @@ linux_wait_for_event (struct thread_info *child) event_child->pending_stop_pc = stop_pc; } - return wstat; + return 0; } /* NOTREACHED */ @@ -908,40 +1422,59 @@ linux_wait_for_event (struct thread_info *child) /* Wait for process, returns status. */ -static unsigned char -linux_wait (char *status) +static ptid_t +linux_wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus, int target_options) { int w; - struct thread_info *child = NULL; + struct thread_info *thread = NULL; + struct lwp_info *lwp = NULL; + int options; + + /* Translate generic target options into linux options. */ + options = __WALL; + if (target_options & TARGET_WNOHANG) + options |= WNOHANG; + + ourstatus->kind = TARGET_WAITKIND_IGNORE; -retry: /* If we were only supposed to resume one thread, only wait for that thread - if it's still alive. If it died, however - which can happen if we're coming from the thread death case below - then we need to make sure we restart the other threads. We could pick a thread at random or restart all; restarting all is less arbitrary. */ - if (cont_thread != 0 && cont_thread != -1) + /* TODO: Check if the incoming PTID is requesting an event for an + lwp of another process? */ + /* Don't do this in non-stop. */ + if (!non_stop + && !ptid_equal (cont_thread, null_ptid) + && !ptid_equal (cont_thread, minus_one_ptid)) { - child = (struct thread_info *) find_inferior_id (&all_threads, - cont_thread); + thread = (struct thread_info *) find_inferior_id (&all_threads, + cont_thread); /* No stepping, no signal - unless one is pending already, of course. */ - if (child == NULL) + if (thread == NULL) { struct thread_resume resume_info; - resume_info.thread = -1; - resume_info.step = resume_info.sig = resume_info.leave_stopped = 0; - linux_resume (&resume_info); + resume_info.thread = minus_one_ptid; + resume_info.kind = rk_continue; + resume_info.sig = 0; + linux_resume (&resume_info, 1); } + else + ptid = cont_thread; } - w = linux_wait_for_event (child); - stop_all_processes (); + if (linux_wait_for_event (ptid, &w, options) != 0) + return minus_one_ptid; + + if (!non_stop) + stop_all_lwps (); if (must_set_ptrace_flags) { - ptrace (PTRACE_SETOPTIONS, inferior_pid, 0, PTRACE_O_TRACECLONE); + linux_enable_event_reporting (inferior_lwpid); must_set_ptrace_flags = 0; } @@ -957,35 +1490,137 @@ retry: Report the exit status of the last thread to exit. This matches LinuxThreads' behavior. */ - if (all_threads.head == all_threads.tail) + lwp = get_thread_lwp (current_inferior); + + /* Now that we've selected our final event LWP, cancel any + breakpoints in other LWPs that have hit a GDB breakpoint. See + the comment in cancel_breakpoints_callback to find out why. */ + if (!non_stop) + find_inferior (&all_lwps, cancel_breakpoints_callback, lwp); + + if (is_last_thread_of_process (current_inferior)) { - if (WIFEXITED (w)) - { - fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); - *status = 'W'; - clear_inferiors (); - free (all_processes.head); - all_processes.head = all_processes.tail = NULL; - return WEXITSTATUS (w); - } - else if (!WIFSTOPPED (w)) + if (WIFEXITED (w) || WIFSIGNALED (w)) { - fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); - *status = 'X'; - clear_inferiors (); - free (all_processes.head); - all_processes.head = all_processes.tail = NULL; - return target_signal_from_host (WTERMSIG (w)); + struct process_info *process; + int pid; + + pid = pid_of (lwp); + + process = (struct process_info *) + find_inferior_id (&all_processes, pid_to_ptid (pid)); + + remove_inferior (&all_lwps, &lwp->head); + remove_thread (current_inferior); + remove_inferior (&all_processes, &process->head); + + current_inferior = NULL; + + if (WIFEXITED (w)) + { + ourstatus->kind = TARGET_WAITKIND_EXITED; + ourstatus->value.integer = WEXITSTATUS (w); + + if (debug_threads) + fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); + + return pid_to_ptid (pid); + } + else + { + ourstatus->kind = TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig = target_signal_from_host (WTERMSIG (w)); + + if (debug_threads) + fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); + + return pid_to_ptid (pid); + } } } else { if (!WIFSTOPPED (w)) - goto retry; + return lwp->head.id; + } + + if (lwp->waitstatus.kind != TARGET_WAITKIND_IGNORE) + { + *ourstatus = lwp->waitstatus; + lwp->waitstatus.kind = TARGET_WAITKIND_IGNORE; + } + else + { + ourstatus->kind = TARGET_WAITKIND_STOPPED; + ourstatus->value.sig = target_signal_from_host (WSTOPSIG (w)); + } + + if (debug_threads) + fprintf (stderr, "linux_wait ret = %s, %d, %d\n", + target_pid_to_str (lwp->head.id), + ourstatus->kind, + ourstatus->value.sig); + + return lwp->head.id; +} + +/* Get rid of any pending event in the pipe. */ +static void +async_file_flush (void) +{ + int ret; + char buf; + + do + ret = read (linux_event_pipe[0], &buf, 1); + while (ret == 0 || (ret == -1 && errno == EINTR)); +} + +/* Put something in the pipe, so the event loop wakes up. */ +static void +async_file_mark (void) +{ + int ret; + + async_file_flush (); + + do + ret = write (linux_event_pipe[1], "+", 1); + while (ret == 0 || (ret == -1 && errno == EINTR)); +} + +/* */ +static ptid_t +linux_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int target_options) +{ + ptid_t event_ptid; + + if (debug_threads) + fprintf (stderr, "linux_wait: [%s]\n", target_pid_to_str (ptid)); + + /* Flush the async file first. We can't do it below, because that + would be racy. A SIGCHLD may arrive any time after we get out of + my_waitpid, which marks the async file. */ + if (target_is_async_p ()) + async_file_flush (); + + do + { + event_ptid = linux_wait_1 (ptid, ourstatus, target_options); + } while ((target_options & TARGET_WNOHANG) == 0 + && ourstatus->kind == TARGET_WAITKIND_IGNORE); + + /* If we requested any event, and something came out, assume there + may be more. If we requested a specific lwp or process, also + assume there may be more. */ + if (target_is_async_p ()) + { + if (!ptid_equal (ptid, minus_one_ptid) + || ourstatus->kind != TARGET_WAITKIND_IGNORE) + async_file_mark (); } - *status = 'T'; - return target_signal_from_host (WSTOPSIG (w)); + return event_ptid; } /* Send a signal to an LWP. For LinuxThreads, kill is enough; however, if @@ -1015,126 +1650,159 @@ kill_lwp (unsigned long lwpid, int signo) static void send_sigstop (struct inferior_list_entry *entry) { - struct process_info *process = (struct process_info *) entry; + int lwpid; + struct lwp_info *lwp; + + lwp = (struct lwp_info *) entry; - if (process->stopped) + if (lwp->stopped) return; - /* If we already have a pending stop signal for this process, don't + lwpid = lwpid_of (lwp); + + /* If we already have a pending stop signal for this lwp, don't send another. */ - if (process->stop_expected) + if (lwp->stop_expected) { if (debug_threads) - fprintf (stderr, "Have pending sigstop for process %ld\n", - process->lwpid); + fprintf (stderr, "Have pending sigstop for lwp %d\n", + lwpid); /* We clear the stop_expected flag so that wait_for_sigstop will receive the SIGSTOP event (instead of silently resuming and waiting again). It'll be reset below. */ - process->stop_expected = 0; + lwp->stop_expected = 0; return; } if (debug_threads) - fprintf (stderr, "Sending sigstop to process %ld\n", process->head.id); + fprintf (stderr, "Sending sigstop to lwp %d\n", lwpid); - kill_lwp (process->head.id, SIGSTOP); + kill_lwp (lwpid, SIGSTOP); } static void wait_for_sigstop (struct inferior_list_entry *entry) { - struct process_info *process = (struct process_info *) entry; - struct thread_info *saved_inferior, *thread; + struct lwp_info *lwp = (struct lwp_info *) entry; + struct thread_info *saved_inferior; int wstat; - unsigned long saved_tid; + ptid_t saved_tid; + ptid_t ptid; - if (process->stopped) + if (lwp->stopped) return; saved_inferior = current_inferior; saved_tid = ((struct inferior_list_entry *) saved_inferior)->id; - thread = (struct thread_info *) find_inferior_id (&all_threads, - process->lwpid); - wstat = linux_wait_for_event (thread); + + ptid = lwp->head.id; + + stopping_threads = 1; + linux_wait_for_lwp (ptid, &wstat, __WALL); + stopping_threads = 0; /* If we stopped with a non-SIGSTOP signal, save it for later - and record the pending SIGSTOP. If the process exited, just + and record the pending SIGSTOP. If the lwp exited, just return. */ if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) != SIGSTOP) { if (debug_threads) fprintf (stderr, "LWP %ld stopped with non-sigstop status %06x\n", - process->lwpid, wstat); - process->status_pending_p = 1; - process->status_pending = wstat; - process->stop_expected = 1; + lwpid_of (lwp), wstat); + lwp->status_pending_p = 1; + lwp->status_pending = wstat; + lwp->stop_expected = 1; + } + else if (!WIFSTOPPED (wstat)) + { + if (debug_threads) + fprintf (stderr, "Process %ld exited while stopping LWPs\n", + ptid_get_lwp (lwp->head.id)); + + /* Leave this status pending for the next time we're able to + report it. In the mean time, we'll report this lwp as + dead to GDB, so GDB doesn't try to read registers and + memory from it. */ + lwp->dead = 1; + lwp->status_pending_p = 1; + lwp->status_pending = wstat; + lwp->stopped = 1; /* prevent trying to stop it again. */ + lwp->stop_expected = 0; /* No stop is expected, the lwp is + dead. */ } - if (linux_thread_alive (saved_tid)) + if (non_stop) + /* We can't change the current inferior behind GDB's back, + otherwise, a subsequent command may apply to the wrong process. + Actually, we have to take care of this in all-stop + + multi-process too. FIXME. */ current_inferior = saved_inferior; else { - if (debug_threads) - fprintf (stderr, "Previously current thread died.\n"); + if (linux_thread_alive (saved_tid)) + current_inferior = saved_inferior; + else + { + if (debug_threads) + fprintf (stderr, "Previously current thread died.\n"); - /* Set a valid thread as current. */ - set_desired_inferior (0); + /* Set a valid thread as current. */ + set_desired_inferior (0); + } } } static void -stop_all_processes (void) +stop_all_lwps (void) { - stopping_threads = 1; - for_each_inferior (&all_processes, send_sigstop); - for_each_inferior (&all_processes, wait_for_sigstop); - stopping_threads = 0; + for_each_inferior (&all_lwps, send_sigstop); + for_each_inferior (&all_lwps, wait_for_sigstop); } -/* Resume execution of the inferior process. +/* Resume execution of the inferior lwp. If STEP is nonzero, single-step it. If SIGNAL is nonzero, give it that signal. */ static void -linux_resume_one_process (struct inferior_list_entry *entry, - int step, int signal, siginfo_t *info) +linux_resume_one_lwp (struct inferior_list_entry *entry, + int step, int signal, siginfo_t *info) { - struct process_info *process = (struct process_info *) entry; + struct lwp_info *lwp = (struct lwp_info *) entry; struct thread_info *saved_inferior; - if (process->stopped == 0) + if (lwp->stopped == 0) return; /* If we have pending signals or status, and a new signal, enqueue the signal. Also enqueue the signal if we are waiting to reinsert a breakpoint; it will be picked up again below. */ if (signal != 0 - && (process->status_pending_p || process->pending_signals != NULL - || process->bp_reinsert != 0)) + && (lwp->status_pending_p || lwp->pending_signals != NULL + || lwp->bp_reinsert != 0)) { struct pending_signals *p_sig; p_sig = malloc (sizeof (*p_sig)); - p_sig->prev = process->pending_signals; + p_sig->prev = lwp->pending_signals; p_sig->signal = signal; if (info == NULL) memset (&p_sig->info, 0, sizeof (siginfo_t)); else memcpy (&p_sig->info, info, sizeof (siginfo_t)); - process->pending_signals = p_sig; + lwp->pending_signals = p_sig; } - if (process->status_pending_p && !check_removed_breakpoint (process)) + if (lwp->status_pending_p && !check_removed_breakpoint (lwp)) return; saved_inferior = current_inferior; - current_inferior = get_process_thread (process); + current_inferior = get_lwp_thread (lwp); if (debug_threads) - fprintf (stderr, "Resuming process %ld (%s, signal %d, stop %s)\n", inferior_pid, + fprintf (stderr, "Resuming lwp %ld (%s, signal %d, stop %s)\n", inferior_lwpid, step ? "step" : "continue", signal, - process->stop_expected ? "expected" : "not expected"); + lwp->stop_expected ? "expected" : "not expected"); /* This bit needs some thinking about. If we get a signal that we must report while a single-step reinsert is still pending, @@ -1143,13 +1811,13 @@ linux_resume_one_process (struct inferior_list_entry *entry, the reinsert happened right away and not lose any signals. Making this stack would also shrink the window in which breakpoints are - uninserted (see comment in linux_wait_for_process) but not enough for + uninserted (see comment in linux_wait_for_lwp) but not enough for complete correctness, so it won't solve that problem. It may be worthwhile just to solve this one, however. */ - if (process->bp_reinsert != 0) + if (lwp->bp_reinsert != 0) { if (debug_threads) - fprintf (stderr, " pending reinsert at %08lx", (long)process->bp_reinsert); + fprintf (stderr, " pending reinsert at %08lx", (long)lwp->bp_reinsert); if (step == 0) fprintf (stderr, "BAD - reinserting but not stepping.\n"); step = 1; @@ -1158,7 +1826,7 @@ linux_resume_one_process (struct inferior_list_entry *entry, signal = 0; } - check_removed_breakpoint (process); + check_removed_breakpoint (lwp); if (debug_threads && the_low_target.get_pc != NULL) { @@ -1168,28 +1836,28 @@ linux_resume_one_process (struct inferior_list_entry *entry, /* If we have pending signals, consume one unless we are trying to reinsert a breakpoint. */ - if (process->pending_signals != NULL && process->bp_reinsert == 0) + if (lwp->pending_signals != NULL && lwp->bp_reinsert == 0) { struct pending_signals **p_sig; - p_sig = &process->pending_signals; + p_sig = &lwp->pending_signals; while ((*p_sig)->prev != NULL) p_sig = &(*p_sig)->prev; signal = (*p_sig)->signal; if ((*p_sig)->info.si_signo != 0) - ptrace (PTRACE_SETSIGINFO, process->lwpid, 0, &(*p_sig)->info); + ptrace (PTRACE_SETSIGINFO, ptid_get_lwp (lwp->head.id), 0, &(*p_sig)->info); free (*p_sig); *p_sig = NULL; } regcache_invalidate_one ((struct inferior_list_entry *) - get_process_thread (process)); + get_lwp_thread (lwp)); errno = 0; - process->stopped = 0; - process->stepping = step; - ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, process->lwpid, 0, signal); + lwp->stopped = 0; + lwp->stepping = step; + ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, lwpid_of (lwp), 0, signal); current_inferior = saved_inferior; if (errno) @@ -1208,147 +1876,412 @@ linux_resume_one_process (struct inferior_list_entry *entry, } } -static struct thread_resume *resume_ptr; +struct resume_info +{ + struct thread_resume *request; + size_t n; +}; -/* This function is called once per thread. We look up the thread - in RESUME_PTR, and mark the thread with a pointer to the appropriate +/* This function is called once per thread. We look up the thread in + RESUME_INFO, and mark the thread with a pointer to the appropriate resume request. This algorithm is O(threads * resume elements), but resume elements is small (and will remain small at least until GDB supports thread suspension). */ -static void -linux_set_resume_request (struct inferior_list_entry *entry) +static int +linux_set_resume_request (struct inferior_list_entry *entry, void *arg) { - struct process_info *process; + struct lwp_info *lwp; struct thread_info *thread; int ndx; + struct resume_info *resume_info; thread = (struct thread_info *) entry; - process = get_thread_process (thread); + lwp = get_thread_lwp (thread); + resume_info = arg; - ndx = 0; - while (resume_ptr[ndx].thread != -1 && resume_ptr[ndx].thread != entry->id) - ndx++; + for (ndx = 0; ndx < resume_info->n; ndx++) + { + ptid_t ptid = resume_info->request[ndx].thread; + if (ptid_equal (ptid, minus_one_ptid) + || ptid_equal (ptid, entry->id) + || (ptid_is_pid (ptid) + && (ptid_get_pid (ptid) == pid_of (lwp))) + || (ptid_get_lwp (ptid) == -1 + && (ptid_get_pid (ptid) == pid_of (lwp)))) + { + lwp->resume = &resume_info->request[ndx]; + return 0; + } + } - process->resume = &resume_ptr[ndx]; + /* Don't touch. */ + lwp->resume = NULL; + + return 0; } -/* This function is called once per thread. We check the thread's resume - request, which will tell us whether to resume, step, or leave the thread - stopped; and what signal, if any, it should be sent. For threads which - we aren't explicitly told otherwise, we preserve the stepping flag; this - is used for stepping over gdbserver-placed breakpoints. */ +/* Set *FLAG_P if this lwp has an interesting status pending. */ +static int +resume_status_pending_p (struct inferior_list_entry *entry, void *flag_p) +{ + struct lwp_info *lwp = (struct lwp_info *) entry; -static void -linux_continue_one_thread (struct inferior_list_entry *entry) + /* LWPs which will not be resumed are not interesting, because + we might not wait for them next time through linux_wait. */ + if (lwp->resume == NULL) + return 0; + + if (lwp->dead) + { + /* If this thread is known to be dead, then the whole process it + belonged to has exited. Report it. */ + + /* TODO: See if the dead flag is really needed, or the + pending_status is enough. */ + if (!lwp->status_pending_p) + warning ("dead thread without a pending status?"); + + * (int *) flag_p = 1; + return 0; + } + + /* If this thread has a removed breakpoint, we won't have any + events to report later, so check now. check_removed_breakpoint + may clear status_pending_p. We avoid calling check_removed_breakpoint + for any thread that we are not otherwise going to resume - this + lets us preserve stopped status when two threads hit a breakpoint. + GDB removes the breakpoint to single-step a particular thread + past it, then re-inserts it and resumes all threads. We want + to report the second thread without resuming it in the interim. */ + if (lwp->status_pending_p && !lwp->suspended) + check_removed_breakpoint (lwp); + + if (lwp->status_pending_p) + * (int *) flag_p = 1; + + return 0; +} + +static int +cancel_breakpoint (struct lwp_info *lwp) { - struct process_info *process; - struct thread_info *thread; - int step; + /* Arrange for a breakpoint to be hit again later. We don't keep + the SIGTRAP status and don't forward the SIGTRAP signal to the + thread. Eventually we will resume this thread, and this + breakpoint will trap again. + + If we do not do this, then we run the risk that the user will + delete or disable the breakpoint, but the thread will have + already tripped on it. */ + + if (lwp->stopped + && !lwp->dead + && !lwp->stepping + && ((lwp->status_pending_p + && WIFSTOPPED (lwp->status_pending) + && WSTOPSIG (lwp->status_pending) == SIGTRAP) + || (!lwp->status_pending_p + && WIFSTOPPED (lwp->last_status) + && WSTOPSIG (lwp->last_status) == SIGTRAP))) + { + CORE_ADDR stop_pc; + struct thread_info *saved_inferior; - thread = (struct thread_info *) entry; - process = get_thread_process (thread); + /* If there's a breakpoint here, adjust the PC, so the + breakpoint is hit again when the thread is resumed. */ - if (process->resume->leave_stopped) - return; + if (debug_threads) + fprintf (stderr, "Checking for breakpoint in lwp %ld.\n", + lwpid_of (lwp)); - if (process->resume->thread == -1) - step = process->stepping || process->resume->step; - else - step = process->resume->step; + saved_inferior = current_inferior; + current_inferior = get_lwp_thread (lwp); + + stop_pc = (*the_low_target.get_pc) (); + stop_pc -= the_low_target.decr_pc_after_break; + + /* If there's a breakpoint there, back off the PC. */ + if (breakpoint_at (stop_pc) + || (*the_low_target.breakpoint_at) (stop_pc)) + { + if (the_low_target.set_pc != NULL) + { + if (debug_threads) + fprintf (stderr, "CB: breakpoint present, backing off PC.\n"); + (*the_low_target.set_pc) (stop_pc); + } + else + { + if (debug_threads) + fprintf (stderr, "CB: breakpoint present ignoring SIGTRAP.\n"); + } + + lwp->pending_is_breakpoint = 0; + lwp->status_pending_p = 0; + lwp->status_pending = 0; + lwp->last_status = W_STOPCODE (0); + + current_inferior = saved_inferior; + return 1; + } + + current_inferior = saved_inferior; + } - linux_resume_one_process (&process->head, step, process->resume->sig, NULL); + return 0; +} + +static int +cancel_breakpoints_callback (struct inferior_list_entry *entry, void *data) +{ + struct lwp_info *lp = (struct lwp_info *) entry; + struct lwp_info *event_lp = data; + + /* Leave the LWP that has been elected to receive a SIGTRAP alone. */ + if (lp == event_lp) + return 0; + + /* If a LWP other than the LWP that we're reporting an event for has + hit a GDB breakpoint (as opposed to some random trap signal), + then just arrange for it to hit it again later. We don't keep + the SIGTRAP status and don't forward the SIGTRAP signal to the + LWP. We will handle the current event, eventually we will resume + all LWPs, and this one will get its breakpoint trap again. + + If we do not do this, then we run the risk that the user will + delete or disable the breakpoint, but the LWP will have already + tripped on it. */ - process->resume = NULL; + cancel_breakpoint (lp); + return 0; } /* This function is called once per thread. We check the thread's resume request, which will tell us whether to resume, step, or leave the thread - stopped; and what signal, if any, it should be sent. We queue any needed - signals, since we won't actually resume. We already have a pending event - to report, so we don't need to preserve any step requests; they should - be re-issued if necessary. */ + stopped; and what signal, if any, it should be sent. -static void -linux_queue_one_thread (struct inferior_list_entry *entry) + For threads which we aren't explicitly told otherwise, we preserve + the stepping flag; this is used for stepping over gdbserver-placed + breakpoints. + + If pending_flags was set in any thread, we queue any needed + signals, since we won't actually resume. We already have a pending + event to report, so we don't need to preserve any step requests; + they should be re-issued if necessary. */ + +static int +linux_resume_one_thread (struct inferior_list_entry *entry, void *arg) { - struct process_info *process; + struct lwp_info *lwp; struct thread_info *thread; + int step; + int pending_flag = * (int *) arg; thread = (struct thread_info *) entry; - process = get_thread_process (thread); + lwp = get_thread_lwp (thread); - if (process->resume->leave_stopped) - return; + if (lwp->resume == NULL) + return 0; - /* If we have a new signal, enqueue the signal. */ - if (process->resume->sig != 0) + if (lwp->resume->kind == rk_stop) { - struct pending_signals *p_sig; - p_sig = malloc (sizeof (*p_sig)); - p_sig->prev = process->pending_signals; - p_sig->signal = process->resume->sig; - memset (&p_sig->info, 0, sizeof (siginfo_t)); + if (debug_threads) + fprintf (stderr, "suspending thread %s\n", + target_pid_to_str (entry->id)); + + /* Do this before waiting for sigstop, as the thread may + die. */ + /* TODO: how do we report that happening to GDB? GDB and the + user can get very confused if a thread exits without GDB + being aware of it. E.g, user tries to interrupts thread, but + GDB doesn't reply back, The simplest seem to be to implement + a thread exit stop reply. Note that returning a generic + error is not an option if we're handling a vCont. The reply + has already been sent at this point, and, the same vCont can + hold multiple resumptions -- to which would the error + apply? */ + lwp->resume = NULL; + + /* To simplify things, we're waiting for the lwp to stop here, + and pushing the stop reply to gdbserver's common code. An + alternative, would be to defer to linux_wait to do the wait, + and notice that an lwp with the "suspended" flag but + "!stopped", should leave any status pending, and report a + sig0 stop status. */ + + /* Do we have to distinguish internal stops from external stops, + due to the support for gdbserver breakpoints? If so, then + thread_info needs to gain a "running" property. */ + if (!lwp->stopped) + { + ptid_t ptid = entry->id; - /* If this is the same signal we were previously stopped by, - make sure to queue its siginfo. We can ignore the return - value of ptrace; if it fails, we'll skip - PTRACE_SETSIGINFO. */ - if (WIFSTOPPED (process->last_status) - && WSTOPSIG (process->last_status) == process->resume->sig) - ptrace (PTRACE_GETSIGINFO, process->lwpid, 0, &p_sig->info); + if (debug_threads) + fprintf (stderr, "running -> suspending %s\n", + target_pid_to_str (entry->id)); + + lwp->suspended = 1; + send_sigstop (&lwp->head); + wait_for_sigstop (&lwp->head); + if (!lwp->dead) + { + struct target_waitstatus status; + status.kind = TARGET_WAITKIND_STOPPED; + + /* Cancel internal breakpoints, otherwise, the user will + see a possibly invalid PC on decr_pc_after_break + archs, because GDB can't tell there's a breakpoint + there. As long as we do it, might as well cancel GDB + breakpoints too, although GDB will also adjust the PC + if we don't in that case. */ + + /* Cancel breakpoints, but leave out finished steps and + watchpoint hits. */ + if (cancel_breakpoint (lwp)) + /* The LWP hit a breakpoint while we tried to stop it, + and we backed off the PC. Report a SIG0. */ + status.value.sig = 0; + else if (lwp->stopped + && lwp->stepping + && ((!lwp->status_pending_p + && WIFSTOPPED (lwp->last_status) + && WSTOPSIG (lwp->last_status) == SIGTRAP) + || (lwp->status_pending_p + && WIFSTOPPED (lwp->status_pending) + && WSTOPSIG (lwp->status_pending) == SIGTRAP))) + { + /* The LWP finished a hardware single-step; report + the SIGTRAP to GDB. */ + lwp->pending_is_breakpoint = 0; + lwp->status_pending_p = 0; + lwp->status_pending = 0; + lwp->last_status = W_STOPCODE (SIGTRAP); + /* Report the finished single-step. When using + displaced stepping, GDB needs this to be able to + fixup the PC. */ + status.value.sig = TARGET_SIGNAL_TRAP; + } + else + /* Leave other signals pending. */ + status.value.sig = 0; + + /* Pass the stop reply back to GDB. */ + push_event (ptid, &status); + } + else + ; + } + else + { + if (debug_threads) + { + if (lwp->suspended) + fprintf (stderr, "already stopped/suspended %s\n", + target_pid_to_str (entry->id)); + else + fprintf (stderr, "already stopped/not suspended %s\n", + target_pid_to_str (entry->id)); + } - process->pending_signals = p_sig; + /* Make sure we leave the LWP suspended, so we don't try to + resume it without GDB telling us to. FIXME: The LWP may + have been stopped in an internal event that was not meant + to be notified back to GDB (e.g., gdbserver breakpoint), + so we should be reporting a stop event in that case + too. */ + lwp->suspended = 1; + } + return 0; } + else + lwp->suspended = 0; - process->resume = NULL; -} + /* If this thread which is about to be resumed has a pending status, + then don't resume any threads - we can just report the pending + status. Make sure to queue any signals that would otherwise be + sent. In all-stop mode, we do this decision based on if *any* + thread has a pending status. */ + if (non_stop) + resume_status_pending_p (&lwp->head, &pending_flag); -/* Set DUMMY if this process has an interesting status pending. */ -static int -resume_status_pending_p (struct inferior_list_entry *entry, void *flag_p) -{ - struct process_info *process = (struct process_info *) entry; + if (!pending_flag) + { + if (debug_threads) + fprintf (stderr, "resuming thread %s\n", + target_pid_to_str (entry->id)); - /* Processes which will not be resumed are not interesting, because - we might not wait for them next time through linux_wait. */ - if (process->resume->leave_stopped) - return 0; + if (ptid_equal (lwp->resume->thread, minus_one_ptid)) + step = lwp->stepping || (lwp->resume->kind == rk_step); + else + step = (lwp->resume->kind == rk_step); - /* If this thread has a removed breakpoint, we won't have any - events to report later, so check now. check_removed_breakpoint - may clear status_pending_p. We avoid calling check_removed_breakpoint - for any thread that we are not otherwise going to resume - this - lets us preserve stopped status when two threads hit a breakpoint. - GDB removes the breakpoint to single-step a particular thread - past it, then re-inserts it and resumes all threads. We want - to report the second thread without resuming it in the interim. */ - if (process->status_pending_p) - check_removed_breakpoint (process); + linux_resume_one_lwp (&lwp->head, step, lwp->resume->sig, NULL); + } + else + { + if (debug_threads) + fprintf (stderr, "leaving thread %s stopped\n", + target_pid_to_str (entry->id)); - if (process->status_pending_p) - * (int *) flag_p = 1; + /* If we have a new signal, enqueue the signal. */ + if (lwp->resume->sig != 0) + { + struct pending_signals *p_sig; + p_sig = malloc (sizeof (*p_sig)); + p_sig->prev = lwp->pending_signals; + p_sig->signal = lwp->resume->sig; + memset (&p_sig->info, 0, sizeof (siginfo_t)); + + /* If this is the same signal we were previously stopped by, + make sure to queue its siginfo. We can ignore the return + value of ptrace; if it fails, we'll skip + PTRACE_SETSIGINFO. */ + if (WIFSTOPPED (lwp->last_status) + && WSTOPSIG (lwp->last_status) == lwp->resume->sig) + ptrace (PTRACE_GETSIGINFO, ptid_get_lwp (lwp->head.id), 0, &p_sig->info); + + lwp->pending_signals = p_sig; + } + } + lwp->resume = NULL; return 0; } static void -linux_resume (struct thread_resume *resume_info) +linux_resume (struct thread_resume *resume_info, size_t n) { int pending_flag; + struct resume_info info = { resume_info, n }; - /* Yes, the use of a global here is rather ugly. */ - resume_ptr = resume_info; + if (debug_threads) + { + int idx; + fprintf (stderr, "linux_resume\n"); + for (idx = 0; idx < n; idx++) + { + fprintf (stderr, " thread(%s), kind(%d), sig(%d)\n", + target_pid_to_str (resume_info[idx].thread), + resume_info[idx].kind, + resume_info[idx].sig); + idx++; + } + } - for_each_inferior (&all_threads, linux_set_resume_request); + find_inferior (&all_threads, linux_set_resume_request, &info); /* If there is a thread which would otherwise be resumed, which has a pending status, then don't resume any threads - we can just report the pending status. Make sure to queue any signals that would otherwise be sent. */ + /* This is only used in all-stop mode however. In non-stop mode, + decide to leave pending based on the thread we're resuming itself + having a pending status already or not. */ pending_flag = 0; - find_inferior (&all_processes, resume_status_pending_p, &pending_flag); + if (!non_stop) + find_inferior (&all_lwps, resume_status_pending_p, &pending_flag); if (debug_threads) { @@ -1358,10 +2291,7 @@ linux_resume (struct thread_resume *resume_info) fprintf (stderr, "Resuming, no pending status\n"); } - if (pending_flag) - for_each_inferior (&all_threads, linux_queue_one_thread); - else - for_each_inferior (&all_threads, linux_continue_one_thread); + find_inferior (&all_threads, linux_resume_one_thread, &pending_flag); } #ifdef HAVE_LINUX_USRREGS @@ -1402,7 +2332,7 @@ fetch_register (int regno) { errno = 0; *(PTRACE_XFER_TYPE *) (buf + i) = - ptrace (PTRACE_PEEKUSER, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, 0); + ptrace (PTRACE_PEEKUSER, inferior_lwpid, (PTRACE_ARG3_TYPE) regaddr, 0); regaddr += sizeof (PTRACE_XFER_TYPE); if (errno != 0) { @@ -1424,7 +2354,7 @@ fetch_register (int regno) error_exit:; } -/* Fetch all registers, or just one, from the child process. */ +/* Fetch all registers, or just one, from the child lwp. */ static void usr_fetch_inferior_registers (int regno) { @@ -1470,13 +2400,13 @@ usr_store_inferior_registers (int regno) for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE)) { errno = 0; - ptrace (PTRACE_POKEUSER, inferior_pid, (PTRACE_ARG3_TYPE) regaddr, + ptrace (PTRACE_POKEUSER, inferior_lwpid, (PTRACE_ARG3_TYPE) regaddr, *(PTRACE_XFER_TYPE *) (buf + i)); if (errno != 0) { - /* At this point, ESRCH should mean the process is already gone, + /* At this point, ESRCH should mean the lwp is already gone, in which case we simply ignore attempts to change its registers. - See also the related comment in linux_resume_one_process. */ + See also the related comment in linux_resume_one_lwp. */ if (errno == ESRCH) return; @@ -1523,17 +2453,13 @@ regsets_fetch_inferior_registers () } buf = malloc (regset->size); -#ifndef __sparc__ - res = ptrace (regset->get_request, inferior_pid, 0, buf); -#else - res = ptrace (regset->get_request, inferior_pid, buf, 0); -#endif + res = ptrace (regset->get_request, inferior_lwpid, 0, buf); if (res < 0) { if (errno == EIO) { /* If we get EIO on a regset, do not try it again for - this process. */ + this lwp. */ disabled_regsets[regset - target_regsets] = 1; continue; } @@ -1541,7 +2467,7 @@ regsets_fetch_inferior_registers () { char s[256]; sprintf (s, "ptrace(regsets_fetch_inferior_registers) PID=%ld", - inferior_pid); + inferior_lwpid); perror (s); } } @@ -1580,11 +2506,7 @@ regsets_store_inferior_registers () /* First fill the buffer with the current register set contents, in case there are any items in the kernel's regset that are not in gdbserver's regcache. */ -#ifndef __sparc__ - res = ptrace (regset->get_request, inferior_pid, 0, buf); -#else - res = ptrace (regset->get_request, inferior_pid, buf, 0); -#endif + res = ptrace (regset->get_request, inferior_lwpid, 0, buf); if (res == 0) { @@ -1592,11 +2514,7 @@ regsets_store_inferior_registers () regset->fill_function (buf); /* Only now do we write the register set. */ -#ifndef __sparc__ - res = ptrace (regset->set_request, inferior_pid, 0, buf); -#else - res = ptrace (regset->set_request, inferior_pid, buf, 0); -#endif + res = ptrace (regset->set_request, inferior_lwpid, 0, buf); } if (res < 0) @@ -1604,15 +2522,16 @@ regsets_store_inferior_registers () if (errno == EIO) { /* If we get EIO on a regset, do not try it again for - this process. */ + this lwp. */ disabled_regsets[regset - target_regsets] = 1; continue; } else if (errno == ESRCH) { - /* At this point, ESRCH should mean the process is already gone, + /* At this point, ESRCH should mean the lwp is already gone, in which case we simply ignore attempts to change its registers. - See also the related comment in linux_resume_one_process. */ + See also the related comment in linux_resume_one_lwp. */ + free (buf); return 0; } else @@ -1684,7 +2603,7 @@ linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) { /* We could keep this file open and cache it - possibly one per thread. That requires some juggling, but is even faster. */ - sprintf (filename, "/proc/%ld/mem", inferior_pid); + sprintf (filename, "/proc/%ld/mem", inferior_lwpid); fd = open (filename, O_RDONLY | O_LARGEFILE); if (fd == -1) goto no_proc; @@ -1712,7 +2631,7 @@ linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) { errno = 0; - buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, 0); + buffer[i] = ptrace (PTRACE_PEEKTEXT, inferior_lwpid, (PTRACE_ARG3_TYPE) addr, 0); if (errno) return errno; } @@ -1747,13 +2666,13 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) /* Fill start and end extra bytes of buffer with existing memory data. */ - buffer[0] = ptrace (PTRACE_PEEKTEXT, inferior_pid, + buffer[0] = ptrace (PTRACE_PEEKTEXT, inferior_lwpid, (PTRACE_ARG3_TYPE) addr, 0); if (count > 1) { buffer[count - 1] - = ptrace (PTRACE_PEEKTEXT, inferior_pid, + = ptrace (PTRACE_PEEKTEXT, inferior_lwpid, (PTRACE_ARG3_TYPE) (addr + (count - 1) * sizeof (PTRACE_XFER_TYPE)), 0); @@ -1768,7 +2687,7 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE)) { errno = 0; - ptrace (PTRACE_POKETEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, buffer[i]); + ptrace (PTRACE_POKETEXT, inferior_lwpid, (PTRACE_ARG3_TYPE) addr, buffer[i]); if (errno) return errno; } @@ -1776,8 +2695,6 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) return 0; } -static int linux_supports_tracefork_flag; - /* Helper functions for linux_test_for_tracefork, called via clone (). */ static int @@ -1803,21 +2720,6 @@ linux_tracefork_child (void *arg) _exit (0); } -/* Wrapper function for waitpid which handles EINTR. */ - -static int -my_waitpid (int pid, int *status, int flags) -{ - int ret; - do - { - ret = waitpid (pid, status, flags); - } - while (ret == -1 && errno == EINTR); - - return ret; -} - /* Determine if PTRACE_O_TRACEFORK can be used to follow fork events. Make sure that we can enable the option, and that it had the desired effect. */ @@ -1914,10 +2816,15 @@ static void linux_look_up_symbols (void) { #ifdef USE_THREAD_DB - if (thread_db_active) + /* It would probably be cleaner now to move this whole function to + thread-db.c. */ + + struct process_info *proc = current_process (); + if (proc->private->thread_db_active) return; - thread_db_active = thread_db_init (!linux_supports_tracefork_flag); + proc->private->thread_db_active + = thread_db_init (!linux_supports_tracefork_flag); #endif } @@ -1926,12 +2833,15 @@ linux_request_interrupt (void) { extern unsigned long signal_pid; - if (cont_thread != 0 && cont_thread != -1) + if (!ptid_equal (cont_thread, null_ptid) + && !ptid_equal (cont_thread, minus_one_ptid)) { - struct process_info *process; + struct lwp_info *lwp; + int lwpid; - process = get_thread_process (current_inferior); - kill_lwp (process->lwpid, SIGINT); + lwp = get_thread_lwp (current_inferior); + lwpid = ptid_get_lwp (lwp->head.id); + kill_lwp (lwpid, SIGINT); } else kill_lwp (signal_pid, SIGINT); @@ -1946,7 +2856,7 @@ linux_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len) char filename[PATH_MAX]; int fd, n; - snprintf (filename, sizeof filename, "/proc/%ld/auxv", inferior_pid); + snprintf (filename, sizeof filename, "/proc/%ld/auxv", inferior_lwpid); fd = open (filename, O_RDONLY); if (fd < 0) @@ -2012,6 +2922,12 @@ linux_stopped_data_address (void) #define PT_TEXT_END_ADDR 51*4 #endif +#if defined(__arm__) +#ifndef PTRACE_GETPROCMAP +#define PTRACE_GETPROCMAP 27 +#endif +#endif + /* Under uClinux, programs are loaded at non-zero offsets, which we need to tell gdb about. */ @@ -2020,7 +2936,7 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p) { #if defined(PT_TEXT_ADDR) && defined(PT_DATA_ADDR) && defined(PT_TEXT_END_ADDR) unsigned long text, text_end, data; - int pid = get_thread_process (current_inferior)->head.id; + int pid = pid_of (get_thread_lwp (current_inferior)); errno = 0; @@ -2045,10 +2961,95 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p) return 1; } #endif +#if defined(PTRACE_GETPROCMAP) + unsigned long data[6]; + int pid = pid_of (get_thread_lwp (current_inferior)); + + if (ptrace (PTRACE_GETPROCMAP, pid, NULL, data) == 0) + { + /* GETPROCMAP returns text_start, text_end, data_start. See above. */ + *text_p = data[0]; + *data_p = data[2] - (data[1] - data[0]); + return 1; + } +#endif return 0; } #endif +/* SIGCHLD handler that serves two purposes: In non-stop/async mode, + so we notice when any child changes state; as the handler for the + sigsuspend in my_waitpid. */ + +static void +sigchld_handler (int signo) +{ + int old_errno = errno; + + if (debug_threads) + /* fprintf is not an async-signal-safe function, so call write + directly. */ + write (2, "sigchld_handler\n", + strlen ("sigchld_handler\n")); + + if (target_is_async_p ()) + async_file_mark (); /* trigger a linux_wait */ + + errno = old_errno; +} + +static int +linux_async (int enable) +{ + int previous = (linux_event_pipe[0] != -1); + + if (previous != enable) + { + sigset_t mask; + sigemptyset (&mask); + sigaddset (&mask, SIGCHLD); + + sigprocmask (SIG_BLOCK, &mask, NULL); + + if (enable) + { + if (pipe (linux_event_pipe) == -1) + internal_error ("creating event pipe failed."); + + fcntl (linux_event_pipe[0], F_SETFL, O_NONBLOCK); + fcntl (linux_event_pipe[1], F_SETFL, O_NONBLOCK); + + /* Register the event loop handler. */ + add_file_handler (linux_event_pipe[0], + handle_target_event, NULL); + + /* Always trigger a linux_wait. */ + async_file_mark (); + } + else + { + delete_file_handler (linux_event_pipe[0]); + + close (linux_event_pipe[0]); + close (linux_event_pipe[1]); + linux_event_pipe[0] = -1; + linux_event_pipe[1] = -1; + } + + sigprocmask (SIG_UNBLOCK, &mask, NULL); + } + + return previous; +} + +static int +linux_start_non_stop (int nonstop) +{ + /* Register or unregister from event-loop accordingly. */ + linux_async (nonstop); + return 0; +} + static struct target_ops linux_target_ops = { linux_create_inferior, linux_attach, @@ -2081,6 +3082,9 @@ static struct target_ops linux_target_ops = { #endif NULL, hostio_last_error_from_errno, + linux_pid_to_exec_file, + linux_async, + linux_start_non_stop, }; static void @@ -2094,7 +3098,8 @@ linux_init_signals () void initialize_low (void) { - thread_db_active = 0; + struct sigaction sigchld_action; + set_target_ops (&linux_target_ops); set_breakpoint_data (the_low_target.breakpoint, the_low_target.breakpoint_len); @@ -2105,4 +3110,9 @@ initialize_low (void) ; disabled_regsets = malloc (num_regsets); #endif + + sigchld_action.sa_handler = sigchld_handler; + sigemptyset (&sigchld_action.sa_mask); + sigchld_action.sa_flags = SA_RESTART; + sigaction (SIGCHLD, &sigchld_action, NULL); } diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index 78734e7..74d4091 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -20,6 +20,8 @@ #include <thread_db.h> #endif +#include "gdb_proc_service.h" + #ifdef HAVE_LINUX_REGSETS typedef void (*regset_fill_func) (void *); typedef void (*regset_store_func) (const void *); @@ -40,6 +42,19 @@ struct regset_info extern struct regset_info target_regsets[]; #endif +struct process_info_private +{ + /* True if this process has loaded thread_db, and it is active. */ + int thread_db_active; + + /* Structure that identifies the child process for the + <proc_service.h> interface. */ + struct ps_prochandle proc_handle; + + /* Connection to the libthread_db library. */ + td_thragent_t *thread_agent; +}; + struct linux_target_ops { /* Architecture-specific setup. */ @@ -77,30 +92,37 @@ struct linux_target_ops extern struct linux_target_ops the_low_target; -#define get_process(inf) ((struct process_info *)(inf)) -#define get_thread_process(thr) (get_process (inferior_target_data (thr))) -#define get_process_thread(proc) ((struct thread_info *) \ +#define get_lwp(inf) ((struct lwp_info *)(inf)) +#define get_thread_lwp(thr) (get_lwp (inferior_target_data (thr))) +#define get_lwp_thread(proc) ((struct thread_info *) \ find_inferior_id (&all_threads, \ - get_process (proc)->lwpid)) + get_lwp (proc)->head.id)) -struct process_info +struct lwp_info { struct inferior_list_entry head; - unsigned long lwpid; /* If this flag is set, the next SIGSTOP will be ignored (the - process will be immediately resumed). This means that either we + lwp will be immediately resumed). This means that either we sent the SIGSTOP to it ourselves and got some other pending event (so the SIGSTOP is still pending), or that we stopped the inferior implicitly via PTRACE_ATTACH and have not waited for it yet. */ int stop_expected; - /* If this flag is set, the process is known to be stopped right now (stop + /* True if this thread was suspended (with vCont;t). */ + int suspended; + + /* If this flag is set, the lwp is known to be stopped right now (stop event already received in a wait()). */ int stopped; - /* When stopped is set, the last wait status recorded for this process. */ + /* If this flag is set, the lwp is known to be dead already (exit + event already received in a wait(), and is cached in + status_pending). */ + int dead; + + /* When stopped is set, the last wait status recorded for this lwp. */ int last_status; /* If this flag is set, STATUS_PENDING is a waitstatus that has not yet @@ -116,12 +138,12 @@ struct process_info stop (SIGTRAP stops only). */ CORE_ADDR bp_reinsert; - /* If this flag is set, the last continue operation on this process + /* If this flag is set, the last continue operation on this lwp was a single-step. */ int stepping; /* If this is non-zero, it points to a chain of signals which need to - be delivered to this process. */ + be delivered to this lwp. */ struct pending_signals *pending_signals; /* A link used when resuming. It is initialized from the resume request, @@ -136,12 +158,19 @@ struct process_info THREAD_KNOWN is set. */ td_thrhandle_t th; #endif + + /* If WAITSTATUS->KIND is not TARGET_WAITKIND_IGNORE, the waitstatus + for this LWP's last event. This may correspond to STATUS above, + or to an extended event (e.g., PTRACE_EVENT_FORK). */ + struct target_waitstatus waitstatus; }; -extern struct inferior_list all_processes; +extern struct inferior_list all_lwps; -void linux_attach_lwp (unsigned long pid); +void linux_attach_lwp (unsigned long pid, int initial); int thread_db_init (int use_events); int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, CORE_ADDR load_module, CORE_ADDR *address); + +struct lwp_info *find_lwp_pid (ptid_t ptid); diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c index 8f5872f..6e6141b 100644 --- a/gdb/gdbserver/mem-break.c +++ b/gdb/gdbserver/mem-break.c @@ -45,16 +45,56 @@ struct breakpoint int (*handler) (CORE_ADDR); }; -struct breakpoint *breakpoints; +struct breakpoint_list +{ + struct inferior_list_entry head; + + struct breakpoint *breakpoints; +}; + +/* ``all_breakpoints'' is keyed by process ID. */ + +struct inferior_list all_breakpoints; + +static struct breakpoint_list * +get_breakpoint_list (int create) +{ + ptid_t ptid; + int pid; + struct inferior_list_entry *list; + + ptid = ((struct inferior_list_entry *)current_inferior)->id; + pid = ptid_get_pid (ptid); + + list = find_inferior_id (&all_breakpoints, pid_to_ptid (pid)); + + if (list == NULL && create) + { + list = calloc (1, sizeof (struct breakpoint_list)); + list->id = pid_to_ptid (pid); + add_inferior_to_list (&all_breakpoints, list); + } + + return (struct breakpoint_list *) list; +} + +static void +remove_breakpoint_list (struct breakpoint_list *list) +{ + remove_inferior (&all_breakpoints, &list->head); +} void set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) { + struct breakpoint_list *list; struct breakpoint *bp; if (breakpoint_data == NULL) error ("Target does not support breakpoints."); + list = get_breakpoint_list (1); + bp = malloc (sizeof (struct breakpoint)); memset (bp, 0, sizeof (struct breakpoint)); @@ -66,24 +106,30 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) bp->pc = where; bp->handler = handler; - bp->next = breakpoints; - breakpoints = bp; + bp->next = list->breakpoints; + list->breakpoints = bp; } static void delete_breakpoint (struct breakpoint *bp) { + struct breakpoint_list *list; struct breakpoint *cur; - if (breakpoints == bp) + list = get_breakpoint_list (0); + + if (list == NULL) + goto out; + + if (list->breakpoints == bp) { - breakpoints = bp->next; + list->breakpoints = bp->next; (*the_target->write_memory) (bp->pc, bp->old_data, breakpoint_len); free (bp); return; } - cur = breakpoints; + cur = list->breakpoints; while (cur->next) { if (cur->next == bp) @@ -95,13 +141,23 @@ delete_breakpoint (struct breakpoint *bp) return; } } + + out: warning ("Could not find breakpoint in list."); } static struct breakpoint * find_breakpoint_at (CORE_ADDR where) { - struct breakpoint *bp = breakpoints; + struct breakpoint_list *list; + struct breakpoint *bp; + + list = get_breakpoint_list (0); + + if (list == NULL) + return NULL; + + bp = list->breakpoints; while (bp != NULL) { @@ -214,6 +270,14 @@ check_breakpoints (CORE_ADDR stop_pc) return 1; } +int +breakpoint_at (CORE_ADDR stop_pc) +{ + struct breakpoint *bp; + bp = find_breakpoint_at (stop_pc); + return (bp != NULL); +} + void set_breakpoint_data (const unsigned char *bp_data, int bp_len) { @@ -224,8 +288,17 @@ set_breakpoint_data (const unsigned char *bp_data, int bp_len) void check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) { - struct breakpoint *bp = breakpoints; - CORE_ADDR mem_end = mem_addr + mem_len; + struct breakpoint_list *list; + struct breakpoint *bp; + CORE_ADDR mem_end; + + list = get_breakpoint_list (0); + + if (list == NULL) + return; + + bp = list->breakpoints; + mem_end = mem_addr + mem_len; for (; bp != NULL; bp = bp->next) { @@ -257,8 +330,17 @@ check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) void check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) { - struct breakpoint *bp = breakpoints; - CORE_ADDR mem_end = mem_addr + mem_len; + struct breakpoint_list *list; + struct breakpoint *bp; + CORE_ADDR mem_end; + + list = get_breakpoint_list (0); + + if (list == NULL) + return; + + bp = list->breakpoints; + mem_end = mem_addr + mem_len; for (; bp != NULL; bp = bp->next) { @@ -294,6 +376,15 @@ check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) void delete_all_breakpoints (void) { - while (breakpoints) - delete_breakpoint (breakpoints); + struct breakpoint_list *list; + + list = get_breakpoint_list (0); + + if (list == NULL) + return; + + while (list->breakpoints) + delete_breakpoint (list->breakpoints); + + remove_breakpoint_list (list); } diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h index 1478959..a01360d 100644 --- a/gdb/gdbserver/mem-break.h +++ b/gdb/gdbserver/mem-break.h @@ -54,6 +54,10 @@ void uninsert_breakpoint (CORE_ADDR where); int check_breakpoints (CORE_ADDR stop_pc); +/* See if any breakpoint claims ownership of STOP_PC. */ + +int breakpoint_at (CORE_ADDR stop_pc); + /* See if any breakpoints shadow the target memory area from MEM_ADDR to MEM_ADDR + MEM_LEN. Update the data already read from the target (in BUF) if necessary. */ diff --git a/gdb/gdbserver/proc-service.c b/gdb/gdbserver/proc-service.c index c875313..beceec5 100644 --- a/gdb/gdbserver/proc-service.c +++ b/gdb/gdbserver/proc-service.c @@ -99,15 +99,14 @@ ps_err_e ps_lgetregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, prgregset_t gregset) { #ifdef HAVE_REGSETS - struct process_info *process; + struct lwp_info *lwp; struct thread_info *reg_inferior, *save_inferior; - process = (struct process_info *) find_inferior_id (&all_processes, - lwpid); - if (process == NULL) + lwp = find_lwp_pid (pid_to_ptid (lwpid)); + if (lwp == NULL) return PS_ERR; - reg_inferior = get_process_thread (process); + reg_inferior = get_lwp_thread (lwp); save_inferior = current_inferior; current_inferior = reg_inferior; diff --git a/gdb/gdbserver/regcache.c b/gdb/gdbserver/regcache.c index b5ec215..a324d43 100644 --- a/gdb/gdbserver/regcache.c +++ b/gdb/gdbserver/regcache.c @@ -91,9 +91,6 @@ new_register_cache (void) { struct inferior_regcache_data *regcache; - if (register_bytes == 0) - return NULL; /* The architecture hasn't been initialized yet. */ - regcache = malloc (sizeof (*regcache)); /* Make sure to zero-initialize the register cache when it is created, @@ -114,11 +111,8 @@ free_register_cache (void *regcache_p) struct inferior_regcache_data *regcache = (struct inferior_regcache_data *) regcache_p; - if (regcache) - { - free (regcache->registers); - free (regcache); - } + free (regcache->registers); + free (regcache); } static void diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c index b5665f5..961b0f4 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -20,6 +20,7 @@ #include "server.h" #include "terminal.h" +#include "target.h" #include <stdio.h> #include <string.h> #if HAVE_SYS_IOCTL_H @@ -78,14 +79,11 @@ typedef int socklen_t; /* A cache entry for a successfully looked-up symbol. */ struct sym_cache { - const char *name; + char *name; CORE_ADDR addr; struct sym_cache *next; }; -/* The symbol cache. */ -static struct sym_cache *symbol_cache; - /* If this flag has been set, assume cache misses are failures. */ int all_symbols_looked_up; @@ -285,11 +283,16 @@ remote_open (char *name) fcntl (remote_desc, F_SETOWN, getpid ()); #endif #endif + + /* Register the event loop handler. */ + add_file_handler (remote_desc, handle_serial_event, NULL); } void remote_close (void) { + delete_file_handler (remote_desc); + #ifdef USE_WIN32API closesocket (remote_desc); #else @@ -311,6 +314,29 @@ fromhex (int a) return 0; } +static const char hexchars[] = "0123456789abcdef"; + +static int +ishex (int ch, int *val) +{ + if ((ch >= 'a') && (ch <= 'f')) + { + *val = ch - 'a' + 10; + return 1; + } + if ((ch >= 'A') && (ch <= 'F')) + { + *val = ch - 'A' + 10; + return 1; + } + if ((ch >= '0') && (ch <= '9')) + { + *val = ch - '0'; + return 1; + } + return 0; +} + int unhexify (char *bin, const char *hex, int count) { @@ -517,12 +543,111 @@ try_rle (char *buf, int remaining, unsigned char *csum, char **p) return n + 1; } +char * +unpack_varlen_hex (char *buff, /* packet to parse */ + ULONGEST *result) +{ + int nibble; + ULONGEST retval = 0; + + while (ishex (*buff, &nibble)) + { + buff++; + retval = retval << 4; + retval |= nibble & 0x0f; + } + *result = retval; + return buff; +} + +/* Write a PTID to BUF. Returns BUF+CHARACTERS_WRITTEN. */ + +char * +write_ptid (char *buf, ptid_t ptid) +{ + int pid, tid; + + if (multi_process) + { + pid = ptid_get_pid (ptid); + if (pid < 0) + buf += sprintf (buf, "p-%x.", -pid); + else + buf += sprintf (buf, "p%x.", pid); + } + tid = ptid_get_lwp (ptid); + if (tid < 0) + buf += sprintf (buf, "-%x", -tid); + else + buf += sprintf (buf, "%x", tid); + + return buf; +} + +ULONGEST +hex_or_minus_one (char *buf, char **obuf) +{ + ULONGEST ret; + + if (strncmp (buf, "-1", 2) == 0) + { + ret = (ULONGEST) -1; + buf += 2; + } + else + buf = unpack_varlen_hex (buf, &ret); + + if (obuf) + *obuf = buf; + + return ret; +} + +/* Extract a PTID from BUF. If non-null, OBUF is set to the to one + passed the last parsed char. Returns null_ptid on error. */ +ptid_t +read_ptid (char *buf, char **obuf) +{ + char *p = buf; + char *pp; + ULONGEST pid = 0, tid = 0; + + if (*p == 'p') + { + /* Multi-process ptid. */ + pp = unpack_varlen_hex (p + 1, &pid); + if (*pp != '.') + error ("invalid remote ptid: %s\n", p); + + p = pp + 1; + + tid = hex_or_minus_one (p, &pp); + + if (obuf) + *obuf = pp; + /* TODO, we really need to gdbid vs target thread id after + all. */ + return ptid_build (pid, tid, 0); + } + + /* No multi-process. Just a tid. */ + tid = hex_or_minus_one (p, &pp); + + /* Since the stub is not sending a process id, then default to + what's in the current inferior. */ + pid = ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id); + + if (obuf) + *obuf = pp; + return ptid_build (pid, tid, 0); +} + /* Send a packet to the remote machine, with error checking. The data of the packet is in BUF, and the length of the packet is in CNT. Returns >= 0 on success, -1 otherwise. */ -int -putpkt_binary (char *buf, int cnt) +static int +putpkt_binary_1 (char *buf, int cnt, int is_notif) { int i; unsigned char csum = 0; @@ -536,7 +661,10 @@ putpkt_binary (char *buf, int cnt) and giving it a checksum. */ p = buf2; - *p++ = '$'; + if (is_notif) + *p++ = '%'; + else + *p++ = '$'; for (i = 0; i < cnt;) i += try_rle (buf + i, cnt - i, &csum, &p); @@ -560,12 +688,15 @@ putpkt_binary (char *buf, int cnt) return -1; } - if (noack_mode) + if (noack_mode || is_notif) { /* Don't expect an ack then. */ if (remote_debug) { - fprintf (stderr, "putpkt (\"%s\"); [noack mode]\n", buf2); + if (is_notif) + fprintf (stderr, "putpkt (\"%s\"); [notif]\n", buf2); + else + fprintf (stderr, "putpkt (\"%s\"); [noack mode]\n", buf2); fflush (stderr); } break; @@ -604,6 +735,12 @@ putpkt_binary (char *buf, int cnt) return 1; /* Success! */ } +int +putpkt_binary (char *buf, int cnt) +{ + return putpkt_binary_1 (buf, cnt, 0); +} + /* Send a packet to the remote machine, with error checking. The data of the packet is in BUF, and the packet should be a NUL-terminated string. Returns >= 0 on success, -1 otherwise. */ @@ -614,6 +751,12 @@ putpkt (char *buf) return putpkt_binary (buf, strlen (buf)); } +int +putpkt_notif (char *buf) +{ + return putpkt_binary_1 (buf, strlen (buf), 1); +} + /* Come here when we get an input interrupt from the remote side. This interrupt should only be active while we are waiting for the child to do something. About the only thing that should come through is a ^C, which @@ -823,6 +966,11 @@ getpkt (char *buf) fflush (stderr); } } + else if (remote_debug) + { + fprintf (stderr, "getpkt (\"%s\"); [noack mode] \n", buf); + fflush (stderr); + } return bp - buf; } @@ -925,88 +1073,141 @@ dead_thread_notify (int id) } void -prepare_resume_reply (char *buf, char status, unsigned char sig) +prepare_resume_reply (char *buf, ptid_t ptid, struct target_waitstatus *status) { - int nib; - - *buf++ = status; + if (debug_threads) + fprintf (stderr, "Writing resume reply for %s:%d\n\n", + target_pid_to_str (ptid), status->kind); - nib = ((sig & 0xf0) >> 4); - *buf++ = tohex (nib); - nib = sig & 0x0f; - *buf++ = tohex (nib); - - if (status == 'T') + switch (status->kind) { - const char **regp = gdbserver_expedite_regs; - - if (the_target->stopped_by_watchpoint != NULL - && (*the_target->stopped_by_watchpoint) ()) - { - CORE_ADDR addr; - int i; - - strncpy (buf, "watch:", 6); - buf += 6; - - addr = (*the_target->stopped_data_address) (); - - /* Convert each byte of the address into two hexadecimal chars. - Note that we take sizeof (void *) instead of sizeof (addr); - this is to avoid sending a 64-bit address to a 32-bit GDB. */ - for (i = sizeof (void *) * 2; i > 0; i--) - { - *buf++ = tohex ((addr >> (i - 1) * 4) & 0xf); - } - *buf++ = ';'; - } - - while (*regp) - { - buf = outreg (find_regno (*regp), buf); - regp ++; - } - - /* Formerly, if the debugger had not used any thread features we would not - burden it with a thread status response. This was for the benefit of - GDB 4.13 and older. However, in recent GDB versions the check - (``if (cont_thread != 0)'') does not have the desired effect because of - sillyness in the way that the remote protocol handles specifying a thread. - Since thread support relies on qSymbol support anyway, assume GDB can handle - threads. */ - - if (using_threads && !disable_packet_Tthread) - { - unsigned int gdb_id_from_wait; - - /* FIXME right place to set this? */ - thread_from_wait = ((struct inferior_list_entry *)current_inferior)->id; - gdb_id_from_wait = thread_to_gdb_id (current_inferior); - - if (debug_threads) - fprintf (stderr, "Writing resume reply for %ld\n\n", thread_from_wait); - /* This if (1) ought to be unnecessary. But remote_wait in GDB - will claim this event belongs to inferior_ptid if we do not - specify a thread, and there's no way for gdbserver to know - what inferior_ptid is. */ - if (1 || old_thread_from_wait != thread_from_wait) - { - general_thread = thread_from_wait; - sprintf (buf, "thread:%x;", gdb_id_from_wait); - buf += strlen (buf); - old_thread_from_wait = thread_from_wait; - } - } + case TARGET_WAITKIND_STOPPED: + { + struct thread_info *saved_inferior; + const char **regp; + + sprintf (buf, "T%02x", status->value.sig); + buf += strlen (buf); + + regp = gdbserver_expedite_regs; + + saved_inferior = current_inferior; + + current_inferior = find_thread_pid (ptid); + + /* Check that the thread is still alive before trying to read + registers or memory from it. */ + if (current_inferior != NULL && mythread_alive (ptid)) + { + if (the_target->stopped_by_watchpoint != NULL + && (*the_target->stopped_by_watchpoint) ()) + { + CORE_ADDR addr; + int i; + + strncpy (buf, "watch:", 6); + buf += 6; + + addr = (*the_target->stopped_data_address) (); + + /* Convert each byte of the address into two hexadecimal chars. + Note that we take sizeof (void *) instead of sizeof (addr); + this is to avoid sending a 64-bit address to a + 32-bit GDB. */ + for (i = sizeof (void *) * 2; i > 0; i--) + *buf++ = tohex ((addr >> (i - 1) * 4) & 0xf); + *buf++ = ';'; + } + + while (*regp) + { + buf = outreg (find_regno (*regp), buf); + regp ++; + } + } + + /* Formerly, if the debugger had not used any thread features + we would not burden it with a thread status response. This + was for the benefit of GDB 4.13 and older. However, in + recent GDB versions the check (``if (cont_thread != 0)'') + does not have the desired effect because of sillyness in + the way that the remote protocol handles specifying a + thread. Since thread support relies on qSymbol support + anyway, assume GDB can handle threads. */ + + if (using_threads && !disable_packet_Tthread) + { + /* This if (1) ought to be unnecessary. But remote_wait + in GDB will claim this event belongs to inferior_ptid + if we do not specify a thread, and there's no way for + gdbserver to know what inferior_ptid is. */ + if (1 || !ptid_equal (general_thread, ptid)) + { + /* In non-stop, don't change the general thread behind + GDB's back. */ + if (!non_stop) + general_thread = ptid; + sprintf (buf, "thread:"); + buf += strlen (buf); + buf = write_ptid (buf, ptid); + strcat (buf, ";"); + buf += strlen (buf); + } + } + + /* FIXME: Can we make this be TARGET_WAITKIND_LOADED? */ + if (dlls_changed) + { + strcpy (buf, "library:;"); + buf += strlen (buf); + dlls_changed = 0; + } + + current_inferior = saved_inferior; + } + break; + case TARGET_WAITKIND_EXITED: + if (multi_process) + sprintf (buf, "W%x;process:%x", + status->value.integer, ptid_get_pid (ptid)); + else + sprintf (buf, "W%02x", status->value.integer); + break; + case TARGET_WAITKIND_SIGNALLED: + if (multi_process) + sprintf (buf, "X%x;process:%x", + status->value.sig, ptid_get_pid (ptid)); + else + sprintf (buf, "X%02x", status->value.sig); + break; + case TARGET_WAITKIND_FORKED: + case TARGET_WAITKIND_VFORKED: + general_thread = ptid; - if (dlls_changed) - { - strcpy (buf, "library:;"); - buf += strlen (buf); - dlls_changed = 0; - } + if (status->kind == TARGET_WAITKIND_FORKED) + sprintf (buf, "Y;fork;"); + else + sprintf (buf, "Y;vfork;"); + buf += strlen (buf); + buf = write_ptid (buf, ptid); + strcat (buf, ";"); + buf += strlen (buf); + buf = write_ptid (buf, status->value.related_pid); + break; + case TARGET_WAITKIND_EXECD: + general_thread = ptid; + + sprintf (buf, "Y;exec;"); + buf += strlen (buf); + buf = write_ptid (buf, ptid); + strcat (buf, ";"); + buf += strlen (buf); + hexify (buf, status->value.execd_pathname, 0); + break; + default: + error ("unhandled waitkind"); + break; } - /* For W and X, we're done. */ - *buf++ = 0; } void @@ -1129,6 +1330,34 @@ decode_search_memory_packet (const char *buf, int packet_len, return 0; } +static void +free_sym_cache (struct sym_cache *sym) +{ + if (sym != NULL) + { + free (sym->name); + free (sym); + } +} + +void +clear_symbol_cache (void) +{ + struct process_info *proc; + struct sym_cache *sym, *next; + + proc = current_process (); + + /* Check the cache first. */ + for (sym = proc->symbol_cache; sym; sym = next) + { + next = sym->next; + free_sym_cache (sym); + } + + proc->symbol_cache = NULL; +} + /* Ask GDB for the address of NAME, and return it in ADDRP if found. Returns 1 if the symbol is found, 0 if it is not, -1 on error. */ @@ -1138,9 +1367,12 @@ look_up_one_symbol (const char *name, CORE_ADDR *addrp) char own_buf[266], *p, *q; int len; struct sym_cache *sym; + struct process_info *proc; + + proc = current_process (); /* Check the cache first. */ - for (sym = symbol_cache; sym; sym = sym->next) + for (sym = proc->symbol_cache; sym; sym = sym->next) if (strcmp (name, sym->name) == 0) { *addrp = sym->addr; @@ -1152,7 +1384,7 @@ look_up_one_symbol (const char *name, CORE_ADDR *addrp) in any libraries loaded after that point, only in symbols in libpthread.so. It might not be an appropriate time to look up a symbol, e.g. while we're trying to fetch registers. */ - if (all_symbols_looked_up) + if (proc->all_symbols_looked_up) return 0; /* Send the request. */ @@ -1212,8 +1444,8 @@ look_up_one_symbol (const char *name, CORE_ADDR *addrp) sym = malloc (sizeof (*sym)); sym->name = strdup (name); sym->addr = *addrp; - sym->next = symbol_cache; - symbol_cache = sym; + sym->next = proc->symbol_cache; + proc->symbol_cache = sym; return 1; } diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 4adbf51..4ccda46 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -32,18 +32,19 @@ #include <malloc.h> #endif -unsigned long cont_thread; -unsigned long general_thread; -unsigned long step_thread; -unsigned long thread_from_wait; -unsigned long old_thread_from_wait; +ptid_t cont_thread; +ptid_t general_thread; +ptid_t step_thread; + int server_waiting; static int extended_protocol; -static int attached; static int response_needed; static int exit_requested; +int multi_process; +int non_stop; + static char **program_argv, **wrapper_argv; /* Enable miscellaneous debugging output. The name is historical - it @@ -87,6 +88,123 @@ int disable_packet_Tthread; int disable_packet_qC; int disable_packet_qfThreadInfo; +/* Last status reported to GDB. */ +static struct target_waitstatus last_status; +static ptid_t last_ptid; + +static char *own_buf; +static unsigned char *mem_buf; + +/* Structure holding information relative to a single stop reply. We + keep a queue of these (realy a singly-linked list) to push to GDB + in non-stop mode. */ +struct vstop_notif +{ + /* Pointer to next in list. */ + struct vstop_notif *next; + + /* Thread or process that got the event. */ + ptid_t ptid; + + /* Event info. */ + struct target_waitstatus status; +}; + +/* The pending stop replies list head. */ +static struct vstop_notif *notif_queue = NULL; + +/* Put a stop reply to the stop reply queue. */ +static void +queue_stop_reply (ptid_t ptid, struct target_waitstatus *status) +{ + struct vstop_notif *new_notif; + + new_notif = malloc (sizeof (*new_notif)); + new_notif->next = NULL; + new_notif->ptid = ptid; + new_notif->status = *status; + + if (notif_queue) + { + struct vstop_notif *tail; + for (tail = notif_queue; + tail && tail->next; + tail = tail->next) + ; + tail->next = new_notif; + } + else + notif_queue = new_notif; + + if (remote_debug) + { + int i = 0; + struct vstop_notif *tail; + + for (tail = notif_queue; tail; tail = tail->next) + i++; + + fprintf (stderr, "pending stop replies: %d\n", i); + } +} + +/* Place an an event in the stop reply queue, and push a notification + if we aren't sending one yet. */ +void +push_event (ptid_t ptid, struct target_waitstatus *status) +{ + queue_stop_reply (ptid, status); + + /* If this is the first stop reply in the queue, then inform GDB + about it, by sending a Stop notification. */ + if (notif_queue->next == NULL) + { + char *p = own_buf; + strcpy (p, "Stop:"); + p += strlen (p); + prepare_resume_reply (p, + notif_queue->ptid, ¬if_queue->status); + putpkt_notif (own_buf); + } +} + +/* Get rid of the currently pending stop replies. */ +static void +discard_queued_stop_replies (int pid) +{ + struct vstop_notif *prev = NULL, *reply, *next; + + for (reply = notif_queue; reply; reply = next) + { + next = reply->next; + + if (pid == -1 + || ptid_get_pid (reply->ptid) == pid) + { + if (reply == notif_queue) + notif_queue = next; + else + prev->next = reply->next; + + free (reply); + } + else + prev = reply; + } +} + +/* If there are more stop replies to push, push one now. */ +static void +send_next_stop_reply (char *own_buf) +{ + if (notif_queue) + prepare_resume_reply (own_buf, + notif_queue->ptid, + ¬if_queue->status); + else + write_ok (own_buf); +} + static int target_running (void) { @@ -94,10 +212,9 @@ target_running (void) } static int -start_inferior (char **argv, char *statusptr) +start_inferior (char **argv) { char **new_argv = argv; - attached = 0; if (wrapper_argv != NULL) { @@ -141,37 +258,39 @@ start_inferior (char **argv, char *statusptr) if (wrapper_argv != NULL) { struct thread_resume resume_info; - int sig; + ptid_t ptid; - resume_info.thread = -1; - resume_info.step = 0; + resume_info.thread = pid_to_ptid (signal_pid); + resume_info.kind = rk_continue; resume_info.sig = 0; - resume_info.leave_stopped = 0; - sig = mywait (statusptr, 0); - if (*statusptr != 'T') - return sig; + ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); + + if (last_status.kind != TARGET_WAITKIND_STOPPED) + return signal_pid; do { - (*the_target->resume) (&resume_info); + (*the_target->resume) (&resume_info, 1); - sig = mywait (statusptr, 0); - if (*statusptr != 'T') - return sig; + mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); + if (last_status.kind != TARGET_WAITKIND_STOPPED) + return signal_pid; } - while (sig != TARGET_SIGNAL_TRAP); + while (last_status.value.sig != TARGET_SIGNAL_TRAP); - return sig; + return signal_pid; } - /* Wait till we are at 1st instruction in program, return signal - number (assuming success). */ - return mywait (statusptr, 0); + /* Wait till we are at 1st instruction in program, return new pid + (assuming success). */ + last_ptid = mywait (pid_to_ptid (signal_pid), &last_status, 0, 0); + + return signal_pid; } static int -attach_inferior (int pid, char *statusptr, int *sigptr) +attach_inferior (int pid) { /* myattach should return -1 if attaching is unsupported, 0 if it succeeded, and call error() otherwise. */ @@ -179,8 +298,6 @@ attach_inferior (int pid, char *statusptr, int *sigptr) if (myattach (pid) != 0) return -1; - attached = 1; - fprintf (stderr, "Attached; pid = %d\n", pid); fflush (stderr); @@ -189,13 +306,17 @@ attach_inferior (int pid, char *statusptr, int *sigptr) whichever we were told to attach to. */ signal_pid = pid; - *sigptr = mywait (statusptr, 0); - - /* GDB knows to ignore the first SIGSTOP after attaching to a running - process using the "attach" command, but this is different; it's - just using "target remote". Pretend it's just starting up. */ - if (*statusptr == 'T' && *sigptr == TARGET_SIGNAL_STOP) - *sigptr = TARGET_SIGNAL_TRAP; + if (!non_stop) + { + last_ptid = mywait (pid_to_ptid (pid), &last_status, 0, 0); + + /* GDB knows to ignore the first SIGSTOP after attaching to a running + process using the "attach" command, but this is different; it's + just using "target remote". Pretend it's just starting up. */ + if (last_status.kind == TARGET_WAITKIND_STOPPED + && last_status.value.sig == TARGET_SIGNAL_STOP) + last_status.value.sig = TARGET_SIGNAL_TRAP; + } return 0; } @@ -283,6 +404,43 @@ handle_general_set (char *own_buf) return; } + if (strncmp (own_buf, "QNonStop:", 9) == 0) + { + char *mode = own_buf + 9; + int req = -1; + char *req_str; + + if (strcmp (mode, "0") == 0) + req = 0; + else if (strcmp (mode, "1") == 0) + req = 1; + else + { + /* We don't know what this mode is, so complain to + GDB. */ + fprintf (stderr, "Unknown non-stop mode requested: %s\n", + own_buf); + write_enn (own_buf); + return; + } + + req_str = req ? "non-stop" : "all-stop"; + if (start_non_stop (req) != 0) + { + fprintf (stderr, "Setting %s mode failed\n", req_str); + write_enn (own_buf); + return; + } + + non_stop = req; + + if (remote_debug) + fprintf (stderr, "[%s mode enabled]\n", req_str); + + write_ok (own_buf); + return; + } + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -501,10 +659,20 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) /* Reply the current thread id. */ if (strcmp ("qC", own_buf) == 0 && !disable_packet_qC) { + ptid_t gdb_id; require_running (own_buf); - thread_ptr = all_threads.head; - sprintf (own_buf, "QC%x", - thread_to_gdb_id ((struct thread_info *)thread_ptr)); + + if (!ptid_equal (general_thread, minus_one_ptid)) + gdb_id = general_thread; + else + { + thread_ptr = all_threads.head; + gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); + } + + sprintf (own_buf, "QC"); + own_buf += 2; + own_buf = write_ptid (own_buf, gdb_id); return; } @@ -521,19 +689,28 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) { if (strcmp ("qfThreadInfo", own_buf) == 0) { + ptid_t gdb_id; + require_running (own_buf); thread_ptr = all_threads.head; - sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); + + *own_buf++ = 'm'; + gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); + write_ptid (own_buf, gdb_id); thread_ptr = thread_ptr->next; return; } if (strcmp ("qsThreadInfo", own_buf) == 0) { + ptid_t gdb_id; + require_running (own_buf); if (thread_ptr != NULL) { - sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); + *own_buf++ = 'm'; + gdb_id = thread_to_gdb_id ((struct thread_info *)thread_ptr); + write_ptid (own_buf, gdb_id); thread_ptr = thread_ptr->next; return; } @@ -772,6 +949,21 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (strncmp ("qSupported", own_buf, 10) == 0 && (own_buf[10] == ':' || own_buf[10] == '\0')) { + char *p = &own_buf[10]; + + /* Process each feature being provided by GDB. The first + feature will follow a ':', and latter features will follow + ';'. */ + if (*p == ':') + for (p = strtok (p + 1, ";"); + p != NULL; + p = strtok (NULL, ";")) + { + /* Record if GDB knows about multiprocess support. */ + if (strcmp (p, "multiprocess+") == 0) + multi_process = 1; + } + sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1); /* We do not have any hook to indicate whether the target backend @@ -792,6 +984,11 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) if (transport_is_reliable) strcat (own_buf, ";QStartNoAckMode+"); + + strcat (own_buf, ";multiprocess+"); + + strcat (own_buf, ";QNonStop+"); + return; } @@ -800,8 +997,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) && strncmp ("qGetTLSAddr:", own_buf, 12) == 0) { char *p = own_buf + 12; - CORE_ADDR parts[3], address = 0; + CORE_ADDR parts[2], address = 0; int i, err; + ptid_t ptid; require_running (own_buf); @@ -825,7 +1023,10 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) p2 = NULL; } - decode_address (&parts[i], p, len); + if (i == 0) + ptid = read_ptid (p, NULL); + else + decode_address (&parts[i - 1], p, len); p = p2; } @@ -833,12 +1034,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) err = 1; else { - struct thread_info *thread = gdb_id_to_thread (parts[0]); + struct thread_info *thread = find_thread_pid (ptid); if (thread == NULL) err = 2; else - err = the_target->get_tls_address (thread, parts[1], parts[2], + err = the_target->get_tls_address (thread, parts[0], parts[1], &address); } @@ -914,6 +1115,40 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) return; } + if (the_target->pid_to_exec_file != NULL + && strncmp ("qExecFile:", own_buf, 10) == 0) + { + char *p = own_buf + 10; + int pid; + char *path; + size_t len; + + pid = strtol (p, NULL, 16); + + path = (*the_target->pid_to_exec_file) (pid); + + if (!path) + { + write_enn (own_buf); + return; + } + + len = strlen (path); + + if (len * 2 >= PBUFSIZ - strlen (own_buf) - 2) + { + /* File name too long for packet. */ + write_enn (own_buf); + return; + } + + own_buf[0] = 'Q'; + p = own_buf + strlen (own_buf); + *p++ = ';'; + hexify (p, path, len); + return; + } + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -921,11 +1156,11 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) /* Parse vCont packets. */ void -handle_v_cont (char *own_buf, char *status, int *signal) +handle_v_cont (char *own_buf) { char *p, *q; int n = 0, i = 0; - struct thread_resume *resume_info, default_action; + struct thread_resume *resume_info; /* Count the number of semicolons in the packet. There should be one for every action. */ @@ -936,28 +1171,20 @@ handle_v_cont (char *own_buf, char *status, int *signal) p++; p = strchr (p, ';'); } - /* Allocate room for one extra action, for the default remain-stopped - behavior; if no default action is in the list, we'll need the extra - slot. */ - resume_info = malloc ((n + 1) * sizeof (resume_info[0])); - default_action.thread = -1; - default_action.leave_stopped = 1; - default_action.step = 0; - default_action.sig = 0; + resume_info = malloc (n * sizeof (resume_info[0])); p = &own_buf[5]; - i = 0; while (*p) { p++; - resume_info[i].leave_stopped = 0; - if (p[0] == 's' || p[0] == 'S') - resume_info[i].step = 1; + resume_info[i].kind = rk_step; else if (p[0] == 'c' || p[0] == 'C') - resume_info[i].step = 0; + resume_info[i].kind = rk_continue; + else if (p[0] == 't') + resume_info[i].kind = rk_stop; else goto err; @@ -980,17 +1207,10 @@ handle_v_cont (char *own_buf, char *status, int *signal) } if (p[0] == 0) - { - resume_info[i].thread = -1; - default_action = resume_info[i]; - - /* Note: we don't increment i here, we'll overwrite this entry - the next time through. */ - } + resume_info[i].thread = minus_one_ptid; else if (p[0] == ':') { - unsigned int gdb_id = strtoul (p + 1, &q, 16); - unsigned long thread_id; + ptid_t ptid = read_ptid (p + 1, &q); if (p == q) goto err; @@ -998,33 +1218,36 @@ handle_v_cont (char *own_buf, char *status, int *signal) if (p[0] != ';' && p[0] != 0) goto err; - thread_id = gdb_id_to_thread_id (gdb_id); - if (thread_id) - resume_info[i].thread = thread_id; - else - goto err; - - i++; + resume_info[i].thread = ptid; } - } - resume_info[i] = default_action; + i++; + } /* Still used in occasional places in the backend. */ - if (n == 1 && resume_info[0].thread != -1) + if (n == 1 + && !ptid_equal (resume_info[0].thread, minus_one_ptid) + && resume_info[0].kind != rk_stop) cont_thread = resume_info[0].thread; else - cont_thread = -1; + cont_thread = minus_one_ptid; set_desired_inferior (0); - enable_async_io (); - (*the_target->resume) (resume_info); + if (!non_stop) + enable_async_io (); + + (*the_target->resume) (resume_info, n); free (resume_info); - *signal = mywait (status, 1); - prepare_resume_reply (own_buf, *status, *signal); - disable_async_io (); + if (non_stop) + write_ok (own_buf); + else + { + last_ptid = mywait (minus_one_ptid, &last_status, 0, 1); + prepare_resume_reply (own_buf, last_ptid, &last_status); + disable_async_io (); + } return; err: @@ -1035,19 +1258,32 @@ err: /* Attach to a new program. Return 1 if successful, 0 if failure. */ int -handle_v_attach (char *own_buf, char *status, int *signal) +handle_v_attach (char *own_buf) { int pid; pid = strtol (own_buf + 8, NULL, 16); - if (pid != 0 && attach_inferior (pid, status, signal) == 0) + if (pid != 0 && attach_inferior (pid) == 0) { /* Don't report shared library events after attaching, even if some libraries are preloaded. GDB will always poll the library list. Avoids the "stopped by shared library event" notice on the GDB side. */ dlls_changed = 0; - prepare_resume_reply (own_buf, *status, *signal); + + if (non_stop) + { + /* In non-stop, we don't send a resume reply. GDB will + query for the current thread, so set it. */ + general_thread = last_ptid; + + /* Stop events will follow up using the normal notification + mechanism. */ + write_ok (own_buf); + } + else + prepare_resume_reply (own_buf, last_ptid, &last_status); + return 1; } else @@ -1059,7 +1295,7 @@ handle_v_attach (char *own_buf, char *status, int *signal) /* Run a new program. Return 1 if successful, 0 if failure. */ static int -handle_v_run (char *own_buf, char *status, int *signal) +handle_v_run (char *own_buf) { char *p, **pp, *next_p, **new_argv; int i, new_argc; @@ -1096,36 +1332,58 @@ handle_v_run (char *own_buf, char *status, int *signal) if (new_argv[0] == NULL) { - /* GDB didn't specify a program to run. Try to use the argv - from the last run: either from the last vRun with a non-empty - argv, or from what the user specified if gdbserver was - started as: `gdbserver :1234 PROG ARGS'. */ - if (program_argv == NULL) { write_enn (own_buf); return 0; } - /* We can reuse the old args. We don't need this then. */ - free (new_argv); + new_argv[0] = strdup (program_argv[0]); + } + + /* Free the old argv. */ + if (program_argv) + { + for (pp = program_argv; *pp != NULL; pp++) + free (*pp); + free (program_argv); + } + program_argv = new_argv; + + start_inferior (program_argv); + if (last_status.kind == TARGET_WAITKIND_STOPPED) + { + prepare_resume_reply (own_buf, last_ptid, &last_status); + + /* In non-stop, sending a resume reply doesn't set the general + thread, but GDB assumes a vRun sets it (this is so GDB can + query which is the main thread of the new inferior. */ + if (non_stop) + general_thread = last_ptid; + + return 1; } else { - /* Free the old argv. */ - if (program_argv) - { - for (pp = program_argv; *pp != NULL; pp++) - free (*pp); - free (program_argv); - } - program_argv = new_argv; + write_enn (own_buf); + return 0; } +} + +/* Attach to a new program. Return 1 if successful, 0 if failure. */ +int +handle_v_kill (char *own_buf) +{ + int pid; + char *p = &own_buf[6]; - *signal = start_inferior (program_argv, status); - if (*status == 'T') + pid = strtol (p, NULL, 16); + if (pid != 0 && kill_inferior (pid) == 0) { - prepare_resume_reply (own_buf, *status, *signal); + last_status.kind = TARGET_WAITKIND_SIGNALLED; + last_status.value.sig = TARGET_SIGNAL_KILL; + discard_queued_stop_replies (pid); + write_ok (own_buf); return 1; } else @@ -1135,23 +1393,45 @@ handle_v_run (char *own_buf, char *status, int *signal) } } +/* Handle a 'vStopped' packet. */ +static void +handle_v_stopped (char *own_buf) +{ + /* If we're waiting for GDB to acknowledge a pending stop reply, + consider that done. */ + if (notif_queue) + { + struct vstop_notif *head; + + if (remote_debug) + fprintf (stderr, "vStopped: acking %s\n", + target_pid_to_str (notif_queue->ptid)); + + head = notif_queue; + notif_queue = notif_queue->next; + free (head); + } + + /* Push another, or if there are no more left, and OK. */ + send_next_stop_reply (own_buf); +} + /* Handle all of the extended 'v' packets. */ void -handle_v_requests (char *own_buf, char *status, int *signal, - int packet_len, int *new_packet_len) +handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) { if (!disable_packet_vCont) { if (strncmp (own_buf, "vCont;", 6) == 0) { require_running (own_buf); - handle_v_cont (own_buf, status, signal); + handle_v_cont (own_buf); return; } if (strncmp (own_buf, "vCont?", 6) == 0) { - strcpy (own_buf, "vCont;c;C;s;S"); + strcpy (own_buf, "vCont;c;C;s;S;t"); return; } } @@ -1162,25 +1442,43 @@ handle_v_requests (char *own_buf, char *status, int *signal, if (strncmp (own_buf, "vAttach;", 8) == 0) { - if (target_running ()) + if (!multi_process && target_running ()) { fprintf (stderr, "Already debugging a process\n"); write_enn (own_buf); return; } - handle_v_attach (own_buf, status, signal); + handle_v_attach (own_buf); return; } if (strncmp (own_buf, "vRun;", 5) == 0) { - if (target_running ()) + if (!multi_process && target_running ()) { fprintf (stderr, "Already debugging a process\n"); write_enn (own_buf); return; } - handle_v_run (own_buf, status, signal); + handle_v_run (own_buf); + return; + } + + if (strncmp (own_buf, "vKill;", 6) == 0) + { + if (!target_running ()) + { + fprintf (stderr, "No process to kill\n"); + write_enn (own_buf); + return; + } + handle_v_kill (own_buf); + return; + } + + if (strncmp (own_buf, "vStopped", 8) == 0) + { + handle_v_stopped (own_buf); return; } @@ -1190,34 +1488,109 @@ handle_v_requests (char *own_buf, char *status, int *signal, return; } +/* Resume inferior and wait for another event. In non-stop mode, + don't really wait here, but return immediatelly to the event + loop. */ void -myresume (char *own_buf, int step, int *signalp, char *statusp) +proceed (char *own_buf, int step, int siggnal) { struct thread_resume resume_info[2]; int n = 0; - int sig = *signalp; + int valid_cont_thread; set_desired_inferior (0); - if (step || sig || (cont_thread != 0 && cont_thread != -1)) + valid_cont_thread = (!ptid_equal (cont_thread, null_ptid) + && !ptid_equal (cont_thread, minus_one_ptid)); + + if (step || siggnal || valid_cont_thread) { resume_info[0].thread = ((struct inferior_list_entry *) current_inferior)->id; - resume_info[0].step = step; - resume_info[0].sig = sig; - resume_info[0].leave_stopped = 0; + if (step) + resume_info[0].kind = rk_step; + else + resume_info[0].kind = rk_continue; + resume_info[0].sig = siggnal; n++; } - resume_info[n].thread = -1; - resume_info[n].step = 0; - resume_info[n].sig = 0; - resume_info[n].leave_stopped = (cont_thread != 0 && cont_thread != -1); - enable_async_io (); - (*the_target->resume) (resume_info); - *signalp = mywait (statusp, 1); - prepare_resume_reply (own_buf, *statusp, *signalp); - disable_async_io (); + if (!valid_cont_thread) + { + resume_info[n].thread = minus_one_ptid; + resume_info[n].kind = rk_continue; + resume_info[n].sig = 0; + } + + if (!non_stop) + enable_async_io (); + + (*the_target->resume) (resume_info, n); + + if (non_stop) + write_ok (own_buf); + else + { + last_ptid = mywait (minus_one_ptid, &last_status, 0, 1); + prepare_resume_reply (own_buf, last_ptid, &last_status); + disable_async_io (); + } +} + +/* Callback for for_each_inferior. Make a new stop reply for each + stopped thread. */ +static int +queue_stop_reply_callback (struct inferior_list_entry *entry, void *arg) +{ + int pid = * (int *) arg; + + if (pid == -1 + || ptid_get_pid (entry->id) == pid) + { + struct target_waitstatus status; + + status.kind = TARGET_WAITKIND_STOPPED; + status.value.sig = TARGET_SIGNAL_TRAP; + + /* Pass the last stop reply back to GDB, but don't notify. */ + queue_stop_reply (entry->id, &status); + } + + return 0; +} + +/* Status handler for the '?' packet. */ + +static void +handle_status (char *own_buf) +{ + struct target_waitstatus status; + status.kind = TARGET_WAITKIND_STOPPED; + status.value.sig = TARGET_SIGNAL_TRAP; + + /* In non-stop mode, we must send a stop reply for each stopped + thread. In all-stop mode, just send one for the first stopped + thread we find. */ + + if (non_stop) + { + int pid = -1; + discard_queued_stop_replies (pid); + find_inferior (&all_threads, queue_stop_reply_callback, &pid); + + /* The first is sent immediatly. OK is sent if there is no + stopped thread, which is the same handling of the vStopped + packet (by design). */ + send_next_stop_reply (own_buf); + } + else + { + if (all_threads.head) + prepare_resume_reply (own_buf, + all_threads.head->id, &status); + else + strcpy (own_buf, "W00"); + } } static void @@ -1241,7 +1614,8 @@ gdbserver_usage (FILE *stream) "HOST:PORT to listen for a TCP connection.\n" "\n" "Options:\n" - " --debug\t\tEnable debugging output.\n" + " --debug\t\tEnable general debugging output.\n" + " --remote-debug\tEnable remote protocol debugging output.\n" " --version\t\tDisplay version information and exit.\n" " --wrapper WRAPPER --\tRun WRAPPER to start new programs.\n"); if (REPORT_BUGS_TO[0] && stream == stdout) @@ -1268,15 +1642,51 @@ gdbserver_show_disableable (FILE *stream) break; \ } +static int +first_thread_of (struct inferior_list_entry *entry, void *args) +{ + int pid = * (int *) args; + + if (ptid_get_pid (entry->id) == pid) + return 1; + + return 0; +} + +static void +kill_inferior_callback (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + int pid = ptid_get_pid (process->head.id); + + kill_inferior (pid); + discard_queued_stop_replies (pid); +} + +static void +detach_or_kill_inferior_callback (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + int pid = ptid_get_pid (process->head.id); + + if (process->attached) + detach_inferior (pid); + else + kill_inferior (pid); + + discard_queued_stop_replies (pid); +} + +static void +join_inferiors_callback (struct inferior_list_entry *entry) +{ + struct process_info *process = (struct process_info *) entry; + join_inferior (ptid_get_pid (process->head.id)); +} + int main (int argc, char *argv[]) { - char ch, status, *own_buf; - unsigned char *mem_buf; - int i = 0; - int signal; - unsigned int len; - CORE_ADDR mem_addr; int bad_attach; int pid; char *arg_end, *port; @@ -1320,6 +1730,8 @@ main (int argc, char *argv[]) } else if (strcmp (*next_arg, "--debug") == 0) debug_threads = 1; + else if (strcmp (*next_arg, "--remote-debug") == 0) + remote_debug = 1; else if (strcmp (*next_arg, "--disable-packet") == 0) { gdbserver_show_disableable (stdout); @@ -1409,6 +1821,7 @@ main (int argc, char *argv[]) exit (1); } + initialize_inferiors (); initialize_async_io (); initialize_low (); @@ -1426,7 +1839,7 @@ main (int argc, char *argv[]) program_argv[i] = NULL; /* Wait till we are at first instruction in program. */ - signal = start_inferior (program_argv, &status); + start_inferior (program_argv); /* We are now (hopefully) stopped at the first instruction of the target process. This assumes that the target process was @@ -1434,15 +1847,16 @@ main (int argc, char *argv[]) } else if (pid != 0) { - if (attach_inferior (pid, &status, &signal) == -1) + if (attach_inferior (pid) == -1) error ("Attaching not supported on this target"); /* Otherwise succeeded. */ } else { - status = 'W'; - signal = 0; + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.integer = 0; + last_ptid = minus_one_ptid; } /* Don't report shared library events on the initial connection, @@ -1452,12 +1866,14 @@ main (int argc, char *argv[]) if (setjmp (toplevel)) { - fprintf (stderr, "Killing inferior\n"); - kill_inferior (); + fprintf (stderr, "Killing all inferiors\n"); + for_each_inferior (&all_processes, + kill_inferior_callback); exit (1); } - if (status == 'W' || status == 'X') + if (last_status.kind == TARGET_WAITKIND_EXITED + || last_status.kind == TARGET_WAITKIND_SIGNALLED) was_running = 0; else was_running = 1; @@ -1471,9 +1887,11 @@ main (int argc, char *argv[]) while (1) { noack_mode = 0; + multi_process = 0; + non_stop = 0; + remote_open (port); - restart: if (setjmp (toplevel) != 0) { /* An error occurred. */ @@ -1484,8 +1902,49 @@ main (int argc, char *argv[]) } } + /* Wait for events. This will return when all event sources are + removed from the event loop. */ + start_event_loop (); + + /* If an exit was requested (using the "monitor exit" command), + terminate now. The only other way to get here is for + getpkt to fail; close the connection and reopen it at the + top of the loop. */ + + if (exit_requested) + { + for_each_inferior (&all_processes, + detach_or_kill_inferior_callback); + exit (0); + } + else + fprintf (stderr, "Remote side has terminated connection. " + "GDBserver will reopen the connection.\n"); + } +} + +/* Event loop callback that handles a serial event. The first byte in + the serial buffer gets us here. We expect characters to arrive at + a brisk pace, so we read the rest of the packet with a blocking + getpkt call. */ +static void +process_serial_event (void) +{ + char ch; + int i = 0; + int signal; + unsigned int len; + CORE_ADDR mem_addr; + int pid; + + /* Used to decide when gdbserver should exit in + multi-mode/remote. */ + static int have_ran = 0; + + if (!have_ran) + have_ran = target_running (); + disable_async_io (); - while (!exit_requested) { unsigned char sig; int packet_len; @@ -1494,7 +1953,11 @@ main (int argc, char *argv[]) response_needed = 0; packet_len = getpkt (own_buf); if (packet_len <= 0) - break; + { + target_async (0); + remote_close (); + return; + } response_needed = 1; i = 0; @@ -1509,29 +1972,46 @@ main (int argc, char *argv[]) break; case 'D': require_running (own_buf); - fprintf (stderr, "Detaching from inferior\n"); - if (detach_inferior () != 0) + + if (multi_process) + { + i++; /* skip ';' */ + pid = strtol (&own_buf[i], NULL, 16); + } + else + pid = ptid_get_pid (((struct inferior_list_entry *)current_inferior)->id); + + fprintf (stderr, "Detaching from process %d\n", pid); + if (detach_inferior (pid) != 0) write_enn (own_buf); else { + discard_queued_stop_replies (pid); write_ok (own_buf); if (extended_protocol) { /* Treat this like a normal program exit. */ - signal = 0; - status = 'W'; + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.integer = 0; + last_ptid = pid_to_ptid (pid); + + current_inferior = NULL; } else { putpkt (own_buf); remote_close (); - /* If we are attached, then we can exit. Otherwise, we - need to hang around doing nothing, until the child - is gone. */ - if (!attached) - join_inferior (); + /* Usually, if we are attached, then we can + exit. Otherwise, we need to hang around + doing nothing, until the child is gone. The + decision to join or not is left to the + target, as some targets may have to join even + if attached (e.g., Windows versions without + DebugSetProcessKillOnExit). */ + for_each_inferior (&all_processes, + join_inferiors_callback); exit (0); } @@ -1542,21 +2022,43 @@ main (int argc, char *argv[]) write_ok (own_buf); break; case '?': - prepare_resume_reply (own_buf, status, signal); + handle_status (own_buf); break; case 'H': if (own_buf[1] == 'c' || own_buf[1] == 'g' || own_buf[1] == 's') { - unsigned long gdb_id, thread_id; + ptid_t gdb_id, thread_id; + int pid; require_running (own_buf); - gdb_id = strtoul (&own_buf[2], NULL, 16); - if (gdb_id == 0 || gdb_id == -1) - thread_id = gdb_id; + + gdb_id = read_ptid (&own_buf[2], NULL); + + pid = ptid_get_pid (gdb_id); + + if (ptid_equal (gdb_id, null_ptid) + || ptid_equal (gdb_id, minus_one_ptid)) + thread_id = null_ptid; + else if (pid != 0 + && ptid_equal (pid_to_ptid (pid), + gdb_id)) + { + struct thread_info *thread = + (struct thread_info *) find_inferior (&all_threads, + first_thread_of, + &pid); + if (!thread) + { + write_enn (own_buf); + break; + } + + thread_id = ((struct inferior_list_entry *)thread)->id; + } else { thread_id = gdb_id_to_thread_id (gdb_id); - if (thread_id == 0) + if (ptid_equal (thread_id, null_ptid)) { write_enn (own_buf); break; @@ -1565,6 +2067,19 @@ main (int argc, char *argv[]) if (own_buf[1] == 'g') { + if (ptid_equal (thread_id, null_ptid)) + { + /* GDB is telling us to choose any thread. + Check if the currently selected thread is + still valid. If it is not, select the + first available. */ + struct thread_info *thread = + (struct thread_info *) find_inferior_id (&all_threads, + general_thread); + if (thread == NULL) + thread_id = all_threads.head->id; + } + general_thread = thread_id; set_desired_inferior (1); } @@ -1611,9 +2126,11 @@ main (int argc, char *argv[]) break; case 'X': require_running (own_buf); + + /* If len == 0, GDB is probing for packet support. */ if (decode_X_packet (&own_buf[1], packet_len - 1, &mem_addr, &len, mem_buf) < 0 - || write_inferior_memory (mem_addr, mem_buf, len) != 0) + || (len > 0 && write_inferior_memory (mem_addr, mem_buf, len) != 0)) write_enn (own_buf); else write_ok (own_buf); @@ -1625,7 +2142,7 @@ main (int argc, char *argv[]) signal = target_signal_to_host (sig); else signal = 0; - myresume (own_buf, 0, &signal, &status); + proceed (own_buf, 0, signal); break; case 'S': require_running (own_buf); @@ -1634,17 +2151,17 @@ main (int argc, char *argv[]) signal = target_signal_to_host (sig); else signal = 0; - myresume (own_buf, 1, &signal, &status); + proceed (own_buf, 1, signal); break; case 'c': require_running (own_buf); signal = 0; - myresume (own_buf, 0, &signal, &status); + proceed (own_buf, 0, signal); break; case 's': require_running (own_buf); signal = 0; - myresume (own_buf, 1, &signal, &status); + proceed (own_buf, 1, signal); break; case 'Z': { @@ -1713,20 +2230,19 @@ main (int argc, char *argv[]) if (!target_running ()) /* The packet we received doesn't make sense - but we can't reply to it, either. */ - goto restart; + return; - fprintf (stderr, "Killing inferior\n"); - kill_inferior (); + fprintf (stderr, "Killing all inferiors\n"); + for_each_inferior (&all_processes, kill_inferior_callback); /* When using the extended protocol, we wait with no program running. The traditional protocol will exit instead. */ if (extended_protocol) { - status = 'X'; - signal = TARGET_SIGNAL_KILL; - was_running = 0; - goto restart; + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.sig = TARGET_SIGNAL_KILL; + return; } else { @@ -1735,12 +2251,13 @@ main (int argc, char *argv[]) } case 'T': { - unsigned long gdb_id, thread_id; + ptid_t gdb_id, thread_id; require_running (own_buf); - gdb_id = strtoul (&own_buf[1], NULL, 16); + + gdb_id = read_ptid (&own_buf[1], NULL); thread_id = gdb_id_to_thread_id (gdb_id); - if (thread_id == 0) + if (ptid_equal (thread_id, null_ptid)) { write_enn (own_buf); break; @@ -1760,18 +2277,19 @@ main (int argc, char *argv[]) if (extended_protocol) { if (target_running ()) - kill_inferior (); + for_each_inferior (&all_processes, + kill_inferior_callback); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ if (program_argv != NULL) - signal = start_inferior (program_argv, &status); + start_inferior (program_argv); else { - status = 'X'; - signal = TARGET_SIGNAL_KILL; + last_status.kind = TARGET_WAITKIND_EXITED; + last_status.value.sig = TARGET_SIGNAL_KILL; } - goto restart; + return; } else { @@ -1783,8 +2301,7 @@ main (int argc, char *argv[]) } case 'v': /* Extended (long) request. */ - handle_v_requests (own_buf, &status, &signal, - packet_len, &new_packet_len); + handle_v_requests (own_buf, packet_len, &new_packet_len); break; default: @@ -1802,50 +2319,52 @@ main (int argc, char *argv[]) response_needed = 0; - if (was_running && (status == 'W' || status == 'X')) + if (!extended_protocol && have_ran && !target_running ()) { - was_running = 0; - - if (status == 'W') - fprintf (stderr, - "\nChild exited with status %d\n", signal); - if (status == 'X') - fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n", - target_signal_to_host (signal), - target_signal_to_name (signal)); - - if (extended_protocol) - goto restart; - else + /* In non-stop, defer exiting until GDB had a chance to + query the whole vStopped list (until it gets an + OK). */ + if (!notif_queue) { fprintf (stderr, "GDBserver exiting\n"); exit (0); } } - - if (status != 'W' && status != 'X') - was_running = 1; } +} - /* If an exit was requested (using the "monitor exit" command), - terminate now. The only other way to get here is for - getpkt to fail; close the connection and reopen it at the - top of the loop. */ +/* Event-loop callback for serial events. */ +void +handle_serial_event (int err, gdb_client_data client_data) +{ + if (debug_threads) + fprintf (stderr, "handling possible serial event\n"); - if (exit_requested) - { - remote_close (); - if (attached && target_running ()) - detach_inferior (); - else if (target_running ()) - kill_inferior (); - exit (0); - } - else - { - fprintf (stderr, "Remote side has terminated connection. " - "GDBserver will reopen the connection.\n"); - remote_close (); - } - } + /* Really handle it. */ + process_serial_event (); + + /* Be sure to not change the selected inferior behind GDB's back. + Important in the non-stop mode asynchronous protocol. */ + set_desired_inferior (1); +} + +/* Event-loop callback for the target events. */ +void +handle_target_event (int err, gdb_client_data client_data) +{ + if (debug_threads) + fprintf (stderr, "handling possible target event\n"); + + last_ptid = mywait (minus_one_ptid, &last_status, + TARGET_WNOHANG, 1); + + if (last_status.kind == TARGET_WAITKIND_IGNORE) + return; /* Nothing interesting found. */ + + /* Something interesting. Tell GDB about it. */ + push_event (last_ptid, &last_status); + + /* Be sure to not change the selected inferior behind GDB's back. + Important in the non-stop mode asynchronous protocol. */ + set_desired_inferior (1); } diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index 817b5c4..07a5cac 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -77,6 +77,70 @@ typedef unsigned char gdb_byte; least the size of a (void *). */ typedef long long CORE_ADDR; +typedef unsigned long long ULONGEST; + +/* The ptid struct is a collection of the various "ids" necessary + for identifying the inferior. This consists of the process id + (pid), thread id (tid), and other fields necessary for uniquely + identifying the inferior process/thread being debugged. When + manipulating ptids, the constructors, accessors, and predicate + declared in server.h should be used. These are as follows: + + ptid_build - Make a new ptid from a pid, lwp, and tid. + pid_to_ptid - Make a new ptid from just a pid. + ptid_get_pid - Fetch the pid component of a ptid. + ptid_get_lwp - Fetch the lwp component of a ptid. + ptid_get_tid - Fetch the tid component of a ptid. + ptid_equal - Test to see if two ptids are equal. + + Please do NOT access the struct ptid members directly (except, of + course, in the implementation of the above ptid manipulation + functions). */ + +struct ptid + { + /* Process id */ + int pid; + + /* Lightweight process id */ + long lwp; + + /* Thread id */ + long tid; + }; + +typedef struct ptid ptid_t; + +/* The -1 ptid, often used to indicate either an error condition or a + "don't care" condition, i.e, "run all threads". */ +extern ptid_t minus_one_ptid; + +/* The null or zero ptid, often used to indicate no process. */ +extern ptid_t null_ptid; + +/* Attempt to find and return an existing ptid with the given PID, + LWP, and TID components. If none exists, create a new one and + return that. */ +ptid_t ptid_build (int pid, long lwp, long tid); + +/* Create a ptid from just a pid. */ +ptid_t pid_to_ptid (int pid); + +/* Fetch the pid (process id) component from a ptid. */ +int ptid_get_pid (ptid_t ptid); + +/* Fetch the lwp (lightweight process) component from a ptid. */ +long ptid_get_lwp (ptid_t ptid); + +/* Fetch the tid (thread id) component from a ptid. */ +long ptid_get_tid (ptid_t ptid); + +/* Compare two ptids to see if they are equal. */ +extern int ptid_equal (ptid_t p1, ptid_t p2); + +/* Return true if this ptid represents a process id. */ +extern int ptid_is_pid (ptid_t ptid); + /* Generic information for tracking a list of ``inferiors'' - threads, processes, etc. */ struct inferior_list @@ -86,7 +150,7 @@ struct inferior_list }; struct inferior_list_entry { - unsigned long id; + ptid_t id; struct inferior_list_entry *next; }; @@ -100,6 +164,32 @@ struct dll_info CORE_ADDR base_addr; }; +struct sym_cache; +struct process_info_private; + +struct process_info +{ + struct inferior_list_entry head; + + int attached; + + /* The symbol cache. */ + struct sym_cache *symbol_cache; + + /* If this flag has been set, assume symbol cache misses are + failures. */ + int all_symbols_looked_up; + + /* Private target data. */ + struct process_info_private *private; +}; + +/* Return a pointer to the process that corresponds to the current + thread (current_inferior). It is an error to call this if there is + no current thread selected. */ + +extern struct process_info *current_process (void); + #include "regcache.h" #include "gdb/signals.h" @@ -112,22 +202,36 @@ void initialize_low (); /* From inferiors.c. */ +extern struct inferior_list all_processes; extern struct inferior_list all_threads; extern struct inferior_list all_dlls; extern int dlls_changed; +void initialize_inferiors (void); + void add_inferior_to_list (struct inferior_list *list, struct inferior_list_entry *new_inferior); void for_each_inferior (struct inferior_list *list, void (*action) (struct inferior_list_entry *)); + +#define FOR_EACH_INFERIOR(TYPE, INF_LIST, INF) \ + for (INF = (TYPE *)(INF_LIST)->head; \ + INF != NULL; \ + INF = (TYPE *)((struct inferior_list_entry *)INF)->next) \ + extern struct thread_info *current_inferior; void remove_inferior (struct inferior_list *list, struct inferior_list_entry *entry); void remove_thread (struct thread_info *thread); -void add_thread (unsigned long thread_id, void *target_data, unsigned int); -unsigned int thread_id_to_gdb_id (unsigned long); -unsigned int thread_to_gdb_id (struct thread_info *); -unsigned long gdb_id_to_thread_id (unsigned int); +void add_thread (ptid_t ptid, void *target_data); + +struct process_info *add_process (int pid, int attached); + +struct thread_info *find_thread_pid (ptid_t ptid); + +ptid_t thread_id_to_gdb_id (ptid_t); +ptid_t thread_to_gdb_id (struct thread_info *); +ptid_t gdb_id_to_thread_id (ptid_t); struct thread_info *gdb_id_to_thread (unsigned int); void clear_inferiors (void); struct inferior_list_entry *find_inferior @@ -136,7 +240,7 @@ struct inferior_list_entry *find_inferior void *), void *arg); struct inferior_list_entry *find_inferior_id (struct inferior_list *list, - unsigned long id); + ptid_t id); void *inferior_target_data (struct thread_info *); void set_inferior_target_data (struct thread_info *, void *); void *inferior_regcache_data (struct thread_info *); @@ -149,11 +253,10 @@ void unloaded_dll (const char *name, CORE_ADDR base_addr); /* Public variables in server.c */ -extern unsigned long cont_thread; -extern unsigned long general_thread; -extern unsigned long step_thread; -extern unsigned long thread_from_wait; -extern unsigned long old_thread_from_wait; +extern ptid_t cont_thread; +extern ptid_t general_thread; +extern ptid_t step_thread; + extern int server_waiting; extern int debug_threads; extern int pass_signals[]; @@ -165,6 +268,26 @@ extern int disable_packet_Tthread; extern int disable_packet_qC; extern int disable_packet_qfThreadInfo; +extern int multi_process; +extern int non_stop; + +/* Functions from event-loop.c. */ +typedef void *gdb_client_data; +typedef void (handler_func) (int, gdb_client_data); + +extern void delete_file_handler (int fd); +extern void add_file_handler (int fd, handler_func *proc, + gdb_client_data client_data); + +extern void start_event_loop (void); + +/* Functions from server.c. */ + +extern void handle_serial_event (int err, gdb_client_data client_data); +extern void handle_target_event (int err, gdb_client_data client_data); + +extern void push_event (ptid_t ptid, struct target_waitstatus *status); + /* Functions from hostio.c. */ extern int handle_vFile (char *, int, int *); @@ -178,8 +301,12 @@ extern int all_symbols_looked_up; extern int noack_mode; extern int transport_is_reliable; +ptid_t read_ptid (char *buf, char **obuf); +char *write_ptid (char *buf, ptid_t ptid); + int putpkt (char *buf); int putpkt_binary (char *buf, int len); +int putpkt_notif (char *buf); int getpkt (char *buf); void remote_open (char *name); void remote_close (void); @@ -193,7 +320,8 @@ void convert_ascii_to_int (char *from, unsigned char *to, int n); void convert_int_to_ascii (unsigned char *from, char *to, int n); void new_thread_notify (int id); void dead_thread_notify (int id); -void prepare_resume_reply (char *buf, char status, unsigned char sig); +void prepare_resume_reply (char *buf, ptid_t ptid, + struct target_waitstatus *status); const char *decode_address_to_semicolon (CORE_ADDR *addrp, const char *start); void decode_address (CORE_ADDR *addrp, const char *start, int len); @@ -236,6 +364,13 @@ void error (const char *string,...) ATTR_NORETURN ATTR_FORMAT (printf, 1, 2); void fatal (const char *string,...) ATTR_NORETURN ATTR_FORMAT (printf, 1, 2); void warning (const char *string,...) ATTR_FORMAT (printf, 1, 2); +void internal_error_file_line (const char *file, int line, + const char *string, ...) + ATTR_NORETURN ATTR_FORMAT (printf, 3, 4); + +#define internal_error(FMT, ...) \ + internal_error_file_line (__FILE__, __LINE__, FMT, ##__VA_ARGS__) + /* Maximum number of bytes to read/write at once. The value here is chosen to fill up a packet (the headers account for the 32). */ #define MAXBUFBYTES(N) (((N)-32)/2) diff --git a/gdb/gdbserver/target.c b/gdb/gdbserver/target.c index 86c62a3..ae54d0d 100644 --- a/gdb/gdbserver/target.c +++ b/gdb/gdbserver/target.c @@ -28,10 +28,7 @@ set_desired_inferior (int use_general) struct thread_info *found; if (use_general == 1) - { - found = (struct thread_info *) find_inferior_id (&all_threads, - general_thread); - } + found = find_thread_pid (general_thread); else { found = NULL; @@ -39,14 +36,14 @@ set_desired_inferior (int use_general) /* If we are continuing any (all) thread(s), use step_thread to decide which thread to step and/or send the specified signal to. */ - if ((step_thread != 0 && step_thread != -1) - && (cont_thread == 0 || cont_thread == -1)) - found = (struct thread_info *) find_inferior_id (&all_threads, - step_thread); + if ((!ptid_equal (step_thread, null_ptid) + && !ptid_equal (step_thread, minus_one_ptid)) + && (ptid_equal (cont_thread, null_ptid) + || ptid_equal (cont_thread, minus_one_ptid))) + found = find_thread_pid (step_thread); if (found == NULL) - found = (struct thread_info *) find_inferior_id (&all_threads, - cont_thread); + found = find_thread_pid (cont_thread); } if (found == NULL) @@ -87,15 +84,28 @@ write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, return res; } -unsigned char -mywait (char *statusp, int connected_wait) +ptid_t +mywait (ptid_t ptid, struct target_waitstatus *ourstatus, int options, + int connected_wait) { - unsigned char ret; + ptid_t ret; if (connected_wait) server_waiting = 1; - ret = (*the_target->wait) (statusp); + ret = (*the_target->wait) (ptid, ourstatus, options); + + if (ourstatus->kind == TARGET_WAITKIND_EXITED + || ourstatus->kind == TARGET_WAITKIND_SIGNALLED) + { + if (ourstatus->kind == TARGET_WAITKIND_EXITED) + fprintf (stderr, + "\nChild exited with status %d\n", ourstatus->value.sig); + if (ourstatus->kind == TARGET_WAITKIND_SIGNALLED) + fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n", + target_signal_to_host (ourstatus->value.sig), + target_signal_to_name (ourstatus->value.sig)); + } if (connected_wait) server_waiting = 0; @@ -103,9 +113,47 @@ mywait (char *statusp, int connected_wait) return ret; } +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 = (struct target_ops *) malloc (sizeof (*the_target)); memcpy (the_target, target, sizeof (*the_target)); } + +/* Convert pid to printable format. */ + +char * +target_pid_to_str (ptid_t ptid) +{ + static char buf[80]; + + if (ptid_equal (ptid, minus_one_ptid)) + snprintf (buf, sizeof (buf), "<all threads>"); + else if (ptid_equal (ptid, null_ptid)) + snprintf (buf, sizeof (buf), "<null thread>"); + else if (ptid_get_tid (ptid) != 0) + snprintf (buf, sizeof (buf), "Thread %d.0x%lx", + ptid_get_pid (ptid), ptid_get_tid (ptid)); + else if (ptid_get_lwp (ptid) != 0) + snprintf (buf, sizeof (buf), "LWP %d.%ld", + ptid_get_pid (ptid), ptid_get_lwp (ptid)); + else + snprintf (buf, sizeof (buf), "Process %d", + ptid_get_pid (ptid)); + + return buf; +} diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index 9f3b899..abce39d 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -22,25 +22,95 @@ #ifndef TARGET_H #define TARGET_H -/* This structure describes how to resume a particular thread (or - all threads) based on the client's request. If thread is -1, then - this entry applies to all threads. These are generally passed around - as an array, and terminated by a thread == -1 entry. */ +/* This structure describes how to resume a particular thread (or all + threads) based on the client's request. If thread is + minus_one_ptid, then this entry applies to all threads. If the + thread points to a whole process, the this entry applies to all + threads of that process. These are generally passed around as an + array, and terminated by a thread == minus_one_ptid entry. */ + +enum resume_kind +{ + /* Thread should be stopped. */ + rk_stop, + + /* Thread should continue. */ + rk_continue, + + /* Thread should single-step. */ + rk_step +}; struct thread_resume { - unsigned long thread; + ptid_t thread; - /* If non-zero, leave this thread stopped. */ - int leave_stopped; + enum resume_kind kind; - /* If non-zero, we want to single-step. */ - int step; - - /* If non-zero, send this signal when we resume. */ + /* If non-zero, send this signal when we resume, or to stop the + thread. If stopping a thread, and this is 0, the target should + stop the thread however it needs. */ int sig; }; +/* Generally, what has the program done? */ +enum target_waitkind + { + /* The program has exited. The exit status is in value.integer. */ + TARGET_WAITKIND_EXITED, + + /* The program has stopped with a signal. Which signal is in + value.sig. */ + TARGET_WAITKIND_STOPPED, + + /* The program has terminated with a signal. Which signal is in + value.sig. */ + TARGET_WAITKIND_SIGNALLED, + + /* The program is letting us know that it dynamically loaded something + (e.g. it called load(2) on AIX). */ + TARGET_WAITKIND_LOADED, + + /* The program has forked. A "related" process' PTID is in + value.related_pid. I.e., if the child forks, value.related_pid + is the parent's ID. */ + + TARGET_WAITKIND_FORKED, + + /* The program has vforked. A "related" process's PTID is in + value.related_pid. */ + + TARGET_WAITKIND_VFORKED, + + /* The program has exec'ed a new executable file. The new file's + pathname is pointed to by value.execd_pathname. */ + + TARGET_WAITKIND_EXECD, + + /* An event has occured, but we should wait again. In this case, + we want to go back to the event loop and wait there for another + event from the inferior. */ + TARGET_WAITKIND_IGNORE + }; + +struct target_waitstatus + { + enum target_waitkind kind; + + /* Forked child pid, execd pathname, exit status or signal number. */ + union + { + int integer; + enum target_signal sig; + ptid_t related_pid; + char *execd_pathname; + } + value; + }; + +/* Options that can be passed to target_ops->wait. */ +#define TARGET_WNOHANG 1 + struct target_ops { /* Start a new process. @@ -64,36 +134,36 @@ struct target_ops int (*attach) (unsigned long pid); - /* Kill all inferiors. */ + /* Kill inferior PID. Return -1 on failure, and 0 on success. */ - void (*kill) (void); + int (*kill) (int pid); - /* Detach from all inferiors. - Return -1 on failure, and 0 on success. */ + /* Detach from inferior PID. Return -1 on failure, and 0 on + success. */ - int (*detach) (void); + int (*detach) (int pid); - /* Wait for inferiors to end. */ + /* GDBServer is exiting, and may need to wait for inferior PID to + end. */ - void (*join) (void); + void (*join) (int pid); /* Return 1 iff the thread with process ID PID is alive. */ - int (*thread_alive) (unsigned long pid); + int (*thread_alive) (ptid_t pid); /* Resume the inferior process. */ - void (*resume) (struct thread_resume *resume_info); + void (*resume) (struct thread_resume *resume_info, size_t n); - /* Wait for the inferior process to change state. + /* Wait for the inferior process or thread to change state. - STATUS will be filled in with a response code to send to GDB. - - Returns the signal which caused the process to stop, in the - remote protocol numbering (e.g. TARGET_SIGNAL_STOP), or the - exit code as an integer if *STATUS is 'W'. */ - - unsigned char (*wait) (char *status); + PTID = -1 to wait for any pid to do something PTID(pid,0,0) to + wait for any thread of process pid to do something. Return ptid + of child, or -1 in case of error; store status through argument + pointer STATUS. OPTIONS is a bit set of options defined was + TARGET_W* above. */ + ptid_t (*wait) (ptid_t ptid, struct target_waitstatus *status, int options); /* Fetch registers from the inferior process. @@ -188,6 +258,25 @@ struct target_ops /* Fill BUF with an hostio error packet representing the last hostio error. */ void (*hostio_last_error) (char *buf); + + /* Attempts to find the pathname of the executable file + that was run to create a specified process. + + If the executable file cannot be determined, NULL is returned. + + Else, a pointer to a character string containing the pathname is + returned. This string should be copied into a buffer by the + client if the string will not be immediately used, or if it must + persist. */ + char* (*pid_to_exec_file) (int pid); + + /* Enables async target events. Returns the previous enable + state. */ + int (*async) (int enable); + + /* Switch to non-stop (1) or all-stop (0) mode. Return 0 on + success, -1 otherwise. */ + int (*start_non_stop) (int); }; extern struct target_ops *the_target; @@ -200,11 +289,11 @@ void set_target_ops (struct target_ops *); #define myattach(pid) \ (*the_target->attach) (pid) -#define kill_inferior() \ - (*the_target->kill) () +#define kill_inferior(pid) \ + (*the_target->kill) (pid) -#define detach_inferior() \ - (*the_target->detach) () +#define detach_inferior(pid) \ + (*the_target->detach) (pid) #define mythread_alive(pid) \ (*the_target->thread_alive) (pid) @@ -215,10 +304,18 @@ void set_target_ops (struct target_ops *); #define store_inferior_registers(regno) \ (*the_target->store_registers) (regno) -#define join_inferior() \ - (*the_target->join) () +#define join_inferior(pid) \ + (*the_target->join) (pid) + +#define target_async(enable) \ + (the_target->async ? (*the_target->async) (enable) : 0) -unsigned char mywait (char *statusp, int connected_wait); +/* Start non-stop mode, returns 0 on success, -1 on failure. */ + +int start_non_stop (int nonstop); + +ptid_t mywait (ptid_t ptid, struct target_waitstatus *ourstatus, int options, + int connected_wait); int read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len); @@ -227,4 +324,6 @@ int write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, void set_desired_inferior (int id); +char *target_pid_to_str (ptid_t); + #endif /* TARGET_H */ diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index 7690001..4962f96 100644 --- a/gdb/gdbserver/thread-db.c +++ b/gdb/gdbserver/thread-db.c @@ -35,15 +35,37 @@ static int thread_db_use_events; #include <stdint.h> -/* Structure that identifies the child process for the - <proc_service.h> interface. */ -static struct ps_prochandle proc_handle; +static int find_one_thread (ptid_t); +static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data); -/* Connection to the libthread_db library. */ -static td_thragent_t *thread_agent; +static void +fixup_proc_handle (void) +{ + ptid_t inferior_ptid; + struct process_info_private *proc = current_process ()->private; -static int find_one_thread (int); -static int find_new_threads_callback (const td_thrhandle_t *th_p, void *data); + /* FIXME drow/2004-10-16: This is the "overall process ID", which + GNU/Linux calls tgid, "thread group ID". When we support + attaching to threads, the original thread may not be the correct + thread. We would have to get the process ID from /proc for NPTL. + For LinuxThreads we could do something similar: follow the chain + of parent processes until we find the highest one we're attached + to, and use its tgid. + + This isn't the only place in gdbserver that assumes that the first + process in the list is the thread group leader. */ + + /* We're actually passing the current lwp to proc_services. This is + because in non-stop mode, the tgid process may be running at this + point --- linux doesn't allow reading memory through running + LWPs. At this point, I'm not sure if it's useful to actually + have a proc_handle.pid instead of just making proc_services + return current_inferior->id.pid directly. */ + + inferior_ptid = ((struct inferior_list_entry *)current_inferior)->id; + + proc->proc_handle.pid = ptid_get_lwp (inferior_ptid); +} static char * thread_db_err_str (td_err_e err) @@ -136,16 +158,18 @@ thread_db_create_event (CORE_ADDR where) { td_event_msg_t msg; td_err_e err; - struct process_info *process; + struct lwp_info *lwp; if (debug_threads) fprintf (stderr, "Thread creation event.\n"); + fixup_proc_handle (); + /* FIXME: This assumes we don't get another event. In the LinuxThreads implementation, this is safe, because all events come from the manager thread (except for its own creation, of course). */ - err = td_ta_event_getmsg (thread_agent, &msg); + err = td_ta_event_getmsg (current_process()->private->thread_agent, &msg); if (err != TD_OK) fprintf (stderr, "thread getmsg err: %s\n", thread_db_err_str (err)); @@ -153,9 +177,9 @@ thread_db_create_event (CORE_ADDR where) /* If we do not know about the main thread yet, this would be a good time to find it. We need to do this to pick up the main thread before any newly created threads. */ - process = get_thread_process (current_inferior); - if (process->thread_known == 0) - find_one_thread (process->lwpid); + lwp = get_thread_lwp (current_inferior); + if (lwp->thread_known == 0) + find_one_thread (lwp->head.id); /* msg.event == TD_EVENT_CREATE */ @@ -181,6 +205,7 @@ thread_db_enable_reporting () td_thr_events_t events; td_notify_t notify; td_err_e err; + struct process_info_private *proc = current_process()->private; /* Set the process wide mask saying which events we're interested in. */ td_event_emptyset (&events); @@ -192,7 +217,7 @@ thread_db_enable_reporting () td_event_addset (&events, TD_DEATH); #endif - err = td_ta_set_event (thread_agent, &events); + err = td_ta_set_event (proc->thread_agent, &events); if (err != TD_OK) { warning ("Unable to set global thread event mask: %s", @@ -201,13 +226,14 @@ thread_db_enable_reporting () } /* Get address for thread creation breakpoint. */ - err = td_ta_event_addr (thread_agent, TD_CREATE, ¬ify); + err = td_ta_event_addr (proc->thread_agent, TD_CREATE, ¬ify); if (err != TD_OK) { warning ("Unable to get location for thread creation breakpoint: %s", thread_db_err_str (err)); return 0; } + set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr, thread_db_create_event); @@ -216,7 +242,7 @@ thread_db_enable_reporting () with actual thread deaths (via wait). */ /* Get address for thread death breakpoint. */ - err = td_ta_event_addr (thread_agent, TD_DEATH, ¬ify); + err = td_ta_event_addr (proc->thread_agent, TD_DEATH, ¬ify); if (err != TD_OK) { warning ("Unable to get location for thread death breakpoint: %s", @@ -231,21 +257,22 @@ thread_db_enable_reporting () } static int -find_one_thread (int lwpid) +find_one_thread (ptid_t ptid) { td_thrhandle_t th; td_thrinfo_t ti; td_err_e err; struct thread_info *inferior; - struct process_info *process; + struct lwp_info *lwp; + int lwpid = ptid_get_lwp (ptid); - inferior = (struct thread_info *) find_inferior_id (&all_threads, lwpid); - process = get_thread_process (inferior); - if (process->thread_known) + inferior = (struct thread_info *) find_inferior_id (&all_threads, ptid); + lwp = get_thread_lwp (inferior); + if (lwp->thread_known) return 1; /* Get information about this thread. */ - err = td_ta_map_lwp2thr (thread_agent, process->lwpid, &th); + err = td_ta_map_lwp2thr (current_process()->private->thread_agent, lwpid, &th); if (err != TD_OK) error ("Cannot get thread handle for LWP %d: %s", lwpid, thread_db_err_str (err)); @@ -259,10 +286,10 @@ find_one_thread (int lwpid) fprintf (stderr, "Found thread %ld (LWP %d)\n", ti.ti_tid, ti.ti_lid); - if (process->lwpid != ti.ti_lid) + if (lwpid != ti.ti_lid) { warning ("PID mismatch! Expected %ld, got %ld", - (long) process->lwpid, (long) ti.ti_lid); + (long) lwpid, (long) ti.ti_lid); return 0; } @@ -279,9 +306,9 @@ find_one_thread (int lwpid) if (ti.ti_tid == 0) return 0; - process->thread_known = 1; - process->tid = ti.ti_tid; - process->th = th; + lwp->thread_known = 1; + lwp->tid = ti.ti_tid; + lwp->th = th; return 1; } @@ -290,32 +317,27 @@ static void maybe_attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p) { td_err_e err; - struct thread_info *inferior; - struct process_info *process; + struct lwp_info *lwp; - inferior = (struct thread_info *) find_inferior_id (&all_threads, - ti_p->ti_lid); - if (inferior != NULL) + lwp = find_lwp_pid (pid_to_ptid (ti_p->ti_lid)); + if (lwp != NULL) return; if (debug_threads) fprintf (stderr, "Attaching to thread %ld (LWP %d)\n", ti_p->ti_tid, ti_p->ti_lid); - linux_attach_lwp (ti_p->ti_lid); - inferior = (struct thread_info *) find_inferior_id (&all_threads, - ti_p->ti_lid); - if (inferior == NULL) + linux_attach_lwp (ti_p->ti_lid, 0); + lwp = find_lwp_pid (pid_to_ptid (ti_p->ti_lid)); + if (lwp == NULL) { warning ("Could not attach to thread %ld (LWP %d)\n", ti_p->ti_tid, ti_p->ti_lid); return; } - process = inferior_target_data (inferior); - - process->tid = ti_p->ti_tid; - process->thread_known = 1; - process->th = *th_p; + lwp->tid = ti_p->ti_tid; + lwp->thread_known = 1; + lwp->th = *th_p; if (thread_db_use_events) { @@ -349,15 +371,22 @@ static void thread_db_find_new_threads (void) { td_err_e err; + ptid_t ptid = ((struct inferior_list_entry *)current_inferior)->id; /* This function is only called when we first initialize thread_db. First locate the initial thread. If it is not ready for debugging yet, then stop. */ - if (find_one_thread (all_threads.head->id) == 0) + /* Is there any harm done in using the current inferior for + this? */ + if (find_one_thread (ptid) == 0) return; + /* Make sure we give thread_db the right lwp to talk to. */ + fixup_proc_handle (); + /* Iterate over all user-space threads to discover new threads. */ - err = td_ta_thr_iter (thread_agent, find_new_threads_callback, NULL, + err = td_ta_thr_iter (current_process()->private->thread_agent, + find_new_threads_callback, NULL, TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); if (err != TD_OK) @@ -386,18 +415,21 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset, #if HAVE_TD_THR_TLS_GET_ADDR psaddr_t addr; td_err_e err; - struct process_info *process; + struct lwp_info *lwp; + + /* Make sure we give thread_db the right lwp to talk to. */ + fixup_proc_handle (); - process = get_thread_process (thread); - if (!process->thread_known) - find_one_thread (process->lwpid); - if (!process->thread_known) + lwp = get_thread_lwp (thread); + if (!lwp->thread_known) + find_one_thread (lwp->head.id); + if (!lwp->thread_known) return TD_NOTHR; /* Note the cast through uintptr_t: this interface only works if a target address fits in a psaddr_t, which is a host pointer. So a 32-bit debugger can not access 64-bit TLS through this. */ - err = td_thr_tls_get_addr (&process->th, (psaddr_t) (uintptr_t) load_module, + err = td_thr_tls_get_addr (&lwp->th, (psaddr_t) (uintptr_t) load_module, offset, &addr); if (err == TD_OK) { @@ -415,25 +447,17 @@ int thread_db_init (int use_events) { int err; + struct process_info *proc = current_process (); + struct process_info_private *priv = proc->private; - /* FIXME drow/2004-10-16: This is the "overall process ID", which - GNU/Linux calls tgid, "thread group ID". When we support - attaching to threads, the original thread may not be the correct - thread. We would have to get the process ID from /proc for NPTL. - For LinuxThreads we could do something similar: follow the chain - of parent processes until we find the highest one we're attached - to, and use its tgid. - - This isn't the only place in gdbserver that assumes that the first - process in the list is the thread group leader. */ - proc_handle.pid = ((struct inferior_list_entry *)current_inferior)->id; + fixup_proc_handle (); /* Allow new symbol lookups. */ - all_symbols_looked_up = 0; + proc->all_symbols_looked_up = 0; thread_db_use_events = use_events; - err = td_ta_new (&proc_handle, &thread_agent); + err = td_ta_new (&priv->proc_handle, &priv->thread_agent); switch (err) { case TD_NOLIBTHREAD: @@ -447,7 +471,7 @@ thread_db_init (int use_events) return 0; thread_db_find_new_threads (); thread_db_look_up_symbols (); - all_symbols_looked_up = 1; + proc->all_symbols_looked_up = 1; return 1; default: diff --git a/gdb/gdbserver/utils.c b/gdb/gdbserver/utils.c index 88cc1e0..607bef2 100644 --- a/gdb/gdbserver/utils.c +++ b/gdb/gdbserver/utils.c @@ -96,3 +96,25 @@ warning (const char *string,...) fprintf (stderr, "\n"); va_end (args); } + +ATTR_NORETURN static void +internal_verror (const char *file, int line, const char *fmt, va_list ap) +{ + fprintf (stderr, "\ +%s:%d: \n\ +A problem internal to gdbserver has been detected,\n\ +further debugging may prove unreliable.", file, line); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "aborting.\n"); + + abort (); /* NOTE: gdbserver has only one call to abort(). */ +} + +void +internal_error_file_line (const char *file, int line, const char *string, ...) +{ + va_list ap; + va_start (ap, string); + internal_verror (file, line, string, ap); + va_end (ap); +} |