diff options
Diffstat (limited to 'gdb')
87 files changed, 4399 insertions, 802 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1803dc5..8f324fb 100644 --- a/gdb/ChangeLog +++ b/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. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index b15f5ce..0172782 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -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/ @@ -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 diff --git a/gdb/alpha-tdep.c b/gdb/alpha-tdep.c index e8b694c..40e9c0a 100644 --- a/gdb/alpha-tdep.c +++ b/gdb/alpha-tdep.c @@ -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; } diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index b58c562..fc10a42 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -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 */ diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index dddae55..5816f2f 100644 --- a/gdb/arch-utils.h +++ b/gdb/arch-utils.h @@ -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 diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c index beb4ecf..7bd74a0 100644 --- a/gdb/arm-linux-tdep.c +++ b/gdb/arm-linux-tdep.c @@ -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; } diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 21d65f3..063e8f5 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -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; } diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 811cdfb..fe2a0cb 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -120,9 +120,15 @@ static CORE_ADDR adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr, enum bptype bptype); -static void describe_other_breakpoints (struct gdbarch *, CORE_ADDR, +static void describe_other_breakpoints (struct gdbarch *, + struct program_space *, CORE_ADDR, struct obj_section *, int); +static int breakpoint_address_match (struct address_space *aspace1, + CORE_ADDR addr1, + struct address_space *aspace2, + CORE_ADDR addr2); + static void breakpoints_info (char *, int); static void breakpoint_1 (int, int); @@ -149,6 +155,7 @@ typedef enum insertion_state_t; static int remove_breakpoint (struct bp_location *, insertion_state_t); +static int remove_breakpoint_1 (struct bp_location *, insertion_state_t); static enum print_stop_action print_it_typical (bpstat); @@ -191,7 +198,8 @@ static void tcatch_command (char *arg, int from_tty); static void ep_skip_leading_whitespace (char **s); -static int single_step_breakpoint_inserted_here_p (CORE_ADDR pc); +static int single_step_breakpoint_inserted_here_p (struct address_space *, + CORE_ADDR pc); static void free_bp_location (struct bp_location *loc); @@ -323,9 +331,6 @@ static int executing_breakpoint_commands; /* Are overlay event breakpoints enabled? */ static int overlay_events_enabled; -/* Are we executing startup code? */ -static int executing_startup; - /* Walk the following statement or block through all breakpoints. ALL_BREAKPOINTS_SAFE does so even if the statment deletes the current breakpoint. */ @@ -409,6 +414,8 @@ int default_breakpoint_valid; CORE_ADDR default_breakpoint_address; struct symtab *default_breakpoint_symtab; int default_breakpoint_line; +struct program_space *default_breakpoint_pspace; + /* *PP is a string denoting a breakpoint. Get the number of the breakpoint. Advance *PP after the string and any trailing whitespace. @@ -757,6 +764,10 @@ breakpoint_restore_shadows (gdb_byte *buf, ULONGEST memaddr, LONGEST len) continue; if (!b->inserted) continue; + if (!breakpoint_address_match (b->target_info.placed_address_space, 0, + current_program_space->aspace, 0)) + continue; + /* Addresses and length of the part of the breakpoint that we need to copy. */ bp_addr = b->target_info.placed_address; @@ -911,6 +922,7 @@ update_watchpoint (struct breakpoint *b, int reparse) struct frame_id saved_frame_id; struct bp_location *loc; bpstat bs; + struct program_space *frame_pspace; /* We don't free locations. They are stored in bp_location_chain and update_global_locations will eventually delete them and remove @@ -939,6 +951,8 @@ update_watchpoint (struct breakpoint *b, int reparse) select_frame (fi); } + frame_pspace = get_frame_program_space (get_selected_frame (NULL)); + if (within_current_scope && reparse) { char *s; @@ -1041,6 +1055,8 @@ update_watchpoint (struct breakpoint *b, int reparse) ; *tmp = loc; loc->gdbarch = get_type_arch (value_type (v)); + + loc->pspace = frame_pspace; loc->address = addr; loc->length = len; loc->watchpoint_type = type; @@ -1120,6 +1136,7 @@ insert_bp_location (struct bp_location *bpt, /* Initialize the target-specific information. */ memset (&bpt->target_info, 0, sizeof (bpt->target_info)); bpt->target_info.placed_address = bpt->address; + bpt->target_info.placed_address_space = bpt->pspace->aspace; if (bpt->loc_type == bp_loc_software_breakpoint || bpt->loc_type == bp_loc_hardware_breakpoint) @@ -1238,7 +1255,7 @@ Note: automatically using hardware breakpoints for read-only addresses.\n")); if (val) { /* Can't set the breakpoint. */ - if (solib_name_from_address (bpt->address)) + if (solib_name_from_address (bpt->pspace, bpt->address)) { /* See also: disable_breakpoints_in_shlibs. */ val = 0; @@ -1316,6 +1333,48 @@ Note: automatically using hardware breakpoints for read-only addresses.\n")); return 0; } +/* This function is called when program space PSPACE is about to be + deleted. It takes care of updating breakpoints to not reference + PSPACE anymore. */ + +void +breakpoint_program_space_exit (struct program_space *pspace) +{ + struct breakpoint *b, *b_temp; + struct bp_location *loc, *loc_temp; + + /* Remove any breakpoint that was set through this program space. */ + ALL_BREAKPOINTS_SAFE (b, b_temp) + { + if (b->pspace == pspace) + delete_breakpoint (b); + } + + /* Breakpoints set through other program spaces could have locations + bound to PSPACE as well. Remove those. */ + ALL_BP_LOCATIONS_SAFE (loc, loc_temp) + { + struct bp_location *tmp; + + if (loc->pspace == pspace) + { + if (loc->owner->loc == loc) + loc->owner->loc = loc->next; + else + for (tmp = loc->owner->loc; tmp->next != NULL; tmp = tmp->next) + if (tmp->next == loc) + { + tmp->next = loc->next; + break; + } + } + } + + /* Now update the global location list to permanently delete the + removed locations above. */ + update_global_location_list (0); +} + /* Make sure all breakpoints are inserted in inferior. Throws exception on any error. A breakpoint that is already inserted won't be inserted @@ -1359,9 +1418,14 @@ insert_breakpoint_locations (void) /* Explicitly mark the warning -- this will only be printed if there was an error. */ fprintf_unfiltered (tmp_error_stream, "Warning:\n"); - + + save_current_space_and_thread (); + ALL_BP_LOCATIONS_SAFE (b, temp) { + struct thread_info *tp; + CORE_ADDR last_addr; + if (!should_be_inserted (b) || b->inserted) continue; @@ -1371,6 +1435,35 @@ insert_breakpoint_locations (void) && !valid_thread_id (b->owner->thread)) continue; + switch_to_program_space_and_thread (b->pspace); + + /* For targets that support global breakpoints, there's no need + to select an inferior to insert breakpoint to. In fact, even + if we aren't attached to any process yet, we should still + insert breakpoints. */ + if (!gdbarch_has_global_breakpoints (target_gdbarch) + && ptid_equal (inferior_ptid, null_ptid)) + continue; + + if (!ptid_equal (inferior_ptid, null_ptid)) + { + struct inferior *inf = current_inferior (); + if (inf->waiting_for_vfork_done) + { + /* This is set when we're attached to the parent of the + vfork, and have detached from the child. The child + is running free, and we expect it to do an exec or + exit, at which point the OS makes the parent + schedulable again (and the target reports that the + vfork is done). Until the child is done with the + shared memory region, do not insert breakpoints in + parent, otherwise the child could still trip on the + parent's breakpoints. Since the parent is blocked + anyway, it won't miss any breakpoint. */ + continue; + } + } + val = insert_bp_location (b, tmp_error_stream, &disabled_breaks, &hw_breakpoint_error); @@ -1445,6 +1538,30 @@ remove_breakpoints (void) return val; } +/* Remove breakpoints of process PID. */ + +int +remove_breakpoints_pid (int pid) +{ + struct bp_location *b; + int val; + struct inferior *inf = find_inferior_pid (pid); + + ALL_BP_LOCATIONS (b) + { + if (b->pspace != inf->pspace) + continue; + + if (b->inserted) + { + val = remove_breakpoint (b, mark_uninserted); + if (val != 0) + return val; + } + } + return 0; +} + int remove_hw_watchpoints (void) { @@ -1462,17 +1579,30 @@ remove_hw_watchpoints (void) int reattach_breakpoints (int pid) { + struct cleanup *old_chain; struct bp_location *b; int val; - struct cleanup *old_chain = save_inferior_ptid (); struct ui_file *tmp_error_stream = mem_fileopen (); int dummy1 = 0, dummy2 = 0; + struct inferior *inf; + struct thread_info *tp; + + tp = any_live_thread_of_process (pid); + if (tp == NULL) + return 1; + + inf = find_inferior_pid (pid); + old_chain = save_inferior_ptid (); + + inferior_ptid = tp->ptid; make_cleanup_ui_file_delete (tmp_error_stream); - inferior_ptid = pid_to_ptid (pid); ALL_BP_LOCATIONS (b) { + if (b->pspace != inf->pspace) + continue; + if (b->inserted) { b->inserted = 0; @@ -1502,6 +1632,7 @@ create_internal_breakpoint (struct gdbarch *gdbarch, sal.pc = address; sal.section = find_pc_overlay (sal.pc); + sal.pspace = current_program_space; b = set_raw_breakpoint (gdbarch, sal, type); b->number = internal_breakpoint_number--; @@ -1546,8 +1677,13 @@ create_overlay_event_breakpoint (char *func_name) static void create_longjmp_master_breakpoint (char *func_name) { + struct program_space *pspace; struct objfile *objfile; + struct cleanup *old_chain; + + old_chain = save_current_program_space (); + ALL_PSPACES (pspace) ALL_OBJFILES (objfile) { struct breakpoint *b; @@ -1556,6 +1692,8 @@ create_longjmp_master_breakpoint (char *func_name) if (!gdbarch_get_longjmp_target_p (get_objfile_arch (objfile))) continue; + set_current_program_space (pspace); + m = lookup_minimal_symbol_text (func_name, objfile); if (m == NULL) continue; @@ -1567,6 +1705,8 @@ create_longjmp_master_breakpoint (char *func_name) b->enable_state = bp_disabled; } update_global_location_list (1); + + do_cleanups (old_chain); } void @@ -1585,10 +1725,14 @@ update_breakpoints_after_exec (void) here instead, because there may be other attempts to delete breakpoints after detecting an exec and before reaching here. */ ALL_BP_LOCATIONS (bploc) - gdb_assert (!bploc->inserted); + if (bploc->pspace == current_program_space) + gdb_assert (!bploc->inserted); ALL_BREAKPOINTS_SAFE (b, temp) { + if (b->pspace != current_program_space) + continue; + /* Solib breakpoints must be explicitly reset after an exec(). */ if (b->type == bp_shlib_event) { @@ -1690,25 +1834,36 @@ detach_breakpoints (int pid) struct bp_location *b; int val = 0; struct cleanup *old_chain = save_inferior_ptid (); + struct inferior *inf = current_inferior (); if (pid == PIDGET (inferior_ptid)) error (_("Cannot detach breakpoints of inferior_ptid")); - /* Set inferior_ptid; remove_breakpoint uses this global. */ + /* Set inferior_ptid; remove_breakpoint_1 uses this global. */ inferior_ptid = pid_to_ptid (pid); ALL_BP_LOCATIONS (b) { + if (b->pspace != inf->pspace) + continue; + if (b->inserted) - val |= remove_breakpoint (b, mark_inserted); + val |= remove_breakpoint_1 (b, mark_inserted); } do_cleanups (old_chain); return val; } +/* Remove the breakpoint location B from the current address space. + Note that this is used to detach breakpoints from a child fork. + When we get here, the child isn't in the inferior list, and neither + do we have objects to represent its address space --- we should + *not* look at b->pspace->aspace here. */ + static int -remove_breakpoint (struct bp_location *b, insertion_state_t is) +remove_breakpoint_1 (struct bp_location *b, insertion_state_t is) { int val; + struct cleanup *old_chain; if (b->owner->enable_state == bp_permanent) /* Permanent breakpoints cannot be inserted or removed. */ @@ -1786,7 +1941,7 @@ remove_breakpoint (struct bp_location *b, insertion_state_t is) /* In some cases, we might not be able to remove a breakpoint in a shared library that has already been removed, but we have not yet processed the shlib unload event. */ - if (val && solib_name_from_address (b->address)) + if (val && solib_name_from_address (b->pspace, b->address)) val = 0; if (val) @@ -1822,6 +1977,30 @@ remove_breakpoint (struct bp_location *b, insertion_state_t is) return 0; } +static int +remove_breakpoint (struct bp_location *b, insertion_state_t is) +{ + int ret; + struct cleanup *old_chain; + + if (b->owner->enable_state == bp_permanent) + /* Permanent breakpoints cannot be inserted or removed. */ + return 0; + + /* The type of none suggests that owner is actually deleted. + This should not ever happen. */ + gdb_assert (b->owner->type != bp_none); + + old_chain = save_current_space_and_thread (); + + switch_to_program_space_and_thread (b->pspace); + + ret = remove_breakpoint_1 (b, is); + + do_cleanups (old_chain); + return ret; +} + /* Clear the "inserted" flag in all breakpoints. */ void @@ -1830,7 +2009,8 @@ mark_breakpoints_out (void) struct bp_location *bpt; ALL_BP_LOCATIONS (bpt) - bpt->inserted = 0; + if (bpt->pspace == current_program_space) + bpt->inserted = 0; } /* Clear the "inserted" flag in all breakpoints and delete any @@ -1851,6 +2031,7 @@ breakpoint_init_inferior (enum inf_context context) struct breakpoint *b, *temp; struct bp_location *bpt; int ix; + struct program_space *pspace = current_program_space; /* If breakpoint locations are shared across processes, then there's nothing to do. */ @@ -1858,11 +2039,17 @@ breakpoint_init_inferior (enum inf_context context) return; ALL_BP_LOCATIONS (bpt) - if (bpt->owner->enable_state != bp_permanent) + { + if (bpt->pspace == pspace + && bpt->owner->enable_state != bp_permanent) bpt->inserted = 0; + } ALL_BREAKPOINTS_SAFE (b, temp) { + if (b->loc && b->loc->pspace != pspace) + continue; + switch (b->type) { case bp_call_dummy: @@ -1905,6 +2092,11 @@ breakpoint_init_inferior (enum inf_context context) VEC_free (bp_location_p, moribund_locations); } +/* These functions concern about actual breakpoints inserted in the + target --- to e.g. check if we need to do decr_pc adjustment or if + we need to hop over the bkpt --- so we check for address space + match, not program space. */ + /* breakpoint_here_p (PC) returns non-zero if an enabled breakpoint exists at PC. It returns ordinary_breakpoint_here if it's an ordinary breakpoint, or permanent_breakpoint_here if it's a @@ -1916,7 +2108,7 @@ breakpoint_init_inferior (enum inf_context context) the target, to advance the PC past the breakpoint. */ enum breakpoint_here -breakpoint_here_p (CORE_ADDR pc) +breakpoint_here_p (struct address_space *aspace, CORE_ADDR pc) { const struct bp_location *bpt; int any_breakpoint_here = 0; @@ -1929,7 +2121,8 @@ breakpoint_here_p (CORE_ADDR pc) if ((breakpoint_enabled (bpt->owner) || bpt->owner->enable_state == bp_permanent) - && bpt->address == pc) /* bp is enabled and matches pc */ + && breakpoint_address_match (bpt->pspace->aspace, bpt->address, + aspace, pc)) { if (overlay_debugging && section_is_overlay (bpt->section) @@ -1948,13 +2141,14 @@ breakpoint_here_p (CORE_ADDR pc) /* Return true if there's a moribund breakpoint at PC. */ int -moribund_breakpoint_here_p (CORE_ADDR pc) +moribund_breakpoint_here_p (struct address_space *aspace, CORE_ADDR pc) { struct bp_location *loc; int ix; for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix) - if (loc->address == pc) + if (breakpoint_address_match (loc->pspace->aspace, loc->address, + aspace, pc)) return 1; return 0; @@ -1966,7 +2160,7 @@ moribund_breakpoint_here_p (CORE_ADDR pc) inserted and removed using direct target manipulation. */ int -regular_breakpoint_inserted_here_p (CORE_ADDR pc) +regular_breakpoint_inserted_here_p (struct address_space *aspace, CORE_ADDR pc) { const struct bp_location *bpt; @@ -1977,7 +2171,8 @@ regular_breakpoint_inserted_here_p (CORE_ADDR pc) continue; if (bpt->inserted - && bpt->address == pc) /* bp is inserted and matches pc */ + && breakpoint_address_match (bpt->pspace->aspace, bpt->address, + aspace, pc)) { if (overlay_debugging && section_is_overlay (bpt->section) @@ -1994,12 +2189,12 @@ regular_breakpoint_inserted_here_p (CORE_ADDR pc) or a single step breakpoint inserted at PC. */ int -breakpoint_inserted_here_p (CORE_ADDR pc) +breakpoint_inserted_here_p (struct address_space *aspace, CORE_ADDR pc) { - if (regular_breakpoint_inserted_here_p (pc)) + if (regular_breakpoint_inserted_here_p (aspace, pc)) return 1; - if (single_step_breakpoint_inserted_here_p (pc)) + if (single_step_breakpoint_inserted_here_p (aspace, pc)) return 1; return 0; @@ -2009,7 +2204,7 @@ breakpoint_inserted_here_p (CORE_ADDR pc) inserted at PC. */ int -software_breakpoint_inserted_here_p (CORE_ADDR pc) +software_breakpoint_inserted_here_p (struct address_space *aspace, CORE_ADDR pc) { const struct bp_location *bpt; int any_breakpoint_here = 0; @@ -2020,7 +2215,8 @@ software_breakpoint_inserted_here_p (CORE_ADDR pc) continue; if (bpt->inserted - && bpt->address == pc) /* bp is enabled and matches pc */ + && breakpoint_address_match (bpt->pspace->aspace, bpt->address, + aspace, pc)) { if (overlay_debugging && section_is_overlay (bpt->section) @@ -2032,7 +2228,7 @@ software_breakpoint_inserted_here_p (CORE_ADDR pc) } /* Also check for software single-step breakpoints. */ - if (single_step_breakpoint_inserted_here_p (pc)) + if (single_step_breakpoint_inserted_here_p (aspace, pc)) return 1; return 0; @@ -2042,7 +2238,8 @@ software_breakpoint_inserted_here_p (CORE_ADDR pc) PC is valid for process/thread PTID. */ int -breakpoint_thread_match (CORE_ADDR pc, ptid_t ptid) +breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc, + ptid_t ptid) { const struct bp_location *bpt; /* The thread and task IDs associated to PTID, computed lazily. */ @@ -2059,7 +2256,8 @@ breakpoint_thread_match (CORE_ADDR pc, ptid_t ptid) && bpt->owner->enable_state != bp_permanent) continue; - if (bpt->address != pc) + if (!breakpoint_address_match (bpt->pspace->aspace, bpt->address, + aspace, pc)) continue; if (bpt->owner->thread != -1) @@ -2919,7 +3117,8 @@ which its expression is valid.\n"); breakpoint location BL. This function does not check if we should stop, only if BL explains the stop. */ static int -bpstat_check_location (const struct bp_location *bl, CORE_ADDR bp_addr) +bpstat_check_location (const struct bp_location *bl, + struct address_space *aspace, CORE_ADDR bp_addr) { struct breakpoint *b = bl->owner; @@ -2930,7 +3129,8 @@ bpstat_check_location (const struct bp_location *bl, CORE_ADDR bp_addr) && b->type != bp_hardware_breakpoint && b->type != bp_catchpoint) /* a non-watchpoint bp */ { - if (bl->address != bp_addr) /* address doesn't match */ + if (!breakpoint_address_match (bl->pspace->aspace, bl->address, + aspace, bp_addr)) return 0; if (overlay_debugging /* unmapped overlay section */ && section_is_overlay (bl->section) @@ -3154,7 +3354,8 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid) commands, FIXME??? fields. */ bpstat -bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid) +bpstat_stop_status (struct address_space *aspace, + CORE_ADDR bp_addr, ptid_t ptid) { struct breakpoint *b = NULL; const struct bp_location *bl; @@ -3182,7 +3383,7 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid) if (b->type == bp_hardware_watchpoint && bl != b->loc) continue; - if (!bpstat_check_location (bl, bp_addr)) + if (!bpstat_check_location (bl, aspace, bp_addr)) continue; /* Come here if it's a watchpoint, or if the break address matches */ @@ -3237,7 +3438,8 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid) for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix) { - if (loc->address == bp_addr) + if (breakpoint_address_match (loc->pspace->aspace, loc->address, + aspace, bp_addr)) { bs = bpstat_alloc (loc, bs); /* For hits of moribund locations, we should just proceed. */ @@ -3540,6 +3742,11 @@ static void print_breakpoint_location (struct breakpoint *b, char *wrap_indent, struct ui_stream *stb) { + struct cleanup *old_chain = save_current_program_space (); + + if (loc != NULL) + set_current_program_space (loc->pspace); + if (b->source_file) { struct symbol *sym @@ -3575,6 +3782,8 @@ static void print_breakpoint_location (struct breakpoint *b, print_address_symbolic (loc->address, stb->stream, demangle, ""); ui_out_field_stream (uiout, "at", stb); } + + do_cleanups (old_chain); } /* Print B to gdb_stdout. */ @@ -3583,7 +3792,8 @@ print_one_breakpoint_location (struct breakpoint *b, struct bp_location *loc, int loc_number, struct bp_location **last_loc, - int print_address_bits) + int print_address_bits, + int allflag) { struct command_line *l; struct symbol *sym; @@ -3763,6 +3973,36 @@ print_one_breakpoint_location (struct breakpoint *b, break; } + + /* For backward compatibility, don't display inferiors unless there + are several. */ + if (loc != NULL + && !header_of_multiple + && (allflag + || (!gdbarch_has_global_breakpoints (target_gdbarch) + && (number_of_program_spaces () > 1 + || number_of_inferiors () > 1) + && loc->owner->type != bp_catchpoint))) + { + struct inferior *inf; + int first = 1; + + for (inf = inferior_list; inf != NULL; inf = inf->next) + { + if (inf->pspace == loc->pspace) + { + if (first) + { + first = 0; + ui_out_text (uiout, " inf "); + } + else + ui_out_text (uiout, ", "); + ui_out_text (uiout, plongest (inf->num)); + } + } + } + if (!part_of_multiple) { if (b->thread != -1) @@ -3896,9 +4136,11 @@ print_one_breakpoint_location (struct breakpoint *b, static void print_one_breakpoint (struct breakpoint *b, - struct bp_location **last_loc, int print_address_bits) + struct bp_location **last_loc, int print_address_bits, + int allflag) { - print_one_breakpoint_location (b, NULL, 0, last_loc, print_address_bits); + print_one_breakpoint_location (b, NULL, 0, last_loc, + print_address_bits, allflag); /* If this breakpoint has custom print function, it's already printed. Otherwise, print individual @@ -3922,7 +4164,7 @@ print_one_breakpoint (struct breakpoint *b, int n = 1; for (loc = b->loc; loc; loc = loc->next, ++n) print_one_breakpoint_location (b, loc, n, last_loc, - print_address_bits); + print_address_bits, allflag); } } } @@ -3959,7 +4201,7 @@ do_captured_breakpoint_query (struct ui_out *uiout, void *data) if (args->bnum == b->number) { int print_address_bits = breakpoint_address_bits (b); - print_one_breakpoint (b, &dummy_loc, print_address_bits); + print_one_breakpoint (b, &dummy_loc, print_address_bits, 0); return GDB_RC_OK; } } @@ -4075,7 +4317,7 @@ breakpoint_1 (int bnum, int allflag) /* We only print out user settable breakpoints unless the allflag is set. */ if (allflag || user_settable_breakpoint (b)) - print_one_breakpoint (b, &last_loc, print_address_bits); + print_one_breakpoint (b, &last_loc, print_address_bits, allflag); } do_cleanups (bkpttbl_chain); @@ -4123,29 +4365,34 @@ maintenance_info_breakpoints (char *bnum_exp, int from_tty) static int breakpoint_has_pc (struct breakpoint *b, + struct program_space *pspace, CORE_ADDR pc, struct obj_section *section) { struct bp_location *bl = b->loc; for (; bl; bl = bl->next) { - if (bl->address == pc + if (bl->pspace == pspace + && bl->address == pc && (!overlay_debugging || bl->section == section)) return 1; } return 0; } -/* Print a message describing any breakpoints set at PC. */ +/* Print a message describing any breakpoints set at PC. This + concerns with logical breakpoints, so we match program spaces, not + address spaces. */ static void -describe_other_breakpoints (struct gdbarch *gdbarch, CORE_ADDR pc, +describe_other_breakpoints (struct gdbarch *gdbarch, + struct program_space *pspace, CORE_ADDR pc, struct obj_section *section, int thread) { int others = 0; struct breakpoint *b; ALL_BREAKPOINTS (b) - others += breakpoint_has_pc (b, pc, section); + others += breakpoint_has_pc (b, pspace, pc, section); if (others > 0) { if (others == 1) @@ -4153,7 +4400,7 @@ describe_other_breakpoints (struct gdbarch *gdbarch, CORE_ADDR pc, else /* if (others == ???) */ printf_filtered (_("Note: breakpoints ")); ALL_BREAKPOINTS (b) - if (breakpoint_has_pc (b, pc, section)) + if (breakpoint_has_pc (b, pspace, pc, section)) { others--; printf_filtered ("%d", b->number); @@ -4182,10 +4429,12 @@ describe_other_breakpoints (struct gdbarch *gdbarch, CORE_ADDR pc, for the `break' command with no arguments. */ void -set_default_breakpoint (int valid, CORE_ADDR addr, struct symtab *symtab, +set_default_breakpoint (int valid, struct program_space *pspace, + CORE_ADDR addr, struct symtab *symtab, int line) { default_breakpoint_valid = valid; + default_breakpoint_pspace = pspace; default_breakpoint_address = addr; default_breakpoint_symtab = symtab; default_breakpoint_line = line; @@ -4219,6 +4468,20 @@ breakpoint_address_is_meaningful (struct breakpoint *bpt) && type != bp_catchpoint); } +/* Returns true if {ASPACE1,ADDR1} and {ASPACE2,ADDR2} represent the + same breakpoint location. In most targets, this can only be true + if ASPACE1 matches ASPACE2. On targets that have global + breakpoints, the address space doesn't really matter. */ + +static int +breakpoint_address_match (struct address_space *aspace1, CORE_ADDR addr1, + struct address_space *aspace2, CORE_ADDR addr2) +{ + return ((gdbarch_has_global_breakpoints (target_gdbarch) + || aspace1 == aspace2) + && addr1 == addr2); +} + /* Rescan breakpoints at the same address and section as BPT, marking the first one as "first" and any others as "duplicates". This is so that the bpt instruction is only inserted once. @@ -4226,7 +4489,9 @@ breakpoint_address_is_meaningful (struct breakpoint *bpt) that one the official one, and the rest as duplicates. */ static void -check_duplicates_for (CORE_ADDR address, struct obj_section *section) +check_duplicates_for (struct address_space *aspace, + CORE_ADDR address, + struct obj_section *section) { struct bp_location *b; int count = 0; @@ -4238,9 +4503,10 @@ check_duplicates_for (CORE_ADDR address, struct obj_section *section) && b->owner->enable_state != bp_startup_disabled && b->enabled && !b->shlib_disabled - && b->address == address /* address / overlay match */ && (!overlay_debugging || b->section == section) - && breakpoint_address_is_meaningful (b->owner)) + && breakpoint_address_is_meaningful (b->owner) + && breakpoint_address_match (b->pspace->aspace, b->address, + aspace, address)) { /* Have we found a permanent breakpoint? */ if (b->owner->enable_state == bp_permanent) @@ -4273,10 +4539,11 @@ check_duplicates_for (CORE_ADDR address, struct obj_section *section) && b->owner->enable_state != bp_disabled && b->owner->enable_state != bp_call_disabled && b->owner->enable_state != bp_startup_disabled - && b->enabled && !b->shlib_disabled - && b->address == address /* address / overlay match */ - && (!overlay_debugging || b->section == section) - && breakpoint_address_is_meaningful (b->owner)) + && b->enabled && !b->shlib_disabled + && breakpoint_address_is_meaningful (b->owner) + && breakpoint_address_match (b->pspace->aspace, b->address, + aspace, address) + && (!overlay_debugging || b->section == section)) { if (b->inserted) internal_error (__FILE__, __LINE__, @@ -4298,7 +4565,7 @@ check_duplicates (struct breakpoint *bpt) return; for (; bl; bl = bl->next) - check_duplicates_for (bl->address, bl->section); + check_duplicates_for (bl->pspace->aspace, bl->address, bl->section); } static void @@ -4522,6 +4789,9 @@ set_raw_breakpoint (struct gdbarch *gdbarch, if (!loc_gdbarch) loc_gdbarch = b->gdbarch; + if (bptype != bp_catchpoint) + gdb_assert (sal.pspace != NULL); + /* Adjust the breakpoint's address prior to allocating a location. Once we call allocate_bp_location(), that mostly uninitialized location will be placed on the location chain. Adjustment of the @@ -4534,6 +4804,11 @@ set_raw_breakpoint (struct gdbarch *gdbarch, b->loc->gdbarch = loc_gdbarch; b->loc->requested_address = sal.pc; b->loc->address = adjusted_address; + b->loc->pspace = sal.pspace; + + /* Store the program space that was used to set the breakpoint, for + breakpoint resetting. */ + b->pspace = sal.pspace; if (sal.symtab == NULL) b->source_file = NULL; @@ -4581,7 +4856,8 @@ set_longjmp_breakpoint (int thread) longjmp "master" breakpoints. Here, we simply create momentary clones of those and enable them for the requested thread. */ ALL_BREAKPOINTS_SAFE (b, temp) - if (b->type == bp_longjmp_master) + if (b->pspace == current_program_space + && b->type == bp_longjmp_master) { struct breakpoint *clone = clone_momentary_breakpoint (b); clone->type = bp_longjmp; @@ -4654,7 +4930,8 @@ remove_thread_event_breakpoints (void) struct breakpoint *b, *temp; ALL_BREAKPOINTS_SAFE (b, temp) - if (b->type == bp_thread_event) + if (b->type == bp_thread_event + && b->loc->pspace == current_program_space) delete_breakpoint (b); } @@ -4690,7 +4967,8 @@ remove_solib_event_breakpoints (void) struct breakpoint *b, *temp; ALL_BREAKPOINTS_SAFE (b, temp) - if (b->type == bp_shlib_event) + if (b->type == bp_shlib_event + && b->loc->pspace == current_program_space) delete_breakpoint (b); } @@ -4723,11 +5001,12 @@ disable_breakpoints_in_shlibs (void) if (((b->type == bp_breakpoint) || (b->type == bp_hardware_breakpoint) || (b->type == bp_tracepoint)) + && loc->pspace == current_program_space && !loc->shlib_disabled #ifdef PC_SOLIB && PC_SOLIB (loc->address) #else - && solib_name_from_address (loc->address) + && solib_name_from_address (loc->pspace, loc->address) #endif ) { @@ -4758,6 +5037,7 @@ disable_breakpoints_in_unloaded_shlib (struct so_list *solib) struct breakpoint *b = loc->owner; if ((loc->loc_type == bp_loc_hardware_breakpoint || loc->loc_type == bp_loc_software_breakpoint) + && solib->pspace == loc->pspace && !loc->shlib_disabled && (b->type == bp_breakpoint || b->type == bp_hardware_breakpoint) && solib_contains_address_p (solib, loc->address)) @@ -5218,6 +5498,7 @@ create_catchpoint_without_mention (struct gdbarch *gdbarch, int tempflag, struct breakpoint *b; init_sal (&sal); + sal.pspace = current_program_space; b = set_raw_breakpoint (gdbarch, sal, bp_catchpoint); set_breakpoint_count (breakpoint_count + 1); @@ -5432,6 +5713,9 @@ disable_breakpoints_before_startup (void) ALL_BREAKPOINTS (b) { + if (b->pspace != current_program_space) + continue; + if ((b->type == bp_breakpoint || b->type == bp_hardware_breakpoint) && breakpoint_enabled (b)) @@ -5444,7 +5728,7 @@ disable_breakpoints_before_startup (void) if (found) update_global_location_list (0); - executing_startup = 1; + current_program_space->executing_startup = 1; } void @@ -5453,10 +5737,13 @@ enable_breakpoints_after_startup (void) struct breakpoint *b; int found = 0; - executing_startup = 0; + current_program_space->executing_startup = 0; ALL_BREAKPOINTS (b) { + if (b->pspace != current_program_space) + continue; + if ((b->type == bp_breakpoint || b->type == bp_hardware_breakpoint) && b->enable_state == bp_startup_disabled) @@ -5521,6 +5808,7 @@ clone_momentary_breakpoint (struct breakpoint *orig) copy->loc->requested_address = orig->loc->requested_address; copy->loc->address = orig->loc->address; copy->loc->section = orig->loc->section; + copy->loc->pspace = orig->loc->pspace; if (orig->source_file == NULL) copy->source_file = NULL; @@ -5530,6 +5818,7 @@ clone_momentary_breakpoint (struct breakpoint *orig) copy->line_number = orig->line_number; copy->frame_id = orig->frame_id; copy->thread = orig->thread; + copy->pspace = orig->pspace; copy->enable_state = bp_enabled; copy->disposition = disp_donttouch; @@ -5712,6 +6001,8 @@ add_location_to_breakpoint (struct breakpoint *b, loc->requested_address = sal->pc; loc->address = adjust_breakpoint_address (loc->gdbarch, loc->requested_address, b->type); + loc->pspace = sal->pspace; + gdb_assert (loc->pspace != NULL); loc->section = sal->section; set_breakpoint_location_function (loc); @@ -5746,7 +6037,10 @@ bp_loc_is_permanent (struct bp_location *loc) /* Enable the automatic memory restoration from breakpoints while we read the memory. Otherwise we could say about our temporary breakpoints they are permanent. */ - cleanup = make_show_memory_breakpoints_cleanup (0); + cleanup = save_current_space_and_thread (); + + switch_to_program_space_and_thread (loc->pspace); + make_show_memory_breakpoints_cleanup (0); if (target_read_memory (loc->address, target_mem, len) == 0 && memcmp (target_mem, brk, len) == 0) @@ -5786,6 +6080,8 @@ create_breakpoint (struct gdbarch *gdbarch, error (_("Hardware breakpoints used exceeds limit.")); } + gdb_assert (sals.nelts > 0); + for (i = 0; i < sals.nelts; ++i) { struct symtab_and_line sal = sals.sals[i]; @@ -5798,7 +6094,7 @@ create_breakpoint (struct gdbarch *gdbarch, loc_gdbarch = gdbarch; describe_other_breakpoints (loc_gdbarch, - sal.pc, sal.section, thread); + sal.pspace, sal.pc, sal.section, thread); } if (i == 0) @@ -5814,7 +6110,9 @@ create_breakpoint (struct gdbarch *gdbarch, b->enable_state = enabled ? bp_enabled : bp_disabled; b->disposition = disposition; - if (enabled && executing_startup + b->pspace = sals.sals[0].pspace; + + if (enabled && b->pspace->executing_startup && (b->type == bp_breakpoint || b->type == bp_hardware_breakpoint)) b->enable_state = bp_startup_disabled; @@ -5864,20 +6162,19 @@ remove_sal (struct symtabs_and_lines *sal, int index_to_remove) --(sal->nelts); } -/* If appropriate, obtains all sals that correspond - to the same file and line as SAL. This is done - only if SAL does not have explicit PC and has - line and file information. If we got just a single - expanded sal, return the original. +/* If appropriate, obtains all sals that correspond to the same file + and line as SAL, in all program spaces. Users debugging with IDEs, + will want to set a breakpoint at foo.c:line, and not really care + about program spaces. This is done only if SAL does not have + explicit PC and has line and file information. If we got just a + single expanded sal, return the original. - Otherwise, if SAL.explicit_line is not set, filter out - all sals for which the name of enclosing function - is different from SAL. This makes sure that if we have - breakpoint originally set in template instantiation, say - foo<int>(), we won't expand SAL to locations at the same - line in all existing instantiations of 'foo'. + Otherwise, if SAL.explicit_line is not set, filter out all sals for + which the name of enclosing function is different from SAL. This + makes sure that if we have breakpoint originally set in template + instantiation, say foo<int>(), we won't expand SAL to locations at + the same line in all existing instantiations of 'foo'. */ -*/ static struct symtabs_and_lines expand_line_sal_maybe (struct symtab_and_line sal) { @@ -5886,6 +6183,7 @@ expand_line_sal_maybe (struct symtab_and_line sal) char *original_function = NULL; int found; int i; + struct cleanup *old_chain; /* If we have explicit pc, don't expand. If we have no line number, we can't expand. */ @@ -5898,9 +6196,16 @@ expand_line_sal_maybe (struct symtab_and_line sal) } sal.pc = 0; + + old_chain = save_current_space_and_thread (); + + switch_to_program_space_and_thread (sal.pspace); + find_pc_partial_function (original_pc, &original_function, NULL, NULL); - + + /* Note that expand_line_sal visits *all* program spaces. */ expanded = expand_line_sal (sal); + if (expanded.nelts == 1) { /* We had one sal, we got one sal. Without futher @@ -5910,6 +6215,7 @@ expand_line_sal_maybe (struct symtab_and_line sal) expanded.sals = xmalloc (sizeof (struct symtab_and_line)); sal.pc = original_pc; expanded.sals[0] = sal; + do_cleanups (old_chain); return expanded; } @@ -5920,6 +6226,11 @@ expand_line_sal_maybe (struct symtab_and_line sal) { CORE_ADDR pc = expanded.sals[i].pc; char *this_function; + + /* We need to switch threads as well since we're about to + read memory. */ + switch_to_program_space_and_thread (expanded.sals[i].pspace); + if (find_pc_partial_function (pc, &this_function, &func_addr, &func_end)) { @@ -5963,7 +6274,8 @@ expand_line_sal_maybe (struct symtab_and_line sal) } } - + do_cleanups (old_chain); + if (expanded.nelts <= 1) { /* This is un ugly workaround. If we get zero @@ -6055,6 +6367,7 @@ parse_breakpoint_sals (char **address, sal.pc = default_breakpoint_address; sal.line = default_breakpoint_line; sal.symtab = default_breakpoint_symtab; + sal.pspace = default_breakpoint_pspace; sal.section = find_pc_overlay (sal.pc); /* "break" without arguments is equivalent to "break *PC" where PC is @@ -6373,8 +6686,9 @@ break_command_really (struct gdbarch *gdbarch, b->condition_not_parsed = 1; b->ops = ops; b->enable_state = enabled ? bp_enabled : bp_disabled; + b->pspace = current_program_space; - if (enabled && executing_startup + if (enabled && b->pspace->executing_startup && (b->type == bp_breakpoint || b->type == bp_hardware_breakpoint)) b->enable_state = bp_startup_disabled; @@ -6447,19 +6761,25 @@ set_breakpoint (struct gdbarch *gdbarch, static void skip_prologue_sal (struct symtab_and_line *sal) { - struct symbol *sym = find_pc_function (sal->pc); + struct symbol *sym; struct symtab_and_line start_sal; + struct cleanup *old_chain; - if (sym == NULL) - return; + old_chain = save_current_space_and_thread (); - start_sal = find_function_start_sal (sym, 1); - if (sal->pc < start_sal.pc) + sym = find_pc_function (sal->pc); + if (sym != NULL) { - start_sal.explicit_line = sal->explicit_line; - start_sal.explicit_pc = sal->explicit_pc; - *sal = start_sal; + start_sal = find_function_start_sal (sym, 1); + if (sal->pc < start_sal.pc) + { + start_sal.explicit_line = sal->explicit_line; + start_sal.explicit_pc = sal->explicit_pc; + *sal = start_sal; + } } + + do_cleanups (old_chain); } /* Helper function for break_command_1 and disassemble_command. */ @@ -6510,10 +6830,15 @@ resolve_sal_pc (struct symtab_and_line *sal) source). */ struct minimal_symbol *msym; + struct cleanup *old_chain = save_current_space_and_thread (); + + switch_to_program_space_and_thread (sal->pspace); msym = lookup_minimal_symbol_by_pc (sal->pc); if (msym) sal->section = SYMBOL_OBJ_SECTION (msym); + + do_cleanups (old_chain); } } } @@ -6704,6 +7029,8 @@ watch_command_1 (char *arg, int accessflag, int from_tty) } } + sal.pspace = current_program_space; + /* Parse the rest of the arguments. */ innermost_block = NULL; exp_start = arg; @@ -7387,7 +7714,8 @@ create_ada_exception_breakpoint (struct gdbarch *gdbarch, if (!loc_gdbarch) loc_gdbarch = gdbarch; - describe_other_breakpoints (loc_gdbarch, sal.pc, sal.section, -1); + describe_other_breakpoints (loc_gdbarch, + sal.pspace, sal.pc, sal.section, -1); /* FIXME: brobecker/2006-12-28: Actually, re-implement a special version for exception catchpoints, because two catchpoints used for different exception names will use the same address. @@ -7611,6 +7939,7 @@ clear_command (char *arg, int from_tty) sal.line = default_breakpoint_line; sal.symtab = default_breakpoint_symtab; sal.pc = default_breakpoint_address; + sal.pspace = default_breakpoint_pspace; if (sal.symtab == 0) error (_("No source file specified.")); @@ -7674,13 +8003,15 @@ clear_command (char *arg, int from_tty) struct bp_location *loc = b->loc; for (; loc; loc = loc->next) { - int pc_match = sal.pc + int pc_match = sal.pc + && (loc->pspace == sal.pspace) && (loc->address == sal.pc) && (!section_is_overlay (loc->section) || loc->section == sal.section); int line_match = ((default_match || (0 == sal.pc)) && b->source_file != NULL && sal.symtab != NULL + && sal.pspace == loc->pspace && strcmp (b->source_file, sal.symtab->filename) == 0 && b->line_number == sal.line); if (pc_match || line_match) @@ -7849,8 +8180,12 @@ update_global_location_list (int should_insert) call to check_duplicates will fix up this later. */ loc2->duplicate = 0; if (should_be_inserted (loc2) - && loc2 != loc && loc2->address == loc->address) - { + && loc2 != loc + && breakpoint_address_match (loc2->pspace->aspace, + loc2->address, + loc->pspace->aspace, + loc->address)) + { loc2->inserted = 1; loc2->target_info = loc->target_info; keep_in_target = 1; @@ -8235,7 +8570,8 @@ update_breakpoint_locations (struct breakpoint *b, if (have_ambiguous_names) { for (; l; l = l->next) - if (e->address == l->address) + if (breakpoint_address_match (e->pspace->aspace, e->address, + l->pspace->aspace, l->address)) { l->enabled = 0; break; @@ -8272,12 +8608,12 @@ breakpoint_re_set_one (void *bint) int i; int not_found = 0; int *not_found_ptr = ¬_found; - struct symtabs_and_lines sals = {}; - struct symtabs_and_lines expanded; + struct symtabs_and_lines sals = {0}; + struct symtabs_and_lines expanded = {0}; char *s; enum enable_state save_enable; struct gdb_exception e; - struct cleanup *cleanups; + struct cleanup *cleanups = make_cleanup (null_cleanup, NULL); switch (b->type) { @@ -8302,6 +8638,10 @@ breakpoint_re_set_one (void *bint) set_language (b->language); input_radix = b->input_radix; s = b->addr_string; + + save_current_space_and_thread (); + switch_to_program_space_and_thread (b->pspace); + TRY_CATCH (e, RETURN_MASK_ERROR) { sals = decode_line_1 (&s, 1, (struct symtab *) NULL, 0, (char ***) NULL, @@ -8335,29 +8675,31 @@ breakpoint_re_set_one (void *bint) } } - if (not_found) - break; - - gdb_assert (sals.nelts == 1); - resolve_sal_pc (&sals.sals[0]); - if (b->condition_not_parsed && s && s[0]) + if (!not_found) { - char *cond_string = 0; - int thread = -1; - int task = 0; - - find_condition_and_thread (s, sals.sals[0].pc, - &cond_string, &thread, &task); - if (cond_string) - b->cond_string = cond_string; - b->thread = thread; - b->task = task; - b->condition_not_parsed = 0; + gdb_assert (sals.nelts == 1); + + resolve_sal_pc (&sals.sals[0]); + if (b->condition_not_parsed && s && s[0]) + { + char *cond_string = 0; + int thread = -1; + int task = 0; + + find_condition_and_thread (s, sals.sals[0].pc, + &cond_string, &thread, &task); + if (cond_string) + b->cond_string = cond_string; + b->thread = thread; + b->task = task; + b->condition_not_parsed = 0; + } + + expanded = expand_line_sal_maybe (sals.sals[0]); } - expanded = expand_line_sal_maybe (sals.sals[0]); - cleanups = make_cleanup (xfree, sals.sals); + + make_cleanup (xfree, sals.sals); update_breakpoint_locations (b, expanded); - do_cleanups (cleanups); break; case bp_watchpoint: @@ -8430,6 +8772,7 @@ breakpoint_re_set_one (void *bint) break; } + do_cleanups (cleanups); return 0; } @@ -8440,9 +8783,12 @@ breakpoint_re_set (void) struct breakpoint *b, *temp; enum language save_language; int save_input_radix; + struct cleanup *old_chain; save_language = current_language->la_language; save_input_radix = input_radix; + old_chain = save_current_program_space (); + ALL_BREAKPOINTS_SAFE (b, temp) { /* Format possible error msg */ @@ -8457,6 +8803,8 @@ breakpoint_re_set (void) jit_breakpoint_re_set (); + do_cleanups (old_chain); + create_overlay_event_breakpoint ("_ovly_debug_event"); create_longjmp_master_breakpoint ("longjmp"); create_longjmp_master_breakpoint ("_longjmp"); @@ -8475,6 +8823,12 @@ breakpoint_re_set_thread (struct breakpoint *b) { if (in_thread_list (inferior_ptid)) b->thread = pid_to_thread_id (inferior_ptid); + + /* We're being called after following a fork. The new fork is + selected as current, and unless this was a vfork will have a + different program space from the original thread. Reset that + as well. */ + b->loc->pspace = current_program_space; } } @@ -8845,14 +9199,16 @@ decode_line_spec_1 (char *string, int funfirstline) someday. */ void * -deprecated_insert_raw_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc) +deprecated_insert_raw_breakpoint (struct gdbarch *gdbarch, + struct address_space *aspace, CORE_ADDR pc) { struct bp_target_info *bp_tgt; - bp_tgt = xmalloc (sizeof (struct bp_target_info)); - memset (bp_tgt, 0, sizeof (struct bp_target_info)); + bp_tgt = XZALLOC (struct bp_target_info); + bp_tgt->placed_address_space = aspace; bp_tgt->placed_address = pc; + if (target_insert_breakpoint (gdbarch, bp_tgt) != 0) { /* Could not insert the breakpoint. */ @@ -8885,7 +9241,8 @@ static struct gdbarch *single_step_gdbarch[2]; /* Create and insert a breakpoint for software single step. */ void -insert_single_step_breakpoint (struct gdbarch *gdbarch, CORE_ADDR next_pc) +insert_single_step_breakpoint (struct gdbarch *gdbarch, + struct address_space *aspace, CORE_ADDR next_pc) { void **bpt_p; @@ -8908,7 +9265,7 @@ insert_single_step_breakpoint (struct gdbarch *gdbarch, CORE_ADDR next_pc) corresponding changes elsewhere where single step breakpoints are handled, however. So, for now, we use this. */ - *bpt_p = deprecated_insert_raw_breakpoint (gdbarch, next_pc); + *bpt_p = deprecated_insert_raw_breakpoint (gdbarch, aspace, next_pc); if (*bpt_p == NULL) error (_("Could not insert single-step breakpoint at %s"), paddress (gdbarch, next_pc)); @@ -8940,14 +9297,17 @@ remove_single_step_breakpoints (void) /* Check whether a software single-step breakpoint is inserted at PC. */ static int -single_step_breakpoint_inserted_here_p (CORE_ADDR pc) +single_step_breakpoint_inserted_here_p (struct address_space *aspace, CORE_ADDR pc) { int i; for (i = 0; i < 2; i++) { struct bp_target_info *bp_tgt = single_step_breakpoints[i]; - if (bp_tgt && bp_tgt->placed_address == pc) + if (bp_tgt + && breakpoint_address_match (bp_tgt->placed_address_space, + bp_tgt->placed_address, + aspace, pc)) return 1; } @@ -9379,6 +9739,16 @@ add_catch_command (char *name, char *docstring, set_cmd_completer (command, completer); } +static void +clear_syscall_counts (int pid) +{ + struct inferior *inf = find_inferior_pid (pid); + + inf->total_syscalls_count = 0; + inf->any_syscall_count = 0; + VEC_free (int, inf->syscalls_counts); +} + void _initialize_breakpoint (void) { @@ -9387,6 +9757,7 @@ _initialize_breakpoint (void) struct cmd_list_element *c; observer_attach_solib_unloaded (disable_breakpoints_in_unloaded_shlib); + observer_attach_inferior_exit (clear_syscall_counts); breakpoint_chain = 0; /* Don't bother to call set_breakpoint_count. $bpnum isn't useful diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index ba499c6..46460b6 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -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 diff --git a/gdb/corelow.c b/gdb/corelow.c index d05dbf2..d0eada6 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -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) diff --git a/gdb/cris-tdep.c b/gdb/cris-tdep.c index 48f45de..e724994 100644 --- a/gdb/cris-tdep.c +++ b/gdb/cris-tdep.c @@ -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); } } diff --git a/gdb/darwin-nat.c b/gdb/darwin-nat.c index daa9df9..54eb3e3 100644 --- a/gdb/darwin-nat.c +++ b/gdb/darwin-nat.c @@ -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 */ diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index a64369e..63a4ff6 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -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". diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 0a4d054..a84d519 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -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. - -Each inferior may in turn have multiple threads running in it. +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. -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 diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi index 77d6b47..0ea9397 100644 --- a/gdb/doc/observer.texi +++ b/gdb/doc/observer.texi @@ -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 @@ -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 diff --git a/gdb/fork-child.c b/gdb/fork-child.c index 9eadbc9..38cda41 100644 --- a/gdb/fork-child.c +++ b/gdb/fork-child.c @@ -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); diff --git a/gdb/frame.c b/gdb/frame.c index 7932b48..12a38e7 100644 --- a/gdb/frame.c +++ b/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 diff --git a/gdb/frame.h b/gdb/frame.h index 994b529..1463064 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -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 diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 6c13dc8..363d003 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -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) @@ -891,6 +895,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *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)); fprintf_unfiltered (file, @@ -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. */ diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index f7b0355..df42828 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -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) diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index cf43819..7491cf5 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -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 } diff --git a/gdb/gdbcore.h b/gdb/gdbcore.h index 7a7dcb2..2bf98cb 100644 --- a/gdb/gdbcore.h +++ b/gdb/gdbcore.h @@ -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. */ diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 79d33fe..2f388fb 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -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); diff --git a/gdb/gnu-nat.c b/gdb/gnu-nat.c index cab0c39..b474037 100644 --- a/gdb/gnu-nat.c +++ b/gdb/gnu-nat.c @@ -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); diff --git a/gdb/go32-nat.c b/gdb/go32-nat.c index 836a819..4c30411 100644 --- a/gdb/go32-nat.c +++ b/gdb/go32-nat.c @@ -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); diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c index 736154a..a138701 100644 --- a/gdb/inf-ptrace.c +++ b/gdb/inf-ptrace.c @@ -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. */ diff --git a/gdb/inf-ttrace.c b/gdb/inf-ttrace.c index 5233619..14e05f3 100644 --- a/gdb/inf-ttrace.c +++ b/gdb/inf-ttrace.c @@ -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. */ diff --git a/gdb/infcall.c b/gdb/infcall.c index 0673d9c..03b1dbf 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -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 diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 9e98290..53fb3b4 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -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 ()) diff --git a/gdb/inferior.c b/gdb/inferior.c index 676fff9..87d9a45 100644 --- a/gdb/inferior.c +++ b/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,32 +165,48 @@ delete_thread_of_inferior (struct thread_info *tp, void *data) return 0; } +void +delete_threads_of_inferior (int pid) +{ + struct inferior *inf; + struct delete_thread_of_inferior_arg arg; + + for (inf = inferior_list; inf; inf = inf->next) + if (inf->pid == pid) + break; + + if (!inf) + 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 (int pid, int silent) +delete_inferior_1 (struct inferior *todel, int silent) { struct inferior *inf, *infprev; - struct delete_thread_of_inferior_arg arg = { pid, silent }; + struct delete_thread_of_inferior_arg arg; infprev = NULL; for (inf = inferior_list; inf; infprev = inf, inf = inf->next) - if (inf->pid == pid) + if (inf == todel) break; if (!inf) return; - arg.pid = pid; + 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; - printf_filtered (_("[Switching to thread %d (%s)] "), - pid_to_thread_id (inferior_ptid), - target_pid_to_str (inferior_ptid)); + inf = find_inferior_id (num); + set_current_inferior (inf); + switch_to_thread (null_ptid); + set_current_program_space (inf->pspace); + } - 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); } + + +/* 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 -_initialize_inferiors (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); + } diff --git a/gdb/inferior.h b/gdb/inferior.h index f1b5d17..14b99e2 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -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) */ diff --git a/gdb/inflow.c b/gdb/inflow.c index 9ec45dd..71e0612 100644 --- a/gdb/inflow.c +++ b/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; + + 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); + } - inf->terminal_info = XZALLOC (struct terminal_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); } diff --git a/gdb/infrun.c b/gdb/infrun.c index 203ab5d..33843b2 100644 --- a/gdb/infrun.c +++ b/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 @@ done: 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); diff --git a/gdb/linespec.c b/gdb/linespec.c index 19df329..1be2686 100644 --- a/gdb/linespec.c +++ b/gdb/linespec.c @@ -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; diff --git a/gdb/linux-fork.c b/gdb/linux-fork.c index 35ca389..a69bc36 100644 --- a/gdb/linux-fork.c +++ b/gdb/linux-fork.c @@ -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. */ diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index fd12d44..724cdee 100644 --- a/gdb/linux-nat.c +++ b/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); + + lp->stopped = 1; + lp->resumed = 1; - 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); + /* 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. */ + if (debug_linux_nat) + fprintf_unfiltered (gdb_stdlog, + "LCFF: no VFORK_DONE support, sleeping a bit\n"); + usleep (10000); - } - /* Since we vforked, breakpoints were removed in the parent - too. Put them back. */ - reattach_breakpoints (parent_pid); + /* 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). - - 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). + parent_pspace = parent_inf->pspace; - 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) + return 0; + } + + if (event == PTRACE_EVENT_VFORK_DONE) + { + if (current_inferior ()->waiting_for_vfork_done) { - detach_breakpoints (linux_parent_pid); - ptrace (PTRACE_DETACH, linux_parent_pid, 0, 0); + if (debug_linux_nat) + fprintf_unfiltered (gdb_stdlog, "\ +LHEW: Got expected PTRACE_EVENT_VFORK_DONE from LWP %ld: stopping\n", + GET_LWP (lp->ptid)); - linux_parent_pid = 0; + 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, diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 6634703..6c7e80b 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -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) { diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c index 907739d..c3787d5 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -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); diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index 5ad4a51..6fdf292 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -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); } diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index 857a5d5..bfad98f 100644 --- a/gdb/mi/mi-main.c +++ b/gdb/mi/mi-main.c @@ -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; } diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c index e2ecc8b..9cf5057 100644 --- a/gdb/mips-tdep.c +++ b/gdb/mips-tdep.c @@ -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)) diff --git a/gdb/monitor.c b/gdb/monitor.c index 1e61afd..db6b335 100644 --- a/gdb/monitor.c +++ b/gdb/monitor.c @@ -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 */ diff --git a/gdb/nto-procfs.c b/gdb/nto-procfs.c index 0adb045..256b0f9 100644 --- a/gdb/nto-procfs.c +++ b/gdb/nto-procfs.c @@ -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. */ diff --git a/gdb/objfiles.c b/gdb/objfiles.c index 3aabc7c..13a8c55 100644 --- a/gdb/objfiles.c +++ b/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); +} diff --git a/gdb/objfiles.h b/gdb/objfiles.h index aff430c..b78e158 100644 --- a/gdb/objfiles.h +++ b/gdb/objfiles.h @@ -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. */ + +/* Traverse all object files in program space SS. */ -#define ALL_OBJFILES(obj) \ - for ((obj) = object_files; (obj) != NULL; (obj) = (obj)->next) +#define ALL_PSPACE_OBJFILES(ss, obj) \ + for ((obj) = ss->objfiles; (obj) != NULL; (obj) = (obj)->next) \ -#define ALL_OBJFILES_SAFE(obj,nxt) \ - for ((obj) = object_files; \ +#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) \ diff --git a/gdb/printcmd.c b/gdb/printcmd.c index 5d8b936..8281341 100644 --- a/gdb/printcmd.c +++ b/gdb/printcmd.c @@ -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) diff --git a/gdb/procfs.c b/gdb/procfs.c index d43031b..0f0b5a5 100644 --- a/gdb/procfs.c +++ b/gdb/procfs.c @@ -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; diff --git a/gdb/progspace.c b/gdb/progspace.c new file mode 100644 index 0000000..1a465ce --- /dev/null +++ b/gdb/progspace.c @@ -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 ()); +} diff --git a/gdb/progspace.h b/gdb/progspace.h new file mode 100644 index 0000000..2a0d1d2 --- /dev/null +++ b/gdb/progspace.h @@ -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 diff --git a/gdb/record.c b/gdb/record.c index 2dd6faf..c9d3251 100644 --- a/gdb/record.c +++ b/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, diff --git a/gdb/regcache.c b/gdb/regcache.c index c921bdd..715dd34 100644 --- a/gdb/regcache.c +++ b/gdb/regcache.c @@ -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; diff --git a/gdb/regcache.h b/gdb/regcache.h index 4a32602..35636b1 100644 --- a/gdb/regcache.h +++ b/gdb/regcache.h @@ -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. */ diff --git a/gdb/remote-sim.c b/gdb/remote-sim.c index 7ca16c7..2031116 100644 --- a/gdb/remote-sim.c +++ b/gdb/remote-sim.c @@ -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 */ diff --git a/gdb/remote.c b/gdb/remote.c index f730a49..43955e9 100644 --- a/gdb/remote.c +++ b/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; diff --git a/gdb/rs6000-aix-tdep.c b/gdb/rs6000-aix-tdep.c index 643d625..a49bcbb 100644 --- a/gdb/rs6000-aix-tdep.c +++ b/gdb/rs6000-aix-tdep.c @@ -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! */ diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c index 26ae109..17304dc 100644 --- a/gdb/rs6000-tdep.c +++ b/gdb/rs6000-tdep.c @@ -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; } diff --git a/gdb/solib-irix.c b/gdb/solib-irix.c index a0c1cd2..13a3e3d 100644 --- a/gdb/solib-irix.c +++ b/gdb/solib-irix.c @@ -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) diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 6cfaa85..d685ea5 100644 --- a/gdb/solib-svr4.c +++ b/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; -}; -/* 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; + CORE_ADDR interp_text_sect_low; + CORE_ADDR interp_text_sect_high; + CORE_ADDR interp_plt_sect_low; + CORE_ADDR interp_plt_sect_high; +}; -/* Get svr4 data for inferior PID (target id). If none is found yet, - add it now. This function always returns a valid object. */ +/* Per-program-space data key. */ +static const struct program_space_data *solib_svr4_pspace_data; -struct svr4_info * -get_svr4_info (int pid) +static void +svr4_pspace_data_cleanup (struct program_space *pspace, void *arg) { - 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); + struct svr4_info *info; - return it; + info = program_space_data (pspace, solib_svr4_pspace_data); + xfree (info); } -/* Get rid of any svr4 related bookkeeping for inferior PID (target - id). */ +/* Get the current svr4 data. If none is found yet, add it now. This + function always returns a valid object. */ -static void -remove_svr4_info (int pid) +static struct svr4_info * +get_svr4_info (void) { - int ix; - struct svr4_info *it; - - 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; - } - } -} + struct svr4_info *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. */ + info = program_space_data (current_program_space, solib_svr4_pspace_data); + if (info != NULL) + return info; -static void -solib_svr4_inferior_exit (int pid) -{ - remove_svr4_info (pid); + 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); } diff --git a/gdb/solib.c b/gdb/solib.c index a2ad0c4..6ad22fe 100644 --- a/gdb/solib.c +++ b/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); diff --git a/gdb/solib.h b/gdb/solib.h index ccc5b63..fee8d1c 100644 --- a/gdb/solib.h +++ b/gdb/solib.h @@ -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. */ diff --git a/gdb/solist.h b/gdb/solist.h index 63a6ba0..4c164e6 100644 --- a/gdb/solist.h +++ b/gdb/solist.h @@ -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. diff --git a/gdb/source.c b/gdb/source.c index 7a2829b..fd6cc8a 100644 --- a/gdb/source.c +++ b/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; + cursal.pc = 0; + cursal.end = 0; + current_source_pspace = sal->pspace; current_source_symtab = sal->symtab; current_source_line = sal->line; - cursal.pc = 0; - cursal.end = 0; - + 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) { diff --git a/gdb/sparc-tdep.c b/gdb/sparc-tdep.c index dc065ed..1ec7d51 100644 --- a/gdb/sparc-tdep.c +++ b/gdb/sparc-tdep.c @@ -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 diff --git a/gdb/spu-tdep.c b/gdb/spu-tdep.c index 7e7ab9c..b223a47 100644 --- a/gdb/spu-tdep.c +++ b/gdb/spu-tdep.c @@ -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)); } diff --git a/gdb/stack.c b/gdb/stack.c index 1c37801..d28de76 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -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) { diff --git a/gdb/symfile.c b/gdb/symfile.c index 3fd8c6d..5ace773 100644 --- a/gdb/symfile.c +++ b/gdb/symfile.c @@ -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); diff --git a/gdb/symmisc.c b/gdb/symmisc.c index eb35369..4044981 100644 --- a/gdb/symmisc.c +++ b/gdb/symmisc.c @@ -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; diff --git a/gdb/symtab.c b/gdb/symtab.c index 8d9d72c..03586c3 100644 --- a/gdb/symtab.c +++ b/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) diff --git a/gdb/symtab.h b/gdb/symtab.h index 740d4e0..acb8510 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -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. diff --git a/gdb/target.c b/gdb/target.c index 1ba8b35..ac1010b 100644 --- a/gdb/target.c +++ b/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); diff --git a/gdb/target.h b/gdb/target.h index b1cb852..3d5a348 100644 --- a/gdb/target.h +++ b/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. */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 73d4ba3..416b9d5 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -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. diff --git a/gdb/testsuite/Makefile.in b/gdb/testsuite/Makefile.in index c574e23..7e750c5 100644 --- a/gdb/testsuite/Makefile.in +++ b/gdb/testsuite/Makefile.in @@ -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) diff --git a/gdb/testsuite/gdb.base/attach.exp b/gdb/testsuite/gdb.base/attach.exp index c55be20..d85a92e 100644 --- a/gdb/testsuite/gdb.base/attach.exp +++ b/gdb/testsuite/gdb.base/attach.exp @@ -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" diff --git a/gdb/testsuite/gdb.base/foll-exec.exp b/gdb/testsuite/gdb.base/foll-exec.exp index 0b1f03a..1882b24 100644 --- a/gdb/testsuite/gdb.base/foll-exec.exp +++ b/gdb/testsuite/gdb.base/foll-exec.exp @@ -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"} diff --git a/gdb/testsuite/gdb.base/foll-fork.exp b/gdb/testsuite/gdb.base/foll-fork.exp index 08a0f49..c1eb1fc 100644 --- a/gdb/testsuite/gdb.base/foll-fork.exp +++ b/gdb/testsuite/gdb.base/foll-fork.exp @@ -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 diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp b/gdb/testsuite/gdb.base/foll-vfork.exp index 307d974..5e68bda 100644 --- a/gdb/testsuite/gdb.base/foll-vfork.exp +++ b/gdb/testsuite/gdb.base/foll-vfork.exp @@ -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 { diff --git a/gdb/testsuite/gdb.base/maint.exp b/gdb/testsuite/gdb.base/maint.exp index 14e599b..2c2f654 100644 --- a/gdb/testsuite/gdb.base/maint.exp +++ b/gdb/testsuite/gdb.base/maint.exp @@ -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" } diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp index c09c559..c9de8f8 100644 --- a/gdb/testsuite/gdb.base/multi-forks.exp +++ b/gdb/testsuite/gdb.base/multi-forks.exp @@ -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 diff --git a/gdb/thread.c b/gdb/thread.c index 55b4b96..689e9d5 100644 --- a/gdb/thread.c +++ b/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)) { @@ -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 */ diff --git a/gdb/tui/tui-disasm.c b/gdb/tui/tui-disasm.c index 85c0e8b..c8996a3 100644 --- a/gdb/tui/tui-disasm.c +++ b/gdb/tui/tui-disasm.c @@ -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); diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c index d8c2599..bb035d0 100644 --- a/gdb/windows-nat.c +++ b/gdb/windows-nat.c @@ -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 |