From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 22578 invoked by alias); 24 Feb 2010 07:53:01 -0000 Received: (qmail 22497 invoked by uid 22791); 24 Feb 2010 07:52:53 -0000 X-SWARE-Spam-Status: No, hits=-2.4 required=5.0 tests=AWL,BAYES_00 X-Spam-Check-By: sourceware.org Received: from mail.codesourcery.com (HELO mail.codesourcery.com) (38.113.113.100) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 24 Feb 2010 07:52:43 +0000 Received: (qmail 3447 invoked from network); 24 Feb 2010 07:52:41 -0000 Received: from unknown (HELO wind.localnet) (vladimir@127.0.0.2) by mail.codesourcery.com with ESMTPA; 24 Feb 2010 07:52:41 -0000 From: Vladimir Prus To: Pedro Alves Subject: Re: Multiexec MI Date: Wed, 24 Feb 2010 07:53:00 -0000 User-Agent: KMail/1.12.2 (Linux/2.6.31-19-generic-pae; KDE/4.3.2; i686; ; ) Cc: gdb-patches@sourceware.org References: <201001132329.30212.vladimir@codesourcery.com> <201002192315.40134.vladimir@codesourcery.com> <201002221216.24109.pedro@codesourcery.com> In-Reply-To: <201002221216.24109.pedro@codesourcery.com> MIME-Version: 1.0 Content-Type: Multipart/Mixed; boundary="Boundary-00=_HrNhLVh42Tfb8ls" Message-Id: <201002241052.39334.vladimir@codesourcery.com> Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2010-02/txt/msg00585.txt.bz2 --Boundary-00=_HrNhLVh42Tfb8ls Content-Type: Text/Plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-length: 3130 On Monday 22 February 2010 15:16:24 Pedro Alves wrote: > On Friday 19 February 2010 20:15:40, Vladimir Prus wrote: > > On Monday 08 February 2010 22:20:13 Pedro Alves wrote: > > > > > > - 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)); > > > > > > Redundant ()'s. > > > > I think the version with () is more readable. > > Well, `without' is the defacto standard. See, > > with ()'s > > >egrep "&\(([a-zA-Z0-9_]+(\->|\.))+[a-zA-Z0-9_]*" * -rn | wc -l > 45 > > without: > > >egrep "&([a-zA-Z0-9_]+(\->|\.))+[a-zA-Z0-9_]*" * -rn | wc -l > 1419 > > Makes me wanna whack those 45 instances for consistency. ;-) > > > > > > > if (parse->thread != -1) > > > > { > > > > struct thread_info *tp = find_thread_id (parse->thread); > > > > @@ -1767,6 +1908,8 @@ mi_cmd_execute (struct mi_parse *parse) > > > > error (_("Invalid frame id: %d"), frame); > > > > } > > > > > > > > + current_context = parse; > > > > + > > > > > > Hmm, aren't the `struct mi_parse' objects leaking > > > for every MI command? I can't see where they're released in > > > mi_execute_command ? > > > > Close to the end of that function, I see: > > > > mi_parse_free (command); > > > > Is it not there for you? > > Ah, there it is. Thanks. > > ( it leaks if bpstat_do_actions throws, though ;-) ) > > > > > > In the latter strncmp above: > > > > > > + if (strncmp (chp, "--all", as) == 0) > > > > > > AS is always larger than strlen("--all"), so the > > > strncmp's check on AS is useless and confusing here. > > > I think you wanted: > > > > > > if (strcmp (chp, "--all") == 0) > > > { > > > parse->all = 1; > > > chp += strlen (chp); > > > } > > > > I've adjusted sizes. I prefer to keep two ifs with the same structure. > > But now it accepts --allfoofoofoo, and isn't checking that --all > is the last token in the input as is described, when my suggestion > did not have such issues. The current code reads: > > /* See if this --all as the last token in the input. > Both the string and count are smaller by 1. */ > if (strncmp (chp, "--all", as - 1) == 0) > { > parse->all = 1; > chp += (as - 1); > } > > (also, typo: s/--all as the last/--all is the last/) > > > I also spotted: > > > - -exec-run > > + -exec-run [--all | --thread-group N ] > ^ -- inconsistent -- ^ > > > +@deftypefun void inferior_removed (struct inferior *@var{inf}) > > +The inferior @var{inf} has been removed from the list of inferiors. > > +This method is called immediate before freeing @var{inf}. > ^^^^^^^^^ > s/immediate/immediately > > > Otherwise, still looks okay to me. Thanks. Thanks. Here's the version I have checked in. Thanks, -- Vladimir Prus CodeSourcery vladimir@codesourcery.com (650) 331-3385 x722 --Boundary-00=_HrNhLVh42Tfb8ls Content-Type: text/x-patch; charset="UTF-8"; name="multiprocess-mi-cvs-final.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="multiprocess-mi-cvs-final.diff" Content-length: 41247 ? .kdev4 Index: gdb/ChangeLog =================================================================== RCS file: /cvs/src/src/gdb/ChangeLog,v retrieving revision 1.11390 diff -u -p -r1.11390 ChangeLog --- gdb/ChangeLog 24 Feb 2010 07:33:54 -0000 1.11390 +++ gdb/ChangeLog 24 Feb 2010 07:49:51 -0000 @@ -1,5 +1,48 @@ 2010-02-24 Vladimir Prus + 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 + Make -exec-run a proper MI commands. * mi/mi-cmds.h (mi_cmd_exec_run): Declare. Index: gdb/breakpoint.c =================================================================== RCS file: /cvs/src/src/gdb/breakpoint.c,v retrieving revision 1.457 diff -u -p -r1.457 breakpoint.c --- gdb/breakpoint.c 24 Feb 2010 00:29:01 -0000 1.457 +++ gdb/breakpoint.c 24 Feb 2010 07:49:52 -0000 @@ -10366,10 +10366,8 @@ add_catch_command (char *name, char *doc } 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); Index: gdb/inferior.c =================================================================== RCS file: /cvs/src/src/gdb/inferior.c,v retrieving revision 1.15 diff -u -p -r1.15 inferior.c --- gdb/inferior.c 20 Jan 2010 14:23:07 -0000 1.15 +++ gdb/inferior.c 24 Feb 2010 07:49:52 -0000 @@ -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 *tode else inferior_list = inf->next; + observer_notify_inferior_removed (inf); + free_inferior (inf); } @@ -265,7 +269,7 @@ exit_inferior_1 (struct inferior *inftoe /* 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, { inf->pid = pid; - observer_notify_inferior_appeared (pid); + observer_notify_inferior_appeared (inf); } void @@ -738,6 +742,24 @@ remove_inferior_command (char *args, int 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 fr 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 fr { /* 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); Index: gdb/inferior.h =================================================================== RCS file: /cvs/src/src/gdb/inferior.h,v retrieving revision 1.139 diff -u -p -r1.139 inferior.h --- gdb/inferior.h 20 Jan 2010 14:23:07 -0000 1.139 +++ gdb/inferior.h 24 Feb 2010 07:49:52 -0000 @@ -523,6 +523,8 @@ extern struct inferior *add_inferior_sil /* 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) */ Index: gdb/inflow.c =================================================================== RCS file: /cvs/src/src/gdb/inflow.c,v retrieving revision 1.57 diff -u -p -r1.57 inflow.c --- gdb/inflow.c 21 Jan 2010 14:26:12 -0000 1.57 +++ gdb/inflow.c 24 Feb 2010 07:49:52 -0000 @@ -500,9 +500,8 @@ get_inflow_inferior_data (struct inferio 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); Index: gdb/jit.c =================================================================== RCS file: /cvs/src/src/gdb/jit.c,v retrieving revision 1.5 diff -u -p -r1.5 jit.c --- gdb/jit.c 1 Jan 2010 07:31:36 -0000 1.5 +++ gdb/jit.c 24 Feb 2010 07:49:52 -0000 @@ -397,7 +397,7 @@ jit_inferior_created_observer (struct ta 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; Index: gdb/doc/ChangeLog =================================================================== RCS file: /cvs/src/src/gdb/doc/ChangeLog,v retrieving revision 1.1011 diff -u -p -r1.1011 ChangeLog --- gdb/doc/ChangeLog 19 Feb 2010 19:14:38 -0000 1.1011 +++ gdb/doc/ChangeLog 24 Feb 2010 07:49:52 -0000 @@ -1,3 +1,20 @@ +2010-02-24 Vladimir Prus + + 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 * gdbint.texinfo (Getting Started): Fix @node. Index: gdb/doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.670 diff -u -p -r1.670 gdb.texinfo --- gdb/doc/gdb.texinfo 12 Feb 2010 21:35:54 -0000 1.670 +++ gdb/doc/gdb.texinfo 24 Feb 2010 07:49:54 -0000 @@ -21736,6 +21736,11 @@ groups can be obtained using @samp{-list 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{c 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 r 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 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 c 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/testsui @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 en 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 i 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 Index: gdb/doc/observer.texi =================================================================== RCS file: /cvs/src/src/gdb/doc/observer.texi,v retrieving revision 1.30 diff -u -p -r1.30 observer.texi --- gdb/doc/observer.texi 1 Jan 2010 07:54:37 -0000 1.30 +++ gdb/doc/observer.texi 24 Feb 2010 07:49:54 -0000 @@ -199,13 +199,23 @@ The thread's ptid has changed. The @var 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} + @v 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 Index: gdb/mi/mi-cmds.c =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-cmds.c,v retrieving revision 1.46 diff -u -p -r1.46 mi-cmds.c --- gdb/mi/mi-cmds.c 24 Feb 2010 07:33:55 -0000 1.46 +++ gdb/mi/mi-cmds.c 24 Feb 2010 07:49:54 -0000 @@ -33,6 +33,7 @@ static void build_table (struct mi_cmd * 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}, Index: gdb/mi/mi-cmds.h =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-cmds.h,v retrieving revision 1.43 diff -u -p -r1.43 mi-cmds.h --- gdb/mi/mi-cmds.h 24 Feb 2010 07:33:55 -0000 1.43 +++ gdb/mi/mi-cmds.h 24 Feb 2010 07:49:54 -0000 @@ -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_interpre 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; Index: gdb/mi/mi-interp.c =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-interp.c,v retrieving revision 1.54 diff -u -p -r1.54 mi-interp.c --- gdb/mi/mi-interp.c 12 Jan 2010 21:40:24 -0000 1.54 +++ gdb/mi/mi-interp.c 24 Feb 2010 07:49:54 -0000 @@ -56,13 +56,17 @@ static void mi_on_normal_stop (struct bp 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,39 +310,65 @@ 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); + "thread-exited,id=\"%d\",group-id=\"i%d\"", + t->num, inf->num); gdb_flush (mi->event_channel); } -void -mi_inferior_appeared (int pid) +static void +mi_inferior_added (struct inferior *inf) +{ + struct mi_interp *mi = top_level_interpreter_data (); + target_terminal_ours (); + 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-created,id=\"%d\"", - pid); + 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 (int pid) +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=\"%d\"", - pid); + 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) { /* Since this can be called when CLI command is executing, @@ -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 */ Index: gdb/mi/mi-main.c =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-main.c,v retrieving revision 1.167 diff -u -p -r1.167 mi-main.c --- gdb/mi/mi-main.c 24 Feb 2010 07:33:55 -0000 1.167 +++ gdb/mi/mi-main.c 24 Feb 2010 07:49:54 -0000 @@ -78,6 +78,11 @@ static struct mi_timestamp *current_comm 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 **arg 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) - { - 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]); + if (non_stop) + { + /* 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 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 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 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]); + struct inferior *inf = find_inferior_id (current_context->thread_group); + iterate_over_threads (interrupt_thread_callback, &inf->pid); + } + else + { + /* Interrupt just the current thread -- either explicitly + specified via --thread or whatever was current before + MI command was sent. */ + interrupt_target_1 (0); + } +} - old_chain = make_cleanup_restore_current_thread (); - iterate_over_threads (interrupt_thread_callback, &pid); - do_cleanups (old_chain); +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 - error ("Usage: -exec-interrupt [--all|--thread-group id]"); + { + 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 *inf 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 *comma 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) Index: gdb/mi/mi-parse.c =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-parse.c,v retrieving revision 1.18 diff -u -p -r1.18 mi-parse.c --- gdb/mi/mi-parse.c 1 Jan 2010 07:31:50 -0000 1.18 +++ gdb/mi/mi-parse.c 24 Feb 2010 07:49:54 -0000 @@ -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++; Index: gdb/mi/mi-parse.h =================================================================== RCS file: /cvs/src/src/gdb/mi/mi-parse.h,v retrieving revision 1.13 diff -u -p -r1.13 mi-parse.h --- gdb/mi/mi-parse.h 1 Jan 2010 07:31:50 -0000 1.13 +++ gdb/mi/mi-parse.h 24 Feb 2010 07:49:54 -0000 @@ -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; }; Index: gdb/testsuite/gdb.mi/mi-nonstop.exp =================================================================== RCS file: /cvs/src/src/gdb/testsuite/gdb.mi/mi-nonstop.exp,v retrieving revision 1.10 diff -u -p -r1.10 mi-nonstop.exp --- gdb/testsuite/gdb.mi/mi-nonstop.exp 28 Jan 2010 22:14:12 -0000 1.10 +++ gdb/testsuite/gdb.mi/mi-nonstop.exp 24 Feb 2010 07:49:54 -0000 @@ -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 { --Boundary-00=_HrNhLVh42Tfb8ls--