From patchwork Mon Mar 21 15:21:14 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 11428 Received: (qmail 93216 invoked by alias); 21 Mar 2016 15:21:48 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 93133 invoked by uid 89); 21 Mar 2016 15:21:47 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=featured, leverage, plumbing X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Mon, 21 Mar 2016 15:21:42 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (Postfix) with ESMTPS id 8133DC049D5B for ; Mon, 21 Mar 2016 15:21:40 +0000 (UTC) Received: from cascais.lan (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u2LFLGPq019569 for ; Mon, 21 Mar 2016 11:21:39 -0400 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH v2 24/25] Add new command to create extra console/mi UI channels Date: Mon, 21 Mar 2016 15:21:14 +0000 Message-Id: <1458573675-15478-25-git-send-email-palves@redhat.com> In-Reply-To: <1458573675-15478-1-git-send-email-palves@redhat.com> References: <1458573675-15478-1-git-send-email-palves@redhat.com> With all the previous plumbing in place, it's now easy to add a command that actually creates a new console/mi UI. The intended use case is to make it possible and easy for MI frontends to provide a fully featured GDB console to users, with readline support, command line editing, history, etc., just like if gdb was started on the command line. Currently MI frontends have to try to implement all of that theirselves and make use of "-interpreter-exec console ...", which is far from perfect. If you ever tried Eclipse's gdb console window, you'll know what I mean... Instead of trying to multiplex console through MI, this command let's just leverage all the built in readline/editing support already inside gdb. The plan is for the MI frontend to start GDB in regular console mode, running inside a terminal emulator widget embedded in Eclipse (which already exists, for supporting the shell widget; other frontends have similar widgets), and then tell GDB to run a full MI interpreter on an extra / separate side channel, independent of the console. My original prototype planned to do things the other way around -- start GDB in MI mode, and then start an extra CLI console on separate tty. I handed over that prototype to Marc Khouzam @ Eclipse CDT, and after experimentation and discussion, we ended up concluding that starting GDB in CLI mode instead was both easier and actually also supported an interesting use case -- connect an Eclipse frontend to a GDB that is already running outside Eclipse. The current usage is "new-ui ". E.g., on a terminal run this scriplet: $ cat gdb-client #!/bin/bash reset tty tail -f /dev/null $ gdb-client /dev/pts/15 Now run gdb on another terminal, and tell it to start a MI interpreter on the tty of the other terminal: ... (gdb) new-ui mi /dev/pts/15 New UI allocated Now back to the the gdb-client terminal, we'll get an MI prompt, ready for MI input: /dev/pts/15 =thread-group-added,id="i1" (gdb) You can also start a new UI running a CLI, with: (gdb) new-ui console /dev/pts/15 Though note that this console won't support readline command editing (yet). It works as if "set editing off" was entered. Starting more than one MI UI is probably very broken, because there are still globals in the MI code that should be made per UI. Should be trivial to fix, though much lower priority than getting one MI working. --- gdb/interps.c | 15 +++++++++++- gdb/interps.h | 5 ++++ gdb/main.c | 12 +--------- gdb/top.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 12 deletions(-) diff --git a/gdb/interps.c b/gdb/interps.c index 90d0de8..c9de292 100644 --- a/gdb/interps.c +++ b/gdb/interps.c @@ -291,6 +291,19 @@ interp_lookup (const char *name) return NULL; } +void +set_top_level_interpreter (const char *name) +{ + /* Find it. */ + struct interp *interp = interp_lookup (name); + + if (interp == NULL) + error (_("Interpreter `%s' unrecognized"), name); + /* Install it. */ + if (!interp_set (interp, 1)) + error (_("Interpreter `%s' failed to initialize."), name); +} + /* Returns the current interpreter. */ struct ui_out * @@ -523,7 +536,7 @@ interpreter_exec_cmd (char *args, int from_tty) } /* List the possible interpreters which could complete the given text. */ -static VEC (char_ptr) * +VEC (char_ptr) * interpreter_completer (struct cmd_list_element *ignore, const char *text, const char *word) { diff --git a/gdb/interps.h b/gdb/interps.h index 6d8930f..3f9f010 100644 --- a/gdb/interps.h +++ b/gdb/interps.h @@ -86,6 +86,7 @@ extern struct interp *interp_new (const char *name, extern void interp_add (struct interp *interp); extern int interp_set (struct interp *interp, int top_level); extern struct interp *interp_lookup (const char *name); +extern void set_top_level_interpreter (const char *name); extern struct ui_out *interp_ui_out (struct interp *interp); extern void *interp_data (struct interp *interp); extern const char *interp_name (struct interp *interp); @@ -119,6 +120,10 @@ extern int interp_supports_command_editing (struct interp *interp); chance to e.g., print a prompt. */ extern void interp_pre_command_loop (struct interp *interp); +extern VEC (char_ptr) *interpreter_completer (struct cmd_list_element *ignore, + const char *text, + const char *word); + /* well-known interpreters */ #define INTERP_CONSOLE "console" #define INTERP_MI1 "mi1" diff --git a/gdb/main.c b/gdb/main.c index 95578e4..a982752 100644 --- a/gdb/main.c +++ b/gdb/main.c @@ -962,17 +962,7 @@ captured_main (void *data) /* Install the default UI. All the interpreters should have had a look at things by now. Initialize the default interpreter. */ - - { - /* Find it. */ - struct interp *interp = interp_lookup (interpreter_p); - - if (interp == NULL) - error (_("Interpreter `%s' unrecognized"), interpreter_p); - /* Install it. */ - if (!interp_set (interp, 1)) - error (_("Interpreter `%s' failed to initialize."), interpreter_p); - } + set_top_level_interpreter (interpreter_p); /* FIXME: cagney/2003-02-03: The big hack (part 2 of 2) that lets GDB retain the old MI1 interpreter startup behavior. Output the diff --git a/gdb/top.c b/gdb/top.c index 7d6d7cb..d74af46 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -313,6 +313,75 @@ delete_ui (struct ui *todel) free_ui (ui); } +static FILE * +open_stream (const char *name) +{ + int fd; + + fd = open (name, O_RDWR | O_NOCTTY); + if (fd < 0) + perror_with_name (_("opening terminal failed")); + + return fdopen (fd, "w+"); +} + +static void +new_ui_command (char *args, int from_tty) +{ + struct ui *ui; + struct interp *interp; + FILE *stream[3] = { NULL, NULL, NULL }; + int i; + int res; + int argc; + char **argv; + const char *interpreter_name; + const char *tty_name; + struct cleanup *back_to; + struct cleanup *streams_chain; + + argv = gdb_buildargv (args); + back_to = make_cleanup_freeargv (argv); + + for (argc = 0; argv[argc] != NULL; argc++) + ; + + if (argc < 2) + error (_("usage: new-ui ")); + + interpreter_name = argv[0]; + tty_name = argv[1]; + + streams_chain = make_cleanup (null_cleanup, NULL); + + /* Open specified terminal, once for each of + stdin/stdout/stderr. */ + for (i = 0; i < 3; i++) + { + stream[i] = open_stream (tty_name); + make_cleanup_fclose (stream[i]); + } + + ui = new_ui (stream[0], stream[1], stream[2]); + + discard_cleanups (streams_chain); + + initialize_stdin_serial (ui); + ui->async = 1; + + make_cleanup (restore_ui_cleanup, current_ui); + current_ui = ui; + + set_top_level_interpreter (interpreter_name); + + interp_pre_command_loop (top_level_interpreter ()); + + /* This restores the previous UI. */ + do_cleanups (back_to); + + printf_unfiltered ("New UI allocated\n"); +} + /* Handler for SIGHUP. */ #ifdef SIGHUP @@ -1878,6 +1947,8 @@ set_history_filename (char *args, int from_tty, struct cmd_list_element *c) static void init_main (void) { + struct cmd_list_element *c; + /* Initialize the prompt to a simple "(gdb) " prompt or to whatever the DEFAULT_PROMPT is. */ set_prompt (DEFAULT_PROMPT); @@ -2001,6 +2072,12 @@ When set, GDB uses the specified path to search for data files."), set_gdb_datadir, show_gdb_datadir, &setlist, &showlist); + + c = add_cmd ("new-ui", class_support, new_ui_command, _("\ +Create a new UI. It takes two arguments:\n\ +The first argument is the name of the interpreter to run.\n\ +The second argument is the terminal the UI runs on.\n"), &cmdlist); + set_cmd_completer (c, interpreter_completer); } void