mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
gdb/gdbserver/
2010-06-01 Pedro Alves <pedro@codesourcery.com> Stan Shebs <stan@codesourcery.com> * Makefile.in (IPA_DEPFILES, extra_libraries): New. (all): Depend on $(extra_libraries). (install-only): Install the IPA. (IPA_OBJS, IPA_LIB): New. (clean): Remove the IPA lib. (IPAGENT_CFLAGS): New. (tracepoint-ipa.o, utils-ipa.o, remote-utils-ipa.o) (regcache-ipa.o, i386-linux-ipa.o, linux-i386-ipa.o) (linux-amd64-ipa.o, amd64-linux-ipa.o): New rules. * linux-amd64-ipa.c, linux-i386-ipa.c: New files. * configure.ac: Check for atomic builtins support in the compiler. (IPA_DEPFILES, extra_libraries): Define. * configure.srv (ipa_obj): Add description. (ipa_i386_linux_regobj, ipa_amd64_linux_regobj): Define. (i[34567]86-*-linux*): Set ipa_obj. (x86_64-*-linux*): Set ipa_obj. * linux-low.c (stabilizing_threads): New. (supports_fast_tracepoints): New. (linux_detach): Stabilize threads before detaching. (handle_tracepoints): Handle internal tracing breakpoints. Assert the lwp is either not stabilizing, or is moving out of a jump pad. (linux_fast_tracepoint_collecting): New. (maybe_move_out_of_jump_pad): New. (enqueue_one_deferred_signal): New. (dequeue_one_deferred_signal): New. (linux_wait_for_event_1): If moving out of a jump pad, defer pending signals to later. (linux_stabilize_threads): New. (linux_wait_1): Check if threads need moving out of jump pads, and do it if so. (stuck_in_jump_pad_callback): New. (move_out_of_jump_pad_callback): New. (lwp_running): New. (linux_resume_one_lwp): Handle moving out of jump pads. (linux_set_resume_request): Dequeue deferred signals. (need_step_over_p): Also step over fast tracepoint jumps. (start_step_over): Also uninsert fast tracepoint jumps. (finish_step_over): Also reinsert fast tracepoint jumps. (linux_install_fast_tracepoint_jump): New. (linux_target_ops): Install linux_stabilize_threads and linux_install_fast_tracepoint_jump_pad. * linux-low.h (linux_target_ops) <get_thread_area, install_fast_tracepoint_jump_pad>: New fields. (struct lwp_info) <collecting_fast_tracepoint, pending_signals_to_report, exit_jump_pad_bkpt>: New fields. (linux_get_thread_area): Declare. * linux-x86-low.c (jump_insn): New. (x86_get_thread_area): New. (append_insns): New. (push_opcode): New. (amd64_install_fast_tracepoint_jump_pad): New. (i386_install_fast_tracepoint_jump_pad): New. (x86_install_fast_tracepoint_jump_pad): New. (the_low_target): Install x86_get_thread_area and x86_install_fast_tracepoint_jump_pad. * mem-break.c (set_raw_breakpoint_at): Use read_inferior_memory. (struct fast_tracepoint_jump): New. (fast_tracepoint_jump_insn): New. (fast_tracepoint_jump_shadow): New. (find_fast_tracepoint_jump_at): New. (fast_tracepoint_jump_here): New. (delete_fast_tracepoint_jump): New. (set_fast_tracepoint_jump): New. (uninsert_fast_tracepoint_jumps_at): New. (reinsert_fast_tracepoint_jumps_at): New. (set_breakpoint_at): Use write_inferior_memory. (uninsert_raw_breakpoint): Use write_inferior_memory. (check_mem_read): Mask out fast tracepoint jumps. (check_mem_write): Mask out fast tracepoint jumps. * mem-break.h (struct fast_tracepoint_jump): Forward declare. (set_fast_tracepoint_jump): Declare. (delete_fast_tracepoint_jump) (fast_tracepoint_jump_here, uninsert_fast_tracepoint_jumps_at) (reinsert_fast_tracepoint_jumps_at): Declare. * regcache.c: Don't compile many functions when building the in-process agent library. (init_register_cache) [IN_PROCESS_AGENT]: Don't allow allocating the register buffer in the heap. (free_register_cache): If the register buffer isn't owned by the regcache, don't free it. (set_register_cache) [IN_PROCESS_AGENT]: Don't re-alocate pre-existing register caches. * remote-utils.c (convert_int_to_ascii): Constify `from' parameter type. (convert_ascii_to_int): : Constify `from' parameter type. (decode_M_packet, decode_X_packet): Replace the `to' parameter by a `to_p' pointer to pointer parameter. If TO_P is NULL, malloc the needed buffer in-place. (relocate_instruction): New. * server.c (handle_query) <qSymbols>: If the target supports tracepoints, give it a chance of looking up symbols. Report support for fast tracepoints. (handle_status): Stabilize threads. (process_serial_event): Adjust. * server.h (struct fast_tracepoint_jump): Forward declare. (struct process_info) <fast_tracepoint_jumps>: New field. (convert_ascii_to_int, convert_int_to_ascii): Adjust. (decode_X_packet, decode_M_packet): Adjust. (relocate_instruction): Declare. (in_process_agent_loaded): Declare. (tracepoint_look_up_symbols): Declare. (struct fast_tpoint_collect_status): Declare. (fast_tracepoint_collecting): Declare. (force_unlock_trace_buffer): Declare. (handle_tracepoint_bkpts): Declare. (initialize_low_tracepoint) (supply_fast_tracepoint_registers) [IN_PROCESS_AGENT]: Declare. * target.h (struct target_ops) <stabilize_threads, install_fast_tracepoint_jump_pad>: New fields. (stabilize_threads, install_fast_tracepoint_jump_pad): New. * tracepoint.c [HAVE_MALLOC_H]: Include malloc.h. [HAVE_STDINT_H]: Include stdint.h. (trace_debug_1): Rename to ... (trace_vdebug): ... this. (trace_debug): Rename to ... (trace_debug_1): ... this. Add `level' parameter. (trace_debug): New. (ATTR_USED, ATTR_NOINLINE): New. (IP_AGENT_EXPORT): New. (gdb_tp_heap_buffer, gdb_jump_pad_buffer, gdb_jump_pad_buffer_end) (collecting, gdb_collect, stop_tracing, flush_trace_buffer) (about_to_request_buffer_space, trace_buffer_is_full) (stopping_tracepoint, expr_eval_result, error_tracepoint) (tracepoints, tracing, trace_buffer_ctrl, trace_buffer_ctrl_curr) (trace_buffer_lo, trace_buffer_hi, traceframe_read_count) (traceframe_write_count, traceframes_created) (trace_state_variables) New renaming defines. (struct ipa_sym_addresses): New. (STRINGIZE_1, STRINGIZE, IPA_SYM): New. (symbol_list): New. (ipa_sym_addrs): New. (all_tracepoint_symbols_looked_up): New. (in_process_agent_loaded): New. (write_e_ipa_not_loaded): New. (maybe_write_ipa_not_loaded): New. (tracepoint_look_up_symbols): New. (debug_threads) [IN_PROCESS_AGENT]: New. (read_inferior_memory) [IN_PROCESS_AGENT]: New. (UNKNOWN_SIDE_EFFECTS): New. (stop_tracing): New. (flush_trace_buffer): New. (stop_tracing_bkpt): New. (flush_trace_buffer_bkpt): New. (read_inferior_integer): New. (read_inferior_uinteger): New. (read_inferior_data_pointer): New. (write_inferior_data_pointer): New. (write_inferior_integer): New. (write_inferior_uinteger): New. (struct collect_static_trace_data_action): Delete. (enum tracepoint_type): New. (struct tracepoint) <type>: New field `type'. <actions_str, step_actions, step_actions_str>: Only include in GDBserver. <orig_size, obj_addr_on_target, adjusted_insn_addr> <adjusted_insn_addr_end, jump_pad, jump_pad_end>: New fields. (tracepoints): Use IP_AGENT_EXPORT. (last_tracepoint): Don't include in the IPA. (stopping_tracepoint): Use IP_AGENT_EXPORT. (trace_buffer_is_full): Use IP_AGENT_EXPORT. (alloced_trace_state_variables): New. (trace_state_variables): Use IP_AGENT_EXPORT. (traceframe_t): Delete unused variable. (circular_trace_buffer): Don't include in the IPA. (trace_buffer_start): Delete. (struct trace_buffer_control): New. (trace_buffer_free): Delete. (struct ipa_trace_buffer_control): New. (GDBSERVER_FLUSH_COUNT_MASK, GDBSERVER_FLUSH_COUNT_MASK_PREV) (GDBSERVER_FLUSH_COUNT_MASK_CURR, GDBSERVER_UPDATED_FLUSH_COUNT_BIT): New. (trace_buffer_ctrl): New. (TRACE_BUFFER_CTRL_CURR): New. (trace_buffer_start, trace_buffer_free, trace_buffer_end_free): Reimplement as macros. (trace_buffer_wrap): Delete. (traceframe_write_count, traceframe_read_count) (traceframes_created, tracing): Use IP_AGENT_EXPORT. (struct tracepoint_hit_ctx) <type>: New field. (struct fast_tracepoint_ctx): New. (memory_barrier): New. (cmpxchg): New. (record_tracepoint_error): Update atomically in the IPA. (clear_inferior_trace_buffer): New. (about_to_request_buffer_space): New. (trace_buffer_alloc): Handle GDBserver and inferior simulatenous updating the same buffer. (add_tracepoint): Default the tracepoint's type to trap tracepoint, and orig_size to -1. (get_trace_state_variable) [IN_PROCESS_AGENT]: Handle allocated internal variables. (create_trace_state_variable): New parameter `gdb'. Handle it. (clear_installed_tracepoints): Clear fast tracepoint jumps. (cmd_qtdp): Handle fast tracepoints. (cmd_qtdv): Adjust. (max_jump_pad_size): New. (gdb_jump_pad_head): New. (get_jump_space_head): New. (claim_jump_space): New. (sort_tracepoints): New. (MAX_JUMP_SIZE): New. (cmd_qtstart): Handle fast tracepoints. Sync tracepoints with the IPA. (stop_tracing) [IN_PROCESS_AGENT]: Don't include the tdisconnected support. Upload fast traceframes, and delete internal IPA breakpoints. (stop_tracing_handler): New. (flush_trace_buffer_handler): New. (cmd_qtstop): Upload fast tracepoints. (response_tracepoint): Handle fast tracepoints. (tracepoint_finished_step): Upload fast traceframes. Set the tracepoint hit context's tracepoint type. (handle_tracepoint_bkpts): New. (tracepoint_was_hit): Set the tracepoint hit context's tracepoint type. Add comment about fast tracepoints. (collect_data_at_tracepoint) [IN_PROCESS_AGENT]: Don't access the non-existing action_str field. (get_context_regcache): Handle fast tracepoints. (do_action_at_tracepoint) [!IN_PROCESS_AGENT]: Don't write the PC to the regcache. (fast_tracepoint_from_jump_pad_address): New. (fast_tracepoint_from_ipa_tpoint_address): New. (collecting_t): New. (force_unlock_trace_buffer): New. (fast_tracepoint_collecting): New. (collecting): New. (gdb_collect): New. (write_inferior_data_ptr): New. (target_tp_heap): New. (target_malloc): New. (download_agent_expr): New. (UALIGN): New. (download_tracepoints): New. (download_trace_state_variables): New. (upload_fast_traceframes): New. (IPA_FIRST_TRACEFRAME): New. (IPA_NEXT_TRACEFRAME_1): New. (IPA_NEXT_TRACEFRAME): New. [IN_PROCESS_AGENT]: Include sys/mman.h and fcntl.h. [IN_PROCESS_AGENT] (gdb_tp_heap_buffer, gdb_jump_pad_buffer) (gdb_jump_pad_buffer_end): New. [IN_PROCESS_AGENT] (initialize_tracepoint_ftlib): New. (initialize_tracepoint): Adjust. [IN_PROCESS_AGENT]: Allocate the IPA heap, and jump pad scratch buffer. Initialize the low module. * utils.c (PREFIX, TOOLNAME): New. (malloc_failure): Use PREFIX. (error): In the IPA, an error causes an exit. (fatal, warning): Use PREFIX. (internal_error): Use TOOLNAME. (NUMCELLS): Increase to 10. * configure, config.in: Regenerate. gdb/ 2010-06-01 Pedro Alves <pedro@codesourcery.com> * NEWS: Mention gdbserver fast tracepoints support. gdb/doc/ 2010-06-01 Pedro Alves <pedro@codesourcery.com> * gdb.texinfo (Set Tracepoints): Mention tracepoints support in gdbserver, and add cross reference. (Tracepoints support in gdbserver): New subsection.
This commit is contained in:
parent
d149dd1dab
commit
fa593d66d5
@ -1,3 +1,7 @@
|
||||
2010-06-01 Pedro Alves <pedro@codesourcery.com>
|
||||
|
||||
* NEWS: Mention gdbserver fast tracepoints support.
|
||||
|
||||
2010-05-31 Pierre Muller <muller@ics.u-strasbg.fr>
|
||||
|
||||
* windows-nat.c (GetConsoleFontSize, GetCurrentConsoleFont):
|
||||
|
6
gdb/NEWS
6
gdb/NEWS
@ -33,8 +33,10 @@ qRelocInsn
|
||||
|
||||
* New features in the GDB remote stub, GDBserver
|
||||
|
||||
- GDBserver now support tracepoints. The feature is currently
|
||||
supported by the i386-linux and amd64-linux builds.
|
||||
- GDBserver now support tracepoints (including fast tracepoints).
|
||||
The feature is currently supported by the i386-linux and
|
||||
amd64-linux builds. See the "Tracepoints support in gdbserver"
|
||||
section in the manual for more information.
|
||||
|
||||
- GDBserver now supports x86_64 Windows 64-bit debugging.
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
2010-06-01 Pedro Alves <pedro@codesourcery.com>
|
||||
|
||||
* gdb.texinfo (Set Tracepoints): Mention tracepoints support in
|
||||
gdbserver, and add cross reference.
|
||||
(Tracepoints support in gdbserver): New subsection.
|
||||
|
||||
2010-05-26 Pedro Alves <pedro@codesourcery.com>
|
||||
|
||||
* gdb.texinfo (General Query Packets) <qSupported>: Describe the
|
||||
|
@ -9440,6 +9440,9 @@ Some targets may support @dfn{fast tracepoints}, which are inserted in
|
||||
a different way (such as with a jump instead of a trap), that is
|
||||
faster but possibly restricted in where they may be installed.
|
||||
|
||||
@code{gdbserver} supports tracepoints on some target systems.
|
||||
@xref{Server,,Tracepoints support in @code{gdbserver}}.
|
||||
|
||||
This section describes commands to set tracepoints and associated
|
||||
conditions and actions.
|
||||
|
||||
@ -15692,6 +15695,82 @@ of a multi-process mode debug session.
|
||||
|
||||
@end table
|
||||
|
||||
@subsection Tracepoints support in @code{gdbserver}
|
||||
@cindex tracepoints support in @code{gdbserver}
|
||||
|
||||
On some targets, @code{gdbserver} supports tracepoints and fast
|
||||
tracepoints.
|
||||
|
||||
For fast tracepoints to work, a special library called the
|
||||
@dfn{in-process agent} (IPA), must be loaded in the inferior process.
|
||||
This library is built and distributed as an integral part of
|
||||
@code{gdbserver}.
|
||||
|
||||
There are several ways to load the in-process agent in your program:
|
||||
|
||||
@table @code
|
||||
@item Specifying it as dependency at link time
|
||||
|
||||
You can link your program dynamically with the in-process agent
|
||||
library. On most systems, this is accomplished by adding
|
||||
@code{-linproctrace} to the link command.
|
||||
|
||||
@item Using the system's preloading mechanisms
|
||||
|
||||
You can force loading the in-process agent at startup time by using
|
||||
your system's support for preloading shared libraries. Many Unixes
|
||||
support the concept of preloading user defined libraries. In most
|
||||
cases, you do that by specifying @code{LD_PRELOAD=libinproctrace.so}
|
||||
in the environment. See also the description of @code{gdbserver}'s
|
||||
@option{--wrapper} command line option.
|
||||
|
||||
@item Using @value{GDBN} to force loading the agent at run time
|
||||
|
||||
On some systems, you can force the inferior to load a shared library,
|
||||
by calling a dynamic loader function in the inferior that takes care
|
||||
of dynamically looking up and loading a shared library. On most Unix
|
||||
systems, the function is @code{dlopen}. You'll use the @code{call}
|
||||
command for that. For example:
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) call dlopen ("libinproctrace.so", ...)
|
||||
@end smallexample
|
||||
|
||||
Note that on most Unix systems, for the @code{dlopen} function to be
|
||||
available, the program needs to be linked with @code{-ldl}.
|
||||
@end table
|
||||
|
||||
On systems that have a userspace dynamic loader, like most Unix
|
||||
systems, when you connect to @code{gdbserver} using @code{target
|
||||
remote}, you'll find that the program is stopped at the dynamic
|
||||
loader's entry point, and no shared library has been loaded in the
|
||||
program's address space yet, including the in-process agent. In that
|
||||
case, before being able to use any of the fast tracepoints features,
|
||||
you need to let the loader run and load the shared libraries. The
|
||||
most simple way to do that is to run the program to the main
|
||||
procedure. E.g., if debugging a C or C@t{++} program, start
|
||||
@code{gdbserver} like so:
|
||||
|
||||
@smallexample
|
||||
$ gdbserver :9999 myprogram
|
||||
@end smallexample
|
||||
|
||||
Start GDB and connect to @code{gdbserver} like so, and run to main:
|
||||
|
||||
@smallexample
|
||||
$ gdb myprogram
|
||||
(@value{GDBP}) target remote myhost:9999
|
||||
0x00007f215893ba60 in ?? () from /lib64/ld-linux-x86-64.so.2
|
||||
(@value{GDBP}) b main
|
||||
(@value{GDBP}) continue
|
||||
@end smallexample
|
||||
|
||||
The in-process tracing agent library should now be loaded into the
|
||||
process; you can confirm it with the @code{info sharedlibrary}
|
||||
command, which will list @file{libinproctrace.so} as loaded in the
|
||||
process. You are now ready to install fast tracepoints and start
|
||||
tracing.
|
||||
|
||||
@node Remote Configuration
|
||||
@section Remote Configuration
|
||||
|
||||
|
@ -1,3 +1,259 @@
|
||||
2010-06-01 Pedro Alves <pedro@codesourcery.com>
|
||||
Stan Shebs <stan@codesourcery.com>
|
||||
|
||||
* Makefile.in (IPA_DEPFILES, extra_libraries): New.
|
||||
(all): Depend on $(extra_libraries).
|
||||
(install-only): Install the IPA.
|
||||
(IPA_OBJS, IPA_LIB): New.
|
||||
(clean): Remove the IPA lib.
|
||||
(IPAGENT_CFLAGS): New.
|
||||
(tracepoint-ipa.o, utils-ipa.o, remote-utils-ipa.o)
|
||||
(regcache-ipa.o, i386-linux-ipa.o, linux-i386-ipa.o)
|
||||
(linux-amd64-ipa.o, amd64-linux-ipa.o): New rules.
|
||||
* linux-amd64-ipa.c, linux-i386-ipa.c: New files.
|
||||
* configure.ac: Check for atomic builtins support in the compiler.
|
||||
(IPA_DEPFILES, extra_libraries): Define.
|
||||
* configure.srv (ipa_obj): Add description.
|
||||
(ipa_i386_linux_regobj, ipa_amd64_linux_regobj): Define.
|
||||
(i[34567]86-*-linux*): Set ipa_obj.
|
||||
(x86_64-*-linux*): Set ipa_obj.
|
||||
* linux-low.c (stabilizing_threads): New.
|
||||
(supports_fast_tracepoints): New.
|
||||
(linux_detach): Stabilize threads before detaching.
|
||||
(handle_tracepoints): Handle internal tracing breakpoints. Assert
|
||||
the lwp is either not stabilizing, or is moving out of a jump pad.
|
||||
(linux_fast_tracepoint_collecting): New.
|
||||
(maybe_move_out_of_jump_pad): New.
|
||||
(enqueue_one_deferred_signal): New.
|
||||
(dequeue_one_deferred_signal): New.
|
||||
(linux_wait_for_event_1): If moving out of a jump pad, defer
|
||||
pending signals to later.
|
||||
(linux_stabilize_threads): New.
|
||||
(linux_wait_1): Check if threads need moving out of jump pads, and
|
||||
do it if so.
|
||||
(stuck_in_jump_pad_callback): New.
|
||||
(move_out_of_jump_pad_callback): New.
|
||||
(lwp_running): New.
|
||||
(linux_resume_one_lwp): Handle moving out of jump pads.
|
||||
(linux_set_resume_request): Dequeue deferred signals.
|
||||
(need_step_over_p): Also step over fast tracepoint jumps.
|
||||
(start_step_over): Also uninsert fast tracepoint jumps.
|
||||
(finish_step_over): Also reinsert fast tracepoint jumps.
|
||||
(linux_install_fast_tracepoint_jump): New.
|
||||
(linux_target_ops): Install linux_stabilize_threads and
|
||||
linux_install_fast_tracepoint_jump_pad.
|
||||
* linux-low.h (linux_target_ops) <get_thread_area,
|
||||
install_fast_tracepoint_jump_pad>: New fields.
|
||||
(struct lwp_info) <collecting_fast_tracepoint,
|
||||
pending_signals_to_report, exit_jump_pad_bkpt>: New fields.
|
||||
(linux_get_thread_area): Declare.
|
||||
* linux-x86-low.c (jump_insn): New.
|
||||
(x86_get_thread_area): New.
|
||||
(append_insns): New.
|
||||
(push_opcode): New.
|
||||
(amd64_install_fast_tracepoint_jump_pad): New.
|
||||
(i386_install_fast_tracepoint_jump_pad): New.
|
||||
(x86_install_fast_tracepoint_jump_pad): New.
|
||||
(the_low_target): Install x86_get_thread_area and
|
||||
x86_install_fast_tracepoint_jump_pad.
|
||||
* mem-break.c (set_raw_breakpoint_at): Use read_inferior_memory.
|
||||
(struct fast_tracepoint_jump): New.
|
||||
(fast_tracepoint_jump_insn): New.
|
||||
(fast_tracepoint_jump_shadow): New.
|
||||
(find_fast_tracepoint_jump_at): New.
|
||||
(fast_tracepoint_jump_here): New.
|
||||
(delete_fast_tracepoint_jump): New.
|
||||
(set_fast_tracepoint_jump): New.
|
||||
(uninsert_fast_tracepoint_jumps_at): New.
|
||||
(reinsert_fast_tracepoint_jumps_at): New.
|
||||
(set_breakpoint_at): Use write_inferior_memory.
|
||||
(uninsert_raw_breakpoint): Use write_inferior_memory.
|
||||
(check_mem_read): Mask out fast tracepoint jumps.
|
||||
(check_mem_write): Mask out fast tracepoint jumps.
|
||||
* mem-break.h (struct fast_tracepoint_jump): Forward declare.
|
||||
(set_fast_tracepoint_jump): Declare.
|
||||
(delete_fast_tracepoint_jump)
|
||||
(fast_tracepoint_jump_here, uninsert_fast_tracepoint_jumps_at)
|
||||
(reinsert_fast_tracepoint_jumps_at): Declare.
|
||||
* regcache.c: Don't compile many functions when building the
|
||||
in-process agent library.
|
||||
(init_register_cache) [IN_PROCESS_AGENT]: Don't allow allocating
|
||||
the register buffer in the heap.
|
||||
(free_register_cache): If the register buffer isn't owned by the
|
||||
regcache, don't free it.
|
||||
(set_register_cache) [IN_PROCESS_AGENT]: Don't re-alocate
|
||||
pre-existing register caches.
|
||||
* remote-utils.c (convert_int_to_ascii): Constify `from' parameter
|
||||
type.
|
||||
(convert_ascii_to_int): : Constify `from' parameter type.
|
||||
(decode_M_packet, decode_X_packet): Replace the `to' parameter by
|
||||
a `to_p' pointer to pointer parameter. If TO_P is NULL, malloc
|
||||
the needed buffer in-place.
|
||||
(relocate_instruction): New.
|
||||
* server.c (handle_query) <qSymbols>: If the target supports
|
||||
tracepoints, give it a chance of looking up symbols. Report
|
||||
support for fast tracepoints.
|
||||
(handle_status): Stabilize threads.
|
||||
(process_serial_event): Adjust.
|
||||
* server.h (struct fast_tracepoint_jump): Forward declare.
|
||||
(struct process_info) <fast_tracepoint_jumps>: New field.
|
||||
(convert_ascii_to_int, convert_int_to_ascii): Adjust.
|
||||
(decode_X_packet, decode_M_packet): Adjust.
|
||||
(relocate_instruction): Declare.
|
||||
(in_process_agent_loaded): Declare.
|
||||
(tracepoint_look_up_symbols): Declare.
|
||||
(struct fast_tpoint_collect_status): Declare.
|
||||
(fast_tracepoint_collecting): Declare.
|
||||
(force_unlock_trace_buffer): Declare.
|
||||
(handle_tracepoint_bkpts): Declare.
|
||||
(initialize_low_tracepoint)
|
||||
(supply_fast_tracepoint_registers) [IN_PROCESS_AGENT]: Declare.
|
||||
* target.h (struct target_ops) <stabilize_threads,
|
||||
install_fast_tracepoint_jump_pad>: New fields.
|
||||
(stabilize_threads, install_fast_tracepoint_jump_pad): New.
|
||||
* tracepoint.c [HAVE_MALLOC_H]: Include malloc.h.
|
||||
[HAVE_STDINT_H]: Include stdint.h.
|
||||
(trace_debug_1): Rename to ...
|
||||
(trace_vdebug): ... this.
|
||||
(trace_debug): Rename to ...
|
||||
(trace_debug_1): ... this. Add `level' parameter.
|
||||
(trace_debug): New.
|
||||
(ATTR_USED, ATTR_NOINLINE): New.
|
||||
(IP_AGENT_EXPORT): New.
|
||||
(gdb_tp_heap_buffer, gdb_jump_pad_buffer, gdb_jump_pad_buffer_end)
|
||||
(collecting, gdb_collect, stop_tracing, flush_trace_buffer)
|
||||
(about_to_request_buffer_space, trace_buffer_is_full)
|
||||
(stopping_tracepoint, expr_eval_result, error_tracepoint)
|
||||
(tracepoints, tracing, trace_buffer_ctrl, trace_buffer_ctrl_curr)
|
||||
(trace_buffer_lo, trace_buffer_hi, traceframe_read_count)
|
||||
(traceframe_write_count, traceframes_created)
|
||||
(trace_state_variables)
|
||||
New renaming defines.
|
||||
(struct ipa_sym_addresses): New.
|
||||
(STRINGIZE_1, STRINGIZE, IPA_SYM): New.
|
||||
(symbol_list): New.
|
||||
(ipa_sym_addrs): New.
|
||||
(all_tracepoint_symbols_looked_up): New.
|
||||
(in_process_agent_loaded): New.
|
||||
(write_e_ipa_not_loaded): New.
|
||||
(maybe_write_ipa_not_loaded): New.
|
||||
(tracepoint_look_up_symbols): New.
|
||||
(debug_threads) [IN_PROCESS_AGENT]: New.
|
||||
(read_inferior_memory) [IN_PROCESS_AGENT]: New.
|
||||
(UNKNOWN_SIDE_EFFECTS): New.
|
||||
(stop_tracing): New.
|
||||
(flush_trace_buffer): New.
|
||||
(stop_tracing_bkpt): New.
|
||||
(flush_trace_buffer_bkpt): New.
|
||||
(read_inferior_integer): New.
|
||||
(read_inferior_uinteger): New.
|
||||
(read_inferior_data_pointer): New.
|
||||
(write_inferior_data_pointer): New.
|
||||
(write_inferior_integer): New.
|
||||
(write_inferior_uinteger): New.
|
||||
(struct collect_static_trace_data_action): Delete.
|
||||
(enum tracepoint_type): New.
|
||||
(struct tracepoint) <type>: New field `type'.
|
||||
<actions_str, step_actions, step_actions_str>: Only include in GDBserver.
|
||||
<orig_size, obj_addr_on_target, adjusted_insn_addr>
|
||||
<adjusted_insn_addr_end, jump_pad, jump_pad_end>: New fields.
|
||||
(tracepoints): Use IP_AGENT_EXPORT.
|
||||
(last_tracepoint): Don't include in the IPA.
|
||||
(stopping_tracepoint): Use IP_AGENT_EXPORT.
|
||||
(trace_buffer_is_full): Use IP_AGENT_EXPORT.
|
||||
(alloced_trace_state_variables): New.
|
||||
(trace_state_variables): Use IP_AGENT_EXPORT.
|
||||
(traceframe_t): Delete unused variable.
|
||||
(circular_trace_buffer): Don't include in the IPA.
|
||||
(trace_buffer_start): Delete.
|
||||
(struct trace_buffer_control): New.
|
||||
(trace_buffer_free): Delete.
|
||||
(struct ipa_trace_buffer_control): New.
|
||||
(GDBSERVER_FLUSH_COUNT_MASK, GDBSERVER_FLUSH_COUNT_MASK_PREV)
|
||||
(GDBSERVER_FLUSH_COUNT_MASK_CURR, GDBSERVER_UPDATED_FLUSH_COUNT_BIT):
|
||||
New.
|
||||
(trace_buffer_ctrl): New.
|
||||
(TRACE_BUFFER_CTRL_CURR): New.
|
||||
(trace_buffer_start, trace_buffer_free, trace_buffer_end_free):
|
||||
Reimplement as macros.
|
||||
(trace_buffer_wrap): Delete.
|
||||
(traceframe_write_count, traceframe_read_count)
|
||||
(traceframes_created, tracing): Use IP_AGENT_EXPORT.
|
||||
(struct tracepoint_hit_ctx) <type>: New field.
|
||||
(struct fast_tracepoint_ctx): New.
|
||||
(memory_barrier): New.
|
||||
(cmpxchg): New.
|
||||
(record_tracepoint_error): Update atomically in the IPA.
|
||||
(clear_inferior_trace_buffer): New.
|
||||
(about_to_request_buffer_space): New.
|
||||
(trace_buffer_alloc): Handle GDBserver and inferior simulatenous
|
||||
updating the same buffer.
|
||||
(add_tracepoint): Default the tracepoint's type to trap
|
||||
tracepoint, and orig_size to -1.
|
||||
(get_trace_state_variable) [IN_PROCESS_AGENT]: Handle allocated
|
||||
internal variables.
|
||||
(create_trace_state_variable): New parameter `gdb'. Handle it.
|
||||
(clear_installed_tracepoints): Clear fast tracepoint jumps.
|
||||
(cmd_qtdp): Handle fast tracepoints.
|
||||
(cmd_qtdv): Adjust.
|
||||
(max_jump_pad_size): New.
|
||||
(gdb_jump_pad_head): New.
|
||||
(get_jump_space_head): New.
|
||||
(claim_jump_space): New.
|
||||
(sort_tracepoints): New.
|
||||
(MAX_JUMP_SIZE): New.
|
||||
(cmd_qtstart): Handle fast tracepoints. Sync tracepoints with the
|
||||
IPA.
|
||||
(stop_tracing) [IN_PROCESS_AGENT]: Don't include the tdisconnected
|
||||
support. Upload fast traceframes, and delete internal IPA
|
||||
breakpoints.
|
||||
(stop_tracing_handler): New.
|
||||
(flush_trace_buffer_handler): New.
|
||||
(cmd_qtstop): Upload fast tracepoints.
|
||||
(response_tracepoint): Handle fast tracepoints.
|
||||
(tracepoint_finished_step): Upload fast traceframes. Set the
|
||||
tracepoint hit context's tracepoint type.
|
||||
(handle_tracepoint_bkpts): New.
|
||||
(tracepoint_was_hit): Set the tracepoint hit context's tracepoint
|
||||
type. Add comment about fast tracepoints.
|
||||
(collect_data_at_tracepoint) [IN_PROCESS_AGENT]: Don't access the
|
||||
non-existing action_str field.
|
||||
(get_context_regcache): Handle fast tracepoints.
|
||||
(do_action_at_tracepoint) [!IN_PROCESS_AGENT]: Don't write the PC
|
||||
to the regcache.
|
||||
(fast_tracepoint_from_jump_pad_address): New.
|
||||
(fast_tracepoint_from_ipa_tpoint_address): New.
|
||||
(collecting_t): New.
|
||||
(force_unlock_trace_buffer): New.
|
||||
(fast_tracepoint_collecting): New.
|
||||
(collecting): New.
|
||||
(gdb_collect): New.
|
||||
(write_inferior_data_ptr): New.
|
||||
(target_tp_heap): New.
|
||||
(target_malloc): New.
|
||||
(download_agent_expr): New.
|
||||
(UALIGN): New.
|
||||
(download_tracepoints): New.
|
||||
(download_trace_state_variables): New.
|
||||
(upload_fast_traceframes): New.
|
||||
(IPA_FIRST_TRACEFRAME): New.
|
||||
(IPA_NEXT_TRACEFRAME_1): New.
|
||||
(IPA_NEXT_TRACEFRAME): New.
|
||||
[IN_PROCESS_AGENT]: Include sys/mman.h and fcntl.h.
|
||||
[IN_PROCESS_AGENT] (gdb_tp_heap_buffer, gdb_jump_pad_buffer)
|
||||
(gdb_jump_pad_buffer_end): New.
|
||||
[IN_PROCESS_AGENT] (initialize_tracepoint_ftlib): New.
|
||||
(initialize_tracepoint): Adjust.
|
||||
[IN_PROCESS_AGENT]: Allocate the IPA heap, and jump pad scratch
|
||||
buffer. Initialize the low module.
|
||||
* utils.c (PREFIX, TOOLNAME): New.
|
||||
(malloc_failure): Use PREFIX.
|
||||
(error): In the IPA, an error causes an exit.
|
||||
(fatal, warning): Use PREFIX.
|
||||
(internal_error): Use TOOLNAME.
|
||||
(NUMCELLS): Increase to 10.
|
||||
* configure, config.in: Regenerate.
|
||||
|
||||
2010-06-01 Pedro Alves <pedro@codesourcery.com>
|
||||
|
||||
* server.c (handle_query) <qSupported>: Do two passes over the
|
||||
|
@ -141,12 +141,15 @@ XML_DIR = $(srcdir)/../features
|
||||
XML_FILES = @srv_xmlfiles@
|
||||
XML_BUILTIN = @srv_xmlbuiltin@
|
||||
|
||||
IPA_DEPFILES = @IPA_DEPFILES@
|
||||
extra_libraries = @extra_libraries@
|
||||
|
||||
# Prevent Sun make from putting in the machine type. Setting
|
||||
# TARGET_ARCH to nothing works for SunOS 3, 4.0, but not for 4.1.
|
||||
.c.o:
|
||||
${CC} -c ${INTERNAL_CFLAGS} $<
|
||||
|
||||
all: gdbserver$(EXEEXT) gdbreplay$(EXEEXT)
|
||||
all: gdbserver$(EXEEXT) gdbreplay$(EXEEXT) $(extra_libraries)
|
||||
|
||||
# Traditionally "install" depends on "all". But it may be useful
|
||||
# not to; for example, if the user has made some trivial change to a
|
||||
@ -157,6 +160,10 @@ install: all install-only
|
||||
install-only:
|
||||
n=`echo gdbserver | sed '$(program_transform_name)'`; \
|
||||
if [ x$$n = x ]; then n=gdbserver; else true; fi; \
|
||||
if [ x$IPA_DEPFILES != x ]; then \
|
||||
$(SHELL) $(srcdir)/../../mkinstalldirs $(DESTDIR)$(libdir); \
|
||||
$(INSTALL_PROGRAM) $(IPA_LIB) $(DESTDIR)$(libdir)/$(IPA_LIB); \
|
||||
fi; \
|
||||
$(SHELL) $(srcdir)/../../mkinstalldirs $(DESTDIR)$(bindir); \
|
||||
$(INSTALL_PROGRAM) gdbserver$(EXEEXT) $(DESTDIR)$(bindir)/$$n$(EXEEXT); \
|
||||
$(SHELL) $(srcdir)/../../mkinstalldirs $(DESTDIR)$(man1dir); \
|
||||
@ -186,6 +193,15 @@ gdbreplay$(EXEEXT): $(GDBREPLAY_OBS)
|
||||
${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $(GDBREPLAY_OBS) \
|
||||
$(XM_CLIBS)
|
||||
|
||||
IPA_OBJS=tracepoint-ipa.o utils-ipa.o regcache-ipa.o ${IPA_DEPFILES}
|
||||
|
||||
IPA_LIB=libinproctrace.so
|
||||
|
||||
$(IPA_LIB): $(IPA_OBJS) ${ADD_DEPS} ${CDEPS}
|
||||
rm -f $(IPA_LIB)
|
||||
${CC-LD} -shared -fPIC -Wl,--no-undefined $(INTERNAL_CFLAGS) \
|
||||
$(INTERNAL_LDFLAGS) -o $(IPA_LIB) ${IPA_OBJS}
|
||||
|
||||
# Put the proper machine-specific files first, so M-. on a machine
|
||||
# specific routine gets the one for the correct machine.
|
||||
# The xyzzy stuff below deals with empty DEPFILES
|
||||
@ -205,6 +221,7 @@ clean:
|
||||
rm -f *.o ${ADD_FILES} *~
|
||||
rm -f version.c
|
||||
rm -f gdbserver$(EXEEXT) gdbreplay$(EXEEXT) core make.log
|
||||
rm -f $(IPA_LIB)
|
||||
rm -f reg-arm.c i386.c reg-ia64.c reg-m32r.c reg-m68k.c
|
||||
rm -f reg-sh.c reg-sparc.c reg-spu.c amd64.c i386-linux.c
|
||||
rm -f reg-cris.c reg-crisv32.c amd64-linux.c reg-xtensa.c
|
||||
@ -278,6 +295,30 @@ linux_low_h = $(srcdir)/linux-low.h
|
||||
|
||||
nto_low_h = $(srcdir)/nto-low.h
|
||||
|
||||
# Note, we only build the IPA if -fvisibility=hidden is supported in
|
||||
# the first place.
|
||||
IPAGENT_CFLAGS = $(CPPFLAGS) $(INTERNAL_CFLAGS) \
|
||||
-fPIC -DGDBSERVER -DIN_PROCESS_AGENT \
|
||||
-fvisibility=hidden
|
||||
|
||||
# In-process agent object rules
|
||||
tracepoint-ipa.o: tracepoint.c $(server_h)
|
||||
$(CC) -c $(IPAGENT_CFLAGS) $< -o tracepoint-ipa.o
|
||||
utils-ipa.o: utils.c $(server_h)
|
||||
$(CC) -c $(IPAGENT_CFLAGS) $< -o utils-ipa.o
|
||||
remote-utils-ipa.o: remote-utils.c $(server_h)
|
||||
$(CC) -c $(IPAGENT_CFLAGS) $< -o remote-utils-ipa.o
|
||||
regcache-ipa.o: regcache.c $(server_h)
|
||||
$(CC) -c $(IPAGENT_CFLAGS) $< -o regcache-ipa.o
|
||||
i386-linux-ipa.o : i386-linux.c $(regdef_h)
|
||||
$(CC) -c $(IPAGENT_CFLAGS) $< -o i386-linux-ipa.o
|
||||
linux-i386-ipa.o: linux-i386-ipa.c $(server_h)
|
||||
$(CC) -c $(IPAGENT_CFLAGS) $< -o linux-i386-ipa.o
|
||||
linux-amd64-ipa.o: linux-amd64-ipa.c $(server_h)
|
||||
$(CC) -c $(IPAGENT_CFLAGS) $< -o linux-amd64-ipa.o
|
||||
amd64-linux-ipa.o : amd64-linux.c $(regdef_h)
|
||||
$(CC) -c $(IPAGENT_CFLAGS) $< -o amd64-linux-ipa.o
|
||||
|
||||
event-loop.o: event-loop.c $(server_h)
|
||||
hostio.o: hostio.c $(server_h)
|
||||
hostio-errno.o: hostio-errno.c $(server_h)
|
||||
|
@ -112,6 +112,9 @@
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if the target supports __sync_*_compare_and_swap */
|
||||
#undef HAVE_SYNC_BUILTINS
|
||||
|
||||
/* Define to 1 if you have the <sys/file.h> header file. */
|
||||
#undef HAVE_SYS_FILE_H
|
||||
|
||||
|
69
gdb/gdbserver/configure
vendored
69
gdb/gdbserver/configure
vendored
@ -590,6 +590,8 @@ ac_includes_default="\
|
||||
#endif"
|
||||
|
||||
ac_subst_vars='LTLIBOBJS
|
||||
extra_libraries
|
||||
IPA_DEPFILES
|
||||
srv_xmlfiles
|
||||
srv_xmlbuiltin
|
||||
USE_THREAD_DB
|
||||
@ -4468,6 +4470,73 @@ fi
|
||||
GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_hostio_err_objs $srv_thread_depfiles"
|
||||
GDBSERVER_LIBS="$srv_libs"
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the target supports __sync_*_compare_and_swap" >&5
|
||||
$as_echo_n "checking whether the target supports __sync_*_compare_and_swap... " >&6; }
|
||||
if test "${gdbsrv_cv_have_sync_builtins+set}" = set; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int foo, bar; bar = __sync_val_compare_and_swap(&foo, 0, 1);
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
gdbsrv_cv_have_sync_builtins=yes
|
||||
else
|
||||
gdbsrv_cv_have_sync_builtins=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdbsrv_cv_have_sync_builtins" >&5
|
||||
$as_echo "$gdbsrv_cv_have_sync_builtins" >&6; }
|
||||
if test $gdbsrv_cv_have_sync_builtins = yes; then
|
||||
|
||||
$as_echo "#define HAVE_SYNC_BUILTINS 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
saved_cflags="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -fvisibility=hidden"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_compile "$LINENO"; then :
|
||||
gdbsrv_cv_have_visibility_hidden=yes
|
||||
else
|
||||
gdbsrv_cv_have_visibility_hidden=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
CFLAGS="$saved_cflags"
|
||||
|
||||
IPA_DEPFILES=""
|
||||
|
||||
# Rather than allowing to build a broken IPA, we simply disable it if
|
||||
# we don't find a compiler supporting all the features we need.
|
||||
if test "$ipa_obj" != "" \
|
||||
-a "$gdbsrv_cv_have_sync_builtins" = yes \
|
||||
-a "$gdbsrv_cv_have_visibility_hidden" = yes; then
|
||||
IPA_DEPFILES="$ipa_obj"
|
||||
extra_libraries="libinproctrace.so"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -245,11 +245,43 @@ fi
|
||||
GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_hostio_err_objs $srv_thread_depfiles"
|
||||
GDBSERVER_LIBS="$srv_libs"
|
||||
|
||||
dnl Check whether the target supports __sync_*_compare_and_swap.
|
||||
AC_CACHE_CHECK([whether the target supports __sync_*_compare_and_swap],
|
||||
gdbsrv_cv_have_sync_builtins, [
|
||||
AC_TRY_LINK([], [int foo, bar; bar = __sync_val_compare_and_swap(&foo, 0, 1);],
|
||||
gdbsrv_cv_have_sync_builtins=yes,
|
||||
gdbsrv_cv_have_sync_builtins=no)])
|
||||
if test $gdbsrv_cv_have_sync_builtins = yes; then
|
||||
AC_DEFINE(HAVE_SYNC_BUILTINS, 1,
|
||||
[Define to 1 if the target supports __sync_*_compare_and_swap])
|
||||
fi
|
||||
|
||||
dnl Check for -fvisibility=hidden support in the compiler.
|
||||
saved_cflags="$CFLAGS"
|
||||
CFLAGS="$CFLAGS -fvisibility=hidden"
|
||||
AC_COMPILE_IFELSE(AC_LANG_PROGRAM([]),
|
||||
[gdbsrv_cv_have_visibility_hidden=yes],
|
||||
[gdbsrv_cv_have_visibility_hidden=no])
|
||||
CFLAGS="$saved_cflags"
|
||||
|
||||
IPA_DEPFILES=""
|
||||
|
||||
# Rather than allowing to build a broken IPA, we simply disable it if
|
||||
# we don't find a compiler supporting all the features we need.
|
||||
if test "$ipa_obj" != "" \
|
||||
-a "$gdbsrv_cv_have_sync_builtins" = yes \
|
||||
-a "$gdbsrv_cv_have_visibility_hidden" = yes; then
|
||||
IPA_DEPFILES="$ipa_obj"
|
||||
extra_libraries="libinproctrace.so"
|
||||
fi
|
||||
|
||||
AC_SUBST(GDBSERVER_DEPFILES)
|
||||
AC_SUBST(GDBSERVER_LIBS)
|
||||
AC_SUBST(USE_THREAD_DB)
|
||||
AC_SUBST(srv_xmlbuiltin)
|
||||
AC_SUBST(srv_xmlfiles)
|
||||
AC_SUBST(IPA_DEPFILES)
|
||||
AC_SUBST(extra_libraries)
|
||||
|
||||
AC_OUTPUT(Makefile,
|
||||
[case x$CONFIG_HEADERS in
|
||||
|
@ -10,6 +10,8 @@
|
||||
# target method.
|
||||
# srv_xmlfiles All XML files which should be available for
|
||||
# gdbserver in this configuration.
|
||||
# ipa_obj Any other target-specific modules appropriate
|
||||
# for this target's in-process agent.
|
||||
#
|
||||
# In addition, on GNU/Linux the following shell variables will be set:
|
||||
# srv_linux_regsets Set to "yes" if ptrace(PTRACE_GETREGS) and friends
|
||||
@ -27,6 +29,9 @@ srv_i386_linux_regobj="i386-linux.o i386-avx-linux.o i386-mmx-linux.o"
|
||||
srv_amd64_regobj="amd64.o amd64-avx.o"
|
||||
srv_amd64_linux_regobj="amd64-linux.o amd64-avx-linux.o"
|
||||
|
||||
ipa_i386_linux_regobj=i386-linux-ipa.o
|
||||
ipa_amd64_linux_regobj=amd64-linux-ipa.o
|
||||
|
||||
srv_i386_32bit_xmlfiles="i386/32bit-core.xml i386/32bit-sse.xml i386/32bit-avx.xml"
|
||||
srv_i386_64bit_xmlfiles="i386/64bit-core.xml i386/64bit-sse.xml i386/64bit-avx.xml"
|
||||
srv_i386_xmlfiles="i386/i386.xml i386/i386-avx.xml i386/i386-mmx.xml $srv_i386_32bit_xmlfiles"
|
||||
@ -86,6 +91,7 @@ case "${target}" in
|
||||
srv_linux_usrregs=yes
|
||||
srv_linux_regsets=yes
|
||||
srv_linux_thread_db=yes
|
||||
ipa_obj="${ipa_i386_linux_regobj} linux-i386-ipa.o"
|
||||
;;
|
||||
i[34567]86-*-mingw32ce*)
|
||||
srv_regobj="$srv_i386_regobj"
|
||||
@ -230,6 +236,7 @@ case "${target}" in
|
||||
srv_linux_usrregs=yes # This is for i386 progs.
|
||||
srv_linux_regsets=yes
|
||||
srv_linux_thread_db=yes
|
||||
ipa_obj="${ipa_amd64_linux_regobj} linux-amd64-ipa.o"
|
||||
;;
|
||||
x86_64-*-mingw*) srv_regobj="$srv_amd64_regobj"
|
||||
srv_tgtobj="i386-low.o i387-fp.o win32-low.o win32-i386-low.o"
|
||||
|
77
gdb/gdbserver/linux-amd64-ipa.c
Normal file
77
gdb/gdbserver/linux-amd64-ipa.c
Normal file
@ -0,0 +1,77 @@
|
||||
/* GNU/Linux/x86-64 specific low level interface, for the in-process
|
||||
agent library for GDB.
|
||||
|
||||
Copyright (C) 2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "server.h"
|
||||
|
||||
/* Defined in auto-generated file amd64-linux.c. */
|
||||
void init_registers_amd64_linux (void);
|
||||
|
||||
/* fast tracepoints collect registers. */
|
||||
|
||||
#define FT_CR_RIP 0
|
||||
#define FT_CR_EFLAGS 1
|
||||
#define FT_CR_R8 2
|
||||
#define FT_CR_R9 3
|
||||
#define FT_CR_R10 4
|
||||
#define FT_CR_R11 5
|
||||
#define FT_CR_R12 6
|
||||
#define FT_CR_R13 7
|
||||
#define FT_CR_R14 8
|
||||
#define FT_CR_R15 9
|
||||
#define FT_CR_RAX 10
|
||||
#define FT_CR_RBX 11
|
||||
#define FT_CR_RCX 12
|
||||
#define FT_CR_RDX 13
|
||||
#define FT_CR_RSI 14
|
||||
#define FT_CR_RDI 15
|
||||
#define FT_CR_RBP 16
|
||||
#define FT_CR_RSP 17
|
||||
|
||||
static const int x86_64_ft_collect_regmap[] = {
|
||||
FT_CR_RAX * 8, FT_CR_RBX * 8, FT_CR_RCX * 8, FT_CR_RDX * 8,
|
||||
FT_CR_RSI * 8, FT_CR_RDI * 8, FT_CR_RBP * 8, FT_CR_RSP * 8,
|
||||
FT_CR_R8 * 8, FT_CR_R9 * 8, FT_CR_R10 * 8, FT_CR_R11 * 8,
|
||||
FT_CR_R12 * 8, FT_CR_R13 * 8, FT_CR_R14 * 8, FT_CR_R15 * 8,
|
||||
FT_CR_RIP * 8, FT_CR_EFLAGS * 8
|
||||
};
|
||||
|
||||
#define X86_64_NUM_FT_COLLECT_GREGS \
|
||||
(sizeof (x86_64_ft_collect_regmap) / sizeof(x86_64_ft_collect_regmap[0]))
|
||||
|
||||
void
|
||||
supply_fast_tracepoint_registers (struct regcache *regcache,
|
||||
const unsigned char *buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < X86_64_NUM_FT_COLLECT_GREGS; i++)
|
||||
supply_register (regcache, i,
|
||||
((char *) buf) + x86_64_ft_collect_regmap[i]);
|
||||
}
|
||||
|
||||
/* This is only needed because reg-i386-linux-lib.o references it. We
|
||||
may use it proper at some point. */
|
||||
const char *gdbserver_xmltarget;
|
||||
|
||||
void
|
||||
initialize_low_tracepoint (void)
|
||||
{
|
||||
init_registers_amd64_linux ();
|
||||
}
|
106
gdb/gdbserver/linux-i386-ipa.c
Normal file
106
gdb/gdbserver/linux-i386-ipa.c
Normal file
@ -0,0 +1,106 @@
|
||||
/* GNU/Linux/x86 specific low level interface, for the in-process
|
||||
agent library for GDB.
|
||||
|
||||
Copyright (C) 2010 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GDB.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "server.h"
|
||||
|
||||
/* GDB register numbers. */
|
||||
|
||||
enum i386_gdb_regnum
|
||||
{
|
||||
I386_EAX_REGNUM, /* %eax */
|
||||
I386_ECX_REGNUM, /* %ecx */
|
||||
I386_EDX_REGNUM, /* %edx */
|
||||
I386_EBX_REGNUM, /* %ebx */
|
||||
I386_ESP_REGNUM, /* %esp */
|
||||
I386_EBP_REGNUM, /* %ebp */
|
||||
I386_ESI_REGNUM, /* %esi */
|
||||
I386_EDI_REGNUM, /* %edi */
|
||||
I386_EIP_REGNUM, /* %eip */
|
||||
I386_EFLAGS_REGNUM, /* %eflags */
|
||||
I386_CS_REGNUM, /* %cs */
|
||||
I386_SS_REGNUM, /* %ss */
|
||||
I386_DS_REGNUM, /* %ds */
|
||||
I386_ES_REGNUM, /* %es */
|
||||
I386_FS_REGNUM, /* %fs */
|
||||
I386_GS_REGNUM, /* %gs */
|
||||
I386_ST0_REGNUM /* %st(0) */
|
||||
};
|
||||
|
||||
#define i386_num_regs 16
|
||||
|
||||
/* Defined in auto-generated file i386-linux.c. */
|
||||
void init_registers_i386_linux (void);
|
||||
|
||||
#define FT_CR_EAX 15
|
||||
#define FT_CR_ECX 14
|
||||
#define FT_CR_EDX 13
|
||||
#define FT_CR_EBX 12
|
||||
#define FT_CR_UESP 11
|
||||
#define FT_CR_EBP 10
|
||||
#define FT_CR_ESI 9
|
||||
#define FT_CR_EDI 8
|
||||
#define FT_CR_EIP 7
|
||||
#define FT_CR_EFL 6
|
||||
#define FT_CR_DS 5
|
||||
#define FT_CR_ES 4
|
||||
#define FT_CR_FS 3
|
||||
#define FT_CR_GS 2
|
||||
#define FT_CR_SS 1
|
||||
#define FT_CR_CS 0
|
||||
|
||||
/* Mapping between the general-purpose registers in jump tracepoint
|
||||
format and GDB's register array layout. */
|
||||
|
||||
static const int i386_ft_collect_regmap[] =
|
||||
{
|
||||
FT_CR_EAX * 4, FT_CR_ECX * 4, FT_CR_EDX * 4, FT_CR_EBX * 4,
|
||||
FT_CR_UESP * 4, FT_CR_EBP * 4, FT_CR_ESI * 4, FT_CR_EDI * 4,
|
||||
FT_CR_EIP * 4, FT_CR_EFL * 4, FT_CR_CS * 4, FT_CR_SS * 4,
|
||||
FT_CR_DS * 4, FT_CR_ES * 4, FT_CR_FS * 4, FT_CR_GS * 4
|
||||
};
|
||||
|
||||
void
|
||||
supply_fast_tracepoint_registers (struct regcache *regcache,
|
||||
const unsigned char *buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < i386_num_regs; i++)
|
||||
{
|
||||
int regval;
|
||||
|
||||
if (i >= I386_CS_REGNUM && i <= I386_GS_REGNUM)
|
||||
regval = *(short *) (((char *) buf) + i386_ft_collect_regmap[i]);
|
||||
else
|
||||
regval = *(int *) (((char *) buf) + i386_ft_collect_regmap[i]);
|
||||
|
||||
supply_register (regcache, i, ®val);
|
||||
}
|
||||
}
|
||||
|
||||
/* This is only needed because reg-i386-linux-lib.o references it. We
|
||||
may use it proper at some point. */
|
||||
const char *gdbserver_xmltarget;
|
||||
|
||||
void
|
||||
initialize_low_tracepoint (void)
|
||||
{
|
||||
init_registers_i386_linux ();
|
||||
}
|
@ -127,6 +127,10 @@ int stopping_threads;
|
||||
/* FIXME make into a target method? */
|
||||
int using_threads = 1;
|
||||
|
||||
/* True if we're presently stabilizing threads (moving them out of
|
||||
jump pads). */
|
||||
static int stabilizing_threads;
|
||||
|
||||
/* 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
|
||||
@ -170,6 +174,16 @@ supports_breakpoints (void)
|
||||
return (the_low_target.get_pc != NULL);
|
||||
}
|
||||
|
||||
/* Returns true if this target can support fast tracepoints. This
|
||||
does not mean that the in-process agent has been loaded in the
|
||||
inferior. */
|
||||
|
||||
static int
|
||||
supports_fast_tracepoints (void)
|
||||
{
|
||||
return the_low_target.install_fast_tracepoint_jump_pad != NULL;
|
||||
}
|
||||
|
||||
struct pending_signals
|
||||
{
|
||||
int signal;
|
||||
@ -846,6 +860,9 @@ linux_detach (int pid)
|
||||
thread_db_detach (process);
|
||||
#endif
|
||||
|
||||
/* Stabilize threads (move out of jump pads). */
|
||||
stabilize_threads ();
|
||||
|
||||
find_inferior (&all_threads, linux_detach_one_lwp, &pid);
|
||||
|
||||
the_target->mourn (process);
|
||||
@ -1127,6 +1144,8 @@ handle_tracepoints (struct lwp_info *lwp)
|
||||
/* Do any necessary step collect actions. */
|
||||
tpoint_related_event |= tracepoint_finished_step (tinfo, lwp->stop_pc);
|
||||
|
||||
tpoint_related_event |= handle_tracepoint_bkpts (tinfo, lwp->stop_pc);
|
||||
|
||||
/* See if we just hit a tracepoint and do its main collect
|
||||
actions. */
|
||||
tpoint_related_event |= tracepoint_was_hit (tinfo, lwp->stop_pc);
|
||||
@ -1134,6 +1153,7 @@ handle_tracepoints (struct lwp_info *lwp)
|
||||
lwp->suspended--;
|
||||
|
||||
gdb_assert (lwp->suspended == 0);
|
||||
gdb_assert (!stabilizing_threads || lwp->collecting_fast_tracepoint);
|
||||
|
||||
if (tpoint_related_event)
|
||||
{
|
||||
@ -1145,6 +1165,231 @@ handle_tracepoints (struct lwp_info *lwp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convenience wrapper. Returns true if LWP is presently collecting a
|
||||
fast tracepoint. */
|
||||
|
||||
static int
|
||||
linux_fast_tracepoint_collecting (struct lwp_info *lwp,
|
||||
struct fast_tpoint_collect_status *status)
|
||||
{
|
||||
CORE_ADDR thread_area;
|
||||
|
||||
if (the_low_target.get_thread_area == NULL)
|
||||
return 0;
|
||||
|
||||
/* Get the thread area address. This is used to recognize which
|
||||
thread is which when tracing with the in-process agent library.
|
||||
We don't read anything from the address, and treat it as opaque;
|
||||
it's the address itself that we assume is unique per-thread. */
|
||||
if ((*the_low_target.get_thread_area) (lwpid_of (lwp), &thread_area) == -1)
|
||||
return 0;
|
||||
|
||||
return fast_tracepoint_collecting (thread_area, lwp->stop_pc, status);
|
||||
}
|
||||
|
||||
/* The reason we resume in the caller, is because we want to be able
|
||||
to pass lwp->status_pending as WSTAT, and we need to clear
|
||||
status_pending_p before resuming, otherwise, linux_resume_one_lwp
|
||||
refuses to resume. */
|
||||
|
||||
static int
|
||||
maybe_move_out_of_jump_pad (struct lwp_info *lwp, int *wstat)
|
||||
{
|
||||
struct thread_info *saved_inferior;
|
||||
|
||||
saved_inferior = current_inferior;
|
||||
current_inferior = get_lwp_thread (lwp);
|
||||
|
||||
if ((wstat == NULL
|
||||
|| (WIFSTOPPED (*wstat) && WSTOPSIG (*wstat) != SIGTRAP))
|
||||
&& supports_fast_tracepoints ()
|
||||
&& in_process_agent_loaded ())
|
||||
{
|
||||
struct fast_tpoint_collect_status status;
|
||||
int r;
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "\
|
||||
Checking whether LWP %ld needs to move out of the jump pad.\n",
|
||||
lwpid_of (lwp));
|
||||
|
||||
r = linux_fast_tracepoint_collecting (lwp, &status);
|
||||
|
||||
if (wstat == NULL
|
||||
|| (WSTOPSIG (*wstat) != SIGILL
|
||||
&& WSTOPSIG (*wstat) != SIGFPE
|
||||
&& WSTOPSIG (*wstat) != SIGSEGV
|
||||
&& WSTOPSIG (*wstat) != SIGBUS))
|
||||
{
|
||||
lwp->collecting_fast_tracepoint = r;
|
||||
|
||||
if (r != 0)
|
||||
{
|
||||
if (r == 1 && lwp->exit_jump_pad_bkpt == NULL)
|
||||
{
|
||||
/* Haven't executed the original instruction yet.
|
||||
Set breakpoint there, and wait till it's hit,
|
||||
then single-step until exiting the jump pad. */
|
||||
lwp->exit_jump_pad_bkpt
|
||||
= set_breakpoint_at (status.adjusted_insn_addr, NULL);
|
||||
}
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "\
|
||||
Checking whether LWP %ld needs to move out of the jump pad...it does\n",
|
||||
lwpid_of (lwp));
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we get a synchronous signal while collecting, *and*
|
||||
while executing the (relocated) original instruction,
|
||||
reset the PC to point at the tpoint address, before
|
||||
reporting to GDB. Otherwise, it's an IPA lib bug: just
|
||||
report the signal to GDB, and pray for the best. */
|
||||
|
||||
lwp->collecting_fast_tracepoint = 0;
|
||||
|
||||
if (r != 0
|
||||
&& (status.adjusted_insn_addr <= lwp->stop_pc
|
||||
&& lwp->stop_pc < status.adjusted_insn_addr_end))
|
||||
{
|
||||
siginfo_t info;
|
||||
struct regcache *regcache;
|
||||
|
||||
/* The si_addr on a few signals references the address
|
||||
of the faulting instruction. Adjust that as
|
||||
well. */
|
||||
if ((WSTOPSIG (*wstat) == SIGILL
|
||||
|| WSTOPSIG (*wstat) == SIGFPE
|
||||
|| WSTOPSIG (*wstat) == SIGBUS
|
||||
|| WSTOPSIG (*wstat) == SIGSEGV)
|
||||
&& ptrace (PTRACE_GETSIGINFO, lwpid_of (lwp), 0, &info) == 0
|
||||
/* Final check just to make sure we don't clobber
|
||||
the siginfo of non-kernel-sent signals. */
|
||||
&& (uintptr_t) info.si_addr == lwp->stop_pc)
|
||||
{
|
||||
info.si_addr = (void *) (uintptr_t) status.tpoint_addr;
|
||||
ptrace (PTRACE_SETSIGINFO, lwpid_of (lwp), 0, &info);
|
||||
}
|
||||
|
||||
regcache = get_thread_regcache (get_lwp_thread (lwp), 1);
|
||||
(*the_low_target.set_pc) (regcache, status.tpoint_addr);
|
||||
lwp->stop_pc = status.tpoint_addr;
|
||||
|
||||
/* Cancel any fast tracepoint lock this thread was
|
||||
holding. */
|
||||
force_unlock_trace_buffer ();
|
||||
}
|
||||
|
||||
if (lwp->exit_jump_pad_bkpt != NULL)
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Cancelling fast exit-jump-pad: removing bkpt. "
|
||||
"stopping all threads momentarily.\n");
|
||||
|
||||
stop_all_lwps (1, lwp);
|
||||
cancel_breakpoints ();
|
||||
|
||||
delete_breakpoint (lwp->exit_jump_pad_bkpt);
|
||||
lwp->exit_jump_pad_bkpt = NULL;
|
||||
|
||||
unstop_all_lwps (1, lwp);
|
||||
|
||||
gdb_assert (lwp->suspended >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "\
|
||||
Checking whether LWP %ld needs to move out of the jump pad...no\n",
|
||||
lwpid_of (lwp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Enqueue one signal in the "signals to report later when out of the
|
||||
jump pad" list. */
|
||||
|
||||
static void
|
||||
enqueue_one_deferred_signal (struct lwp_info *lwp, int *wstat)
|
||||
{
|
||||
struct pending_signals *p_sig;
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "\
|
||||
Deferring signal %d for LWP %ld.\n", WSTOPSIG (*wstat), lwpid_of (lwp));
|
||||
|
||||
if (debug_threads)
|
||||
{
|
||||
struct pending_signals *sig;
|
||||
|
||||
for (sig = lwp->pending_signals_to_report;
|
||||
sig != NULL;
|
||||
sig = sig->prev)
|
||||
fprintf (stderr,
|
||||
" Already queued %d\n",
|
||||
sig->signal);
|
||||
|
||||
fprintf (stderr, " (no more currently queued signals)\n");
|
||||
}
|
||||
|
||||
p_sig = xmalloc (sizeof (*p_sig));
|
||||
p_sig->prev = lwp->pending_signals_to_report;
|
||||
p_sig->signal = WSTOPSIG (*wstat);
|
||||
memset (&p_sig->info, 0, sizeof (siginfo_t));
|
||||
ptrace (PTRACE_GETSIGINFO, lwpid_of (lwp), 0, &p_sig->info);
|
||||
|
||||
lwp->pending_signals_to_report = p_sig;
|
||||
}
|
||||
|
||||
/* Dequeue one signal from the "signals to report later when out of
|
||||
the jump pad" list. */
|
||||
|
||||
static int
|
||||
dequeue_one_deferred_signal (struct lwp_info *lwp, int *wstat)
|
||||
{
|
||||
if (lwp->pending_signals_to_report != NULL)
|
||||
{
|
||||
struct pending_signals **p_sig;
|
||||
|
||||
p_sig = &lwp->pending_signals_to_report;
|
||||
while ((*p_sig)->prev != NULL)
|
||||
p_sig = &(*p_sig)->prev;
|
||||
|
||||
*wstat = W_STOPCODE ((*p_sig)->signal);
|
||||
if ((*p_sig)->info.si_signo != 0)
|
||||
ptrace (PTRACE_SETSIGINFO, lwpid_of (lwp), 0, &(*p_sig)->info);
|
||||
free (*p_sig);
|
||||
*p_sig = NULL;
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "Reporting deferred signal %d for LWP %ld.\n",
|
||||
WSTOPSIG (*wstat), lwpid_of (lwp));
|
||||
|
||||
if (debug_threads)
|
||||
{
|
||||
struct pending_signals *sig;
|
||||
|
||||
for (sig = lwp->pending_signals_to_report;
|
||||
sig != NULL;
|
||||
sig = sig->prev)
|
||||
fprintf (stderr,
|
||||
" Still queued %d\n",
|
||||
sig->signal);
|
||||
|
||||
fprintf (stderr, " (no more queued signals)\n");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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 LWP. We
|
||||
will handle the current event, eventually we will resume this LWP,
|
||||
@ -1226,6 +1471,21 @@ linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
|
||||
{
|
||||
requested_child = find_lwp_pid (ptid);
|
||||
|
||||
if (!stopping_threads
|
||||
&& requested_child->status_pending_p
|
||||
&& requested_child->collecting_fast_tracepoint)
|
||||
{
|
||||
enqueue_one_deferred_signal (requested_child,
|
||||
&requested_child->status_pending);
|
||||
requested_child->status_pending_p = 0;
|
||||
requested_child->status_pending = 0;
|
||||
linux_resume_one_lwp (requested_child, 0, 0, NULL);
|
||||
}
|
||||
|
||||
if (requested_child->suspended
|
||||
&& requested_child->status_pending_p)
|
||||
fatal ("requesting an event out of a suspended child?");
|
||||
|
||||
if (requested_child->status_pending_p)
|
||||
event_child = requested_child;
|
||||
}
|
||||
@ -1601,6 +1861,113 @@ gdb_wants_all_stopped (void)
|
||||
for_each_inferior (&all_lwps, gdb_wants_lwp_stopped);
|
||||
}
|
||||
|
||||
static void move_out_of_jump_pad_callback (struct inferior_list_entry *entry);
|
||||
static int stuck_in_jump_pad_callback (struct inferior_list_entry *entry,
|
||||
void *data);
|
||||
static int lwp_running (struct inferior_list_entry *entry, void *data);
|
||||
static ptid_t linux_wait_1 (ptid_t ptid,
|
||||
struct target_waitstatus *ourstatus,
|
||||
int target_options);
|
||||
|
||||
/* Stabilize threads (move out of jump pads).
|
||||
|
||||
If a thread is midway collecting a fast tracepoint, we need to
|
||||
finish the collection and move it out of the jump pad before
|
||||
reporting the signal.
|
||||
|
||||
This avoids recursion while collecting (when a signal arrives
|
||||
midway, and the signal handler itself collects), which would trash
|
||||
the trace buffer. In case the user set a breakpoint in a signal
|
||||
handler, this avoids the backtrace showing the jump pad, etc..
|
||||
Most importantly, there are certain things we can't do safely if
|
||||
threads are stopped in a jump pad (or in its callee's). For
|
||||
example:
|
||||
|
||||
- starting a new trace run. A thread still collecting the
|
||||
previous run, could trash the trace buffer when resumed. The trace
|
||||
buffer control structures would have been reset but the thread had
|
||||
no way to tell. The thread could even midway memcpy'ing to the
|
||||
buffer, which would mean that when resumed, it would clobber the
|
||||
trace buffer that had been set for a new run.
|
||||
|
||||
- we can't rewrite/reuse the jump pads for new tracepoints
|
||||
safely. Say you do tstart while a thread is stopped midway while
|
||||
collecting. When the thread is later resumed, it finishes the
|
||||
collection, and returns to the jump pad, to execute the original
|
||||
instruction that was under the tracepoint jump at the time the
|
||||
older run had been started. If the jump pad had been rewritten
|
||||
since for something else in the new run, the thread would now
|
||||
execute the wrong / random instructions. */
|
||||
|
||||
static void
|
||||
linux_stabilize_threads (void)
|
||||
{
|
||||
struct thread_info *save_inferior;
|
||||
struct lwp_info *lwp_stuck;
|
||||
|
||||
lwp_stuck
|
||||
= (struct lwp_info *) find_inferior (&all_lwps,
|
||||
stuck_in_jump_pad_callback, NULL);
|
||||
if (lwp_stuck != NULL)
|
||||
{
|
||||
fprintf (stderr, "can't stabilize, LWP %ld is stuck in jump pad\n",
|
||||
lwpid_of (lwp_stuck));
|
||||
return;
|
||||
}
|
||||
|
||||
save_inferior = current_inferior;
|
||||
|
||||
stabilizing_threads = 1;
|
||||
|
||||
/* Kick 'em all. */
|
||||
for_each_inferior (&all_lwps, move_out_of_jump_pad_callback);
|
||||
|
||||
/* Loop until all are stopped out of the jump pads. */
|
||||
while (find_inferior (&all_lwps, lwp_running, NULL) != NULL)
|
||||
{
|
||||
struct target_waitstatus ourstatus;
|
||||
struct lwp_info *lwp;
|
||||
ptid_t ptid;
|
||||
int wstat;
|
||||
|
||||
/* Note that we go through the full wait even loop. While
|
||||
moving threads out of jump pad, we need to be able to step
|
||||
over internal breakpoints and such. */
|
||||
ptid = linux_wait_1 (minus_one_ptid, &ourstatus, 0);
|
||||
|
||||
if (ourstatus.kind == TARGET_WAITKIND_STOPPED)
|
||||
{
|
||||
lwp = get_thread_lwp (current_inferior);
|
||||
|
||||
/* Lock it. */
|
||||
lwp->suspended++;
|
||||
|
||||
if (ourstatus.value.sig != TARGET_SIGNAL_0
|
||||
|| current_inferior->last_resume_kind == resume_stop)
|
||||
{
|
||||
wstat = W_STOPCODE (target_signal_to_host (ourstatus.value.sig));
|
||||
enqueue_one_deferred_signal (lwp, &wstat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
find_inferior (&all_lwps, unsuspend_one_lwp, NULL);
|
||||
|
||||
stabilizing_threads = 0;
|
||||
|
||||
current_inferior = save_inferior;
|
||||
|
||||
lwp_stuck
|
||||
= (struct lwp_info *) find_inferior (&all_lwps,
|
||||
stuck_in_jump_pad_callback, NULL);
|
||||
if (lwp_stuck != NULL)
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "couldn't stabilize, LWP %ld got stuck in jump pad\n",
|
||||
lwpid_of (lwp_stuck));
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for process, returns status. */
|
||||
|
||||
static ptid_t
|
||||
@ -1623,6 +1990,8 @@ linux_wait_1 (ptid_t ptid,
|
||||
options |= WNOHANG;
|
||||
|
||||
retry:
|
||||
bp_explains_trap = 0;
|
||||
trace_event = 0;
|
||||
ourstatus->kind = TARGET_WAITKIND_IGNORE;
|
||||
|
||||
/* If we were only supposed to resume one thread, only wait for
|
||||
@ -1765,8 +2134,110 @@ linux_wait_1 (ptid_t ptid,
|
||||
/* We have some other signal, possibly a step-over dance was in
|
||||
progress, and it should be cancelled too. */
|
||||
step_over_finished = finish_step_over (event_child);
|
||||
}
|
||||
|
||||
trace_event = 0;
|
||||
/* We have all the data we need. Either report the event to GDB, or
|
||||
resume threads and keep waiting for more. */
|
||||
|
||||
/* If we're collecting a fast tracepoint, finish the collection and
|
||||
move out of the jump pad before delivering a signal. See
|
||||
linux_stabilize_threads. */
|
||||
|
||||
if (WIFSTOPPED (w)
|
||||
&& WSTOPSIG (w) != SIGTRAP
|
||||
&& supports_fast_tracepoints ()
|
||||
&& in_process_agent_loaded ())
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Got signal %d for LWP %ld. Check if we need "
|
||||
"to defer or adjust it.\n",
|
||||
WSTOPSIG (w), lwpid_of (event_child));
|
||||
|
||||
/* Allow debugging the jump pad itself. */
|
||||
if (current_inferior->last_resume_kind != resume_step
|
||||
&& maybe_move_out_of_jump_pad (event_child, &w))
|
||||
{
|
||||
enqueue_one_deferred_signal (event_child, &w);
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Signal %d for LWP %ld deferred (in jump pad)\n",
|
||||
WSTOPSIG (w), lwpid_of (event_child));
|
||||
|
||||
linux_resume_one_lwp (event_child, 0, 0, NULL);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
if (event_child->collecting_fast_tracepoint)
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "\
|
||||
LWP %ld was trying to move out of the jump pad (%d). \
|
||||
Check if we're already there.\n",
|
||||
lwpid_of (event_child),
|
||||
event_child->collecting_fast_tracepoint);
|
||||
|
||||
trace_event = 1;
|
||||
|
||||
event_child->collecting_fast_tracepoint
|
||||
= linux_fast_tracepoint_collecting (event_child, NULL);
|
||||
|
||||
if (event_child->collecting_fast_tracepoint != 1)
|
||||
{
|
||||
/* No longer need this breakpoint. */
|
||||
if (event_child->exit_jump_pad_bkpt != NULL)
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"No longer need exit-jump-pad bkpt; removing it."
|
||||
"stopping all threads momentarily.\n");
|
||||
|
||||
/* Other running threads could hit this breakpoint.
|
||||
We don't handle moribund locations like GDB does,
|
||||
instead we always pause all threads when removing
|
||||
breakpoints, so that any step-over or
|
||||
decr_pc_after_break adjustment is always taken
|
||||
care of while the breakpoint is still
|
||||
inserted. */
|
||||
stop_all_lwps (1, event_child);
|
||||
cancel_breakpoints ();
|
||||
|
||||
delete_breakpoint (event_child->exit_jump_pad_bkpt);
|
||||
event_child->exit_jump_pad_bkpt = NULL;
|
||||
|
||||
unstop_all_lwps (1, event_child);
|
||||
|
||||
gdb_assert (event_child->suspended >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (event_child->collecting_fast_tracepoint == 0)
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"fast tracepoint finished "
|
||||
"collecting successfully.\n");
|
||||
|
||||
/* We may have a deferred signal to report. */
|
||||
if (dequeue_one_deferred_signal (event_child, &w))
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "dequeued one signal.\n");
|
||||
}
|
||||
else if (debug_threads)
|
||||
{
|
||||
fprintf (stderr, "no deferred signals.\n");
|
||||
|
||||
if (stabilizing_threads)
|
||||
{
|
||||
ourstatus->kind = TARGET_WAITKIND_STOPPED;
|
||||
ourstatus->value.sig = TARGET_SIGNAL_0;
|
||||
return ptid_of (event_child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check whether GDB would be interested in this event. */
|
||||
@ -1877,7 +2348,7 @@ linux_wait_1 (ptid_t ptid,
|
||||
|
||||
/* Alright, we're going to report a stop. */
|
||||
|
||||
if (!non_stop)
|
||||
if (!non_stop && !stabilizing_threads)
|
||||
{
|
||||
/* In all-stop, stop all threads. */
|
||||
stop_all_lwps (0, NULL);
|
||||
@ -1902,6 +2373,9 @@ linux_wait_1 (ptid_t ptid,
|
||||
See the comment in cancel_breakpoints_callback to find out
|
||||
why. */
|
||||
find_inferior (&all_lwps, cancel_breakpoints_callback, event_child);
|
||||
|
||||
/* Stabilize threads (move out of jump pads). */
|
||||
stabilize_threads ();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1939,6 +2413,9 @@ linux_wait_1 (ptid_t ptid,
|
||||
|
||||
gdb_assert (ptid_equal (step_over_bkpt, null_ptid));
|
||||
|
||||
if (stabilizing_threads)
|
||||
return ptid_of (event_child);
|
||||
|
||||
if (!non_stop)
|
||||
{
|
||||
/* From GDB's perspective, all-stop mode always stops all
|
||||
@ -2210,6 +2687,82 @@ wait_for_sigstop (struct inferior_list_entry *entry)
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if LWP ENTRY is stopped in a jump pad, and we can't
|
||||
move it out, because we need to report the stop event to GDB. For
|
||||
example, if the user puts a breakpoint in the jump pad, it's
|
||||
because she wants to debug it. */
|
||||
|
||||
static int
|
||||
stuck_in_jump_pad_callback (struct inferior_list_entry *entry, void *data)
|
||||
{
|
||||
struct lwp_info *lwp = (struct lwp_info *) entry;
|
||||
struct thread_info *thread = get_lwp_thread (lwp);
|
||||
|
||||
gdb_assert (lwp->suspended == 0);
|
||||
gdb_assert (lwp->stopped);
|
||||
|
||||
/* Allow debugging the jump pad, gdb_collect, etc.. */
|
||||
return (supports_fast_tracepoints ()
|
||||
&& in_process_agent_loaded ()
|
||||
&& (gdb_breakpoint_here (lwp->stop_pc)
|
||||
|| lwp->stopped_by_watchpoint
|
||||
|| thread->last_resume_kind == resume_step)
|
||||
&& linux_fast_tracepoint_collecting (lwp, NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
move_out_of_jump_pad_callback (struct inferior_list_entry *entry)
|
||||
{
|
||||
struct lwp_info *lwp = (struct lwp_info *) entry;
|
||||
struct thread_info *thread = get_lwp_thread (lwp);
|
||||
int *wstat;
|
||||
|
||||
gdb_assert (lwp->suspended == 0);
|
||||
gdb_assert (lwp->stopped);
|
||||
|
||||
wstat = lwp->status_pending_p ? &lwp->status_pending : NULL;
|
||||
|
||||
/* Allow debugging the jump pad, gdb_collect, etc. */
|
||||
if (!gdb_breakpoint_here (lwp->stop_pc)
|
||||
&& !lwp->stopped_by_watchpoint
|
||||
&& thread->last_resume_kind != resume_step
|
||||
&& maybe_move_out_of_jump_pad (lwp, wstat))
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"LWP %ld needs stabilizing (in jump pad)\n",
|
||||
lwpid_of (lwp));
|
||||
|
||||
if (wstat)
|
||||
{
|
||||
lwp->status_pending_p = 0;
|
||||
enqueue_one_deferred_signal (lwp, wstat);
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Signal %d for LWP %ld deferred "
|
||||
"(in jump pad)\n",
|
||||
WSTOPSIG (*wstat), lwpid_of (lwp));
|
||||
}
|
||||
|
||||
linux_resume_one_lwp (lwp, 0, 0, NULL);
|
||||
}
|
||||
else
|
||||
lwp->suspended++;
|
||||
}
|
||||
|
||||
static int
|
||||
lwp_running (struct inferior_list_entry *entry, void *data)
|
||||
{
|
||||
struct lwp_info *lwp = (struct lwp_info *) entry;
|
||||
|
||||
if (lwp->dead)
|
||||
return 0;
|
||||
if (lwp->stopped)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Stop all lwps that aren't stopped yet, except EXCEPT, if not NULL.
|
||||
If SUSPEND, then also increase the suspend count of every LWP,
|
||||
except EXCEPT. */
|
||||
@ -2236,10 +2789,15 @@ linux_resume_one_lwp (struct lwp_info *lwp,
|
||||
int step, int signal, siginfo_t *info)
|
||||
{
|
||||
struct thread_info *saved_inferior;
|
||||
int fast_tp_collecting;
|
||||
|
||||
if (lwp->stopped == 0)
|
||||
return;
|
||||
|
||||
fast_tp_collecting = lwp->collecting_fast_tracepoint;
|
||||
|
||||
gdb_assert (!stabilizing_threads || fast_tp_collecting);
|
||||
|
||||
/* Cancel actions that rely on GDB not changing the PC (e.g., the
|
||||
user used the "jump" command, or "set $pc = foo"). */
|
||||
if (lwp->stop_pc != get_pc (lwp))
|
||||
@ -2253,8 +2811,10 @@ linux_resume_one_lwp (struct lwp_info *lwp,
|
||||
signal. Also enqueue the signal if we are waiting to reinsert a
|
||||
breakpoint; it will be picked up again below. */
|
||||
if (signal != 0
|
||||
&& (lwp->status_pending_p || lwp->pending_signals != NULL
|
||||
|| lwp->bp_reinsert != 0))
|
||||
&& (lwp->status_pending_p
|
||||
|| lwp->pending_signals != NULL
|
||||
|| lwp->bp_reinsert != 0
|
||||
|| fast_tp_collecting))
|
||||
{
|
||||
struct pending_signals *p_sig;
|
||||
p_sig = xmalloc (sizeof (*p_sig));
|
||||
@ -2303,11 +2863,14 @@ linux_resume_one_lwp (struct lwp_info *lwp,
|
||||
|
||||
if (lwp->bp_reinsert != 0 && can_hardware_single_step ())
|
||||
{
|
||||
if (step == 0)
|
||||
fprintf (stderr, "BAD - reinserting but not stepping.\n");
|
||||
if (lwp->suspended)
|
||||
fprintf (stderr, "BAD - reinserting and suspended(%d).\n",
|
||||
lwp->suspended);
|
||||
if (fast_tp_collecting == 0)
|
||||
{
|
||||
if (step == 0)
|
||||
fprintf (stderr, "BAD - reinserting but not stepping.\n");
|
||||
if (lwp->suspended)
|
||||
fprintf (stderr, "BAD - reinserting and suspended(%d).\n",
|
||||
lwp->suspended);
|
||||
}
|
||||
|
||||
step = 1;
|
||||
}
|
||||
@ -2316,6 +2879,33 @@ linux_resume_one_lwp (struct lwp_info *lwp,
|
||||
signal = 0;
|
||||
}
|
||||
|
||||
if (fast_tp_collecting == 1)
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "\
|
||||
lwp %ld wants to get out of fast tracepoint jump pad (exit-jump-pad-bkpt)\n",
|
||||
lwpid_of (lwp));
|
||||
|
||||
/* Postpone any pending signal. It was enqueued above. */
|
||||
signal = 0;
|
||||
}
|
||||
else if (fast_tp_collecting == 2)
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr, "\
|
||||
lwp %ld wants to get out of fast tracepoint jump pad single-stepping\n",
|
||||
lwpid_of (lwp));
|
||||
|
||||
if (can_hardware_single_step ())
|
||||
step = 1;
|
||||
else
|
||||
fatal ("moving out of jump pad single-stepping"
|
||||
" not implemented on this target");
|
||||
|
||||
/* Postpone any pending signal. It was enqueued above. */
|
||||
signal = 0;
|
||||
}
|
||||
|
||||
/* If we have while-stepping actions in this thread set it stepping.
|
||||
If we have a signal to deliver, it may or may not be set to
|
||||
SIG_IGN, we don't know. Assume so, and allow collecting
|
||||
@ -2341,9 +2931,12 @@ linux_resume_one_lwp (struct lwp_info *lwp,
|
||||
fprintf (stderr, " resuming from pc 0x%lx\n", (long) pc);
|
||||
}
|
||||
|
||||
/* If we have pending signals, consume one unless we are trying to reinsert
|
||||
a breakpoint. */
|
||||
if (lwp->pending_signals != NULL && lwp->bp_reinsert == 0)
|
||||
/* If we have pending signals, consume one unless we are trying to
|
||||
reinsert a breakpoint or we're trying to finish a fast tracepoint
|
||||
collect. */
|
||||
if (lwp->pending_signals != NULL
|
||||
&& lwp->bp_reinsert == 0
|
||||
&& fast_tp_collecting == 0)
|
||||
{
|
||||
struct pending_signals **p_sig;
|
||||
|
||||
@ -2440,6 +3033,23 @@ linux_set_resume_request (struct inferior_list_entry *entry, void *arg)
|
||||
|
||||
lwp->resume = &r->resume[ndx];
|
||||
thread->last_resume_kind = lwp->resume->kind;
|
||||
|
||||
/* If we had a deferred signal to report, dequeue one now.
|
||||
This can happen if LWP gets more than one signal while
|
||||
trying to get out of a jump pad. */
|
||||
if (lwp->stopped
|
||||
&& !lwp->status_pending_p
|
||||
&& dequeue_one_deferred_signal (lwp, &lwp->status_pending))
|
||||
{
|
||||
lwp->status_pending_p = 1;
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Dequeueing deferred signal %d for LWP %ld, "
|
||||
"leaving status pending.\n",
|
||||
WSTOPSIG (lwp->status_pending), lwpid_of (lwp));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -2556,7 +3166,7 @@ need_step_over_p (struct inferior_list_entry *entry, void *dummy)
|
||||
current_inferior = thread;
|
||||
|
||||
/* We can only step over breakpoints we know about. */
|
||||
if (breakpoint_here (pc))
|
||||
if (breakpoint_here (pc) || fast_tracepoint_jump_here (pc))
|
||||
{
|
||||
/* Don't step over a breakpoint that GDB expects to hit
|
||||
though. */
|
||||
@ -2645,6 +3255,7 @@ start_step_over (struct lwp_info *lwp)
|
||||
|
||||
lwp->bp_reinsert = pc;
|
||||
uninsert_breakpoints_at (pc);
|
||||
uninsert_fast_tracepoint_jumps_at (pc);
|
||||
|
||||
if (can_hardware_single_step ())
|
||||
{
|
||||
@ -2681,6 +3292,7 @@ finish_step_over (struct lwp_info *lwp)
|
||||
/* Reinsert any breakpoint at LWP->BP_REINSERT. Note that there
|
||||
may be no breakpoint to reinsert there by now. */
|
||||
reinsert_breakpoints_at (lwp->bp_reinsert);
|
||||
reinsert_fast_tracepoint_jumps_at (lwp->bp_reinsert);
|
||||
|
||||
lwp->bp_reinsert = 0;
|
||||
|
||||
@ -4425,6 +5037,23 @@ linux_unpause_all (int unfreeze)
|
||||
unstop_all_lwps (unfreeze, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
linux_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
|
||||
CORE_ADDR collector,
|
||||
CORE_ADDR lockaddr,
|
||||
ULONGEST orig_size,
|
||||
CORE_ADDR *jump_entry,
|
||||
unsigned char *jjump_pad_insn,
|
||||
ULONGEST *jjump_pad_insn_size,
|
||||
CORE_ADDR *adjusted_insn_addr,
|
||||
CORE_ADDR *adjusted_insn_addr_end)
|
||||
{
|
||||
return (*the_low_target.install_fast_tracepoint_jump_pad)
|
||||
(tpoint, tpaddr, collector, lockaddr, orig_size,
|
||||
jump_entry, jjump_pad_insn, jjump_pad_insn_size,
|
||||
adjusted_insn_addr, adjusted_insn_addr_end);
|
||||
}
|
||||
|
||||
static struct target_ops linux_target_ops = {
|
||||
linux_create_inferior,
|
||||
linux_attach,
|
||||
@ -4478,7 +5107,9 @@ static struct target_ops linux_target_ops = {
|
||||
NULL,
|
||||
linux_pause_all,
|
||||
linux_unpause_all,
|
||||
linux_cancel_breakpoints
|
||||
linux_cancel_breakpoints,
|
||||
linux_stabilize_threads,
|
||||
linux_install_fast_tracepoint_jump_pad
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -120,6 +120,22 @@ struct linux_target_ops
|
||||
|
||||
/* Returns true if the low target supports tracepoints. */
|
||||
int (*supports_tracepoints) (void);
|
||||
|
||||
/* Fill ADDRP with the thread area address of LWPID. Returns 0 on
|
||||
success, -1 on failure. */
|
||||
int (*get_thread_area) (int lwpid, CORE_ADDR *addrp);
|
||||
|
||||
/* Install a fast tracepoint jump pad. See target.h for
|
||||
comments. */
|
||||
int (*install_fast_tracepoint_jump_pad) (CORE_ADDR tpoint, CORE_ADDR tpaddr,
|
||||
CORE_ADDR collector,
|
||||
CORE_ADDR lockaddr,
|
||||
ULONGEST orig_size,
|
||||
CORE_ADDR *jump_entry,
|
||||
unsigned char *jjump_pad_insn,
|
||||
ULONGEST *jjump_pad_insn_size,
|
||||
CORE_ADDR *adjusted_insn_addr,
|
||||
CORE_ADDR *adjusted_insn_addr_end);
|
||||
};
|
||||
|
||||
extern struct linux_target_ops the_low_target;
|
||||
@ -201,6 +217,22 @@ struct lwp_info
|
||||
and then processed and cleared in linux_resume_one_lwp. */
|
||||
struct thread_resume *resume;
|
||||
|
||||
/* True if it is known that this lwp is presently collecting a fast
|
||||
tracepoint (it is in the jump pad or in some code that will
|
||||
return to the jump pad. Normally, we won't care about this, but
|
||||
we will if a signal arrives to this lwp while it is
|
||||
collecting. */
|
||||
int collecting_fast_tracepoint;
|
||||
|
||||
/* If this is non-zero, it points to a chain of signals which need
|
||||
to be reported to GDB. These were deferred because the thread
|
||||
was doing a fast tracepoint collect when they arrived. */
|
||||
struct pending_signals *pending_signals_to_report;
|
||||
|
||||
/* When collecting_fast_tracepoint is first found to be 1, we insert
|
||||
a exit-jump-pad-quickly breakpoint. This is it. */
|
||||
struct breakpoint *exit_jump_pad_bkpt;
|
||||
|
||||
/* True if the LWP was seen stop at an internal breakpoint and needs
|
||||
stepping over later when it is resumed. */
|
||||
int need_step_over;
|
||||
@ -223,6 +255,7 @@ int elf_64_file_p (const char *file);
|
||||
|
||||
void linux_attach_lwp (unsigned long pid);
|
||||
struct lwp_info *find_lwp_pid (ptid_t ptid);
|
||||
int linux_get_thread_area (int lwpid, CORE_ADDR *area);
|
||||
|
||||
/* From thread-db.c */
|
||||
int thread_db_init (int use_events);
|
||||
|
@ -40,6 +40,8 @@ void init_registers_amd64_avx_linux (void);
|
||||
/* Defined in auto-generated file i386-mmx-linux.c. */
|
||||
void init_registers_i386_mmx_linux (void);
|
||||
|
||||
static unsigned char jump_insn[] = { 0xe9, 0, 0, 0, 0 };
|
||||
|
||||
/* Backward compatibility for gdb without XML support. */
|
||||
|
||||
static const char *xmltarget_i386_linux_no_xml = "@<target>\
|
||||
@ -191,6 +193,53 @@ ps_get_thread_area (const struct ps_prochandle *ph,
|
||||
return PS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the thread area address. This is used to recognize which
|
||||
thread is which when tracing with the in-process agent library. We
|
||||
don't read anything from the address, and treat it as opaque; it's
|
||||
the address itself that we assume is unique per-thread. */
|
||||
|
||||
static int
|
||||
x86_get_thread_area (int lwpid, CORE_ADDR *addr)
|
||||
{
|
||||
#ifdef __x86_64__
|
||||
int use_64bit = register_size (0) == 8;
|
||||
|
||||
if (use_64bit)
|
||||
{
|
||||
void *base;
|
||||
if (ptrace (PTRACE_ARCH_PRCTL, lwpid, &base, ARCH_GET_FS) == 0)
|
||||
{
|
||||
*addr = (CORE_ADDR) (uintptr_t) base;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
struct lwp_info *lwp = find_lwp_pid (pid_to_ptid (lwpid));
|
||||
struct regcache *regcache = get_thread_regcache (get_lwp_thread (lwp), 1);
|
||||
unsigned int desc[4];
|
||||
ULONGEST gs = 0;
|
||||
const int reg_thread_area = 3; /* bits to scale down register value. */
|
||||
int idx;
|
||||
|
||||
collect_register_by_name (regcache, "gs", &gs);
|
||||
|
||||
idx = gs >> reg_thread_area;
|
||||
|
||||
if (ptrace (PTRACE_GET_THREAD_AREA,
|
||||
lwpid_of (lwp), (void *) (long) idx, (unsigned long) &desc) < 0)
|
||||
return -1;
|
||||
|
||||
*addr = desc[1];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
i386_cannot_store_register (int regno)
|
||||
@ -1041,6 +1090,386 @@ x86_supports_tracepoints (void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
append_insns (CORE_ADDR *to, size_t len, const unsigned char *buf)
|
||||
{
|
||||
write_inferior_memory (*to, buf, len);
|
||||
*to += len;
|
||||
}
|
||||
|
||||
static int
|
||||
push_opcode (unsigned char *buf, char *op)
|
||||
{
|
||||
unsigned char *buf_org = buf;
|
||||
|
||||
while (1)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long ul = strtoul (op, &endptr, 16);
|
||||
|
||||
if (endptr == op)
|
||||
break;
|
||||
|
||||
*buf++ = ul;
|
||||
op = endptr;
|
||||
}
|
||||
|
||||
return buf - buf_org;
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
/* Build a jump pad that saves registers and calls a collection
|
||||
function. Writes a jump instruction to the jump pad to
|
||||
JJUMPAD_INSN. The caller is responsible to write it in at the
|
||||
tracepoint address. */
|
||||
|
||||
static int
|
||||
amd64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
|
||||
CORE_ADDR collector,
|
||||
CORE_ADDR lockaddr,
|
||||
ULONGEST orig_size,
|
||||
CORE_ADDR *jump_entry,
|
||||
unsigned char *jjump_pad_insn,
|
||||
ULONGEST *jjump_pad_insn_size,
|
||||
CORE_ADDR *adjusted_insn_addr,
|
||||
CORE_ADDR *adjusted_insn_addr_end)
|
||||
{
|
||||
unsigned char buf[40];
|
||||
int i, offset;
|
||||
CORE_ADDR buildaddr = *jump_entry;
|
||||
|
||||
/* Build the jump pad. */
|
||||
|
||||
/* First, do tracepoint data collection. Save registers. */
|
||||
i = 0;
|
||||
/* Need to ensure stack pointer saved first. */
|
||||
buf[i++] = 0x54; /* push %rsp */
|
||||
buf[i++] = 0x55; /* push %rbp */
|
||||
buf[i++] = 0x57; /* push %rdi */
|
||||
buf[i++] = 0x56; /* push %rsi */
|
||||
buf[i++] = 0x52; /* push %rdx */
|
||||
buf[i++] = 0x51; /* push %rcx */
|
||||
buf[i++] = 0x53; /* push %rbx */
|
||||
buf[i++] = 0x50; /* push %rax */
|
||||
buf[i++] = 0x41; buf[i++] = 0x57; /* push %r15 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x56; /* push %r14 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x55; /* push %r13 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x54; /* push %r12 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x53; /* push %r11 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x52; /* push %r10 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x51; /* push %r9 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x50; /* push %r8 */
|
||||
buf[i++] = 0x9c; /* pushfq */
|
||||
buf[i++] = 0x48; /* movl <addr>,%rdi */
|
||||
buf[i++] = 0xbf;
|
||||
*((unsigned long *)(buf + i)) = (unsigned long) tpaddr;
|
||||
i += sizeof (unsigned long);
|
||||
buf[i++] = 0x57; /* push %rdi */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* Stack space for the collecting_t object. */
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "48 83 ec 18"); /* sub $0x18,%rsp */
|
||||
i += push_opcode (&buf[i], "48 b8"); /* mov <tpoint>,%rax */
|
||||
memcpy (buf + i, &tpoint, 8);
|
||||
i += 8;
|
||||
i += push_opcode (&buf[i], "48 89 04 24"); /* mov %rax,(%rsp) */
|
||||
i += push_opcode (&buf[i],
|
||||
"64 48 8b 04 25 00 00 00 00"); /* mov %fs:0x0,%rax */
|
||||
i += push_opcode (&buf[i], "48 89 44 24 08"); /* mov %rax,0x8(%rsp) */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* spin-lock. */
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "48 be"); /* movl <lockaddr>,%rsi */
|
||||
memcpy (&buf[i], (void *) &lockaddr, 8);
|
||||
i += 8;
|
||||
i += push_opcode (&buf[i], "48 89 e1"); /* mov %rsp,%rcx */
|
||||
i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
|
||||
i += push_opcode (&buf[i], "f0 48 0f b1 0e"); /* lock cmpxchg %rcx,(%rsi) */
|
||||
i += push_opcode (&buf[i], "48 85 c0"); /* test %rax,%rax */
|
||||
i += push_opcode (&buf[i], "75 f4"); /* jne <again> */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* Set up the gdb_collect call. */
|
||||
/* At this point, (stack pointer + 0x18) is the base of our saved
|
||||
register block. */
|
||||
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "48 89 e6"); /* mov %rsp,%rsi */
|
||||
i += push_opcode (&buf[i], "48 83 c6 18"); /* add $0x18,%rsi */
|
||||
|
||||
/* tpoint address may be 64-bit wide. */
|
||||
i += push_opcode (&buf[i], "48 bf"); /* movl <addr>,%rdi */
|
||||
memcpy (buf + i, &tpoint, 8);
|
||||
i += 8;
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* The collector function being in the shared library, may be
|
||||
>31-bits away off the jump pad. */
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "48 b8"); /* mov $collector,%rax */
|
||||
memcpy (buf + i, &collector, 8);
|
||||
i += 8;
|
||||
i += push_opcode (&buf[i], "ff d0"); /* callq *%rax */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* Clear the spin-lock. */
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
|
||||
i += push_opcode (&buf[i], "48 a3"); /* mov %rax, lockaddr */
|
||||
memcpy (buf + i, &lockaddr, 8);
|
||||
i += 8;
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* Remove stack that had been used for the collect_t object. */
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "48 83 c4 18"); /* add $0x18,%rsp */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* Restore register state. */
|
||||
i = 0;
|
||||
buf[i++] = 0x48; /* add $0x8,%rsp */
|
||||
buf[i++] = 0x83;
|
||||
buf[i++] = 0xc4;
|
||||
buf[i++] = 0x08;
|
||||
buf[i++] = 0x9d; /* popfq */
|
||||
buf[i++] = 0x41; buf[i++] = 0x58; /* pop %r8 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x59; /* pop %r9 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x5a; /* pop %r10 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x5b; /* pop %r11 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x5c; /* pop %r12 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x5d; /* pop %r13 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x5e; /* pop %r14 */
|
||||
buf[i++] = 0x41; buf[i++] = 0x5f; /* pop %r15 */
|
||||
buf[i++] = 0x58; /* pop %rax */
|
||||
buf[i++] = 0x5b; /* pop %rbx */
|
||||
buf[i++] = 0x59; /* pop %rcx */
|
||||
buf[i++] = 0x5a; /* pop %rdx */
|
||||
buf[i++] = 0x5e; /* pop %rsi */
|
||||
buf[i++] = 0x5f; /* pop %rdi */
|
||||
buf[i++] = 0x5d; /* pop %rbp */
|
||||
buf[i++] = 0x5c; /* pop %rsp */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* Now, adjust the original instruction to execute in the jump
|
||||
pad. */
|
||||
*adjusted_insn_addr = buildaddr;
|
||||
relocate_instruction (&buildaddr, tpaddr);
|
||||
*adjusted_insn_addr_end = buildaddr;
|
||||
|
||||
/* Finally, write a jump back to the program. */
|
||||
offset = (tpaddr + orig_size) - (buildaddr + sizeof (jump_insn));
|
||||
memcpy (buf, jump_insn, sizeof (jump_insn));
|
||||
memcpy (buf + 1, &offset, 4);
|
||||
append_insns (&buildaddr, sizeof (jump_insn), buf);
|
||||
|
||||
/* The jump pad is now built. Wire in a jump to our jump pad. This
|
||||
is always done last (by our caller actually), so that we can
|
||||
install fast tracepoints with threads running. This relies on
|
||||
the agent's atomic write support. */
|
||||
offset = *jump_entry - (tpaddr + sizeof (jump_insn));
|
||||
memcpy (buf, jump_insn, sizeof (jump_insn));
|
||||
memcpy (buf + 1, &offset, 4);
|
||||
memcpy (jjump_pad_insn, buf, sizeof (jump_insn));
|
||||
*jjump_pad_insn_size = sizeof (jump_insn);
|
||||
|
||||
/* Return the end address of our pad. */
|
||||
*jump_entry = buildaddr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
||||
/* Build a jump pad that saves registers and calls a collection
|
||||
function. Writes a jump instruction to the jump pad to
|
||||
JJUMPAD_INSN. The caller is responsible to write it in at the
|
||||
tracepoint address. */
|
||||
|
||||
static int
|
||||
i386_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
|
||||
CORE_ADDR collector,
|
||||
CORE_ADDR lockaddr,
|
||||
ULONGEST orig_size,
|
||||
CORE_ADDR *jump_entry,
|
||||
unsigned char *jjump_pad_insn,
|
||||
ULONGEST *jjump_pad_insn_size,
|
||||
CORE_ADDR *adjusted_insn_addr,
|
||||
CORE_ADDR *adjusted_insn_addr_end)
|
||||
{
|
||||
unsigned char buf[0x100];
|
||||
int i, offset;
|
||||
CORE_ADDR buildaddr = *jump_entry;
|
||||
|
||||
/* Build the jump pad. */
|
||||
|
||||
/* First, do tracepoint data collection. Save registers. */
|
||||
i = 0;
|
||||
buf[i++] = 0x60; /* pushad */
|
||||
buf[i++] = 0x68; /* push tpaddr aka $pc */
|
||||
*((int *)(buf + i)) = (int) tpaddr;
|
||||
i += 4;
|
||||
buf[i++] = 0x9c; /* pushf */
|
||||
buf[i++] = 0x1e; /* push %ds */
|
||||
buf[i++] = 0x06; /* push %es */
|
||||
buf[i++] = 0x0f; /* push %fs */
|
||||
buf[i++] = 0xa0;
|
||||
buf[i++] = 0x0f; /* push %gs */
|
||||
buf[i++] = 0xa8;
|
||||
buf[i++] = 0x16; /* push %ss */
|
||||
buf[i++] = 0x0e; /* push %cs */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* Stack space for the collecting_t object. */
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "83 ec 08"); /* sub $0x8,%esp */
|
||||
|
||||
/* Build the object. */
|
||||
i += push_opcode (&buf[i], "b8"); /* mov <tpoint>,%eax */
|
||||
memcpy (buf + i, &tpoint, 4);
|
||||
i += 4;
|
||||
i += push_opcode (&buf[i], "89 04 24"); /* mov %eax,(%esp) */
|
||||
|
||||
i += push_opcode (&buf[i], "65 a1 00 00 00 00"); /* mov %gs:0x0,%eax */
|
||||
i += push_opcode (&buf[i], "89 44 24 04"); /* mov %eax,0x4(%esp) */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* spin-lock. Note this is using cmpxchg, which leaves i386 behind.
|
||||
If we cared for it, this could be using xchg alternatively. */
|
||||
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
|
||||
i += push_opcode (&buf[i], "f0 0f b1 25"); /* lock cmpxchg
|
||||
%esp,<lockaddr> */
|
||||
memcpy (&buf[i], (void *) &lockaddr, 4);
|
||||
i += 4;
|
||||
i += push_opcode (&buf[i], "85 c0"); /* test %eax,%eax */
|
||||
i += push_opcode (&buf[i], "75 f2"); /* jne <again> */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
|
||||
/* Set up arguments to the gdb_collect call. */
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "89 e0"); /* mov %esp,%eax */
|
||||
i += push_opcode (&buf[i], "83 c0 08"); /* add $0x08,%eax */
|
||||
i += push_opcode (&buf[i], "89 44 24 fc"); /* mov %eax,-0x4(%esp) */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "83 ec 08"); /* sub $0x8,%esp */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "c7 04 24"); /* movl <addr>,(%esp) */
|
||||
memcpy (&buf[i], (void *) &tpoint, 4);
|
||||
i += 4;
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
buf[0] = 0xe8; /* call <reladdr> */
|
||||
offset = collector - (buildaddr + sizeof (jump_insn));
|
||||
memcpy (buf + 1, &offset, 4);
|
||||
append_insns (&buildaddr, 5, buf);
|
||||
/* Clean up after the call. */
|
||||
buf[0] = 0x83; /* add $0x8,%esp */
|
||||
buf[1] = 0xc4;
|
||||
buf[2] = 0x08;
|
||||
append_insns (&buildaddr, 3, buf);
|
||||
|
||||
|
||||
/* Clear the spin-lock. This would need the LOCK prefix on older
|
||||
broken archs. */
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "31 c0"); /* xor %eax,%eax */
|
||||
i += push_opcode (&buf[i], "a3"); /* mov %eax, lockaddr */
|
||||
memcpy (buf + i, &lockaddr, 4);
|
||||
i += 4;
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
|
||||
/* Remove stack that had been used for the collect_t object. */
|
||||
i = 0;
|
||||
i += push_opcode (&buf[i], "83 c4 08"); /* add $0x08,%esp */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
i = 0;
|
||||
buf[i++] = 0x83; /* add $0x4,%esp (no pop of %cs, assume unchanged) */
|
||||
buf[i++] = 0xc4;
|
||||
buf[i++] = 0x04;
|
||||
buf[i++] = 0x17; /* pop %ss */
|
||||
buf[i++] = 0x0f; /* pop %gs */
|
||||
buf[i++] = 0xa9;
|
||||
buf[i++] = 0x0f; /* pop %fs */
|
||||
buf[i++] = 0xa1;
|
||||
buf[i++] = 0x07; /* pop %es */
|
||||
buf[i++] = 0x1f; /* pop %de */
|
||||
buf[i++] = 0x9d; /* popf */
|
||||
buf[i++] = 0x83; /* add $0x4,%esp (pop of tpaddr aka $pc) */
|
||||
buf[i++] = 0xc4;
|
||||
buf[i++] = 0x04;
|
||||
buf[i++] = 0x61; /* popad */
|
||||
append_insns (&buildaddr, i, buf);
|
||||
|
||||
/* Now, adjust the original instruction to execute in the jump
|
||||
pad. */
|
||||
*adjusted_insn_addr = buildaddr;
|
||||
relocate_instruction (&buildaddr, tpaddr);
|
||||
*adjusted_insn_addr_end = buildaddr;
|
||||
|
||||
/* Write the jump back to the program. */
|
||||
offset = (tpaddr + orig_size) - (buildaddr + sizeof (jump_insn));
|
||||
memcpy (buf, jump_insn, sizeof (jump_insn));
|
||||
memcpy (buf + 1, &offset, 4);
|
||||
append_insns (&buildaddr, sizeof (jump_insn), buf);
|
||||
|
||||
/* The jump pad is now built. Wire in a jump to our jump pad. This
|
||||
is always done last (by our caller actually), so that we can
|
||||
install fast tracepoints with threads running. This relies on
|
||||
the agent's atomic write support. */
|
||||
offset = *jump_entry - (tpaddr + sizeof (jump_insn));
|
||||
memcpy (buf, jump_insn, sizeof (jump_insn));
|
||||
memcpy (buf + 1, &offset, 4);
|
||||
memcpy (jjump_pad_insn, buf, sizeof (jump_insn));
|
||||
*jjump_pad_insn_size = sizeof (jump_insn);
|
||||
|
||||
/* Return the end address of our pad. */
|
||||
*jump_entry = buildaddr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
x86_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, CORE_ADDR tpaddr,
|
||||
CORE_ADDR collector,
|
||||
CORE_ADDR lockaddr,
|
||||
ULONGEST orig_size,
|
||||
CORE_ADDR *jump_entry,
|
||||
unsigned char *jjump_pad_insn,
|
||||
ULONGEST *jjump_pad_insn_size,
|
||||
CORE_ADDR *adjusted_insn_addr,
|
||||
CORE_ADDR *adjusted_insn_addr_end)
|
||||
{
|
||||
#ifdef __x86_64__
|
||||
if (register_size (0) == 8)
|
||||
return amd64_install_fast_tracepoint_jump_pad (tpoint, tpaddr,
|
||||
collector, lockaddr,
|
||||
orig_size, jump_entry,
|
||||
jjump_pad_insn,
|
||||
jjump_pad_insn_size,
|
||||
adjusted_insn_addr,
|
||||
adjusted_insn_addr_end);
|
||||
#endif
|
||||
|
||||
return i386_install_fast_tracepoint_jump_pad (tpoint, tpaddr,
|
||||
collector, lockaddr,
|
||||
orig_size, jump_entry,
|
||||
jjump_pad_insn,
|
||||
jjump_pad_insn_size,
|
||||
adjusted_insn_addr,
|
||||
adjusted_insn_addr_end);
|
||||
}
|
||||
|
||||
/* This is initialized assuming an amd64 target.
|
||||
x86_arch_setup will correct it for i386 or amd64 targets. */
|
||||
|
||||
@ -1073,5 +1502,7 @@ struct linux_target_ops the_low_target =
|
||||
x86_linux_new_thread,
|
||||
x86_linux_prepare_to_resume,
|
||||
x86_linux_process_qsupported,
|
||||
x86_supports_tracepoints
|
||||
x86_supports_tracepoints,
|
||||
x86_get_thread_area,
|
||||
x86_install_fast_tracepoint_jump_pad
|
||||
};
|
||||
|
@ -137,8 +137,10 @@ set_raw_breakpoint_at (CORE_ADDR where)
|
||||
bp->pc = where;
|
||||
bp->refcount = 1;
|
||||
|
||||
err = (*the_target->read_memory) (where, bp->old_data,
|
||||
breakpoint_len);
|
||||
/* Note that there can be fast tracepoint jumps installed in the
|
||||
same memory range, so to get at the original memory, we need to
|
||||
use read_inferior_memory, which masks those out. */
|
||||
err = read_inferior_memory (where, bp->old_data, breakpoint_len);
|
||||
if (err != 0)
|
||||
{
|
||||
if (debug_threads)
|
||||
@ -169,6 +171,302 @@ set_raw_breakpoint_at (CORE_ADDR where)
|
||||
return bp;
|
||||
}
|
||||
|
||||
/* Notice that breakpoint traps are always installed on top of fast
|
||||
tracepoint jumps. This is even if the fast tracepoint is installed
|
||||
at a later time compared to when the breakpoint was installed.
|
||||
This means that a stopping breakpoint or tracepoint has higher
|
||||
"priority". In turn, this allows having fast and slow tracepoints
|
||||
(and breakpoints) at the same address behave correctly. */
|
||||
|
||||
|
||||
/* A fast tracepoint jump. */
|
||||
|
||||
struct fast_tracepoint_jump
|
||||
{
|
||||
struct fast_tracepoint_jump *next;
|
||||
|
||||
/* A reference count. GDB can install more than one fast tracepoint
|
||||
at the same address (each with its own action list, for
|
||||
example). */
|
||||
int refcount;
|
||||
|
||||
/* The fast tracepoint's insertion address. There can only be one
|
||||
of these for a given PC. */
|
||||
CORE_ADDR pc;
|
||||
|
||||
/* Non-zero if this fast tracepoint jump is currently inserted in
|
||||
the inferior. */
|
||||
int inserted;
|
||||
|
||||
/* The length of the jump instruction. */
|
||||
int length;
|
||||
|
||||
/* A poor-man's flexible array member, holding both the jump
|
||||
instruction to insert, and a copy of the instruction that would
|
||||
be in memory had not been a jump there (the shadow memory of the
|
||||
tracepoint jump). */
|
||||
unsigned char insn_and_shadow[0];
|
||||
};
|
||||
|
||||
/* Fast tracepoint FP's jump instruction to insert. */
|
||||
#define fast_tracepoint_jump_insn(fp) \
|
||||
((fp)->insn_and_shadow + 0)
|
||||
|
||||
/* The shadow memory of fast tracepoint jump FP. */
|
||||
#define fast_tracepoint_jump_shadow(fp) \
|
||||
((fp)->insn_and_shadow + (fp)->length)
|
||||
|
||||
|
||||
/* Return the fast tracepoint jump set at WHERE. */
|
||||
|
||||
static struct fast_tracepoint_jump *
|
||||
find_fast_tracepoint_jump_at (CORE_ADDR where)
|
||||
{
|
||||
struct process_info *proc = current_process ();
|
||||
struct fast_tracepoint_jump *jp;
|
||||
|
||||
for (jp = proc->fast_tracepoint_jumps; jp != NULL; jp = jp->next)
|
||||
if (jp->pc == where)
|
||||
return jp;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
fast_tracepoint_jump_here (CORE_ADDR where)
|
||||
{
|
||||
struct fast_tracepoint_jump *jp = find_fast_tracepoint_jump_at (where);
|
||||
|
||||
return (jp != NULL);
|
||||
}
|
||||
|
||||
int
|
||||
delete_fast_tracepoint_jump (struct fast_tracepoint_jump *todel)
|
||||
{
|
||||
struct fast_tracepoint_jump *bp, **bp_link;
|
||||
int ret;
|
||||
struct process_info *proc = current_process ();
|
||||
|
||||
bp = proc->fast_tracepoint_jumps;
|
||||
bp_link = &proc->fast_tracepoint_jumps;
|
||||
|
||||
while (bp)
|
||||
{
|
||||
if (bp == todel)
|
||||
{
|
||||
if (--bp->refcount == 0)
|
||||
{
|
||||
struct fast_tracepoint_jump *prev_bp_link = *bp_link;
|
||||
|
||||
/* Unlink it. */
|
||||
*bp_link = bp->next;
|
||||
|
||||
/* Since there can be breakpoints inserted in the same
|
||||
address range, we use `write_inferior_memory', which
|
||||
takes care of layering breakpoints on top of fast
|
||||
tracepoints, and on top of the buffer we pass it.
|
||||
This works because we've already unlinked the fast
|
||||
tracepoint jump above. Also note that we need to
|
||||
pass the current shadow contents, because
|
||||
write_inferior_memory updates any shadow memory with
|
||||
what we pass here, and we want that to be a nop. */
|
||||
ret = write_inferior_memory (bp->pc,
|
||||
fast_tracepoint_jump_shadow (bp),
|
||||
bp->length);
|
||||
if (ret != 0)
|
||||
{
|
||||
/* Something went wrong, relink the jump. */
|
||||
*bp_link = prev_bp_link;
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Failed to uninsert fast tracepoint jump "
|
||||
"at 0x%s (%s) while deleting it.\n",
|
||||
paddress (bp->pc), strerror (ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
free (bp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bp_link = &bp->next;
|
||||
bp = *bp_link;
|
||||
}
|
||||
}
|
||||
|
||||
warning ("Could not find fast tracepoint jump in list.");
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
struct fast_tracepoint_jump *
|
||||
set_fast_tracepoint_jump (CORE_ADDR where,
|
||||
unsigned char *insn, ULONGEST length)
|
||||
{
|
||||
struct process_info *proc = current_process ();
|
||||
struct fast_tracepoint_jump *jp;
|
||||
int err;
|
||||
|
||||
/* We refcount fast tracepoint jumps. Check if we already know
|
||||
about a jump at this address. */
|
||||
jp = find_fast_tracepoint_jump_at (where);
|
||||
if (jp != NULL)
|
||||
{
|
||||
jp->refcount++;
|
||||
return jp;
|
||||
}
|
||||
|
||||
/* We don't, so create a new object. Double the length, because the
|
||||
flexible array member holds both the jump insn, and the
|
||||
shadow. */
|
||||
jp = xcalloc (1, sizeof (*jp) + (length * 2));
|
||||
jp->pc = where;
|
||||
jp->length = length;
|
||||
memcpy (fast_tracepoint_jump_insn (jp), insn, length);
|
||||
jp->refcount = 1;
|
||||
|
||||
/* Note that there can be trap breakpoints inserted in the same
|
||||
address range. To access the original memory contents, we use
|
||||
`read_inferior_memory', which masks out breakpoints. */
|
||||
err = read_inferior_memory (where,
|
||||
fast_tracepoint_jump_shadow (jp), jp->length);
|
||||
if (err != 0)
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Failed to read shadow memory of"
|
||||
" fast tracepoint at 0x%s (%s).\n",
|
||||
paddress (where), strerror (err));
|
||||
free (jp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Link the jump in. */
|
||||
jp->inserted = 1;
|
||||
jp->next = proc->fast_tracepoint_jumps;
|
||||
proc->fast_tracepoint_jumps = jp;
|
||||
|
||||
/* Since there can be trap breakpoints inserted in the same address
|
||||
range, we use use `write_inferior_memory', which takes care of
|
||||
layering breakpoints on top of fast tracepoints, on top of the
|
||||
buffer we pass it. This works because we've already linked in
|
||||
the fast tracepoint jump above. Also note that we need to pass
|
||||
the current shadow contents, because write_inferior_memory
|
||||
updates any shadow memory with what we pass here, and we want
|
||||
that to be a nop. */
|
||||
err = write_inferior_memory (where, fast_tracepoint_jump_shadow (jp), length);
|
||||
if (err != 0)
|
||||
{
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Failed to insert fast tracepoint jump at 0x%s (%s).\n",
|
||||
paddress (where), strerror (err));
|
||||
|
||||
/* Unlink it. */
|
||||
proc->fast_tracepoint_jumps = jp->next;
|
||||
free (jp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return jp;
|
||||
}
|
||||
|
||||
void
|
||||
uninsert_fast_tracepoint_jumps_at (CORE_ADDR pc)
|
||||
{
|
||||
struct fast_tracepoint_jump *jp;
|
||||
int err;
|
||||
|
||||
jp = find_fast_tracepoint_jump_at (pc);
|
||||
if (jp == NULL)
|
||||
{
|
||||
/* This can happen when we remove all breakpoints while handling
|
||||
a step-over. */
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Could not find fast tracepoint jump at 0x%s "
|
||||
"in list (uninserting).\n",
|
||||
paddress (pc));
|
||||
return;
|
||||
}
|
||||
|
||||
if (jp->inserted)
|
||||
{
|
||||
jp->inserted = 0;
|
||||
|
||||
/* Since there can be trap breakpoints inserted in the same
|
||||
address range, we use use `write_inferior_memory', which
|
||||
takes care of layering breakpoints on top of fast
|
||||
tracepoints, and on top of the buffer we pass it. This works
|
||||
because we've already marked the fast tracepoint fast
|
||||
tracepoint jump uninserted above. Also note that we need to
|
||||
pass the current shadow contents, because
|
||||
write_inferior_memory updates any shadow memory with what we
|
||||
pass here, and we want that to be a nop. */
|
||||
err = write_inferior_memory (jp->pc,
|
||||
fast_tracepoint_jump_shadow (jp),
|
||||
jp->length);
|
||||
if (err != 0)
|
||||
{
|
||||
jp->inserted = 1;
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Failed to uninsert fast tracepoint jump at 0x%s (%s).\n",
|
||||
paddress (pc), strerror (err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
reinsert_fast_tracepoint_jumps_at (CORE_ADDR where)
|
||||
{
|
||||
struct fast_tracepoint_jump *jp;
|
||||
int err;
|
||||
|
||||
jp = find_fast_tracepoint_jump_at (where);
|
||||
if (jp == NULL)
|
||||
{
|
||||
/* This can happen when we remove breakpoints when a tracepoint
|
||||
hit causes a tracing stop, while handling a step-over. */
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Could not find fast tracepoint jump at 0x%s "
|
||||
"in list (reinserting).\n",
|
||||
paddress (where));
|
||||
return;
|
||||
}
|
||||
|
||||
if (jp->inserted)
|
||||
error ("Jump already inserted at reinsert time.");
|
||||
|
||||
jp->inserted = 1;
|
||||
|
||||
/* Since there can be trap breakpoints inserted in the same address
|
||||
range, we use `write_inferior_memory', which takes care of
|
||||
layering breakpoints on top of fast tracepoints, and on top of
|
||||
the buffer we pass it. This works because we've already marked
|
||||
the fast tracepoint jump inserted above. Also note that we need
|
||||
to pass the current shadow contents, because
|
||||
write_inferior_memory updates any shadow memory with what we pass
|
||||
here, and we want that to be a nop. */
|
||||
err = write_inferior_memory (where,
|
||||
fast_tracepoint_jump_shadow (jp), jp->length);
|
||||
if (err != 0)
|
||||
{
|
||||
jp->inserted = 0;
|
||||
|
||||
if (debug_threads)
|
||||
fprintf (stderr,
|
||||
"Failed to reinsert fast tracepoint jump at 0x%s (%s).\n",
|
||||
paddress (where), strerror (err));
|
||||
}
|
||||
}
|
||||
|
||||
struct breakpoint *
|
||||
set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR))
|
||||
{
|
||||
@ -215,8 +513,17 @@ delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel)
|
||||
|
||||
*bp_link = bp->next;
|
||||
|
||||
ret = (*the_target->write_memory) (bp->pc, bp->old_data,
|
||||
breakpoint_len);
|
||||
/* Since there can be trap breakpoints inserted in the
|
||||
same address range, we use `write_inferior_memory',
|
||||
which takes care of layering breakpoints on top of
|
||||
fast tracepoints, and on top of the buffer we pass
|
||||
it. This works because we've already unlinked the
|
||||
fast tracepoint jump above. Also note that we need
|
||||
to pass the current shadow contents, because
|
||||
write_inferior_memory updates any shadow memory with
|
||||
what we pass here, and we want that to be a nop. */
|
||||
ret = write_inferior_memory (bp->pc, bp->old_data,
|
||||
breakpoint_len);
|
||||
if (ret != 0)
|
||||
{
|
||||
/* Something went wrong, relink the breakpoint. */
|
||||
@ -426,8 +733,16 @@ uninsert_raw_breakpoint (struct raw_breakpoint *bp)
|
||||
int err;
|
||||
|
||||
bp->inserted = 0;
|
||||
err = (*the_target->write_memory) (bp->pc, bp->old_data,
|
||||
breakpoint_len);
|
||||
/* Since there can be fast tracepoint jumps inserted in the same
|
||||
address range, we use `write_inferior_memory', which takes
|
||||
care of layering breakpoints on top of fast tracepoints, and
|
||||
on top of the buffer we pass it. This works because we've
|
||||
already unlinked the fast tracepoint jump above. Also note
|
||||
that we need to pass the current shadow contents, because
|
||||
write_inferior_memory updates any shadow memory with what we
|
||||
pass here, and we want that to be a nop. */
|
||||
err = write_inferior_memory (bp->pc, bp->old_data,
|
||||
breakpoint_len);
|
||||
if (err != 0)
|
||||
{
|
||||
bp->inserted = 1;
|
||||
@ -621,9 +936,39 @@ check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
|
||||
{
|
||||
struct process_info *proc = current_process ();
|
||||
struct raw_breakpoint *bp = proc->raw_breakpoints;
|
||||
struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps;
|
||||
CORE_ADDR mem_end = mem_addr + mem_len;
|
||||
int disabled_one = 0;
|
||||
|
||||
for (; jp != NULL; jp = jp->next)
|
||||
{
|
||||
CORE_ADDR bp_end = jp->pc + jp->length;
|
||||
CORE_ADDR start, end;
|
||||
int copy_offset, copy_len, buf_offset;
|
||||
|
||||
if (mem_addr >= bp_end)
|
||||
continue;
|
||||
if (jp->pc >= mem_end)
|
||||
continue;
|
||||
|
||||
start = jp->pc;
|
||||
if (mem_addr > start)
|
||||
start = mem_addr;
|
||||
|
||||
end = bp_end;
|
||||
if (end > mem_end)
|
||||
end = mem_end;
|
||||
|
||||
copy_len = end - start;
|
||||
copy_offset = start - jp->pc;
|
||||
buf_offset = start - mem_addr;
|
||||
|
||||
if (jp->inserted)
|
||||
memcpy (buf + buf_offset,
|
||||
fast_tracepoint_jump_shadow (jp) + copy_offset,
|
||||
copy_len);
|
||||
}
|
||||
|
||||
for (; bp != NULL; bp = bp->next)
|
||||
{
|
||||
CORE_ADDR bp_end = bp->pc + breakpoint_len;
|
||||
@ -665,9 +1010,42 @@ check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
|
||||
{
|
||||
struct process_info *proc = current_process ();
|
||||
struct raw_breakpoint *bp = proc->raw_breakpoints;
|
||||
struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps;
|
||||
CORE_ADDR mem_end = mem_addr + mem_len;
|
||||
int disabled_one = 0;
|
||||
|
||||
/* First fast tracepoint jumps, then breakpoint traps on top. */
|
||||
|
||||
for (; jp != NULL; jp = jp->next)
|
||||
{
|
||||
CORE_ADDR jp_end = jp->pc + jp->length;
|
||||
CORE_ADDR start, end;
|
||||
int copy_offset, copy_len, buf_offset;
|
||||
|
||||
if (mem_addr >= jp_end)
|
||||
continue;
|
||||
if (jp->pc >= mem_end)
|
||||
continue;
|
||||
|
||||
start = jp->pc;
|
||||
if (mem_addr > start)
|
||||
start = mem_addr;
|
||||
|
||||
end = jp_end;
|
||||
if (end > mem_end)
|
||||
end = mem_end;
|
||||
|
||||
copy_len = end - start;
|
||||
copy_offset = start - jp->pc;
|
||||
buf_offset = start - mem_addr;
|
||||
|
||||
memcpy (fast_tracepoint_jump_shadow (jp) + copy_offset,
|
||||
buf + buf_offset, copy_len);
|
||||
if (jp->inserted)
|
||||
memcpy (buf + buf_offset,
|
||||
fast_tracepoint_jump_insn (jp) + copy_offset, copy_len);
|
||||
}
|
||||
|
||||
for (; bp != NULL; bp = bp->next)
|
||||
{
|
||||
CORE_ADDR bp_end = bp->pc + breakpoint_len;
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
/* Breakpoints are opaque. */
|
||||
struct breakpoint;
|
||||
struct fast_tracepoint_jump;
|
||||
|
||||
/* Create a new GDB breakpoint at WHERE. Returns -1 if breakpoints
|
||||
are not supported on this target, 0 otherwise. */
|
||||
@ -116,4 +117,30 @@ void free_all_breakpoints (struct process_info *proc);
|
||||
|
||||
void validate_breakpoints (void);
|
||||
|
||||
/* Insert a fast tracepoint jump at WHERE, using instruction INSN, of
|
||||
LENGTH bytes. */
|
||||
|
||||
struct fast_tracepoint_jump *set_fast_tracepoint_jump (CORE_ADDR where,
|
||||
unsigned char *insn,
|
||||
ULONGEST length);
|
||||
|
||||
/* Delete fast tracepoint jump TODEL from our tables, and uninsert if
|
||||
from memory. */
|
||||
|
||||
int delete_fast_tracepoint_jump (struct fast_tracepoint_jump *todel);
|
||||
|
||||
/* Returns true if there's fast tracepoint jump set at WHERE. */
|
||||
|
||||
int fast_tracepoint_jump_here (CORE_ADDR);
|
||||
|
||||
/* Uninsert fast tracepoint jumps at WHERE (and change their status to
|
||||
uninserted). This still leaves the tracepoints in the table. */
|
||||
|
||||
void uninsert_fast_tracepoint_jumps_at (CORE_ADDR pc);
|
||||
|
||||
/* Reinsert fast tracepoint jumps at WHERE (and change their status to
|
||||
inserted). */
|
||||
|
||||
void reinsert_fast_tracepoint_jumps_at (CORE_ADDR where);
|
||||
|
||||
#endif /* MEM_BREAK_H */
|
||||
|
@ -30,6 +30,8 @@ static int num_registers;
|
||||
|
||||
const char **gdbserver_expedite_regs;
|
||||
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
|
||||
struct regcache *
|
||||
get_thread_regcache (struct thread_info *thread, int fetch)
|
||||
{
|
||||
@ -82,9 +84,12 @@ regcache_invalidate (void)
|
||||
for_each_inferior (&all_threads, regcache_invalidate_one);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct regcache *
|
||||
init_register_cache (struct regcache *regcache, unsigned char *regbuf)
|
||||
{
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
if (regbuf == NULL)
|
||||
{
|
||||
/* Make sure to zero-initialize the register cache when it is
|
||||
@ -95,6 +100,11 @@ init_register_cache (struct regcache *regcache, unsigned char *regbuf)
|
||||
regcache->registers_owned = 1;
|
||||
}
|
||||
else
|
||||
#else
|
||||
if (regbuf == NULL)
|
||||
fatal ("init_register_cache: can't allocate memory from the heap");
|
||||
else
|
||||
#endif
|
||||
{
|
||||
regcache->registers = regbuf;
|
||||
regcache->registers_owned = 0;
|
||||
@ -105,6 +115,8 @@ init_register_cache (struct regcache *regcache, unsigned char *regbuf)
|
||||
return regcache;
|
||||
}
|
||||
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
|
||||
struct regcache *
|
||||
new_register_cache (void)
|
||||
{
|
||||
@ -122,11 +134,14 @@ free_register_cache (struct regcache *regcache)
|
||||
{
|
||||
if (regcache)
|
||||
{
|
||||
free (regcache->registers);
|
||||
if (regcache->registers_owned)
|
||||
free (regcache->registers);
|
||||
free (regcache);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
regcache_cpy (struct regcache *dst, struct regcache *src)
|
||||
{
|
||||
@ -134,6 +149,7 @@ regcache_cpy (struct regcache *dst, struct regcache *src)
|
||||
dst->registers_valid = src->registers_valid;
|
||||
}
|
||||
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
static void
|
||||
realloc_register_cache (struct inferior_list_entry *thread_p)
|
||||
{
|
||||
@ -146,15 +162,18 @@ realloc_register_cache (struct inferior_list_entry *thread_p)
|
||||
free_register_cache (regcache);
|
||||
set_inferior_regcache_data (thread, new_register_cache ());
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
set_register_cache (struct reg *regs, int n)
|
||||
{
|
||||
int offset, i;
|
||||
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
/* Before changing the register cache internal layout, flush the
|
||||
contents of valid caches back to the threads. */
|
||||
regcache_invalidate ();
|
||||
#endif
|
||||
|
||||
reg_defs = regs;
|
||||
num_registers = n;
|
||||
@ -172,8 +191,10 @@ set_register_cache (struct reg *regs, int n)
|
||||
if (2 * register_bytes + 32 > PBUFSIZ)
|
||||
fatal ("Register packet size exceeds PBUFSIZ.");
|
||||
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
/* Re-allocate all pre-existing register caches. */
|
||||
for_each_inferior (&all_threads, realloc_register_cache);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
@ -182,6 +203,8 @@ register_cache_size (void)
|
||||
return register_bytes;
|
||||
}
|
||||
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
|
||||
void
|
||||
registers_to_string (struct regcache *regcache, char *buf)
|
||||
{
|
||||
@ -236,6 +259,8 @@ find_register_by_number (int n)
|
||||
return ®_defs[n];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
register_size (int n)
|
||||
{
|
||||
@ -266,6 +291,8 @@ supply_regblock (struct regcache *regcache, const void *buf)
|
||||
memset (regcache->registers, 0, register_bytes);
|
||||
}
|
||||
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
|
||||
void
|
||||
supply_register_by_name (struct regcache *regcache,
|
||||
const char *name, const void *buf)
|
||||
@ -273,12 +300,16 @@ supply_register_by_name (struct regcache *regcache,
|
||||
supply_register (regcache, find_regno (name), buf);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
collect_register (struct regcache *regcache, int n, void *buf)
|
||||
{
|
||||
memcpy (buf, register_data (regcache, n, 1), register_size (n));
|
||||
}
|
||||
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
|
||||
void
|
||||
collect_register_as_string (struct regcache *regcache, int n, char *buf)
|
||||
{
|
||||
@ -318,3 +349,5 @@ regcache_write_pc (struct regcache *regcache, CORE_ADDR pc)
|
||||
internal_error (__FILE__, __LINE__,
|
||||
"regcache_write_pc: Unable to update PC");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1127,7 +1127,7 @@ write_enn (char *buf)
|
||||
}
|
||||
|
||||
void
|
||||
convert_int_to_ascii (unsigned char *from, char *to, int n)
|
||||
convert_int_to_ascii (const unsigned char *from, char *to, int n)
|
||||
{
|
||||
int nib;
|
||||
int ch;
|
||||
@ -1144,7 +1144,7 @@ convert_int_to_ascii (unsigned char *from, char *to, int n)
|
||||
|
||||
|
||||
void
|
||||
convert_ascii_to_int (char *from, unsigned char *to, int n)
|
||||
convert_ascii_to_int (const char *from, unsigned char *to, int n)
|
||||
{
|
||||
int nib1, nib2;
|
||||
while (n--)
|
||||
@ -1354,7 +1354,7 @@ decode_m_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr)
|
||||
|
||||
void
|
||||
decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr,
|
||||
unsigned char *to)
|
||||
unsigned char **to_p)
|
||||
{
|
||||
int i = 0;
|
||||
char ch;
|
||||
@ -1372,12 +1372,15 @@ decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr,
|
||||
*len_ptr |= fromhex (ch) & 0x0f;
|
||||
}
|
||||
|
||||
convert_ascii_to_int (&from[i++], to, *len_ptr);
|
||||
if (*to_p == NULL)
|
||||
*to_p = xmalloc (*len_ptr);
|
||||
|
||||
convert_ascii_to_int (&from[i++], *to_p, *len_ptr);
|
||||
}
|
||||
|
||||
int
|
||||
decode_X_packet (char *from, int packet_len, CORE_ADDR *mem_addr_ptr,
|
||||
unsigned int *len_ptr, unsigned char *to)
|
||||
unsigned int *len_ptr, unsigned char **to_p)
|
||||
{
|
||||
int i = 0;
|
||||
char ch;
|
||||
@ -1395,8 +1398,11 @@ decode_X_packet (char *from, int packet_len, CORE_ADDR *mem_addr_ptr,
|
||||
*len_ptr |= fromhex (ch) & 0x0f;
|
||||
}
|
||||
|
||||
if (*to_p == NULL)
|
||||
*to_p = xmalloc (*len_ptr);
|
||||
|
||||
if (remote_unescape_input ((const gdb_byte *) &from[i], packet_len - i,
|
||||
to, *len_ptr) != *len_ptr)
|
||||
*to_p, *len_ptr) != *len_ptr)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
@ -1565,6 +1571,101 @@ look_up_one_symbol (const char *name, CORE_ADDR *addrp, int may_ask_gdb)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Relocate an instruction to execute at a different address. OLDLOC
|
||||
is the address in the inferior memory where the instruction to
|
||||
relocate is currently at. On input, TO points to the destination
|
||||
where we want the instruction to be copied (and possibly adjusted)
|
||||
to. On output, it points to one past the end of the resulting
|
||||
instruction(s). The effect of executing the instruction at TO
|
||||
shall be the same as if executing it at FROM. For example, call
|
||||
instructions that implicitly push the return address on the stack
|
||||
should be adjusted to return to the instruction after OLDLOC;
|
||||
relative branches, and other PC-relative instructions need the
|
||||
offset adjusted; etc. Returns 0 on success, -1 on failure. */
|
||||
|
||||
int
|
||||
relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc)
|
||||
{
|
||||
char own_buf[266];
|
||||
int len;
|
||||
ULONGEST written = 0;
|
||||
|
||||
/* Send the request. */
|
||||
strcpy (own_buf, "qRelocInsn:");
|
||||
sprintf (own_buf, "qRelocInsn:%s;%s", paddress (oldloc),
|
||||
paddress (*to));
|
||||
if (putpkt (own_buf) < 0)
|
||||
return -1;
|
||||
|
||||
/* FIXME: Eventually add buffer overflow checking (to getpkt?) */
|
||||
len = getpkt (own_buf);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
/* We ought to handle pretty much any packet at this point while we
|
||||
wait for the qRelocInsn "response". That requires re-entering
|
||||
the main loop. For now, this is an adequate approximation; allow
|
||||
GDB to access memory. */
|
||||
while (own_buf[0] == 'm' || own_buf[0] == 'M' || own_buf[0] == 'X')
|
||||
{
|
||||
CORE_ADDR mem_addr;
|
||||
unsigned char *mem_buf = NULL;
|
||||
unsigned int mem_len;
|
||||
|
||||
if (own_buf[0] == 'm')
|
||||
{
|
||||
decode_m_packet (&own_buf[1], &mem_addr, &mem_len);
|
||||
mem_buf = xmalloc (mem_len);
|
||||
if (read_inferior_memory (mem_addr, mem_buf, mem_len) == 0)
|
||||
convert_int_to_ascii (mem_buf, own_buf, mem_len);
|
||||
else
|
||||
write_enn (own_buf);
|
||||
}
|
||||
else if (own_buf[0] == 'X')
|
||||
{
|
||||
if (decode_X_packet (&own_buf[1], len - 1, &mem_addr,
|
||||
&mem_len, &mem_buf) < 0
|
||||
|| write_inferior_memory (mem_addr, mem_buf, mem_len) != 0)
|
||||
write_enn (own_buf);
|
||||
else
|
||||
write_ok (own_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
decode_M_packet (&own_buf[1], &mem_addr, &mem_len, &mem_buf);
|
||||
if (write_inferior_memory (mem_addr, mem_buf, mem_len) == 0)
|
||||
write_ok (own_buf);
|
||||
else
|
||||
write_enn (own_buf);
|
||||
}
|
||||
free (mem_buf);
|
||||
if (putpkt (own_buf) < 0)
|
||||
return -1;
|
||||
len = getpkt (own_buf);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (own_buf[0] == 'E')
|
||||
{
|
||||
warning ("An error occurred while relocating an instruction: %s\n",
|
||||
own_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strncmp (own_buf, "qRelocInsn:", strlen ("qRelocInsn:")) != 0)
|
||||
{
|
||||
warning ("Malformed response to qRelocInsn, ignoring: %s\n",
|
||||
own_buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
unpack_varlen_hex (own_buf + strlen ("qRelocInsn:"), &written);
|
||||
|
||||
*to += written;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
monitor_output (const char *msg)
|
||||
{
|
||||
|
@ -920,6 +920,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
|
||||
we access breakpoint shadows. */
|
||||
validate_breakpoints ();
|
||||
|
||||
if (target_supports_tracepoints ())
|
||||
tracepoint_look_up_symbols ();
|
||||
|
||||
if (target_running () && the_target->look_up_symbols != NULL)
|
||||
(*the_target->look_up_symbols) ();
|
||||
|
||||
@ -1338,6 +1341,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
|
||||
&& (own_buf[10] == ':' || own_buf[10] == '\0'))
|
||||
{
|
||||
char *p = &own_buf[10];
|
||||
int gdb_supports_qRelocInsn = 0;
|
||||
|
||||
/* Start processing qSupported packet. */
|
||||
target_process_qsupported (NULL);
|
||||
@ -1372,6 +1376,11 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
|
||||
if (target_supports_multi_process ())
|
||||
multi_process = 1;
|
||||
}
|
||||
else if (strcmp (p, "qRelocInsn+") == 0)
|
||||
{
|
||||
/* GDB supports relocate instruction requests. */
|
||||
gdb_supports_qRelocInsn = 1;
|
||||
}
|
||||
else
|
||||
target_process_qsupported (p);
|
||||
|
||||
@ -1422,6 +1431,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
|
||||
strcat (own_buf, ";TraceStateVariables+");
|
||||
strcat (own_buf, ";TracepointSource+");
|
||||
strcat (own_buf, ";DisconnectedTracing+");
|
||||
if (gdb_supports_qRelocInsn && target_supports_fast_tracepoints ())
|
||||
strcat (own_buf, ";FastTracepoints+");
|
||||
}
|
||||
|
||||
return;
|
||||
@ -2122,6 +2133,7 @@ handle_status (char *own_buf)
|
||||
else
|
||||
{
|
||||
pause_all (0);
|
||||
stabilize_threads ();
|
||||
gdb_wants_all_threads_stopped ();
|
||||
|
||||
if (all_threads.head)
|
||||
@ -2821,7 +2833,7 @@ process_serial_event (void)
|
||||
break;
|
||||
case 'M':
|
||||
require_running (own_buf);
|
||||
decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf);
|
||||
decode_M_packet (&own_buf[1], &mem_addr, &len, &mem_buf);
|
||||
if (write_memory (mem_addr, mem_buf, len) == 0)
|
||||
write_ok (own_buf);
|
||||
else
|
||||
@ -2830,7 +2842,7 @@ process_serial_event (void)
|
||||
case 'X':
|
||||
require_running (own_buf);
|
||||
if (decode_X_packet (&own_buf[1], packet_len - 1,
|
||||
&mem_addr, &len, mem_buf) < 0
|
||||
&mem_addr, &len, &mem_buf) < 0
|
||||
|| write_memory (mem_addr, mem_buf, len) != 0)
|
||||
write_enn (own_buf);
|
||||
else
|
||||
|
@ -221,6 +221,7 @@ struct dll_info
|
||||
struct sym_cache;
|
||||
struct breakpoint;
|
||||
struct raw_breakpoint;
|
||||
struct fast_tracepoint_jump;
|
||||
struct process_info_private;
|
||||
|
||||
struct process_info
|
||||
@ -244,6 +245,9 @@ struct process_info
|
||||
/* The list of raw memory breakpoints. */
|
||||
struct raw_breakpoint *raw_breakpoints;
|
||||
|
||||
/* The list of installed fast tracepoints. */
|
||||
struct fast_tracepoint_jump *fast_tracepoint_jumps;
|
||||
|
||||
/* Private target data. */
|
||||
struct process_info_private *private;
|
||||
};
|
||||
@ -379,8 +383,8 @@ void initialize_async_io (void);
|
||||
void enable_async_io (void);
|
||||
void disable_async_io (void);
|
||||
void check_remote_input_interrupt_request (void);
|
||||
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 convert_ascii_to_int (const char *from, unsigned char *to, int n);
|
||||
void convert_int_to_ascii (const 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, ptid_t ptid,
|
||||
@ -391,9 +395,9 @@ void decode_address (CORE_ADDR *addrp, const char *start, int len);
|
||||
void decode_m_packet (char *from, CORE_ADDR * mem_addr_ptr,
|
||||
unsigned int *len_ptr);
|
||||
void decode_M_packet (char *from, CORE_ADDR * mem_addr_ptr,
|
||||
unsigned int *len_ptr, unsigned char *to);
|
||||
unsigned int *len_ptr, unsigned char **to_p);
|
||||
int decode_X_packet (char *from, int packet_len, CORE_ADDR * mem_addr_ptr,
|
||||
unsigned int *len_ptr, unsigned char *to);
|
||||
unsigned int *len_ptr, unsigned char **to_p);
|
||||
int decode_xfer_write (char *buf, int packet_len, char **annex,
|
||||
CORE_ADDR *offset, unsigned int *len,
|
||||
unsigned char *data);
|
||||
@ -412,6 +416,8 @@ char *unpack_varlen_hex (char *buff, ULONGEST *result);
|
||||
void clear_symbol_cache (struct sym_cache **symcache_p);
|
||||
int look_up_one_symbol (const char *name, CORE_ADDR *addrp, int may_ask_gdb);
|
||||
|
||||
int relocate_instruction (CORE_ADDR *to, CORE_ADDR oldloc);
|
||||
|
||||
void monitor_output (const char *msg);
|
||||
|
||||
char *xml_escape_text (const char *text);
|
||||
@ -507,11 +513,15 @@ char *phex_nz (ULONGEST l, int sizeof_l);
|
||||
|
||||
/* Functions from tracepoint.c */
|
||||
|
||||
int in_process_agent_loaded (void);
|
||||
|
||||
void initialize_tracepoint (void);
|
||||
|
||||
extern int tracing;
|
||||
extern int disconnected_tracing;
|
||||
|
||||
void tracepoint_look_up_symbols (void);
|
||||
|
||||
void stop_tracing (void);
|
||||
|
||||
int handle_tracepoint_general_set (char *own_buf);
|
||||
@ -532,6 +542,37 @@ int fetch_traceframe_registers (int tfnum,
|
||||
struct regcache *regcache,
|
||||
int regnum);
|
||||
|
||||
/* If a thread is determined to be collecting a fast tracepoint, this
|
||||
structure holds the collect status. */
|
||||
|
||||
struct fast_tpoint_collect_status
|
||||
{
|
||||
/* The tracepoint that is presently being collected. */
|
||||
int tpoint_num;
|
||||
CORE_ADDR tpoint_addr;
|
||||
|
||||
/* The address range in the jump pad of where the original
|
||||
instruction the tracepoint jump was inserted was relocated
|
||||
to. */
|
||||
CORE_ADDR adjusted_insn_addr;
|
||||
CORE_ADDR adjusted_insn_addr_end;
|
||||
};
|
||||
|
||||
int fast_tracepoint_collecting (CORE_ADDR thread_area,
|
||||
CORE_ADDR stop_pc,
|
||||
struct fast_tpoint_collect_status *status);
|
||||
void force_unlock_trace_buffer (void);
|
||||
|
||||
int handle_tracepoint_bkpts (struct thread_info *tinfo, CORE_ADDR stop_pc);
|
||||
|
||||
#ifdef IN_PROCESS_AGENT
|
||||
void initialize_low_tracepoint (void);
|
||||
void supply_fast_tracepoint_registers (struct regcache *regcache,
|
||||
const unsigned char *regs);
|
||||
#else
|
||||
void stop_tracing (void);
|
||||
#endif
|
||||
|
||||
/* Version information, from version.c. */
|
||||
extern const char version[];
|
||||
extern const char host_name[];
|
||||
|
@ -324,6 +324,31 @@ struct target_ops
|
||||
|
||||
/* Cancel all pending breakpoints hits in all threads. */
|
||||
void (*cancel_breakpoints) (void);
|
||||
|
||||
/* Stabilize all threads. That is, force them out of jump pads. */
|
||||
void (*stabilize_threads) (void);
|
||||
|
||||
/* Install a fast tracepoint jump pad. TPOINT is the address of the
|
||||
tracepoint internal object as used by the IPA agent. TPADDR is
|
||||
the address of tracepoint. COLLECTOR is address of the function
|
||||
the jump pad redirects to. LOCKADDR is the address of the jump
|
||||
pad lock object. ORIG_SIZE is the size in bytes of the
|
||||
instruction at TPADDR. JUMP_ENTRY points to the address of the
|
||||
jump pad entry, and on return holds the address past the end of
|
||||
the created jump pad. JJUMP_PAD_INSN is a buffer containing a
|
||||
copy of the instruction at TPADDR. ADJUST_INSN_ADDR and
|
||||
ADJUST_INSN_ADDR_END are output parameters that return the
|
||||
address range where the instruction at TPADDR was relocated
|
||||
to. */
|
||||
int (*install_fast_tracepoint_jump_pad) (CORE_ADDR tpoint, CORE_ADDR tpaddr,
|
||||
CORE_ADDR collector,
|
||||
CORE_ADDR lockaddr,
|
||||
ULONGEST orig_size,
|
||||
CORE_ADDR *jump_entry,
|
||||
unsigned char *jjump_pad_insn,
|
||||
ULONGEST *jjump_pad_insn_size,
|
||||
CORE_ADDR *adjusted_insn_addr,
|
||||
CORE_ADDR *adjusted_insn_addr_end);
|
||||
};
|
||||
|
||||
extern struct target_ops *the_target;
|
||||
@ -378,6 +403,9 @@ void set_target_ops (struct target_ops *);
|
||||
(the_target->supports_tracepoints \
|
||||
? (*the_target->supports_tracepoints) () : 0)
|
||||
|
||||
#define target_supports_fast_tracepoints() \
|
||||
(the_target->install_fast_tracepoint_jump_pad != NULL)
|
||||
|
||||
#define thread_stopped(thread) \
|
||||
(*the_target->thread_stopped) (thread)
|
||||
|
||||
@ -402,6 +430,28 @@ void set_target_ops (struct target_ops *);
|
||||
(*the_target->cancel_breakpoints) (); \
|
||||
} while (0)
|
||||
|
||||
#define stabilize_threads() \
|
||||
do \
|
||||
{ \
|
||||
if (the_target->stabilize_threads) \
|
||||
(*the_target->stabilize_threads) (); \
|
||||
} while (0)
|
||||
|
||||
#define install_fast_tracepoint_jump_pad(tpoint, tpaddr, \
|
||||
collector, lockaddr, \
|
||||
orig_size, \
|
||||
jump_entry, jjump_pad_insn, \
|
||||
jjump_pad_insn_size, \
|
||||
adjusted_insn_addr, \
|
||||
adjusted_insn_addr_end) \
|
||||
(*the_target->install_fast_tracepoint_jump_pad) (tpoint, tpaddr, \
|
||||
collector,lockaddr, \
|
||||
orig_size, jump_entry, \
|
||||
jjump_pad_insn, \
|
||||
jjump_pad_insn_size, \
|
||||
adjusted_insn_addr, \
|
||||
adjusted_insn_addr_end)
|
||||
|
||||
/* Start non-stop mode, returns 0 on success, -1 on failure. */
|
||||
|
||||
int start_non_stop (int nonstop);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,6 +28,14 @@
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#ifdef IN_PROCESS_AGENT
|
||||
# define PREFIX "ipa: "
|
||||
# define TOOLNAME "GDBserver in-process agent"
|
||||
#else
|
||||
# define PREFIX "gdbserver: "
|
||||
# define TOOLNAME "GDBserver"
|
||||
#endif
|
||||
|
||||
/* Generally useful subroutines used throughout the program. */
|
||||
|
||||
static void malloc_failure (size_t size) ATTR_NORETURN;
|
||||
@ -35,7 +43,7 @@ static void malloc_failure (size_t size) ATTR_NORETURN;
|
||||
static void
|
||||
malloc_failure (size_t size)
|
||||
{
|
||||
fprintf (stderr, "gdbserver: ran out of memory while trying to allocate %lu bytes\n",
|
||||
fprintf (stderr, PREFIX "ran out of memory while trying to allocate %lu bytes\n",
|
||||
(unsigned long) size);
|
||||
exit (1);
|
||||
}
|
||||
@ -107,6 +115,8 @@ xstrdup (const char *s)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
|
||||
/* Free a standard argv vector. */
|
||||
|
||||
void
|
||||
@ -124,6 +134,8 @@ freeargv (char **vector)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Print the system error message for errno, and also mention STRING
|
||||
as the file name for which the error was encountered.
|
||||
Then return to command level. */
|
||||
@ -153,13 +165,19 @@ perror_with_name (const char *string)
|
||||
void
|
||||
error (const char *string,...)
|
||||
{
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
extern jmp_buf toplevel;
|
||||
#endif
|
||||
va_list args;
|
||||
va_start (args, string);
|
||||
fflush (stdout);
|
||||
vfprintf (stderr, string, args);
|
||||
fprintf (stderr, "\n");
|
||||
#ifndef IN_PROCESS_AGENT
|
||||
longjmp (toplevel, 1);
|
||||
#else
|
||||
exit (1);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Print an error message and exit reporting failure.
|
||||
@ -172,7 +190,7 @@ fatal (const char *string,...)
|
||||
{
|
||||
va_list args;
|
||||
va_start (args, string);
|
||||
fprintf (stderr, "gdbserver: ");
|
||||
fprintf (stderr, PREFIX);
|
||||
vfprintf (stderr, string, args);
|
||||
fprintf (stderr, "\n");
|
||||
va_end (args);
|
||||
@ -185,7 +203,7 @@ warning (const char *string,...)
|
||||
{
|
||||
va_list args;
|
||||
va_start (args, string);
|
||||
fprintf (stderr, "gdbserver: ");
|
||||
fprintf (stderr, PREFIX);
|
||||
vfprintf (stderr, string, args);
|
||||
fprintf (stderr, "\n");
|
||||
va_end (args);
|
||||
@ -200,7 +218,7 @@ internal_error (const char *file, int line, const char *fmt, ...)
|
||||
va_start (args, fmt);
|
||||
|
||||
fprintf (stderr, "\
|
||||
%s:%d: A problem internal to GDBserver has been detected.\n", file, line);
|
||||
%s:%d: A problem internal to " TOOLNAME " has been detected.\n", file, line);
|
||||
vfprintf (stderr, fmt, args);
|
||||
fprintf (stderr, "\n");
|
||||
va_end (args);
|
||||
@ -208,7 +226,7 @@ internal_error (const char *file, int line, const char *fmt, ...)
|
||||
}
|
||||
|
||||
/* Temporary storage using circular buffer. */
|
||||
#define NUMCELLS 4
|
||||
#define NUMCELLS 10
|
||||
#define CELLSIZE 50
|
||||
|
||||
/* Return the next entry in the circular buffer. */
|
||||
|
Loading…
Reference in New Issue
Block a user