Multiexec MI

* breakpoint.c (clear_syscall_counts): Take struct inferior*.
	* inferior.c (add_inferior_silent): Notify inferior_added
	observer.
	(delete_inferior_1): Notify inferior_removed observer.
	(exit_inferior_1): Pass inferior, not pid, to observer.
	(inferior_appeared): Likewise.
	(add_inferior_with_spaces): New.
	(add_inferior_command): Use the above.
	* inferior.h (delete_inferior_1, add_inferior_with_spaces):
	Declare.

	* inflow.c (inflow_inferior_exit): Likewise.
	* jit.c (jit_inferior_exit_hook): Likewise.

	* mi/mi-cmds.c (mi_cmds): Register add-inferior and
	remove-inferior.
	* mi/mi-cmds.h (mi_cmd_add_inferior, mi_cmd_remove_inferior): New.
	* mi/mi-interp.c (mi_inferior_added, mi_inferior_removed): New.
	(report_initial_inferior): New.
	(mi_inferior_removed): Register the above. Make sure
	inferior_added observer is called on the first inferior.
	(mi_new_thread, mi_thread_exit): Thread group is now identified by
	inferior number, not pid.
	(mi_solib_loaded, mi_solib_unloaded): Report which inferiors are
	affected.
	* mi/mi-main.c (current_context): New.
	(proceed_thread_callback): Use typed closure.
	Proceed everything if pid is 0. Most implementation split into
	(proceed_thread): ... this.
	(run_one_inferior): New.
	(mi_cmd_exec_continue, mi_cmd_exec_interrupt, mi_cmd_exec_run):
	Adjust for multiexec behaviour.
	(mi_cmd_add_inferior, mi_cmd_remove_inferior): New.
	(mi_cmd_execute): Handle the 'thread-group' option here.
	Do some extra checks.
	* mi-parse.c (mi_parse): Handle the --all and --thread-group
	options.
	* mi-parse.h (struct mi_parse): New fields all and thread_group.
This commit is contained in:
Vladimir Prus 2010-02-24 07:51:46 +00:00
parent 115d30f9b6
commit a79b8f6ea8
16 changed files with 516 additions and 127 deletions

View File

@ -1,3 +1,46 @@
2010-02-24 Vladimir Prus <vladimir@codesourcery.com>
Multiexec MI
* breakpoint.c (clear_syscall_counts): Take struct inferior*.
* inferior.c (add_inferior_silent): Notify inferior_added
observer.
(delete_inferior_1): Notify inferior_removed observer.
(exit_inferior_1): Pass inferior, not pid, to observer.
(inferior_appeared): Likewise.
(add_inferior_with_spaces): New.
(add_inferior_command): Use the above.
* inferior.h (delete_inferior_1, add_inferior_with_spaces):
Declare.
* inflow.c (inflow_inferior_exit): Likewise.
* jit.c (jit_inferior_exit_hook): Likewise.
* mi/mi-cmds.c (mi_cmds): Register add-inferior and
remove-inferior.
* mi/mi-cmds.h (mi_cmd_add_inferior, mi_cmd_remove_inferior): New.
* mi/mi-interp.c (mi_inferior_added, mi_inferior_removed): New.
(report_initial_inferior): New.
(mi_inferior_removed): Register the above. Make sure
inferior_added observer is called on the first inferior.
(mi_new_thread, mi_thread_exit): Thread group is now identified by
inferior number, not pid.
(mi_solib_loaded, mi_solib_unloaded): Report which inferiors are
affected.
* mi/mi-main.c (current_context): New.
(proceed_thread_callback): Use typed closure.
Proceed everything if pid is 0. Most implementation split into
(proceed_thread): ... this.
(run_one_inferior): New.
(mi_cmd_exec_continue, mi_cmd_exec_interrupt, mi_cmd_exec_run):
Adjust for multiexec behaviour.
(mi_cmd_add_inferior, mi_cmd_remove_inferior): New.
(mi_cmd_execute): Handle the 'thread-group' option here.
Do some extra checks.
* mi-parse.c (mi_parse): Handle the --all and --thread-group
options.
* mi-parse.h (struct mi_parse): New fields all and thread_group.
2010-02-24 Vladimir Prus <vladimir@codesourcery.com>
Make -exec-run a proper MI commands.

View File

