+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.
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 \
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/
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
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;
}
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 */
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
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
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;
}
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;
}
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);
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);
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);
/* 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. */
CORE_ADDR default_breakpoint_address;
struct symtab *default_breakpoint_symtab;
int default_breakpoint_line;
+struct program_space *default_breakpoint_pspace;
+
\f
/* *PP is a string denoting a breakpoint. Get the number of the breakpoint.
Advance *PP after the string and any trailing whitespace.
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;
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
select_frame (fi);
}
+ frame_pspace = get_frame_program_space (get_selected_frame (NULL));
+
if (within_current_scope && reparse)
{
char *s;
;
*tmp = loc;
loc->gdbarch = get_type_arch (value_type (v));
+
+ loc->pspace = frame_pspace;
loc->address = addr;
loc->length = len;
loc->watchpoint_type = type;
/* 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)
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;
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
/* 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;
&& !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);
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)
{
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;
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--;
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;
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;
b->enable_state = bp_disabled;
}
update_global_location_list (1);
+
+ do_cleanups (old_chain);
}
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)
{
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. */
/* 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)
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
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
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. */
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:
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
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;
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)
/* 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;
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;
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)
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;
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;
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)
}
/* 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;
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. */
&& 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)
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;
&& 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)
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;
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 */
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. */
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
print_address_symbolic (loc->address, stb->stream, demangle, "");
ui_out_field_stream (uiout, "at", stb);
}
+
+ do_cleanups (old_chain);
}
/* Print B to gdb_stdout. */
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;
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)
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
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);
}
}
}
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;
}
}
/* 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);
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)
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);
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;
&& 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.
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;
&& 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)
&& 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__,
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
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
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;
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;
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);
}
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);
}
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
)
{
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))
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);
ALL_BREAKPOINTS (b)
{
+ if (b->pspace != current_program_space)
+ continue;
+
if ((b->type == bp_breakpoint
|| b->type == bp_hardware_breakpoint)
&& breakpoint_enabled (b))
if (found)
update_global_location_list (0);
- executing_startup = 1;
+ current_program_space->executing_startup = 1;
}
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)
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;
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;
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);
/* 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)
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];
loc_gdbarch = gdbarch;
describe_other_breakpoints (loc_gdbarch,
- sal.pc, sal.section, thread);
+ sal.pspace, sal.pc, sal.section, thread);
}
if (i == 0)
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;
--(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)
{
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. */
}
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
expanded.sals = xmalloc (sizeof (struct symtab_and_line));
sal.pc = original_pc;
expanded.sals[0] = sal;
+ do_cleanups (old_chain);
return expanded;
}
{
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))
{
}
}
-
+ do_cleanups (old_chain);
+
if (expanded.nelts <= 1)
{
/* This is un ugly workaround. If we get zero
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
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;
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. */
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);
}
}
}
}
}
+ sal.pspace = current_program_space;
+
/* Parse the rest of the arguments. */
innermost_block = NULL;
exp_start = arg;
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.
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."));
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)
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;
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;
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)
{
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,
}
}
- 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:
break;
}
+ do_cleanups (cleanups);
return 0;
}
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 */
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");
{
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;
}
}
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. */
/* 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;
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));
/* 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;
}
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)
{
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
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
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
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. */
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);
\f
/* This bpstat_what stuff tells wait_for_inferior what to do with a
breakpoint (a challenging task). */
/* 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);
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);
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
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);
/* 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
#include "exceptions.h"
#include "solib.h"
#include "filenames.h"
+#include "progspace.h"
#ifndef O_LARGEFILE
{
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. */
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);
int scratch_chan;
int flags;
int corelow_pid = CORELOW_PID;
+ struct inferior *inf;
target_preopen (from_tty);
if (!filename)
{
/* 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;
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
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);
}
}
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);
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 */
+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".
* 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
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
@item
the target system's inferior identifier
+
+@item
+the name of the executable the inferior is running.
+
@end enumerate
@noindent
@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:
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}
@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}}:
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
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
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
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
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
#include "exec.h"
#include "observer.h"
#include "arch-utils.h"
+#include "gdbthread.h"
+#include "progspace.h"
#include <fcntl.h>
#include "readline/readline.h"
/* Prototypes for local functions */
-static void exec_close (int);
-
static void file_command (char *, int);
static void set_section_command (char *, int);
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. */
/* 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)
{
}
}
+/* 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;
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"));
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. */
{
/* 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 ()));
}
{
/* 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 ()));
}
{
/* 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 ()));
}
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);
/* 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);
+ }
}
}
/* 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);
+ }
}
}
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;
#define EXEC_H
#include "target.h"
+#include "progspace.h"
struct target_section;
struct target_ops;
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. */
extern void print_section_info (struct target_section_table *table,
bfd *abfd);
+extern void exec_close (void);
#endif
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. */
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);
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
/* 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. */
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)
{
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
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);
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. */
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
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
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;
};
0, /* get_syscall_number */
0, /* has_global_solist */
0, /* has_global_breakpoints */
+ default_has_shared_address_space, /* has_shared_address_space */
/* startup_gdbarch() */
};
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;
/* 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)
fprintf_unfiltered (file,
"gdbarch_dump: has_global_solist = %s\n",
plongest (gdbarch->has_global_solist));
+ fprintf_unfiltered (file,
+ "gdbarch_dump: has_shared_address_space = <%s>\n",
+ host_address_to_string (gdbarch->has_shared_address_space));
fprintf_unfiltered (file,
"gdbarch_dump: have_nonsteppable_watchpoint = %s\n",
plongest (gdbarch->have_nonsteppable_watchpoint));
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. */
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)
# 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
}
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;
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. */
/* 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);
push_target (ops);
- inferior = add_inferior (pid);
+ inferior = current_inferior ();
+ inferior_appeared (inferior, pid);
inferior->attach_flag = 1;
inf_update_procs (inf);
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. */
#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);
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. */
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. */
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);
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. */
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
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 ())
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"
#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);
}
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;
}
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
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);
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;
{
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;
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)
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
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;
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);
}
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);
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,
fprintf_filtered (file, _("Printing of inferior events is %s.\n"), value);
}
+\f
+
+/* 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."),
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);
+
}
/* 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
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);
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;
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;
/* 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);
/* 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);
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
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) */
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. */
PROCESS_GROUP_TYPE
inferior_process_group (void)
{
- return current_inferior ()->terminal_info->process_group;
+ return get_inflow_inferior_data (current_inferior ())->process_group;
}
#endif
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
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;
/* 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
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)
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
terminal_ours_1 (int output_only)
{
struct inferior *inf;
+ struct terminal_info *tinfo;
if (terminal_is_ours)
return;
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;
{
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
*/
serial_noflush_set_tty_state (stdin_serial, our_terminal_info.ttystate,
- inf->terminal_info->ttystate);
+ tinfo->ttystate);
if (job_control)
{
}
#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
}
}
-/* 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
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
child_terminal_info (char *args, int from_tty)
{
struct inferior *inf;
+ struct terminal_info *tinfo;
if (!gdb_has_a_terminal ())
{
return;
inf = current_inferior ();
+ tinfo = get_inflow_inferior_data (inf);
printf_filtered (_("Inferior's terminal status (currently saved by GDB):\n"));
{
int flags;
- flags = inf->terminal_info->tflags;
+ flags = tinfo->tflags;
printf_filtered ("File descriptor flags = ");
}
#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);
}
\f
/* NEW_TTY_PREFORK is called before forking a new child process,
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;
}
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;
#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);
}
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,
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
{
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
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
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. */
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. */
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);
struct regcache *regcache;
struct gdbarch *gdbarch;
CORE_ADDR actual_pc;
+ struct address_space *aspace;
head = displaced_step_request_queue;
ptid = head->ptid;
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,
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;
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);
/* 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;
}
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;
}
{
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)
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
regcache = get_current_regcache ();
gdbarch = get_regcache_arch (regcache);
+ aspace = get_regcache_aspace (regcache);
pc = regcache_read_pc (regcache);
if (step > 0)
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
{
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
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)
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)
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)
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);
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 */
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;
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");
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
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)
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))
}
/* 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;
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);
/* 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);
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);
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);
}
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
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
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));
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));
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
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."), _("\
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);
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
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));
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;
/* 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
{
{
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. */
#include "xml-support.h"
#include "terminal.h"
#include <sys/vfs.h>
+#include "solib.h"
#ifndef SPUFS_MAGIC
#define SPUFS_MAGIC 0x23c9b64e
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 ();
else
{
struct inferior *parent_inf, *child_inf;
- struct lwp_info *lp;
struct cleanup *old_chain;
/* Add process to GDB's tables. */
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);
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
{
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
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
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 ();
}
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,
}
}
- 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? */
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__,
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;
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,
#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. */
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. */
in this session.\n"));
}
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_linux_tdep;
+
void
_initialize_linux_tdep (void)
{
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;
}
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);
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);
{
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);
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);
}
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;
}
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;
/* 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;
}
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;
}
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))
{
char *name;
char **p;
+ struct inferior *inf;
if (mon_ops->magic != MONITOR_OPS_MAGIC)
error (_("Magic number of monitor_ops struct wrong."));
/* 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 */
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);
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. */
/* 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. */
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. */
/* 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. */
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
}
/* 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)
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);
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. */
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;
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. */
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);
+}
#include "gdb_obstack.h" /* For obstack internals. */
#include "symfile.h" /* For struct psymbol_allocation_list */
+#include "progspace.h"
struct bcache;
struct htab;
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. */
#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. */
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);
extern void gdb_bfd_unref (struct bfd *abfd);
\f
-/* 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))
#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) \
{
/* 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;
};
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;
}
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)
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;
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)
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;
--- /dev/null
+/* 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 *);
+\f
+
+/* 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;
+}
+
+\f
+
+/* 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);
+}
+
+\f
+
+/* 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];
+}
+
+\f
+
+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 ());
+}
--- /dev/null
+/* 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
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
{
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);
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,
/* 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,
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). */
= 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;
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 *
{
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)
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);
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;
struct regcache;
struct gdbarch;
+struct address_space;
extern struct regcache *get_current_regcache (void);
extern struct regcache *get_thread_regcache (ptid_t ptid);
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. */
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 */
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;
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))
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);
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;
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;
/* 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! */
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};
/* 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;
}
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)
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. */
/* 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 */
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? ")))
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;
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;
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)
/* 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));
}
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
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);
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. */
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 ();
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
_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;
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);
}
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 */
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,
*/
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);
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. */
/* 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. */
/* 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.
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"
{
struct symtab_and_line cursal = { 0 };
+ cursal.pspace = current_source_pspace;
cursal.symtab = current_source_symtab;
cursal.line = current_source_line;
cursal.pc = 0;
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;
}
{
current_source_symtab = s;
current_source_line = 1;
+ current_source_pspace = SYMTAB_PSPACE (s);
return;
}
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)
current_source_line = 1;
- for (ofp = object_files; ofp != NULL; ofp = ofp->next)
+ ALL_OBJFILES (ofp)
{
for (s = ofp->symtabs; s; s = s->next)
{
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)
{
}
else
{
+ current_source_pspace = current_program_space;
current_source_symtab = PSYMTAB_TO_SYMTAB (cs_pst);
}
}
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)
{
{
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;
/* 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
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;
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, ®))
{
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));
}
}
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 ();
#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)
{
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);
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");
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)
struct cleanup *cleanups;
char *filename = DEV_TTY;
char *symname = NULL;
+ struct program_space *pspace;
struct objfile *objfile;
struct stat sym_st, obj_st;
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);
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--;
}
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;
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;
void
init_sal (struct symtab_and_line *sal)
{
+ sal->pspace = NULL;
sal->symtab = 0;
sal->section = 0;
sal->line = 0;
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
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
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)
}
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. */
sal.symtab = SYMBOL_SYMTAB (BLOCK_FUNCTION (function_block));
}
+ do_cleanups (old_chain);
return sal;
}
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)
{
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;
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)
{
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
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)
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));
}
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
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,
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)
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:
#define BLOCKVECTOR(symtab) (symtab)->blockvector
#define LINETABLE(symtab) (symtab)->linetable
+#define SYMTAB_PSPACE(symtab) (symtab)->objfile->pspace
\f
/* Each source file that has not been fully read in is represented by
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.
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
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)
{
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)
{
if (!ptid_equal (ptid, null_ptid))
{
int pid = ptid_get_pid (ptid);
- delete_inferior (pid);
+ exit_inferior (pid);
}
breakpoint_init_inferior (inf_exited);
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 */
#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. */
+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.
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)
# 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"
#
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"}
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"}
#
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"}
}
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"}
#
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"}
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"}
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"}
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"}
}
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"}
}
}
- 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
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"}
}
}
"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 {
-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
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"}
}
}
.*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
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 {
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 {
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" }
# 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.
#
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 {
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 {
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 {
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" }
# 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"
# 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" \
#
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
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
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;
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"),
struct frame_id selected_frame_id;
int selected_frame_level;
int was_stopped;
+ int inf_id;
};
static void
&& 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. */
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))
{
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)
{
{
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,
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 */
#include "tui/tui-stack.h"
#include "tui/tui-file.h"
#include "tui/tui-disasm.h"
+#include "progspace.h"
#include "gdb_curses.h"
/* 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);
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