12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061 |
- /* Multi-process control for GDB, the GNU debugger.
- Copyright (C) 2008-2022 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 "exec.h"
- #include "inferior.h"
- #include "target.h"
- #include "command.h"
- #include "completer.h"
- #include "gdbcmd.h"
- #include "gdbthread.h"
- #include "ui-out.h"
- #include "observable.h"
- #include "gdbcore.h"
- #include "symfile.h"
- #include "gdbsupport/environ.h"
- #include "cli/cli-utils.h"
- #include "arch-utils.h"
- #include "target-descriptions.h"
- #include "readline/tilde.h"
- #include "progspace-and-thread.h"
- #include "gdbsupport/buildargv.h"
- #include "cli/cli-style.h"
- /* Keep a registry of per-inferior data-pointers required by other GDB
- modules. */
- DEFINE_REGISTRY (inferior, REGISTRY_ACCESS_FIELD)
- intrusive_list<inferior> inferior_list;
- static int highest_inferior_num;
- /* See inferior.h. */
- bool print_inferior_events = true;
- /* The Current Inferior. This is a strong reference. I.e., whenever
- an inferior is the current inferior, its refcount is
- incremented. */
- static inferior_ref current_inferior_;
- struct inferior*
- current_inferior (void)
- {
- return current_inferior_.get ();
- }
- void
- set_current_inferior (struct inferior *inf)
- {
- /* There's always an inferior. */
- gdb_assert (inf != NULL);
- current_inferior_ = inferior_ref::new_reference (inf);
- }
- private_inferior::~private_inferior () = default;
- inferior::~inferior ()
- {
- inferior *inf = this;
- m_continuations.clear ();
- inferior_free_data (inf);
- target_desc_info_free (inf->tdesc_info);
- }
- inferior::inferior (int pid_)
- : num (++highest_inferior_num),
- pid (pid_),
- environment (gdb_environ::from_host_environ ()),
- registry_data ()
- {
- inferior_alloc_data (this);
- m_target_stack.push (get_dummy_target ());
- }
- /* See inferior.h. */
- int
- inferior::unpush_target (struct target_ops *t)
- {
- /* If unpushing the process stratum target from the inferior while threads
- exist in the inferior, ensure that we don't leave any threads of the
- inferior in the target's "resumed with pending wait status" list.
- See also the comment in set_thread_exited. */
- if (t->stratum () == process_stratum)
- {
- process_stratum_target *proc_target = as_process_stratum_target (t);
- for (thread_info *thread : this->non_exited_threads ())
- proc_target->maybe_remove_resumed_with_pending_wait_status (thread);
- }
- return m_target_stack.unpush (t);
- }
- void
- inferior::set_tty (std::string terminal_name)
- {
- m_terminal = std::move (terminal_name);
- }
- const std::string &
- inferior::tty ()
- {
- return m_terminal;
- }
- void
- inferior::add_continuation (std::function<void ()> &&cont)
- {
- m_continuations.emplace_front (std::move (cont));
- }
- void
- inferior::do_all_continuations ()
- {
- while (!m_continuations.empty ())
- {
- auto iter = m_continuations.begin ();
- (*iter) ();
- m_continuations.erase (iter);
- }
- }
- struct inferior *
- add_inferior_silent (int pid)
- {
- inferior *inf = new inferior (pid);
- inferior_list.push_back (*inf);
- gdb::observers::inferior_added.notify (inf);
- if (pid != 0)
- inferior_appeared (inf, pid);
- return inf;
- }
- struct inferior *
- add_inferior (int pid)
- {
- struct inferior *inf = add_inferior_silent (pid);
- if (print_inferior_events)
- {
- if (pid != 0)
- gdb_printf (_("[New inferior %d (%s)]\n"),
- inf->num,
- target_pid_to_str (ptid_t (pid)).c_str ());
- else
- gdb_printf (_("[New inferior %d]\n"), inf->num);
- }
- return inf;
- }
- /* See inferior.h. */
- void
- inferior::clear_thread_list (bool silent)
- {
- thread_list.clear_and_dispose ([=] (thread_info *thr)
- {
- threads_debug_printf ("deleting thread %s, silent = %d",
- thr->ptid.to_string ().c_str (), silent);
- set_thread_exited (thr, silent);
- if (thr->deletable ())
- delete thr;
- });
- ptid_thread_map.clear ();
- }
- void
- delete_inferior (struct inferior *inf)
- {
- inf->clear_thread_list (true);
- auto it = inferior_list.iterator_to (*inf);
- inferior_list.erase (it);
- gdb::observers::inferior_removed.notify (inf);
- /* If this program space is rendered useless, remove it. */
- if (inf->pspace->empty ())
- delete inf->pspace;
- delete inf;
- }
- /* If SILENT then be quiet -- don't announce a inferior exit, or the
- exit of its threads. */
- static void
- exit_inferior_1 (struct inferior *inf, int silent)
- {
- inf->clear_thread_list (silent);
- gdb::observers::inferior_exit.notify (inf);
- inf->pid = 0;
- inf->fake_pid_p = false;
- inf->priv = NULL;
- if (inf->vfork_parent != NULL)
- {
- inf->vfork_parent->vfork_child = NULL;
- inf->vfork_parent = NULL;
- }
- if (inf->vfork_child != NULL)
- {
- inf->vfork_child->vfork_parent = NULL;
- inf->vfork_child = NULL;
- }
- inf->pending_detach = 0;
- /* Reset it. */
- inf->control = inferior_control_state (NO_STOP_QUIETLY);
- /* Clear the register cache and the frame cache. */
- registers_changed ();
- reinit_frame_cache ();
- }
- void
- exit_inferior (inferior *inf)
- {
- exit_inferior_1 (inf, 0);
- }
- void
- exit_inferior_silent (inferior *inf)
- {
- exit_inferior_1 (inf, 1);
- }
- /* See inferior.h. */
- void
- detach_inferior (inferior *inf)
- {
- /* Save the pid, since exit_inferior_1 will reset it. */
- int pid = inf->pid;
- exit_inferior_1 (inf, 0);
- if (print_inferior_events)
- gdb_printf (_("[Inferior %d (%s) detached]\n"),
- inf->num,
- target_pid_to_str (ptid_t (pid)).c_str ());
- }
- void
- inferior_appeared (struct inferior *inf, int pid)
- {
- /* If this is the first inferior with threads, reset the global
- thread id. */
- delete_exited_threads ();
- if (!any_thread_p ())
- init_thread_list ();
- inf->pid = pid;
- inf->has_exit_code = 0;
- inf->exit_code = 0;
- gdb::observers::inferior_appeared.notify (inf);
- }
- struct inferior *
- find_inferior_id (int num)
- {
- for (inferior *inf : all_inferiors ())
- if (inf->num == num)
- return inf;
- return NULL;
- }
- struct inferior *
- find_inferior_pid (process_stratum_target *targ, int pid)
- {
- /* 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 (inferior *inf : all_inferiors (targ))
- if (inf->pid == pid)
- return inf;
- return NULL;
- }
- /* See inferior.h */
- struct inferior *
- find_inferior_ptid (process_stratum_target *targ, ptid_t ptid)
- {
- return find_inferior_pid (targ, ptid.pid ());
- }
- /* See inferior.h. */
- struct inferior *
- find_inferior_for_program_space (struct program_space *pspace)
- {
- struct inferior *cur_inf = current_inferior ();
- if (cur_inf->pspace == pspace)
- return cur_inf;
- for (inferior *inf : all_inferiors ())
- if (inf->pspace == pspace)
- return inf;
- return NULL;
- }
- int
- have_inferiors (void)
- {
- for (inferior *inf ATTRIBUTE_UNUSED : all_non_exited_inferiors ())
- return 1;
- return 0;
- }
- /* Return the number of live inferiors. We account for the case
- where an inferior might have a non-zero pid but no threads, as
- in the middle of a 'mourn' operation. */
- int
- number_of_live_inferiors (process_stratum_target *proc_target)
- {
- int num_inf = 0;
- for (inferior *inf : all_non_exited_inferiors (proc_target))
- if (inf->has_execution ())
- for (thread_info *tp ATTRIBUTE_UNUSED : inf->non_exited_threads ())
- {
- /* Found a live thread in this inferior, go to the next
- inferior. */
- ++num_inf;
- break;
- }
- return num_inf;
- }
- /* Return true if there is at least one live inferior. */
- int
- have_live_inferiors (void)
- {
- return number_of_live_inferiors (NULL) > 0;
- }
- /* Prune away any unused inferiors, and then prune away no longer used
- program spaces. */
- void
- prune_inferiors (void)
- {
- for (inferior *inf : all_inferiors_safe ())
- {
- if (!inf->deletable ()
- || !inf->removable
- || inf->pid != 0)
- continue;
- delete_inferior (inf);
- }
- }
- /* Simply returns the count of inferiors. */
- int
- number_of_inferiors (void)
- {
- auto rng = all_inferiors ();
- return std::distance (rng.begin (), rng.end ());
- }
- /* Converts an inferior process id to a string. Like
- target_pid_to_str, but special cases the null process. */
- static std::string
- inferior_pid_to_str (int pid)
- {
- if (pid != 0)
- return target_pid_to_str (ptid_t (pid));
- else
- return _("<null>");
- }
- /* See inferior.h. */
- void
- print_selected_inferior (struct ui_out *uiout)
- {
- struct inferior *inf = current_inferior ();
- const char *filename = inf->pspace->exec_filename.get ();
- if (filename == NULL)
- filename = _("<noexec>");
- uiout->message (_("[Switching to inferior %d [%s] (%s)]\n"),
- inf->num, inferior_pid_to_str (inf->pid).c_str (), filename);
- }
- /* Helper for print_inferior. Returns the 'connection-id' string for
- PROC_TARGET. */
- static std::string
- uiout_field_connection (process_stratum_target *proc_target)
- {
- if (proc_target == NULL)
- {
- return {};
- }
- else if (proc_target->connection_string () != NULL)
- {
- return string_printf ("%d (%s %s)",
- proc_target->connection_number,
- proc_target->shortname (),
- proc_target->connection_string ());
- }
- else
- {
- return string_printf ("%d (%s)",
- proc_target->connection_number,
- proc_target->shortname ());
- }
- }
- /* Prints the list of inferiors and their details on UIOUT. This is a
- version of 'info_inferior_command' suitable for use from MI.
- If REQUESTED_INFERIORS is not NULL, it's a list of GDB ids of the
- inferiors that should be printed. Otherwise, all inferiors are
- printed. */
- static void
- print_inferior (struct ui_out *uiout, const char *requested_inferiors)
- {
- int inf_count = 0;
- size_t connection_id_len = 20;
- /* Compute number of inferiors we will print. */
- for (inferior *inf : all_inferiors ())
- {
- if (!number_is_in_list (requested_inferiors, inf->num))
- continue;
- std::string conn = uiout_field_connection (inf->process_target ());
- if (connection_id_len < conn.size ())
- connection_id_len = conn.size ();
- ++inf_count;
- }
- if (inf_count == 0)
- {
- uiout->message ("No inferiors.\n");
- return;
- }
- ui_out_emit_table table_emitter (uiout, 5, inf_count, "inferiors");
- uiout->table_header (1, ui_left, "current", "");
- uiout->table_header (4, ui_left, "number", "Num");
- uiout->table_header (17, ui_left, "target-id", "Description");
- uiout->table_header (connection_id_len, ui_left,
- "connection-id", "Connection");
- uiout->table_header (17, ui_left, "exec", "Executable");
- uiout->table_body ();
- /* Restore the current thread after the loop because we switch the
- inferior in the loop. */
- scoped_restore_current_pspace_and_thread restore_pspace_thread;
- inferior *current_inf = current_inferior ();
- for (inferior *inf : all_inferiors ())
- {
- if (!number_is_in_list (requested_inferiors, inf->num))
- continue;
- ui_out_emit_tuple tuple_emitter (uiout, NULL);
- if (inf == current_inf)
- uiout->field_string ("current", "*");
- else
- uiout->field_skip ("current");
- uiout->field_signed ("number", inf->num);
- /* Because target_pid_to_str uses the current inferior,
- switch the inferior. */
- switch_to_inferior_no_thread (inf);
- uiout->field_string ("target-id", inferior_pid_to_str (inf->pid));
- std::string conn = uiout_field_connection (inf->process_target ());
- uiout->field_string ("connection-id", conn);
- if (inf->pspace->exec_filename != nullptr)
- uiout->field_string ("exec", inf->pspace->exec_filename.get (),
- file_name_style.style ());
- else
- uiout->field_skip ("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)
- {
- uiout->text (_("\n\tis vfork child of inferior "));
- uiout->field_signed ("vfork-parent", inf->vfork_parent->num);
- }
- if (inf->vfork_child)
- {
- uiout->text (_("\n\tis vfork parent of inferior "));
- uiout->field_signed ("vfork-child", inf->vfork_child->num);
- }
- uiout->text ("\n");
- }
- }
- static void
- detach_inferior_command (const char *args, int from_tty)
- {
- if (!args || !*args)
- error (_("Requires argument (inferior id(s) to detach)"));
- scoped_restore_current_thread restore_thread;
- number_or_range_parser parser (args);
- while (!parser.finished ())
- {
- int num = parser.get_number ();
- inferior *inf = find_inferior_id (num);
- if (inf == NULL)
- {
- warning (_("Inferior ID %d not known."), num);
- continue;
- }
- if (inf->pid == 0)
- {
- warning (_("Inferior ID %d is not running."), num);
- continue;
- }
- thread_info *tp = any_thread_of_inferior (inf);
- if (tp == NULL)
- {
- warning (_("Inferior ID %d has no threads."), num);
- continue;
- }
- switch_to_thread (tp);
- detach_command (NULL, from_tty);
- }
- }
- static void
- kill_inferior_command (const char *args, int from_tty)
- {
- if (!args || !*args)
- error (_("Requires argument (inferior id(s) to kill)"));
- scoped_restore_current_thread restore_thread;
- number_or_range_parser parser (args);
- while (!parser.finished ())
- {
- int num = parser.get_number ();
- inferior *inf = find_inferior_id (num);
- if (inf == NULL)
- {
- warning (_("Inferior ID %d not known."), num);
- continue;
- }
- if (inf->pid == 0)
- {
- warning (_("Inferior ID %d is not running."), num);
- continue;
- }
- thread_info *tp = any_thread_of_inferior (inf);
- if (tp == NULL)
- {
- warning (_("Inferior ID %d has no threads."), num);
- continue;
- }
- switch_to_thread (tp);
- target_kill ();
- }
- bfd_cache_close_all ();
- }
- /* See inferior.h. */
- void
- switch_to_inferior_no_thread (inferior *inf)
- {
- set_current_inferior (inf);
- switch_to_no_thread ();
- set_current_program_space (inf->pspace);
- }
- static void
- inferior_command (const char *args, int from_tty)
- {
- struct inferior *inf;
- int num;
- if (args == nullptr)
- {
- inf = current_inferior ();
- gdb_assert (inf != nullptr);
- const char *filename = inf->pspace->exec_filename.get ();
- if (filename == nullptr)
- filename = _("<noexec>");
- gdb_printf (_("[Current inferior is %d [%s] (%s)]\n"),
- inf->num, inferior_pid_to_str (inf->pid).c_str (),
- filename);
- }
- else
- {
- num = parse_and_eval_long (args);
- inf = find_inferior_id (num);
- if (inf == NULL)
- error (_("Inferior ID %d not known."), num);
- if (inf->pid != 0)
- {
- if (inf != current_inferior ())
- {
- thread_info *tp = any_thread_of_inferior (inf);
- if (tp == NULL)
- error (_("Inferior has no threads."));
- switch_to_thread (tp);
- }
- gdb::observers::user_selected_context_changed.notify
- (USER_SELECTED_INFERIOR
- | USER_SELECTED_THREAD
- | USER_SELECTED_FRAME);
- }
- else
- {
- switch_to_inferior_no_thread (inf);
- gdb::observers::user_selected_context_changed.notify
- (USER_SELECTED_INFERIOR);
- }
- }
- }
- /* Print information about currently known inferiors. */
- static void
- info_inferiors_command (const char *args, int from_tty)
- {
- print_inferior (current_uiout, args);
- }
- /* remove-inferior ID */
- static void
- remove_inferior_command (const char *args, int from_tty)
- {
- if (args == NULL || *args == '\0')
- error (_("Requires an argument (inferior id(s) to remove)"));
- number_or_range_parser parser (args);
- while (!parser.finished ())
- {
- int num = parser.get_number ();
- struct inferior *inf = find_inferior_id (num);
- if (inf == NULL)
- {
- warning (_("Inferior ID %d not known."), num);
- continue;
- }
- if (!inf->deletable ())
- {
- warning (_("Can not remove current inferior %d."), num);
- continue;
- }
-
- if (inf->pid != 0)
- {
- warning (_("Can not remove active inferior %d."), num);
- continue;
- }
- delete_inferior (inf);
- }
- }
- struct inferior *
- add_inferior_with_spaces (void)
- {
- 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 = new program_space (aspace);
- inf = add_inferior (0);
- inf->pspace = pspace;
- inf->aspace = pspace->aspace;
- /* Setup the inferior's initial arch, based on information obtained
- from the global "set ..." options. */
- gdbarch_info info;
- inf->gdbarch = gdbarch_find_by_info (info);
- /* The "set ..." options reject invalid settings, so we should
- always have a valid arch by now. */
- gdb_assert (inf->gdbarch != NULL);
- return inf;
- }
- /* See inferior.h. */
- void
- switch_to_inferior_and_push_target (inferior *new_inf,
- bool no_connection, inferior *org_inf)
- {
- process_stratum_target *proc_target = org_inf->process_target ();
- /* Switch over temporarily, while reading executable and
- symbols. */
- switch_to_inferior_no_thread (new_inf);
- /* Reuse the target for new inferior. */
- if (!no_connection && proc_target != NULL)
- {
- new_inf->push_target (proc_target);
- if (proc_target->connection_string () != NULL)
- gdb_printf (_("Added inferior %d on connection %d (%s %s)\n"),
- new_inf->num,
- proc_target->connection_number,
- proc_target->shortname (),
- proc_target->connection_string ());
- else
- gdb_printf (_("Added inferior %d on connection %d (%s)\n"),
- new_inf->num,
- proc_target->connection_number,
- proc_target->shortname ());
- }
- else
- gdb_printf (_("Added inferior %d\n"), new_inf->num);
- }
- /* add-inferior [-copies N] [-exec FILENAME] [-no-connection] */
- static void
- add_inferior_command (const char *args, int from_tty)
- {
- int i, copies = 1;
- gdb::unique_xmalloc_ptr<char> exec;
- symfile_add_flags add_flags = 0;
- bool no_connection = false;
- if (from_tty)
- add_flags |= SYMFILE_VERBOSE;
- if (args)
- {
- gdb_argv built_argv (args);
- for (char **argv = built_argv.get (); *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, "-no-connection") == 0)
- no_connection = true;
- else if (strcmp (*argv, "-exec") == 0)
- {
- ++argv;
- if (!*argv)
- error (_("No argument to -exec"));
- exec.reset (tilde_expand (*argv));
- }
- }
- else
- error (_("Invalid argument"));
- }
- }
- inferior *orginf = current_inferior ();
- scoped_restore_current_pspace_and_thread restore_pspace_thread;
- for (i = 0; i < copies; ++i)
- {
- inferior *inf = add_inferior_with_spaces ();
- switch_to_inferior_and_push_target (inf, no_connection, orginf);
- if (exec != NULL)
- {
- exec_file_attach (exec.get (), from_tty);
- symbol_file_add_main (exec.get (), add_flags);
- }
- }
- }
- /* clone-inferior [-copies N] [ID] [-no-connection] */
- static void
- clone_inferior_command (const char *args, int from_tty)
- {
- int i, copies = 1;
- struct inferior *orginf = NULL;
- bool no_connection = false;
- if (args)
- {
- gdb_argv built_argv (args);
- char **argv = built_argv.get ();
- 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 (strcmp (*argv, "-no-connection") == 0)
- no_connection = true;
- }
- 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 ();
- scoped_restore_current_pspace_and_thread restore_pspace_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 = new program_space (aspace);
- inf = add_inferior (0);
- inf->pspace = pspace;
- inf->aspace = pspace->aspace;
- inf->gdbarch = orginf->gdbarch;
- switch_to_inferior_and_push_target (inf, no_connection, orginf);
- /* If the original inferior had a user specified target
- description, make the clone use it too. */
- if (target_desc_info_from_user_p (inf->tdesc_info))
- copy_inferior_target_desc_info (inf, orginf);
- clone_program_space (pspace, orginf->pspace);
- /* Copy properties from the original inferior to the new one. */
- inf->set_args (orginf->args ());
- inf->set_cwd (orginf->cwd ());
- inf->set_tty (orginf->tty ());
- for (const std::string &set_var : orginf->environment.user_set_env ())
- {
- /* set_var has the form NAME=value. Split on the first '='. */
- const std::string::size_type pos = set_var.find ('=');
- gdb_assert (pos != std::string::npos);
- const std::string varname = set_var.substr (0, pos);
- inf->environment.set
- (varname.c_str (), orginf->environment.get (varname.c_str ()));
- }
- for (const std::string &unset_var
- : orginf->environment.user_unset_env ())
- inf->environment.unset (unset_var.c_str ());
- }
- }
- /* Print notices when new inferiors are created and die. */
- static void
- show_print_inferior_events (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
- {
- gdb_printf (file, _("Printing of inferior events is %s.\n"), value);
- }
- /* Return a new value for the selected inferior's id. */
- static struct value *
- inferior_id_make_value (struct gdbarch *gdbarch, struct internalvar *var,
- void *ignore)
- {
- struct inferior *inf = current_inferior ();
- return value_from_longest (builtin_type (gdbarch)->builtin_int, inf->num);
- }
- /* Implementation of `$_inferior' variable. */
- static const struct internalvar_funcs inferior_funcs =
- {
- inferior_id_make_value,
- NULL,
- };
- void
- initialize_inferiors (void)
- {
- struct cmd_list_element *c = NULL;
- /* 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. */
- set_current_inferior (add_inferior_silent (0));
- current_inferior_->pspace = current_program_space;
- current_inferior_->aspace = current_program_space->aspace;
- /* The architecture will be initialized shortly, by
- initialize_current_architecture. */
- add_info ("inferiors", info_inferiors_command,
- _("Print a list of inferiors being managed.\n\
- Usage: info inferiors [ID]...\n\
- If IDs are specified, the list is limited to just those inferiors.\n\
- By default all inferiors are displayed."));
- c = add_com ("add-inferior", no_class, add_inferior_command, _("\
- Add a new inferior.\n\
- Usage: add-inferior [-copies N] [-exec FILENAME] [-no-connection]\n\
- N is the optional number of inferiors to add, default is 1.\n\
- FILENAME is the file name of the executable to use\n\
- as main program.\n\
- By default, the new inferior inherits the current inferior's connection.\n\
- If -no-connection is specified, the new inferior begins with\n\
- no target connection yet."));
- set_cmd_completer (c, filename_completer);
- add_com ("remove-inferiors", no_class, remove_inferior_command, _("\
- Remove inferior ID (or list of IDs).\n\
- Usage: remove-inferiors ID..."));
- add_com ("clone-inferior", no_class, clone_inferior_command, _("\
- Clone inferior ID.\n\
- Usage: clone-inferior [-copies N] [-no-connection] [ID]\n\
- Add N copies of inferior ID. The new inferiors have 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.\n\
- By default, the new inferiors inherit the copied inferior's connection.\n\
- If -no-connection is specified, the new inferiors begin with\n\
- no target connection yet."));
- add_cmd ("inferiors", class_run, detach_inferior_command, _("\
- Detach from inferior ID (or list of IDS).\n\
- Usage; detach inferiors ID..."),
- &detachlist);
- add_cmd ("inferiors", class_run, kill_inferior_command, _("\
- Kill inferior ID (or list of IDs).\n\
- Usage: kill inferiors ID..."),
- &killlist);
- add_cmd ("inferior", class_run, inferior_command, _("\
- Use this command to switch between inferiors.\n\
- Usage: inferior ID\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 (such as inferior start and exit)."), _("\
- Show printing of inferior events (such as inferior start and exit)."), NULL,
- NULL,
- show_print_inferior_events,
- &setprintlist, &showprintlist);
- create_internalvar_type_lazy ("_inferior", &inferior_funcs, NULL);
- }
|