aboutsummaryrefslogtreecommitdiff
path: root/gdb/cli
diff options
context:
space:
mode:
authorPedro Alves <pedro@palves.net>2023-02-08 16:06:23 +0000
committerPedro Alves <pedro@palves.net>2023-02-15 20:58:00 +0000
commit91265a7d7cddc10314335ffcfbfae7159c7cecb1 (patch)
tree3449a623ebe58d5e59ea3c72eb9be336cc0dbc03 /gdb/cli
parent751495be92b2b319fb66ce4e12b562a0e27c15fe (diff)
downloadgdb-91265a7d7cddc10314335ffcfbfae7159c7cecb1.zip
gdb-91265a7d7cddc10314335ffcfbfae7159c7cecb1.tar.gz
gdb-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.c96
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\