@ -10366,10 +10366,8 @@ add_catch_command (char *name, char *docstring,
}
static void
clear_syscall_counts (int pid)
clear_syscall_counts (struct inferior *inf)
{
struct inferior *inf = find_inferior_pid (pid);
inf->total_syscalls_count = 0;
inf->any_syscall_count = 0;
VEC_free (int, inf->syscalls_counts);

View File

@ -1,3 +1,20 @@
2010-02-24 Vladimir Prus <vladimir@codesourcery.com>
Multiexec MI
gdb/
* breakpoint.c (clear_syscall_counts): Take struct inferior*.
gdb/doc/
* gdb.texinfo (GDB/MI Command Syntax): Document notification
changes.
(GDB/MI Program Execution): Document current behaviour of
--all and --thread-group.
(GDB/MI Miscellaneous Commands): Document -add-inferior and
-remove-inferior.
* observer.texi (inferior_added, inferior_removed): New
observers.
2010-02-19 Tom Tromey <tromey@redhat.com>
* gdbint.texinfo (Getting Started): Fix @node.

View File

@ -21736,6 +21736,11 @@ groups can be obtained using @samp{-list-thread-groups --available}.
In general, the content of a thread group may be only retrieved only
after attaching to that thread group.
Thread groups are related to inferiors (@pxref{Inferiors and
Programs}). Each inferior corresponds to a thread group of a special
type @samp{process}, and some additional operations are permitted on
such thread groups.
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI Command Syntax
@section @sc{gdb/mi} Command Syntax
@ -22180,9 +22185,24 @@ several threads in the list. The @var{core} field reports the
processor core on which the stop event has happened. This field may be absent
if such information is not available.
@item =thread-group-created,id="@var{id}"
@item =thread-group-added,id="@var{id}"
@itemx =thread-group-removed,id="@var{id}"
A thread group was either added or removed. The @var{id} field
contains the @value{GDBN} identifier of the thread group. When a thread
group is added, it generally might not be associated with a running
process. When a thread group is removed, its id becomes invalid and
cannot be used in any way.
@item =thread-group-started,id="@var{id}",pid="@var{pid}"
A thread group became associated with a running program,
either because the program was just started or the thread group
was attached to a program. The @var{id} field contains the
@value{GDBN} identifier of the thread group. The @var{pid} field
contains process identifier, specific to the operating system.
@itemx =thread-group-exited,id="@var{id}"
A thread thread group either was attached to, or has exited/detached
A thread group is no longer associated with a running program,
either because the program has exited, or because it was detached
from. The @var{id} field contains the @value{GDBN} identifier of the
thread group.
@ -22213,12 +22233,19 @@ opaque identifier of the library. For remote debugging case,
library file on the target, and on the host respectively. For native
debugging, both those fields have the same value. The
@var{symbols-loaded} field reports if the debug symbols for this
library are loaded.
library are loaded. The @var{thread-group} field, if present,
specifies the id of the thread group in whose context the library was loaded.
If the field is absent, it means the library was loaded in the context
of all present thread groups.
@item =library-unloaded,...
Reports that a library was unloaded by the program. This notification
has 3 fields---@var{id}, @var{target-name} and @var{host-name} with
the same meaning as for the @code{=library-loaded} notification
the same meaning as for the @code{=library-loaded} notification.
The @var{thread-group} field, if present, specifies the id of the
thread group in whose context the library was unloaded. If the field is
absent, it means the library was unloaded in the context of all present
thread groups.
@end table
@ -23337,7 +23364,7 @@ the end or beginning of a replay log if one is being used.
In all-stop mode (@pxref{All-Stop
Mode}), may resume only one thread, or all threads, depending on the
value of the @samp{scheduler-locking} variable. If @samp{--all} is
specified, all threads will be resumed. The @samp{--all} option is
specified, all threads (in all inferiors) will be resumed. The @samp{--all} option is
ignored in all-stop mode. If the @samp{--thread-group} options is
specified, then all threads in that thread group are resumed.
@ -23429,9 +23456,9 @@ asynchronous just like other execution commands. That is, first the
reported after that using the @samp{*stopped} notification.
In non-stop mode, only the context thread is interrupted by default.
All threads will be interrupted if the @samp{--all} option is
specified. If the @samp{--thread-group} option is specified, all
threads in that group will be interrupted.
All threads (in all inferiors) will be interrupted if the
@samp{--all} option is specified. If the @samp{--thread-group}
option is specified, all threads in that group will be interrupted.
@subsubheading @value{GDBN} Command
@ -23608,7 +23635,7 @@ fullname="/home/foo/bar/devo/gdb/testsuite/gdb.mi/basics.c",line="18"@}
@subsubheading Synopsis
@smallexample
-exec-run
-exec-run [--all | --thread-group N]
@end smallexample
Starts execution of the inferior from the beginning. The inferior
@ -23616,6 +23643,11 @@ executes until either a breakpoint is encountered or the program
exits. In the latter case the output will include an exit code, if
the program has exited exceptionally.
When no option is specified, the current inferior is started. If the
@samp{--thread-group} option is specified, it should refer to a thread
group of type @samp{process}, and that thread group will be started.
If the @samp{--all} option is specified, then all inferiors will be started.
@subsubheading @value{GDBN} Command
The corresponding @value{GDBN} command is @samp{run}.
@ -26678,6 +26710,8 @@ have the following fields:
@table @code
@item id
Identifier of the thread group. This field is always present.
The identifier is an opaque string; frontends should not try to
convert it to an integer, even though it might look like one.
@item type
The type of the thread group. At present, only @samp{process} is a
@ -26685,7 +26719,7 @@ valid type.
@item pid
The target-specific process identifier. This field is only present
for thread groups of type @samp{process}.
for thread groups of type @samp{process} and only if the process exists.
@item num_children
The number of children this thread group has. This field may be
@ -26701,6 +26735,11 @@ This field is a list of integers, each identifying a core that one
thread of the group is running on. This field may be absent if
such information is not available.
@item executable
The name of the executable file that corresponds to this thread group.
The field is only present for thread groups of type @samp{process},
and only if there is a corresponding executable file.
@end table
@subheading Example
@ -26727,6 +26766,31 @@ such information is not available.
@{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},...]
@end smallexample
@subheading The @code{-add-inferior} Command
@findex -add-inferior
@subheading Synopsis
@smallexample
-add-inferior
@end smallexample
Creates a new inferior (@pxref{Inferiors and Programs}). The created
inferior is not associated with any executable. Such association may
be established with the @samp{-file-exec-and-symbols} command
(@pxref{GDB/MI File Commands}). The command response has a single
field, @samp{thread-group}, whose value is the identifier of the
thread group corresponding to the new inferior.
@subheading Example
@smallexample
@value{GDBP}
-add-inferior
^done,thread-group="i3"
@end smallexample
@subheading The @code{-interpreter-exec} Command
@findex -interpreter-exec

