diff options
author | Pedro Alves <pedro@palves.net> | 2023-02-08 16:06:23 +0000 |
---|---|---|
committer | Pedro Alves <pedro@palves.net> | 2023-02-15 20:58:00 +0000 |
commit | 91265a7d7cddc10314335ffcfbfae7159c7cecb1 (patch) | |
tree | 3449a623ebe58d5e59ea3c72eb9be336cc0dbc03 /gdb/cli | |
parent | 751495be92b2b319fb66ce4e12b562a0e27c15fe (diff) | |
download | binutils-91265a7d7cddc10314335ffcfbfae7159c7cecb1.zip binutils-91265a7d7cddc10314335ffcfbfae7159c7cecb1.tar.gz binutils-91265a7d7cddc10314335ffcfbfae7159c7cecb1.tar.bz2 |
Add new "$_shell(CMD)" internal function
For testing a following patch, I wanted a way to send a SIGINT to GDB
from a breakpoint condition. And I didn't want to do it from a Python
breakpoint or Python function, as I wanted to exercise non-Python code
paths. So I thought I'd add a new $_shell internal function, that
runs a command under the shell, and returns the exit code. With this,
I could write:
(gdb) b foo if $_shell("kill -SIGINT $gdb_pid") != 0 || <other condition>
I think this is generally useful, hence I'm proposing it here.
Here's the new function in action:
(gdb) p $_shell("true")
$1 = 0
(gdb) p $_shell("false")
$2 = 1
(gdb) p $_shell("echo hello")
hello
$3 = 0
(gdb) p $_shell("foobar")
bash: line 1: foobar: command not found
$4 = 127
(gdb) help function _shell
$_shell - execute a shell command and returns the result.
Usage: $_shell (command)
Returns the command's exit code: zero on success, non-zero otherwise.
(gdb)
NEWS and manual changes included.
Approved-By: Andrew Burgess <aburgess@redhat.com>
Approved-By: Tom Tromey <tom@tromey.com>
Approved-By: Eli Zaretskii <eliz@gnu.org>
Change-Id: I7e36d451ee6b428cbf41fded415ae2d6b4efaa4e
Diffstat (limited to 'gdb/cli')
-rw-r--r-- | gdb/cli/cli-cmds.c | 96 |
1 files changed, 92 insertions, 4 deletions
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index 871785d..0c68089 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -39,6 +39,7 @@ #include "gdbsupport/filestuff.h" #include "location.h" #include "block.h" +#include "valprint.h" #include "ui-out.h" #include "interps.h" @@ -873,6 +874,9 @@ exit_status_set_internal_vars (int exit_status) clear_internalvar (var_code); clear_internalvar (var_signal); + + /* Keep the logic here in sync with shell_internal_fn. */ + if (WIFEXITED (exit_status)) set_internalvar_integer (var_code, WEXITSTATUS (exit_status)); #ifdef __MINGW32__ @@ -893,8 +897,11 @@ exit_status_set_internal_vars (int exit_status) warning (_("unexpected shell command exit status %d"), exit_status); } -static void -shell_escape (const char *arg, int from_tty) +/* Run ARG under the shell, and return the exit status. If ARG is + NULL, run an interactive shell. */ + +static int +run_under_shell (const char *arg, int from_tty) { #if defined(CANT_FORK) || \ (!defined(HAVE_WORKING_VFORK) && !defined(HAVE_WORKING_FORK)) @@ -915,7 +922,7 @@ shell_escape (const char *arg, int from_tty) the shell command we just ran changed it. */ chdir (current_directory); #endif - exit_status_set_internal_vars (rc); + return rc; #else /* Can fork. */ int status, pid; @@ -942,10 +949,21 @@ shell_escape (const char *arg, int from_tty) waitpid (pid, &status, 0); else error (_("Fork failed")); - exit_status_set_internal_vars (status); + return status; #endif /* Can fork. */ } +/* Escape out to the shell to run ARG. If ARG is NULL, launch and + interactive shell. Sets $_shell_exitcode and $_shell_exitsignal + convenience variables based on the exits status. */ + +static void +shell_escape (const char *arg, int from_tty) +{ + int status = run_under_shell (arg, from_tty); + exit_status_set_internal_vars (status); +} + /* Implementation of the "shell" command. */ static void @@ -2417,6 +2435,63 @@ gdb_maint_setting_str_internal_fn (struct gdbarch *gdbarch, return str_value_from_setting (*show_cmd->var, gdbarch); } +/* Implementation of the convenience function $_shell. */ + +static struct value * +shell_internal_fn (struct gdbarch *gdbarch, + const struct language_defn *language, + void *cookie, int argc, struct value **argv) +{ + if (argc != 1) + error (_("You must provide one argument for $_shell.")); + + value *val = argv[0]; + struct type *type = check_typedef (val->type ()); + + if (!language->is_string_type_p (type)) + error (_("Argument must be a string.")); + + value_print_options opts; + get_no_prettyformat_print_options (&opts); + + string_file stream; + value_print (val, &stream, &opts); + + /* We should always have two quote chars, which we'll strip. */ + gdb_assert (stream.size () >= 2); + + /* Now strip them. We don't need the original string, so it's + cheaper to do it in place, avoiding a string allocation. */ + std::string str = stream.release (); + str[str.size () - 1] = 0; + const char *command = str.c_str () + 1; + + int exit_status = run_under_shell (command, 0); + + struct type *int_type = builtin_type (gdbarch)->builtin_int; + + /* Keep the logic here in sync with + exit_status_set_internal_vars. */ + + if (WIFEXITED (exit_status)) + return value_from_longest (int_type, WEXITSTATUS (exit_status)); +#ifdef __MINGW32__ + else if (WIFSIGNALED (exit_status) && WTERMSIG (exit_status) == -1) + { + /* See exit_status_set_internal_vars. */ + return value_from_longest (int_type, exit_status); + } +#endif + else if (WIFSIGNALED (exit_status)) + { + /* (0x80 | SIGNO) is what most (all?) POSIX-like shells set as + exit code on fatal signal termination. */ + return value_from_longest (int_type, 0x80 | WTERMSIG (exit_status)); + } + else + return value::allocate_optimized_out (int_type); +} + void _initialize_cli_cmds (); void _initialize_cli_cmds () @@ -2606,6 +2681,19 @@ Some integer settings accept an unlimited value, returned\n\ as 0 or -1 depending on the setting."), gdb_maint_setting_internal_fn, NULL); + add_internal_function ("_shell", _("\ +$_shell - execute a shell command and return the result.\n\ +\n\ + Usage: $_shell (COMMAND)\n\ +\n\ + Arguments:\n\ +\n\ + COMMAND: The command to execute. Must be a string.\n\ +\n\ + Returns:\n\ + The command's exit code: zero on success, non-zero otherwise."), + shell_internal_fn, NULL); + add_cmd ("commands", no_set_class, show_commands, _("\ Show the history of commands you typed.\n\ You can supply a command number to start with, or a `+' to start after\n\ |