mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-21 01:12:32 +08:00
2009-10-19 Pedro Alves <pedro@codesourcery.com>
Stan Shebs <stan@codesourcery.com> Add base multi-executable/process support to GDB. gdb/ * Makefile.in (SFILES): Add progspace.c. (COMMON_OBS): Add progspace.o. * progspace.h: New. * progspace.c: New. * breakpoint.h (struct bp_target_info) <placed_address_space>: New field. (struct bp_location) <pspace>: New field. (struct breakpoint) <pspace>: New field. (bpstat_stop_status, breakpoint_here_p) (moribund_breakpoint_here_p, breakpoint_inserted_here_p) (regular_breakpoint_inserted_here_p) (software_breakpoint_inserted_here_p, breakpoint_thread_match) (set_default_breakpoint): Adjust prototypes. (remove_breakpoints_pid, breakpoint_program_space_exit): Declare. (insert_single_step_breakpoint, deprecated_insert_raw_breakpoint): Adjust prototypes. * breakpoint.c (executing_startup): Delete. (default_breakpoint_sspace): New. (breakpoint_restore_shadows): Skip if the address space doesn't match. (update_watchpoint): Record the frame's program space in the breakpoint location. (insert_bp_location): Record the address space in target_info. Adjust to pass the symbol space to solib_name_from_address. (breakpoint_program_space_exit): New. (insert_breakpoint_locations): Switch the symbol space and thread when inserting breakpoints. Don't insert breakpoints in a vfork parent waiting for vfork done if we're not attached to the vfork child. (remove_breakpoints_pid): New. (reattach_breakpoints): Switch to a thread of PID. Ignore breakpoints of other symbol spaces. (create_internal_breakpoint): Store the symbol space in the sal. (create_longjmp_master_breakpoint): Iterate over all symbol spaces. (update_breakpoints_after_exec): Ignore breakpoints for other symbol spaces. (remove_breakpoint): Rename to ... (remove_breakpoint_1): ... this. Pass the breakpoints symbol space to solib_name_from_address. (remove_breakpoint): New. (mark_breakpoints_out): Ignore breakpoints from other symbol spaces. (breakpoint_init_inferior): Ditto. (breakpoint_here_p): Add an address space argument and adjust to use breakpoint_address_match. (moribund_breakpoint_here_p): Ditto. (regular_breakpoint_inserted_here_p): Ditto. (breakpoint_inserted_here_p): Ditto. (software_breakpoint_inserted_here_p): Ditto. (breakpoint_thread_match): Ditto. (bpstat_check_location): Ditto. (bpstat_stop_status): Ditto. (print_breakpoint_location): If there's a location to print, switch the current symbol space. (print_one_breakpoint_location): Add `allflag' argument. (print_one_breakpoint): Ditto. Adjust. (do_captured_breakpoint_query): Adjust. (breakpoint_1): Adjust. (breakpoint_has_pc): Also match the symbol space. (describe_other_breakpoints): Add a symbol space argument and adjust. (set_default_breakpoint): Add a symbol space argument. Set default_breakpoint_sspace. (breakpoint_address_match): New. (check_duplicates_for): Add an address space argument, and adjust. (set_raw_breakpoint): Record the symbol space in the location and in the breakpoint. (set_longjmp_breakpoint): Skip longjmp master breakpoints from other symbol spaces. (remove_thread_event_breakpoints, remove_solib_event_breakpoints) (disable_breakpoints_in_shlibs): Skip breakpoints from other symbol spaces. (disable_breakpoints_in_unloaded_shlib): Match symbol spaces. (create_catchpoint): Set the symbol space in the sal. (disable_breakpoints_before_startup): Skip breakpoints from other symbol spaces. Set executing_startup in the current symbol space. (enable_breakpoints_after_startup): Clear executing_startup in the current symbol space. Skip breakpoints from other symbol spaces. (clone_momentary_breakpoint): Also copy the symbol space. (add_location_to_breakpoint): Set the location's symbol space. (bp_loc_is_permanent): Switch thread and symbol space. (create_breakpoint): Adjust. (expand_line_sal_maybe): Expand comment to mention symbol spaces. Switch thread and symbol space when reading memory. (parse_breakpoint_sals): Set the symbol space in the sal. (break_command_really): Ditto. (skip_prologue_sal): Switch and space. (resolve_sal_pc): Ditto. (watch_command_1): Record the symbol space in the sal. (create_ada_exception_breakpoint): Adjust. (clear_command): Adjust. Match symbol spaces. (update_global_location_list): Use breakpoint_address_match. (breakpoint_re_set_one): Switch thread and space. (breakpoint_re_set): Save symbol space. (breakpoint_re_set_thread): Also reset the symbol space. (deprecated_insert_raw_breakpoint): Add an address space argument. Adjust. (insert_single_step_breakpoint): Ditto. (single_step_breakpoint_inserted_here_p): Ditto. (clear_syscall_counts): New. (_initialize_breakpoint): Install it as inferior_exit observer. * exec.h: Include "progspace.h". (exec_bfd, exec_bfd_mtime): New defines. (exec_close): Declare. * exec.c: Include "gdbthread.h" and "progspace.h". (exec_bfd, exec_bfd_mtime, current_target_sections_1): Delete. (using_exec_ops): New. (exec_close_1): Rename to exec_close, and make public. (exec_close): Rename to exec_close_1, and adjust all callers. Add description. Remove target sections and close executables from all program spaces. (exec_file_attach): Add comment. (add_target_sections): Check on `using_exec_ops' to check if the target should be pushed. (remove_target_sections): Only unpush the target if there are no more target sections in any symbol space. * gdbcore.h: Include "exec.h". (exec_bfd, exec_bfd_mtime): Remove declarations. * frame.h (get_frame_program_space, get_frame_address_space) (frame_unwind_program_space): Declare. * frame.c (struct frame_info) <pspace, aspace>: New fields. (create_sentinel_frame): Add program space argument. Set the pspace and aspace fields of the frame object. (get_current_frame, create_new_frame): Adjust. (get_frame_program_space): New. (frame_unwind_program_space): New. (get_frame_address_space): New. * stack.c (print_frame_info): Adjust. (print_frame): Use the frame's program space. * gdbthread.h (any_live_thread_of_process): Declare. * thread.c (any_live_thread_of_process): New. (switch_to_thread): Switch the program space as well. (restore_selected_frame): Don't warn if trying to restore frame level 0. * inferior.h: Include "progspace.h". (detach_fork): Declare. (struct inferior) <removable, aspace, pspace> <vfork_parent, vfork_child, pending_detach> <waiting_for_vfork_done>: New fields. <terminal_info>: Remove field. <data, num_data>: New fields. (register_inferior_data, register_inferior_data_with_cleanup) (clear_inferior_data, set_inferior_data, inferior_data): Declare. (exit_inferior, exit_inferior_silent, exit_inferior_num_silent) (inferior_appeared): Declare. (find_inferior_pid): Typo. (find_inferior_id, find_inferior_for_program_space): Declare. (set_current_inferior, save_current_inferior, prune_inferiors) (number_of_inferiors): Declare. (inferior_list): Declare. * inferior.c: Include "gdbcore.h" and "symfile.h". (inferior_list): Make public. (delete_inferior_1): Always delete thread silently. (find_inferior_id): Make public. (current_inferior_): New. (current_inferior): Use it. (set_current_inferior): New. (restore_inferior): New. (save_current_inferior): New. (free_inferior): Free the per-inferior data. (add_inferior_silent): Allocate per-inferior data. Call inferior_appeared. (delete_threads_of_inferior): New. (delete_inferior_1): Adjust interface to take an inferior pointer. (delete_inferior): Adjust. (delete_inferior_silent): Adjust. (exit_inferior_1): New. (exit_inferior): New. (exit_inferior_silent): New. (exit_inferior_num_silent): New. (detach_inferior): Adjust. (inferior_appeared): New. (discard_all_inferiors): Adjust. (find_inferior_id): Make public. Assert pid is not zero. (find_inferior_for_program_space): New. (have_inferiors): Check if we have any inferior with pid not zero. (have_live_inferiors): Go over all pushed targets looking for process_stratum. (prune_inferiors): New. (number_of_inferiors): New. (print_inferior): Add executable column. Print vfork parent/child relationships. (inferior_command): Adjust to cope with not running inferiors. (remove_inferior_command): New. (add_inferior_command): New. (clone_inferior_command): New. (struct inferior_data): New. (struct inferior_data_registration): New. (struct inferior_data_registry): New. (inferior_data_registry): New. (register_inferior_data_with_cleanup): New. (register_inferior_data): New. (inferior_alloc_data): New. (inferior_free_data): New. (clear_inferior_data): New. (set_inferior_data): New. (inferior_data): New. (initialize_inferiors): New. (_initialize_inferiors): Register "add-inferior", "remove-inferior" and "clone-inferior" commands. * objfiles.h: Include "progspace.h". (struct objfile) <pspace>: New field. (symfile_objfile, object_files): Don't declare. (ALL_PSPACE_OBJFILES): New. (ALL_PSPACE_OBJFILES_SAFE): New. (ALL_OBJFILES, ALL_OBJFILES_SAFE): Adjust. (ALL_PSPACE_SYMTABS): New. (ALL_PRIMARY_SYMTABS): Adjust. (ALL_PSPACE_PRIMARY_SYMTABS): New. (ALL_PSYMTABS): Adjust. (ALL_PSPACE_PSYMTABS): New. * objfiles.c (object_files, symfile_objfile): Delete. (struct objfile_sspace_info): New. (objfiles_pspace_data): New. (objfiles_pspace_data_cleanup): New. (get_objfile_pspace_data): New. (objfiles_changed_p): Delete. (allocate_objfile): Set the objfile's program space. Adjust to reference objfiles_changed_p in pspace data. (free_objfile): Adjust to reference objfiles_changed_p in pspace data. (objfile_relocate): Ditto. (update_section_map): Add pspace argument. Adjust to iterate over objfiles in the passed in pspace. (find_pc_section): Delete sections and num_sections statics. Adjust to refer to program space's objfiles_changed_p. Adjust to refer to sections and num_sections store in the objfile's pspace data. (objfiles_changed): Adjust to reference objfiles_changed_p in pspace data. (_initialize_objfiles): New. * linespec.c (decode_all_digits, decode_dollar): Set the sal's program space. * source.c (current_source_pspace): New. (get_current_source_symtab_and_line): Set the sal's program space. (set_current_source_symtab_and_line): Set current_source_pspace. (select_source_symtab): Ditto. Use ALL_OBJFILES. (forget_cached_source_info): Iterate over all program spaces. * symfile.c (clear_symtab_users): Adjust. * symmisc.c (print_symbol_bcache_statistics): Iterate over all program spaces. (print_objfile_statistics): Ditto. (maintenance_print_msymbols): Ditto. (maintenance_print_objfiles): Ditto. (maintenance_info_symtabs): Ditto. (maintenance_info_psymtabs): Ditto. * symtab.h (SYMTAB_PSPACE): New. (struct symtab_and_line) <pspace>: New field. * symtab.c (init_sal): Clear the sal's program space. (find_pc_sect_symtab): Set the sal's program space. Switch thread and space. (append_expanded_sal): Add program space argument. Iterate over all program spaces. (expand_line_sal): Iterate over all program spaces. Switch program space. * target.h (enum target_waitkind) <TARGET_WAITKIND_VFORK_DONE>: New. (struct target_ops) <to_thread_address_space>: New field. (target_thread_address_space): Define. * target.c (target_detach): Only remove breakpoints from the inferior we're detaching. (target_thread_address_space): New. * defs.h (initialize_progspace): Declare. * top.c (gdb_init): Call it. * solist.h (struct so_list) <sspace>: New field. * solib.h (struct program_space): Forward declare. (solib_name_from_address): Adjust prototype. * solib.c (so_list_head): Replace with a macro referencing the program space. (update_solib_list): Set the so's program space. (solib_name_from_address): Add a program space argument and adjust. * solib-svr4.c (struct svr4_info) <pid>: Delete field. <interp_text_sect_low, interp_text_sect_high, interp_plt_sect_low> <interp_plt_sect_high>: New fields. (svr4_info_p, svr4_info): Delete. (solib_svr4_sspace_data): New. (get_svr4_info): Rewrite. (svr4_sspace_data_cleanup): New. (open_symbol_file_object): Adjust. (svr4_default_sos): Adjust. (svr4_fetch_objfile_link_map): Adjust. (interp_text_sect_low, interp_text_sect_high, interp_plt_sect_low) (interp_plt_sect_high): Delete. (svr4_in_dynsym_resolve_code): Adjust. (enable_break): Adjust. (svr4_clear_solib): Revert bit that removed the svr4_info here, and reinstate clearing debug_base, debug_loader_offset_p, debug_loader_offset and debug_loader_name. (_initialize_svr4_solib): Register solib_svr4_pspace_data. Don't install an inferior_exit observer anymore. * printcmd.c (struct display) <pspace>: New field. (display_command): Set the display's sspace. (do_one_display): Match the display's sspace. (display_uses_solib_p): Ditto. * linux-fork.c (detach_fork): Moved to infrun.c. (_initialize_linux_fork): Moved "detach-on-fork" command to infrun.c. * infrun.c (detach_fork): Moved from linux-fork.c. (proceed_after_vfork_done): New. (handle_vfork_child_exec_or_exit): New. (follow_exec_mode_replace, follow_exec_mode_keep) (follow_exec_mode_names, follow_exec_mode_string) (show_follow_exec_mode_string): New. (follow_exec): New. Reinstate the mark_breakpoints_out call. Remove shared libraries before attaching new executable. If user wants to keep the inferior, keep it. (displaced_step_fixup): Adjust to pass an address space to the breakpoints module. (resume): Ditto. (clear_proceed_status): In all-stop mode, always clear the proceed status of all threads. (prepare_to_proceed): Adjust to pass an address space to the breakpoints module. (proceed): Ditto. (adjust_pc_after_break): Ditto. (handle_inferior_event): When handling a process exit, switch the program space to the inferior's that had exited. Call handle_vfork_child_exec_or_exit. Adjust to pass an address space to the breakpoints module. In non-stop mode, when following a fork and detach-fork is off, also resume the other branch. Handle TARGET_WAITKIND_VFORK_DONE. Set the program space in sals. (normal_stop): Prune inferiors. (_initialize_infrun): Install the new "follow-exec-mode" command. "detach-on-fork" moved here. * regcache.h (get_regcache_aspace): Declare. * regcache.c (struct regcache) <aspace>: New field. (regcache_xmalloc): Clear the aspace. (get_regcache_aspace): New. (regcache_cpy): Copy the aspace field. (regcache_cpy_no_passthrough): Ditto. (get_thread_regcache): Fetch the thread's address space from the target, and store it in the regcache. * infcall.c (call_function_by_hand): Set the sal's pspace. * arch-utils.c (default_has_shared_address_space): New. * arch-utils.h (default_has_shared_address_space): Declare. * gdbarch.sh (has_shared_address_space): New. * gdbarch.h, gdbarch.c: Regenerate. * linux-tdep.c: Include auxv.h, target.h, elf/common.h. (linux_has_shared_address_space): New. (_initialize_linux_tdep): Declare. * arm-tdep.c (arm_software_single_step): Pass the frame's address space to insert_single_step_breakpoint. * arm-linux-tdep.c (arm_linux_software_single_step): Pass the frame's pspace to breakpoint functions. * cris-tdep.c (crisv32_single_step_through_delay): Ditto. (cris_software_single_step): Ditto. * mips-tdep.c (deal_with_atomic_sequence): Add frame argument. Pass the frame's pspace to breakpoint functions. (mips_software_single_step): Adjust. (mips_single_step_through_delay): Adjust. * rs6000-aix-tdep.c (rs6000_software_single_step): Adjust. * rs6000-tdep.c (ppc_deal_with_atomic_sequence): Adjust. * solib-irix.c (enable_break): Adjust to pass the current frame's address space to breakpoint functions. * sparc-tdep.c (sparc_software_single_step): Ditto. * spu-tdep.c (spu_software_single_step): Ditto. * alpha-tdep.c (alpha_software_single_step): Ditto. * record.c (record_wait): Adjust to pass an address space to the breakpoints module. * fork-child.c (fork_inferior): Set the new inferior's program and address spaces. * inf-ptrace.c (inf_ptrace_follow_fork): Copy the parent's program and address spaces. (inf_ptrace_attach): Set the inferior's program and address spaces. * linux-nat.c: Include "solib.h". (linux_child_follow_fork): Manage parent and child's program and address spaces. Clone the parent's program space if necessary. Don't wait for the vfork to be done here. Refuse to resume if following the vfork parent while leaving the child stopped. (resume_callback): Don't resume a vfork parent. (linux_nat_resume): Also check for pending events in the lp->waitstatus field. (linux_handle_extended_wait): Report TARGET_WAITKIND_VFORK_DONE events to the core. (stop_wait_callback): Don't wait for SIGSTOP on vfork parents. (cancel_breakpoint): Adjust. * linux-thread-db.c (thread_db_wait): Don't remove thread event breakpoints here. (thread_db_mourn_inferior): Don't mark breakpoints out here. Remove thread event breakpoints after mourning. * corelow.c: Include progspace.h. (core_open): Set the inferior's program and address spaces. * remote.c (remote_add_inferior): Set the new inferior's program and address spaces. (remote_start_remote): Update address spaces. (extended_remote_create_inferior_1): Don't init the thread list if we already debugging other inferiors. * darwin-nat.c (darwin_attach): Set the new inferior's program and address spaces. * gnu-nat.c (gnu_attach): Ditto. * go32-nat.c (go32_create_inferior): Ditto. * inf-ttrace.c (inf_ttrace_follow_fork, inf_ttrace_attach): Ditto. * monitor.c (monitor_open): Ditto. * nto-procfs.c (procfs_attach, procfs_create_inferior): Ditto. * procfs.c (do_attach): Ditto. * windows-nat.c (do_initial_windows_stuff): Ditto. * inflow.c (inferior_process_group) (terminal_init_inferior_with_pgrp, terminal_inferior, (terminal_ours_1, inflow_inferior_exit, copy_terminal_info) (child_terminal_info, new_tty_postfork, set_sigint_trap): Adjust to use per-inferior data instead of inferior->terminal_info. (inflow_inferior_data): New. (inflow_new_inferior): Delete. (inflow_inferior_data_cleanup): New. (get_inflow_inferior_data): New. * mi/mi-interp.c (mi_new_inferior): Rename to... (mi_inferior_appeared): ... this. (mi_interpreter_init): Adjust. * tui/tui-disasm.c: Include "progspace.h". (tui_set_disassem_content): Pass an address space to breakpoint_here_p. * NEWS: Mention multi-program debugging support. Mention new commands "add-inferior", "clone-inferior", "remove-inferior", "maint info program-spaces", and new option "set follow-exec-mode". 2009-10-19 Pedro Alves <pedro@codesourcery.com> Stan Shebs <stan@codesourcery.com> gdb/doc/ * observer.texi (new_inferior): Rename to... (inferior_appeared): ... this. 2009-10-19 Pedro Alves <pedro@codesourcery.com> Stan Shebs <stan@codesourcery.com> gdb/testsuite/ * gdb.base/foll-vfork.exp: Adjust to spell out "follow-fork". * gdb.base/foll-exec.exp: Adjust to expect a process id before "Executing new program". * gdb.base/foll-fork.exp: Adjust to spell out "follow-fork". * gdb.base/multi-forks.exp: Ditto. Adjust to the inferior being left listed after having been killed. * gdb.base/attach.exp: Adjust to spell out "symbol-file". * gdb.base/maint.exp: Adjust test. * Makefile.in (ALL_SUBDIRS): Add gdb.multi. * gdb.multi/Makefile.in: New. * gdb.multi/base.exp: New. * gdb.multi/goodbye.c: New. * gdb.multi/hangout.c: New. * gdb.multi/hello.c: New. * gdb.multi/bkpt-multi-exec.c: New. * gdb.multi/bkpt-multi-exec.exp: New. * gdb.multi/crashme.c: New. 2009-10-19 Pedro Alves <pedro@codesourcery.com> Stan Shebs <stan@codesourcery.com> gdb/doc/ * gdb.texinfo (Inferiors): Rename node to ... (Inferiors and Programs): ... this. Mention running multiple programs in the same debug session. <info inferiors>: Mention the new 'Executable' column if "info inferiors". Update examples. Document the "add-inferior", "clone-inferior", "remove-inferior" and "maint info program-spaces" commands. (Process): Rename node to... (Forks): ... this. Document "set|show follow-exec-mode".
This commit is contained in:
parent
8dcb1aa7ca
commit
6c95b8df7f
444
gdb/ChangeLog
444
gdb/ChangeLog
@ -1,3 +1,447 @@
|
||||
2009-10-19 Pedro Alves <pedro@codesourcery.com>
|
||||
Stan Shebs <stan@codesourcery.com>
|
||||
|
||||
Add base multi-executable/process support to GDB.
|
||||
|
||||
* Makefile.in (SFILES): Add progspace.c.
|
||||
(COMMON_OBS): Add progspace.o.
|
||||
* progspace.h: New.
|
||||
* progspace.c: New.
|
||||
|
||||
* breakpoint.h (struct bp_target_info) <placed_address_space>: New
|
||||
field.
|
||||
(struct bp_location) <pspace>: New field.
|
||||
(struct breakpoint) <pspace>: New field.
|
||||
(bpstat_stop_status, breakpoint_here_p)
|
||||
(moribund_breakpoint_here_p, breakpoint_inserted_here_p)
|
||||
(regular_breakpoint_inserted_here_p)
|
||||
(software_breakpoint_inserted_here_p, breakpoint_thread_match)
|
||||
(set_default_breakpoint): Adjust prototypes.
|
||||
(remove_breakpoints_pid, breakpoint_program_space_exit): Declare.
|
||||
(insert_single_step_breakpoint, deprecated_insert_raw_breakpoint):
|
||||
Adjust prototypes.
|
||||
* breakpoint.c (executing_startup): Delete.
|
||||
(default_breakpoint_sspace): New.
|
||||
(breakpoint_restore_shadows): Skip if the address space doesn't
|
||||
match.
|
||||
(update_watchpoint): Record the frame's program space in the
|
||||
breakpoint location.
|
||||
(insert_bp_location): Record the address space in target_info.
|
||||
Adjust to pass the symbol space to solib_name_from_address.
|
||||
(breakpoint_program_space_exit): New.
|
||||
(insert_breakpoint_locations): Switch the symbol space and thread
|
||||
when inserting breakpoints. Don't insert breakpoints in a vfork
|
||||
parent waiting for vfork done if we're not attached to the vfork
|
||||
child.
|
||||
(remove_breakpoints_pid): New.
|
||||
(reattach_breakpoints): Switch to a thread of PID. Ignore
|
||||
breakpoints of other symbol spaces.
|
||||
(create_internal_breakpoint): Store the symbol space in the sal.
|
||||
(create_longjmp_master_breakpoint): Iterate over all symbol
|
||||
spaces.
|
||||
(update_breakpoints_after_exec): Ignore breakpoints for other
|
||||
symbol spaces.
|
||||
(remove_breakpoint): Rename to ...
|
||||
(remove_breakpoint_1): ... this. Pass the breakpoints symbol
|
||||
space to solib_name_from_address.
|
||||
(remove_breakpoint): New.
|
||||
(mark_breakpoints_out): Ignore breakpoints from other symbol
|
||||
spaces.
|
||||
(breakpoint_init_inferior): Ditto.
|
||||
(breakpoint_here_p): Add an address space argument and adjust to
|
||||
use breakpoint_address_match.
|
||||
(moribund_breakpoint_here_p): Ditto.
|
||||
(regular_breakpoint_inserted_here_p): Ditto.
|
||||
(breakpoint_inserted_here_p): Ditto.
|
||||
(software_breakpoint_inserted_here_p): Ditto.
|
||||
(breakpoint_thread_match): Ditto.
|
||||
(bpstat_check_location): Ditto.
|
||||
(bpstat_stop_status): Ditto.
|
||||
(print_breakpoint_location): If there's a location to print,
|
||||
switch the current symbol space.
|
||||
(print_one_breakpoint_location): Add `allflag' argument.
|
||||
(print_one_breakpoint): Ditto. Adjust.
|
||||
(do_captured_breakpoint_query): Adjust.
|
||||
(breakpoint_1): Adjust.
|
||||
(breakpoint_has_pc): Also match the symbol space.
|
||||
(describe_other_breakpoints): Add a symbol space argument and
|
||||
adjust.
|
||||
(set_default_breakpoint): Add a symbol space argument. Set
|
||||
default_breakpoint_sspace.
|
||||
(breakpoint_address_match): New.
|
||||
(check_duplicates_for): Add an address space argument, and adjust.
|
||||
(set_raw_breakpoint): Record the symbol space in the location and
|
||||
in the breakpoint.
|
||||
(set_longjmp_breakpoint): Skip longjmp master breakpoints from
|
||||
other symbol spaces.
|
||||
(remove_thread_event_breakpoints, remove_solib_event_breakpoints)
|
||||
(disable_breakpoints_in_shlibs): Skip breakpoints from other
|
||||
symbol spaces.
|
||||
(disable_breakpoints_in_unloaded_shlib): Match symbol spaces.
|
||||
(create_catchpoint): Set the symbol space in the sal.
|
||||
(disable_breakpoints_before_startup): Skip breakpoints from other
|
||||
symbol spaces. Set executing_startup in the current symbol space.
|
||||
(enable_breakpoints_after_startup): Clear executing_startup in the
|
||||
current symbol space. Skip breakpoints from other symbol spaces.
|
||||
(clone_momentary_breakpoint): Also copy the symbol space.
|
||||
(add_location_to_breakpoint): Set the location's symbol space.
|
||||
(bp_loc_is_permanent): Switch thread and symbol space.
|
||||
(create_breakpoint): Adjust.
|
||||
(expand_line_sal_maybe): Expand comment to mention symbol spaces.
|
||||
Switch thread and symbol space when reading memory.
|
||||
(parse_breakpoint_sals): Set the symbol space in the sal.
|
||||
(break_command_really): Ditto.
|
||||
(skip_prologue_sal): Switch and space.
|
||||
(resolve_sal_pc): Ditto.
|
||||
(watch_command_1): Record the symbol space in the sal.
|
||||
(create_ada_exception_breakpoint): Adjust.
|
||||
(clear_command): Adjust. Match symbol spaces.
|
||||
(update_global_location_list): Use breakpoint_address_match.
|
||||
(breakpoint_re_set_one): Switch thread and space.
|
||||
(breakpoint_re_set): Save symbol space.
|
||||
(breakpoint_re_set_thread): Also reset the symbol space.
|
||||
(deprecated_insert_raw_breakpoint): Add an address space argument.
|
||||
Adjust.
|
||||
(insert_single_step_breakpoint): Ditto.
|
||||
(single_step_breakpoint_inserted_here_p): Ditto.
|
||||
(clear_syscall_counts): New.
|
||||
(_initialize_breakpoint): Install it as inferior_exit observer.
|
||||
|
||||
* exec.h: Include "progspace.h".
|
||||
(exec_bfd, exec_bfd_mtime): New defines.
|
||||
(exec_close): Declare.
|
||||
* exec.c: Include "gdbthread.h" and "progspace.h".
|
||||
(exec_bfd, exec_bfd_mtime, current_target_sections_1): Delete.
|
||||
(using_exec_ops): New.
|
||||
(exec_close_1): Rename to exec_close, and make public.
|
||||
(exec_close): Rename to exec_close_1, and adjust all callers. Add
|
||||
description. Remove target sections and close executables from
|
||||
all program spaces.
|
||||
(exec_file_attach): Add comment.
|
||||
(add_target_sections): Check on `using_exec_ops' to check if the
|
||||
target should be pushed.
|
||||
(remove_target_sections): Only unpush the target if there are no
|
||||
more target sections in any symbol space.
|
||||
* gdbcore.h: Include "exec.h".
|
||||
(exec_bfd, exec_bfd_mtime): Remove declarations.
|
||||
|
||||
* frame.h (get_frame_program_space, get_frame_address_space)
|
||||
(frame_unwind_program_space): Declare.
|
||||
* frame.c (struct frame_info) <pspace, aspace>: New fields.
|
||||
(create_sentinel_frame): Add program space argument. Set the
|
||||
pspace and aspace fields of the frame object.
|
||||
(get_current_frame, create_new_frame): Adjust.
|
||||
(get_frame_program_space): New.
|
||||
(frame_unwind_program_space): New.
|
||||
(get_frame_address_space): New.
|
||||
* stack.c (print_frame_info): Adjust.
|
||||
(print_frame): Use the frame's program space.
|
||||
|
||||
* gdbthread.h (any_live_thread_of_process): Declare.
|
||||
* thread.c (any_live_thread_of_process): New.
|
||||
(switch_to_thread): Switch the program space as well.
|
||||
(restore_selected_frame): Don't warn if trying to restore frame
|
||||
level 0.
|
||||
|
||||
* inferior.h: Include "progspace.h".
|
||||
(detach_fork): Declare.
|
||||
(struct inferior) <removable, aspace, pspace>
|
||||
<vfork_parent, vfork_child, pending_detach>
|
||||
<waiting_for_vfork_done>: New fields.
|
||||
<terminal_info>: Remove field.
|
||||
<data, num_data>: New fields.
|
||||
(register_inferior_data, register_inferior_data_with_cleanup)
|
||||
(clear_inferior_data, set_inferior_data, inferior_data): Declare.
|
||||
(exit_inferior, exit_inferior_silent, exit_inferior_num_silent)
|
||||
(inferior_appeared): Declare.
|
||||
(find_inferior_pid): Typo.
|
||||
(find_inferior_id, find_inferior_for_program_space): Declare.
|
||||
(set_current_inferior, save_current_inferior, prune_inferiors)
|
||||
(number_of_inferiors): Declare.
|
||||
(inferior_list): Declare.
|
||||
* inferior.c: Include "gdbcore.h" and "symfile.h".
|
||||
(inferior_list): Make public.
|
||||
(delete_inferior_1): Always delete thread silently.
|
||||
(find_inferior_id): Make public.
|
||||
(current_inferior_): New.
|
||||
(current_inferior): Use it.
|
||||
(set_current_inferior): New.
|
||||
(restore_inferior): New.
|
||||
(save_current_inferior): New.
|
||||
(free_inferior): Free the per-inferior data.
|
||||
(add_inferior_silent): Allocate per-inferior data.
|
||||
Call inferior_appeared.
|
||||
(delete_threads_of_inferior): New.
|
||||
(delete_inferior_1): Adjust interface to take an inferior pointer.
|
||||
(delete_inferior): Adjust.
|
||||
(delete_inferior_silent): Adjust.
|
||||
(exit_inferior_1): New.
|
||||
(exit_inferior): New.
|
||||
(exit_inferior_silent): New.
|
||||
(exit_inferior_num_silent): New.
|
||||
(detach_inferior): Adjust.
|
||||
(inferior_appeared): New.
|
||||
(discard_all_inferiors): Adjust.
|
||||
(find_inferior_id): Make public. Assert pid is not zero.
|
||||
(find_inferior_for_program_space): New.
|
||||
(have_inferiors): Check if we have any inferior with pid not zero.
|
||||
(have_live_inferiors): Go over all pushed targets looking for
|
||||
process_stratum.
|
||||
(prune_inferiors): New.
|
||||
(number_of_inferiors): New.
|
||||
(print_inferior): Add executable column. Print vfork parent/child
|
||||
relationships.
|
||||
(inferior_command): Adjust to cope with not running inferiors.
|
||||
(remove_inferior_command): New.
|
||||
(add_inferior_command): New.
|
||||
(clone_inferior_command): New.
|
||||
(struct inferior_data): New.
|
||||
(struct inferior_data_registration): New.
|
||||
(struct inferior_data_registry): New.
|
||||
(inferior_data_registry): New.
|
||||
(register_inferior_data_with_cleanup): New.
|
||||
(register_inferior_data): New.
|
||||
(inferior_alloc_data): New.
|
||||
(inferior_free_data): New.
|
||||
(clear_inferior_data): New.
|
||||
(set_inferior_data): New.
|
||||
(inferior_data): New.
|
||||
(initialize_inferiors): New.
|
||||
(_initialize_inferiors): Register "add-inferior",
|
||||
"remove-inferior" and "clone-inferior" commands.
|
||||
|
||||
* objfiles.h: Include "progspace.h".
|
||||
(struct objfile) <pspace>: New field.
|
||||
(symfile_objfile, object_files): Don't declare.
|
||||
(ALL_PSPACE_OBJFILES): New.
|
||||
(ALL_PSPACE_OBJFILES_SAFE): New.
|
||||
(ALL_OBJFILES, ALL_OBJFILES_SAFE): Adjust.
|
||||
(ALL_PSPACE_SYMTABS): New.
|
||||
(ALL_PRIMARY_SYMTABS): Adjust.
|
||||
(ALL_PSPACE_PRIMARY_SYMTABS): New.
|
||||
(ALL_PSYMTABS): Adjust.
|
||||
(ALL_PSPACE_PSYMTABS): New.
|
||||
* objfiles.c (object_files, symfile_objfile): Delete.
|
||||
(struct objfile_sspace_info): New.
|
||||
(objfiles_pspace_data): New.
|
||||
(objfiles_pspace_data_cleanup): New.
|
||||
(get_objfile_pspace_data): New.
|
||||
(objfiles_changed_p): Delete.
|
||||
(allocate_objfile): Set the objfile's program space. Adjust to
|
||||
reference objfiles_changed_p in pspace data.
|
||||
(free_objfile): Adjust to reference objfiles_changed_p in pspace
|
||||
data.
|
||||
(objfile_relocate): Ditto.
|
||||
(update_section_map): Add pspace argument. Adjust to iterate over
|
||||
objfiles in the passed in pspace.
|
||||
(find_pc_section): Delete sections and num_sections statics.
|
||||
Adjust to refer to program space's objfiles_changed_p. Adjust to
|
||||
refer to sections and num_sections store in the objfile's pspace
|
||||
data.
|
||||
(objfiles_changed): Adjust to reference objfiles_changed_p in
|
||||
pspace data.
|
||||
(_initialize_objfiles): New.
|
||||
* linespec.c (decode_all_digits, decode_dollar): Set the sal's
|
||||
program space.
|
||||
* source.c (current_source_pspace): New.
|
||||
(get_current_source_symtab_and_line): Set the sal's program space.
|
||||
(set_current_source_symtab_and_line): Set current_source_pspace.
|
||||
(select_source_symtab): Ditto. Use ALL_OBJFILES.
|
||||
(forget_cached_source_info): Iterate over all program spaces.
|
||||
* symfile.c (clear_symtab_users): Adjust.
|
||||
* symmisc.c (print_symbol_bcache_statistics): Iterate over all
|
||||
program spaces.
|
||||
(print_objfile_statistics): Ditto.
|
||||
(maintenance_print_msymbols): Ditto.
|
||||
(maintenance_print_objfiles): Ditto.
|
||||
(maintenance_info_symtabs): Ditto.
|
||||
(maintenance_info_psymtabs): Ditto.
|
||||
* symtab.h (SYMTAB_PSPACE): New.
|
||||
(struct symtab_and_line) <pspace>: New field.
|
||||
* symtab.c (init_sal): Clear the sal's program space.
|
||||
(find_pc_sect_symtab): Set the sal's program space. Switch thread
|
||||
and space.
|
||||
(append_expanded_sal): Add program space argument. Iterate over
|
||||
all program spaces.
|
||||
(expand_line_sal): Iterate over all program spaces. Switch
|
||||
program space.
|
||||
|
||||
* target.h (enum target_waitkind) <TARGET_WAITKIND_VFORK_DONE>: New.
|
||||
(struct target_ops) <to_thread_address_space>: New field.
|
||||
(target_thread_address_space): Define.
|
||||
* target.c (target_detach): Only remove breakpoints from the
|
||||
inferior we're detaching.
|
||||
(target_thread_address_space): New.
|
||||
|
||||
* defs.h (initialize_progspace): Declare.
|
||||
* top.c (gdb_init): Call it.
|
||||
|
||||
* solist.h (struct so_list) <sspace>: New field.
|
||||
* solib.h (struct program_space): Forward declare.
|
||||
(solib_name_from_address): Adjust prototype.
|
||||
* solib.c (so_list_head): Replace with a macro referencing the
|
||||
program space.
|
||||
(update_solib_list): Set the so's program space.
|
||||
(solib_name_from_address): Add a program space argument and adjust.
|
||||
|
||||
* solib-svr4.c (struct svr4_info) <pid>: Delete field.
|
||||
<interp_text_sect_low, interp_text_sect_high, interp_plt_sect_low>
|
||||
<interp_plt_sect_high>: New fields.
|
||||
(svr4_info_p, svr4_info): Delete.
|
||||
(solib_svr4_sspace_data): New.
|
||||
(get_svr4_info): Rewrite.
|
||||
(svr4_sspace_data_cleanup): New.
|
||||
(open_symbol_file_object): Adjust.
|
||||
(svr4_default_sos): Adjust.
|
||||
(svr4_fetch_objfile_link_map): Adjust.
|
||||
(interp_text_sect_low, interp_text_sect_high, interp_plt_sect_low)
|
||||
(interp_plt_sect_high): Delete.
|
||||
(svr4_in_dynsym_resolve_code): Adjust.
|
||||
(enable_break): Adjust.
|
||||
(svr4_clear_solib): Revert bit that removed the svr4_info here,
|
||||
and reinstate clearing debug_base, debug_loader_offset_p,
|
||||
debug_loader_offset and debug_loader_name.
|
||||
(_initialize_svr4_solib): Register solib_svr4_pspace_data. Don't
|
||||
install an inferior_exit observer anymore.
|
||||
|
||||
* printcmd.c (struct display) <pspace>: New field.
|
||||
(display_command): Set the display's sspace.
|
||||
(do_one_display): Match the display's sspace.
|
||||
(display_uses_solib_p): Ditto.
|
||||
|
||||
* linux-fork.c (detach_fork): Moved to infrun.c.
|
||||
(_initialize_linux_fork): Moved "detach-on-fork" command to
|
||||
infrun.c.
|
||||
* infrun.c (detach_fork): Moved from linux-fork.c.
|
||||
(proceed_after_vfork_done): New.
|
||||
(handle_vfork_child_exec_or_exit): New.
|
||||
(follow_exec_mode_replace, follow_exec_mode_keep)
|
||||
(follow_exec_mode_names, follow_exec_mode_string)
|
||||
(show_follow_exec_mode_string): New.
|
||||
(follow_exec): New. Reinstate the mark_breakpoints_out call.
|
||||
Remove shared libraries before attaching new executable. If user
|
||||
wants to keep the inferior, keep it.
|
||||
(displaced_step_fixup): Adjust to pass an address space to the
|
||||
breakpoints module.
|
||||
(resume): Ditto.
|
||||
(clear_proceed_status): In all-stop mode, always clear the proceed
|
||||
status of all threads.
|
||||
(prepare_to_proceed): Adjust to pass an address space to the
|
||||
breakpoints module.
|
||||
(proceed): Ditto.
|
||||
(adjust_pc_after_break): Ditto.
|
||||
(handle_inferior_event): When handling a process exit, switch the
|
||||
program space to the inferior's that had exited. Call
|
||||
handle_vfork_child_exec_or_exit. Adjust to pass an address space
|
||||
to the breakpoints module. In non-stop mode, when following a
|
||||
fork and detach-fork is off, also resume the other branch. Handle
|
||||
TARGET_WAITKIND_VFORK_DONE. Set the program space in sals.
|
||||
(normal_stop): Prune inferiors.
|
||||
(_initialize_infrun): Install the new "follow-exec-mode" command.
|
||||
"detach-on-fork" moved here.
|
||||
|
||||
* regcache.h (get_regcache_aspace): Declare.
|
||||
* regcache.c (struct regcache) <aspace>: New field.
|
||||
(regcache_xmalloc): Clear the aspace.
|
||||
(get_regcache_aspace): New.
|
||||
(regcache_cpy): Copy the aspace field.
|
||||
(regcache_cpy_no_passthrough): Ditto.
|
||||
(get_thread_regcache): Fetch the thread's address space from the
|
||||
target, and store it in the regcache.
|
||||
|
||||
* infcall.c (call_function_by_hand): Set the sal's pspace.
|
||||
|
||||
* arch-utils.c (default_has_shared_address_space): New.
|
||||
* arch-utils.h (default_has_shared_address_space): Declare.
|
||||
|
||||
* gdbarch.sh (has_shared_address_space): New.
|
||||
* gdbarch.h, gdbarch.c: Regenerate.
|
||||
|
||||
* linux-tdep.c: Include auxv.h, target.h, elf/common.h.
|
||||
(linux_has_shared_address_space): New.
|
||||
(_initialize_linux_tdep): Declare.
|
||||
|
||||
* arm-tdep.c (arm_software_single_step): Pass the frame's address
|
||||
space to insert_single_step_breakpoint.
|
||||
* arm-linux-tdep.c (arm_linux_software_single_step): Pass the
|
||||
frame's pspace to breakpoint functions.
|
||||
* cris-tdep.c (crisv32_single_step_through_delay): Ditto.
|
||||
(cris_software_single_step): Ditto.
|
||||
* mips-tdep.c (deal_with_atomic_sequence): Add frame argument.
|
||||
Pass the frame's pspace to breakpoint functions.
|
||||
(mips_software_single_step): Adjust.
|
||||
(mips_single_step_through_delay): Adjust.
|
||||
* rs6000-aix-tdep.c (rs6000_software_single_step): Adjust.
|
||||
* rs6000-tdep.c (ppc_deal_with_atomic_sequence): Adjust.
|
||||
* solib-irix.c (enable_break): Adjust to pass the current frame's
|
||||
address space to breakpoint functions.
|
||||
* sparc-tdep.c (sparc_software_single_step): Ditto.
|
||||
* spu-tdep.c (spu_software_single_step): Ditto.
|
||||
* alpha-tdep.c (alpha_software_single_step): Ditto.
|
||||
* record.c (record_wait): Adjust to pass an address space to the
|
||||
breakpoints module.
|
||||
|
||||
* fork-child.c (fork_inferior): Set the new inferior's program and
|
||||
address spaces.
|
||||
* inf-ptrace.c (inf_ptrace_follow_fork): Copy the parent's program
|
||||
and address spaces.
|
||||
(inf_ptrace_attach): Set the inferior's program and address spaces.
|
||||
* linux-nat.c: Include "solib.h".
|
||||
(linux_child_follow_fork): Manage parent and child's program and
|
||||
address spaces. Clone the parent's program space if necessary.
|
||||
Don't wait for the vfork to be done here. Refuse to resume if
|
||||
following the vfork parent while leaving the child stopped.
|
||||
(resume_callback): Don't resume a vfork parent.
|
||||
(linux_nat_resume): Also check for pending events in the
|
||||
lp->waitstatus field.
|
||||
(linux_handle_extended_wait): Report TARGET_WAITKIND_VFORK_DONE
|
||||
events to the core.
|
||||
(stop_wait_callback): Don't wait for SIGSTOP on vfork parents.
|
||||
(cancel_breakpoint): Adjust.
|
||||
* linux-thread-db.c (thread_db_wait): Don't remove thread event
|
||||
breakpoints here.
|
||||
(thread_db_mourn_inferior): Don't mark breakpoints out here.
|
||||
Remove thread event breakpoints after mourning.
|
||||
* corelow.c: Include progspace.h.
|
||||
(core_open): Set the inferior's program and address spaces.
|
||||
* remote.c (remote_add_inferior): Set the new inferior's program
|
||||
and address spaces.
|
||||
(remote_start_remote): Update address spaces.
|
||||
(extended_remote_create_inferior_1): Don't init the thread list if
|
||||
we already debugging other inferiors.
|
||||
* darwin-nat.c (darwin_attach): Set the new inferior's program and
|
||||
address spaces.
|
||||
* gnu-nat.c (gnu_attach): Ditto.
|
||||
* go32-nat.c (go32_create_inferior): Ditto.
|
||||
* inf-ttrace.c (inf_ttrace_follow_fork, inf_ttrace_attach): Ditto.
|
||||
* monitor.c (monitor_open): Ditto.
|
||||
* nto-procfs.c (procfs_attach, procfs_create_inferior): Ditto.
|
||||
* procfs.c (do_attach): Ditto.
|
||||
* windows-nat.c (do_initial_windows_stuff): Ditto.
|
||||
|
||||
* inflow.c (inferior_process_group)
|
||||
(terminal_init_inferior_with_pgrp, terminal_inferior,
|
||||
(terminal_ours_1, inflow_inferior_exit, copy_terminal_info)
|
||||
(child_terminal_info, new_tty_postfork, set_sigint_trap): Adjust
|
||||
to use per-inferior data instead of inferior->terminal_info.
|
||||
(inflow_inferior_data): New.
|
||||
(inflow_new_inferior): Delete.
|
||||
(inflow_inferior_data_cleanup): New.
|
||||
(get_inflow_inferior_data): New.
|
||||
|
||||
* mi/mi-interp.c (mi_new_inferior): Rename to...
|
||||
(mi_inferior_appeared): ... this.
|
||||
(mi_interpreter_init): Adjust.
|
||||
|
||||
* tui/tui-disasm.c: Include "progspace.h".
|
||||
(tui_set_disassem_content): Pass an address space to
|
||||
breakpoint_here_p.
|
||||
|
||||
* NEWS: Mention multi-program debugging support. Mention new
|
||||
commands "add-inferior", "clone-inferior", "remove-inferior",
|
||||
"maint info program-spaces", and new option "set
|
||||
follow-exec-mode".
|
||||
|
||||
2009-10-19 Don Lee <don.lee@sunplusct.com>
|
||||
|
||||
* score-tdep.c: Delete dead codes.
|
||||
|
@ -661,6 +661,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
|
||||
objc-exp.y objc-lang.c \
|
||||
objfiles.c osabi.c observer.c osdata.c \
|
||||
p-exp.y p-lang.c p-typeprint.c p-valprint.c parse.c printcmd.c \
|
||||
progspace.c \
|
||||
prologue-value.c \
|
||||
regcache.c reggroups.c remote.c remote-fileio.c reverse.c \
|
||||
scm-exp.c scm-lang.c scm-valprint.c \
|
||||
@ -834,7 +835,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
|
||||
prologue-value.o memory-map.o xml-support.o xml-syscall.o \
|
||||
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
|
||||
inferior.o osdata.o gdb_usleep.o record.o \
|
||||
jit.o
|
||||
jit.o progspace.o
|
||||
|
||||
# Definitions for the syscall's XML files and dir
|
||||
XML_SYSCALLS_DIR = syscalls/
|
||||
|
34
gdb/NEWS
34
gdb/NEWS
@ -11,6 +11,40 @@ Xilinx MicroBlaze microblaze-*-*
|
||||
|
||||
Xilinx MicroBlaze microblaze
|
||||
|
||||
* Multi-program debugging.
|
||||
|
||||
GDB now has support for multi-program (a.k.a. multi-executable or
|
||||
multi-exec) debugging. This allows for debugging multiple inferiors
|
||||
simultaneously each running a different program under the same GDB
|
||||
session. See "Debugging Multiple Inferiors and Programs" in the
|
||||
manual for more information. This implied some user visible changes
|
||||
in the multi-inferior support. For example, "info inferiors" now
|
||||
lists inferiors that are not running yet or that have exited
|
||||
already. See also "New commands" and "New options" below.
|
||||
|
||||
* New commands (for set/show, see "New options" below)
|
||||
|
||||
add-inferior [-copies <N>] [-exec <FILENAME>]
|
||||
Add a new inferior.
|
||||
|
||||
clone-inferior [-copies <N>] [ID]
|
||||
Make a new inferior ready to execute the same program another
|
||||
inferior has loaded.
|
||||
|
||||
remove-inferior ID
|
||||
Remove an inferior.
|
||||
|
||||
maint info program-spaces
|
||||
List the program spaces loaded into GDB.
|
||||
|
||||
* New options
|
||||
|
||||
set follow-exec-mode new|same
|
||||
show follow-exec-mode
|
||||
Control whether GDB reuses the same inferior across an exec call or
|
||||
creates a new one. This is useful to be able to restart the old
|
||||
executable after the inferior having done an exec call.
|
||||
|
||||
*** Changes in GDB 7.0
|
||||
|
||||
* GDB now has an interface for JIT compilation. Applications that
|
||||
|
@ -1489,12 +1489,13 @@ int
|
||||
alpha_software_single_step (struct frame_info *frame)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
CORE_ADDR pc, next_pc;
|
||||
|
||||
pc = get_frame_pc (frame);
|
||||
next_pc = alpha_next_pc (frame, pc);
|
||||
|
||||
insert_single_step_breakpoint (gdbarch, next_pc);
|
||||
insert_single_step_breakpoint (gdbarch, aspace, next_pc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -757,6 +757,14 @@ get_current_arch (void)
|
||||
return target_gdbarch;
|
||||
}
|
||||
|
||||
int
|
||||
default_has_shared_address_space (struct gdbarch *gdbarch)
|
||||
{
|
||||
/* Simply say no. In most unix-like targets each inferior/process
|
||||
has its own address space. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* */
|
||||
|
||||
extern initialize_file_ftype _initialize_gdbarch_utils; /* -Wmissing-prototypes */
|
||||
|
@ -153,4 +153,6 @@ extern struct gdbarch *gdbarch_from_bfd (bfd *abfd);
|
||||
routines to determine the architecture to execute a command in. */
|
||||
extern struct gdbarch *get_current_arch (void);
|
||||
|
||||
extern int default_has_shared_address_space (struct gdbarch *);
|
||||
|
||||
#endif
|
||||
|
@ -586,6 +586,7 @@ static int
|
||||
arm_linux_software_single_step (struct frame_info *frame)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
CORE_ADDR next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
|
||||
|
||||
/* The Linux kernel offers some user-mode helpers in a high page. We can
|
||||
@ -596,7 +597,7 @@ arm_linux_software_single_step (struct frame_info *frame)
|
||||
if (next_pc > 0xffff0000)
|
||||
next_pc = get_frame_register_unsigned (frame, ARM_LR_REGNUM);
|
||||
|
||||
insert_single_step_breakpoint (gdbarch, next_pc);
|
||||
insert_single_step_breakpoint (gdbarch, aspace, next_pc);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -2792,13 +2792,14 @@ int
|
||||
arm_software_single_step (struct frame_info *frame)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
|
||||
/* NOTE: This may insert the wrong breakpoint instruction when
|
||||
single-stepping over a mode-changing instruction, if the
|
||||
CPSR heuristics are used. */
|
||||
|
||||
CORE_ADDR next_pc = arm_get_next_pc (frame, get_frame_pc (frame));
|
||||
insert_single_step_breakpoint (gdbarch, next_pc);
|
||||
insert_single_step_breakpoint (gdbarch, aspace, next_pc);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
613
gdb/breakpoint.c
613
gdb/breakpoint.c
File diff suppressed because it is too large
Load Diff
@ -176,6 +176,9 @@ enum target_hw_bp_type
|
||||
|
||||
struct bp_target_info
|
||||
{
|
||||
/* Address space at which the breakpoint was placed. */
|
||||
struct address_space *placed_address_space;
|
||||
|
||||
/* Address at which the breakpoint was placed. This is normally the
|
||||
same as ADDRESS from the bp_location, except when adjustment
|
||||
happens in gdbarch_breakpoint_from_pc. The most common form of
|
||||
@ -272,6 +275,14 @@ struct bp_location
|
||||
different from the breakpoint architecture. */
|
||||
struct gdbarch *gdbarch;
|
||||
|
||||
/* The program space associated with this breakpoint location
|
||||
address. Note that an address space may be represented in more
|
||||
than one program space (e.g. each uClinux program will be given
|
||||
its own program space, but there will only be one address space
|
||||
for all of them), but we must not insert more than one location
|
||||
at the same address in the same address space. */
|
||||
struct program_space *pspace;
|
||||
|
||||
/* Note that zero is a perfectly valid code address on some platforms
|
||||
(for example, the mn10200 (OBSOLETE) and mn10300 simulators). NULL
|
||||
is not a special value for this field. Valid for all types except
|
||||
@ -409,6 +420,9 @@ struct breakpoint
|
||||
equals this. */
|
||||
struct frame_id frame_id;
|
||||
|
||||
/* The program space used to set the breakpoint. */
|
||||
struct program_space *pspace;
|
||||
|
||||
/* String we used to set the breakpoint (malloc'd). */
|
||||
char *addr_string;
|
||||
/* Architecture we used to set the breakpoint. */
|
||||
@ -520,7 +534,8 @@ extern void bpstat_clear (bpstat *);
|
||||
is part of the bpstat is copied as well. */
|
||||
extern bpstat bpstat_copy (bpstat);
|
||||
|
||||
extern bpstat bpstat_stop_status (CORE_ADDR pc, ptid_t ptid);
|
||||
extern bpstat bpstat_stop_status (struct address_space *aspace,
|
||||
CORE_ADDR pc, ptid_t ptid);
|
||||
|
||||
/* This bpstat_what stuff tells wait_for_inferior what to do with a
|
||||
breakpoint (a challenging task). */
|
||||
@ -707,17 +722,17 @@ enum breakpoint_here
|
||||
|
||||
/* Prototypes for breakpoint-related functions. */
|
||||
|
||||
extern enum breakpoint_here breakpoint_here_p (CORE_ADDR);
|
||||
extern enum breakpoint_here breakpoint_here_p (struct address_space *, CORE_ADDR);
|
||||
|
||||
extern int moribund_breakpoint_here_p (CORE_ADDR);
|
||||
extern int moribund_breakpoint_here_p (struct address_space *, CORE_ADDR);
|
||||
|
||||
extern int breakpoint_inserted_here_p (CORE_ADDR);
|
||||
extern int breakpoint_inserted_here_p (struct address_space *, CORE_ADDR);
|
||||
|
||||
extern int regular_breakpoint_inserted_here_p (CORE_ADDR);
|
||||
extern int regular_breakpoint_inserted_here_p (struct address_space *, CORE_ADDR);
|
||||
|
||||
extern int software_breakpoint_inserted_here_p (CORE_ADDR);
|
||||
extern int software_breakpoint_inserted_here_p (struct address_space *, CORE_ADDR);
|
||||
|
||||
extern int breakpoint_thread_match (CORE_ADDR, ptid_t);
|
||||
extern int breakpoint_thread_match (struct address_space *, CORE_ADDR, ptid_t);
|
||||
|
||||
extern void until_break_command (char *, int, int);
|
||||
|
||||
@ -735,7 +750,8 @@ extern struct breakpoint *clone_momentary_breakpoint (struct breakpoint *bpkt);
|
||||
|
||||
extern void set_ignore_count (int, int, int);
|
||||
|
||||
extern void set_default_breakpoint (int, CORE_ADDR, struct symtab *, int);
|
||||
extern void set_default_breakpoint (int, struct program_space *,
|
||||
CORE_ADDR, struct symtab *, int);
|
||||
|
||||
extern void breakpoint_init_inferior (enum inf_context);
|
||||
|
||||
@ -766,6 +782,8 @@ extern void insert_breakpoints (void);
|
||||
|
||||
extern int remove_breakpoints (void);
|
||||
|
||||
extern int remove_breakpoints_pid (int pid);
|
||||
|
||||
/* This function can be used to physically insert eventpoints from the
|
||||
specified traced inferior process, without modifying the breakpoint
|
||||
package's state. This can be useful for those targets which support
|
||||
@ -801,6 +819,11 @@ extern void update_breakpoints_after_exec (void);
|
||||
inferior_ptid. */
|
||||
extern int detach_breakpoints (int);
|
||||
|
||||
/* This function is called when program space PSPACE is about to be
|
||||
deleted. It takes care of updating breakpoints to not reference
|
||||
this PSPACE anymore. */
|
||||
extern void breakpoint_program_space_exit (struct program_space *pspace);
|
||||
|
||||
extern void set_longjmp_breakpoint (int thread);
|
||||
extern void delete_longjmp_breakpoint (int thread);
|
||||
|
||||
@ -909,13 +932,15 @@ extern int remove_hw_watchpoints (void);
|
||||
|
||||
/* Manage a software single step breakpoint (or two). Insert may be called
|
||||
twice before remove is called. */
|
||||
extern void insert_single_step_breakpoint (struct gdbarch *, CORE_ADDR);
|
||||
extern void insert_single_step_breakpoint (struct gdbarch *,
|
||||
struct address_space *, CORE_ADDR);
|
||||
extern void remove_single_step_breakpoints (void);
|
||||
|
||||
/* Manage manual breakpoints, separate from the normal chain of
|
||||
breakpoints. These functions are used in murky target-specific
|
||||
ways. Please do not add more uses! */
|
||||
extern void *deprecated_insert_raw_breakpoint (struct gdbarch *, CORE_ADDR);
|
||||
extern void *deprecated_insert_raw_breakpoint (struct gdbarch *,
|
||||
struct address_space *, CORE_ADDR);
|
||||
extern int deprecated_remove_raw_breakpoint (struct gdbarch *, void *);
|
||||
|
||||
/* Check if any hardware watchpoints have triggered, according to the
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "exceptions.h"
|
||||
#include "solib.h"
|
||||
#include "filenames.h"
|
||||
#include "progspace.h"
|
||||
|
||||
|
||||
#ifndef O_LARGEFILE
|
||||
@ -208,7 +209,7 @@ core_close (int quitting)
|
||||
{
|
||||
int pid = ptid_get_pid (inferior_ptid);
|
||||
inferior_ptid = null_ptid; /* Avoid confusion from thread stuff */
|
||||
delete_inferior_silent (pid);
|
||||
exit_inferior_silent (pid);
|
||||
|
||||
/* Clear out solib state while the bfd is still open. See
|
||||
comments in clear_solib in solib.c. */
|
||||
@ -275,8 +276,8 @@ add_to_thread_list (bfd *abfd, asection *asect, void *reg_sect_arg)
|
||||
lwpid = core_tid;
|
||||
}
|
||||
|
||||
if (!in_inferior_list (pid))
|
||||
add_inferior_silent (pid);
|
||||
if (current_inferior ()->pid == 0)
|
||||
inferior_appeared (current_inferior (), pid);
|
||||
|
||||
ptid = ptid_build (pid, lwpid, 0);
|
||||
|
||||
@ -302,6 +303,7 @@ core_open (char *filename, int from_tty)
|
||||
int scratch_chan;
|
||||
int flags;
|
||||
int corelow_pid = CORELOW_PID;
|
||||
struct inferior *inf;
|
||||
|
||||
target_preopen (from_tty);
|
||||
if (!filename)
|
||||
|
@ -471,7 +471,7 @@ crisv32_single_step_through_delay (struct gdbarch *gdbarch,
|
||||
{
|
||||
/* In delay slot - check if there's a breakpoint at the preceding
|
||||
instruction. */
|
||||
if (breakpoint_here_p (erp & ~0x1))
|
||||
if (breakpoint_here_p (get_frame_address_space (this_frame), erp & ~0x1))
|
||||
ret = 1;
|
||||
}
|
||||
return ret;
|
||||
@ -2132,6 +2132,7 @@ static int
|
||||
cris_software_single_step (struct frame_info *frame)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
inst_env_type inst_env;
|
||||
|
||||
/* Analyse the present instruction environment and insert
|
||||
@ -2149,13 +2150,14 @@ cris_software_single_step (struct frame_info *frame)
|
||||
and possibly another one for a branch, jump, etc. */
|
||||
CORE_ADDR next_pc
|
||||
= (CORE_ADDR) inst_env.reg[gdbarch_pc_regnum (gdbarch)];
|
||||
insert_single_step_breakpoint (gdbarch, next_pc);
|
||||
insert_single_step_breakpoint (gdbarch, aspace, next_pc);
|
||||
if (inst_env.branch_found
|
||||
&& (CORE_ADDR) inst_env.branch_break_address != next_pc)
|
||||
{
|
||||
CORE_ADDR branch_target_address
|
||||
= (CORE_ADDR) inst_env.branch_break_address;
|
||||
insert_single_step_breakpoint (gdbarch, branch_target_address);
|
||||
insert_single_step_breakpoint (gdbarch,
|
||||
aspace, branch_target_address);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1543,8 +1543,10 @@ darwin_attach (struct target_ops *ops, char *args, int from_tty)
|
||||
pid, safe_strerror (errno), errno);
|
||||
|
||||
inferior_ptid = pid_to_ptid (pid);
|
||||
inf = add_inferior (pid);
|
||||
inf = current_inferior ();
|
||||
inferior_appeared (inf, pid);
|
||||
inf->attach_flag = 1;
|
||||
|
||||
/* Always add a main thread. */
|
||||
add_thread_silent (inferior_ptid);
|
||||
|
||||
|
@ -1218,4 +1218,9 @@ extern ULONGEST align_down (ULONGEST v, int n);
|
||||
void *hashtab_obstack_allocate (void *data, size_t size, size_t count);
|
||||
void dummy_obstack_deallocate (void *object, void *data);
|
||||
|
||||
/* From progspace.c */
|
||||
|
||||
extern void initialize_progspace (void);
|
||||
extern void initialize_inferiors (void);
|
||||
|
||||
#endif /* #ifndef DEFS_H */
|
||||
|
@ -1,3 +1,19 @@
|
||||
2009-10-19 Pedro Alves <pedro@codesourcery.com>
|
||||
Stan Shebs <stan@codesourcery.com>
|
||||
|
||||
* observer.texi (new_inferior): Rename to...
|
||||
(inferior_appeared): ... this.
|
||||
|
||||
* gdb.texinfo (Inferiors): Rename node to ...
|
||||
(Inferiors and Programs): ... this. Mention running multiple
|
||||
programs in the same debug session.
|
||||
<info inferiors>: Mention the new 'Executable' column if "info
|
||||
inferiors". Update examples. Document the "add-inferior",
|
||||
"clone-inferior", "remove-inferior" and "maint info
|
||||
program-spaces" commands.
|
||||
(Process): Rename node to...
|
||||
(Forks): ... this. Document "set|show follow-exec-mode".
|
||||
|
||||
2009-10-11 Michael Snyder <msnyder@vmware.com>
|
||||
|
||||
* gdb.texinfo (ReverseStep): Show default as "unsupported".
|
||||
|
@ -1794,9 +1794,9 @@ kill a child process.
|
||||
* Attach:: Debugging an already-running process
|
||||
* Kill Process:: Killing the child process
|
||||
|
||||
* Inferiors:: Debugging multiple inferiors
|
||||
* Inferiors and Programs:: Debugging multiple inferiors and programs
|
||||
* Threads:: Debugging programs with multiple threads
|
||||
* Processes:: Debugging programs with multiple processes
|
||||
* Forks:: Debugging forks
|
||||
* Checkpoint/Restart:: Setting a @emph{bookmark} to return to later
|
||||
@end menu
|
||||
|
||||
@ -2349,30 +2349,30 @@ next type @code{run}, @value{GDBN} notices that the file has changed, and
|
||||
reads the symbol table again (while trying to preserve your current
|
||||
breakpoint settings).
|
||||
|
||||
@node Inferiors
|
||||
@section Debugging Multiple Inferiors
|
||||
@node Inferiors and Programs
|
||||
@section Debugging Multiple Inferiors and Programs
|
||||
|
||||
Some @value{GDBN} targets are able to run multiple processes created
|
||||
from a single executable. This can happen, for instance, with an
|
||||
embedded system reporting back several processes via the remote
|
||||
protocol.
|
||||
@value{GDBN} lets you run and debug multiple programs in a single
|
||||
session. In addition, @value{GDBN} on some systems may let you run
|
||||
several programs simultaneously (otherwise you have to exit from one
|
||||
before starting another). In the most general case, you can have
|
||||
multiple threads of execution in each of multiple processes, launched
|
||||
from multiple executables.
|
||||
|
||||
@cindex inferior
|
||||
@value{GDBN} represents the state of each program execution with an
|
||||
object called an @dfn{inferior}. An inferior typically corresponds to
|
||||
a process, but is more general and applies also to targets that do not
|
||||
have processes. Inferiors may be created before a process runs, and
|
||||
may (in future) be retained after a process exits. Each run of an
|
||||
executable creates a new inferior, as does each attachment to an
|
||||
existing process. Inferiors have unique identifiers that are
|
||||
different from process ids, and may optionally be named as well.
|
||||
Usually each inferior will also have its own distinct address space,
|
||||
although some embedded targets may have several inferiors running in
|
||||
different parts of a single space.
|
||||
may be retained after a process exits. Inferiors have unique
|
||||
identifiers that are different from process ids. Usually each
|
||||
inferior will also have its own distinct address space, although some
|
||||
embedded targets may have several inferiors running in different parts
|
||||
of a single address space. Each inferior may in turn have multiple
|
||||
threads running in it.
|
||||
|
||||
Each inferior may in turn have multiple threads running in it.
|
||||
|
||||
To find out what inferiors exist at any moment, use @code{info inferiors}:
|
||||
To find out what inferiors exist at any moment, use @w{@code{info
|
||||
inferiors}}:
|
||||
|
||||
@table @code
|
||||
@kindex info inferiors
|
||||
@ -2387,6 +2387,10 @@ the inferior number assigned by @value{GDBN}
|
||||
|
||||
@item
|
||||
the target system's inferior identifier
|
||||
|
||||
@item
|
||||
the name of the executable the inferior is running.
|
||||
|
||||
@end enumerate
|
||||
|
||||
@noindent
|
||||
@ -2399,9 +2403,9 @@ For example,
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) info inferiors
|
||||
Num Description
|
||||
* 1 process 2307
|
||||
2 process 3401
|
||||
Num Description Executable
|
||||
2 process 2307 hello
|
||||
* 1 process 3401 goodbye
|
||||
@end smallexample
|
||||
|
||||
To switch focus between inferiors, use the @code{inferior} command:
|
||||
@ -2414,9 +2418,57 @@ Make inferior number @var{infno} the current inferior. The argument
|
||||
in the first field of the @samp{info inferiors} display.
|
||||
@end table
|
||||
|
||||
To quit debugging one of the inferiors, you can either detach from it
|
||||
by using the @w{@code{detach inferior}} command (allowing it to run
|
||||
independently), or kill it using the @w{@code{kill inferior}} command:
|
||||
|
||||
You can get multiple executables into a debugging session via the
|
||||
@code{add-inferior} and @w{@code{clone-inferior}} commands. On some
|
||||
systems @value{GDBN} can add inferiors to the debug session
|
||||
automatically by following calls to @code{fork} and @code{exec}. To
|
||||
remove inferiors from the debugging session use the
|
||||
@w{@code{remove-inferior}} command.
|
||||
|
||||
@table @code
|
||||
@kindex add-inferior
|
||||
@item add-inferior [ -copies @var{n} ] [ -exec @var{executable} ]
|
||||
Adds @var{n} inferiors to be run using @var{executable} as the
|
||||
executable. @var{n} defaults to 1. If no executable is specified,
|
||||
the inferiors begins empty, with no program. You can still assign or
|
||||
change the program assigned to the inferior at any time by using the
|
||||
@code{file} command with the executable name as its argument.
|
||||
|
||||
@kindex clone-inferior
|
||||
@item clone-inferior [ -copies @var{n} ] [ @var{infno} ]
|
||||
Adds @var{n} inferiors ready to execute the same program as inferior
|
||||
@var{infno}. @var{n} defaults to 1. @var{infno} defaults to the
|
||||
number of the current inferior. This is a convenient command when you
|
||||
want to run another instance of the inferior you are debugging.
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) info inferiors
|
||||
Num Description Executable
|
||||
* 1 process 29964 helloworld
|
||||
(@value{GDBP}) clone-inferior
|
||||
Added inferior 2.
|
||||
1 inferiors added.
|
||||
(@value{GDBP}) info inferiors
|
||||
Num Description Executable
|
||||
2 <null> helloworld
|
||||
* 1 process 29964 helloworld
|
||||
@end smallexample
|
||||
|
||||
You can now simply switch focus to inferior 2 and run it.
|
||||
|
||||
@kindex remove-inferior
|
||||
@item remove-inferior @var{infno}
|
||||
Removes the inferior @var{infno}. It is not possible to remove an
|
||||
inferior that is running with this command. For those, use the
|
||||
@code{kill} or @code{detach} command first.
|
||||
|
||||
@end table
|
||||
|
||||
To quit debugging one of the running inferiors that is not the current
|
||||
inferior, you can either detach from it by using the @w{@code{detach
|
||||
inferior}} command (allowing it to run independently), or kill it
|
||||
using the @w{@code{kill inferior}} command:
|
||||
|
||||
@table @code
|
||||
@kindex detach inferior @var{infno}
|
||||
@ -2430,6 +2482,12 @@ Kill the inferior identified by @value{GDBN} inferior number
|
||||
@var{infno}, and remove it from the inferior list.
|
||||
@end table
|
||||
|
||||
After the successful completion of a command such as @code{detach},
|
||||
@code{detach inferior}, @code{kill} or @code{kill inferior}, or after
|
||||
a normal process exit, the inferior is still valid and listed with
|
||||
@code{info inferiors}, ready to be restarted.
|
||||
|
||||
|
||||
To be notified when inferiors are started or exit under @value{GDBN}'s
|
||||
control use @w{@code{set print inferior-events}}:
|
||||
|
||||
@ -2450,6 +2508,67 @@ Show whether messages will be printed when @value{GDBN} detects that
|
||||
inferiors have started, exited or have been detached.
|
||||
@end table
|
||||
|
||||
Many commands will work the same with multiple programs as with a
|
||||
single program: e.g., @code{print myglobal} will simply display the
|
||||
value of @code{myglobal} in the current inferior.
|
||||
|
||||
|
||||
Occasionaly, when debugging @value{GDBN} itself, it may be useful to
|
||||
get more info about the relationship of inferiors, programs, address
|
||||
spaces in a debug session. You can do that with the @w{@code{maint
|
||||
info program-spaces}} command.
|
||||
|
||||
@table @code
|
||||
@kindex maint info program-spaces
|
||||
@item maint info program-spaces
|
||||
Print a list of all program spaces currently being managed by
|
||||
@value{GDBN}.
|
||||
|
||||
@value{GDBN} displays for each program space (in this order):
|
||||
|
||||
@enumerate
|
||||
@item
|
||||
the program space number assigned by @value{GDBN}
|
||||
|
||||
@item
|
||||
the name of the executable loaded into the program space, with e.g.,
|
||||
the @code{file} command.
|
||||
|
||||
@end enumerate
|
||||
|
||||
@noindent
|
||||
An asterisk @samp{*} preceding the @value{GDBN} program space number
|
||||
indicates the current program space.
|
||||
|
||||
In addition, below each program space line, @value{GDBN} prints extra
|
||||
information that isn't suitable to display in tabular form. For
|
||||
example, the list of inferiors bound to the program space.
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) maint info program-spaces
|
||||
Id Executable
|
||||
2 goodbye
|
||||
Bound inferiors: ID 1 (process 21561)
|
||||
* 1 hello
|
||||
@end smallexample
|
||||
|
||||
Here we can see that no inferior is running the program @code{hello},
|
||||
while @code{process 21561} is running the program @code{goodbye}. On
|
||||
some targets, it is possible that multiple inferiors are bound to the
|
||||
same program space. The most common example is that of debugging both
|
||||
the parent and child processes of a @code{vfork} call. For example,
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) maint info program-spaces
|
||||
Id Executable
|
||||
* 1 vfork-test
|
||||
Bound inferiors: ID 2 (process 18050), ID 1 (process 18045)
|
||||
@end smallexample
|
||||
|
||||
Here, both inferior 2 and inferior 1 are running in the same program
|
||||
space as a result of inferior 1 having executed a @code{vfork} call.
|
||||
@end table
|
||||
|
||||
@node Threads
|
||||
@section Debugging Programs with Multiple Threads
|
||||
|
||||
@ -2729,8 +2848,8 @@ only on some platforms.
|
||||
Display current libthread_db search path.
|
||||
@end table
|
||||
|
||||
@node Processes
|
||||
@section Debugging Programs with Multiple Processes
|
||||
@node Forks
|
||||
@section Debugging Forks
|
||||
|
||||
@cindex fork, debugging programs which call
|
||||
@cindex multiple processes
|
||||
@ -2821,13 +2940,14 @@ If you choose to set @samp{detach-on-fork} mode off, then @value{GDBN}
|
||||
will retain control of all forked processes (including nested forks).
|
||||
You can list the forked processes under the control of @value{GDBN} by
|
||||
using the @w{@code{info inferiors}} command, and switch from one fork
|
||||
to another by using the @code{inferior} command (@pxref{Inferiors,
|
||||
,Debugging Multiple Inferiors}).
|
||||
to another by using the @code{inferior} command (@pxref{Inferiors and
|
||||
Programs, ,Debugging Multiple Inferiors and Programs}).
|
||||
|
||||
To quit debugging one of the forked processes, you can either detach
|
||||
from it by using the @w{@code{detach inferior}} command (allowing it
|
||||
to run independently), or kill it using the @w{@code{kill inferior}}
|
||||
command. @xref{Inferiors, ,Debugging Multiple Inferiors}.
|
||||
command. @xref{Inferiors and Programs, ,Debugging Multiple Inferiors
|
||||
and Programs}.
|
||||
|
||||
If you ask to debug a child process and a @code{vfork} is followed by an
|
||||
@code{exec}, @value{GDBN} executes the new target up to the first
|
||||
@ -2839,9 +2959,68 @@ On some systems, when a child process is spawned by @code{vfork}, you
|
||||
cannot debug the child or parent until an @code{exec} call completes.
|
||||
|
||||
If you issue a @code{run} command to @value{GDBN} after an @code{exec}
|
||||
call executes, the new target restarts. To restart the parent process,
|
||||
use the @code{file} command with the parent executable name as its
|
||||
argument.
|
||||
call executes, the new target restarts. To restart the parent
|
||||
process, use the @code{file} command with the parent executable name
|
||||
as its argument. By default, after an @code{exec} call executes,
|
||||
@value{GDBN} discards the symbols of the previous executable image.
|
||||
You can change this behaviour with the @w{@code{set follow-exec-mode}}
|
||||
command.
|
||||
|
||||
@table @code
|
||||
@kindex set follow-exec-mode
|
||||
@item set follow-exec-mode @var{mode}
|
||||
|
||||
Set debugger response to a program call of @code{exec}. An
|
||||
@code{exec} call replaces the program image of a process.
|
||||
|
||||
@code{follow-exec-mode} can be:
|
||||
|
||||
@table @code
|
||||
@item new
|
||||
@value{GDBN} creates a new inferior and rebinds the process to this
|
||||
new inferior. The program the process was running before the
|
||||
@code{exec} call can be restarted afterwards by restarting the
|
||||
original inferior.
|
||||
|
||||
For example:
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) info inferiors
|
||||
(gdb) info inferior
|
||||
Id Description Executable
|
||||
* 1 <null> prog1
|
||||
(@value{GDBP}) run
|
||||
process 12020 is executing new program: prog2
|
||||
Program exited normally.
|
||||
(@value{GDBP}) info inferiors
|
||||
Id Description Executable
|
||||
* 2 <null> prog2
|
||||
1 <null> prog1
|
||||
@end smallexample
|
||||
|
||||
@item same
|
||||
@value{GDBN} keeps the process bound to the same inferior. The new
|
||||
executable image replaces the previous executable loaded in the
|
||||
inferior. Restarting the inferior after the @code{exec} call, with
|
||||
e.g., the @code{run} command, restarts the executable the process was
|
||||
running after the @code{exec} call. This is the default mode.
|
||||
|
||||
For example:
|
||||
|
||||
@smallexample
|
||||
(@value{GDBP}) info inferiors
|
||||
Id Description Executable
|
||||
* 1 <null> prog1
|
||||
(@value{GDBP}) run
|
||||
process 12020 is executing new program: prog2
|
||||
Program exited normally.
|
||||
(@value{GDBP}) info inferiors
|
||||
Id Description Executable
|
||||
* 1 <null> prog2
|
||||
@end smallexample
|
||||
|
||||
@end table
|
||||
@end table
|
||||
|
||||
You can use the @code{catch} command to make @value{GDBN} stop whenever
|
||||
a @code{fork}, @code{vfork}, or @code{exec} call is made. @xref{Set
|
||||
|
@ -199,7 +199,7 @@ The thread's ptid has changed. The @var{old_ptid} parameter specifies
|
||||
the old value, and @var{new_ptid} specifies the new value.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun void new_inferior (int @var{pid})
|
||||
@deftypefun void inferior_appeared (int @var{pid})
|
||||
@value{GDBN} has attached to a new inferior identified by @var{pid}.
|
||||
@end deftypefun
|
||||
|
||||
|
88
gdb/exec.c
88
gdb/exec.c
@ -32,6 +32,8 @@
|
||||
#include "exec.h"
|
||||
#include "observer.h"
|
||||
#include "arch-utils.h"
|
||||
#include "gdbthread.h"
|
||||
#include "progspace.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include "readline/readline.h"
|
||||
@ -50,8 +52,6 @@ void (*deprecated_file_changed_hook) (char *);
|
||||
|
||||
/* Prototypes for local functions */
|
||||
|
||||
static void exec_close (int);
|
||||
|
||||
static void file_command (char *, int);
|
||||
|
||||
static void set_section_command (char *, int);
|
||||
@ -66,20 +66,8 @@ void _initialize_exec (void);
|
||||
|
||||
struct target_ops exec_ops;
|
||||
|
||||
/* The Binary File Descriptor handle for the executable file. */
|
||||
|
||||
bfd *exec_bfd = NULL;
|
||||
long exec_bfd_mtime = 0;
|
||||
|
||||
/* GDB currently only supports a single symbol/address space for the
|
||||
whole debug session. When that limitation is lifted, this global
|
||||
goes away. */
|
||||
static struct target_section_table current_target_sections_1;
|
||||
|
||||
/* The set of target sections matching the sections mapped into the
|
||||
current inferior's address space. */
|
||||
static struct target_section_table *current_target_sections
|
||||
= ¤t_target_sections_1;
|
||||
/* True if the exec target is pushed on the stack. */
|
||||
static int using_exec_ops;
|
||||
|
||||
/* Whether to open exec and core files read-only or read-write. */
|
||||
|
||||
@ -105,8 +93,8 @@ exec_open (char *args, int from_tty)
|
||||
/* Close and clear exec_bfd. If we end up with no target sections to
|
||||
read memory from, this unpushes the exec_ops target. */
|
||||
|
||||
static void
|
||||
exec_close_1 (void)
|
||||
void
|
||||
exec_close (void)
|
||||
{
|
||||
if (exec_bfd)
|
||||
{
|
||||
@ -127,12 +115,17 @@ exec_close_1 (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* This is the target_close implementation. Clears all target
|
||||
sections and closes all executable bfds from all program spaces. */
|
||||
|
||||
static void
|
||||
exec_close (int quitting)
|
||||
exec_close_1 (int quitting)
|
||||
{
|
||||
int need_symtab_cleanup = 0;
|
||||
struct vmap *vp, *nxt;
|
||||
|
||||
using_exec_ops = 0;
|
||||
|
||||
for (nxt = vmap; nxt != NULL;)
|
||||
{
|
||||
vp = nxt;
|
||||
@ -163,20 +156,32 @@ exec_close (int quitting)
|
||||
|
||||
vmap = NULL;
|
||||
|
||||
/* Delete all target sections. */
|
||||
resize_section_table
|
||||
(current_target_sections,
|
||||
-resize_section_table (current_target_sections, 0));
|
||||
{
|
||||
struct program_space *ss;
|
||||
struct cleanup *old_chain;
|
||||
|
||||
/* Remove exec file. */
|
||||
exec_close_1 ();
|
||||
old_chain = save_current_program_space ();
|
||||
ALL_PSPACES (ss)
|
||||
{
|
||||
set_current_program_space (ss);
|
||||
|
||||
/* Delete all target sections. */
|
||||
resize_section_table
|
||||
(current_target_sections,
|
||||
-resize_section_table (current_target_sections, 0));
|
||||
|
||||
exec_close ();
|
||||
}
|
||||
|
||||
do_cleanups (old_chain);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
exec_file_clear (int from_tty)
|
||||
{
|
||||
/* Remove exec file. */
|
||||
exec_close_1 ();
|
||||
exec_close ();
|
||||
|
||||
if (from_tty)
|
||||
printf_unfiltered (_("No executable file now.\n"));
|
||||
@ -203,7 +208,7 @@ void
|
||||
exec_file_attach (char *filename, int from_tty)
|
||||
{
|
||||
/* Remove any previous exec file. */
|
||||
exec_close_1 ();
|
||||
exec_close ();
|
||||
|
||||
/* Now open and digest the file the user requested, if any. */
|
||||
|
||||
@ -258,7 +263,7 @@ exec_file_attach (char *filename, int from_tty)
|
||||
{
|
||||
/* Make sure to close exec_bfd, or else "run" might try to use
|
||||
it. */
|
||||
exec_close_1 ();
|
||||
exec_close ();
|
||||
error (_("\"%s\": not in executable format: %s"),
|
||||
scratch_pathname, bfd_errmsg (bfd_get_error ()));
|
||||
}
|
||||
@ -273,7 +278,7 @@ exec_file_attach (char *filename, int from_tty)
|
||||
{
|
||||
/* Make sure to close exec_bfd, or else "run" might try to use
|
||||
it. */
|
||||
exec_close_1 ();
|
||||
exec_close ();
|
||||
error (_("\"%s\": can't find the file sections: %s"),
|
||||
scratch_pathname, bfd_errmsg (bfd_get_error ()));
|
||||
}
|
||||
@ -283,7 +288,7 @@ exec_file_attach (char *filename, int from_tty)
|
||||
{
|
||||
/* Make sure to close exec_bfd, or else "run" might try to use
|
||||
it. */
|
||||
exec_close_1 ();
|
||||
exec_close ();
|
||||
error (_("\"%s\": can't find the file sections: %s"),
|
||||
scratch_pathname, bfd_errmsg (bfd_get_error ()));
|
||||
}
|
||||
@ -295,7 +300,8 @@ exec_file_attach (char *filename, int from_tty)
|
||||
set_gdbarch_from_file (exec_bfd);
|
||||
|
||||
/* Add the executable's sections to the current address spaces'
|
||||
list of sections. */
|
||||
list of sections. This possibly pushes the exec_ops
|
||||
target. */
|
||||
add_target_sections (sections, sections_end);
|
||||
xfree (sections);
|
||||
|
||||
@ -465,8 +471,11 @@ add_target_sections (struct target_section *sections,
|
||||
|
||||
/* If these are the first file sections we can provide memory
|
||||
from, push the file_stratum target. */
|
||||
if (space == 0)
|
||||
push_target (&exec_ops);
|
||||
if (!using_exec_ops)
|
||||
{
|
||||
using_exec_ops = 1;
|
||||
push_target (&exec_ops);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -499,7 +508,16 @@ remove_target_sections (bfd *abfd)
|
||||
/* If we don't have any more sections to read memory from,
|
||||
remove the file_stratum target from the stack. */
|
||||
if (old_count + (dest - src) == 0)
|
||||
unpush_target (&exec_ops);
|
||||
{
|
||||
struct program_space *pspace;
|
||||
|
||||
ALL_PSPACES (pspace)
|
||||
if (pspace->target_sections.sections
|
||||
!= pspace->target_sections.sections_end)
|
||||
return;
|
||||
|
||||
unpush_target (&exec_ops);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -817,7 +835,7 @@ init_exec_ops (void)
|
||||
exec_ops.to_doc = "Use an executable file as a target.\n\
|
||||
Specify the filename of the executable file.";
|
||||
exec_ops.to_open = exec_open;
|
||||
exec_ops.to_close = exec_close;
|
||||
exec_ops.to_close = exec_close_1;
|
||||
exec_ops.to_attach = find_default_attach;
|
||||
exec_ops.to_xfer_partial = exec_xfer_partial;
|
||||
exec_ops.to_get_section_table = exec_get_section_table;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define EXEC_H
|
||||
|
||||
#include "target.h"
|
||||
#include "progspace.h"
|
||||
|
||||
struct target_section;
|
||||
struct target_ops;
|
||||
@ -28,6 +29,9 @@ struct bfd;
|
||||
|
||||
extern struct target_ops exec_ops;
|
||||
|
||||
#define exec_bfd current_program_space->ebfd
|
||||
#define exec_bfd_mtime current_program_space->ebfd_mtime
|
||||
|
||||
/* Builds a section table, given args BFD, SECTABLE_PTR, SECEND_PTR.
|
||||
Returns 0 if OK, 1 on error. */
|
||||
|
||||
@ -82,5 +86,6 @@ extern void add_target_sections (struct target_section *sections,
|
||||
extern void print_section_info (struct target_section_table *table,
|
||||
bfd *abfd);
|
||||
|
||||
extern void exec_close (void);
|
||||
|
||||
#endif
|
||||
|
@ -138,6 +138,7 @@ fork_inferior (char *exec_file_arg, char *allargs, char **env,
|
||||
int shell = 0;
|
||||
static char **argv;
|
||||
const char *inferior_io_terminal = get_inferior_io_terminal ();
|
||||
struct inferior *inf;
|
||||
|
||||
/* If no exec file handed to us, get it from the exec-file command
|
||||
-- with a good, common error message if none is specified. */
|
||||
@ -395,7 +396,9 @@ fork_inferior (char *exec_file_arg, char *allargs, char **env,
|
||||
if (!have_inferiors ())
|
||||
init_thread_list ();
|
||||
|
||||
add_inferior (pid);
|
||||
inf = current_inferior ();
|
||||
|
||||
inferior_appeared (inf, pid);
|
||||
|
||||
/* Needed for wait_for_inferior stuff below. */
|
||||
inferior_ptid = pid_to_ptid (pid);
|
||||
|
46
gdb/frame.c
46
gdb/frame.c
@ -70,6 +70,12 @@ struct frame_info
|
||||
moment leave this as speculation. */
|
||||
int level;
|
||||
|
||||
/* The frame's program space. */
|
||||
struct program_space *pspace;
|
||||
|
||||
/* The frame's address space. */
|
||||
struct address_space *aspace;
|
||||
|
||||
/* The frame's low-level unwinder and corresponding cache. The
|
||||
low-level unwinder is responsible for unwinding register values
|
||||
for the previous frame. The low-level unwind methods are
|
||||
@ -1059,10 +1065,12 @@ put_frame_register_bytes (struct frame_info *frame, int regnum,
|
||||
/* Create a sentinel frame. */
|
||||
|
||||
static struct frame_info *
|
||||
create_sentinel_frame (struct regcache *regcache)
|
||||
create_sentinel_frame (struct program_space *pspace, struct regcache *regcache)
|
||||
{
|
||||
struct frame_info *frame = FRAME_OBSTACK_ZALLOC (struct frame_info);
|
||||
frame->level = -1;
|
||||
frame->pspace = pspace;
|
||||
frame->aspace = get_regcache_aspace (regcache);
|
||||
/* Explicitly initialize the sentinel frame's cache. Provide it
|
||||
with the underlying regcache. In the future additional
|
||||
information, such as the frame's thread will be added. */
|
||||
@ -1144,7 +1152,7 @@ get_current_frame (void)
|
||||
if (current_frame == NULL)
|
||||
{
|
||||
struct frame_info *sentinel_frame =
|
||||
create_sentinel_frame (get_current_regcache ());
|
||||
create_sentinel_frame (current_program_space, get_current_regcache ());
|
||||
if (catch_exceptions (uiout, unwind_to_current_frame, sentinel_frame,
|
||||
RETURN_MASK_ERROR) != 0)
|
||||
{
|
||||
@ -1275,7 +1283,7 @@ create_new_frame (CORE_ADDR addr, CORE_ADDR pc)
|
||||
|
||||
fi = FRAME_OBSTACK_ZALLOC (struct frame_info);
|
||||
|
||||
fi->next = create_sentinel_frame (get_current_regcache ());
|
||||
fi->next = create_sentinel_frame (current_program_space, get_current_regcache ());
|
||||
|
||||
/* Set/update this frame's cached PC value, found in the next frame.
|
||||
Do this before looking for this frame's unwinder. A sniffer is
|
||||
@ -1284,6 +1292,10 @@ create_new_frame (CORE_ADDR addr, CORE_ADDR pc)
|
||||
fi->next->prev_pc.value = pc;
|
||||
fi->next->prev_pc.p = 1;
|
||||
|
||||
/* We currently assume that frame chain's can't cross spaces. */
|
||||
fi->pspace = fi->next->pspace;
|
||||
fi->aspace = fi->next->aspace;
|
||||
|
||||
/* Select/initialize both the unwind function and the frame's type
|
||||
based on the PC. */
|
||||
fi->unwind = frame_unwind_find_by_frame (fi, &fi->prologue_cache);
|
||||
@ -1557,6 +1569,11 @@ get_prev_frame_raw (struct frame_info *this_frame)
|
||||
prev_frame = FRAME_OBSTACK_ZALLOC (struct frame_info);
|
||||
prev_frame->level = this_frame->level + 1;
|
||||
|
||||
/* For now, assume we don't have frame chains crossing address
|
||||
spaces. */
|
||||
prev_frame->pspace = this_frame->pspace;
|
||||
prev_frame->aspace = this_frame->aspace;
|
||||
|
||||
/* Don't yet compute ->unwind (and hence ->type). It is computed
|
||||
on-demand in get_frame_type, frame_register_unwind, and
|
||||
get_frame_id. */
|
||||
@ -1939,6 +1956,29 @@ get_frame_type (struct frame_info *frame)
|
||||
return frame->unwind->type;
|
||||
}
|
||||
|
||||
struct program_space *
|
||||
get_frame_program_space (struct frame_info *frame)
|
||||
{
|
||||
return frame->pspace;
|
||||
}
|
||||
|
||||
struct program_space *
|
||||
frame_unwind_program_space (struct frame_info *this_frame)
|
||||
{
|
||||
gdb_assert (this_frame);
|
||||
|
||||
/* This is really a placeholder to keep the API consistent --- we
|
||||
assume for now that we don't have frame chains crossing
|
||||
spaces. */
|
||||
return this_frame->pspace;
|
||||
}
|
||||
|
||||
struct address_space *
|
||||
get_frame_address_space (struct frame_info *frame)
|
||||
{
|
||||
return frame->aspace;
|
||||
}
|
||||
|
||||
/* Memory access methods. */
|
||||
|
||||
void
|
||||
|
@ -406,6 +406,15 @@ extern int frame_relative_level (struct frame_info *fi);
|
||||
|
||||
extern enum frame_type get_frame_type (struct frame_info *);
|
||||
|
||||
/* Return the frame's program space. */
|
||||
extern struct program_space *get_frame_program_space (struct frame_info *);
|
||||
|
||||
/* Unwind THIS frame's program space from the NEXT frame. */
|
||||
extern struct program_space *frame_unwind_program_space (struct frame_info *);
|
||||
|
||||
/* Return the frame's address space. */
|
||||
extern struct address_space *get_frame_address_space (struct frame_info *);
|
||||
|
||||
/* For frames where we can not unwind further, describe why. */
|
||||
|
||||
enum unwind_stop_reason
|
||||
|
@ -249,6 +249,7 @@ struct gdbarch
|
||||
gdbarch_get_syscall_number_ftype *get_syscall_number;
|
||||
int has_global_solist;
|
||||
int has_global_breakpoints;
|
||||
gdbarch_has_shared_address_space_ftype *has_shared_address_space;
|
||||
};
|
||||
|
||||
|
||||
@ -389,6 +390,7 @@ struct gdbarch startup_gdbarch =
|
||||
0, /* get_syscall_number */
|
||||
0, /* has_global_solist */
|
||||
0, /* has_global_breakpoints */
|
||||
default_has_shared_address_space, /* has_shared_address_space */
|
||||
/* startup_gdbarch() */
|
||||
};
|
||||
|
||||
@ -472,6 +474,7 @@ gdbarch_alloc (const struct gdbarch_info *info,
|
||||
gdbarch->displaced_step_location = NULL;
|
||||
gdbarch->target_signal_from_host = default_target_signal_from_host;
|
||||
gdbarch->target_signal_to_host = default_target_signal_to_host;
|
||||
gdbarch->has_shared_address_space = default_has_shared_address_space;
|
||||
/* gdbarch_alloc() */
|
||||
|
||||
return gdbarch;
|
||||
@ -649,6 +652,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
|
||||
/* Skip verify of get_syscall_number, has predicate */
|
||||
/* Skip verify of has_global_solist, invalid_p == 0 */
|
||||
/* Skip verify of has_global_breakpoints, invalid_p == 0 */
|
||||
/* Skip verify of has_shared_address_space, invalid_p == 0 */
|
||||
buf = ui_file_xstrdup (log, &length);
|
||||
make_cleanup (xfree, buf);
|
||||
if (length > 0)
|
||||
@ -890,6 +894,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: has_global_solist = %s\n",
|
||||
plongest (gdbarch->has_global_solist));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: has_shared_address_space = <%s>\n",
|
||||
host_address_to_string (gdbarch->has_shared_address_space));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: have_nonsteppable_watchpoint = %s\n",
|
||||
plongest (gdbarch->have_nonsteppable_watchpoint));
|
||||
@ -3504,6 +3511,23 @@ set_gdbarch_has_global_breakpoints (struct gdbarch *gdbarch,
|
||||
gdbarch->has_global_breakpoints = has_global_breakpoints;
|
||||
}
|
||||
|
||||
int
|
||||
gdbarch_has_shared_address_space (struct gdbarch *gdbarch)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
gdb_assert (gdbarch->has_shared_address_space != NULL);
|
||||
if (gdbarch_debug >= 2)
|
||||
fprintf_unfiltered (gdb_stdlog, "gdbarch_has_shared_address_space called\n");
|
||||
return gdbarch->has_shared_address_space (gdbarch);
|
||||
}
|
||||
|
||||
void
|
||||
set_gdbarch_has_shared_address_space (struct gdbarch *gdbarch,
|
||||
gdbarch_has_shared_address_space_ftype has_shared_address_space)
|
||||
{
|
||||
gdbarch->has_shared_address_space = has_shared_address_space;
|
||||
}
|
||||
|
||||
|
||||
/* Keep a registry of per-architecture data-pointers required by GDB
|
||||
modules. */
|
||||
|
@ -903,6 +903,12 @@ extern void set_gdbarch_has_global_solist (struct gdbarch *gdbarch, int has_glob
|
||||
extern int gdbarch_has_global_breakpoints (struct gdbarch *gdbarch);
|
||||
extern void set_gdbarch_has_global_breakpoints (struct gdbarch *gdbarch, int has_global_breakpoints);
|
||||
|
||||
/* True if inferiors share an address space (e.g., uClinux). */
|
||||
|
||||
typedef int (gdbarch_has_shared_address_space_ftype) (struct gdbarch *gdbarch);
|
||||
extern int gdbarch_has_shared_address_space (struct gdbarch *gdbarch);
|
||||
extern void set_gdbarch_has_shared_address_space (struct gdbarch *gdbarch, gdbarch_has_shared_address_space_ftype *has_shared_address_space);
|
||||
|
||||
/* Definition for an unknown syscall, used basically in error-cases. */
|
||||
#define UNKNOWN_SYSCALL (-1)
|
||||
|
||||
|
@ -756,6 +756,9 @@ v:int:has_global_solist:::0:0::0
|
||||
# visible to all address spaces automatically. For such cases,
|
||||
# this property should be set to true.
|
||||
v:int:has_global_breakpoints:::0:0::0
|
||||
|
||||
# True if inferiors share an address space (e.g., uClinux).
|
||||
m:int:has_shared_address_space:void:::default_has_shared_address_space::0
|
||||
EOF
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ struct type;
|
||||
struct regcache;
|
||||
|
||||
#include "bfd.h"
|
||||
#include "exec.h"
|
||||
|
||||
/* Return the name of the executable file as a string.
|
||||
ERR nonzero means get error if there is none specified;
|
||||
@ -103,13 +104,9 @@ extern void (*deprecated_file_changed_hook) (char *filename);
|
||||
|
||||
extern void specify_exec_file_hook (void (*hook) (char *filename));
|
||||
|
||||
/* Binary File Diddlers for the exec and core files. */
|
||||
/* Binary File Diddler for the core file. */
|
||||
|
||||
extern bfd *core_bfd;
|
||||
extern bfd *exec_bfd;
|
||||
|
||||
/* The mtime when we last opened exec_bfd. */
|
||||
extern long exec_bfd_mtime;
|
||||
|
||||
/* Whether to open exec and core files read-only or read-write. */
|
||||
|
||||
|
@ -246,6 +246,10 @@ struct thread_info *first_thread_of_process (int pid);
|
||||
/* Returns any thread of process PID. */
|
||||
extern struct thread_info *any_thread_of_process (int pid);
|
||||
|
||||
/* Returns any non-exited thread of process PID, giving preference for
|
||||
already stopped threads. */
|
||||
extern struct thread_info *any_live_thread_of_process (int pid);
|
||||
|
||||
/* Change the ptid of thread OLD_PTID to NEW_PTID. */
|
||||
void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
|
||||
|
||||
|
@ -2166,7 +2166,8 @@ gnu_attach (struct target_ops *ops, char *args, int from_tty)
|
||||
|
||||
push_target (ops);
|
||||
|
||||
inferior = add_inferior (pid);
|
||||
inferior = current_inferior ();
|
||||
inferior_appeared (inferior, pid);
|
||||
inferior->attach_flag = 1;
|
||||
|
||||
inf_update_procs (inf);
|
||||
|
@ -649,6 +649,7 @@ go32_create_inferior (struct target_ops *ops, char *exec_file,
|
||||
char *cmdline;
|
||||
char **env_save = environ;
|
||||
size_t cmdlen;
|
||||
struct inferior *inf;
|
||||
|
||||
/* If no exec file handed to us, get it from the exec-file command -- with
|
||||
a good, common error message if none is specified. */
|
||||
@ -714,7 +715,8 @@ go32_create_inferior (struct target_ops *ops, char *exec_file,
|
||||
#endif
|
||||
|
||||
inferior_ptid = pid_to_ptid (SOME_PID);
|
||||
add_inferior_silent (SOME_PID);
|
||||
inf = current_inferior ();
|
||||
inferior_appeared_silent (inf, SOME_PID);
|
||||
|
||||
push_target (&go32_ops);
|
||||
|
||||
|
@ -67,6 +67,8 @@ inf_ptrace_follow_fork (struct target_ops *ops, int follow_child)
|
||||
child_inf = add_inferior (fpid);
|
||||
child_inf->attach_flag = parent_inf->attach_flag;
|
||||
copy_terminal_info (child_inf, parent_inf);
|
||||
inf->pspace = parent_inf->pspace;
|
||||
inf->pspace = parent_inf->aspace;
|
||||
|
||||
/* Before detaching from the parent, remove all breakpoints from
|
||||
it. */
|
||||
@ -223,10 +225,10 @@ inf_ptrace_attach (struct target_ops *ops, char *args, int from_tty)
|
||||
error (_("This system does not support attaching to a process"));
|
||||
#endif
|
||||
|
||||
inferior_ptid = pid_to_ptid (pid);
|
||||
|
||||
inf = add_inferior (pid);
|
||||
inf = current_inferior ();
|
||||
inferior_appeared (inf, pid);
|
||||
inf->attach_flag = 1;
|
||||
inferior_ptid = pid_to_ptid (pid);
|
||||
|
||||
/* Always add a main thread. If some target extends the ptrace
|
||||
target, it should decorate the ptid later with more info. */
|
||||
|
@ -453,6 +453,8 @@ inf_ttrace_follow_fork (struct target_ops *ops, int follow_child)
|
||||
inferior_ptid = ptid_build (fpid, flwpid, 0);
|
||||
inf = add_inferior (fpid);
|
||||
inf->attach_flag = parent_inf->attach_flag;
|
||||
inf->pspace = parent_inf->pspace;
|
||||
inf->aspace = parent_inf->aspace;
|
||||
copy_terminal_info (inf, parent_inf);
|
||||
detach_breakpoints (pid);
|
||||
|
||||
@ -725,7 +727,8 @@ inf_ttrace_attach (struct target_ops *ops, char *args, int from_tty)
|
||||
if (ttrace (TT_PROC_ATTACH, pid, 0, TT_KILL_ON_EXIT, TT_VERSION, 0) == -1)
|
||||
perror_with_name (("ttrace"));
|
||||
|
||||
inf = add_inferior (pid);
|
||||
inf = current_inferior ();
|
||||
inferior_appeared (inf, pid);
|
||||
inf->attach_flag = 1;
|
||||
|
||||
/* Set the initial event mask. */
|
||||
|
@ -731,6 +731,7 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
|
||||
struct breakpoint *bpt;
|
||||
struct symtab_and_line sal;
|
||||
init_sal (&sal); /* initialize to zeroes */
|
||||
sal.pspace = current_program_space;
|
||||
sal.pc = bp_addr;
|
||||
sal.section = find_pc_overlay (sal.pc);
|
||||
/* Sanity. The exact same SP value is returned by
|
||||
|
@ -1515,14 +1515,13 @@ finish_forward (struct symbol *function, struct frame_info *frame)
|
||||
old_chain = make_cleanup_delete_breakpoint (breakpoint);
|
||||
|
||||
tp->proceed_to_finish = 1; /* We want stop_registers, please... */
|
||||
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
|
||||
|
||||
cargs = xmalloc (sizeof (*cargs));
|
||||
|
||||
cargs->breakpoint = breakpoint;
|
||||
cargs->function = function;
|
||||
add_continuation (tp, finish_command_continuation, cargs,
|
||||
finish_command_continuation_free_arg);
|
||||
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
|
||||
|
||||
discard_cleanups (old_chain);
|
||||
if (!target_can_async_p ())
|
||||
|
683
gdb/inferior.c
683
gdb/inferior.c
@ -18,6 +18,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "defs.h"
|
||||
#include "exec.h"
|
||||
#include "inferior.h"
|
||||
#include "target.h"
|
||||
#include "command.h"
|
||||
@ -26,28 +27,66 @@
|
||||
#include "ui-out.h"
|
||||
#include "observer.h"
|
||||
#include "gdbthread.h"
|
||||
#include "gdbcore.h"
|
||||
#include "symfile.h"
|
||||
|
||||
void _initialize_inferiors (void);
|
||||
|
||||
static struct inferior *inferior_list = NULL;
|
||||
static void inferior_alloc_data (struct inferior *inf);
|
||||
static void inferior_free_data (struct inferior *inf);
|
||||
|
||||
struct inferior *inferior_list = NULL;
|
||||
static int highest_inferior_num;
|
||||
|
||||
/* Print notices on inferior events (attach, detach, etc.), set with
|
||||
`set print inferior-events'. */
|
||||
static int print_inferior_events = 0;
|
||||
|
||||
/* The Current Inferior. */
|
||||
static struct inferior *current_inferior_ = NULL;
|
||||
|
||||
struct inferior*
|
||||
current_inferior (void)
|
||||
{
|
||||
struct inferior *inf = find_inferior_pid (ptid_get_pid (inferior_ptid));
|
||||
gdb_assert (inf);
|
||||
return inf;
|
||||
return current_inferior_;
|
||||
}
|
||||
|
||||
void
|
||||
set_current_inferior (struct inferior *inf)
|
||||
{
|
||||
/* There's always an inferior. */
|
||||
gdb_assert (inf != NULL);
|
||||
|
||||
current_inferior_ = inf;
|
||||
}
|
||||
|
||||
/* A cleanups callback, helper for save_current_program_space
|
||||
below. */
|
||||
|
||||
static void
|
||||
restore_inferior (void *arg)
|
||||
{
|
||||
struct inferior *saved_inferior = arg;
|
||||
set_current_inferior (saved_inferior);
|
||||
}
|
||||
|
||||
/* Save the current program space so that it may be restored by a later
|
||||
call to do_cleanups. Returns the struct cleanup pointer needed for
|
||||
later doing the cleanup. */
|
||||
|
||||
struct cleanup *
|
||||
save_current_inferior (void)
|
||||
{
|
||||
struct cleanup *old_chain = make_cleanup (restore_inferior,
|
||||
current_inferior_);
|
||||
return old_chain;
|
||||
}
|
||||
|
||||
static void
|
||||
free_inferior (struct inferior *inf)
|
||||
{
|
||||
discard_all_inferior_continuations (inf);
|
||||
inferior_free_data (inf);
|
||||
xfree (inf->private);
|
||||
xfree (inf);
|
||||
}
|
||||
@ -85,7 +124,10 @@ add_inferior_silent (int pid)
|
||||
inf->next = inferior_list;
|
||||
inferior_list = inf;
|
||||
|
||||
observer_notify_new_inferior (pid);
|
||||
inferior_alloc_data (inf);
|
||||
|
||||
if (pid != 0)
|
||||
inferior_appeared (inf, pid);
|
||||
|
||||
return inf;
|
||||
}
|
||||
@ -123,17 +165,13 @@ delete_thread_of_inferior (struct thread_info *tp, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If SILENT then be quiet -- don't announce a inferior death, or the
|
||||
exit of its threads. */
|
||||
static void
|
||||
delete_inferior_1 (int pid, int silent)
|
||||
void
|
||||
delete_threads_of_inferior (int pid)
|
||||
{
|
||||
struct inferior *inf, *infprev;
|
||||
struct delete_thread_of_inferior_arg arg = { pid, silent };
|
||||
struct inferior *inf;
|
||||
struct delete_thread_of_inferior_arg arg;
|
||||
|
||||
infprev = NULL;
|
||||
|
||||
for (inf = inferior_list; inf; infprev = inf, inf = inf->next)
|
||||
for (inf = inferior_list; inf; inf = inf->next)
|
||||
if (inf->pid == pid)
|
||||
break;
|
||||
|
||||
@ -141,14 +179,34 @@ delete_inferior_1 (int pid, int silent)
|
||||
return;
|
||||
|
||||
arg.pid = pid;
|
||||
arg.silent = 1;
|
||||
|
||||
iterate_over_threads (delete_thread_of_inferior, &arg);
|
||||
}
|
||||
|
||||
/* If SILENT then be quiet -- don't announce a inferior death, or the
|
||||
exit of its threads. */
|
||||
|
||||
static void
|
||||
delete_inferior_1 (struct inferior *todel, int silent)
|
||||
{
|
||||
struct inferior *inf, *infprev;
|
||||
struct delete_thread_of_inferior_arg arg;
|
||||
|
||||
infprev = NULL;
|
||||
|
||||
for (inf = inferior_list; inf; infprev = inf, inf = inf->next)
|
||||
if (inf == todel)
|
||||
break;
|
||||
|
||||
if (!inf)
|
||||
return;
|
||||
|
||||
arg.pid = inf->pid;
|
||||
arg.silent = silent;
|
||||
|
||||
iterate_over_threads (delete_thread_of_inferior, &arg);
|
||||
|
||||
/* Notify the observers before removing the inferior from the list,
|
||||
so that the observers have a change to look it up. */
|
||||
observer_notify_inferior_exit (pid);
|
||||
|
||||
if (infprev)
|
||||
infprev->next = inf->next;
|
||||
else
|
||||
@ -160,7 +218,9 @@ delete_inferior_1 (int pid, int silent)
|
||||
void
|
||||
delete_inferior (int pid)
|
||||
{
|
||||
delete_inferior_1 (pid, 0);
|
||||
struct inferior *inf = find_inferior_pid (pid);
|
||||
|
||||
delete_inferior_1 (inf, 0);
|
||||
|
||||
if (print_inferior_events)
|
||||
printf_unfiltered (_("[Inferior %d exited]\n"), pid);
|
||||
@ -169,31 +229,101 @@ delete_inferior (int pid)
|
||||
void
|
||||
delete_inferior_silent (int pid)
|
||||
{
|
||||
delete_inferior_1 (pid, 1);
|
||||
struct inferior *inf = find_inferior_pid (pid);
|
||||
|
||||
delete_inferior_1 (inf, 1);
|
||||
}
|
||||
|
||||
|
||||
/* If SILENT then be quiet -- don't announce a inferior exit, or the
|
||||
exit of its threads. */
|
||||
|
||||
static void
|
||||
exit_inferior_1 (struct inferior *inftoex, int silent)
|
||||
{
|
||||
struct inferior *inf;
|
||||
struct delete_thread_of_inferior_arg arg;
|
||||
|
||||
for (inf = inferior_list; inf; inf = inf->next)
|
||||
if (inf == inftoex)
|
||||
break;
|
||||
|
||||
if (!inf)
|
||||
return;
|
||||
|
||||
arg.pid = inf->pid;
|
||||
arg.silent = silent;
|
||||
|
||||
iterate_over_threads (delete_thread_of_inferior, &arg);
|
||||
|
||||
/* Notify the observers before removing the inferior from the list,
|
||||
so that the observers have a chance to look it up. */
|
||||
observer_notify_inferior_exit (inf->pid);
|
||||
|
||||
inf->pid = 0;
|
||||
if (inf->vfork_parent != NULL)
|
||||
{
|
||||
inf->vfork_parent->vfork_child = NULL;
|
||||
inf->vfork_parent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
exit_inferior (int pid)
|
||||
{
|
||||
struct inferior *inf = find_inferior_pid (pid);
|
||||
exit_inferior_1 (inf, 0);
|
||||
|
||||
if (print_inferior_events)
|
||||
printf_unfiltered (_("[Inferior %d exited]\n"), pid);
|
||||
}
|
||||
|
||||
void
|
||||
exit_inferior_silent (int pid)
|
||||
{
|
||||
struct inferior *inf = find_inferior_pid (pid);
|
||||
exit_inferior_1 (inf, 1);
|
||||
}
|
||||
|
||||
void
|
||||
exit_inferior_num_silent (int num)
|
||||
{
|
||||
struct inferior *inf = find_inferior_id (num);
|
||||
|
||||
exit_inferior_1 (inf, 1);
|
||||
}
|
||||
|
||||
void
|
||||
detach_inferior (int pid)
|
||||
{
|
||||
delete_inferior_1 (pid, 1);
|
||||
struct inferior *inf = find_inferior_pid (pid);
|
||||
exit_inferior_1 (inf, 1);
|
||||
|
||||
if (print_inferior_events)
|
||||
printf_unfiltered (_("[Inferior %d detached]\n"), pid);
|
||||
}
|
||||
|
||||
void
|
||||
inferior_appeared (struct inferior *inf, int pid)
|
||||
{
|
||||
inf->pid = pid;
|
||||
|
||||
observer_notify_inferior_appeared (pid);
|
||||
}
|
||||
|
||||
void
|
||||
discard_all_inferiors (void)
|
||||
{
|
||||
struct inferior *inf, *infnext;
|
||||
struct inferior *inf;
|
||||
|
||||
for (inf = inferior_list; inf; inf = infnext)
|
||||
for (inf = inferior_list; inf; inf = inf->next)
|
||||
{
|
||||
infnext = inf->next;
|
||||
delete_inferior_silent (inf->pid);
|
||||
if (inf->pid != 0)
|
||||
exit_inferior_silent (inf->pid);
|
||||
}
|
||||
}
|
||||
|
||||
static struct inferior *
|
||||
struct inferior *
|
||||
find_inferior_id (int num)
|
||||
{
|
||||
struct inferior *inf;
|
||||
@ -210,6 +340,11 @@ find_inferior_pid (int pid)
|
||||
{
|
||||
struct inferior *inf;
|
||||
|
||||
/* Looking for inferior pid == 0 is always wrong, and indicative of
|
||||
a bug somewhere else. There may be more than one with pid == 0,
|
||||
for instance. */
|
||||
gdb_assert (pid != 0);
|
||||
|
||||
for (inf = inferior_list; inf; inf = inf->next)
|
||||
if (inf->pid == pid)
|
||||
return inf;
|
||||
@ -217,6 +352,22 @@ find_inferior_pid (int pid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find an inferior bound to PSPACE. */
|
||||
|
||||
struct inferior *
|
||||
find_inferior_for_program_space (struct program_space *pspace)
|
||||
{
|
||||
struct inferior *inf;
|
||||
|
||||
for (inf = inferior_list; inf != NULL; inf = inf->next)
|
||||
{
|
||||
if (inf->pspace == pspace)
|
||||
return inf;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct inferior *
|
||||
iterate_over_inferiors (int (*callback) (struct inferior *, void *),
|
||||
void *data)
|
||||
@ -282,15 +433,72 @@ in_inferior_list (int pid)
|
||||
int
|
||||
have_inferiors (void)
|
||||
{
|
||||
return inferior_list != NULL;
|
||||
struct inferior *inf;
|
||||
|
||||
for (inf = inferior_list; inf; inf = inf->next)
|
||||
if (inf->pid != 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
have_live_inferiors (void)
|
||||
{
|
||||
struct target_ops *t;
|
||||
|
||||
/* The check on stratum suffices, as GDB doesn't currently support
|
||||
multiple target interfaces. */
|
||||
return (current_target.to_stratum >= process_stratum && have_inferiors ());
|
||||
if (have_inferiors ())
|
||||
for (t = current_target.beneath; t != NULL; t = t->beneath)
|
||||
if (t->to_stratum == process_stratum)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prune away automatically added program spaces that aren't required
|
||||
anymore. */
|
||||
|
||||
void
|
||||
prune_inferiors (void)
|
||||
{
|
||||
struct inferior *ss, **ss_link;
|
||||
struct inferior *current = current_inferior ();
|
||||
|
||||
ss = inferior_list;
|
||||
ss_link = &inferior_list;
|
||||
while (ss)
|
||||
{
|
||||
if (ss == current
|
||||
|| !ss->removable
|
||||
|| ss->pid != 0)
|
||||
{
|
||||
ss_link = &ss->next;
|
||||
ss = *ss_link;
|
||||
continue;
|
||||
}
|
||||
|
||||
*ss_link = ss->next;
|
||||
delete_inferior_1 (ss, 1);
|
||||
ss = *ss_link;
|
||||
}
|
||||
|
||||
prune_program_spaces ();
|
||||
}
|
||||
|
||||
/* Simply returns the count of inferiors. */
|
||||
|
||||
int
|
||||
number_of_inferiors (void)
|
||||
{
|
||||
struct inferior *inf;
|
||||
int count = 0;
|
||||
|
||||
for (inf = inferior_list; inf != NULL; inf = inf->next)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Prints the list of inferiors and their details on UIOUT. This is a
|
||||
@ -322,13 +530,14 @@ print_inferior (struct ui_out *uiout, int requested_inferior)
|
||||
return;
|
||||
}
|
||||
|
||||
old_chain = make_cleanup_ui_out_table_begin_end (uiout, 3, inf_count,
|
||||
old_chain = make_cleanup_ui_out_table_begin_end (uiout, 4, inf_count,
|
||||
"inferiors");
|
||||
ui_out_table_header (uiout, 1, ui_left, "current", "");
|
||||
ui_out_table_header (uiout, 4, ui_left, "number", "Num");
|
||||
ui_out_table_header (uiout, 17, ui_left, "target-id", "Description");
|
||||
ui_out_table_body (uiout);
|
||||
ui_out_table_header (uiout, 17, ui_left, "exec", "Executable");
|
||||
|
||||
ui_out_table_body (uiout);
|
||||
for (inf = inferior_list; inf; inf = inf->next)
|
||||
{
|
||||
struct cleanup *chain2;
|
||||
@ -338,24 +547,43 @@ print_inferior (struct ui_out *uiout, int requested_inferior)
|
||||
|
||||
chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
|
||||
|
||||
if (inf->pid == ptid_get_pid (inferior_ptid))
|
||||
if (inf == current_inferior ())
|
||||
ui_out_field_string (uiout, "current", "*");
|
||||
else
|
||||
ui_out_field_skip (uiout, "current");
|
||||
|
||||
ui_out_field_int (uiout, "number", inf->num);
|
||||
ui_out_field_string (uiout, "target-id",
|
||||
target_pid_to_str (pid_to_ptid (inf->pid)));
|
||||
|
||||
if (inf->pid)
|
||||
ui_out_field_string (uiout, "target-id",
|
||||
target_pid_to_str (pid_to_ptid (inf->pid)));
|
||||
else
|
||||
ui_out_field_string (uiout, "target-id", "<null>");
|
||||
|
||||
if (inf->pspace->ebfd)
|
||||
ui_out_field_string (uiout, "exec",
|
||||
bfd_get_filename (inf->pspace->ebfd));
|
||||
else
|
||||
ui_out_field_skip (uiout, "exec");
|
||||
|
||||
/* Print extra info that isn't really fit to always present in
|
||||
tabular form. Currently we print the vfork parent/child
|
||||
relationships, if any. */
|
||||
if (inf->vfork_parent)
|
||||
{
|
||||
ui_out_text (uiout, _("\n\tis vfork child of inferior "));
|
||||
ui_out_field_int (uiout, "vfork-parent", inf->vfork_parent->num);
|
||||
}
|
||||
if (inf->vfork_child)
|
||||
{
|
||||
ui_out_text (uiout, _("\n\tis vfork parent of inferior "));
|
||||
ui_out_field_int (uiout, "vfork-child", inf->vfork_child->num);
|
||||
}
|
||||
|
||||
ui_out_text (uiout, "\n");
|
||||
do_cleanups (chain2);
|
||||
}
|
||||
|
||||
if (inferior_list
|
||||
&& ptid_equal (inferior_ptid, null_ptid))
|
||||
ui_out_message (uiout, 0, "\n\
|
||||
No selected inferior/thread. See `help thread' or `help inferior'.\n");
|
||||
|
||||
do_cleanups (old_chain);
|
||||
}
|
||||
|
||||
@ -414,36 +642,52 @@ kill_inferior_command (char *args, int from_tty)
|
||||
static void
|
||||
inferior_command (char *args, int from_tty)
|
||||
{
|
||||
int num, pid;
|
||||
|
||||
if (!have_inferiors ())
|
||||
error (_("No inferiors"));
|
||||
struct inferior *inf;
|
||||
int num;
|
||||
|
||||
num = parse_and_eval_long (args);
|
||||
|
||||
if (!valid_gdb_inferior_id (num))
|
||||
inf = find_inferior_id (num);
|
||||
if (inf == NULL)
|
||||
error (_("Inferior ID %d not known."), num);
|
||||
|
||||
pid = gdb_inferior_id_to_pid (num);
|
||||
printf_filtered (_("[Switching to inferior %d [%s] (%s)]\n"),
|
||||
inf->num,
|
||||
target_pid_to_str (pid_to_ptid (inf->pid)),
|
||||
(inf->pspace->ebfd
|
||||
? bfd_get_filename (inf->pspace->ebfd)
|
||||
: _("<noexec>")));
|
||||
|
||||
if (pid != ptid_get_pid (inferior_ptid))
|
||||
if (inf->pid != 0)
|
||||
{
|
||||
struct thread_info *tp;
|
||||
if (inf->pid != ptid_get_pid (inferior_ptid))
|
||||
{
|
||||
struct thread_info *tp;
|
||||
|
||||
tp = any_thread_of_process (pid);
|
||||
if (!tp)
|
||||
error (_("Inferior has no threads."));
|
||||
tp = any_thread_of_process (inf->pid);
|
||||
if (!tp)
|
||||
error (_("Inferior has no threads."));
|
||||
|
||||
switch_to_thread (tp->ptid);
|
||||
switch_to_thread (tp->ptid);
|
||||
}
|
||||
|
||||
printf_filtered (_("[Switching to thread %d (%s)] "),
|
||||
pid_to_thread_id (inferior_ptid),
|
||||
target_pid_to_str (inferior_ptid));
|
||||
}
|
||||
else
|
||||
{
|
||||
struct inferior *inf;
|
||||
|
||||
inf = find_inferior_id (num);
|
||||
set_current_inferior (inf);
|
||||
switch_to_thread (null_ptid);
|
||||
set_current_program_space (inf->pspace);
|
||||
}
|
||||
|
||||
printf_filtered (_("[Switching to thread %d (%s)] "),
|
||||
pid_to_thread_id (inferior_ptid),
|
||||
target_pid_to_str (inferior_ptid));
|
||||
|
||||
if (is_running (inferior_ptid))
|
||||
if (inf->pid != 0 && is_running (inferior_ptid))
|
||||
ui_out_text (uiout, "(running)\n");
|
||||
else
|
||||
else if (inf->pid != 0)
|
||||
{
|
||||
ui_out_text (uiout, "\n");
|
||||
print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
|
||||
@ -467,6 +711,184 @@ info_inferiors_command (char *args, int from_tty)
|
||||
print_inferior (uiout, requested);
|
||||
}
|
||||
|
||||
/* remove-inferior ID */
|
||||
|
||||
void
|
||||
remove_inferior_command (char *args, int from_tty)
|
||||
{
|
||||
int num;
|
||||
struct inferior *inf;
|
||||
|
||||
num = parse_and_eval_long (args);
|
||||
inf = find_inferior_id (num);
|
||||
|
||||
if (inf == NULL)
|
||||
error (_("Inferior ID %d not known."), num);
|
||||
|
||||
if (inf == current_inferior ())
|
||||
error (_("Can not remove current symbol inferior."));
|
||||
|
||||
delete_inferior_1 (inf, 1);
|
||||
}
|
||||
|
||||
|
||||
/* add-inferior [-copies N] [-exec FILENAME] */
|
||||
|
||||
void
|
||||
add_inferior_command (char *args, int from_tty)
|
||||
{
|
||||
int i, copies = 1;
|
||||
char *exec = NULL;
|
||||
char **argv;
|
||||
struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
|
||||
|
||||
if (args)
|
||||
{
|
||||
argv = gdb_buildargv (args);
|
||||
make_cleanup_freeargv (argv);
|
||||
|
||||
for (; *argv != NULL; argv++)
|
||||
{
|
||||
if (**argv == '-')
|
||||
{
|
||||
if (strcmp (*argv, "-copies") == 0)
|
||||
{
|
||||
++argv;
|
||||
if (!*argv)
|
||||
error (_("No argument to -copies"));
|
||||
copies = parse_and_eval_long (*argv);
|
||||
}
|
||||
else if (strcmp (*argv, "-exec") == 0)
|
||||
{
|
||||
++argv;
|
||||
if (!*argv)
|
||||
error (_("No argument to -exec"));
|
||||
exec = *argv;
|
||||
}
|
||||
}
|
||||
else
|
||||
error (_("Invalid argument"));
|
||||
}
|
||||
}
|
||||
|
||||
save_current_space_and_thread ();
|
||||
|
||||
for (i = 0; i < copies; ++i)
|
||||
{
|
||||
struct address_space *aspace;
|
||||
struct program_space *pspace;
|
||||
struct inferior *inf;
|
||||
|
||||
/* If all inferiors share an address space on this system, this
|
||||
doesn't really return a new address space; otherwise, it
|
||||
really does. */
|
||||
aspace = maybe_new_address_space ();
|
||||
pspace = add_program_space (aspace);
|
||||
inf = add_inferior (0);
|
||||
inf->pspace = pspace;
|
||||
inf->aspace = pspace->aspace;
|
||||
|
||||
printf_filtered (_("Added inferior %d\n"), inf->num);
|
||||
|
||||
if (exec != NULL)
|
||||
{
|
||||
/* Switch over temporarily, while reading executable and
|
||||
symbols.q */
|
||||
set_current_program_space (pspace);
|
||||
set_current_inferior (inf);
|
||||
switch_to_thread (null_ptid);
|
||||
|
||||
exec_file_attach (exec, from_tty);
|
||||
symbol_file_add_main (exec, from_tty);
|
||||
}
|
||||
}
|
||||
|
||||
do_cleanups (old_chain);
|
||||
}
|
||||
|
||||
/* clone-inferior [-copies N] [ID] */
|
||||
|
||||
void
|
||||
clone_inferior_command (char *args, int from_tty)
|
||||
{
|
||||
int i, copies = 1;
|
||||
char **argv;
|
||||
struct inferior *orginf = NULL;
|
||||
struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
|
||||
|
||||
if (args)
|
||||
{
|
||||
argv = gdb_buildargv (args);
|
||||
make_cleanup_freeargv (argv);
|
||||
|
||||
for (; *argv != NULL; argv++)
|
||||
{
|
||||
if (**argv == '-')
|
||||
{
|
||||
if (strcmp (*argv, "-copies") == 0)
|
||||
{
|
||||
++argv;
|
||||
if (!*argv)
|
||||
error (_("No argument to -copies"));
|
||||
copies = parse_and_eval_long (*argv);
|
||||
|
||||
if (copies < 0)
|
||||
error (_("Invalid copies number"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (orginf == NULL)
|
||||
{
|
||||
int num;
|
||||
|
||||
/* The first non-option (-) argument specified the
|
||||
program space ID. */
|
||||
num = parse_and_eval_long (*argv);
|
||||
orginf = find_inferior_id (num);
|
||||
|
||||
if (orginf == NULL)
|
||||
error (_("Inferior ID %d not known."), num);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
error (_("Invalid argument"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If no inferior id was specified, then the user wants to clone the
|
||||
current inferior. */
|
||||
if (orginf == NULL)
|
||||
orginf = current_inferior ();
|
||||
|
||||
save_current_space_and_thread ();
|
||||
|
||||
for (i = 0; i < copies; ++i)
|
||||
{
|
||||
struct address_space *aspace;
|
||||
struct program_space *pspace;
|
||||
struct inferior *inf;
|
||||
|
||||
/* If all inferiors share an address space on this system, this
|
||||
doesn't really return a new address space; otherwise, it
|
||||
really does. */
|
||||
aspace = maybe_new_address_space ();
|
||||
pspace = add_program_space (aspace);
|
||||
inf = add_inferior (0);
|
||||
inf->pspace = pspace;
|
||||
inf->aspace = pspace->aspace;
|
||||
|
||||
printf_filtered (_("Added inferior %d.\n"), inf->num);
|
||||
|
||||
set_current_inferior (inf);
|
||||
switch_to_thread (null_ptid);
|
||||
clone_program_space (pspace, orginf->pspace);
|
||||
}
|
||||
|
||||
do_cleanups (old_chain);
|
||||
}
|
||||
|
||||
/* Print notices when new inferiors are created and die. */
|
||||
static void
|
||||
show_print_inferior_events (struct ui_file *file, int from_tty,
|
||||
@ -475,19 +897,141 @@ show_print_inferior_events (struct ui_file *file, int from_tty,
|
||||
fprintf_filtered (file, _("Printing of inferior events is %s.\n"), value);
|
||||
}
|
||||
|
||||
void
|
||||
_initialize_inferiors (void)
|
||||
|
||||
|
||||
/* Keep a registry of per-inferior data-pointers required by other GDB
|
||||
modules. */
|
||||
|
||||
struct inferior_data
|
||||
{
|
||||
unsigned index;
|
||||
void (*cleanup) (struct inferior *, void *);
|
||||
};
|
||||
|
||||
struct inferior_data_registration
|
||||
{
|
||||
struct inferior_data *data;
|
||||
struct inferior_data_registration *next;
|
||||
};
|
||||
|
||||
struct inferior_data_registry
|
||||
{
|
||||
struct inferior_data_registration *registrations;
|
||||
unsigned num_registrations;
|
||||
};
|
||||
|
||||
static struct inferior_data_registry inferior_data_registry
|
||||
= { NULL, 0 };
|
||||
|
||||
const struct inferior_data *
|
||||
register_inferior_data_with_cleanup
|
||||
(void (*cleanup) (struct inferior *, void *))
|
||||
{
|
||||
struct inferior_data_registration **curr;
|
||||
|
||||
/* Append new registration. */
|
||||
for (curr = &inferior_data_registry.registrations;
|
||||
*curr != NULL; curr = &(*curr)->next);
|
||||
|
||||
*curr = XMALLOC (struct inferior_data_registration);
|
||||
(*curr)->next = NULL;
|
||||
(*curr)->data = XMALLOC (struct inferior_data);
|
||||
(*curr)->data->index = inferior_data_registry.num_registrations++;
|
||||
(*curr)->data->cleanup = cleanup;
|
||||
|
||||
return (*curr)->data;
|
||||
}
|
||||
|
||||
const struct inferior_data *
|
||||
register_inferior_data (void)
|
||||
{
|
||||
return register_inferior_data_with_cleanup (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
inferior_alloc_data (struct inferior *inf)
|
||||
{
|
||||
gdb_assert (inf->data == NULL);
|
||||
inf->num_data = inferior_data_registry.num_registrations;
|
||||
inf->data = XCALLOC (inf->num_data, void *);
|
||||
}
|
||||
|
||||
static void
|
||||
inferior_free_data (struct inferior *inf)
|
||||
{
|
||||
gdb_assert (inf->data != NULL);
|
||||
clear_inferior_data (inf);
|
||||
xfree (inf->data);
|
||||
inf->data = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
clear_inferior_data (struct inferior *inf)
|
||||
{
|
||||
struct inferior_data_registration *registration;
|
||||
int i;
|
||||
|
||||
gdb_assert (inf->data != NULL);
|
||||
|
||||
for (registration = inferior_data_registry.registrations, i = 0;
|
||||
i < inf->num_data;
|
||||
registration = registration->next, i++)
|
||||
if (inf->data[i] != NULL && registration->data->cleanup)
|
||||
registration->data->cleanup (inf, inf->data[i]);
|
||||
|
||||
memset (inf->data, 0, inf->num_data * sizeof (void *));
|
||||
}
|
||||
|
||||
void
|
||||
set_inferior_data (struct inferior *inf,
|
||||
const struct inferior_data *data,
|
||||
void *value)
|
||||
{
|
||||
gdb_assert (data->index < inf->num_data);
|
||||
inf->data[data->index] = value;
|
||||
}
|
||||
|
||||
void *
|
||||
inferior_data (struct inferior *inf, const struct inferior_data *data)
|
||||
{
|
||||
gdb_assert (data->index < inf->num_data);
|
||||
return inf->data[data->index];
|
||||
}
|
||||
|
||||
void
|
||||
initialize_inferiors (void)
|
||||
{
|
||||
/* There's always one inferior. Note that this function isn't an
|
||||
automatic _initialize_foo function, since other _initialize_foo
|
||||
routines may need to install their per-inferior data keys. We
|
||||
can only allocate an inferior when all those modules have done
|
||||
that. Do this after initialize_progspace, due to the
|
||||
current_program_space reference. */
|
||||
current_inferior_ = add_inferior (0);
|
||||
current_inferior_->pspace = current_program_space;
|
||||
current_inferior_->aspace = current_program_space->aspace;
|
||||
|
||||
add_info ("inferiors", info_inferiors_command,
|
||||
_("IDs of currently known inferiors."));
|
||||
|
||||
add_setshow_boolean_cmd ("inferior-events", no_class,
|
||||
&print_inferior_events, _("\
|
||||
Set printing of inferior events (e.g., inferior start and exit)."), _("\
|
||||
Show printing of inferior events (e.g., inferior start and exit)."), NULL,
|
||||
NULL,
|
||||
show_print_inferior_events,
|
||||
&setprintlist, &showprintlist);
|
||||
add_com ("add-inferior", no_class, add_inferior_command, _("\
|
||||
Add a new inferior.\n\
|
||||
Usage: add-inferior [-copies <N>] [-exec <FILENAME>]\n\
|
||||
N is the optional number of inferior to add, default is 1.\n\
|
||||
FILENAME is the file name of the executable to use\n\
|
||||
as main program."));
|
||||
|
||||
add_com ("remove-inferior", no_class, remove_inferior_command, _("\
|
||||
Remove inferior ID.\n\
|
||||
Usage: remove-inferior ID"));
|
||||
|
||||
add_com ("clone-inferior", no_class, clone_inferior_command, _("\
|
||||
Clone inferior ID.\n\
|
||||
Usage: clone-inferior [-copies <N>] [ID]\n\
|
||||
Add N copies of inferior ID. The new inferior has the same\n\
|
||||
executable loaded as the copied inferior. If -copies is not specified,\n\
|
||||
adds 1 copy. If ID is not specified, it is the current inferior\n\
|
||||
that is cloned."));
|
||||
|
||||
add_cmd ("inferior", class_run, detach_inferior_command, _("\
|
||||
Detach from inferior ID."),
|
||||
@ -501,4 +1045,13 @@ Kill inferior ID."),
|
||||
Use this command to switch between inferiors.\n\
|
||||
The new inferior ID must be currently known."),
|
||||
&cmdlist);
|
||||
|
||||
add_setshow_boolean_cmd ("inferior-events", no_class,
|
||||
&print_inferior_events, _("\
|
||||
Set printing of inferior events (e.g., inferior start and exit)."), _("\
|
||||
Show printing of inferior events (e.g., inferior start and exit)."), NULL,
|
||||
NULL,
|
||||
show_print_inferior_events,
|
||||
&setprintlist, &showprintlist);
|
||||
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ struct terminal_info;
|
||||
/* For struct frame_id. */
|
||||
#include "frame.h"
|
||||
|
||||
#include "progspace.h"
|
||||
|
||||
/* Two structures are used to record inferior state.
|
||||
|
||||
inferior_thread_state contains state about the program itself like its
|
||||
@ -149,6 +151,11 @@ extern int step_stop_if_no_debug;
|
||||
are kept running freely. */
|
||||
extern int non_stop;
|
||||
|
||||
/* If set (default), when following a fork, GDB will detach from one
|
||||
the fork branches, child or parent. Exactly which branch is
|
||||
detached depends on 'set follow-fork-mode' setting. */
|
||||
extern int detach_fork;
|
||||
|
||||
extern void generic_mourn_inferior (void);
|
||||
|
||||
extern void terminal_save_ours (void);
|
||||
@ -408,6 +415,18 @@ struct inferior
|
||||
the ptid_t.pid member of threads of this inferior. */
|
||||
int pid;
|
||||
|
||||
/* True if this was an auto-created inferior, e.g. created from
|
||||
following a fork; false, if this inferior was manually added by
|
||||
the user, and we should not attempt to prune it
|
||||
automatically. */
|
||||
int removable;
|
||||
|
||||
/* The address space bound to this inferior. */
|
||||
struct address_space *aspace;
|
||||
|
||||
/* The program space bound to this inferior. */
|
||||
struct program_space *pspace;
|
||||
|
||||
/* See the definition of stop_kind above. */
|
||||
enum stop_kind stop_soon;
|
||||
|
||||
@ -415,14 +434,30 @@ struct inferior
|
||||
forked. */
|
||||
int attach_flag;
|
||||
|
||||
/* If this inferior is a vfork child, then this is the pointer to
|
||||
its vfork parent, if GDB is still attached to it. */
|
||||
struct inferior *vfork_parent;
|
||||
|
||||
/* If this process is a vfork parent, this is the pointer to the
|
||||
child. Since a vfork parent is left frozen by the kernel until
|
||||
the child execs or exits, a process can only have one vfork child
|
||||
at a given time. */
|
||||
struct inferior *vfork_child;
|
||||
|
||||
/* True if this inferior should be detached when it's vfork sibling
|
||||
exits or execs. */
|
||||
int pending_detach;
|
||||
|
||||
/* True if this inferior is a vfork parent waiting for a vfork child
|
||||
not under our control to be done with the shared memory region,
|
||||
either by exiting or execing. */
|
||||
int waiting_for_vfork_done;
|
||||
|
||||
/* What is left to do for an execution command after any thread of
|
||||
this inferior stops. For continuations associated with a
|
||||
specific thread, see `struct thread_info'. */
|
||||
struct continuation *continuations;
|
||||
|
||||
/* Terminal info and state managed by inflow.c. */
|
||||
struct terminal_info *terminal_info;
|
||||
|
||||
/* Private data used by the target vector implementation. */
|
||||
struct private_inferior *private;
|
||||
|
||||
@ -439,8 +474,24 @@ struct inferior
|
||||
/* This counts all syscall catch requests, so we can readily determine
|
||||
if any catching is necessary. */
|
||||
int total_syscalls_count;
|
||||
|
||||
/* Per inferior data-pointers required by other GDB modules. */
|
||||
void **data;
|
||||
unsigned num_data;
|
||||
};
|
||||
|
||||
/* Keep a registry of per-inferior data-pointers required by other GDB
|
||||
modules. */
|
||||
|
||||
extern const struct inferior_data *register_inferior_data (void);
|
||||
extern const struct inferior_data *register_inferior_data_with_cleanup
|
||||
(void (*cleanup) (struct inferior *, void *));
|
||||
extern void clear_inferior_data (struct inferior *inf);
|
||||
extern void set_inferior_data (struct inferior *inf,
|
||||
const struct inferior_data *data, void *value);
|
||||
extern void *inferior_data (struct inferior *inf,
|
||||
const struct inferior_data *data);
|
||||
|
||||
/* Create an empty inferior list, or empty the existing one. */
|
||||
extern void init_inferior_list (void);
|
||||
|
||||
@ -464,6 +515,14 @@ extern void delete_inferior_silent (int pid);
|
||||
/* Delete an existing inferior list entry, due to inferior detaching. */
|
||||
extern void detach_inferior (int pid);
|
||||
|
||||
extern void exit_inferior (int pid);
|
||||
|
||||
extern void exit_inferior_silent (int pid);
|
||||
|
||||
extern void exit_inferior_num_silent (int num);
|
||||
|
||||
extern void inferior_appeared (struct inferior *inf, int pid);
|
||||
|
||||
/* Get rid of all inferiors. */
|
||||
extern void discard_all_inferiors (void);
|
||||
|
||||
@ -482,9 +541,16 @@ extern int in_inferior_list (int pid);
|
||||
not the system's). */
|
||||
extern int valid_gdb_inferior_id (int num);
|
||||
|
||||
/* Search function to lookup a inferior by target 'pid'. */
|
||||
/* Search function to lookup an inferior by target 'pid'. */
|
||||
extern struct inferior *find_inferior_pid (int pid);
|
||||
|
||||
/* Search function to lookup an inferior by GDB 'num'. */
|
||||
extern struct inferior *find_inferior_id (int num);
|
||||
|
||||
/* Find an inferior bound to PSPACE. */
|
||||
extern struct inferior *
|
||||
find_inferior_for_program_space (struct program_space *pspace);
|
||||
|
||||
/* Inferior iterator function.
|
||||
|
||||
Calls a callback function once for each inferior, so long as the
|
||||
@ -516,4 +582,16 @@ extern int have_live_inferiors (void);
|
||||
this if there is no current inferior. */
|
||||
extern struct inferior *current_inferior (void);
|
||||
|
||||
extern void set_current_inferior (struct inferior *);
|
||||
|
||||
extern struct cleanup *save_current_inferior (void);
|
||||
|
||||
extern struct inferior *inferior_list;
|
||||
|
||||
/* Prune away automatically added inferiors that aren't required
|
||||
anymore. */
|
||||
extern void prune_inferiors (void);
|
||||
|
||||
extern int number_of_inferiors (void);
|
||||
|
||||
#endif /* !defined (INFERIOR_H) */
|
||||
|
128
gdb/inflow.c
128
gdb/inflow.c
@ -86,6 +86,8 @@ struct terminal_info
|
||||
unimportant. */
|
||||
static struct terminal_info our_terminal_info;
|
||||
|
||||
static struct terminal_info *get_inflow_inferior_data (struct inferior *);
|
||||
|
||||
#ifdef PROCESS_GROUP_TYPE
|
||||
|
||||
/* Return the process group of the current inferior. */
|
||||
@ -93,7 +95,7 @@ static struct terminal_info our_terminal_info;
|
||||
PROCESS_GROUP_TYPE
|
||||
inferior_process_group (void)
|
||||
{
|
||||
return current_inferior ()->terminal_info->process_group;
|
||||
return get_inflow_inferior_data (current_inferior ())->process_group;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -201,15 +203,15 @@ terminal_init_inferior_with_pgrp (int pgrp)
|
||||
if (gdb_has_a_terminal ())
|
||||
{
|
||||
struct inferior *inf = current_inferior ();
|
||||
struct terminal_info *tinfo = get_inflow_inferior_data (inf);
|
||||
|
||||
/* We could just as well copy our_ttystate (if we felt like
|
||||
adding a new function serial_copy_tty_state()). */
|
||||
xfree (inf->terminal_info->ttystate);
|
||||
inf->terminal_info->ttystate
|
||||
= serial_get_tty_state (stdin_serial);
|
||||
xfree (tinfo->ttystate);
|
||||
tinfo->ttystate = serial_get_tty_state (stdin_serial);
|
||||
|
||||
#ifdef PROCESS_GROUP_TYPE
|
||||
inf->terminal_info->process_group = pgrp;
|
||||
tinfo->process_group = pgrp;
|
||||
#endif
|
||||
|
||||
/* Make sure that next time we call terminal_inferior (which will be
|
||||
@ -256,15 +258,17 @@ void
|
||||
terminal_inferior (void)
|
||||
{
|
||||
struct inferior *inf;
|
||||
struct terminal_info *tinfo;
|
||||
|
||||
if (!terminal_is_ours)
|
||||
return;
|
||||
|
||||
inf = current_inferior ();
|
||||
tinfo = get_inflow_inferior_data (inf);
|
||||
|
||||
if (gdb_has_a_terminal ()
|
||||
&& inf->terminal_info->ttystate != NULL
|
||||
&& inf->terminal_info->run_terminal == NULL)
|
||||
&& tinfo->ttystate != NULL
|
||||
&& tinfo->run_terminal == NULL)
|
||||
{
|
||||
int result;
|
||||
|
||||
@ -272,8 +276,8 @@ terminal_inferior (void)
|
||||
/* Is there a reason this is being done twice? It happens both
|
||||
places we use F_SETFL, so I'm inclined to think perhaps there
|
||||
is some reason, however perverse. Perhaps not though... */
|
||||
result = fcntl (0, F_SETFL, inf->terminal_info->tflags);
|
||||
result = fcntl (0, F_SETFL, inf->terminal_info->tflags);
|
||||
result = fcntl (0, F_SETFL, tinfo->tflags);
|
||||
result = fcntl (0, F_SETFL, tinfo->tflags);
|
||||
OOPSY ("fcntl F_SETFL");
|
||||
#endif
|
||||
|
||||
@ -281,7 +285,7 @@ terminal_inferior (void)
|
||||
terminal_ours, we will not change in our out of raw mode with
|
||||
this call, so we don't flush any input. */
|
||||
result = serial_set_tty_state (stdin_serial,
|
||||
inf->terminal_info->ttystate);
|
||||
tinfo->ttystate);
|
||||
OOPSY ("setting tty state");
|
||||
|
||||
if (!job_control)
|
||||
@ -307,13 +311,13 @@ terminal_inferior (void)
|
||||
if (job_control)
|
||||
{
|
||||
#ifdef HAVE_TERMIOS
|
||||
result = tcsetpgrp (0, inf->terminal_info->process_group);
|
||||
result = tcsetpgrp (0, tinfo->process_group);
|
||||
if (!inf->attach_flag)
|
||||
OOPSY ("tcsetpgrp");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SGTTY
|
||||
result = ioctl (0, TIOCSPGRP, &inf->terminal_info->process_group);
|
||||
result = ioctl (0, TIOCSPGRP, &tinfo->process_group);
|
||||
if (!inf->attach_flag)
|
||||
OOPSY ("TIOCSPGRP");
|
||||
#endif
|
||||
@ -355,6 +359,7 @@ static void
|
||||
terminal_ours_1 (int output_only)
|
||||
{
|
||||
struct inferior *inf;
|
||||
struct terminal_info *tinfo;
|
||||
|
||||
if (terminal_is_ours)
|
||||
return;
|
||||
@ -367,8 +372,9 @@ terminal_ours_1 (int output_only)
|
||||
avoids attempting all the ioctl's when running in batch. */
|
||||
|
||||
inf = current_inferior ();
|
||||
tinfo = get_inflow_inferior_data (inf);
|
||||
|
||||
if (inf->terminal_info->run_terminal != NULL || gdb_has_a_terminal () == 0)
|
||||
if (tinfo->run_terminal != NULL || gdb_has_a_terminal () == 0)
|
||||
return;
|
||||
|
||||
{
|
||||
@ -384,15 +390,15 @@ terminal_ours_1 (int output_only)
|
||||
osigttou = (void (*)()) signal (SIGTTOU, SIG_IGN);
|
||||
#endif
|
||||
|
||||
xfree (inf->terminal_info->ttystate);
|
||||
inf->terminal_info->ttystate = serial_get_tty_state (stdin_serial);
|
||||
xfree (tinfo->ttystate);
|
||||
tinfo->ttystate = serial_get_tty_state (stdin_serial);
|
||||
|
||||
#ifdef PROCESS_GROUP_TYPE
|
||||
if (!inf->attach_flag)
|
||||
/* If setpgrp failed in terminal_inferior, this would give us
|
||||
our process group instead of the inferior's. See
|
||||
terminal_inferior for details. */
|
||||
inf->terminal_info->process_group = gdb_getpgrp ();
|
||||
tinfo->process_group = gdb_getpgrp ();
|
||||
#endif
|
||||
|
||||
/* Here we used to set ICANON in our ttystate, but I believe this
|
||||
@ -410,7 +416,7 @@ terminal_ours_1 (int output_only)
|
||||
*/
|
||||
|
||||
serial_noflush_set_tty_state (stdin_serial, our_terminal_info.ttystate,
|
||||
inf->terminal_info->ttystate);
|
||||
tinfo->ttystate);
|
||||
|
||||
if (job_control)
|
||||
{
|
||||
@ -446,7 +452,7 @@ terminal_ours_1 (int output_only)
|
||||
}
|
||||
|
||||
#ifdef F_GETFL
|
||||
inf->terminal_info->tflags = fcntl (0, F_GETFL, 0);
|
||||
tinfo->tflags = fcntl (0, F_GETFL, 0);
|
||||
|
||||
/* Is there a reason this is being done twice? It happens both
|
||||
places we use F_SETFL, so I'm inclined to think perhaps there
|
||||
@ -457,18 +463,38 @@ terminal_ours_1 (int output_only)
|
||||
}
|
||||
}
|
||||
|
||||
/* This is a "new_inferior" observer. It's business is to allocate
|
||||
the TERMINAL_INFO member of the inferior structure. This field is
|
||||
private to inflow.c, and its type is opaque to the rest of GDB.
|
||||
PID is the target pid of the inferior that has just been added to
|
||||
the inferior list. */
|
||||
/* Per-inferior data key. */
|
||||
static const struct inferior_data *inflow_inferior_data;
|
||||
|
||||
static void
|
||||
inflow_new_inferior (int pid)
|
||||
inflow_inferior_data_cleanup (struct inferior *inf, void *arg)
|
||||
{
|
||||
struct inferior *inf = find_inferior_pid (pid);
|
||||
struct terminal_info *info;
|
||||
|
||||
inf->terminal_info = XZALLOC (struct terminal_info);
|
||||
info = inferior_data (inf, inflow_inferior_data);
|
||||
if (info != NULL)
|
||||
{
|
||||
xfree (info->run_terminal);
|
||||
xfree (info);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the current svr4 data. If none is found yet, add it now. This
|
||||
function always returns a valid object. */
|
||||
|
||||
static struct terminal_info *
|
||||
get_inflow_inferior_data (struct inferior *inf)
|
||||
{
|
||||
struct terminal_info *info;
|
||||
|
||||
info = inferior_data (inf, inflow_inferior_data);
|
||||
if (info == NULL)
|
||||
{
|
||||
info = XZALLOC (struct terminal_info);
|
||||
set_inferior_data (inf, inflow_inferior_data, info);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/* This is a "inferior_exit" observer. Releases the TERMINAL_INFO member
|
||||
@ -481,19 +507,28 @@ static void
|
||||
inflow_inferior_exit (int pid)
|
||||
{
|
||||
struct inferior *inf = find_inferior_pid (pid);
|
||||
struct terminal_info *info;
|
||||
|
||||
xfree (inf->terminal_info->run_terminal);
|
||||
xfree (inf->terminal_info);
|
||||
inf->terminal_info = NULL;
|
||||
info = inferior_data (inf, inflow_inferior_data);
|
||||
if (info != NULL)
|
||||
{
|
||||
xfree (info->run_terminal);
|
||||
xfree (info);
|
||||
set_inferior_data (inf, inflow_inferior_data, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
copy_terminal_info (struct inferior *to, struct inferior *from)
|
||||
{
|
||||
*to->terminal_info = *from->terminal_info;
|
||||
if (from->terminal_info->run_terminal)
|
||||
to->terminal_info->run_terminal
|
||||
= xstrdup (from->terminal_info->run_terminal);
|
||||
struct terminal_info *tinfo_to, *tinfo_from;
|
||||
|
||||
tinfo_to = get_inflow_inferior_data (to);
|
||||
tinfo_from = get_inflow_inferior_data (from);
|
||||
*tinfo_to = *tinfo_from;
|
||||
if (tinfo_from->run_terminal)
|
||||
tinfo_to->run_terminal
|
||||
= xstrdup (tinfo_from->run_terminal);
|
||||
}
|
||||
|
||||
void
|
||||
@ -506,6 +541,7 @@ void
|
||||
child_terminal_info (char *args, int from_tty)
|
||||
{
|
||||
struct inferior *inf;
|
||||
struct terminal_info *tinfo;
|
||||
|
||||
if (!gdb_has_a_terminal ())
|
||||
{
|
||||
@ -517,6 +553,7 @@ child_terminal_info (char *args, int from_tty)
|
||||
return;
|
||||
|
||||
inf = current_inferior ();
|
||||
tinfo = get_inflow_inferior_data (inf);
|
||||
|
||||
printf_filtered (_("Inferior's terminal status (currently saved by GDB):\n"));
|
||||
|
||||
@ -524,7 +561,7 @@ child_terminal_info (char *args, int from_tty)
|
||||
{
|
||||
int flags;
|
||||
|
||||
flags = inf->terminal_info->tflags;
|
||||
flags = tinfo->tflags;
|
||||
|
||||
printf_filtered ("File descriptor flags = ");
|
||||
|
||||
@ -577,13 +614,10 @@ child_terminal_info (char *args, int from_tty)
|
||||
}
|
||||
|
||||
#ifdef PROCESS_GROUP_TYPE
|
||||
printf_filtered ("Process group = %d\n",
|
||||
(int) inf->terminal_info->process_group);
|
||||
printf_filtered ("Process group = %d\n", (int) tinfo->process_group);
|
||||
#endif
|
||||
|
||||
serial_print_tty_state (stdin_serial,
|
||||
inf->terminal_info->ttystate,
|
||||
gdb_stdout);
|
||||
serial_print_tty_state (stdin_serial, tinfo->ttystate, gdb_stdout);
|
||||
}
|
||||
|
||||
/* NEW_TTY_PREFORK is called before forking a new child process,
|
||||
@ -687,8 +721,12 @@ new_tty_postfork (void)
|
||||
are sharing a tty. */
|
||||
|
||||
if (inferior_thisrun_terminal)
|
||||
current_inferior ()->terminal_info->run_terminal
|
||||
= xstrdup (inferior_thisrun_terminal);
|
||||
{
|
||||
struct inferior *inf = current_inferior ();
|
||||
struct terminal_info *tinfo = get_inflow_inferior_data (inf);
|
||||
|
||||
tinfo->run_terminal = xstrdup (inferior_thisrun_terminal);
|
||||
}
|
||||
|
||||
inferior_thisrun_terminal = NULL;
|
||||
}
|
||||
@ -712,7 +750,9 @@ void
|
||||
set_sigint_trap (void)
|
||||
{
|
||||
struct inferior *inf = current_inferior ();
|
||||
if (inf->attach_flag || inf->terminal_info->run_terminal)
|
||||
struct terminal_info *tinfo = get_inflow_inferior_data (inf);
|
||||
|
||||
if (inf->attach_flag || tinfo->run_terminal)
|
||||
{
|
||||
osig = (void (*)()) signal (SIGINT, pass_signal);
|
||||
osig_set = 1;
|
||||
@ -841,6 +881,8 @@ _initialize_inflow (void)
|
||||
#endif /* TIOCGPGRP */
|
||||
#endif /* sgtty */
|
||||
|
||||
observer_attach_new_inferior (inflow_new_inferior);
|
||||
observer_attach_inferior_exit (inflow_inferior_exit);
|
||||
|
||||
inflow_inferior_data
|
||||
= register_inferior_data_with_cleanup (inflow_inferior_data_cleanup);
|
||||
}
|
||||
|
398
gdb/infrun.c
398
gdb/infrun.c
@ -109,6 +109,9 @@ int sync_execution = 0;
|
||||
|
||||
static ptid_t previous_inferior_ptid;
|
||||
|
||||
/* Default behavior is to detach newly forked processes (legacy). */
|
||||
int detach_fork = 1;
|
||||
|
||||
int debug_displaced = 0;
|
||||
static void
|
||||
show_debug_displaced (struct ui_file *file, int from_tty,
|
||||
@ -461,6 +464,198 @@ follow_inferior_reset_breakpoints (void)
|
||||
insert_breakpoints ();
|
||||
}
|
||||
|
||||
/* The child has exited or execed: resume threads of the parent the
|
||||
user wanted to be executing. */
|
||||
|
||||
static int
|
||||
proceed_after_vfork_done (struct thread_info *thread,
|
||||
void *arg)
|
||||
{
|
||||
int pid = * (int *) arg;
|
||||
|
||||
if (ptid_get_pid (thread->ptid) == pid
|
||||
&& is_running (thread->ptid)
|
||||
&& !is_executing (thread->ptid)
|
||||
&& !thread->stop_requested
|
||||
&& thread->stop_signal == TARGET_SIGNAL_0)
|
||||
{
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"infrun: resuming vfork parent thread %s\n",
|
||||
target_pid_to_str (thread->ptid));
|
||||
|
||||
switch_to_thread (thread->ptid);
|
||||
clear_proceed_status ();
|
||||
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called whenever we notice an exec or exit event, to handle
|
||||
detaching or resuming a vfork parent. */
|
||||
|
||||
static void
|
||||
handle_vfork_child_exec_or_exit (int exec)
|
||||
{
|
||||
struct inferior *inf = current_inferior ();
|
||||
|
||||
if (inf->vfork_parent)
|
||||
{
|
||||
int resume_parent = -1;
|
||||
|
||||
/* This exec or exit marks the end of the shared memory region
|
||||
between the parent and the child. If the user wanted to
|
||||
detach from the parent, now is the time. */
|
||||
|
||||
if (inf->vfork_parent->pending_detach)
|
||||
{
|
||||
struct thread_info *tp;
|
||||
struct cleanup *old_chain;
|
||||
struct program_space *pspace;
|
||||
struct address_space *aspace;
|
||||
|
||||
/* follow-fork child, detach-on-fork on */
|
||||
|
||||
old_chain = make_cleanup_restore_current_thread ();
|
||||
|
||||
/* We're letting loose of the parent. */
|
||||
tp = any_live_thread_of_process (inf->vfork_parent->pid);
|
||||
switch_to_thread (tp->ptid);
|
||||
|
||||
/* We're about to detach from the parent, which implicitly
|
||||
removes breakpoints from its address space. There's a
|
||||
catch here: we want to reuse the spaces for the child,
|
||||
but, parent/child are still sharing the pspace at this
|
||||
point, although the exec in reality makes the kernel give
|
||||
the child a fresh set of new pages. The problem here is
|
||||
that the breakpoints module being unaware of this, would
|
||||
likely chose the child process to write to the parent
|
||||
address space. Swapping the child temporarily away from
|
||||
the spaces has the desired effect. Yes, this is "sort
|
||||
of" a hack. */
|
||||
|
||||
pspace = inf->pspace;
|
||||
aspace = inf->aspace;
|
||||
inf->aspace = NULL;
|
||||
inf->pspace = NULL;
|
||||
|
||||
if (debug_infrun || info_verbose)
|
||||
{
|
||||
target_terminal_ours ();
|
||||
|
||||
if (exec)
|
||||
fprintf_filtered (gdb_stdlog,
|
||||
"Detaching vfork parent process %d after child exec.\n",
|
||||
inf->vfork_parent->pid);
|
||||
else
|
||||
fprintf_filtered (gdb_stdlog,
|
||||
"Detaching vfork parent process %d after child exit.\n",
|
||||
inf->vfork_parent->pid);
|
||||
}
|
||||
|
||||
target_detach (NULL, 0);
|
||||
|
||||
/* Put it back. */
|
||||
inf->pspace = pspace;
|
||||
inf->aspace = aspace;
|
||||
|
||||
do_cleanups (old_chain);
|
||||
}
|
||||
else if (exec)
|
||||
{
|
||||
/* We're staying attached to the parent, so, really give the
|
||||
child a new address space. */
|
||||
inf->pspace = add_program_space (maybe_new_address_space ());
|
||||
inf->aspace = inf->pspace->aspace;
|
||||
inf->removable = 1;
|
||||
set_current_program_space (inf->pspace);
|
||||
|
||||
resume_parent = inf->vfork_parent->pid;
|
||||
|
||||
/* Break the bonds. */
|
||||
inf->vfork_parent->vfork_child = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct cleanup *old_chain;
|
||||
struct program_space *pspace;
|
||||
|
||||
/* If this is a vfork child exiting, then the pspace and
|
||||
aspaces were shared with the parent. Since we're
|
||||
reporting the process exit, we'll be mourning all that is
|
||||
found in the address space, and switching to null_ptid,
|
||||
preparing to start a new inferior. But, since we don't
|
||||
want to clobber the parent's address/program spaces, we
|
||||
go ahead and create a new one for this exiting
|
||||
inferior. */
|
||||
|
||||
/* Switch to null_ptid, so that clone_program_space doesn't want
|
||||
to read the selected frame of a dead process. */
|
||||
old_chain = save_inferior_ptid ();
|
||||
inferior_ptid = null_ptid;
|
||||
|
||||
/* This inferior is dead, so avoid giving the breakpoints
|
||||
module the option to write through to it (cloning a
|
||||
program space resets breakpoints). */
|
||||
inf->aspace = NULL;
|
||||
inf->pspace = NULL;
|
||||
pspace = add_program_space (maybe_new_address_space ());
|
||||
set_current_program_space (pspace);
|
||||
inf->removable = 1;
|
||||
clone_program_space (pspace, inf->vfork_parent->pspace);
|
||||
inf->pspace = pspace;
|
||||
inf->aspace = pspace->aspace;
|
||||
|
||||
/* Put back inferior_ptid. We'll continue mourning this
|
||||
inferior. */
|
||||
do_cleanups (old_chain);
|
||||
|
||||
resume_parent = inf->vfork_parent->pid;
|
||||
/* Break the bonds. */
|
||||
inf->vfork_parent->vfork_child = NULL;
|
||||
}
|
||||
|
||||
inf->vfork_parent = NULL;
|
||||
|
||||
gdb_assert (current_program_space == inf->pspace);
|
||||
|
||||
if (non_stop && resume_parent != -1)
|
||||
{
|
||||
/* If the user wanted the parent to be running, let it go
|
||||
free now. */
|
||||
struct cleanup *old_chain = make_cleanup_restore_current_thread ();
|
||||
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog, "infrun: resuming vfork parent process %d\n",
|
||||
resume_parent);
|
||||
|
||||
iterate_over_threads (proceed_after_vfork_done, &resume_parent);
|
||||
|
||||
do_cleanups (old_chain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enum strings for "set|show displaced-stepping". */
|
||||
|
||||
static const char follow_exec_mode_new[] = "new";
|
||||
static const char follow_exec_mode_same[] = "same";
|
||||
static const char *follow_exec_mode_names[] =
|
||||
{
|
||||
follow_exec_mode_new,
|
||||
follow_exec_mode_same,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const char *follow_exec_mode_string = follow_exec_mode_same;
|
||||
static void
|
||||
show_follow_exec_mode_string (struct ui_file *file, int from_tty,
|
||||
struct cmd_list_element *c, const char *value)
|
||||
{
|
||||
fprintf_filtered (file, _("Follow exec mode is \"%s\".\n"), value);
|
||||
}
|
||||
|
||||
/* EXECD_PATHNAME is assumed to be non-NULL. */
|
||||
|
||||
static void
|
||||
@ -468,6 +663,7 @@ follow_exec (ptid_t pid, char *execd_pathname)
|
||||
{
|
||||
struct target_ops *tgt;
|
||||
struct thread_info *th = inferior_thread ();
|
||||
struct inferior *inf = current_inferior ();
|
||||
|
||||
/* This is an exec event that we actually wish to pay attention to.
|
||||
Refresh our symbol table to the newly exec'd program, remove any
|
||||
@ -489,6 +685,9 @@ follow_exec (ptid_t pid, char *execd_pathname)
|
||||
that may write the bp's "shadow contents" (the instruction
|
||||
value that was overwritten witha TRAP instruction). Since
|
||||
we now have a new a.out, those shadow contents aren't valid. */
|
||||
|
||||
mark_breakpoints_out ();
|
||||
|
||||
update_breakpoints_after_exec ();
|
||||
|
||||
/* If there was one, it's gone now. We cannot truly step-to-next
|
||||
@ -506,7 +705,9 @@ follow_exec (ptid_t pid, char *execd_pathname)
|
||||
th->stop_requested = 0;
|
||||
|
||||
/* What is this a.out's name? */
|
||||
printf_unfiltered (_("Executing new program: %s\n"), execd_pathname);
|
||||
printf_unfiltered (_("%s is executing new program: %s\n"),
|
||||
target_pid_to_str (inferior_ptid),
|
||||
execd_pathname);
|
||||
|
||||
/* We've followed the inferior through an exec. Therefore, the
|
||||
inferior has essentially been killed & reborn. */
|
||||
@ -525,9 +726,6 @@ follow_exec (ptid_t pid, char *execd_pathname)
|
||||
execd_pathname = name;
|
||||
}
|
||||
|
||||
/* That a.out is now the one to use. */
|
||||
exec_file_attach (execd_pathname, 0);
|
||||
|
||||
/* Reset the shared library package. This ensures that we get a
|
||||
shlib event when the child reaches "_start", at which point the
|
||||
dld will have had a chance to initialize the child. */
|
||||
@ -536,6 +734,30 @@ follow_exec (ptid_t pid, char *execd_pathname)
|
||||
previous incarnation of this process. */
|
||||
no_shared_libraries (NULL, 0);
|
||||
|
||||
if (follow_exec_mode_string == follow_exec_mode_new)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
struct inferior *new_inf;
|
||||
|
||||
/* The user wants to keep the old inferior and program spaces
|
||||
around. Create a new fresh one, and switch to it. */
|
||||
|
||||
inf = add_inferior (current_inferior ()->pid);
|
||||
pspace = add_program_space (maybe_new_address_space ());
|
||||
inf->pspace = pspace;
|
||||
inf->aspace = pspace->aspace;
|
||||
|
||||
exit_inferior_num_silent (current_inferior ()->num);
|
||||
|
||||
set_current_inferior (inf);
|
||||
set_current_program_space (pspace);
|
||||
}
|
||||
|
||||
gdb_assert (current_program_space == inf->pspace);
|
||||
|
||||
/* That a.out is now the one to use. */
|
||||
exec_file_attach (execd_pathname, 0);
|
||||
|
||||
/* Load the main file's symbols. */
|
||||
symbol_file_add_main (execd_pathname, 0);
|
||||
|
||||
@ -969,6 +1191,7 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
|
||||
struct regcache *regcache;
|
||||
struct gdbarch *gdbarch;
|
||||
CORE_ADDR actual_pc;
|
||||
struct address_space *aspace;
|
||||
|
||||
head = displaced_step_request_queue;
|
||||
ptid = head->ptid;
|
||||
@ -979,8 +1202,9 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
|
||||
|
||||
regcache = get_thread_regcache (ptid);
|
||||
actual_pc = regcache_read_pc (regcache);
|
||||
aspace = get_regcache_aspace (regcache);
|
||||
|
||||
if (breakpoint_here_p (actual_pc))
|
||||
if (breakpoint_here_p (aspace, actual_pc))
|
||||
{
|
||||
if (debug_displaced)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
@ -1145,6 +1369,7 @@ resume (int step, enum target_signal sig)
|
||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
||||
struct thread_info *tp = inferior_thread ();
|
||||
CORE_ADDR pc = regcache_read_pc (regcache);
|
||||
struct address_space *aspace = get_regcache_aspace (regcache);
|
||||
|
||||
QUIT;
|
||||
|
||||
@ -1170,7 +1395,7 @@ resume (int step, enum target_signal sig)
|
||||
removed or inserted, as appropriate. The exception is if we're sitting
|
||||
at a permanent breakpoint; we need to step over it, but permanent
|
||||
breakpoints can't be removed. So we have to test for it here. */
|
||||
if (breakpoint_here_p (pc) == permanent_breakpoint_here)
|
||||
if (breakpoint_here_p (aspace, pc) == permanent_breakpoint_here)
|
||||
{
|
||||
if (gdbarch_skip_permanent_breakpoint_p (gdbarch))
|
||||
gdbarch_skip_permanent_breakpoint (gdbarch, regcache);
|
||||
@ -1287,7 +1512,7 @@ a command like `return' or `jump' to continue execution."));
|
||||
/* Most targets can step a breakpoint instruction, thus
|
||||
executing it normally. But if this one cannot, just
|
||||
continue and we will hit it anyway. */
|
||||
if (step && breakpoint_inserted_here_p (pc))
|
||||
if (step && breakpoint_inserted_here_p (aspace, pc))
|
||||
step = 0;
|
||||
}
|
||||
|
||||
@ -1361,23 +1586,26 @@ clear_proceed_status_callback (struct thread_info *tp, void *data)
|
||||
void
|
||||
clear_proceed_status (void)
|
||||
{
|
||||
if (!non_stop)
|
||||
{
|
||||
/* In all-stop mode, delete the per-thread status of all
|
||||
threads, even if inferior_ptid is null_ptid, there may be
|
||||
threads on the list. E.g., we may be launching a new
|
||||
process, while selecting the executable. */
|
||||
iterate_over_threads (clear_proceed_status_callback, NULL);
|
||||
}
|
||||
|
||||
if (!ptid_equal (inferior_ptid, null_ptid))
|
||||
{
|
||||
struct inferior *inferior;
|
||||
|
||||
if (non_stop)
|
||||
{
|
||||
/* If in non-stop mode, only delete the per-thread status
|
||||
of the current thread. */
|
||||
/* If in non-stop mode, only delete the per-thread status of
|
||||
the current thread. */
|
||||
clear_proceed_status_thread (inferior_thread ());
|
||||
}
|
||||
else
|
||||
{
|
||||
/* In all-stop mode, delete the per-thread status of
|
||||
*all* threads. */
|
||||
iterate_over_threads (clear_proceed_status_callback, NULL);
|
||||
}
|
||||
|
||||
|
||||
inferior = current_inferior ();
|
||||
inferior->stop_soon = NO_STOP_QUIETLY;
|
||||
}
|
||||
@ -1439,7 +1667,8 @@ prepare_to_proceed (int step)
|
||||
{
|
||||
struct regcache *regcache = get_thread_regcache (wait_ptid);
|
||||
|
||||
if (breakpoint_here_p (regcache_read_pc (regcache)))
|
||||
if (breakpoint_here_p (get_regcache_aspace (regcache),
|
||||
regcache_read_pc (regcache)))
|
||||
{
|
||||
/* If stepping, remember current thread to switch back to. */
|
||||
if (step)
|
||||
@ -1477,6 +1706,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
|
||||
struct gdbarch *gdbarch;
|
||||
struct thread_info *tp;
|
||||
CORE_ADDR pc;
|
||||
struct address_space *aspace;
|
||||
int oneproc = 0;
|
||||
|
||||
/* If we're stopped at a fork/vfork, follow the branch set by the
|
||||
@ -1491,6 +1721,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
|
||||
|
||||
regcache = get_current_regcache ();
|
||||
gdbarch = get_regcache_arch (regcache);
|
||||
aspace = get_regcache_aspace (regcache);
|
||||
pc = regcache_read_pc (regcache);
|
||||
|
||||
if (step > 0)
|
||||
@ -1500,7 +1731,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
|
||||
|
||||
if (addr == (CORE_ADDR) -1)
|
||||
{
|
||||
if (pc == stop_pc && breakpoint_here_p (pc)
|
||||
if (pc == stop_pc && breakpoint_here_p (aspace, pc)
|
||||
&& execution_direction != EXEC_REVERSE)
|
||||
/* There is a breakpoint at the address we will resume at,
|
||||
step one instruction before inserting breakpoints so that
|
||||
@ -2231,6 +2462,7 @@ adjust_pc_after_break (struct execution_control_state *ecs)
|
||||
{
|
||||
struct regcache *regcache;
|
||||
struct gdbarch *gdbarch;
|
||||
struct address_space *aspace;
|
||||
CORE_ADDR breakpoint_pc;
|
||||
|
||||
/* If we've hit a breakpoint, we'll normally be stopped with SIGTRAP. If
|
||||
@ -2296,6 +2528,8 @@ adjust_pc_after_break (struct execution_control_state *ecs)
|
||||
if (gdbarch_decr_pc_after_break (gdbarch) == 0)
|
||||
return;
|
||||
|
||||
aspace = get_regcache_aspace (regcache);
|
||||
|
||||
/* Find the location where (if we've hit a breakpoint) the
|
||||
breakpoint would be. */
|
||||
breakpoint_pc = regcache_read_pc (regcache)
|
||||
@ -2309,8 +2543,8 @@ adjust_pc_after_break (struct execution_control_state *ecs)
|
||||
already queued and arrive later. To suppress those spurious
|
||||
SIGTRAPs, we keep a list of such breakpoint locations for a bit,
|
||||
and retire them after a number of stop events are reported. */
|
||||
if (software_breakpoint_inserted_here_p (breakpoint_pc)
|
||||
|| (non_stop && moribund_breakpoint_here_p (breakpoint_pc)))
|
||||
if (software_breakpoint_inserted_here_p (aspace, breakpoint_pc)
|
||||
|| (non_stop && moribund_breakpoint_here_p (aspace, breakpoint_pc)))
|
||||
{
|
||||
struct cleanup *old_cleanups = NULL;
|
||||
if (RECORD_IS_USED)
|
||||
@ -2411,7 +2645,9 @@ handle_syscall_event (struct execution_control_state *ecs)
|
||||
fprintf_unfiltered (gdb_stdlog, "infrun: syscall number = '%d'\n",
|
||||
syscall_number);
|
||||
|
||||
ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
|
||||
ecs->event_thread->stop_bpstat
|
||||
= bpstat_stop_status (get_regcache_aspace (regcache),
|
||||
stop_pc, ecs->ptid);
|
||||
ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
|
||||
|
||||
if (!ecs->random_signal)
|
||||
@ -2622,6 +2858,9 @@ handle_inferior_event (struct execution_control_state *ecs)
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_EXITED\n");
|
||||
inferior_ptid = ecs->ptid;
|
||||
set_current_inferior (find_inferior_pid (ptid_get_pid (ecs->ptid)));
|
||||
set_current_program_space (current_inferior ()->pspace);
|
||||
handle_vfork_child_exec_or_exit (0);
|
||||
target_terminal_ours (); /* Must do this before mourn anyway */
|
||||
print_stop_reason (EXITED, ecs->ws.value.integer);
|
||||
|
||||
@ -2640,6 +2879,9 @@ handle_inferior_event (struct execution_control_state *ecs)
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SIGNALLED\n");
|
||||
inferior_ptid = ecs->ptid;
|
||||
set_current_inferior (find_inferior_pid (ptid_get_pid (ecs->ptid)));
|
||||
set_current_program_space (current_inferior ()->pspace);
|
||||
handle_vfork_child_exec_or_exit (0);
|
||||
stop_print_frame = 0;
|
||||
target_terminal_ours (); /* Must do this before mourn anyway */
|
||||
|
||||
@ -2696,19 +2938,45 @@ handle_inferior_event (struct execution_control_state *ecs)
|
||||
|
||||
stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
|
||||
|
||||
ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
|
||||
ecs->event_thread->stop_bpstat
|
||||
= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
|
||||
stop_pc, ecs->ptid);
|
||||
|
||||
ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
|
||||
|
||||
/* If no catchpoint triggered for this, then keep going. */
|
||||
if (ecs->random_signal)
|
||||
{
|
||||
ptid_t parent;
|
||||
ptid_t child;
|
||||
int should_resume;
|
||||
int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
|
||||
|
||||
ecs->event_thread->stop_signal = TARGET_SIGNAL_0;
|
||||
|
||||
should_resume = follow_fork ();
|
||||
|
||||
parent = ecs->ptid;
|
||||
child = ecs->ws.value.related_pid;
|
||||
|
||||
/* In non-stop mode, also resume the other branch. */
|
||||
if (non_stop && !detach_fork)
|
||||
{
|
||||
if (follow_child)
|
||||
switch_to_thread (parent);
|
||||
else
|
||||
switch_to_thread (child);
|
||||
|
||||
ecs->event_thread = inferior_thread ();
|
||||
ecs->ptid = inferior_ptid;
|
||||
keep_going (ecs);
|
||||
}
|
||||
|
||||
if (follow_child)
|
||||
switch_to_thread (child);
|
||||
else
|
||||
switch_to_thread (parent);
|
||||
|
||||
ecs->event_thread = inferior_thread ();
|
||||
ecs->ptid = inferior_ptid;
|
||||
|
||||
@ -2721,6 +2989,22 @@ handle_inferior_event (struct execution_control_state *ecs)
|
||||
ecs->event_thread->stop_signal = TARGET_SIGNAL_TRAP;
|
||||
goto process_event_stop_test;
|
||||
|
||||
case TARGET_WAITKIND_VFORK_DONE:
|
||||
/* Done with the shared memory region. Re-insert breakpoints in
|
||||
the parent, and keep going. */
|
||||
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_VFORK_DONE\n");
|
||||
|
||||
if (!ptid_equal (ecs->ptid, inferior_ptid))
|
||||
context_switch (ecs->ptid);
|
||||
|
||||
current_inferior ()->waiting_for_vfork_done = 0;
|
||||
/* This also takes care of reinserting breakpoints in the
|
||||
previously locked inferior. */
|
||||
keep_going (ecs);
|
||||
return;
|
||||
|
||||
case TARGET_WAITKIND_EXECD:
|
||||
if (debug_infrun)
|
||||
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_EXECD\n");
|
||||
@ -2733,12 +3017,17 @@ handle_inferior_event (struct execution_control_state *ecs)
|
||||
|
||||
stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
|
||||
|
||||
/* Do whatever is necessary to the parent branch of the vfork. */
|
||||
handle_vfork_child_exec_or_exit (1);
|
||||
|
||||
/* This causes the eventpoints and symbol table to be reset.
|
||||
Must do this now, before trying to determine whether to
|
||||
stop. */
|
||||
follow_exec (inferior_ptid, ecs->ws.value.execd_pathname);
|
||||
|
||||
ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
|
||||
ecs->event_thread->stop_bpstat
|
||||
= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
|
||||
stop_pc, ecs->ptid);
|
||||
ecs->random_signal = !bpstat_explains_signal (ecs->event_thread->stop_bpstat);
|
||||
|
||||
/* Note that this may be referenced from inside
|
||||
@ -2929,14 +3218,15 @@ targets should add new threads to the thread list themselves in non-stop mode.")
|
||||
if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP)
|
||||
{
|
||||
int thread_hop_needed = 0;
|
||||
struct address_space *aspace = get_regcache_aspace (get_current_regcache ());
|
||||
|
||||
/* Check if a regular breakpoint has been hit before checking
|
||||
for a potential single step breakpoint. Otherwise, GDB will
|
||||
not see this breakpoint hit when stepping onto breakpoints. */
|
||||
if (regular_breakpoint_inserted_here_p (stop_pc))
|
||||
if (regular_breakpoint_inserted_here_p (aspace, stop_pc))
|
||||
{
|
||||
ecs->random_signal = 0;
|
||||
if (!breakpoint_thread_match (stop_pc, ecs->ptid))
|
||||
if (!breakpoint_thread_match (aspace, stop_pc, ecs->ptid))
|
||||
thread_hop_needed = 1;
|
||||
}
|
||||
else if (singlestep_breakpoints_inserted_p)
|
||||
@ -3223,7 +3513,8 @@ targets should add new threads to the thread list themselves in non-stop mode.")
|
||||
non-standard signals can't be explained by the breakpoint. */
|
||||
if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP
|
||||
|| (! ecs->event_thread->trap_expected
|
||||
&& breakpoint_inserted_here_p (stop_pc)
|
||||
&& breakpoint_inserted_here_p (get_regcache_aspace (get_current_regcache ()),
|
||||
stop_pc)
|
||||
&& (ecs->event_thread->stop_signal == TARGET_SIGNAL_ILL
|
||||
|| ecs->event_thread->stop_signal == TARGET_SIGNAL_SEGV
|
||||
|| ecs->event_thread->stop_signal == TARGET_SIGNAL_EMT))
|
||||
@ -3280,8 +3571,10 @@ targets should add new threads to the thread list themselves in non-stop mode.")
|
||||
}
|
||||
|
||||
/* See if there is a breakpoint at the current PC. */
|
||||
ecs->event_thread->stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
|
||||
|
||||
ecs->event_thread->stop_bpstat
|
||||
= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
|
||||
stop_pc, ecs->ptid);
|
||||
|
||||
/* Following in case break condition called a
|
||||
function. */
|
||||
stop_print_frame = 1;
|
||||
@ -3812,6 +4105,7 @@ infrun: not switching back to stepped thread, it has vanished\n");
|
||||
struct symtab_and_line sr_sal;
|
||||
init_sal (&sr_sal);
|
||||
sr_sal.pc = pc_after_resolver;
|
||||
sr_sal.pspace = get_frame_program_space (frame);
|
||||
|
||||
insert_step_resume_breakpoint_at_sal (gdbarch,
|
||||
sr_sal, null_frame_id);
|
||||
@ -3922,8 +4216,9 @@ infrun: not switching back to stepped thread, it has vanished\n");
|
||||
/* Normal function call return (static or dynamic). */
|
||||
init_sal (&sr_sal);
|
||||
sr_sal.pc = ecs->stop_func_start;
|
||||
insert_step_resume_breakpoint_at_sal (gdbarch,
|
||||
sr_sal, null_frame_id);
|
||||
sr_sal.pspace = get_frame_program_space (frame);
|
||||
insert_step_resume_breakpoint_at_sal (gdbarch,
|
||||
sr_sal, null_frame_id);
|
||||
}
|
||||
else
|
||||
insert_step_resume_breakpoint_at_caller (frame);
|
||||
@ -3948,6 +4243,7 @@ infrun: not switching back to stepped thread, it has vanished\n");
|
||||
struct symtab_and_line sr_sal;
|
||||
init_sal (&sr_sal);
|
||||
sr_sal.pc = ecs->stop_func_start;
|
||||
sr_sal.pspace = get_frame_program_space (frame);
|
||||
|
||||
insert_step_resume_breakpoint_at_sal (gdbarch,
|
||||
sr_sal, null_frame_id);
|
||||
@ -3994,6 +4290,7 @@ infrun: not switching back to stepped thread, it has vanished\n");
|
||||
struct symtab_and_line sr_sal;
|
||||
init_sal (&sr_sal);
|
||||
sr_sal.pc = ecs->stop_func_start;
|
||||
sr_sal.pspace = get_frame_program_space (frame);
|
||||
insert_step_resume_breakpoint_at_sal (gdbarch,
|
||||
sr_sal, null_frame_id);
|
||||
}
|
||||
@ -4059,6 +4356,7 @@ infrun: not switching back to stepped thread, it has vanished\n");
|
||||
init_sal (&sr_sal); /* initialize to zeroes */
|
||||
sr_sal.pc = real_stop_pc;
|
||||
sr_sal.section = find_pc_overlay (sr_sal.pc);
|
||||
sr_sal.pspace = get_frame_program_space (frame);
|
||||
|
||||
/* Do not specify what the fp should be when we stop since
|
||||
on some machines the prologue is where the new fp value
|
||||
@ -4338,6 +4636,7 @@ handle_step_into_function (struct gdbarch *gdbarch,
|
||||
init_sal (&sr_sal); /* initialize to zeroes */
|
||||
sr_sal.pc = ecs->stop_func_start;
|
||||
sr_sal.section = find_pc_overlay (ecs->stop_func_start);
|
||||
sr_sal.pspace = get_frame_program_space (get_current_frame ());
|
||||
|
||||
/* Do not specify what the fp should be when we stop since on
|
||||
some machines the prologue is where the new fp value is
|
||||
@ -4429,6 +4728,7 @@ insert_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
|
||||
gdbarch = get_frame_arch (return_frame);
|
||||
sr_sal.pc = gdbarch_addr_bits_remove (gdbarch, get_frame_pc (return_frame));
|
||||
sr_sal.section = find_pc_overlay (sr_sal.pc);
|
||||
sr_sal.pspace = get_frame_program_space (return_frame);
|
||||
|
||||
insert_step_resume_breakpoint_at_sal (gdbarch, sr_sal,
|
||||
get_stack_frame_id (return_frame));
|
||||
@ -4465,6 +4765,7 @@ insert_step_resume_breakpoint_at_caller (struct frame_info *next_frame)
|
||||
sr_sal.pc = gdbarch_addr_bits_remove (gdbarch,
|
||||
frame_unwind_caller_pc (next_frame));
|
||||
sr_sal.section = find_pc_overlay (sr_sal.pc);
|
||||
sr_sal.pspace = frame_unwind_program_space (next_frame);
|
||||
|
||||
insert_step_resume_breakpoint_at_sal (gdbarch, sr_sal,
|
||||
frame_unwind_caller_id (next_frame));
|
||||
@ -4968,6 +5269,11 @@ Further execution is probably impossible.\n"));
|
||||
Delete any breakpoint that is to be deleted at the next stop. */
|
||||
breakpoint_auto_delete (inferior_thread ()->stop_bpstat);
|
||||
}
|
||||
|
||||
/* Try to get rid of automatically added inferiors that are no
|
||||
longer needed. Keeping those around slows down things linearly.
|
||||
Note that this never removes the current inferior. */
|
||||
prune_inferiors ();
|
||||
}
|
||||
|
||||
static int
|
||||
@ -6039,6 +6345,30 @@ By default, the debugger will follow the parent process."),
|
||||
show_follow_fork_mode_string,
|
||||
&setlist, &showlist);
|
||||
|
||||
add_setshow_enum_cmd ("follow-exec-mode", class_run,
|
||||
follow_exec_mode_names,
|
||||
&follow_exec_mode_string, _("\
|
||||
Set debugger response to a program call of exec."), _("\
|
||||
Show debugger response to a program call of exec."), _("\
|
||||
An exec call replaces the program image of a process.\n\
|
||||
\n\
|
||||
follow-exec-mode can be:\n\
|
||||
\n\
|
||||
new - the debugger creates a new inferior and rebinds the process \n\
|
||||
to this new inferior. The program the process was running before\n\
|
||||
the exec call can be restarted afterwards by restarting the original\n\
|
||||
inferior.\n\
|
||||
\n\
|
||||
same - the debugger keeps the process bound to the same inferior.\n\
|
||||
The new executable image replaces the previous executable loaded in\n\
|
||||
the inferior. Restarting the inferior after the exec call restarts\n\
|
||||
the executable the process was running after the exec call.\n\
|
||||
\n\
|
||||
By default, the debugger will use the same inferior."),
|
||||
NULL,
|
||||
show_follow_exec_mode_string,
|
||||
&setlist, &showlist);
|
||||
|
||||
add_setshow_enum_cmd ("scheduler-locking", class_run,
|
||||
scheduler_enums, &scheduler_mode, _("\
|
||||
Set mode for locking scheduler during execution."), _("\
|
||||
@ -6097,6 +6427,14 @@ Options are 'forward' or 'reverse'."),
|
||||
set_exec_direction_func, show_exec_direction_func,
|
||||
&setlist, &showlist);
|
||||
|
||||
/* Set/show detach-on-fork: user-settable mode. */
|
||||
|
||||
add_setshow_boolean_cmd ("detach-on-fork", class_run, &detach_fork, _("\
|
||||
Set whether gdb will detach the child of a fork."), _("\
|
||||
Show whether gdb will detach the child of a fork."), _("\
|
||||
Tells gdb whether to detach the child of a fork."),
|
||||
NULL, NULL, &setlist, &showlist);
|
||||
|
||||
/* ptid initializations */
|
||||
null_ptid = ptid_build (0, 0, 0);
|
||||
minus_one_ptid = ptid_build (-1, 0, 0);
|
||||
|
@ -1584,6 +1584,8 @@ decode_all_digits (char **argptr, struct symtab *default_symtab,
|
||||
|
||||
init_sal (&val);
|
||||
|
||||
val.pspace = current_program_space;
|
||||
|
||||
/* This is where we need to make sure that we have good defaults.
|
||||
We must guarantee that this section of code is never executed
|
||||
when we are called with just a function name, since
|
||||
@ -1635,6 +1637,7 @@ decode_all_digits (char **argptr, struct symtab *default_symtab,
|
||||
if (val.symtab == 0)
|
||||
val.symtab = file_symtab;
|
||||
|
||||
val.pspace = SYMTAB_PSPACE (val.symtab);
|
||||
val.pc = 0;
|
||||
values.sals = (struct symtab_and_line *)
|
||||
xmalloc (sizeof (struct symtab_and_line));
|
||||
@ -1706,6 +1709,7 @@ decode_dollar (char *copy, int funfirstline, struct symtab *default_symtab,
|
||||
val.symtab = file_symtab ? file_symtab : default_symtab;
|
||||
val.line = valx;
|
||||
val.pc = 0;
|
||||
val.pspace = current_program_space;
|
||||
|
||||
values.sals = (struct symtab_and_line *) xmalloc (sizeof val);
|
||||
values.sals[0] = val;
|
||||
|
@ -41,9 +41,6 @@ static int highest_fork_num;
|
||||
/* Prevent warning from -Wmissing-prototypes. */
|
||||
extern void _initialize_linux_fork (void);
|
||||
|
||||
int detach_fork = 1; /* Default behavior is to detach
|
||||
newly forked processes (legacy). */
|
||||
|
||||
/* Fork list data structure: */
|
||||
struct fork_info
|
||||
{
|
||||
@ -648,14 +645,6 @@ _initialize_linux_fork (void)
|
||||
{
|
||||
init_fork_list ();
|
||||
|
||||
/* Set/show detach-on-fork: user-settable mode. */
|
||||
|
||||
add_setshow_boolean_cmd ("detach-on-fork", class_obscure, &detach_fork, _("\
|
||||
Set whether gdb will detach the child of a fork."), _("\
|
||||
Show whether gdb will detach the child of a fork."), _("\
|
||||
Tells gdb whether to detach the child of a fork."),
|
||||
NULL, NULL, &setlist, &showlist);
|
||||
|
||||
/* Checkpoint command: create a fork of the inferior process
|
||||
and set it aside for later debugging. */
|
||||
|
||||
|
293
gdb/linux-nat.c
293
gdb/linux-nat.c
@ -55,6 +55,7 @@
|
||||
#include "xml-support.h"
|
||||
#include "terminal.h"
|
||||
#include <sys/vfs.h>
|
||||
#include "solib.h"
|
||||
|
||||
#ifndef SPUFS_MAGIC
|
||||
#define SPUFS_MAGIC 0x23c9b64e
|
||||
@ -669,24 +670,45 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
||||
if (!detach_fork)
|
||||
linux_enable_event_reporting (pid_to_ptid (child_pid));
|
||||
|
||||
if (has_vforked
|
||||
&& !non_stop /* Non-stop always resumes both branches. */
|
||||
&& (!target_is_async_p () || sync_execution)
|
||||
&& !(follow_child || detach_fork || sched_multi))
|
||||
{
|
||||
/* The parent stays blocked inside the vfork syscall until the
|
||||
child execs or exits. If we don't let the child run, then
|
||||
the parent stays blocked. If we're telling the parent to run
|
||||
in the foreground, the user will not be able to ctrl-c to get
|
||||
back the terminal, effectively hanging the debug session. */
|
||||
fprintf_filtered (gdb_stderr, _("\
|
||||
Can not resume the parent process over vfork in the foreground while \n\
|
||||
holding the child stopped. Try \"set detach-on-fork\" or \
|
||||
\"set schedule-multiple\".\n"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (! follow_child)
|
||||
{
|
||||
/* We're already attached to the parent, by default. */
|
||||
struct lwp_info *child_lp = NULL;
|
||||
|
||||
/* Before detaching from the child, remove all breakpoints from
|
||||
it. If we forked, then this has already been taken care of
|
||||
by infrun.c. If we vforked however, any breakpoint inserted
|
||||
in the parent is visible in the child, even those added while
|
||||
stopped in a vfork catchpoint. This won't actually modify
|
||||
the breakpoint list, but will physically remove the
|
||||
breakpoints from the child. This will remove the breakpoints
|
||||
from the parent also, but they'll be reinserted below. */
|
||||
if (has_vforked)
|
||||
detach_breakpoints (child_pid);
|
||||
/* We're already attached to the parent, by default. */
|
||||
|
||||
/* Detach new forked process? */
|
||||
if (detach_fork)
|
||||
{
|
||||
/* Before detaching from the child, remove all breakpoints
|
||||
from it. If we forked, then this has already been taken
|
||||
care of by infrun.c. If we vforked however, any
|
||||
breakpoint inserted in the parent is visible in the
|
||||
child, even those added while stopped in a vfork
|
||||
catchpoint. This will remove the breakpoints from the
|
||||
parent also, but they'll be reinserted below. */
|
||||
if (has_vforked)
|
||||
{
|
||||
/* keep breakpoints list in sync. */
|
||||
remove_breakpoints_pid (GET_PID (inferior_ptid));
|
||||
}
|
||||
|
||||
if (info_verbose || debug_linux_nat)
|
||||
{
|
||||
target_terminal_ours ();
|
||||
@ -700,7 +722,6 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
||||
else
|
||||
{
|
||||
struct inferior *parent_inf, *child_inf;
|
||||
struct lwp_info *lp;
|
||||
struct cleanup *old_chain;
|
||||
|
||||
/* Add process to GDB's tables. */
|
||||
@ -711,12 +732,47 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
||||
copy_terminal_info (child_inf, parent_inf);
|
||||
|
||||
old_chain = save_inferior_ptid ();
|
||||
save_current_program_space ();
|
||||
|
||||
inferior_ptid = ptid_build (child_pid, child_pid, 0);
|
||||
add_thread (inferior_ptid);
|
||||
lp = add_lwp (inferior_ptid);
|
||||
lp->stopped = 1;
|
||||
child_lp = add_lwp (inferior_ptid);
|
||||
child_lp->stopped = 1;
|
||||
child_lp->resumed = 1;
|
||||
|
||||
/* If this is a vfork child, then the address-space is
|
||||
shared with the parent. */
|
||||
if (has_vforked)
|
||||
{
|
||||
child_inf->pspace = parent_inf->pspace;
|
||||
child_inf->aspace = parent_inf->aspace;
|
||||
|
||||
/* The parent will be frozen until the child is done
|
||||
with the shared region. Keep track of the
|
||||
parent. */
|
||||
child_inf->vfork_parent = parent_inf;
|
||||
child_inf->pending_detach = 0;
|
||||
parent_inf->vfork_child = child_inf;
|
||||
parent_inf->pending_detach = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
child_inf->aspace = new_address_space ();
|
||||
child_inf->pspace = add_program_space (child_inf->aspace);
|
||||
child_inf->removable = 1;
|
||||
set_current_program_space (child_inf->pspace);
|
||||
clone_program_space (child_inf->pspace, parent_inf->pspace);
|
||||
|
||||
/* Let the shared library layer (solib-svr4) learn about
|
||||
this new process, relocate the cloned exec, pull in
|
||||
shared libraries, and install the solib event
|
||||
breakpoint. If a "cloned-VM" event was propagated
|
||||
better throughout the core, this wouldn't be
|
||||
required. */
|
||||
solib_create_inferior_hook ();
|
||||
}
|
||||
|
||||
/* Let the thread_db layer learn about this new process. */
|
||||
check_for_thread_db ();
|
||||
|
||||
do_cleanups (old_chain);
|
||||
@ -724,16 +780,34 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
||||
|
||||
if (has_vforked)
|
||||
{
|
||||
struct lwp_info *lp;
|
||||
struct inferior *parent_inf;
|
||||
|
||||
parent_inf = current_inferior ();
|
||||
|
||||
/* If we detached from the child, then we have to be careful
|
||||
to not insert breakpoints in the parent until the child
|
||||
is done with the shared memory region. However, if we're
|
||||
staying attached to the child, then we can and should
|
||||
insert breakpoints, so that we can debug it. A
|
||||
subsequent child exec or exit is enough to know when does
|
||||
the child stops using the parent's address space. */
|
||||
parent_inf->waiting_for_vfork_done = detach_fork;
|
||||
|
||||
lp = find_lwp_pid (pid_to_ptid (parent_pid));
|
||||
gdb_assert (linux_supports_tracefork_flag >= 0);
|
||||
if (linux_supports_tracevforkdone (0))
|
||||
{
|
||||
int status;
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"LCFF: waiting for VFORK_DONE on %d\n",
|
||||
parent_pid);
|
||||
|
||||
ptrace (PTRACE_CONT, parent_pid, 0, 0);
|
||||
my_waitpid (parent_pid, &status, __WALL);
|
||||
if ((status >> 16) != PTRACE_EVENT_VFORK_DONE)
|
||||
warning (_("Unexpected waitpid result %06x when waiting for "
|
||||
"vfork-done"), status);
|
||||
lp->stopped = 1;
|
||||
lp->resumed = 1;
|
||||
|
||||
/* We'll handle the VFORK_DONE event like any other
|
||||
event, in target_wait. */
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -768,12 +842,26 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
||||
is only the single-step breakpoint at vfork's return
|
||||
point. */
|
||||
|
||||
usleep (10000);
|
||||
}
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"LCFF: no VFORK_DONE support, sleeping a bit\n");
|
||||
|
||||
/* Since we vforked, breakpoints were removed in the parent
|
||||
too. Put them back. */
|
||||
reattach_breakpoints (parent_pid);
|
||||
usleep (10000);
|
||||
|
||||
/* Pretend we've seen a PTRACE_EVENT_VFORK_DONE event,
|
||||
and leave it pending. The next linux_nat_resume call
|
||||
will notice a pending event, and bypasses actually
|
||||
resuming the inferior. */
|
||||
lp->status = 0;
|
||||
lp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
|
||||
lp->stopped = 0;
|
||||
lp->resumed = 1;
|
||||
|
||||
/* If we're in async mode, need to tell the event loop
|
||||
there's something here to process. */
|
||||
if (target_can_async_p ())
|
||||
async_file_mark ();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -781,16 +869,19 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
||||
struct thread_info *tp;
|
||||
struct inferior *parent_inf, *child_inf;
|
||||
struct lwp_info *lp;
|
||||
|
||||
/* Before detaching from the parent, remove all breakpoints from it. */
|
||||
remove_breakpoints ();
|
||||
struct program_space *parent_pspace;
|
||||
|
||||
if (info_verbose || debug_linux_nat)
|
||||
{
|
||||
target_terminal_ours ();
|
||||
fprintf_filtered (gdb_stdlog,
|
||||
"Attaching after fork to child process %d.\n",
|
||||
child_pid);
|
||||
if (has_vforked)
|
||||
fprintf_filtered (gdb_stdlog, _("\
|
||||
Attaching after process %d vfork to child process %d.\n"),
|
||||
parent_pid, child_pid);
|
||||
else
|
||||
fprintf_filtered (gdb_stdlog, _("\
|
||||
Attaching after process %d fork to child process %d.\n"),
|
||||
parent_pid, child_pid);
|
||||
}
|
||||
|
||||
/* Add the new inferior first, so that the target_detach below
|
||||
@ -802,53 +893,68 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child)
|
||||
child_inf->attach_flag = parent_inf->attach_flag;
|
||||
copy_terminal_info (child_inf, parent_inf);
|
||||
|
||||
/* If we're vforking, we may want to hold on to the parent until
|
||||
the child exits or execs. At exec time we can remove the old
|
||||
breakpoints from the parent and detach it; at exit time we
|
||||
could do the same (or even, sneakily, resume debugging it - the
|
||||
child's exec has failed, or something similar).
|
||||
parent_pspace = parent_inf->pspace;
|
||||
|
||||
This doesn't clean up "properly", because we can't call
|
||||
target_detach, but that's OK; if the current target is "child",
|
||||
then it doesn't need any further cleanups, and lin_lwp will
|
||||
generally not encounter vfork (vfork is defined to fork
|
||||
in libpthread.so).
|
||||
|
||||
The holding part is very easy if we have VFORKDONE events;
|
||||
but keeping track of both processes is beyond GDB at the
|
||||
moment. So we don't expose the parent to the rest of GDB.
|
||||
Instead we quietly hold onto it until such time as we can
|
||||
safely resume it. */
|
||||
/* If we're vforking, we want to hold on to the parent until the
|
||||
child exits or execs. At child exec or exit time we can
|
||||
remove the old breakpoints from the parent and detach or
|
||||
resume debugging it. Otherwise, detach the parent now; we'll
|
||||
want to reuse it's program/address spaces, but we can't set
|
||||
them to the child before removing breakpoints from the
|
||||
parent, otherwise, the breakpoints module could decide to
|
||||
remove breakpoints from the wrong process (since they'd be
|
||||
assigned to the same address space). */
|
||||
|
||||
if (has_vforked)
|
||||
{
|
||||
struct lwp_info *parent_lwp;
|
||||
|
||||
linux_parent_pid = parent_pid;
|
||||
|
||||
/* Get rid of the inferior on the core side as well. */
|
||||
inferior_ptid = null_ptid;
|
||||
detach_inferior (parent_pid);
|
||||
|
||||
/* Also get rid of all its lwps. We will detach from this
|
||||
inferior soon-ish, but, we will still get an exit event
|
||||
reported through waitpid when it exits. If we didn't get
|
||||
rid of the lwps from our list, we would end up reporting
|
||||
the inferior exit to the core, which would then try to
|
||||
mourn a non-existing (from the core's perspective)
|
||||
inferior. */
|
||||
parent_lwp = find_lwp_pid (pid_to_ptid (parent_pid));
|
||||
purge_lwp_list (GET_PID (parent_lwp->ptid));
|
||||
linux_parent_pid = parent_pid;
|
||||
gdb_assert (child_inf->vfork_parent == NULL);
|
||||
gdb_assert (parent_inf->vfork_child == NULL);
|
||||
child_inf->vfork_parent = parent_inf;
|
||||
child_inf->pending_detach = 0;
|
||||
parent_inf->vfork_child = child_inf;
|
||||
parent_inf->pending_detach = detach_fork;
|
||||
parent_inf->waiting_for_vfork_done = 0;
|
||||
}
|
||||
else if (detach_fork)
|
||||
target_detach (NULL, 0);
|
||||
|
||||
/* Note that the detach above makes PARENT_INF dangling. */
|
||||
|
||||
/* Add the child thread to the appropriate lists, and switch to
|
||||
this new thread, before cloning the program space, and
|
||||
informing the solib layer about this new process. */
|
||||
|
||||
inferior_ptid = ptid_build (child_pid, child_pid, 0);
|
||||
add_thread (inferior_ptid);
|
||||
lp = add_lwp (inferior_ptid);
|
||||
lp->stopped = 1;
|
||||
lp->resumed = 1;
|
||||
|
||||
/* If this is a vfork child, then the address-space is shared
|
||||
with the parent. If we detached from the parent, then we can
|
||||
reuse the parent's program/address spaces. */
|
||||
if (has_vforked || detach_fork)
|
||||
{
|
||||
child_inf->pspace = parent_pspace;
|
||||
child_inf->aspace = child_inf->pspace->aspace;
|
||||
}
|
||||
else
|
||||
{
|
||||
child_inf->aspace = new_address_space ();
|
||||
child_inf->pspace = add_program_space (child_inf->aspace);
|
||||
child_inf->removable = 1;
|
||||
set_current_program_space (child_inf->pspace);
|
||||
clone_program_space (child_inf->pspace, parent_pspace);
|
||||
|
||||
/* Let the shared library layer (solib-svr4) learn about
|
||||
this new process, relocate the cloned exec, pull in
|
||||
shared libraries, and install the solib event breakpoint.
|
||||
If a "cloned-VM" event was propagated better throughout
|
||||
the core, this wouldn't be required. */
|
||||
solib_create_inferior_hook ();
|
||||
}
|
||||
|
||||
/* Let the thread_db layer learn about this new process. */
|
||||
check_for_thread_db ();
|
||||
}
|
||||
|
||||
@ -1749,7 +1855,16 @@ linux_nat_detach (struct target_ops *ops, char *args, int from_tty)
|
||||
static int
|
||||
resume_callback (struct lwp_info *lp, void *data)
|
||||
{
|
||||
if (lp->stopped && lp->status == 0)
|
||||
struct inferior *inf = find_inferior_pid (GET_PID (lp->ptid));
|
||||
|
||||
if (lp->stopped && inf->vfork_child != NULL)
|
||||
{
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
"RC: Not resuming %s (vfork parent)\n",
|
||||
target_pid_to_str (lp->ptid));
|
||||
}
|
||||
else if (lp->stopped && lp->status == 0)
|
||||
{
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
@ -1870,7 +1985,7 @@ linux_nat_resume (struct target_ops *ops,
|
||||
}
|
||||
}
|
||||
|
||||
if (lp->status)
|
||||
if (lp->status || lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
|
||||
{
|
||||
/* FIXME: What should we do if we are supposed to continue
|
||||
this thread with a signal? */
|
||||
@ -2236,25 +2351,28 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
|
||||
ourstatus->value.execd_pathname
|
||||
= xstrdup (linux_child_pid_to_exec_file (pid));
|
||||
|
||||
if (linux_parent_pid)
|
||||
{
|
||||
detach_breakpoints (linux_parent_pid);
|
||||
ptrace (PTRACE_DETACH, linux_parent_pid, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
linux_parent_pid = 0;
|
||||
if (event == PTRACE_EVENT_VFORK_DONE)
|
||||
{
|
||||
if (current_inferior ()->waiting_for_vfork_done)
|
||||
{
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog, "\
|
||||
LHEW: Got expected PTRACE_EVENT_VFORK_DONE from LWP %ld: stopping\n",
|
||||
GET_LWP (lp->ptid));
|
||||
|
||||
ourstatus->kind = TARGET_WAITKIND_VFORK_DONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* At this point, all inserted breakpoints are gone. Doing this
|
||||
as soon as we detect an exec prevents the badness of deleting
|
||||
a breakpoint writing the current "shadow contents" to lift
|
||||
the bp. That shadow is NOT valid after an exec.
|
||||
|
||||
Note that we have to do this after the detach_breakpoints
|
||||
call above, otherwise breakpoints wouldn't be lifted from the
|
||||
parent on a vfork, because detach_breakpoints would think
|
||||
that breakpoints are not inserted. */
|
||||
mark_breakpoints_out ();
|
||||
return 0;
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog, "\
|
||||
LHEW: Got PTRACE_EVENT_VFORK_DONE from LWP %ld: resuming\n",
|
||||
GET_LWP (lp->ptid));
|
||||
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
internal_error (__FILE__, __LINE__,
|
||||
@ -2456,6 +2574,13 @@ maybe_clear_ignore_sigint (struct lwp_info *lp)
|
||||
static int
|
||||
stop_wait_callback (struct lwp_info *lp, void *data)
|
||||
{
|
||||
struct inferior *inf = find_inferior_pid (GET_PID (lp->ptid));
|
||||
|
||||
/* If this is a vfork parent, bail out, it is not going to report
|
||||
any SIGSTOP until the vfork is done with. */
|
||||
if (inf->vfork_child != NULL)
|
||||
return 0;
|
||||
|
||||
if (!lp->stopped)
|
||||
{
|
||||
int status;
|
||||
@ -2690,7 +2815,7 @@ cancel_breakpoint (struct lwp_info *lp)
|
||||
CORE_ADDR pc;
|
||||
|
||||
pc = regcache_read_pc (regcache) - gdbarch_decr_pc_after_break (gdbarch);
|
||||
if (breakpoint_inserted_here_p (pc))
|
||||
if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
|
||||
{
|
||||
if (debug_linux_nat)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
|
@ -22,8 +22,10 @@
|
||||
#include "gdbtypes.h"
|
||||
#include "linux-tdep.h"
|
||||
#include "observer.h"
|
||||
|
||||
#include "auxv.h"
|
||||
#include "target.h"
|
||||
#include "elf-bfd.h"
|
||||
#include "elf/common.h"
|
||||
|
||||
/* This function is suitable for architectures that don't
|
||||
extend/override the standard siginfo structure. */
|
||||
@ -139,6 +141,21 @@ linux_get_siginfo_type (struct gdbarch *gdbarch)
|
||||
return siginfo_type;
|
||||
}
|
||||
|
||||
int
|
||||
linux_has_shared_address_space (void)
|
||||
{
|
||||
/* Determine whether we are running on uClinux or normal Linux
|
||||
kernel. */
|
||||
CORE_ADDR dummy;
|
||||
int target_is_uclinux;
|
||||
|
||||
target_is_uclinux
|
||||
= (target_auxv_search (¤t_target, AT_NULL, &dummy) > 0
|
||||
&& target_auxv_search (¤t_target, AT_PAGESZ, &dummy) == 0);
|
||||
|
||||
return target_is_uclinux;
|
||||
}
|
||||
|
||||
/* Observer for the executable_changed event, to check whether the new
|
||||
exec binary is a PIE (Position Independent Executable) specimen, which
|
||||
is currently unsupported. */
|
||||
@ -160,6 +177,9 @@ GDB does NOT currently support. Most debugger features will fail if used\n\
|
||||
in this session.\n"));
|
||||
}
|
||||
|
||||
/* Provide a prototype to silence -Wmissing-prototypes. */
|
||||
extern initialize_file_ftype _initialize_linux_tdep;
|
||||
|
||||
void
|
||||
_initialize_linux_tdep (void)
|
||||
{
|
||||
|
@ -1226,18 +1226,15 @@ thread_db_wait (struct target_ops *ops,
|
||||
|
||||
if (ourstatus->kind == TARGET_WAITKIND_EXECD)
|
||||
{
|
||||
/* Breakpoints have already been marked non-inserted by the
|
||||
layer below. We're safe in knowing that removing them will
|
||||
not write the shadows of the old image into the new
|
||||
image. */
|
||||
remove_thread_event_breakpoints ();
|
||||
|
||||
/* New image, it may or may not end up using thread_db. Assume
|
||||
not unless we find otherwise. */
|
||||
delete_thread_db_info (GET_PID (ptid));
|
||||
if (!thread_db_list)
|
||||
unpush_target (&thread_db_ops);
|
||||
|
||||
/* Thread event breakpoints are deleted by
|
||||
update_breakpoints_after_exec. */
|
||||
|
||||
return ptid;
|
||||
}
|
||||
|
||||
@ -1274,13 +1271,12 @@ thread_db_mourn_inferior (struct target_ops *ops)
|
||||
|
||||
delete_thread_db_info (GET_PID (inferior_ptid));
|
||||
|
||||
/* Delete the old thread event breakpoints. Mark breakpoints out,
|
||||
so that we don't try to un-insert them. */
|
||||
mark_breakpoints_out ();
|
||||
remove_thread_event_breakpoints ();
|
||||
|
||||
target_beneath->to_mourn_inferior (target_beneath);
|
||||
|
||||
/* Delete the old thread event breakpoints. Do this after mourning
|
||||
the inferior, so that we don't try to uninsert them. */
|
||||
remove_thread_event_breakpoints ();
|
||||
|
||||
/* Detach thread_db target ops. */
|
||||
if (!thread_db_list)
|
||||
unpush_target (ops);
|
||||
|
@ -56,7 +56,7 @@ static void mi_on_normal_stop (struct bpstats *bs, int print_frame);
|
||||
|
||||
static void mi_new_thread (struct thread_info *t);
|
||||
static void mi_thread_exit (struct thread_info *t, int silent);
|
||||
static void mi_new_inferior (int pid);
|
||||
static void mi_inferior_appeared (int pid);
|
||||
static void mi_inferior_exit (int pid);
|
||||
static void mi_on_resume (ptid_t ptid);
|
||||
static void mi_solib_loaded (struct so_list *solib);
|
||||
@ -86,7 +86,7 @@ mi_interpreter_init (int top_level)
|
||||
{
|
||||
observer_attach_new_thread (mi_new_thread);
|
||||
observer_attach_thread_exit (mi_thread_exit);
|
||||
observer_attach_new_inferior (mi_new_inferior);
|
||||
observer_attach_inferior_appeared (mi_inferior_appeared);
|
||||
observer_attach_inferior_exit (mi_inferior_exit);
|
||||
observer_attach_normal_stop (mi_on_normal_stop);
|
||||
observer_attach_target_resumed (mi_on_resume);
|
||||
@ -308,12 +308,12 @@ mi_thread_exit (struct thread_info *t, int silent)
|
||||
gdb_flush (mi->event_channel);
|
||||
}
|
||||
|
||||
static void
|
||||
mi_new_inferior (int pid)
|
||||
void
|
||||
mi_inferior_appeared (int pid)
|
||||
{
|
||||
struct mi_interp *mi = top_level_interpreter_data ();
|
||||
target_terminal_ours ();
|
||||
fprintf_unfiltered (mi->event_channel, "thread-group-created,id=\"%d\"",
|
||||
fprintf_unfiltered (mi->event_channel, "thread-group-created,id=\"%d\"",
|
||||
pid);
|
||||
gdb_flush (mi->event_channel);
|
||||
}
|
||||
|
@ -362,13 +362,18 @@ mi_cmd_thread_info (char *command, char **argv, int argc)
|
||||
static int
|
||||
print_one_inferior (struct inferior *inferior, void *arg)
|
||||
{
|
||||
struct cleanup *back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
|
||||
if (inferior->pid != 0)
|
||||
{
|
||||
struct cleanup *back_to
|
||||
= make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
|
||||
|
||||
ui_out_field_fmt (uiout, "id", "%d", inferior->pid);
|
||||
ui_out_field_string (uiout, "type", "process");
|
||||
ui_out_field_int (uiout, "pid", inferior->pid);
|
||||
|
||||
do_cleanups (back_to);
|
||||
}
|
||||
|
||||
ui_out_field_fmt (uiout, "id", "%d", inferior->pid);
|
||||
ui_out_field_string (uiout, "type", "process");
|
||||
ui_out_field_int (uiout, "pid", inferior->pid);
|
||||
|
||||
do_cleanups (back_to);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2383,7 +2383,8 @@ mips_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr)
|
||||
the sequence. */
|
||||
|
||||
static int
|
||||
deal_with_atomic_sequence (struct gdbarch *gdbarch, CORE_ADDR pc)
|
||||
deal_with_atomic_sequence (struct gdbarch *gdbarch,
|
||||
struct address_space *aspace, CORE_ADDR pc)
|
||||
{
|
||||
CORE_ADDR breaks[2] = {-1, -1};
|
||||
CORE_ADDR loc = pc;
|
||||
@ -2471,7 +2472,7 @@ deal_with_atomic_sequence (struct gdbarch *gdbarch, CORE_ADDR pc)
|
||||
|
||||
/* Effectively inserts the breakpoints. */
|
||||
for (index = 0; index <= last_breakpoint; index++)
|
||||
insert_single_step_breakpoint (gdbarch, breaks[index]);
|
||||
insert_single_step_breakpoint (gdbarch, aspace, breaks[index]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -2485,15 +2486,16 @@ int
|
||||
mips_software_single_step (struct frame_info *frame)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
CORE_ADDR pc, next_pc;
|
||||
|
||||
pc = get_frame_pc (frame);
|
||||
if (deal_with_atomic_sequence (gdbarch, pc))
|
||||
if (deal_with_atomic_sequence (gdbarch, aspace, pc))
|
||||
return 1;
|
||||
|
||||
next_pc = mips_next_pc (frame, pc);
|
||||
|
||||
insert_single_step_breakpoint (gdbarch, next_pc);
|
||||
insert_single_step_breakpoint (gdbarch, aspace, next_pc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -4677,7 +4679,7 @@ mips_single_step_through_delay (struct gdbarch *gdbarch,
|
||||
if (mips_pc_is_mips16 (pc))
|
||||
return 0;
|
||||
|
||||
if (!breakpoint_here_p (pc + 4))
|
||||
if (!breakpoint_here_p (get_frame_address_space (frame), pc + 4))
|
||||
return 0;
|
||||
|
||||
if (!safe_frame_unwind_memory (frame, pc, buf, sizeof buf))
|
||||
|
@ -705,6 +705,7 @@ monitor_open (char *args, struct monitor_ops *mon_ops, int from_tty)
|
||||
{
|
||||
char *name;
|
||||
char **p;
|
||||
struct inferior *inf;
|
||||
|
||||
if (mon_ops->magic != MONITOR_OPS_MAGIC)
|
||||
error (_("Magic number of monitor_ops struct wrong."));
|
||||
@ -822,7 +823,8 @@ monitor_open (char *args, struct monitor_ops *mon_ops, int from_tty)
|
||||
|
||||
/* Make run command think we are busy... */
|
||||
inferior_ptid = monitor_ptid;
|
||||
add_inferior_silent (ptid_get_pid (inferior_ptid));
|
||||
inf = current_inferior ();
|
||||
inferior_appeared (inf, ptid_get_pid (inferior_ptid));
|
||||
add_thread_silent (inferior_ptid);
|
||||
|
||||
/* Give monitor_wait something to read */
|
||||
|
@ -640,7 +640,8 @@ procfs_attach (struct target_ops *ops, char *args, int from_tty)
|
||||
gdb_flush (gdb_stdout);
|
||||
}
|
||||
inferior_ptid = do_attach (pid_to_ptid (pid));
|
||||
inf = add_inferior (pid);
|
||||
inf = current_inferior ();
|
||||
inferior_appeared (inf, pid);
|
||||
inf->attach_flag = 1;
|
||||
|
||||
push_target (ops);
|
||||
@ -1196,7 +1197,8 @@ procfs_create_inferior (struct target_ops *ops, char *exec_file,
|
||||
inferior_ptid = do_attach (pid_to_ptid (pid));
|
||||
procfs_find_new_threads (ops);
|
||||
|
||||
inf = add_inferior (pid);
|
||||
inf = current_inferior ();
|
||||
inferior_appeared (inf, pid);
|
||||
inf->attach_flag = 0;
|
||||
|
||||
flags = _DEBUG_FLAG_KLC; /* Kill-on-Last-Close flag. */
|
||||
|
116
gdb/objfiles.c
116
gdb/objfiles.c
@ -61,16 +61,53 @@ static void objfile_free_data (struct objfile *objfile);
|
||||
/* Externally visible variables that are owned by this module.
|
||||
See declarations in objfile.h for more info. */
|
||||
|
||||
struct objfile *object_files; /* Linked list of all objfiles */
|
||||
struct objfile *current_objfile; /* For symbol file being read in */
|
||||
struct objfile *symfile_objfile; /* Main symbol table loaded from */
|
||||
struct objfile *rt_common_objfile; /* For runtime common symbols */
|
||||
|
||||
struct objfile_pspace_info
|
||||
{
|
||||
int objfiles_changed_p;
|
||||
struct obj_section **sections;
|
||||
int num_sections;
|
||||
};
|
||||
|
||||
/* Per-program-space data key. */
|
||||
static const struct program_space_data *objfiles_pspace_data;
|
||||
|
||||
static void
|
||||
objfiles_pspace_data_cleanup (struct program_space *pspace, void *arg)
|
||||
{
|
||||
struct objfile_pspace_info *info;
|
||||
|
||||
info = program_space_data (pspace, objfiles_pspace_data);
|
||||
if (info != NULL)
|
||||
{
|
||||
xfree (info->sections);
|
||||
xfree (info);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the current svr4 data. If none is found yet, add it now. This
|
||||
function always returns a valid object. */
|
||||
|
||||
static struct objfile_pspace_info *
|
||||
get_objfile_pspace_data (struct program_space *pspace)
|
||||
{
|
||||
struct objfile_pspace_info *info;
|
||||
|
||||
info = program_space_data (pspace, objfiles_pspace_data);
|
||||
if (info == NULL)
|
||||
{
|
||||
info = XZALLOC (struct objfile_pspace_info);
|
||||
set_program_space_data (pspace, objfiles_pspace_data, info);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/* Records whether any objfiles appeared or disappeared since we last updated
|
||||
address to obj section map. */
|
||||
|
||||
static int objfiles_changed_p;
|
||||
|
||||
/* Locate all mappable sections of a BFD file.
|
||||
objfile_p_char is a char * to get it through
|
||||
bfd_map_over_sections; we cast it back to its proper type. */
|
||||
@ -208,6 +245,8 @@ allocate_objfile (bfd *abfd, int flags)
|
||||
objfile->name = xstrdup ("<<anonymous objfile>>");
|
||||
}
|
||||
|
||||
objfile->pspace = current_program_space;
|
||||
|
||||
/* Initialize the section indexes for this objfile, so that we can
|
||||
later detect if they are used w/o being properly assigned to. */
|
||||
|
||||
@ -236,9 +275,10 @@ allocate_objfile (bfd *abfd, int flags)
|
||||
/* Save passed in flag bits. */
|
||||
objfile->flags |= flags;
|
||||
|
||||
objfiles_changed_p = 1; /* Rebuild section map next time we need it. */
|
||||
/* Rebuild section map next time we need it. */
|
||||
get_objfile_pspace_data (objfile->pspace)->objfiles_changed_p = 1;
|
||||
|
||||
return (objfile);
|
||||
return objfile;
|
||||
}
|
||||
|
||||
/* Retrieve the gdbarch associated with OBJFILE. */
|
||||
@ -512,9 +552,11 @@ free_objfile (struct objfile *objfile)
|
||||
if (objfile->demangled_names_hash)
|
||||
htab_delete (objfile->demangled_names_hash);
|
||||
obstack_free (&objfile->objfile_obstack, 0);
|
||||
|
||||
/* Rebuild section map next time we need it. */
|
||||
get_objfile_pspace_data (objfile->pspace)->objfiles_changed_p = 1;
|
||||
|
||||
xfree (objfile);
|
||||
objfile = NULL;
|
||||
objfiles_changed_p = 1; /* Rebuild section map next time we need it. */
|
||||
}
|
||||
|
||||
static void
|
||||
@ -686,7 +728,7 @@ objfile_relocate (struct objfile *objfile, struct section_offsets *new_offsets)
|
||||
}
|
||||
|
||||
/* Rebuild section map next time we need it. */
|
||||
objfiles_changed_p = 1;
|
||||
get_objfile_pspace_data (objfile->pspace)->objfiles_changed_p = 1;
|
||||
|
||||
/* Update the table in exec_ops, used to read memory. */
|
||||
ALL_OBJFILE_OSECTIONS (objfile, s)
|
||||
@ -1053,28 +1095,31 @@ filter_overlapping_sections (struct obj_section **map, int map_size)
|
||||
TLS, overlay and overlapping sections. */
|
||||
|
||||
static void
|
||||
update_section_map (struct obj_section ***pmap, int *pmap_size)
|
||||
update_section_map (struct program_space *pspace,
|
||||
struct obj_section ***pmap, int *pmap_size)
|
||||
{
|
||||
int alloc_size, map_size, i;
|
||||
struct obj_section *s, **map;
|
||||
struct objfile *objfile;
|
||||
|
||||
gdb_assert (objfiles_changed_p != 0);
|
||||
gdb_assert (get_objfile_pspace_data (pspace)->objfiles_changed_p != 0);
|
||||
|
||||
map = *pmap;
|
||||
xfree (map);
|
||||
|
||||
alloc_size = 0;
|
||||
ALL_OBJSECTIONS (objfile, s)
|
||||
if (insert_section_p (objfile->obfd, s->the_bfd_section))
|
||||
alloc_size += 1;
|
||||
ALL_PSPACE_OBJFILES (pspace, objfile)
|
||||
ALL_OBJFILE_OSECTIONS (objfile, s)
|
||||
if (insert_section_p (objfile->obfd, s->the_bfd_section))
|
||||
alloc_size += 1;
|
||||
|
||||
map = xmalloc (alloc_size * sizeof (*map));
|
||||
|
||||
i = 0;
|
||||
ALL_OBJSECTIONS (objfile, s)
|
||||
if (insert_section_p (objfile->obfd, s->the_bfd_section))
|
||||
map[i++] = s;
|
||||
ALL_PSPACE_OBJFILES (pspace, objfile)
|
||||
ALL_OBJFILE_OSECTIONS (objfile, s)
|
||||
if (insert_section_p (objfile->obfd, s->the_bfd_section))
|
||||
map[i++] = s;
|
||||
|
||||
qsort (map, alloc_size, sizeof (*map), qsort_cmp);
|
||||
map_size = filter_debuginfo_sections(map, alloc_size);
|
||||
@ -1110,9 +1155,7 @@ bsearch_cmp (const void *key, const void *elt)
|
||||
struct obj_section *
|
||||
find_pc_section (CORE_ADDR pc)
|
||||
{
|
||||
static struct obj_section **sections;
|
||||
static int num_sections;
|
||||
|
||||
struct objfile_pspace_info *pspace_info;
|
||||
struct obj_section *s, **sp;
|
||||
|
||||
/* Check for mapped overlay section first. */
|
||||
@ -1120,17 +1163,23 @@ find_pc_section (CORE_ADDR pc)
|
||||
if (s)
|
||||
return s;
|
||||
|
||||
if (objfiles_changed_p != 0)
|
||||
pspace_info = get_objfile_pspace_data (current_program_space);
|
||||
if (pspace_info->objfiles_changed_p != 0)
|
||||
{
|
||||
update_section_map (§ions, &num_sections);
|
||||
update_section_map (current_program_space,
|
||||
&pspace_info->sections,
|
||||
&pspace_info->num_sections);
|
||||
|
||||
/* Don't need updates to section map until objfiles are added
|
||||
or removed. */
|
||||
objfiles_changed_p = 0;
|
||||
/* Don't need updates to section map until objfiles are added,
|
||||
removed or relocated. */
|
||||
pspace_info->objfiles_changed_p = 0;
|
||||
}
|
||||
|
||||
sp = (struct obj_section **) bsearch (&pc, sections, num_sections,
|
||||
sizeof (*sections), bsearch_cmp);
|
||||
sp = (struct obj_section **) bsearch (&pc,
|
||||
pspace_info->sections,
|
||||
pspace_info->num_sections,
|
||||
sizeof (*pspace_info->sections),
|
||||
bsearch_cmp);
|
||||
if (sp != NULL)
|
||||
return *sp;
|
||||
return NULL;
|
||||
@ -1271,7 +1320,8 @@ objfile_data (struct objfile *objfile, const struct objfile_data *data)
|
||||
void
|
||||
objfiles_changed (void)
|
||||
{
|
||||
objfiles_changed_p = 1; /* Rebuild section map next time we need it. */
|
||||
/* Rebuild section map next time we need it. */
|
||||
get_objfile_pspace_data (current_program_space)->objfiles_changed_p = 1;
|
||||
}
|
||||
|
||||
/* Add reference to ABFD. Returns ABFD. */
|
||||
@ -1322,3 +1372,13 @@ gdb_bfd_unref (struct bfd *abfd)
|
||||
name, bfd_errmsg (bfd_get_error ()));
|
||||
xfree (name);
|
||||
}
|
||||
|
||||
/* Provide a prototype to silence -Wmissing-prototypes. */
|
||||
extern initialize_file_ftype _initialize_objfiles;
|
||||
|
||||
void
|
||||
_initialize_objfiles (void)
|
||||
{
|
||||
objfiles_pspace_data
|
||||
= register_program_space_data_with_cleanup (objfiles_pspace_data_cleanup);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "gdb_obstack.h" /* For obstack internals. */
|
||||
#include "symfile.h" /* For struct psymbol_allocation_list */
|
||||
#include "progspace.h"
|
||||
|
||||
struct bcache;
|
||||
struct htab;
|
||||
@ -200,6 +201,10 @@ struct objfile
|
||||
|
||||
unsigned short flags;
|
||||
|
||||
/* The program space associated with this objfile. */
|
||||
|
||||
struct program_space *pspace;
|
||||
|
||||
/* Each objfile points to a linked list of symtabs derived from this file,
|
||||
one symtab structure for each compilation unit (source file). Each link
|
||||
in the symtab list contains a backpointer to this objfile. */
|
||||
@ -414,11 +419,6 @@ struct objfile
|
||||
|
||||
#define OBJF_USERLOADED (1 << 3) /* User loaded */
|
||||
|
||||
/* The object file that the main symbol table was loaded from (e.g. the
|
||||
argument to the "symbol-file" or "file" command). */
|
||||
|
||||
extern struct objfile *symfile_objfile;
|
||||
|
||||
/* The object file that contains the runtime common minimal symbols
|
||||
for SunOS4. Note that this objfile has no associated BFD. */
|
||||
|
||||
@ -439,11 +439,6 @@ extern struct objfile *rt_common_objfile;
|
||||
|
||||
extern struct objfile *current_objfile;
|
||||
|
||||
/* All known objfiles are kept in a linked list. This points to the
|
||||
root of this list. */
|
||||
|
||||
extern struct objfile *object_files;
|
||||
|
||||
/* Declarations for functions defined in objfiles.c */
|
||||
|
||||
extern struct objfile *allocate_objfile (bfd *, int);
|
||||
@ -524,14 +519,27 @@ extern struct bfd *gdb_bfd_ref (struct bfd *abfd);
|
||||
extern void gdb_bfd_unref (struct bfd *abfd);
|
||||
|
||||
|
||||
/* Traverse all object files. ALL_OBJFILES_SAFE works even if you delete
|
||||
the objfile during the traversal. */
|
||||
/* Traverse all object files in the current program space.
|
||||
ALL_OBJFILES_SAFE works even if you delete the objfile during the
|
||||
traversal. */
|
||||
|
||||
#define ALL_OBJFILES(obj) \
|
||||
for ((obj) = object_files; (obj) != NULL; (obj) = (obj)->next)
|
||||
/* Traverse all object files in program space SS. */
|
||||
|
||||
#define ALL_OBJFILES_SAFE(obj,nxt) \
|
||||
for ((obj) = object_files; \
|
||||
#define ALL_PSPACE_OBJFILES(ss, obj) \
|
||||
for ((obj) = ss->objfiles; (obj) != NULL; (obj) = (obj)->next) \
|
||||
|
||||
#define ALL_PSPACE_OBJFILES_SAFE(ss, obj, nxt) \
|
||||
for ((obj) = ss->objfiles; \
|
||||
(obj) != NULL? ((nxt)=(obj)->next,1) :0; \
|
||||
(obj) = (nxt))
|
||||
|
||||
#define ALL_OBJFILES(obj) \
|
||||
for ((obj) = current_program_space->objfiles; \
|
||||
(obj) != NULL; \
|
||||
(obj) = (obj)->next)
|
||||
|
||||
#define ALL_OBJFILES_SAFE(obj,nxt) \
|
||||
for ((obj) = current_program_space->objfiles; \
|
||||
(obj) != NULL? ((nxt)=(obj)->next,1) :0; \
|
||||
(obj) = (nxt))
|
||||
|
||||
@ -550,27 +558,44 @@ extern void gdb_bfd_unref (struct bfd *abfd);
|
||||
#define ALL_OBJFILE_MSYMBOLS(objfile, m) \
|
||||
for ((m) = (objfile) -> msymbols; SYMBOL_LINKAGE_NAME(m) != NULL; (m)++)
|
||||
|
||||
/* Traverse all symtabs in all objfiles. */
|
||||
/* Traverse all symtabs in all objfiles in the current symbol
|
||||
space. */
|
||||
|
||||
#define ALL_SYMTABS(objfile, s) \
|
||||
ALL_OBJFILES (objfile) \
|
||||
ALL_OBJFILE_SYMTABS (objfile, s)
|
||||
|
||||
/* Traverse all symtabs in all objfiles, skipping included files
|
||||
(which share a blockvector with their primary symtab). */
|
||||
#define ALL_PSPACE_SYMTABS(ss, objfile, s) \
|
||||
ALL_PSPACE_OBJFILES (ss, objfile) \
|
||||
ALL_OBJFILE_SYMTABS (objfile, s)
|
||||
|
||||
/* Traverse all symtabs in all objfiles in the current program space,
|
||||
skipping included files (which share a blockvector with their
|
||||
primary symtab). */
|
||||
|
||||
#define ALL_PRIMARY_SYMTABS(objfile, s) \
|
||||
ALL_OBJFILES (objfile) \
|
||||
ALL_OBJFILE_SYMTABS (objfile, s) \
|
||||
if ((s)->primary)
|
||||
|
||||
/* Traverse all psymtabs in all objfiles. */
|
||||
#define ALL_PSPACE_PRIMARY_SYMTABS(pspace, objfile, s) \
|
||||
ALL_PSPACE_OBJFILES (ss, objfile) \
|
||||
ALL_OBJFILE_SYMTABS (objfile, s) \
|
||||
if ((s)->primary)
|
||||
|
||||
/* Traverse all psymtabs in all objfiles in the current symbol
|
||||
space. */
|
||||
|
||||
#define ALL_PSYMTABS(objfile, p) \
|
||||
ALL_OBJFILES (objfile) \
|
||||
ALL_OBJFILE_PSYMTABS (objfile, p)
|
||||
|
||||
/* Traverse all minimal symbols in all objfiles. */
|
||||
#define ALL_PSPACE_PSYMTABS(ss, objfile, p) \
|
||||
ALL_PSPACE_OBJFILES (ss, objfile) \
|
||||
ALL_OBJFILE_PSYMTABS (objfile, p)
|
||||
|
||||
/* Traverse all minimal symbols in all objfiles in the current symbol
|
||||
space. */
|
||||
|
||||
#define ALL_MSYMBOLS(objfile, m) \
|
||||
ALL_OBJFILES (objfile) \
|
||||
|
@ -135,16 +135,25 @@ struct display
|
||||
{
|
||||
/* Chain link to next auto-display item. */
|
||||
struct display *next;
|
||||
|
||||
/* The expression as the user typed it. */
|
||||
char *exp_string;
|
||||
|
||||
/* Expression to be evaluated and displayed. */
|
||||
struct expression *exp;
|
||||
|
||||
/* Item number of this auto-display item. */
|
||||
int number;
|
||||
|
||||
/* Display format specified. */
|
||||
struct format_data format;
|
||||
|
||||
/* Program space associated with `block'. */
|
||||
struct program_space *pspace;
|
||||
|
||||
/* Innermost block required by this expression when evaluated */
|
||||
struct block *block;
|
||||
|
||||
/* Status of this display (enabled or disabled) */
|
||||
int enabled_p;
|
||||
};
|
||||
@ -1449,6 +1458,7 @@ display_command (char *exp, int from_tty)
|
||||
new->exp_string = xstrdup (exp);
|
||||
new->exp = expr;
|
||||
new->block = innermost_block;
|
||||
new->pspace = current_program_space;
|
||||
new->next = display_chain;
|
||||
new->number = ++display_number;
|
||||
new->format = fmt;
|
||||
@ -1585,7 +1595,12 @@ do_one_display (struct display *d)
|
||||
}
|
||||
|
||||
if (d->block)
|
||||
within_current_scope = contained_in (get_selected_block (0), d->block);
|
||||
{
|
||||
if (d->pspace == current_program_space)
|
||||
within_current_scope = contained_in (get_selected_block (0), d->block);
|
||||
else
|
||||
within_current_scope = 0;
|
||||
}
|
||||
else
|
||||
within_current_scope = 1;
|
||||
if (!within_current_scope)
|
||||
@ -1810,6 +1825,7 @@ display_uses_solib_p (const struct display *d,
|
||||
const union exp_element *const elts = exp->elts;
|
||||
|
||||
if (d->block != NULL
|
||||
&& d->pspace == solib->pspace
|
||||
&& solib_contains_address_p (solib, d->block->startaddr))
|
||||
return 1;
|
||||
|
||||
@ -1830,7 +1846,8 @@ display_uses_solib_p (const struct display *d,
|
||||
SYMBOL_OBJ_SECTION (symbol);
|
||||
|
||||
if (block != NULL
|
||||
&& solib_contains_address_p (solib, block->startaddr))
|
||||
&& solib_contains_address_p (solib,
|
||||
block->startaddr))
|
||||
return 1;
|
||||
|
||||
if (section && section->objfile == solib->objfile)
|
||||
|
@ -3705,7 +3705,8 @@ do_attach (ptid_t ptid)
|
||||
if ((fail = procfs_debug_inferior (pi)) != 0)
|
||||
dead_procinfo (pi, "do_attach: failed in procfs_debug_inferior", NOKILL);
|
||||
|
||||
inf = add_inferior (pi->pid);
|
||||
inf = current_inferior ();
|
||||
inferior_appeared (inf, pi->pid);
|
||||
/* Let GDB know that the inferior was attached. */
|
||||
inf->attach_flag = 1;
|
||||
|
||||
|
625
gdb/progspace.c
Normal file
625
gdb/progspace.c
Normal file
@ -0,0 +1,625 @@
|
||||
/* Program and address space management, for GDB, the GNU debugger.
|
||||
|
||||
Copyright (C) 2009 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 "defs.h"
|
||||
#include "gdbcmd.h"
|
||||
#include "objfiles.h"
|
||||
#include "arch-utils.h"
|
||||
#include "gdbcore.h"
|
||||
#include "solib.h"
|
||||
#include "gdbthread.h"
|
||||
|
||||
/* The last program space number assigned. */
|
||||
int last_program_space_num = 0;
|
||||
|
||||
/* The head of the program spaces list. */
|
||||
struct program_space *program_spaces;
|
||||
|
||||
/* Pointer to the current program space. */
|
||||
struct program_space *current_program_space;
|
||||
|
||||
/* The last address space number assigned. */
|
||||
static int highest_address_space_num;
|
||||
|
||||
/* Prototypes for local functions */
|
||||
|
||||
static void program_space_alloc_data (struct program_space *);
|
||||
static void program_space_free_data (struct program_space *);
|
||||
|
||||
|
||||
/* An address space. Currently this is not used for much other than
|
||||
for comparing if pspaces/inferior/threads see the same address
|
||||
space. */
|
||||
|
||||
struct address_space
|
||||
{
|
||||
int num;
|
||||
};
|
||||
|
||||
/* Create a new address space object, and add it to the list. */
|
||||
|
||||
struct address_space *
|
||||
new_address_space (void)
|
||||
{
|
||||
struct address_space *aspace;
|
||||
|
||||
aspace = XZALLOC (struct address_space);
|
||||
aspace->num = ++highest_address_space_num;
|
||||
|
||||
return aspace;
|
||||
}
|
||||
|
||||
/* Maybe create a new address space object, and add it to the list, or
|
||||
return a pointer to an existing address space, in case inferiors
|
||||
share an address space on this target system. */
|
||||
|
||||
struct address_space *
|
||||
maybe_new_address_space (void)
|
||||
{
|
||||
int shared_aspace = gdbarch_has_shared_address_space (target_gdbarch);
|
||||
|
||||
if (shared_aspace)
|
||||
{
|
||||
/* Just return the first in the list. */
|
||||
return program_spaces->aspace;
|
||||
}
|
||||
|
||||
return new_address_space ();
|
||||
}
|
||||
|
||||
static void
|
||||
free_address_space (struct address_space *aspace)
|
||||
{
|
||||
xfree (aspace);
|
||||
}
|
||||
|
||||
/* Start counting over from scratch. */
|
||||
|
||||
static void
|
||||
init_address_spaces (void)
|
||||
{
|
||||
highest_address_space_num = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Adds a new empty program space to the program space list, and binds
|
||||
it to ASPACE. Returns the pointer to the new object. */
|
||||
|
||||
struct program_space *
|
||||
add_program_space (struct address_space *aspace)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
|
||||
pspace = XZALLOC (struct program_space);
|
||||
|
||||
pspace->num = ++last_program_space_num;
|
||||
pspace->aspace = aspace;
|
||||
|
||||
program_space_alloc_data (pspace);
|
||||
|
||||
pspace->next = program_spaces;
|
||||
program_spaces = pspace;
|
||||
|
||||
return pspace;
|
||||
}
|
||||
|
||||
/* Releases program space PSPACE, and all its contents (shared
|
||||
libraries, objfiles, and any other references to the PSPACE in
|
||||
other modules). It is an internal error to call this when PSPACE
|
||||
is the current program space, since there should always be a
|
||||
program space. */
|
||||
|
||||
static void
|
||||
release_program_space (struct program_space *pspace)
|
||||
{
|
||||
struct cleanup *old_chain = save_current_program_space ();
|
||||
|
||||
gdb_assert (pspace != current_program_space);
|
||||
|
||||
set_current_program_space (pspace);
|
||||
|
||||
breakpoint_program_space_exit (pspace);
|
||||
no_shared_libraries (NULL, 0);
|
||||
exec_close ();
|
||||
free_all_objfiles ();
|
||||
if (!gdbarch_has_shared_address_space (target_gdbarch))
|
||||
free_address_space (pspace->aspace);
|
||||
resize_section_table (&pspace->target_sections,
|
||||
-resize_section_table (&pspace->target_sections, 0));
|
||||
/* Discard any data modules have associated with the PSPACE. */
|
||||
program_space_free_data (pspace);
|
||||
xfree (pspace);
|
||||
|
||||
do_cleanups (old_chain);
|
||||
}
|
||||
|
||||
/* Unlinks PSPACE from the pspace list, and releases it. */
|
||||
|
||||
void
|
||||
remove_program_space (struct program_space *pspace)
|
||||
{
|
||||
struct program_space *ss, **ss_link;
|
||||
|
||||
ss = program_spaces;
|
||||
ss_link = &program_spaces;
|
||||
while (ss)
|
||||
{
|
||||
if (ss != pspace)
|
||||
{
|
||||
ss_link = &ss->next;
|
||||
ss = *ss_link;
|
||||
continue;
|
||||
}
|
||||
|
||||
*ss_link = ss->next;
|
||||
release_program_space (ss);
|
||||
ss = *ss_link;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copies program space SRC to DEST. Copies the main executable file,
|
||||
and the main symbol file. Returns DEST. */
|
||||
|
||||
struct program_space *
|
||||
clone_program_space (struct program_space *dest, struct program_space *src)
|
||||
{
|
||||
struct program_space *new_pspace;
|
||||
struct cleanup *old_chain;
|
||||
|
||||
old_chain = save_current_program_space ();
|
||||
|
||||
set_current_program_space (dest);
|
||||
|
||||
if (src->ebfd != NULL)
|
||||
exec_file_attach (bfd_get_filename (src->ebfd), 0);
|
||||
|
||||
if (src->symfile_object_file != NULL)
|
||||
symbol_file_add_main (src->symfile_object_file->name, 0);
|
||||
|
||||
do_cleanups (old_chain);
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* Sets PSPACE as the current program space. It is the caller's
|
||||
responsibility to make sure that the currently selected
|
||||
inferior/thread matches the selected program space. */
|
||||
|
||||
void
|
||||
set_current_program_space (struct program_space *pspace)
|
||||
{
|
||||
if (current_program_space == pspace)
|
||||
return;
|
||||
|
||||
gdb_assert (pspace != NULL);
|
||||
|
||||
current_program_space = pspace;
|
||||
|
||||
/* Different symbols change our view of the frame chain. */
|
||||
reinit_frame_cache ();
|
||||
}
|
||||
|
||||
/* A cleanups callback, helper for save_current_program_space
|
||||
below. */
|
||||
|
||||
static void
|
||||
restore_program_space (void *arg)
|
||||
{
|
||||
struct program_space *saved_pspace = arg;
|
||||
set_current_program_space (saved_pspace);
|
||||
}
|
||||
|
||||
/* Save the current program space so that it may be restored by a later
|
||||
call to do_cleanups. Returns the struct cleanup pointer needed for
|
||||
later doing the cleanup. */
|
||||
|
||||
struct cleanup *
|
||||
save_current_program_space (void)
|
||||
{
|
||||
struct cleanup *old_chain = make_cleanup (restore_program_space,
|
||||
current_program_space);
|
||||
return old_chain;
|
||||
}
|
||||
|
||||
/* Find program space number NUM; returns NULL if not found. */
|
||||
|
||||
static struct program_space *
|
||||
find_program_space_by_num (int num)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
|
||||
ALL_PSPACES (pspace)
|
||||
if (pspace->num == num)
|
||||
return pspace;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns true iff there's no inferior bound to PSPACE. */
|
||||
|
||||
static int
|
||||
pspace_empty_p (struct program_space *pspace)
|
||||
{
|
||||
struct inferior *inf;
|
||||
|
||||
if (find_inferior_for_program_space (pspace) != NULL)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Prune away automatically added program spaces that aren't required
|
||||
anymore. */
|
||||
|
||||
void
|
||||
prune_program_spaces (void)
|
||||
{
|
||||
struct program_space *ss, **ss_link;
|
||||
struct program_space *current = current_program_space;
|
||||
|
||||
ss = program_spaces;
|
||||
ss_link = &program_spaces;
|
||||
while (ss)
|
||||
{
|
||||
if (ss == current || !pspace_empty_p (ss))
|
||||
{
|
||||
ss_link = &ss->next;
|
||||
ss = *ss_link;
|
||||
continue;
|
||||
}
|
||||
|
||||
*ss_link = ss->next;
|
||||
release_program_space (ss);
|
||||
ss = *ss_link;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prints the list of program spaces and their details on UIOUT. If
|
||||
REQUESTED is not -1, it's the ID of the pspace that should be
|
||||
printed. Otherwise, all spaces are printed. */
|
||||
|
||||
static void
|
||||
print_program_space (struct ui_out *uiout, int requested)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
int count = 0;
|
||||
struct cleanup *old_chain;
|
||||
|
||||
/* Might as well prune away unneeded ones, so the user doesn't even
|
||||
seem them. */
|
||||
prune_program_spaces ();
|
||||
|
||||
/* Compute number of pspaces we will print. */
|
||||
ALL_PSPACES (pspace)
|
||||
{
|
||||
if (requested != -1 && pspace->num != requested)
|
||||
continue;
|
||||
|
||||
++count;
|
||||
}
|
||||
|
||||
/* There should always be at least one. */
|
||||
gdb_assert (count > 0);
|
||||
|
||||
old_chain = make_cleanup_ui_out_table_begin_end (uiout, 3, count, "pspaces");
|
||||
ui_out_table_header (uiout, 1, ui_left, "current", "");
|
||||
ui_out_table_header (uiout, 4, ui_left, "id", "Id");
|
||||
ui_out_table_header (uiout, 17, ui_left, "exec", "Executable");
|
||||
ui_out_table_body (uiout);
|
||||
|
||||
ALL_PSPACES (pspace)
|
||||
{
|
||||
struct cleanup *chain2;
|
||||
struct inferior *inf;
|
||||
int printed_header;
|
||||
|
||||
if (requested != -1 && requested != pspace->num)
|
||||
continue;
|
||||
|
||||
chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
|
||||
|
||||
if (pspace == current_program_space)
|
||||
ui_out_field_string (uiout, "current", "*");
|
||||
else
|
||||
ui_out_field_skip (uiout, "current");
|
||||
|
||||
ui_out_field_int (uiout, "id", pspace->num);
|
||||
|
||||
if (pspace->ebfd)
|
||||
ui_out_field_string (uiout, "exec",
|
||||
bfd_get_filename (pspace->ebfd));
|
||||
else
|
||||
ui_out_field_skip (uiout, "exec");
|
||||
|
||||
/* Print extra info that doesn't really fit in tabular form.
|
||||
Currently, we print the list of inferiors bound to a pspace.
|
||||
There can be more than one inferior bound to the same pspace,
|
||||
e.g., both parent/child inferiors in a vfork, or, on targets
|
||||
that share pspaces between inferiors. */
|
||||
printed_header = 0;
|
||||
for (inf = inferior_list; inf; inf = inf->next)
|
||||
if (inf->pspace == pspace)
|
||||
{
|
||||
if (!printed_header)
|
||||
{
|
||||
printed_header = 1;
|
||||
printf_filtered ("\n\tBound inferiors: ID %d (%s)",
|
||||
inf->num,
|
||||
target_pid_to_str (pid_to_ptid (inf->pid)));
|
||||
}
|
||||
else
|
||||
printf_filtered (", ID %d (%s)",
|
||||
inf->num,
|
||||
target_pid_to_str (pid_to_ptid (inf->pid)));
|
||||
}
|
||||
|
||||
ui_out_text (uiout, "\n");
|
||||
do_cleanups (chain2);
|
||||
}
|
||||
|
||||
do_cleanups (old_chain);
|
||||
}
|
||||
|
||||
/* Boolean test for an already-known program space id. */
|
||||
|
||||
static int
|
||||
valid_program_space_id (int num)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
|
||||
ALL_PSPACES (pspace)
|
||||
if (pspace->num == num)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If ARGS is NULL or empty, print information about all program
|
||||
spaces. Otherwise, ARGS is a text representation of a LONG
|
||||
indicating which the program space to print information about. */
|
||||
|
||||
static void
|
||||
maintenance_info_program_spaces_command (char *args, int from_tty)
|
||||
{
|
||||
int requested = -1;
|
||||
|
||||
if (args && *args)
|
||||
{
|
||||
requested = parse_and_eval_long (args);
|
||||
if (!valid_program_space_id (requested))
|
||||
error (_("program space ID %d not known."), requested);
|
||||
}
|
||||
|
||||
print_program_space (uiout, requested);
|
||||
}
|
||||
|
||||
/* Simply returns the count of program spaces. */
|
||||
|
||||
int
|
||||
number_of_program_spaces (void)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
int count = 0;
|
||||
|
||||
ALL_PSPACES (pspace)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Update all program spaces matching to address spaces. The user may
|
||||
have created several program spaces, and loaded executables into
|
||||
them before connecting to the target interface that will create the
|
||||
inferiors. All that happens before GDB has a chance to know if the
|
||||
inferiors will share an address space or not. Call this after
|
||||
having connected to the target interface and having fetched the
|
||||
target description, to fixup the program/address spaces mappings.
|
||||
|
||||
It is assumed that there are no bound inferiors yet, otherwise,
|
||||
they'd be left with stale referenced to released aspaces. */
|
||||
|
||||
void
|
||||
update_address_spaces (void)
|
||||
{
|
||||
int shared_aspace = gdbarch_has_shared_address_space (target_gdbarch);
|
||||
struct address_space *aspace = NULL;
|
||||
struct program_space *pspace;
|
||||
|
||||
init_address_spaces ();
|
||||
|
||||
ALL_PSPACES (pspace)
|
||||
{
|
||||
free_address_space (pspace->aspace);
|
||||
|
||||
if (shared_aspace)
|
||||
{
|
||||
if (aspace == NULL)
|
||||
aspace = new_address_space ();
|
||||
pspace->aspace = aspace;
|
||||
}
|
||||
else
|
||||
pspace->aspace = new_address_space ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the current program space so that it may be restored by a later
|
||||
call to do_cleanups. Returns the struct cleanup pointer needed for
|
||||
later doing the cleanup. */
|
||||
|
||||
struct cleanup *
|
||||
save_current_space_and_thread (void)
|
||||
{
|
||||
struct cleanup *old_chain;
|
||||
|
||||
/* If restoring to null thread, we need to restore the pspace as
|
||||
well, hence, we need to save the current program space first. */
|
||||
old_chain = save_current_program_space ();
|
||||
save_current_inferior ();
|
||||
make_cleanup_restore_current_thread ();
|
||||
|
||||
return old_chain;
|
||||
}
|
||||
|
||||
/* Switches full context to program space PSPACE. Switches to the
|
||||
first thread found bound to PSPACE. */
|
||||
|
||||
void
|
||||
switch_to_program_space_and_thread (struct program_space *pspace)
|
||||
{
|
||||
struct inferior *inf;
|
||||
|
||||
inf = find_inferior_for_program_space (pspace);
|
||||
if (inf != NULL)
|
||||
{
|
||||
struct thread_info *tp;
|
||||
|
||||
tp = any_live_thread_of_process (inf->pid);
|
||||
if (tp != NULL)
|
||||
{
|
||||
switch_to_thread (tp->ptid);
|
||||
/* Switching thread switches pspace implicitly. We're
|
||||
done. */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch_to_thread (null_ptid);
|
||||
set_current_program_space (pspace);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Keep a registry of per-program_space data-pointers required by other GDB
|
||||
modules. */
|
||||
|
||||
struct program_space_data
|
||||
{
|
||||
unsigned index;
|
||||
void (*cleanup) (struct program_space *, void *);
|
||||
};
|
||||
|
||||
struct program_space_data_registration
|
||||
{
|
||||
struct program_space_data *data;
|
||||
struct program_space_data_registration *next;
|
||||
};
|
||||
|
||||
struct program_space_data_registry
|
||||
{
|
||||
struct program_space_data_registration *registrations;
|
||||
unsigned num_registrations;
|
||||
};
|
||||
|
||||
static struct program_space_data_registry program_space_data_registry
|
||||
= { NULL, 0 };
|
||||
|
||||
const struct program_space_data *
|
||||
register_program_space_data_with_cleanup
|
||||
(void (*cleanup) (struct program_space *, void *))
|
||||
{
|
||||
struct program_space_data_registration **curr;
|
||||
|
||||
/* Append new registration. */
|
||||
for (curr = &program_space_data_registry.registrations;
|
||||
*curr != NULL; curr = &(*curr)->next);
|
||||
|
||||
*curr = XMALLOC (struct program_space_data_registration);
|
||||
(*curr)->next = NULL;
|
||||
(*curr)->data = XMALLOC (struct program_space_data);
|
||||
(*curr)->data->index = program_space_data_registry.num_registrations++;
|
||||
(*curr)->data->cleanup = cleanup;
|
||||
|
||||
return (*curr)->data;
|
||||
}
|
||||
|
||||
const struct program_space_data *
|
||||
register_program_space_data (void)
|
||||
{
|
||||
return register_program_space_data_with_cleanup (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
program_space_alloc_data (struct program_space *pspace)
|
||||
{
|
||||
gdb_assert (pspace->data == NULL);
|
||||
pspace->num_data = program_space_data_registry.num_registrations;
|
||||
pspace->data = XCALLOC (pspace->num_data, void *);
|
||||
}
|
||||
|
||||
static void
|
||||
program_space_free_data (struct program_space *pspace)
|
||||
{
|
||||
gdb_assert (pspace->data != NULL);
|
||||
clear_program_space_data (pspace);
|
||||
xfree (pspace->data);
|
||||
pspace->data = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
clear_program_space_data (struct program_space *pspace)
|
||||
{
|
||||
struct program_space_data_registration *registration;
|
||||
int i;
|
||||
|
||||
gdb_assert (pspace->data != NULL);
|
||||
|
||||
for (registration = program_space_data_registry.registrations, i = 0;
|
||||
i < pspace->num_data;
|
||||
registration = registration->next, i++)
|
||||
if (pspace->data[i] != NULL && registration->data->cleanup)
|
||||
registration->data->cleanup (pspace, pspace->data[i]);
|
||||
|
||||
memset (pspace->data, 0, pspace->num_data * sizeof (void *));
|
||||
}
|
||||
|
||||
void
|
||||
set_program_space_data (struct program_space *pspace,
|
||||
const struct program_space_data *data,
|
||||
void *value)
|
||||
{
|
||||
gdb_assert (data->index < pspace->num_data);
|
||||
pspace->data[data->index] = value;
|
||||
}
|
||||
|
||||
void *
|
||||
program_space_data (struct program_space *pspace, const struct program_space_data *data)
|
||||
{
|
||||
gdb_assert (data->index < pspace->num_data);
|
||||
return pspace->data[data->index];
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
initialize_progspace (void)
|
||||
{
|
||||
add_cmd ("program-spaces", class_maintenance,
|
||||
maintenance_info_program_spaces_command, _("\
|
||||
Info about currently known program spaces."),
|
||||
&maintenanceinfolist);
|
||||
|
||||
/* There's always one program space. Note that this function isn't
|
||||
an automatic _initialize_foo function, since other
|
||||
_initialize_foo routines may need to install their per-pspace
|
||||
data keys. We can only allocate a progspace when all those
|
||||
modules have done that. Do this before
|
||||
initialize_current_architecture, because that accesses exec_bfd,
|
||||
which in turn dereferences current_program_space. */
|
||||
current_program_space = add_program_space (new_address_space ());
|
||||
}
|
283
gdb/progspace.h
Normal file
283
gdb/progspace.h
Normal file
@ -0,0 +1,283 @@
|
||||
/* Program and address space management, for GDB, the GNU debugger.
|
||||
|
||||
Copyright (C) 2009 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/>. */
|
||||
|
||||
|
||||
#ifndef PROGSPACE_H
|
||||
#define PROGSPACE_H
|
||||
|
||||
#include "target.h"
|
||||
#include "vec.h"
|
||||
|
||||
struct target_ops;
|
||||
struct bfd;
|
||||
struct objfile;
|
||||
struct inferior;
|
||||
struct exec;
|
||||
struct address_space;
|
||||
struct program_space_data;
|
||||
|
||||
/* A program space represents a symbolic view of an address space.
|
||||
Roughly speaking, it holds all the data associated with a
|
||||
non-running-yet program (main executable, main symbols), and when
|
||||
an inferior is running and is bound to it, includes the list of its
|
||||
mapped in shared libraries.
|
||||
|
||||
In the traditional debugging scenario, there's a 1-1 correspondence
|
||||
among program spaces, inferiors and address spaces, like so:
|
||||
|
||||
pspace1 (prog1) <--> inf1(pid1) <--> aspace1
|
||||
|
||||
In the case of debugging more than one traditional unix process or
|
||||
program, we still have:
|
||||
|
||||
|-----------------+------------+---------|
|
||||
| pspace1 (prog1) | inf1(pid1) | aspace1 |
|
||||
|----------------------------------------|
|
||||
| pspace2 (prog1) | no inf yet | aspace2 |
|
||||
|-----------------+------------+---------|
|
||||
| pspace3 (prog2) | inf2(pid2) | aspace3 |
|
||||
|-----------------+------------+---------|
|
||||
|
||||
In the former example, if inf1 forks (and GDB stays attached to
|
||||
both processes), the new child will have its own program and
|
||||
address spaces. Like so:
|
||||
|
||||
|-----------------+------------+---------|
|
||||
| pspace1 (prog1) | inf1(pid1) | aspace1 |
|
||||
|-----------------+------------+---------|
|
||||
| pspace2 (prog1) | inf2(pid2) | aspace2 |
|
||||
|-----------------+------------+---------|
|
||||
|
||||
However, had inf1 from the latter case vforked instead, it would
|
||||
share the program and address spaces with its parent, until it
|
||||
execs or exits, like so:
|
||||
|
||||
|-----------------+------------+---------|
|
||||
| pspace1 (prog1) | inf1(pid1) | aspace1 |
|
||||
| | inf2(pid2) | |
|
||||
|-----------------+------------+---------|
|
||||
|
||||
When the vfork child execs, it is finally given new program and
|
||||
address spaces.
|
||||
|
||||
|-----------------+------------+---------|
|
||||
| pspace1 (prog1) | inf1(pid1) | aspace1 |
|
||||
|-----------------+------------+---------|
|
||||
| pspace2 (prog1) | inf2(pid2) | aspace2 |
|
||||
|-----------------+------------+---------|
|
||||
|
||||
There are targets where the OS (if any) doesn't provide memory
|
||||
management or VM protection, where all inferiors share the same
|
||||
address space --- e.g. uClinux. GDB models this by having all
|
||||
inferiors share the same address space, but, giving each its own
|
||||
program space, like so:
|
||||
|
||||
|-----------------+------------+---------|
|
||||
| pspace1 (prog1) | inf1(pid1) | |
|
||||
|-----------------+------------+ |
|
||||
| pspace2 (prog1) | inf2(pid2) | aspace1 |
|
||||
|-----------------+------------+ |
|
||||
| pspace3 (prog2) | inf3(pid3) | |
|
||||
|-----------------+------------+---------|
|
||||
|
||||
The address space sharing matters for run control and breakpoints
|
||||
management. E.g., did we just hit a known breakpoint that we need
|
||||
to step over? Is this breakpoint a duplicate of this other one, or
|
||||
do I need to insert a trap?
|
||||
|
||||
Then, there are targets where all symbols look the same for all
|
||||
inferiors, although each has its own address space, as e.g.,
|
||||
Ericsson DICOS. In such case, the model is:
|
||||
|
||||
|---------+------------+---------|
|
||||
| | inf1(pid1) | aspace1 |
|
||||
| +------------+---------|
|
||||
| pspace | inf2(pid2) | aspace2 |
|
||||
| +------------+---------|
|
||||
| | inf3(pid3) | aspace3 |
|
||||
|---------+------------+---------|
|
||||
|
||||
Note however, that the DICOS debug API takes care of making GDB
|
||||
believe that breakpoints are "global". That is, although each
|
||||
process does have its own private copy of data symbols (just like a
|
||||
bunch of forks), to the breakpoints module, all processes share a
|
||||
single address space, so all breakpoints set at the same address
|
||||
are duplicates of each other, even breakpoints set in the data
|
||||
space (e.g., call dummy breakpoints placed on stack). This allows
|
||||
a simplification in the spaces implementation: we avoid caring for
|
||||
a many-many links between address and program spaces. Either
|
||||
there's a single address space bound to the program space
|
||||
(traditional unix/uClinux), or, in the DICOS case, the address
|
||||
space bound to the program space is mostly ignored. */
|
||||
|
||||
/* The program space structure. */
|
||||
|
||||
struct program_space
|
||||
{
|
||||
/* Pointer to next in linked list. */
|
||||
struct program_space *next;
|
||||
|
||||
/* Unique ID number. */
|
||||
int num;
|
||||
|
||||
/* The main executable loaded into this program space. This is
|
||||
managed by the exec target. */
|
||||
|
||||
/* The BFD handle for the main executable. */
|
||||
bfd *ebfd;
|
||||
/* The last-modified time, from when the exec was brought in. */
|
||||
long ebfd_mtime;
|
||||
|
||||
/* The address space attached to this program space. More than one
|
||||
program space may be bound to the same address space. In the
|
||||
traditional unix-like debugging scenario, this will usually
|
||||
match the address space bound to the inferior, and is mostly
|
||||
used by the breakpoints module for address matches. If the
|
||||
target shares a program space for all inferiors and breakpoints
|
||||
are global, then this field is ignored (we don't currently
|
||||
support inferiors sharing a program space if the target doesn't
|
||||
make breakpoints global). */
|
||||
struct address_space *aspace;
|
||||
|
||||
/* True if this program space's section offsets don't yet represent
|
||||
the final offsets of the "live" address space (that is, the
|
||||
section addresses still require the relocation offsets to be
|
||||
applied, and hence we can't trust the section addresses for
|
||||
anything that pokes at live memory). E.g., for qOffsets
|
||||
targets, or for PIE executables, until we connect and ask the
|
||||
target for the final relocation offsets, the symbols we've used
|
||||
to set breakpoints point at the wrong addresses. */
|
||||
int executing_startup;
|
||||
|
||||
/* The object file that the main symbol table was loaded from
|
||||
(e.g. the argument to the "symbol-file" or "file" command). */
|
||||
struct objfile *symfile_object_file;
|
||||
|
||||
/* All known objfiles are kept in a linked list. This points to
|
||||
the head of this list. */
|
||||
struct objfile *objfiles;
|
||||
|
||||
/* The set of target sections matching the sections mapped into
|
||||
this program space. Managed by both exec_ops and solib.c. */
|
||||
struct target_section_table target_sections;
|
||||
|
||||
/* List of shared objects mapped into this space. Managed by
|
||||
solib.c. */
|
||||
struct so_list *so_list;
|
||||
|
||||
/* Per pspace data-pointers required by other GDB modules. */
|
||||
void **data;
|
||||
unsigned num_data;
|
||||
};
|
||||
|
||||
/* The object file that the main symbol table was loaded from (e.g. the
|
||||
argument to the "symbol-file" or "file" command). */
|
||||
|
||||
#define symfile_objfile current_program_space->symfile_object_file
|
||||
|
||||
/* All known objfiles are kept in a linked list. This points to the
|
||||
root of this list. */
|
||||
#define object_files current_program_space->objfiles
|
||||
|
||||
/* The set of target sections matching the sections mapped into the
|
||||
current program space. */
|
||||
#define current_target_sections (¤t_program_space->target_sections)
|
||||
|
||||
/* The list of all program spaces. There's always at least one. */
|
||||
extern struct program_space *program_spaces;
|
||||
|
||||
/* The current program space. This is always non-null. */
|
||||
extern struct program_space *current_program_space;
|
||||
|
||||
#define ALL_PSPACES(pspace) \
|
||||
for ((pspace) = program_spaces; (pspace) != NULL; (pspace) = (pspace)->next)
|
||||
|
||||
/* Add a new empty program space, and assign ASPACE to it. Returns the
|
||||
pointer to the new object. */
|
||||
extern struct program_space *add_program_space (struct address_space *aspace);
|
||||
|
||||
/* Release PSPACE and removes it from the pspace list. */
|
||||
extern void remove_program_space (struct program_space *pspace);
|
||||
|
||||
/* Returns the number of program spaces listed. */
|
||||
extern int number_of_program_spaces (void);
|
||||
|
||||
/* Copies program space SRC to DEST. Copies the main executable file,
|
||||
and the main symbol file. Returns DEST. */
|
||||
extern struct program_space *clone_program_space (struct program_space *dest,
|
||||
struct program_space *src);
|
||||
|
||||
/* Save the current program space so that it may be restored by a later
|
||||
call to do_cleanups. Returns the struct cleanup pointer needed for
|
||||
later doing the cleanup. */
|
||||
extern struct cleanup *save_current_program_space (void);
|
||||
|
||||
/* Sets PSPACE as the current program space. This is usually used
|
||||
instead of set_current_space_and_thread when the current
|
||||
thread/inferior is not important for the operations that follow.
|
||||
E.g., when accessing the raw symbol tables. If memory access is
|
||||
required, then you should use switch_to_program_space_and_thread.
|
||||
Otherwise, it is the caller's responsibility to make sure that the
|
||||
currently selected inferior/thread matches the selected program
|
||||
space. */
|
||||
extern void set_current_program_space (struct program_space *pspace);
|
||||
|
||||
/* Saves the current thread (may be null), frame and program space in
|
||||
the current cleanup chain. */
|
||||
extern struct cleanup *save_current_space_and_thread (void);
|
||||
|
||||
/* Switches full context to program space PSPACE. Switches to the
|
||||
first thread found bound to PSPACE. */
|
||||
extern void switch_to_program_space_and_thread (struct program_space *pspace);
|
||||
|
||||
/* Create a new address space object, and add it to the list. */
|
||||
extern struct address_space *new_address_space (void);
|
||||
|
||||
/* Maybe create a new address space object, and add it to the list, or
|
||||
return a pointer to an existing address space, in case inferiors
|
||||
share an address space. */
|
||||
extern struct address_space *maybe_new_address_space (void);
|
||||
|
||||
/* Update all program spaces matching to address spaces. The user may
|
||||
have created several program spaces, and loaded executables into
|
||||
them before connecting to the target interface that will create the
|
||||
inferiors. All that happens before GDB has a chance to know if the
|
||||
inferiors will share an address space or not. Call this after
|
||||
having connected to the target interface and having fetched the
|
||||
target description, to fixup the program/address spaces
|
||||
mappings. */
|
||||
extern void update_address_spaces (void);
|
||||
|
||||
/* Prune away automatically added program spaces that aren't required
|
||||
anymore. */
|
||||
extern void prune_program_spaces (void);
|
||||
|
||||
/* Keep a registry of per-pspace data-pointers required by other GDB
|
||||
modules. */
|
||||
|
||||
extern const struct program_space_data *register_program_space_data (void);
|
||||
extern const struct program_space_data *register_program_space_data_with_cleanup
|
||||
(void (*cleanup) (struct program_space *, void *));
|
||||
extern void clear_program_space_data (struct program_space *pspace);
|
||||
extern void set_program_space_data (struct program_space *pspace,
|
||||
const struct program_space_data *data, void *value);
|
||||
extern void *program_space_data (struct program_space *pspace,
|
||||
const struct program_space_data *data);
|
||||
|
||||
#endif
|
34
gdb/record.c
34
gdb/record.c
@ -778,20 +778,22 @@ record_wait (struct target_ops *ops,
|
||||
if (status->kind == TARGET_WAITKIND_STOPPED
|
||||
&& status->value.sig == TARGET_SIGNAL_TRAP)
|
||||
{
|
||||
struct regcache *regcache;
|
||||
|
||||
/* Yes -- check if there is a breakpoint. */
|
||||
registers_changed ();
|
||||
tmp_pc = regcache_read_pc (get_current_regcache ());
|
||||
if (breakpoint_inserted_here_p (tmp_pc))
|
||||
regcache = get_current_regcache ();
|
||||
tmp_pc = regcache_read_pc (regcache);
|
||||
if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
|
||||
tmp_pc))
|
||||
{
|
||||
/* There is a breakpoint. GDB will want to stop. */
|
||||
CORE_ADDR decr_pc_after_break =
|
||||
gdbarch_decr_pc_after_break
|
||||
(get_regcache_arch (get_current_regcache ()));
|
||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
||||
CORE_ADDR decr_pc_after_break
|
||||
= gdbarch_decr_pc_after_break (gdbarch);
|
||||
if (decr_pc_after_break)
|
||||
{
|
||||
regcache_write_pc (get_thread_regcache (ret),
|
||||
tmp_pc + decr_pc_after_break);
|
||||
}
|
||||
regcache_write_pc (regcache,
|
||||
tmp_pc + decr_pc_after_break);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -799,11 +801,9 @@ record_wait (struct target_ops *ops,
|
||||
stepping, therefore gdb will not stop.
|
||||
Therefore we will not return to gdb.
|
||||
Record the insn and resume. */
|
||||
if (!do_record_message (get_current_regcache (),
|
||||
TARGET_SIGNAL_0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!do_record_message (regcache, TARGET_SIGNAL_0))
|
||||
break;
|
||||
|
||||
record_beneath_to_resume (record_beneath_to_resume_ops,
|
||||
ptid, 1,
|
||||
TARGET_SIGNAL_0);
|
||||
@ -833,7 +833,8 @@ record_wait (struct target_ops *ops,
|
||||
if (execution_direction == EXEC_FORWARD)
|
||||
{
|
||||
tmp_pc = regcache_read_pc (regcache);
|
||||
if (breakpoint_inserted_here_p (tmp_pc))
|
||||
if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
|
||||
tmp_pc))
|
||||
{
|
||||
if (record_debug)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
@ -981,7 +982,8 @@ record_wait (struct target_ops *ops,
|
||||
|
||||
/* check breakpoint */
|
||||
tmp_pc = regcache_read_pc (regcache);
|
||||
if (breakpoint_inserted_here_p (tmp_pc))
|
||||
if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
|
||||
tmp_pc))
|
||||
{
|
||||
if (record_debug)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
|
@ -185,6 +185,11 @@ register_size (struct gdbarch *gdbarch, int regnum)
|
||||
struct regcache
|
||||
{
|
||||
struct regcache_descr *descr;
|
||||
|
||||
/* The address space of this register cache (for registers where it
|
||||
makes sense, like PC or SP). */
|
||||
struct address_space *aspace;
|
||||
|
||||
/* The register buffers. A read-only register cache can hold the
|
||||
full [0 .. gdbarch_num_regs + gdbarch_num_pseudo_regs) while a read/write
|
||||
register cache can only hold [0 .. gdbarch_num_regs). */
|
||||
@ -219,6 +224,7 @@ regcache_xmalloc (struct gdbarch *gdbarch)
|
||||
= XCALLOC (descr->sizeof_raw_registers, gdb_byte);
|
||||
regcache->register_valid_p
|
||||
= XCALLOC (descr->sizeof_raw_register_valid_p, gdb_byte);
|
||||
regcache->aspace = NULL;
|
||||
regcache->readonly_p = 1;
|
||||
regcache->ptid = minus_one_ptid;
|
||||
return regcache;
|
||||
@ -254,6 +260,12 @@ get_regcache_arch (const struct regcache *regcache)
|
||||
return regcache->descr->gdbarch;
|
||||
}
|
||||
|
||||
struct address_space *
|
||||
get_regcache_aspace (const struct regcache *regcache)
|
||||
{
|
||||
return regcache->aspace;
|
||||
}
|
||||
|
||||
/* Return a pointer to register REGNUM's buffer cache. */
|
||||
|
||||
static gdb_byte *
|
||||
@ -340,10 +352,14 @@ regcache_cpy (struct regcache *dst, struct regcache *src)
|
||||
{
|
||||
int i;
|
||||
gdb_byte *buf;
|
||||
|
||||
gdb_assert (src != NULL && dst != NULL);
|
||||
gdb_assert (src->descr->gdbarch == dst->descr->gdbarch);
|
||||
gdb_assert (src != dst);
|
||||
gdb_assert (src->readonly_p || dst->readonly_p);
|
||||
|
||||
dst->aspace = src->aspace;
|
||||
|
||||
if (!src->readonly_p)
|
||||
regcache_save (dst, do_cooked_read, src);
|
||||
else if (!dst->readonly_p)
|
||||
@ -362,6 +378,8 @@ regcache_cpy_no_passthrough (struct regcache *dst, struct regcache *src)
|
||||
move of data into the current regcache. Doing this would be
|
||||
silly - it would mean that valid_p would be completely invalid. */
|
||||
gdb_assert (dst->readonly_p);
|
||||
|
||||
dst->aspace = src->aspace;
|
||||
memcpy (dst->registers, src->registers, dst->descr->sizeof_raw_registers);
|
||||
memcpy (dst->register_valid_p, src->register_valid_p,
|
||||
dst->descr->sizeof_raw_register_valid_p);
|
||||
@ -438,6 +456,8 @@ get_thread_arch_regcache (ptid_t ptid, struct gdbarch *gdbarch)
|
||||
new_regcache = regcache_xmalloc (gdbarch);
|
||||
new_regcache->readonly_p = 0;
|
||||
new_regcache->ptid = ptid;
|
||||
new_regcache->aspace = target_thread_address_space (ptid);
|
||||
gdb_assert (new_regcache->aspace != NULL);
|
||||
|
||||
list = xmalloc (sizeof (struct regcache_list));
|
||||
list->regcache = new_regcache;
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
struct regcache;
|
||||
struct gdbarch;
|
||||
struct address_space;
|
||||
|
||||
extern struct regcache *get_current_regcache (void);
|
||||
extern struct regcache *get_thread_regcache (ptid_t ptid);
|
||||
@ -36,6 +37,10 @@ struct regcache *regcache_xmalloc (struct gdbarch *gdbarch);
|
||||
|
||||
extern struct gdbarch *get_regcache_arch (const struct regcache *regcache);
|
||||
|
||||
/* Return REGCACHE's address space. */
|
||||
|
||||
extern struct address_space *get_regcache_aspace (const struct regcache *regcache);
|
||||
|
||||
/* Transfer a raw register [0..NUM_REGS) between core-gdb and the
|
||||
regcache. */
|
||||
|
||||
|
@ -471,7 +471,7 @@ gdbsim_create_inferior (struct target_ops *target, char *exec_file, char *args,
|
||||
sim_create_inferior (gdbsim_desc, exec_bfd, argv, env);
|
||||
|
||||
inferior_ptid = remote_sim_ptid;
|
||||
add_inferior_silent (ptid_get_pid (inferior_ptid));
|
||||
inferior_appeared_silent (current_inferior (), ptid_get_pid (inferior_ptid));
|
||||
add_thread_silent (inferior_ptid);
|
||||
|
||||
insert_breakpoints (); /* Needed to get correct instruction in cache */
|
||||
|
40
gdb/remote.c
40
gdb/remote.c
@ -1183,7 +1183,26 @@ remote_add_inferior (int pid, int attached)
|
||||
if (attached == -1)
|
||||
attached = remote_query_attached (pid);
|
||||
|
||||
inf = add_inferior (pid);
|
||||
if (gdbarch_has_global_solist (target_gdbarch))
|
||||
{
|
||||
/* If the target shares code across all inferiors, then every
|
||||
attach adds a new inferior. */
|
||||
inf = add_inferior (pid);
|
||||
|
||||
/* ... and every inferior is bound to the same program space.
|
||||
However, each inferior may still have its own address
|
||||
space. */
|
||||
inf->aspace = maybe_new_address_space ();
|
||||
inf->pspace = current_program_space;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* In the traditional debugging scenario, there's a 1-1 match
|
||||
between program/address spaces. We simply bind the inferior
|
||||
to the program space's address space. */
|
||||
inf = current_inferior ();
|
||||
inferior_appeared (inf, pid);
|
||||
}
|
||||
|
||||
inf->attach_flag = attached;
|
||||
|
||||
@ -2639,6 +2658,10 @@ remote_start_remote (struct ui_out *uiout, void *opaque)
|
||||
this before anything involving memory or registers. */
|
||||
target_find_description ();
|
||||
|
||||
/* Next, now that we know something about the target, update the
|
||||
address spaces in the program spaces. */
|
||||
update_address_spaces ();
|
||||
|
||||
/* On OSs where the list of libraries is global to all
|
||||
processes, we fetch them early. */
|
||||
if (gdbarch_has_global_solist (target_gdbarch))
|
||||
@ -3486,7 +3509,7 @@ extended_remote_attach_1 (struct target_ops *target, char *args, int from_tty)
|
||||
error (_("Attaching to %s failed"),
|
||||
target_pid_to_str (pid_to_ptid (pid)));
|
||||
|
||||
remote_add_inferior (pid, 1);
|
||||
set_current_inferior (remote_add_inferior (pid, 1));
|
||||
|
||||
inferior_ptid = pid_to_ptid (pid);
|
||||
|
||||
@ -6779,11 +6802,14 @@ extended_remote_create_inferior_1 (char *exec_file, char *args,
|
||||
extended_remote_restart ();
|
||||
}
|
||||
|
||||
/* Clean up from the last time we ran, before we mark the target
|
||||
running again. This will mark breakpoints uninserted, and
|
||||
get_offsets may insert breakpoints. */
|
||||
init_thread_list ();
|
||||
init_wait_for_inferior ();
|
||||
if (!have_inferiors ())
|
||||
{
|
||||
/* Clean up from the last time we ran, before we mark the target
|
||||
running again. This will mark breakpoints uninserted, and
|
||||
get_offsets may insert breakpoints. */
|
||||
init_thread_list ();
|
||||
init_wait_for_inferior ();
|
||||
}
|
||||
|
||||
/* Now mark the inferior as running before we do anything else. */
|
||||
inferior_ptid = magic_null_ptid;
|
||||
|
@ -671,6 +671,7 @@ static int
|
||||
rs6000_software_single_step (struct frame_info *frame)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||||
int ii, insn;
|
||||
CORE_ADDR loc;
|
||||
@ -697,7 +698,7 @@ rs6000_software_single_step (struct frame_info *frame)
|
||||
/* ignore invalid breakpoint. */
|
||||
if (breaks[ii] == -1)
|
||||
continue;
|
||||
insert_single_step_breakpoint (gdbarch, breaks[ii]);
|
||||
insert_single_step_breakpoint (gdbarch, aspace, breaks[ii]);
|
||||
}
|
||||
|
||||
errno = 0; /* FIXME, don't ignore errors! */
|
||||
|
@ -1084,6 +1084,7 @@ int
|
||||
ppc_deal_with_atomic_sequence (struct frame_info *frame)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||||
CORE_ADDR pc = get_frame_pc (frame);
|
||||
CORE_ADDR breaks[2] = {-1, -1};
|
||||
@ -1157,7 +1158,7 @@ ppc_deal_with_atomic_sequence (struct frame_info *frame)
|
||||
|
||||
/* Effectively inserts the breakpoints. */
|
||||
for (index = 0; index <= last_breakpoint; index++)
|
||||
insert_single_step_breakpoint (gdbarch, breaks[index]);
|
||||
insert_single_step_breakpoint (gdbarch, aspace, breaks[index]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -365,10 +365,14 @@ disable_break (void)
|
||||
static int
|
||||
enable_break (void)
|
||||
{
|
||||
if (symfile_objfile != NULL)
|
||||
if (symfile_objfile != NULL && has_stack_frames ())
|
||||
{
|
||||
struct frame_info *frame = get_current_frame ();
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
|
||||
base_breakpoint
|
||||
= deprecated_insert_raw_breakpoint (target_gdbarch,
|
||||
aspace,
|
||||
entry_point_address ());
|
||||
|
||||
if (base_breakpoint != NULL)
|
||||
|
155
gdb/solib-svr4.c
155
gdb/solib-svr4.c
@ -273,12 +273,10 @@ IGNORE_FIRST_LINK_MAP_ENTRY (struct so_list *so)
|
||||
ptr_type) == 0;
|
||||
}
|
||||
|
||||
/* Per-inferior SVR4 specific data. */
|
||||
/* Per pspace SVR4 specific data. */
|
||||
|
||||
struct svr4_info
|
||||
{
|
||||
int pid;
|
||||
|
||||
CORE_ADDR debug_base; /* Base of dynamic linker structures */
|
||||
|
||||
/* Validity flag for debug_loader_offset. */
|
||||
@ -292,69 +290,40 @@ struct svr4_info
|
||||
|
||||
/* Load map address for the main executable. */
|
||||
CORE_ADDR main_lm_addr;
|
||||
|
||||
CORE_ADDR interp_text_sect_low;
|
||||
CORE_ADDR interp_text_sect_high;
|
||||
CORE_ADDR interp_plt_sect_low;
|
||||
CORE_ADDR interp_plt_sect_high;
|
||||
};
|
||||
|
||||
/* List of known processes using solib-svr4 shared libraries, storing
|
||||
the required bookkeeping for each. */
|
||||
|
||||
typedef struct svr4_info *svr4_info_p;
|
||||
DEF_VEC_P(svr4_info_p);
|
||||
VEC(svr4_info_p) *svr4_info = NULL;
|
||||
|
||||
/* Get svr4 data for inferior PID (target id). If none is found yet,
|
||||
add it now. This function always returns a valid object. */
|
||||
|
||||
struct svr4_info *
|
||||
get_svr4_info (int pid)
|
||||
{
|
||||
int ix;
|
||||
struct svr4_info *it;
|
||||
|
||||
gdb_assert (pid != 0);
|
||||
|
||||
for (ix = 0; VEC_iterate (svr4_info_p, svr4_info, ix, it); ++ix)
|
||||
{
|
||||
if (it->pid == pid)
|
||||
return it;
|
||||
}
|
||||
|
||||
it = XZALLOC (struct svr4_info);
|
||||
it->pid = pid;
|
||||
|
||||
VEC_safe_push (svr4_info_p, svr4_info, it);
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
/* Get rid of any svr4 related bookkeeping for inferior PID (target
|
||||
id). */
|
||||
/* Per-program-space data key. */
|
||||
static const struct program_space_data *solib_svr4_pspace_data;
|
||||
|
||||
static void
|
||||
remove_svr4_info (int pid)
|
||||
svr4_pspace_data_cleanup (struct program_space *pspace, void *arg)
|
||||
{
|
||||
int ix;
|
||||
struct svr4_info *it;
|
||||
struct svr4_info *info;
|
||||
|
||||
for (ix = 0; VEC_iterate (svr4_info_p, svr4_info, ix, it); ++ix)
|
||||
{
|
||||
if (it->pid == pid)
|
||||
{
|
||||
VEC_unordered_remove (svr4_info_p, svr4_info, ix);
|
||||
return;
|
||||
}
|
||||
}
|
||||
info = program_space_data (pspace, solib_svr4_pspace_data);
|
||||
xfree (info);
|
||||
}
|
||||
|
||||
/* This is an "inferior_exit" observer. Inferior PID (target id) is
|
||||
being removed from the inferior list, because it exited, was
|
||||
killed, detached, or we just dropped the connection to the debug
|
||||
interface --- discard any solib-svr4 related bookkeeping for this
|
||||
inferior. */
|
||||
/* Get the current svr4 data. If none is found yet, add it now. This
|
||||
function always returns a valid object. */
|
||||
|
||||
static void
|
||||
solib_svr4_inferior_exit (int pid)
|
||||
static struct svr4_info *
|
||||
get_svr4_info (void)
|
||||
{
|
||||
remove_svr4_info (pid);
|
||||
struct svr4_info *info;
|
||||
|
||||
info = program_space_data (current_program_space, solib_svr4_pspace_data);
|
||||
if (info != NULL)
|
||||
return info;
|
||||
|
||||
info = XZALLOC (struct svr4_info);
|
||||
set_program_space_data (current_program_space, solib_svr4_pspace_data, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
/* Local function prototypes */
|
||||
@ -931,7 +900,7 @@ open_symbol_file_object (void *from_ttyp)
|
||||
int l_name_size = TYPE_LENGTH (ptr_type);
|
||||
gdb_byte *l_name_buf = xmalloc (l_name_size);
|
||||
struct cleanup *cleanups = make_cleanup (xfree, l_name_buf);
|
||||
struct svr4_info *info = get_svr4_info (PIDGET (inferior_ptid));
|
||||
struct svr4_info *info = get_svr4_info ();
|
||||
|
||||
if (symfile_objfile)
|
||||
if (!query (_("Attempt to reload symbols from process? ")))
|
||||
@ -982,8 +951,7 @@ open_symbol_file_object (void *from_ttyp)
|
||||
static struct so_list *
|
||||
svr4_default_sos (void)
|
||||
{
|
||||
struct inferior *inf = current_inferior ();
|
||||
struct svr4_info *info = get_svr4_info (inf->pid);
|
||||
struct svr4_info *info = get_svr4_info ();
|
||||
|
||||
struct so_list *head = NULL;
|
||||
struct so_list **link_ptr = &head;
|
||||
@ -1038,14 +1006,9 @@ svr4_current_sos (void)
|
||||
struct so_list *head = 0;
|
||||
struct so_list **link_ptr = &head;
|
||||
CORE_ADDR ldsomap = 0;
|
||||
struct inferior *inf;
|
||||
struct svr4_info *info;
|
||||
|
||||
if (ptid_equal (inferior_ptid, null_ptid))
|
||||
return NULL;
|
||||
|
||||
inf = current_inferior ();
|
||||
info = get_svr4_info (inf->pid);
|
||||
info = get_svr4_info ();
|
||||
|
||||
/* Always locate the debug struct, in case it has moved. */
|
||||
info->debug_base = 0;
|
||||
@ -1142,7 +1105,7 @@ CORE_ADDR
|
||||
svr4_fetch_objfile_link_map (struct objfile *objfile)
|
||||
{
|
||||
struct so_list *so;
|
||||
struct svr4_info *info = get_svr4_info (PIDGET (inferior_ptid));
|
||||
struct svr4_info *info = get_svr4_info ();
|
||||
|
||||
/* Cause svr4_current_sos() to be run if it hasn't been already. */
|
||||
if (info->main_lm_addr == 0)
|
||||
@ -1182,16 +1145,16 @@ match_main (char *soname)
|
||||
|
||||
/* Return 1 if PC lies in the dynamic symbol resolution code of the
|
||||
SVR4 run time loader. */
|
||||
static CORE_ADDR interp_text_sect_low;
|
||||
static CORE_ADDR interp_text_sect_high;
|
||||
static CORE_ADDR interp_plt_sect_low;
|
||||
static CORE_ADDR interp_plt_sect_high;
|
||||
|
||||
int
|
||||
svr4_in_dynsym_resolve_code (CORE_ADDR pc)
|
||||
{
|
||||
return ((pc >= interp_text_sect_low && pc < interp_text_sect_high)
|
||||
|| (pc >= interp_plt_sect_low && pc < interp_plt_sect_high)
|
||||
struct svr4_info *info = get_svr4_info ();
|
||||
|
||||
return ((pc >= info->interp_text_sect_low
|
||||
&& pc < info->interp_text_sect_high)
|
||||
|| (pc >= info->interp_plt_sect_low
|
||||
&& pc < info->interp_plt_sect_high)
|
||||
|| in_plt_section (pc, NULL));
|
||||
}
|
||||
|
||||
@ -1265,14 +1228,13 @@ enable_break (struct svr4_info *info)
|
||||
asection *interp_sect;
|
||||
gdb_byte *interp_name;
|
||||
CORE_ADDR sym_addr;
|
||||
struct inferior *inf = current_inferior ();
|
||||
|
||||
/* First, remove all the solib event breakpoints. Their addresses
|
||||
may have changed since the last time we ran the program. */
|
||||
remove_solib_event_breakpoints ();
|
||||
|
||||
interp_text_sect_low = interp_text_sect_high = 0;
|
||||
interp_plt_sect_low = interp_plt_sect_high = 0;
|
||||
info->interp_text_sect_low = info->interp_text_sect_high = 0;
|
||||
info->interp_plt_sect_low = info->interp_plt_sect_high = 0;
|
||||
|
||||
/* If we already have a shared library list in the target, and
|
||||
r_debug contains r_brk, set the breakpoint there - this should
|
||||
@ -1308,18 +1270,20 @@ enable_break (struct svr4_info *info)
|
||||
interp_sect = bfd_get_section_by_name (tmp_bfd, ".text");
|
||||
if (interp_sect)
|
||||
{
|
||||
interp_text_sect_low =
|
||||
info->interp_text_sect_low =
|
||||
bfd_section_vma (tmp_bfd, interp_sect) + load_addr;
|
||||
interp_text_sect_high =
|
||||
interp_text_sect_low + bfd_section_size (tmp_bfd, interp_sect);
|
||||
info->interp_text_sect_high =
|
||||
info->interp_text_sect_low
|
||||
+ bfd_section_size (tmp_bfd, interp_sect);
|
||||
}
|
||||
interp_sect = bfd_get_section_by_name (tmp_bfd, ".plt");
|
||||
if (interp_sect)
|
||||
{
|
||||
interp_plt_sect_low =
|
||||
info->interp_plt_sect_low =
|
||||
bfd_section_vma (tmp_bfd, interp_sect) + load_addr;
|
||||
interp_plt_sect_high =
|
||||
interp_plt_sect_low + bfd_section_size (tmp_bfd, interp_sect);
|
||||
info->interp_plt_sect_high =
|
||||
info->interp_plt_sect_low
|
||||
+ bfd_section_size (tmp_bfd, interp_sect);
|
||||
}
|
||||
|
||||
create_solib_event_breakpoint (target_gdbarch, sym_addr);
|
||||
@ -1412,18 +1376,20 @@ enable_break (struct svr4_info *info)
|
||||
interp_sect = bfd_get_section_by_name (tmp_bfd, ".text");
|
||||
if (interp_sect)
|
||||
{
|
||||
interp_text_sect_low =
|
||||
info->interp_text_sect_low =
|
||||
bfd_section_vma (tmp_bfd, interp_sect) + load_addr;
|
||||
interp_text_sect_high =
|
||||
interp_text_sect_low + bfd_section_size (tmp_bfd, interp_sect);
|
||||
info->interp_text_sect_high =
|
||||
info->interp_text_sect_low
|
||||
+ bfd_section_size (tmp_bfd, interp_sect);
|
||||
}
|
||||
interp_sect = bfd_get_section_by_name (tmp_bfd, ".plt");
|
||||
if (interp_sect)
|
||||
{
|
||||
interp_plt_sect_low =
|
||||
info->interp_plt_sect_low =
|
||||
bfd_section_vma (tmp_bfd, interp_sect) + load_addr;
|
||||
interp_plt_sect_high =
|
||||
interp_plt_sect_low + bfd_section_size (tmp_bfd, interp_sect);
|
||||
info->interp_plt_sect_high =
|
||||
info->interp_plt_sect_low
|
||||
+ bfd_section_size (tmp_bfd, interp_sect);
|
||||
}
|
||||
|
||||
/* Now try to set a breakpoint in the dynamic linker. */
|
||||
@ -1686,7 +1652,7 @@ svr4_solib_create_inferior_hook (void)
|
||||
struct thread_info *tp;
|
||||
struct svr4_info *info;
|
||||
|
||||
info = get_svr4_info (PIDGET (inferior_ptid));
|
||||
info = get_svr4_info ();
|
||||
|
||||
/* Relocate the main executable if necessary. */
|
||||
svr4_relocate_main_executable ();
|
||||
@ -1726,7 +1692,14 @@ svr4_solib_create_inferior_hook (void)
|
||||
static void
|
||||
svr4_clear_solib (void)
|
||||
{
|
||||
remove_svr4_info (PIDGET (inferior_ptid));
|
||||
struct svr4_info *info;
|
||||
|
||||
info = get_svr4_info ();
|
||||
info->debug_base = 0;
|
||||
info->debug_loader_offset_p = 0;
|
||||
info->debug_loader_offset = 0;
|
||||
xfree (info->debug_loader_name);
|
||||
info->debug_loader_name = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1925,6 +1898,8 @@ void
|
||||
_initialize_svr4_solib (void)
|
||||
{
|
||||
solib_svr4_data = gdbarch_data_register_pre_init (solib_svr4_init);
|
||||
solib_svr4_pspace_data
|
||||
= register_program_space_data_with_cleanup (svr4_pspace_data_cleanup);
|
||||
|
||||
svr4_so_ops.relocate_section_addresses = svr4_relocate_section_addresses;
|
||||
svr4_so_ops.free_so = svr4_free_so;
|
||||
@ -1937,6 +1912,4 @@ _initialize_svr4_solib (void)
|
||||
svr4_so_ops.bfd_open = solib_bfd_open;
|
||||
svr4_so_ops.lookup_lib_global_symbol = elf_lookup_lib_symbol;
|
||||
svr4_so_ops.same = svr4_same;
|
||||
|
||||
observer_attach_inferior_exit (solib_svr4_inferior_exit);
|
||||
}
|
||||
|
12
gdb/solib.c
12
gdb/solib.c
@ -86,9 +86,8 @@ set_solib_ops (struct gdbarch *gdbarch, struct target_so_ops *new_ops)
|
||||
configuration needs to call set_solib_ops. */
|
||||
struct target_so_ops *current_target_so_ops;
|
||||
|
||||
/* local data declarations */
|
||||
|
||||
static struct so_list *so_list_head; /* List of known shared objects */
|
||||
/* List of known shared objects */
|
||||
#define so_list_head current_program_space->so_list
|
||||
|
||||
/* Local function prototypes */
|
||||
|
||||
@ -651,6 +650,7 @@ update_solib_list (int from_tty, struct target_ops *target)
|
||||
for (i = inferior; i; i = i->next)
|
||||
{
|
||||
i->from_tty = from_tty;
|
||||
i->pspace = current_program_space;
|
||||
|
||||
/* Fill in the rest of the `struct so_list' node. */
|
||||
catch_errors (solib_map_sections, i,
|
||||
@ -937,11 +937,11 @@ solib_contains_address_p (const struct so_list *const solib,
|
||||
*/
|
||||
|
||||
char *
|
||||
solib_name_from_address (CORE_ADDR address)
|
||||
solib_name_from_address (struct program_space *pspace, CORE_ADDR address)
|
||||
{
|
||||
struct so_list *so = 0; /* link map state variable */
|
||||
struct so_list *so = NULL;
|
||||
|
||||
for (so = so_list_head; so; so = so->next)
|
||||
for (so = pspace->so_list; so; so = so->next)
|
||||
if (solib_contains_address_p (so, address))
|
||||
return (so->so_name);
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
struct so_list;
|
||||
struct target_ops;
|
||||
struct target_so_ops;
|
||||
struct program_space;
|
||||
|
||||
/* Called when we free all symtabs, to free the shared library information
|
||||
as well. */
|
||||
@ -45,7 +46,7 @@ extern void solib_create_inferior_hook (void);
|
||||
|
||||
/* If ADDR lies in a shared library, return its name. */
|
||||
|
||||
extern char *solib_name_from_address (CORE_ADDR);
|
||||
extern char *solib_name_from_address (struct program_space *, CORE_ADDR);
|
||||
|
||||
/* Return 1 if ADDR lies within SOLIB. */
|
||||
|
||||
|
@ -52,6 +52,9 @@ struct so_list
|
||||
/* shared object file name, expanded to something GDB can open */
|
||||
char so_name[SO_NAME_MAX_PATH_SIZE];
|
||||
|
||||
/* Program space this shared library belongs to. */
|
||||
struct program_space *pspace;
|
||||
|
||||
/* The following fields of the structure are built from
|
||||
information gathered from the shared object file itself, and
|
||||
are set when we actually add it to our symbol tables.
|
||||
|
32
gdb/source.c
32
gdb/source.c
@ -92,6 +92,8 @@ static struct symtab *current_source_symtab;
|
||||
|
||||
static int current_source_line;
|
||||
|
||||
static struct program_space *current_source_pspace;
|
||||
|
||||
/* Default number of lines to print with commands like "list".
|
||||
This is based on guessing how many long (i.e. more than chars_per_line
|
||||
characters) lines there will be. To be completely correct, "list"
|
||||
@ -152,6 +154,7 @@ get_current_source_symtab_and_line (void)
|
||||
{
|
||||
struct symtab_and_line cursal = { 0 };
|
||||
|
||||
cursal.pspace = current_source_pspace;
|
||||
cursal.symtab = current_source_symtab;
|
||||
cursal.line = current_source_line;
|
||||
cursal.pc = 0;
|
||||
@ -190,15 +193,17 @@ struct symtab_and_line
|
||||
set_current_source_symtab_and_line (const struct symtab_and_line *sal)
|
||||
{
|
||||
struct symtab_and_line cursal = { 0 };
|
||||
|
||||
|
||||
cursal.pspace = current_source_pspace;
|
||||
cursal.symtab = current_source_symtab;
|
||||
cursal.line = current_source_line;
|
||||
|
||||
current_source_symtab = sal->symtab;
|
||||
current_source_line = sal->line;
|
||||
cursal.pc = 0;
|
||||
cursal.end = 0;
|
||||
|
||||
|
||||
current_source_pspace = sal->pspace;
|
||||
current_source_symtab = sal->symtab;
|
||||
current_source_line = sal->line;
|
||||
|
||||
return cursal;
|
||||
}
|
||||
|
||||
@ -232,6 +237,7 @@ select_source_symtab (struct symtab *s)
|
||||
{
|
||||
current_source_symtab = s;
|
||||
current_source_line = 1;
|
||||
current_source_pspace = SYMTAB_PSPACE (s);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -245,6 +251,7 @@ select_source_symtab (struct symtab *s)
|
||||
sals = decode_line_spec (main_name (), 1);
|
||||
sal = sals.sals[0];
|
||||
xfree (sals.sals);
|
||||
current_source_pspace = sal.pspace;
|
||||
current_source_symtab = sal.symtab;
|
||||
current_source_line = max (sal.line - (lines_to_list - 1), 1);
|
||||
if (current_source_symtab)
|
||||
@ -256,7 +263,7 @@ select_source_symtab (struct symtab *s)
|
||||
|
||||
current_source_line = 1;
|
||||
|
||||
for (ofp = object_files; ofp != NULL; ofp = ofp->next)
|
||||
ALL_OBJFILES (ofp)
|
||||
{
|
||||
for (s = ofp->symtabs; s; s = s->next)
|
||||
{
|
||||
@ -264,15 +271,19 @@ select_source_symtab (struct symtab *s)
|
||||
int len = strlen (name);
|
||||
if (!(len > 2 && (strcmp (&name[len - 2], ".h") == 0
|
||||
|| strcmp (name, "<<C++-namespaces>>") == 0)))
|
||||
current_source_symtab = s;
|
||||
{
|
||||
current_source_pspace = current_program_space;
|
||||
current_source_symtab = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current_source_symtab)
|
||||
return;
|
||||
|
||||
/* How about the partial symbol tables? */
|
||||
|
||||
for (ofp = object_files; ofp != NULL; ofp = ofp->next)
|
||||
ALL_OBJFILES (ofp)
|
||||
{
|
||||
for (ps = ofp->psymtabs; ps != NULL; ps = ps->next)
|
||||
{
|
||||
@ -293,6 +304,7 @@ select_source_symtab (struct symtab *s)
|
||||
}
|
||||
else
|
||||
{
|
||||
current_source_pspace = current_program_space;
|
||||
current_source_symtab = PSYMTAB_TO_SYMTAB (cs_pst);
|
||||
}
|
||||
}
|
||||
@ -317,11 +329,13 @@ show_directories (char *ignore, int from_tty)
|
||||
void
|
||||
forget_cached_source_info (void)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
struct symtab *s;
|
||||
struct objfile *objfile;
|
||||
struct partial_symtab *pst;
|
||||
|
||||
for (objfile = object_files; objfile != NULL; objfile = objfile->next)
|
||||
ALL_PSPACES (pspace)
|
||||
ALL_PSPACE_OBJFILES (pspace, objfile)
|
||||
{
|
||||
for (s = objfile->symtabs; s != NULL; s = s->next)
|
||||
{
|
||||
|
@ -1312,6 +1312,7 @@ sparc_software_single_step (struct frame_info *frame)
|
||||
{
|
||||
struct gdbarch *arch = get_frame_arch (frame);
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (arch);
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
CORE_ADDR npc, nnpc;
|
||||
|
||||
CORE_ADDR pc, orig_npc;
|
||||
@ -1322,10 +1323,10 @@ sparc_software_single_step (struct frame_info *frame)
|
||||
/* Analyze the instruction at PC. */
|
||||
nnpc = sparc_analyze_control_transfer (frame, pc, &npc);
|
||||
if (npc != 0)
|
||||
insert_single_step_breakpoint (arch, npc);
|
||||
insert_single_step_breakpoint (arch, aspace, npc);
|
||||
|
||||
if (nnpc != 0)
|
||||
insert_single_step_breakpoint (arch, nnpc);
|
||||
insert_single_step_breakpoint (arch, aspace, nnpc);
|
||||
|
||||
/* Assert that we have set at least one breakpoint, and that
|
||||
they're not set at the same spot - unless we're going
|
||||
|
@ -1504,6 +1504,7 @@ static int
|
||||
spu_software_single_step (struct frame_info *frame)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_frame_arch (frame);
|
||||
struct address_space *aspace = get_frame_address_space (frame);
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||||
CORE_ADDR pc, next_pc;
|
||||
unsigned int insn;
|
||||
@ -1524,7 +1525,8 @@ spu_software_single_step (struct frame_info *frame)
|
||||
else
|
||||
next_pc = (SPUADDR_ADDR (pc) + 4) & (SPU_LS_SIZE - 1);
|
||||
|
||||
insert_single_step_breakpoint (gdbarch, SPUADDR (SPUADDR_SPU (pc), next_pc));
|
||||
insert_single_step_breakpoint (gdbarch,
|
||||
aspace, SPUADDR (SPUADDR_SPU (pc), next_pc));
|
||||
|
||||
if (is_branch (insn, &offset, ®))
|
||||
{
|
||||
@ -1540,7 +1542,7 @@ spu_software_single_step (struct frame_info *frame)
|
||||
|
||||
target = target & (SPU_LS_SIZE - 1);
|
||||
if (target != next_pc)
|
||||
insert_single_step_breakpoint (gdbarch,
|
||||
insert_single_step_breakpoint (gdbarch, aspace,
|
||||
SPUADDR (SPUADDR_SPU (pc), target));
|
||||
}
|
||||
|
||||
|
@ -648,7 +648,8 @@ print_frame_info (struct frame_info *frame, int print_level,
|
||||
}
|
||||
|
||||
if (print_what != LOCATION)
|
||||
set_default_breakpoint (1, get_frame_pc (frame), sal.symtab, sal.line);
|
||||
set_default_breakpoint (1, sal.pspace,
|
||||
get_frame_pc (frame), sal.symtab, sal.line);
|
||||
|
||||
annotate_frame_end ();
|
||||
|
||||
@ -825,7 +826,8 @@ print_frame (struct frame_info *frame, int print_level,
|
||||
#ifdef PC_SOLIB
|
||||
char *lib = PC_SOLIB (get_frame_pc (frame));
|
||||
#else
|
||||
char *lib = solib_name_from_address (get_frame_pc (frame));
|
||||
char *lib = solib_name_from_address (get_frame_program_space (frame),
|
||||
get_frame_pc (frame));
|
||||
#endif
|
||||
if (lib)
|
||||
{
|
||||
|
@ -2807,7 +2807,7 @@ clear_symtab_users (void)
|
||||
|
||||
clear_displays ();
|
||||
breakpoint_re_set ();
|
||||
set_default_breakpoint (0, 0, 0, 0);
|
||||
set_default_breakpoint (0, NULL, 0, 0, 0);
|
||||
clear_pc_function_cache ();
|
||||
observer_notify_new_objfile (NULL);
|
||||
|
||||
|
@ -130,10 +130,12 @@ free_symtab (struct symtab *s)
|
||||
void
|
||||
print_symbol_bcache_statistics (void)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
struct objfile *objfile;
|
||||
|
||||
immediate_quit++;
|
||||
ALL_OBJFILES (objfile)
|
||||
ALL_PSPACES (pspace)
|
||||
ALL_PSPACE_OBJFILES (pspace, objfile)
|
||||
{
|
||||
printf_filtered (_("Byte cache statistics for '%s':\n"), objfile->name);
|
||||
print_bcache_statistics (objfile->psymbol_cache, "partial symbol cache");
|
||||
@ -145,13 +147,15 @@ print_symbol_bcache_statistics (void)
|
||||
void
|
||||
print_objfile_statistics (void)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
struct objfile *objfile;
|
||||
struct symtab *s;
|
||||
struct partial_symtab *ps;
|
||||
int i, linetables, blockvectors;
|
||||
|
||||
immediate_quit++;
|
||||
ALL_OBJFILES (objfile)
|
||||
ALL_PSPACES (pspace)
|
||||
ALL_PSPACE_OBJFILES (pspace, objfile)
|
||||
{
|
||||
printf_filtered (_("Statistics for '%s':\n"), objfile->name);
|
||||
if (OBJSTAT (objfile, n_stabs) > 0)
|
||||
@ -886,6 +890,7 @@ maintenance_print_msymbols (char *args, int from_tty)
|
||||
struct cleanup *cleanups;
|
||||
char *filename = DEV_TTY;
|
||||
char *symname = NULL;
|
||||
struct program_space *pspace;
|
||||
struct objfile *objfile;
|
||||
|
||||
struct stat sym_st, obj_st;
|
||||
@ -921,10 +926,11 @@ maintenance_print_msymbols (char *args, int from_tty)
|
||||
make_cleanup_ui_file_delete (outfile);
|
||||
|
||||
immediate_quit++;
|
||||
ALL_OBJFILES (objfile)
|
||||
if (symname == NULL
|
||||
|| (!stat (objfile->name, &obj_st) && sym_st.st_ino == obj_st.st_ino))
|
||||
dump_msymbols (objfile, outfile);
|
||||
ALL_PSPACES (pspace)
|
||||
ALL_PSPACE_OBJFILES (pspace, objfile)
|
||||
if (symname == NULL
|
||||
|| (!stat (objfile->name, &obj_st) && sym_st.st_ino == obj_st.st_ino))
|
||||
dump_msymbols (objfile, outfile);
|
||||
immediate_quit--;
|
||||
fprintf_filtered (outfile, "\n\n");
|
||||
do_cleanups (cleanups);
|
||||
@ -933,13 +939,15 @@ maintenance_print_msymbols (char *args, int from_tty)
|
||||
void
|
||||
maintenance_print_objfiles (char *ignore, int from_tty)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
struct objfile *objfile;
|
||||
|
||||
dont_repeat ();
|
||||
|
||||
immediate_quit++;
|
||||
ALL_OBJFILES (objfile)
|
||||
dump_objfile (objfile);
|
||||
ALL_PSPACES (pspace)
|
||||
ALL_PSPACE_OBJFILES (pspace, objfile)
|
||||
dump_objfile (objfile);
|
||||
immediate_quit--;
|
||||
}
|
||||
|
||||
@ -948,12 +956,14 @@ maintenance_print_objfiles (char *ignore, int from_tty)
|
||||
void
|
||||
maintenance_info_symtabs (char *regexp, int from_tty)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
struct objfile *objfile;
|
||||
|
||||
if (regexp)
|
||||
re_comp (regexp);
|
||||
|
||||
ALL_OBJFILES (objfile)
|
||||
ALL_PSPACES (pspace)
|
||||
ALL_PSPACE_OBJFILES (pspace, objfile)
|
||||
{
|
||||
struct symtab *symtab;
|
||||
|
||||
@ -1005,12 +1015,14 @@ maintenance_info_symtabs (char *regexp, int from_tty)
|
||||
void
|
||||
maintenance_info_psymtabs (char *regexp, int from_tty)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
struct objfile *objfile;
|
||||
|
||||
if (regexp)
|
||||
re_comp (regexp);
|
||||
|
||||
ALL_OBJFILES (objfile)
|
||||
ALL_PSPACES (pspace)
|
||||
ALL_PSPACE_OBJFILES (pspace, objfile)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_objfile_arch (objfile);
|
||||
struct partial_symtab *psymtab;
|
||||
|
64
gdb/symtab.c
64
gdb/symtab.c
@ -690,6 +690,7 @@ symbol_search_name (const struct general_symbol_info *gsymbol)
|
||||
void
|
||||
init_sal (struct symtab_and_line *sal)
|
||||
{
|
||||
sal->pspace = NULL;
|
||||
sal->symtab = 0;
|
||||
sal->section = 0;
|
||||
sal->line = 0;
|
||||
@ -1994,9 +1995,12 @@ find_pc_sect_symtab (CORE_ADDR pc, struct obj_section *section)
|
||||
struct symtab *best_s = NULL;
|
||||
struct partial_symtab *ps;
|
||||
struct objfile *objfile;
|
||||
struct program_space *pspace;
|
||||
CORE_ADDR distance = 0;
|
||||
struct minimal_symbol *msymbol;
|
||||
|
||||
pspace = current_program_space;
|
||||
|
||||
/* If we know that this is not a text address, return failure. This is
|
||||
necessary because we loop based on the block's high and low code
|
||||
addresses, which do not include the data ranges, and because
|
||||
@ -2152,6 +2156,8 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
|
||||
|
||||
init_sal (&val); /* initialize to zeroes */
|
||||
|
||||
val.pspace = current_program_space;
|
||||
|
||||
/* It's tempting to assume that, if we can't find debugging info for
|
||||
any function enclosing PC, that we shouldn't search for line
|
||||
number info, either. However, GAS can emit line number info for
|
||||
@ -2656,6 +2662,11 @@ find_function_start_sal (struct symbol *sym, int funfirstline)
|
||||
struct symtab_and_line sal;
|
||||
struct block *b, *function_block;
|
||||
|
||||
struct cleanup *old_chain;
|
||||
|
||||
old_chain = save_current_space_and_thread ();
|
||||
switch_to_program_space_and_thread (objfile->pspace);
|
||||
|
||||
pc = BLOCK_START (block);
|
||||
fixup_symbol_section (sym, objfile);
|
||||
if (funfirstline)
|
||||
@ -2707,6 +2718,7 @@ find_function_start_sal (struct symbol *sym, int funfirstline)
|
||||
}
|
||||
|
||||
sal.pc = pc;
|
||||
sal.pspace = objfile->pspace;
|
||||
|
||||
/* Check if we are now inside an inlined function. If we can,
|
||||
use the call site of the function instead. */
|
||||
@ -2727,6 +2739,7 @@ find_function_start_sal (struct symbol *sym, int funfirstline)
|
||||
sal.symtab = SYMBOL_SYMTAB (BLOCK_FUNCTION (function_block));
|
||||
}
|
||||
|
||||
do_cleanups (old_chain);
|
||||
return sal;
|
||||
}
|
||||
|
||||
@ -4560,6 +4573,7 @@ symtab_observer_executable_changed (void)
|
||||
initializing it from SYMTAB, LINENO and PC. */
|
||||
static void
|
||||
append_expanded_sal (struct symtabs_and_lines *sal,
|
||||
struct program_space *pspace,
|
||||
struct symtab *symtab,
|
||||
int lineno, CORE_ADDR pc)
|
||||
{
|
||||
@ -4567,6 +4581,7 @@ append_expanded_sal (struct symtabs_and_lines *sal,
|
||||
sizeof (sal->sals[0])
|
||||
* (sal->nelts + 1));
|
||||
init_sal (sal->sals + sal->nelts);
|
||||
sal->sals[sal->nelts].pspace = pspace;
|
||||
sal->sals[sal->nelts].symtab = symtab;
|
||||
sal->sals[sal->nelts].section = NULL;
|
||||
sal->sals[sal->nelts].end = 0;
|
||||
@ -4586,14 +4601,16 @@ append_exact_match_to_sals (char *filename, int lineno,
|
||||
struct linetable_entry **best_item,
|
||||
struct symtab **best_symtab)
|
||||
{
|
||||
struct program_space *pspace;
|
||||
struct objfile *objfile;
|
||||
struct symtab *symtab;
|
||||
int exact = 0;
|
||||
int j;
|
||||
*best_item = 0;
|
||||
*best_symtab = 0;
|
||||
|
||||
ALL_SYMTABS (objfile, symtab)
|
||||
|
||||
ALL_PSPACES (pspace)
|
||||
ALL_PSPACE_SYMTABS (pspace, objfile, symtab)
|
||||
{
|
||||
if (strcmp (filename, symtab->filename) == 0)
|
||||
{
|
||||
@ -4611,7 +4628,8 @@ append_exact_match_to_sals (char *filename, int lineno,
|
||||
if (item->line == lineno)
|
||||
{
|
||||
exact = 1;
|
||||
append_expanded_sal (ret, symtab, lineno, item->pc);
|
||||
append_expanded_sal (ret, objfile->pspace,
|
||||
symtab, lineno, item->pc);
|
||||
}
|
||||
else if (!exact && item->line > lineno
|
||||
&& (*best_item == NULL
|
||||
@ -4626,11 +4644,10 @@ append_exact_match_to_sals (char *filename, int lineno,
|
||||
return exact;
|
||||
}
|
||||
|
||||
/* Compute a set of all sals in
|
||||
the entire program that correspond to same file
|
||||
and line as SAL and return those. If there
|
||||
are several sals that belong to the same block,
|
||||
only one sal for the block is included in results. */
|
||||
/* Compute a set of all sals in all program spaces that correspond to
|
||||
same file and line as SAL and return those. If there are several
|
||||
sals that belong to the same block, only one sal for the block is
|
||||
included in results. */
|
||||
|
||||
struct symtabs_and_lines
|
||||
expand_line_sal (struct symtab_and_line sal)
|
||||
@ -4644,10 +4661,12 @@ expand_line_sal (struct symtab_and_line sal)
|
||||
int deleted = 0;
|
||||
struct block **blocks = NULL;
|
||||
int *filter;
|
||||
struct cleanup *old_chain;
|
||||
|
||||
ret.nelts = 0;
|
||||
ret.sals = NULL;
|
||||
|
||||
/* Only expand sals that represent file.c:line. */
|
||||
if (sal.symtab == NULL || sal.line == 0 || sal.pc != 0)
|
||||
{
|
||||
ret.sals = xmalloc (sizeof (struct symtab_and_line));
|
||||
@ -4657,11 +4676,14 @@ expand_line_sal (struct symtab_and_line sal)
|
||||
}
|
||||
else
|
||||
{
|
||||
struct program_space *pspace;
|
||||
struct linetable_entry *best_item = 0;
|
||||
struct symtab *best_symtab = 0;
|
||||
int exact = 0;
|
||||
char *match_filename;
|
||||
|
||||
lineno = sal.line;
|
||||
match_filename = sal.symtab->filename;
|
||||
|
||||
/* We need to find all symtabs for a file which name
|
||||
is described by sal. We cannot just directly
|
||||
@ -4674,17 +4696,23 @@ expand_line_sal (struct symtab_and_line sal)
|
||||
the right name. Then, we iterate over symtabs, knowing
|
||||
that all symtabs we're interested in are loaded. */
|
||||
|
||||
ALL_PSYMTABS (objfile, psymtab)
|
||||
old_chain = save_current_program_space ();
|
||||
ALL_PSPACES (pspace)
|
||||
ALL_PSPACE_PSYMTABS (pspace, objfile, psymtab)
|
||||
{
|
||||
if (strcmp (sal.symtab->filename,
|
||||
psymtab->filename) == 0)
|
||||
PSYMTAB_TO_SYMTAB (psymtab);
|
||||
if (strcmp (match_filename, psymtab->filename) == 0)
|
||||
{
|
||||
set_current_program_space (pspace);
|
||||
|
||||
PSYMTAB_TO_SYMTAB (psymtab);
|
||||
}
|
||||
}
|
||||
do_cleanups (old_chain);
|
||||
|
||||
/* Now search the symtab for exact matches and append them. If
|
||||
none is found, append the best_item and all its exact
|
||||
matches. */
|
||||
exact = append_exact_match_to_sals (sal.symtab->filename, lineno,
|
||||
exact = append_exact_match_to_sals (match_filename, lineno,
|
||||
&ret, &best_item, &best_symtab);
|
||||
if (!exact && best_item)
|
||||
append_exact_match_to_sals (best_symtab->filename, best_item->line,
|
||||
@ -4700,13 +4728,21 @@ expand_line_sal (struct symtab_and_line sal)
|
||||
blocks -- for each PC found above we see if there are other PCs
|
||||
that are in the same block. If yes, the other PCs are filtered out. */
|
||||
|
||||
old_chain = save_current_program_space ();
|
||||
filter = alloca (ret.nelts * sizeof (int));
|
||||
blocks = alloca (ret.nelts * sizeof (struct block *));
|
||||
for (i = 0; i < ret.nelts; ++i)
|
||||
{
|
||||
struct blockvector *bl;
|
||||
struct block *b;
|
||||
|
||||
set_current_program_space (ret.sals[i].pspace);
|
||||
|
||||
filter[i] = 1;
|
||||
blocks[i] = block_for_pc (ret.sals[i].pc);
|
||||
blocks[i] = block_for_pc_sect (ret.sals[i].pc, ret.sals[i].section);
|
||||
|
||||
}
|
||||
do_cleanups (old_chain);
|
||||
|
||||
for (i = 0; i < ret.nelts; ++i)
|
||||
if (blocks[i] != NULL)
|
||||
|
@ -32,6 +32,7 @@ struct block;
|
||||
struct blockvector;
|
||||
struct axs_value;
|
||||
struct agent_expr;
|
||||
struct program_space;
|
||||
|
||||
/* Some of the structures in this file are space critical.
|
||||
The space-critical structures are:
|
||||
@ -823,6 +824,7 @@ struct symtab
|
||||
|
||||
#define BLOCKVECTOR(symtab) (symtab)->blockvector
|
||||
#define LINETABLE(symtab) (symtab)->linetable
|
||||
#define SYMTAB_PSPACE(symtab) (symtab)->objfile->pspace
|
||||
|
||||
|
||||
/* Each source file that has not been fully read in is represented by
|
||||
@ -1170,6 +1172,9 @@ extern void msymbols_sort (struct objfile *objfile);
|
||||
|
||||
struct symtab_and_line
|
||||
{
|
||||
/* The program space of this sal. */
|
||||
struct program_space *pspace;
|
||||
|
||||
struct symtab *symtab;
|
||||
struct obj_section *section;
|
||||
/* Line number. Line numbers start at 1 and proceed through symtab->nlines.
|
||||
|
30
gdb/target.c
30
gdb/target.c
@ -1267,7 +1267,10 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object,
|
||||
return -1;
|
||||
}
|
||||
|
||||
inf = find_inferior_pid (ptid_get_pid (inferior_ptid));
|
||||
if (!ptid_equal (inferior_ptid, null_ptid))
|
||||
inf = find_inferior_pid (ptid_get_pid (inferior_ptid));
|
||||
else
|
||||
inf = NULL;
|
||||
|
||||
if (inf != NULL
|
||||
&& (region->attrib.cache
|
||||
@ -2046,7 +2049,7 @@ target_detach (char *args, int from_tty)
|
||||
else
|
||||
/* If we're in breakpoints-always-inserted mode, have to remove
|
||||
them before detaching. */
|
||||
remove_breakpoints ();
|
||||
remove_breakpoints_pid (PIDGET (inferior_ptid));
|
||||
|
||||
for (t = current_target.beneath; t != NULL; t = t->beneath)
|
||||
{
|
||||
@ -2547,6 +2550,27 @@ target_get_osdata (const char *type)
|
||||
return target_read_stralloc (t, TARGET_OBJECT_OSDATA, type);
|
||||
}
|
||||
|
||||
/* Determine the current address space of thread PTID. */
|
||||
|
||||
struct address_space *
|
||||
target_thread_address_space (ptid_t ptid)
|
||||
{
|
||||
struct inferior *inf;
|
||||
|
||||
/* For now, assume frame chains and inferiors only see one address
|
||||
space. */
|
||||
|
||||
/* Fall-back to the "main" address space of the inferior. */
|
||||
inf = find_inferior_pid (ptid_get_pid (ptid));
|
||||
|
||||
if (inf == NULL || inf->aspace == NULL)
|
||||
internal_error (__FILE__, __LINE__, "\
|
||||
Can't determine the current address space of thread %s\n",
|
||||
target_pid_to_str (ptid));
|
||||
|
||||
return inf->aspace;
|
||||
}
|
||||
|
||||
static int
|
||||
default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
|
||||
{
|
||||
@ -2658,7 +2682,7 @@ generic_mourn_inferior (void)
|
||||
if (!ptid_equal (ptid, null_ptid))
|
||||
{
|
||||
int pid = ptid_get_pid (ptid);
|
||||
delete_inferior (pid);
|
||||
exit_inferior (pid);
|
||||
}
|
||||
|
||||
breakpoint_init_inferior (inf_exited);
|
||||
|
13
gdb/target.h
13
gdb/target.h
@ -110,6 +110,15 @@ enum target_waitkind
|
||||
|
||||
TARGET_WAITKIND_EXECD,
|
||||
|
||||
/* The program had previously vforked, and now the child is done
|
||||
with the shared memory region, because it exec'ed or exited.
|
||||
Note that the event is reported to the vfork parent. This is
|
||||
only used if GDB did not stay attached to the vfork child,
|
||||
otherwise, a TARGET_WAITKIND_EXECD or
|
||||
TARGET_WAITKIND_EXIT|SIGNALLED event associated with the child
|
||||
has the same effect. */
|
||||
TARGET_WAITKIND_VFORK_DONE,
|
||||
|
||||
/* The program has entered or returned from a system call. On
|
||||
HP-UX, this is used in the hardware watchpoint implementation.
|
||||
The syscall's unique integer ID number is in value.syscall_id */
|
||||
@ -685,6 +694,10 @@ extern void target_store_registers (struct regcache *regcache, int regs);
|
||||
#define target_prepare_to_store(regcache) \
|
||||
(*current_target.to_prepare_to_store) (regcache)
|
||||
|
||||
/* Determine current address space of thread PTID. */
|
||||
|
||||
struct address_space *target_thread_address_space (ptid_t);
|
||||
|
||||
/* Returns true if this target can debug multiple processes
|
||||
simultaneously. */
|
||||
|
||||
|
@ -1,3 +1,25 @@
|
||||
2009-10-19 Pedro Alves <pedro@codesourcery.com>
|
||||
Stan Shebs <stan@codesourcery.com>
|
||||
|
||||
* gdb.base/foll-vfork.exp: Adjust to spell out "follow-fork".
|
||||
* gdb.base/foll-exec.exp: Adjust to expect a process id before
|
||||
"Executing new program".
|
||||
* gdb.base/foll-fork.exp: Adjust to spell out "follow-fork".
|
||||
* gdb.base/multi-forks.exp: Ditto. Adjust to the inferior being
|
||||
left listed after having been killed.
|
||||
* gdb.base/attach.exp: Adjust to spell out "symbol-file".
|
||||
* gdb.base/maint.exp: Adjust test.
|
||||
|
||||
* Makefile.in (ALL_SUBDIRS): Add gdb.multi.
|
||||
* gdb.multi/Makefile.in: New.
|
||||
* gdb.multi/base.exp: New.
|
||||
* gdb.multi/goodbye.c: New.
|
||||
* gdb.multi/hangout.c: New.
|
||||
* gdb.multi/hello.c: New.
|
||||
* gdb.multi/bkpt-multi-exec.c: New.
|
||||
* gdb.multi/bkpt-multi-exec.exp: New.
|
||||
* gdb.multi/crashme.c: New.
|
||||
|
||||
2009-10-13 Tristan Gingold <gingold@adacore.com>
|
||||
|
||||
* gdb.base/sepdebug.exp: Check debug info are found.
|
||||
|
@ -35,7 +35,7 @@ SUBDIRS = @subdirs@
|
||||
RPATH_ENVVAR = @RPATH_ENVVAR@
|
||||
ALL_SUBDIRS = gdb.ada gdb.arch gdb.asm gdb.base gdb.cp gdb.disasm \
|
||||
gdb.dwarf2 \
|
||||
gdb.fortran gdb.server gdb.java gdb.mi \
|
||||
gdb.fortran gdb.server gdb.java gdb.mi gdb.multi \
|
||||
gdb.objc gdb.opt gdb.pascal gdb.python gdb.threads gdb.trace \
|
||||
gdb.xml \
|
||||
$(SUBDIRS)
|
||||
|
@ -291,7 +291,7 @@ proc do_attach_tests {} {
|
||||
# Explicitly flush out any knowledge of the previous attachment.
|
||||
|
||||
set test "before attach3, flush symbols"
|
||||
gdb_test_multiple "symbol" "$test" {
|
||||
gdb_test_multiple "symbol-file" "$test" {
|
||||
-re "Discard symbol table from.*y or n. $" {
|
||||
gdb_test "y" "No symbol file now." \
|
||||
"$test"
|
||||
|
@ -152,7 +152,7 @@ proc do_exec_tests {} {
|
||||
#
|
||||
send_gdb "next\n"
|
||||
gdb_expect {
|
||||
-re "Executing new program: .*${testfile2}.*${srcfile2}:23.*int local_j = argc;.*$gdb_prompt $"\
|
||||
-re ".*xecuting new program: .*${testfile2}.*${srcfile2}:23.*int local_j = argc;.*$gdb_prompt $"\
|
||||
{pass "step through execlp call"}
|
||||
-re "$gdb_prompt $" {fail "step through execlp call"}
|
||||
timeout {fail "(timeout) step through execlp call"}
|
||||
@ -230,7 +230,7 @@ proc do_exec_tests {} {
|
||||
setup_xfail hppa2.0w-hp-hpux* CLLbs16760
|
||||
send_gdb "continue\n"
|
||||
gdb_expect {
|
||||
-re ".*Executing new program:.*${testfile2}.*Catchpoint .*(exec\'d .*${testfile2}).*in .*$gdb_prompt $"\
|
||||
-re ".*xecuting new program:.*${testfile2}.*Catchpoint .*(exec\'d .*${testfile2}).*in .*$gdb_prompt $"\
|
||||
{pass "hit catch exec"}
|
||||
-re "$gdb_prompt $" {fail "hit catch exec"}
|
||||
timeout {fail "(timeout) hit catch exec"}
|
||||
@ -299,7 +299,7 @@ proc do_exec_tests {} {
|
||||
#
|
||||
send_gdb "next 2\n"
|
||||
gdb_expect {
|
||||
-re "Executing new program: .*${testfile2}.*${srcfile2}:23.*int local_j = argc;.*$gdb_prompt $"\
|
||||
-re ".*xecuting new program: .*${testfile2}.*${srcfile2}:23.*int local_j = argc;.*$gdb_prompt $"\
|
||||
{pass "step through execl call"}
|
||||
-re "$gdb_prompt $" {fail "step through execl call"}
|
||||
timeout {fail "(timeout) step through execl call"}
|
||||
@ -353,7 +353,7 @@ proc do_exec_tests {} {
|
||||
}
|
||||
send_gdb "next\n"
|
||||
gdb_expect {
|
||||
-re "Executing new program: .*${testfile2}.*${srcfile2}:23.*int local_j = argc;.*$gdb_prompt $"\
|
||||
-re ".*xecuting new program: .*${testfile2}.*${srcfile2}:23.*int local_j = argc;.*$gdb_prompt $"\
|
||||
{pass "step through execv call"}
|
||||
-re "$gdb_prompt $" {fail "step through execv call"}
|
||||
timeout {fail "(timeout) step through execv call"}
|
||||
@ -394,7 +394,7 @@ proc do_exec_tests {} {
|
||||
#
|
||||
send_gdb "continue\n"
|
||||
gdb_expect {
|
||||
-re "Executing new program: .*${testfile2}.*${srcfile2}:23.*int local_j = argc;.*$gdb_prompt $"\
|
||||
-re ".*xecuting new program: .*${testfile2}.*${srcfile2}:23.*int local_j = argc;.*$gdb_prompt $"\
|
||||
{pass "continue through exec"}
|
||||
-re "$gdb_prompt $" {fail "continue through exec"}
|
||||
timeout {fail "(timeout) continue through exec"}
|
||||
|
@ -64,7 +64,7 @@ proc check_fork_catchpoints {} {
|
||||
proc default_fork_parent_follow {} {
|
||||
global gdb_prompt
|
||||
|
||||
send_gdb "show follow\n"
|
||||
send_gdb "show follow-fork\n"
|
||||
gdb_expect {
|
||||
-re "Debugger response to a program call of fork or vfork is \"parent\"..*$gdb_prompt $"\
|
||||
{pass "default show parent follow, no catchpoints"}
|
||||
@ -88,12 +88,12 @@ proc default_fork_parent_follow {} {
|
||||
proc explicit_fork_parent_follow {} {
|
||||
global gdb_prompt
|
||||
|
||||
send_gdb "set follow parent\n"
|
||||
send_gdb "set follow-fork parent\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow parent"}
|
||||
timeout {fail "(timeout) set follow parent"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork parent"}
|
||||
timeout {fail "(timeout) set follow-fork parent"}
|
||||
}
|
||||
send_gdb "show follow\n"
|
||||
send_gdb "show follow-fork\n"
|
||||
gdb_expect {
|
||||
-re "Debugger response to a program call of fork or vfork is \"parent\"..*$gdb_prompt $"\
|
||||
{pass "explicit show parent follow, no catchpoints"}
|
||||
@ -117,12 +117,12 @@ proc explicit_fork_parent_follow {} {
|
||||
proc explicit_fork_child_follow {} {
|
||||
global gdb_prompt
|
||||
|
||||
send_gdb "set follow child\n"
|
||||
send_gdb "set follow-fork child\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow child"}
|
||||
timeout {fail "(timeout) set follow child"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork child"}
|
||||
timeout {fail "(timeout) set follow-fork child"}
|
||||
}
|
||||
send_gdb "show follow\n"
|
||||
send_gdb "show follow-fork\n"
|
||||
gdb_expect {
|
||||
-re "Debugger response to a program call of fork or vfork is \"child\"..*$gdb_prompt $"\
|
||||
{pass "explicit show child follow, no catchpoints"}
|
||||
@ -131,7 +131,7 @@ proc explicit_fork_child_follow {} {
|
||||
}
|
||||
send_gdb "next 2\n"
|
||||
gdb_expect {
|
||||
-re "Attaching after fork to.*$gdb_prompt $"\
|
||||
-re "Attaching after.* fork to.*$gdb_prompt $"\
|
||||
{pass "explicit child follow, no catchpoints"}
|
||||
-re "$gdb_prompt $" {fail "explicit child follow, no catchpoints"}
|
||||
timeout {fail "(timeout) explicit child follow, no catchpoints"}
|
||||
@ -185,24 +185,24 @@ proc catch_fork_child_follow {} {
|
||||
}
|
||||
}
|
||||
|
||||
send_gdb "set follow child\n"
|
||||
send_gdb "set follow-fork child\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow child"}
|
||||
timeout {fail "(timeout) set follow child"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork child"}
|
||||
timeout {fail "(timeout) set follow-fork child"}
|
||||
}
|
||||
send_gdb "tbreak ${srcfile}:$bp_after_fork\n"
|
||||
gdb_expect {
|
||||
-re "Temporary breakpoint.*, line $bp_after_fork.*$gdb_prompt $"\
|
||||
{pass "set follow child, tbreak"}
|
||||
-re "$gdb_prompt $" {fail "set follow child, tbreak"}
|
||||
timeout {fail "(timeout) set follow child, tbreak"}
|
||||
{pass "set follow-fork child, tbreak"}
|
||||
-re "$gdb_prompt $" {fail "set follow-fork child, tbreak"}
|
||||
timeout {fail "(timeout) set follow-fork child, tbreak"}
|
||||
}
|
||||
send_gdb "continue\n"
|
||||
gdb_expect {
|
||||
-re "Attaching after fork to.* at .*$bp_after_fork.*$gdb_prompt $"\
|
||||
{pass "set follow child, hit tbreak"}
|
||||
-re "$gdb_prompt $" {fail "set follow child, hit tbreak"}
|
||||
timeout {fail "(timeout) set follow child, hit tbreak"}
|
||||
-re "Attaching after.* fork to.* at .*$bp_after_fork.*$gdb_prompt $"\
|
||||
{pass "set follow-fork child, hit tbreak"}
|
||||
-re "$gdb_prompt $" {fail "set follow-fork child, hit tbreak"}
|
||||
timeout {fail "(timeout) set follow-fork child, hit tbreak"}
|
||||
}
|
||||
# The parent has been detached; allow time for any output it might
|
||||
# generate to arrive, so that output doesn't get confused with
|
||||
@ -215,12 +215,12 @@ proc catch_fork_child_follow {} {
|
||||
send_gdb "y\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $"\
|
||||
{pass "set follow child, cleanup"}
|
||||
timeout {fail "(timeout) set follow child, cleanup"}
|
||||
{pass "set follow-fork child, cleanup"}
|
||||
timeout {fail "(timeout) set follow-fork child, cleanup"}
|
||||
}
|
||||
}
|
||||
-re "$gdb_prompt $" {fail "set follow child, cleanup"}
|
||||
timeout {fail "(timeout) set follow child, cleanup"}
|
||||
-re "$gdb_prompt $" {fail "set follow-fork child, cleanup"}
|
||||
timeout {fail "(timeout) set follow-fork child, cleanup"}
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ proc catch_fork_unpatch_child {} {
|
||||
"Breakpoint .*file .*$srcfile, line .*" \
|
||||
"unpatch child, breakpoint at exit call"
|
||||
|
||||
gdb_test "set follow child" "" "unpatch child, set follow child"
|
||||
gdb_test "set follow-fork child" "" "unpatch child, set follow-fork child"
|
||||
|
||||
set test "unpatch child, unpatched parent breakpoints from child"
|
||||
gdb_test_multiple "continue" $test {
|
||||
@ -297,24 +297,24 @@ proc tcatch_fork_parent_follow {} {
|
||||
-re "$gdb_prompt $" {fail "explicit parent follow, tcatch fork"}
|
||||
timeout {fail "(timeout) explicit parent follow, tcatch fork"}
|
||||
}
|
||||
send_gdb "set follow parent\n"
|
||||
send_gdb "set follow-fork parent\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow parent"}
|
||||
timeout {fail "(timeout) set follow parent"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork parent"}
|
||||
timeout {fail "(timeout) set follow-fork parent"}
|
||||
}
|
||||
send_gdb "tbreak ${srcfile}:$bp_after_fork\n"
|
||||
gdb_expect {
|
||||
-re "Temporary breakpoint.*, line $bp_after_fork.*$gdb_prompt $"\
|
||||
{pass "set follow parent, tbreak"}
|
||||
-re "$gdb_prompt $" {fail "set follow parent, tbreak"}
|
||||
timeout {fail "(timeout) set follow child, tbreak"}
|
||||
{pass "set follow-fork parent, tbreak"}
|
||||
-re "$gdb_prompt $" {fail "set follow-fork parent, tbreak"}
|
||||
timeout {fail "(timeout) set follow-fork child, tbreak"}
|
||||
}
|
||||
send_gdb "continue\n"
|
||||
gdb_expect {
|
||||
-re ".*Detaching after fork from.* at .*$bp_after_fork.*$gdb_prompt $"\
|
||||
{pass "set follow parent, hit tbreak"}
|
||||
-re "$gdb_prompt $" {fail "set follow parent, hit tbreak"}
|
||||
timeout {fail "(timeout) set follow parent, hit tbreak"}
|
||||
{pass "set follow-fork parent, hit tbreak"}
|
||||
-re "$gdb_prompt $" {fail "set follow-fork parent, hit tbreak"}
|
||||
timeout {fail "(timeout) set follow-fork parent, hit tbreak"}
|
||||
}
|
||||
# The child has been detached; allow time for any output it might
|
||||
# generate to arrive, so that output doesn't get confused with
|
||||
@ -327,12 +327,12 @@ proc tcatch_fork_parent_follow {} {
|
||||
send_gdb "y\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $"\
|
||||
{pass "set follow parent, cleanup"}
|
||||
timeout {fail "(timeout) set follow parent, cleanup"}
|
||||
{pass "set follow-fork parent, cleanup"}
|
||||
timeout {fail "(timeout) set follow-fork parent, cleanup"}
|
||||
}
|
||||
}
|
||||
-re "$gdb_prompt $" {fail "set follow parent, cleanup"}
|
||||
timeout {fail "(timeout) set follow parent, cleanup"}
|
||||
-re "$gdb_prompt $" {fail "set follow-fork parent, cleanup"}
|
||||
timeout {fail "(timeout) set follow-fork parent, cleanup"}
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,35 +349,35 @@ A fork or vfork creates a new process. follow-fork-mode can be:.*
|
||||
.*child - the new process is debugged after a fork.*
|
||||
The unfollowed process will continue to run..*
|
||||
By default, the debugger will follow the parent process..*$gdb_prompt $"\
|
||||
{ pass "help set follow" }
|
||||
{ pass "help set follow-fork" }
|
||||
-re "$gdb_prompt $" { fail "help set follow" }
|
||||
timeout { fail "(timeout) help set follow" }
|
||||
timeout { fail "(timeout) help set follow-fork" }
|
||||
}
|
||||
|
||||
# Verify that we can set follow-fork-mode, using an abbreviation
|
||||
# for both the flag and its value.
|
||||
#
|
||||
send_gdb "set follow ch\n"
|
||||
send_gdb "show fol\n"
|
||||
send_gdb "set follow-fork ch\n"
|
||||
send_gdb "show follow-fork\n"
|
||||
gdb_expect {
|
||||
-re "Debugger response to a program call of fork or vfork is \"child\".*$gdb_prompt $"\
|
||||
{pass "set follow, using abbreviations"}
|
||||
timeout {fail "(timeout) set follow, using abbreviations"}
|
||||
{pass "set follow-fork, using abbreviations"}
|
||||
timeout {fail "(timeout) set follow-fork, using abbreviations"}
|
||||
}
|
||||
|
||||
# Verify that we cannot set follow-fork-mode to nonsense.
|
||||
#
|
||||
send_gdb "set follow chork\n"
|
||||
send_gdb "set follow-fork chork\n"
|
||||
gdb_expect {
|
||||
-re "Undefined item: \"chork\".*$gdb_prompt $"\
|
||||
{pass "set follow to nonsense is prohibited"}
|
||||
-re "$gdb_prompt $" {fail "set follow to nonsense is prohibited"}
|
||||
timeout {fail "(timeout) set follow to nonsense is prohibited"}
|
||||
{pass "set follow-fork to nonsense is prohibited"}
|
||||
-re "$gdb_prompt $" {fail "set follow-fork to nonsense is prohibited"}
|
||||
timeout {fail "(timeout) set follow-fork to nonsense is prohibited"}
|
||||
}
|
||||
send_gdb "set follow parent\n"
|
||||
send_gdb "set follow-fork parent\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow to nonsense is prohibited (reset parent)"}
|
||||
timeout {fail "set follow to nonsense is prohibited (reset parent)"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork to nonsense is prohibited (reset parent)"}
|
||||
timeout {fail "set follow-fork to nonsense is prohibited (reset parent)"}
|
||||
}
|
||||
|
||||
# Check that fork catchpoints are supported, as an indicator for whether
|
||||
|
@ -93,10 +93,10 @@ proc check_vfork_catchpoints {} {
|
||||
proc vfork_parent_follow_through_step {} {
|
||||
global gdb_prompt
|
||||
|
||||
send_gdb "set follow parent\n"
|
||||
send_gdb "set follow-fork parent\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow parent, vfork through step"}
|
||||
timeout {fail "set follow parent, vfork through step"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork parent, vfork through step"}
|
||||
timeout {fail "set follow-fork parent, vfork through step"}
|
||||
}
|
||||
send_gdb "next\n"
|
||||
gdb_expect {
|
||||
@ -116,10 +116,10 @@ proc vfork_parent_follow_to_bp {} {
|
||||
global gdb_prompt
|
||||
global srcfile
|
||||
|
||||
send_gdb "set follow parent\n"
|
||||
send_gdb "set follow-fork parent\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow parent, vfork to bp"}
|
||||
timeout {fail "set follow parent, vfork to bp"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork parent, vfork to bp"}
|
||||
timeout {fail "set follow-fork parent, vfork to bp"}
|
||||
}
|
||||
send_gdb "break ${srcfile}:18\n"
|
||||
gdb_expect {
|
||||
@ -144,14 +144,14 @@ proc vfork_and_exec_child_follow_to_main_bp {} {
|
||||
global gdb_prompt
|
||||
global binfile
|
||||
|
||||
send_gdb "set follow child\n"
|
||||
send_gdb "set follow-fork child\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow child, vfork and exec to main bp"}
|
||||
timeout {fail "set follow child, vfork and exec to main bp"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork child, vfork and exec to main bp"}
|
||||
timeout {fail "set follow-fork child, vfork and exec to main bp"}
|
||||
}
|
||||
send_gdb "continue\n"
|
||||
gdb_expect {
|
||||
-re "Attaching after fork to.*Executing new program.*Breakpoint.*vforked-prog.c:9.*$gdb_prompt "\
|
||||
-re "Attaching after.* vfork to.*xecuting new program.*Breakpoint.*vforked-prog.c:9.*$gdb_prompt "\
|
||||
{pass "vfork and exec child follow, to main bp"}
|
||||
-re "$gdb_prompt $" {fail "vfork and exec child follow, to main bp"}
|
||||
timeout {fail "(timeout) vfork and exec child follow, to main bp" }
|
||||
@ -193,7 +193,7 @@ proc vfork_and_exec_child_follow_through_step {} {
|
||||
# This test cannot be performed prior to HP-UX 10.30, because ptrace-based
|
||||
# debugging of a vforking program basically doesn't allow the child to do
|
||||
# things like hit a breakpoint between a vfork and exec. This means that
|
||||
# saying "set follow child; next" at a vfork() call won't work, because
|
||||
# saying "set follow-fork child; next" at a vfork() call won't work, because
|
||||
# the implementation of "next" sets a "step resume" breakpoint at the
|
||||
# return from the vfork(), which the child will hit on its way to exec'ing.
|
||||
#
|
||||
@ -202,10 +202,10 @@ proc vfork_and_exec_child_follow_through_step {} {
|
||||
return 0
|
||||
}
|
||||
|
||||
send_gdb "set follow child\n"
|
||||
send_gdb "set follow-fork child\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow child, vfork and exec through step"}
|
||||
timeout {fail "set follow child, vfork and exec through step"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork child, vfork and exec through step"}
|
||||
timeout {fail "set follow-fork child, vfork and exec through step"}
|
||||
}
|
||||
send_gdb "next\n"
|
||||
gdb_expect {
|
||||
@ -248,10 +248,10 @@ proc tcatch_vfork_then_parent_follow {} {
|
||||
global gdb_prompt
|
||||
global srcfile
|
||||
|
||||
send_gdb "set follow parent\n"
|
||||
send_gdb "set follow-fork parent\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow parent, tcatch vfork"}
|
||||
timeout {fail "set follow parent, tcatch vfork"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork parent, tcatch vfork"}
|
||||
timeout {fail "set follow-fork parent, tcatch vfork"}
|
||||
}
|
||||
send_gdb "tcatch vfork\n"
|
||||
gdb_expect {
|
||||
@ -294,10 +294,10 @@ proc tcatch_vfork_then_child_follow {} {
|
||||
global srcfile
|
||||
global srcfile2
|
||||
|
||||
send_gdb "set follow child\n"
|
||||
send_gdb "set follow-fork child\n"
|
||||
gdb_expect {
|
||||
-re "$gdb_prompt $" {pass "set follow child, tcatch vfork"}
|
||||
timeout {fail "set follow child, tcatch vfork"}
|
||||
-re "$gdb_prompt $" {pass "set follow-fork child, tcatch vfork"}
|
||||
timeout {fail "set follow-fork child, tcatch vfork"}
|
||||
}
|
||||
send_gdb "tcatch vfork\n"
|
||||
gdb_expect {
|
||||
|
@ -478,9 +478,9 @@ set bp_location6 [gdb_get_line_number "set breakpoint 6 here"]
|
||||
|
||||
send_gdb "maint info breakpoints\n"
|
||||
gdb_expect {
|
||||
-re "Num\[ \t\]+Type\[ \t\]+Disp\[ \t\]+Enb\[ \t\]+Address\[ \t\]+What\r\n1\[ \t\]+breakpoint\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex\[ \t\]+in main at.*break.c:$bp_location6\r\n\[ \t\]+breakpoint already hit 1 time\r\n.*$gdb_prompt $"\
|
||||
-re "Num\[ \t\]+Type\[ \t\]+Disp\[ \t\]+Enb\[ \t\]+Address\[ \t\]+What\r\n1\[ \t\]+breakpoint\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex\[ \t\]+in main at.*break.c:$bp_location6 inf 1\r\n\[ \t\]+breakpoint already hit 1 time\r\n.*$gdb_prompt $"\
|
||||
{ pass "maint info breakpoints" }
|
||||
-re "Num\[ \t\]+Type\[ \t\]+Disp\[ \t\]+Enb\[ \t\]+Address\[ \t\]+What\r\n1\[ \t\]+breakpoint\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex in main at.*break.c:$bp_location6\r\n\[ \t\]+breakpoint already hit 1 time\r\n-1\[ \t\]+shlib events\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex.*breakpoint already hit.*$gdb_prompt $"\
|
||||
-re "Num\[ \t\]+Type\[ \t\]+Disp\[ \t\]+Enb\[ \t\]+Address\[ \t\]+What\r\n1\[ \t\]+breakpoint\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex in main at.*break.c:$bp_location6 sspace 1\r\n\[ \t\]+breakpoint already hit 1 time\r\n-1\[ \t\]+shlib events\[ \t\]+keep\[ \t\]+y\[ \t\]+$hex.*breakpoint already hit.*$gdb_prompt $"\
|
||||
{ pass "maint info breakpoints (with shlib events)" }
|
||||
-re ".*$gdb_prompt $" { fail "maint info breakpoints" }
|
||||
timeout { fail "(timeout) maint info breakpoints" }
|
||||
|
@ -121,7 +121,7 @@ proc continue_to_exit_bp_loc {} {
|
||||
# The result should be that each of the 4 forks returns zero.
|
||||
|
||||
runto_main
|
||||
gdb_test "set follow child"
|
||||
gdb_test "set follow-fork child"
|
||||
continue_to_exit_bp_loc
|
||||
|
||||
gdb_test "print pids" "\\$.* = \\{0, 0, 0, 0\\}.*" "follow child, print pids"
|
||||
@ -130,7 +130,7 @@ gdb_test "print pids" "\\$.* = \\{0, 0, 0, 0\\}.*" "follow child, print pids"
|
||||
# Result should be that none of the 4 forks returns zero.
|
||||
|
||||
runto_main
|
||||
gdb_test "set follow parent" "" ""
|
||||
gdb_test "set follow-fork parent" "" ""
|
||||
continue_to_exit_bp_loc
|
||||
|
||||
gdb_test "print pids\[0\]==0 || pids\[1\]==0 || pids\[2\]==0 || pids\[3\]==0" \
|
||||
@ -198,26 +198,26 @@ gdb_test "detach inferior 5" "Detaching .*" "Detach 5"
|
||||
#
|
||||
|
||||
gdb_test "kill inferior 6" "" "Kill 6"
|
||||
gdb_test "info inferior 6" "Inferior ID 6 not known." "Did kill 6"
|
||||
gdb_test "info inferior 6" "<null>.*" "Did kill 6"
|
||||
gdb_test "kill inferior 7" "" "Kill 7"
|
||||
gdb_test "info inferior 7" "Inferior ID 7 not known." "Did kill 7"
|
||||
gdb_test "info inferior 7" "<null>.*" "Did kill 7"
|
||||
gdb_test "kill inferior 8" "" "Kill 8"
|
||||
gdb_test "info inferior 8" "Inferior ID 8 not known." "Did kill 8"
|
||||
gdb_test "info inferior 8" "<null>.*" "Did kill 8"
|
||||
gdb_test "kill inferior 9" "" "Kill 9"
|
||||
gdb_test "info inferior 9" "Inferior ID 9 not known." "Did kill 9"
|
||||
gdb_test "info inferior 9" "<null>.*" "Did kill 9"
|
||||
gdb_test "kill inferior 10" "" "Kill 10"
|
||||
gdb_test "info inferior 10" "Inferior ID 10 not known." "Did kill 10"
|
||||
gdb_test "info inferior 10" "<null>.*" "Did kill 10"
|
||||
gdb_test "kill inferior 11" "" "Kill 11"
|
||||
gdb_test "info inferior 11" "Inferior ID 11 not known." "Did kill 11"
|
||||
gdb_test "info inferior 11" "<null>.*" "Did kill 11"
|
||||
gdb_test "kill inferior 12" "" "Kill 12"
|
||||
gdb_test "info inferior 12" "Inferior ID 12 not known." "Did kill 12"
|
||||
gdb_test "info inferior 12" "<null>.*" "Did kill 12"
|
||||
gdb_test "kill inferior 13" "" "Kill 13"
|
||||
gdb_test "info inferior 13" "Inferior ID 13 not known." "Did kill 13"
|
||||
gdb_test "info inferior 13" "<null>.*" "Did kill 13"
|
||||
gdb_test "kill inferior 14" "" "Kill 14"
|
||||
gdb_test "info inferior 14" "Inferior ID 14 not known." "Did kill 14"
|
||||
gdb_test "info inferior 14" "<null>.*" "Did kill 14"
|
||||
gdb_test "kill inferior 15" "" "Kill 15"
|
||||
gdb_test "info inferior 15" "Inferior ID 15 not known." "Did kill 15"
|
||||
gdb_test "info inferior 15" "<null>.*" "Did kill 15"
|
||||
gdb_test "kill inferior 16" "" "Kill 16"
|
||||
gdb_test "info inferior 16" "Inferior ID 16 not known." "Did kill 16"
|
||||
gdb_test "info inferior 16" "<null>.*" "Did kill 16"
|
||||
|
||||
return 0
|
||||
|
40
gdb/thread.c
40
gdb/thread.c
@ -440,6 +440,24 @@ any_thread_of_process (int pid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct thread_info *
|
||||
any_live_thread_of_process (int pid)
|
||||
{
|
||||
struct thread_info *tp;
|
||||
struct thread_info *tp_running = NULL;
|
||||
|
||||
for (tp = thread_list; tp; tp = tp->next)
|
||||
if (ptid_get_pid (tp->ptid) == pid)
|
||||
{
|
||||
if (tp->state_ == THREAD_STOPPED)
|
||||
return tp;
|
||||
else if (tp->state_ == THREAD_RUNNING)
|
||||
tp_running = tp;
|
||||
}
|
||||
|
||||
return tp_running;
|
||||
}
|
||||
|
||||
/* Print a list of thread ids currently known, and the total number of
|
||||
threads. To be used from within catch_errors. */
|
||||
static int
|
||||
@ -845,6 +863,19 @@ info_threads_command (char *arg, int from_tty)
|
||||
void
|
||||
switch_to_thread (ptid_t ptid)
|
||||
{
|
||||
/* Switch the program space as well, if we can infer it from the now
|
||||
current thread. Otherwise, it's up to the caller to select the
|
||||
space it wants. */
|
||||
if (!ptid_equal (ptid, null_ptid))
|
||||
{
|
||||
struct inferior *inf;
|
||||
|
||||
inf = find_inferior_pid (ptid_get_pid (ptid));
|
||||
gdb_assert (inf != NULL);
|
||||
set_current_program_space (inf->pspace);
|
||||
set_current_inferior (inf);
|
||||
}
|
||||
|
||||
if (ptid_equal (ptid, inferior_ptid))
|
||||
return;
|
||||
|
||||
@ -909,7 +940,7 @@ restore_selected_frame (struct frame_id a_frame_id, int frame_level)
|
||||
select_frame (get_current_frame ());
|
||||
|
||||
/* Warn the user. */
|
||||
if (!ui_out_is_mi_like_p (uiout))
|
||||
if (frame_level > 0 && !ui_out_is_mi_like_p (uiout))
|
||||
{
|
||||
warning (_("\
|
||||
Couldn't restore frame #%d in current thread, at reparsed frame #0\n"),
|
||||
@ -927,6 +958,7 @@ struct current_thread_cleanup
|
||||
struct frame_id selected_frame_id;
|
||||
int selected_frame_level;
|
||||
int was_stopped;
|
||||
int inf_id;
|
||||
};
|
||||
|
||||
static void
|
||||
@ -945,7 +977,10 @@ do_restore_current_thread_cleanup (void *arg)
|
||||
&& find_inferior_pid (ptid_get_pid (tp->ptid)) != NULL)
|
||||
restore_current_thread (old->inferior_ptid);
|
||||
else
|
||||
restore_current_thread (null_ptid);
|
||||
{
|
||||
restore_current_thread (null_ptid);
|
||||
set_current_inferior (find_inferior_id (old->inf_id));
|
||||
}
|
||||
|
||||
/* The running state of the originally selected thread may have
|
||||
changed, so we have to recheck it here. */
|
||||
@ -979,6 +1014,7 @@ make_cleanup_restore_current_thread (void)
|
||||
|
||||
old = xmalloc (sizeof (struct current_thread_cleanup));
|
||||
old->inferior_ptid = inferior_ptid;
|
||||
old->inf_id = current_inferior ()->num;
|
||||
|
||||
if (!ptid_equal (inferior_ptid, null_ptid))
|
||||
{
|
||||
|
13
gdb/top.c
13
gdb/top.c
@ -1187,6 +1187,9 @@ kill_or_detach (struct inferior *inf, void *args)
|
||||
struct qt_args *qt = args;
|
||||
struct thread_info *thread;
|
||||
|
||||
if (inf->pid == 0)
|
||||
return 0;
|
||||
|
||||
thread = any_thread_of_process (inf->pid);
|
||||
if (thread != NULL)
|
||||
{
|
||||
@ -1214,6 +1217,9 @@ print_inferior_quit_action (struct inferior *inf, void *arg)
|
||||
{
|
||||
struct ui_file *stb = arg;
|
||||
|
||||
if (inf->pid == 0)
|
||||
return 0;
|
||||
|
||||
if (inf->attach_flag)
|
||||
fprintf_filtered (stb,
|
||||
_("\tInferior %d [%s] will be detached.\n"), inf->num,
|
||||
@ -1721,6 +1727,13 @@ gdb_init (char *argv0)
|
||||
initialize_targets (); /* Setup target_terminal macros for utils.c */
|
||||
initialize_utils (); /* Make errors and warnings possible */
|
||||
initialize_all_files ();
|
||||
/* This creates the current_program_space. Do this after all the
|
||||
_initialize_foo routines have had a chance to install their
|
||||
per-sspace data keys. Also do this before
|
||||
initialize_current_architecture is called, because it accesses
|
||||
exec_bfd of the current program space. */
|
||||
initialize_progspace ();
|
||||
initialize_inferiors ();
|
||||
initialize_current_architecture ();
|
||||
init_cli_cmds();
|
||||
init_main (); /* But that omits this file! Do it now */
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "tui/tui-stack.h"
|
||||
#include "tui/tui-file.h"
|
||||
#include "tui/tui-disasm.h"
|
||||
#include "progspace.h"
|
||||
|
||||
#include "gdb_curses.h"
|
||||
|
||||
@ -259,7 +260,8 @@ tui_set_disassem_content (struct gdbarch *gdbarch, CORE_ADDR pc)
|
||||
|
||||
/* See whether there is a breakpoint installed. */
|
||||
src->has_break = (!src->is_exec_point
|
||||
&& breakpoint_here_p (pc) != no_breakpoint_here);
|
||||
&& breakpoint_here_p (current_program_space->aspace, pc)
|
||||
!= no_breakpoint_here);
|
||||
|
||||
xfree (asm_lines[i].addr_string);
|
||||
xfree (asm_lines[i].insn);
|
||||
|
@ -1585,7 +1585,8 @@ do_initial_windows_stuff (struct target_ops *ops, DWORD pid, int attaching)
|
||||
clear_proceed_status ();
|
||||
init_wait_for_inferior ();
|
||||
|
||||
inf = add_inferior (pid);
|
||||
inf = current_inferior ();
|
||||
inferior_appeared (inf, pid);
|
||||
inf->attach_flag = attaching;
|
||||
|
||||
/* Make the new process the current inferior, so terminal handling
|
||||
|
Loading…
Reference in New Issue
Block a user