View File

@ -199,13 +199,23 @@ The thread's ptid has changed. The @var{old_ptid} parameter specifies
the old value, and @var{new_ptid} specifies the new value.
@end deftypefun
@deftypefun void inferior_appeared (int @var{pid})
@value{GDBN} has attached to a new inferior identified by @var{pid}.
@deftypefun void inferior_added (struct inferior *@var{inf})
The inferior @var{inf} has been added to the list of inferiors. At
this point, it might not be associated with any process.
@end deftypefun
@deftypefun void inferior_exit (int @var{pid})
Either @value{GDBN} detached from the inferior, or the inferior
exited. The argument @var{pid} identifies the inferior.
@deftypefun void inferior_appeared (struct inferior *@var{inf})
The inferior identified by @var{inf} has been attached to a process.
@end deftypefun
@deftypefun void inferior_exit (struct inferior *@var{inf})
Either the inferior associated with @var{inf} has been detached from the
process, or the process has exited.
@end deftypefun
@deftypefun void inferior_removed (struct inferior *@var{inf})
The inferior @var{inf} has been removed from the list of inferiors.
This method is called immediately before freeing @var{inf}.
@end deftypefun
@deftypefun void memory_changed (CORE_ADDR @var{addr}, int @var{len}, const bfd_byte *@var{data})
@ -213,8 +223,8 @@ Bytes from @var{data} to @var{data} + @var{len} have been written
to the current inferior at @var{addr}.
@end deftypefun
@deftypefun void test_notification (int @var{somearg})
@deftypefun void test_notification (int @var{somearg})
This observer is used for internal testing. Do not use.
See testsuite/gdb.gdb/observer.exp.
@end deftypefun
@end deftypefun

View File

@ -133,6 +133,8 @@ add_inferior_silent (int pid)
inferior_alloc_data (inf);
observer_notify_inferior_added (inf);
if (pid != 0)
inferior_appeared (inf, pid);
@ -194,7 +196,7 @@ delete_threads_of_inferior (int pid)
/* If SILENT then be quiet -- don't announce a inferior death, or the
exit of its threads. */
static void
void
delete_inferior_1 (struct inferior *todel, int silent)
{
struct inferior *inf, *infprev;
@ -219,6 +221,8 @@ delete_inferior_1 (struct inferior *todel, int silent)
else
inferior_list = inf->next;
observer_notify_inferior_removed (inf);
free_inferior (inf);
}
@ -265,7 +269,7 @@ exit_inferior_1 (struct inferior *inftoex, int silent)
/* 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);
observer_notify_inferior_exit (inf);
inf->pid = 0;
if (inf->vfork_parent != NULL)
@ -315,7 +319,7 @@ inferior_appeared (struct inferior *inf, int pid)
{
inf->pid = pid;
observer_notify_inferior_appeared (pid);
observer_notify_inferior_appeared (inf);
}
void
@ -738,6 +742,24 @@ remove_inferior_command (char *args, int from_tty)
delete_inferior_1 (inf, 1);
}
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 = add_program_space (aspace);
inf = add_inferior (0);
inf->pspace = pspace;
inf->aspace = pspace->aspace;
return inf;
}
/* add-inferior [-copies N] [-exec FILENAME] */
@ -782,18 +804,7 @@ add_inferior_command (char *args, int from_tty)
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;
struct inferior *inf = add_inferior_with_spaces ();
printf_filtered (_("Added inferior %d\n"), inf->num);
@ -801,7 +812,7 @@ add_inferior_command (char *args, int from_tty)
{
/* Switch over temporarily, while reading executable and
symbols.q */
set_current_program_space (pspace);
set_current_program_space (inf->pspace);
set_current_inferior (inf);
switch_to_thread (null_ptid);

View File

@ -523,6 +523,8 @@ extern struct inferior *add_inferior_silent (int pid);
/* Delete an existing inferior list entry, due to inferior exit. */
extern void delete_inferior (int pid);
extern void delete_inferior_1 (struct inferior *todel, int silent);
/* Same as delete_inferior, but don't print new inferior notifications
to the CLI. */
extern void delete_inferior_silent (int pid);
@ -609,4 +611,6 @@ extern void prune_inferiors (void);
extern int number_of_inferiors (void);
extern struct inferior *add_inferior_with_spaces (void);
#endif /* !defined (INFERIOR_H) */

View File

@ -500,9 +500,8 @@ get_inflow_inferior_data (struct inferior *inf)
list. */
static void
inflow_inferior_exit (int pid)
inflow_inferior_exit (struct inferior *inf)
{
struct inferior *inf = find_inferior_pid (pid);
struct terminal_info *info;
info = inferior_data (inf, inflow_inferior_data);

View File

@ -397,7 +397,7 @@ jit_inferior_created_observer (struct target_ops *objfile, int from_tty)
for example when it crashes. */
static void
jit_inferior_exit_hook (int pid)
jit_inferior_exit_hook (struct inferior *inf)
{
struct objfile *objf;
struct objfile *temp;

View File

@ -33,6 +33,7 @@ static void build_table (struct mi_cmd *commands);
struct mi_cmd mi_cmds[] =
{
{ "add-inferior", { NULL, 0 }, mi_cmd_add_inferior },
{ "break-after", { "ignore", 1 }, NULL },
{ "break-condition", { "cond", 1 }, NULL },
{ "break-commands", { NULL, 0 }, mi_cmd_break_commands },
@ -84,6 +85,7 @@ struct mi_cmd mi_cmds[] =
{ "list-features", { NULL, 0 }, mi_cmd_list_features},
{ "list-target-features", { NULL, 0 }, mi_cmd_list_target_features},
{ "list-thread-groups", { NULL, 0 }, mi_cmd_list_thread_groups },
{ "remove-inferior", { NULL, 0 }, mi_cmd_remove_inferior },
{ "stack-info-depth", { NULL, 0 }, mi_cmd_stack_info_depth},
{ "stack-info-frame", { NULL, 0 }, mi_cmd_stack_info_frame},
{ "stack-list-arguments", { NULL, 0 }, mi_cmd_stack_list_args},

View File

@ -36,6 +36,7 @@ extern const char mi_all_values[];
typedef void (mi_cmd_argv_ftype) (char *command, char **argv, int argc);
/* Function implementing each command */
extern mi_cmd_argv_ftype mi_cmd_add_inferior;
extern mi_cmd_argv_ftype mi_cmd_break_insert;
extern mi_cmd_argv_ftype mi_cmd_break_commands;
extern mi_cmd_argv_ftype mi_cmd_break_watch;
@ -71,6 +72,7 @@ extern mi_cmd_argv_ftype mi_cmd_interpreter_exec;
extern mi_cmd_argv_ftype mi_cmd_list_features;
extern mi_cmd_argv_ftype mi_cmd_list_target_features;
extern mi_cmd_argv_ftype mi_cmd_list_thread_groups;
extern mi_cmd_argv_ftype mi_cmd_remove_inferior;
extern mi_cmd_argv_ftype mi_cmd_stack_info_depth;
extern mi_cmd_argv_ftype mi_cmd_stack_info_frame;
extern mi_cmd_argv_ftype mi_cmd_stack_list_args;

View File

@ -56,13 +56,17 @@ static void mi_on_normal_stop (struct bpstats *bs, int print_frame);
static void mi_new_thread (struct thread_info *t);
static void mi_thread_exit (struct thread_info *t, int silent);
static void mi_inferior_appeared (int pid);
static void mi_inferior_exit (int pid);
static void mi_inferior_added (struct inferior *inf);
static void mi_inferior_appeared (struct inferior *inf);
static void mi_inferior_exit (struct inferior *inf);
static void mi_inferior_removed (struct inferior *inf);
static void mi_on_resume (ptid_t ptid);
static void mi_solib_loaded (struct so_list *solib);
static void mi_solib_unloaded (struct so_list *solib);
static void mi_about_to_proceed (void);
static int report_initial_inferior (struct inferior *inf, void *closure);
static void *
mi_interpreter_init (int top_level)
{
@ -86,13 +90,20 @@ mi_interpreter_init (int top_level)
{
observer_attach_new_thread (mi_new_thread);
observer_attach_thread_exit (mi_thread_exit);
observer_attach_inferior_added (mi_inferior_added);
observer_attach_inferior_appeared (mi_inferior_appeared);
observer_attach_inferior_exit (mi_inferior_exit);
observer_attach_inferior_removed (mi_inferior_removed);
observer_attach_normal_stop (mi_on_normal_stop);
observer_attach_target_resumed (mi_on_resume);
observer_attach_solib_loaded (mi_solib_loaded);
observer_attach_solib_unloaded (mi_solib_unloaded);
observer_attach_about_to_proceed (mi_about_to_proceed);
/* The initial inferior is created before this function is called, so we
need to report it explicitly. Use iteration in case future version
of GDB creates more than one inferior up-front. */
iterate_over_inferiors (report_initial_inferior, mi);
}
return mi;
@ -285,10 +296,13 @@ static void
mi_new_thread (struct thread_info *t)
{
struct mi_interp *mi = top_level_interpreter_data ();
struct inferior *inf = find_inferior_pid (ptid_get_pid (t->ptid));
gdb_assert (inf);
fprintf_unfiltered (mi->event_channel,
"thread-created,id=\"%d\",group-id=\"%d\"",
t->num, t->ptid.pid);
"thread-created,id=\"%d\",group-id=\"i%d\"",
t->num, inf->num);
gdb_flush (mi->event_channel);
}
@ -296,38 +310,64 @@ static void
mi_thread_exit (struct thread_info *t, int silent)
{
struct mi_interp *mi;
struct inferior *inf;
if (silent)
return;
inf = find_inferior_pid (ptid_get_pid (t->ptid));
mi = top_level_interpreter_data ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
"thread-exited,id=\"%d\",group-id=\"%d\"",
t->num,t->ptid.pid);
gdb_flush (mi->event_channel);
}
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\"",
pid);
"thread-exited,id=\"%d\",group-id=\"i%d\"",
t->num, inf->num);
gdb_flush (mi->event_channel);
}
static void
mi_inferior_exit (int pid)
mi_inferior_added (struct inferior *inf)
{
struct mi_interp *mi = top_level_interpreter_data ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel, "thread-group-exited,id=\"%d\"",
pid);
fprintf_unfiltered (mi->event_channel,
"thread-group-added,id=\"i%d\"",
inf->num);
gdb_flush (mi->event_channel);
}
static void
mi_inferior_appeared (struct inferior *inf)
{
struct mi_interp *mi = top_level_interpreter_data ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
"thread-group-started,id=\"i%d\",pid=\"%d\"",
inf->num, inf->pid);
gdb_flush (mi->event_channel);
}
static void
mi_inferior_exit (struct inferior *inf)
{
struct mi_interp *mi = top_level_interpreter_data ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel, "thread-group-exited,id=\"i%d\"",
inf->num);
gdb_flush (mi->event_channel);
}
static void
mi_inferior_removed (struct inferior *inf)
{
struct mi_interp *mi = top_level_interpreter_data ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
"thread-group-removed,id=\"i%d\"",
inf->num);
gdb_flush (mi->event_channel);
}
static void
mi_on_normal_stop (struct bpstats *bs, int print_frame)
{
@ -489,10 +529,21 @@ mi_solib_loaded (struct so_list *solib)
{
struct mi_interp *mi = top_level_interpreter_data ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
"library-loaded,id=\"%s\",target-name=\"%s\",host-name=\"%s\",symbols-loaded=\"%d\"",
solib->so_original_name, solib->so_original_name,
solib->so_name, solib->symbols_loaded);
if (gdbarch_has_global_solist (target_gdbarch))
fprintf_unfiltered (mi->event_channel,
"library-loaded,id=\"%s\",target-name=\"%s\","
"host-name=\"%s\",symbols-loaded=\"%d\"",
solib->so_original_name, solib->so_original_name,
solib->so_name, solib->symbols_loaded);
else
fprintf_unfiltered (mi->event_channel,
"library-loaded,id=\"%s\",target-name=\"%s\","
"host-name=\"%s\",symbols-loaded=\"%d\","
"thread-group=\"i%d\"",
solib->so_original_name, solib->so_original_name,
solib->so_name, solib->symbols_loaded,
current_inferior ()->num);
gdb_flush (mi->event_channel);
}
@ -501,13 +552,37 @@ mi_solib_unloaded (struct so_list *solib)
{
struct mi_interp *mi = top_level_interpreter_data ();
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
"library-unloaded,id=\"%s\",target-name=\"%s\",host-name=\"%s\"",
solib->so_original_name, solib->so_original_name,
solib->so_name);
if (gdbarch_has_global_solist (target_gdbarch))
fprintf_unfiltered (mi->event_channel,
"library-unloaded,id=\"%s\",target-name=\"%s\","
"host-name=\"%s\"",
solib->so_original_name, solib->so_original_name,
solib->so_name);
else
fprintf_unfiltered (mi->event_channel,
"library-unloaded,id=\"%s\",target-name=\"%s\","
"host-name=\"%s\",thread-group=\"i%d\"",
solib->so_original_name, solib->so_original_name,
solib->so_name, current_inferior ()->num);
gdb_flush (mi->event_channel);
}
static int
report_initial_inferior (struct inferior *inf, void *closure)
{
/* This function is called from mi_intepreter_init, and since
mi_inferior_added assumes that inferior is fully initialized
and top_level_interpreter_data is set, we cannot call
it here. */
struct mi_interp *mi = closure;
target_terminal_ours ();
fprintf_unfiltered (mi->event_channel,
"thread-group-added,id=\"i%d\"",
inf->num);
gdb_flush (mi->event_channel);
return 0;
}
extern initialize_file_ftype _initialize_mi_interp; /* -Wmissing-prototypes */

View File

@ -78,6 +78,11 @@ static struct mi_timestamp *current_command_ts;
static int do_timings = 0;
char *current_token;
/* Few commands would like to know if options like --thread-group
were explicitly specified. This variable keeps the current
parsed command including all option, and make it possible. */
static struct mi_parse *current_context;
int running_result_record_printed = 1;
/* Flag indicating that the target has proceeded since the last
@ -193,55 +198,79 @@ mi_cmd_exec_jump (char *args, char **argv, int argc)
mi_execute_async_cli_command ("jump", argv, argc);
}
static int
proceed_thread_callback (struct thread_info *thread, void *arg)
static void
proceed_thread (struct thread_info *thread, int pid)
{
int pid = *(int *)arg;
if (!is_stopped (thread->ptid))
return 0;
return;
if (PIDGET (thread->ptid) != pid)
return 0;
if (pid != 0 && PIDGET (thread->ptid) != pid)
return;
switch_to_thread (thread->ptid);
clear_proceed_status ();
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
}
static int
proceed_thread_callback (struct thread_info *thread, void *arg)
{
int pid = *(int *)arg;
proceed_thread (thread, pid);
return 0;
}
static void
exec_continue (char **argv, int argc)
{
if (argc == 0)
continue_1 (0);
else if (argc == 1 && strcmp (argv[0], "--all") == 0)
continue_1 (1);
else if (argc == 2 && strcmp (argv[0], "--thread-group") == 0)
if (non_stop)
{
struct cleanup *old_chain;
int pid;
if (argv[1] == NULL || argv[1] == '\0')
error ("Thread group id not specified");
pid = atoi (argv[1]);
if (!in_inferior_list (pid))
error ("Invalid thread group id '%s'", argv[1]);
/* In non-stop mode, 'resume' always resumes a single thread. Therefore,
to resume all threads of the current inferior, or all threads in all
inferiors, we need to iterate over threads.
old_chain = make_cleanup_restore_current_thread ();
iterate_over_threads (proceed_thread_callback, &pid);
do_cleanups (old_chain);
See comment on infcmd.c:proceed_thread_callback for rationale. */
if (current_context->all || current_context->thread_group != -1)
{
int pid = 0;
struct cleanup *back_to = make_cleanup_restore_current_thread ();
if (!current_context->all)
{
struct inferior *inf = find_inferior_id (current_context->thread_group);
pid = inf->pid;
}
iterate_over_threads (proceed_thread_callback, &pid);
do_cleanups (back_to);
}
else
{
continue_1 (0);
}
}
else
error ("Usage: -exec-continue [--reverse] [--all|--thread-group id]");
{
struct cleanup *back_to = make_cleanup_restore_integer (&sched_multi);
if (current_context->all)
{
sched_multi = 1;
continue_1 (0);
}
else
{
/* In all-stop mode, -exec-continue traditionally resumed either
all threads, or one thread, depending on the 'scheduler-locking'
variable. Let's continue to do the same. */
continue_1 (1);
}
do_cleanups (back_to);
}
}
/* continue in reverse direction:
XXX: code duplicated from reverse.c */
static void
exec_direction_default (void *notused)
exec_direction_forward (void *notused)
{
/* Return execution direction to default state. */
execution_direction = EXEC_FORWARD;
}
@ -260,7 +289,7 @@ exec_reverse_continue (char **argv, int argc)
if (!target_can_execute_reverse)
error (_("Target %s does not support this command."), target_shortname);
old_chain = make_cleanup (exec_direction_default, NULL);
old_chain = make_cleanup (exec_direction_forward, NULL);
execution_direction = EXEC_REVERSE;
exec_continue (argv, argc);
do_cleanups (old_chain);
@ -269,7 +298,7 @@ exec_reverse_continue (char **argv, int argc)
void
mi_cmd_exec_continue (char *command, char **argv, int argc)
{
if (argc > 0 && strcmp(argv[0], "--reverse") == 0)
if (argc > 0 && strcmp (argv[0], "--reverse") == 0)
exec_reverse_continue (argv + 1, argc - 1);
else
exec_continue (argv, argc);
@ -298,45 +327,79 @@ interrupt_thread_callback (struct thread_info *thread, void *arg)
void
mi_cmd_exec_interrupt (char *command, char **argv, int argc)
{
if (argc == 0)
/* In all-stop mode, everything stops, so we don't need to try
anything specific. */
if (!non_stop)
{
if (!is_running (inferior_ptid))
error ("Current thread is not running.");
interrupt_target_1 (0);
return;
}
else if (argc == 1 && strcmp (argv[0], "--all") == 0)
if (current_context->all)
{
if (!any_running ())
error ("Inferior not running.");
/* This will interrupt all threads in all inferiors. */
interrupt_target_1 (1);
}
else if (argc == 2 && strcmp (argv[0], "--thread-group") == 0)
else if (current_context->thread_group != -1)
{
struct cleanup *old_chain;
int pid;
if (argv[1] == NULL || argv[1] == '\0')
error ("Thread group id not specified");
pid = atoi (argv[1]);
if (!in_inferior_list (pid))
error ("Invalid thread group id '%s'", argv[1]);
old_chain = make_cleanup_restore_current_thread ();
iterate_over_threads (interrupt_thread_callback, &pid);
do_cleanups (old_chain);
struct inferior *inf = find_inferior_id (current_context->thread_group);
iterate_over_threads (interrupt_thread_callback, &inf->pid);
}
else
error ("Usage: -exec-interrupt [--all|--thread-group id]");
{
/* Interrupt just the current thread -- either explicitly
specified via --thread or whatever was current before
MI command was sent. */
interrupt_target_1 (0);
}
}
static int
run_one_inferior (struct inferior *inf, void *arg)
{
struct thread_info *tp = 0;
if (inf->pid != 0)
{
if (inf->pid != ptid_get_pid (inferior_ptid))
{
struct thread_info *tp;
tp = any_thread_of_process (inf->pid);
if (!tp)
error (_("Inferior has no threads."));
switch_to_thread (tp->ptid);
}
}
else
{
set_current_inferior (inf);
switch_to_thread (null_ptid);
set_current_program_space (inf->pspace);
}
mi_execute_cli_command ("run", target_can_async_p (),
target_can_async_p () ? "&" : NULL);
return 0;
}
void
mi_cmd_exec_run (char *command, char **argv, int argc)
{
mi_execute_cli_command ("run", target_can_async_p (),
target_can_async_p () ? "&" : NULL);
if (current_context->all)
{
struct cleanup *back_to = save_current_space_and_thread ();
iterate_over_inferiors (run_one_inferior, NULL);
do_cleanups (back_to);
}
else
{
mi_execute_cli_command ("run", target_can_async_p (),
target_can_async_p () ? "&" : NULL);
}
}
static int
find_thread_of_process (struct thread_info *ti, void *p)
{
@ -475,13 +538,23 @@ print_one_inferior (struct inferior *inferior, void *xdata)
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_fmt (uiout, "id", "i%d", inferior->num);
ui_out_field_string (uiout, "type", "process");
ui_out_field_int (uiout, "pid", inferior->pid);
if (inferior->pid != 0)
ui_out_field_int (uiout, "pid", inferior->pid);
if (inferior->pspace->ebfd)
{
ui_out_field_string (uiout, "executable",
bfd_get_filename (inferior->pspace->ebfd));
}
data.pid = inferior->pid;
data.cores = 0;
iterate_over_threads (collect_cores, &data);
if (inferior->pid != 0)
{
data.pid = inferior->pid;
iterate_over_threads (collect_cores, &data);
}
if (!VEC_empty (int, data.cores))
{
@ -1492,6 +1565,40 @@ mi_cmd_list_target_features (char *command, char **argv, int argc)
error ("-list-target-features should be passed no arguments");
}
void
mi_cmd_add_inferior (char *command, char **argv, int argc)
{
struct inferior *inf;
if (argc != 0)
error (_("-add-inferior should be passed no arguments"));
inf = add_inferior_with_spaces ();
ui_out_field_fmt (uiout, "inferior", "i%d", inf->num);
}
void
mi_cmd_remove_inferior (char *command, char **argv, int argc)
{
int id;
struct inferior *inf;
if (argc != 1)
error ("-remove-inferior should be passed a single argument");
if (sscanf (argv[1], "i%d", &id) != 1)
error ("the thread group id is syntactically invalid");
inf = find_inferior_id (id);
if (!inf)
error ("the specified thread group does not exist");
delete_inferior_1 (inf, 1 /* silent */);
}
/* Execute a command within a safe environment.
Return <0 for error; >=0 for ok.
@ -1693,9 +1800,37 @@ mi_cmd_execute (struct mi_parse *parse)
cleanup = make_cleanup (null_cleanup, NULL);
if (parse->all && parse->thread_group != -1)
error (_("Cannot specify --thread-group together with --all"));
if (parse->all && parse->thread != -1)
error (_("Cannot specify --thread together with --all"));
if (parse->thread_group != -1 && parse->thread != -1)
error (_("Cannot specify --thread together with --thread-group"));
if (parse->frame != -1 && parse->thread == -1)
error (_("Cannot specify --frame without --thread"));
if (parse->thread_group != -1)
{
struct inferior *inf = find_inferior_id (parse->thread_group);
struct thread_info *tp = 0;
if (!inf)
error (_("Invalid thread group for the --tread-group option"));
set_current_inferior (inf);
/* This behaviour means that if --thread-group option identifies
an inferior with multiple threads, then a random one will be picked.
This is not a problem -- frontend should always provide --thread if
it wishes to operate on a specific thread. */
if (inf->pid != 0)
tp = any_thread_of_process (inf->pid);
switch_to_thread (tp ? tp->ptid : null_ptid);
set_current_program_space (inf->pspace);
}
if (parse->thread != -1)
{
struct thread_info *tp = find_thread_id (parse->thread);
@ -1720,6 +1855,8 @@ mi_cmd_execute (struct mi_parse *parse)
error (_("Invalid frame id: %d"), frame);
}
current_context = parse;
if (parse->cmd->argv_func != NULL)
parse->cmd->argv_func (parse->command, parse->argv, parse->argc);
else if (parse->cmd->cli.cmd != 0)

View File

@ -151,6 +151,8 @@ mi_parse (char *cmd)
char *chp;
struct mi_parse *parse = XMALLOC (struct mi_parse);
memset (parse, 0, sizeof (*parse));
parse->all = 0;
parse->thread_group = -1;
parse->thread = -1;
parse->frame = -1;
@ -210,19 +212,42 @@ mi_parse (char *cmd)
for (;;)
{
char *start = chp;
size_t as = sizeof ("--all ") - 1;
size_t tgs = sizeof ("--thread-group ") - 1;
size_t ts = sizeof ("--thread ") - 1;
size_t fs = sizeof ("--frame ") - 1;
if (strncmp (chp, "--all ", as) == 0)
{
parse->all = 1;
chp += as;
}
/* See if --all is the last token in the input. */
if (strcmp (chp, "--all") == 0)
{
parse->all = 1;
chp += strlen (chp);
}
if (strncmp (chp, "--thread-group ", tgs) == 0)
{
if (parse->thread_group != -1)
error (_("Duplicate '--thread-group' option"));
chp += tgs;
if (*chp != 'i')
error (_("Invalid thread group id"));
chp += 1;
parse->thread_group = strtol (chp, &chp, 10);
}
if (strncmp (chp, "--thread ", ts) == 0)
{
if (parse->thread != -1)
error ("Duplicate '--thread' option");
error (_("Duplicate '--thread' option"));
chp += ts;
parse->thread = strtol (chp, &chp, 10);
}
else if (strncmp (chp, "--frame ", fs) == 0)
{
if (parse->frame != -1)
error ("Duplicate '--frame' option");
error (_("Duplicate '--frame' option"));
chp += fs;
parse->frame = strtol (chp, &chp, 10);
}
@ -230,7 +255,7 @@ mi_parse (char *cmd)
break;
if (*chp != '\0' && !isspace (*chp))
error ("Invalid value for the '%s' option",
error (_("Invalid value for the '%s' option"),
start[2] == 't' ? "--thread" : "--frame");
while (isspace (*chp))
chp++;

View File

@ -46,6 +46,8 @@ struct mi_parse
char *args;
char **argv;
int argc;
int all;
int thread_group; /* At present, the same as inferior number. */
int thread;
int frame;
};

View File

@ -160,7 +160,7 @@ if { [is_remote target] } {
unsupported $test
} else {
gdb_expect {
-re ".*=thread-exited,id=\"2\",group-id=\"\[0-9\]+\"\r\n$" {
-re ".*=thread-exited,id=\"2\",group-id=\"i\[0-9\]+\"\r\n$" {
pass $test
}
timeout {