Patchwork [RFAv4,1/3] Implement convenience functions to examine GDB settings.

login
register
mail settings
Submitter Philippe Waroquiers
Date Sept. 7, 2019, 11:38 a.m.
Message ID <20190907113823.17436-2-philippe.waroquiers@skynet.be>
Download mbox | patch
Permalink /patch/34442/
State New
Headers show

Comments

Philippe Waroquiers - Sept. 7, 2019, 11:38 a.m.
The new convenience functions $_gdb_setting and $_gdb_setting_str
provide access to the GDB settings in user-defined commands.
Simimilarly, $_gdb_maint_setting and $_gdb_maint_setting_str
provide access to the GDB maintenance settings.

The patch was developed following a comment of Eli about the
'set may-call-functions'.  Eli said that user-defined functions
should have a way to change their behavior according to this setting.
Rather than have a specialized $_may_call_functions, this patch
implements a general way to access any GDB setting.

Compared to doing such access via Python 'gdb.parameter' and/or
'gdb.execute("set somesetting tosomevalue"):
* The 'with' command is much better than the above python usage:
  if the user types C-c or an error happens between the set pagination off
  and the python "set pagination on", the above python
  does not restor the original setting.

* Effectively, with the "gdb.parameter" python one liner, it is possible to do
  simple 'if' conditions, such as set and restore pagination.
  But mixing the "python if" within canned
  sequence of commands is cumbersome for non trivial combinations.
  E.g. if several commands have to be done for a certain condition
  accessed from python, I guess something like will be needed:
     python if __some_setting: gdb.execute("some command")
     python if __some_setting: gdb.execute("some other command")
     python if __some_setting: gdb.execute("some different command")
  (without speaking about nested "if-s").

  With the convenience function:
     if $_gdb_setting("some_setting")
        some command
        some other command
        some different command
     end
  Integer settings (for example print elements) will also be more difficult
  to use.
  For example, a user defined function that scans and prints a linked list
  might want to use the value of "set print elements" to stop printing
  the linked list.
  Doing that by mixing python expression/if is likely doable, but seems
  not easy with the above one liners.

So, in summary, the $_gdb_setting and $_gdb_setting_str avoids to have the
heterogenous mix of python and GDB commands in one single script
(and of course, it works even if python is not configured, but that
must be an unusual setup I guess).

gdb/ChangeLog
2019-09-07  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* cli/cli-cmds.c (setting_cmd, value_from_setting,
	gdb_setting_internal_fn, gdb_maint_setting_internal_fn,
	str_value_from_setting, gdb_setting_str_internal_fn,
	gdb_maint_setting_str_internal_fn): New functions.
	(_initialize_cli_cmds): Define the new convenience functions.
	* gdb/cli/cli-setshow.h (get_setshow_command_value_string): Constify.
	* gdb/cli/cli-setshow.c (get_setshow_command_value_string): Constify.
---
 gdb/cli/cli-cmds.c    | 231 ++++++++++++++++++++++++++++++++++++++++++
 gdb/cli/cli-setshow.c |   2 +-
 gdb/cli/cli-setshow.h |   2 +-
 3 files changed, 233 insertions(+), 2 deletions(-)
Pedro Alves - Sept. 8, 2019, 5:39 p.m.
Hi Philippe,

I like this version a lot better!  Thanks for doing this.

On 9/7/19 12:38 PM, Philippe Waroquiers wrote:

> The new convenience functions $_gdb_setting and $_gdb_setting_str
> provide access to the GDB settings in user-defined commands.
> Simimilarly, $_gdb_maint_setting and $_gdb_maint_setting_str

"Simimilarly" -> "Similarly"

> So, in summary, the $_gdb_setting and $_gdb_setting_str avoids to have the
> heterogenous mix of python and GDB commands in one single script

heterogenous -> heterogeneous

> +
> +static cmd_list_element *
> +setting_cmd (const char *fnname, struct cmd_list_element *showlist,
> +	     int argc, struct value **argv)
> +{
> +  if (argc == 0)
> +    error (_("You must provide an argument to %s"), fnname);
> +  if (argc != 1)
> +    error (_("You can only provide one argument to %s"), fnname);
> +
> +  struct type *type0 = check_typedef (value_type (argv[0]));
> +
> +  if (TYPE_CODE (type0) != TYPE_CODE_ARRAY
> +      && TYPE_CODE (type0) != TYPE_CODE_STRING)
> +    error (_("First argument of %s must be a string."), fnname);
> +
> +  int len0 = TYPE_LENGTH (type0);
> +  std::vector<char> arg0 (len0);
> +

gdb::def_vector<char> avoids the unnecessary memset.

> +  memcpy (arg0.data (), value_contents (argv[0]), len0);
> +

But, OTH, it's not immediately obvious why we need this copy
at all?

> +  const char *a0 = arg0.data ();
> +  cmd_list_element *cmd = lookup_cmd (&a0, showlist, "", -1, 0);
> +
> +  if (cmd == nullptr || cmd_type (cmd) != show_cmd)
> +    error (_("First argument of %s must be a "
> +	     "valid setting of the 'show' command."), fnname);
> +
> +  return cmd;
> +}
> +
> +/* Builds a value from the show CMD.  */
> +
> +static struct value *
> +value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
> +{
> +  switch (cmd->var_type)
> +    {
> +    case var_integer:
> +      if (*(int *) cmd->var == INT_MAX)
> +	return value_from_longest (builtin_type (gdbarch)->builtin_int,
> +				   0);
> +      else
> +	return value_from_longest (builtin_type (gdbarch)->builtin_int,
> +				   *(int *) cmd->var);
> +    case var_zinteger:
> +      return value_from_longest (builtin_type (gdbarch)->builtin_int,
> +				 *(int *) cmd->var);
> +    case var_boolean:
> +    case var_zuinteger_unlimited:
> +      return value_from_longest (builtin_type (gdbarch)->builtin_int,
> +				 *(int *) cmd->var);
> +    case var_auto_boolean:
> +      {
> +	int val;
> +
> +	switch (*(enum auto_boolean*) cmd->var)
> +	  {
> +	  case AUTO_BOOLEAN_TRUE:
> +	    val = 1;
> +	    break;
> +	case AUTO_BOOLEAN_FALSE:
> +	  val = 0;
> +	  break;

Something's odd in the indentation of these case statements.
Tabs vs spaces?

> +	case AUTO_BOOLEAN_AUTO:
> +	  val = -1;
> +	  break;
> +	  default:
> +	    gdb_assert_not_reached ("invalid var_auto_boolean");
> +	    break;

This break is unreachable / not necessary.

> +
> +/* Builds a string value from the show CMD.  */
> +
> +static struct value *
> +str_value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
> +{
> +  switch (cmd->var_type)
> +    {
> +    case var_integer:
> +    case var_zinteger:
> +    case var_boolean:
> +    case var_zuinteger_unlimited:
> +    case var_auto_boolean:
> +    case var_uinteger:
> +    case var_zuinteger:
> +      {
> +	std::string cmd_val = get_setshow_command_value_string (cmd);
> +
> +	return value_cstring (cmd_val.c_str (), strlen (cmd_val.c_str ()),
> +			      builtin_type (gdbarch)->builtin_char);

'cmd_val.size ()' instead of 'strlen (cmd_val.c_str ())'.


> +      }
> +
> +    case var_string:
> +    case var_string_noescape:
> +    case var_optional_filename:
> +    case var_filename:
> +    case var_enum:

It's not immediately obvious why not just use
get_setshow_command_value_string for strings types too.  I think this
warrants a comment.


> +      if (*(char **) cmd->var)
> +	return value_cstring (*(char **) cmd->var, strlen (*(char **) cmd->var),
> +			      builtin_type (gdbarch)->builtin_char);
> +      else
> +	return value_cstring (nullptr, 0,
> +			      builtin_type (gdbarch)->builtin_char);
> +    default:
> +      gdb_assert_not_reached ("bad var_type");
> +    }
> +}
Thanks,
Pedro Alves

Patch

diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 4fc656775c..c49fd56554 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -1908,6 +1908,199 @@  show_max_user_call_depth (struct ui_file *file, int from_tty,
 		    value);
 }
 
+/* Returns the cmd_list_element in SHOWLIST corresponding to the first
+   argument of ARGV, which must contain one single value.
+   Throws an error if no value provided, or value not correct.
+   FNNAME is used in the error message.  */
+
+static cmd_list_element *
+setting_cmd (const char *fnname, struct cmd_list_element *showlist,
+	     int argc, struct value **argv)
+{
+  if (argc == 0)
+    error (_("You must provide an argument to %s"), fnname);
+  if (argc != 1)
+    error (_("You can only provide one argument to %s"), fnname);
+
+  struct type *type0 = check_typedef (value_type (argv[0]));
+
+  if (TYPE_CODE (type0) != TYPE_CODE_ARRAY
+      && TYPE_CODE (type0) != TYPE_CODE_STRING)
+    error (_("First argument of %s must be a string."), fnname);
+
+  int len0 = TYPE_LENGTH (type0);
+  std::vector<char> arg0 (len0);
+
+  memcpy (arg0.data (), value_contents (argv[0]), len0);
+
+  const char *a0 = arg0.data ();
+  cmd_list_element *cmd = lookup_cmd (&a0, showlist, "", -1, 0);
+
+  if (cmd == nullptr || cmd_type (cmd) != show_cmd)
+    error (_("First argument of %s must be a "
+	     "valid setting of the 'show' command."), fnname);
+
+  return cmd;
+}
+
+/* Builds a value from the show CMD.  */
+
+static struct value *
+value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
+{
+  switch (cmd->var_type)
+    {
+    case var_integer:
+      if (*(int *) cmd->var == INT_MAX)
+	return value_from_longest (builtin_type (gdbarch)->builtin_int,
+				   0);
+      else
+	return value_from_longest (builtin_type (gdbarch)->builtin_int,
+				   *(int *) cmd->var);
+    case var_zinteger:
+      return value_from_longest (builtin_type (gdbarch)->builtin_int,
+				 *(int *) cmd->var);
+    case var_boolean:
+    case var_zuinteger_unlimited:
+      return value_from_longest (builtin_type (gdbarch)->builtin_int,
+				 *(int *) cmd->var);
+    case var_auto_boolean:
+      {
+	int val;
+
+	switch (*(enum auto_boolean*) cmd->var)
+	  {
+	  case AUTO_BOOLEAN_TRUE:
+	    val = 1;
+	    break;
+	case AUTO_BOOLEAN_FALSE:
+	  val = 0;
+	  break;
+	case AUTO_BOOLEAN_AUTO:
+	  val = -1;
+	  break;
+	  default:
+	    gdb_assert_not_reached ("invalid var_auto_boolean");
+	    break;
+	  }
+	return value_from_longest (builtin_type (gdbarch)->builtin_int,
+				   val);
+      }
+    case var_uinteger:
+      if (*(unsigned int *) cmd->var == UINT_MAX)
+	return value_from_ulongest
+	  (builtin_type (gdbarch)->builtin_unsigned_int, 0);
+      else
+	return value_from_ulongest
+	  (builtin_type (gdbarch)->builtin_unsigned_int,
+	   *(unsigned int *) cmd->var);
+    case var_zuinteger:
+      return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
+				 *(unsigned int *) cmd->var);
+    case var_string:
+    case var_string_noescape:
+    case var_optional_filename:
+    case var_filename:
+    case var_enum:
+      if (*(char **) cmd->var)
+	return value_cstring (*(char **) cmd->var, strlen (*(char **) cmd->var),
+			      builtin_type (gdbarch)->builtin_char);
+      else
+	return value_cstring (nullptr, 0,
+			      builtin_type (gdbarch)->builtin_char);
+    default:
+      gdb_assert_not_reached ("bad var_type");
+    }
+}
+
+/* Implementation of the convenience function $_gdb_setting.  */
+
+static struct value *
+gdb_setting_internal_fn (struct gdbarch *gdbarch,
+			 const struct language_defn *language,
+			 void *cookie, int argc, struct value **argv)
+{
+  return value_from_setting (setting_cmd ("$_gdb_setting", showlist,
+					  argc, argv),
+			     gdbarch);
+}
+
+/* Implementation of the convenience function $_gdb_maint_setting.  */
+
+static struct value *
+gdb_maint_setting_internal_fn (struct gdbarch *gdbarch,
+			       const struct language_defn *language,
+			       void *cookie, int argc, struct value **argv)
+{
+  return value_from_setting (setting_cmd ("$_gdb_maint_setting",
+					  maintenance_show_cmdlist,
+					  argc, argv),
+			     gdbarch);
+}
+
+/* Builds a string value from the show CMD.  */
+
+static struct value *
+str_value_from_setting (const cmd_list_element *cmd, struct gdbarch *gdbarch)
+{
+  switch (cmd->var_type)
+    {
+    case var_integer:
+    case var_zinteger:
+    case var_boolean:
+    case var_zuinteger_unlimited:
+    case var_auto_boolean:
+    case var_uinteger:
+    case var_zuinteger:
+      {
+	std::string cmd_val = get_setshow_command_value_string (cmd);
+
+	return value_cstring (cmd_val.c_str (), strlen (cmd_val.c_str ()),
+			      builtin_type (gdbarch)->builtin_char);
+      }
+
+    case var_string:
+    case var_string_noescape:
+    case var_optional_filename:
+    case var_filename:
+    case var_enum:
+      if (*(char **) cmd->var)
+	return value_cstring (*(char **) cmd->var, strlen (*(char **) cmd->var),
+			      builtin_type (gdbarch)->builtin_char);
+      else
+	return value_cstring (nullptr, 0,
+			      builtin_type (gdbarch)->builtin_char);
+    default:
+      gdb_assert_not_reached ("bad var_type");
+    }
+}
+
+/* Implementation of the convenience function $_gdb_setting_str.  */
+
+static struct value *
+gdb_setting_str_internal_fn (struct gdbarch *gdbarch,
+			     const struct language_defn *language,
+			     void *cookie, int argc, struct value **argv)
+{
+  return str_value_from_setting (setting_cmd ("$_gdb_setting_str",
+					      showlist, argc, argv),
+				 gdbarch);
+}
+
+
+/* Implementation of the convenience function $_gdb_maint_setting_str.  */
+
+static struct value *
+gdb_maint_setting_str_internal_fn (struct gdbarch *gdbarch,
+				   const struct language_defn *language,
+				   void *cookie, int argc, struct value **argv)
+{
+  return str_value_from_setting (setting_cmd ("$_gdb_maint_setting_str",
+					      maintenance_show_cmdlist,
+					      argc, argv),
+				 gdbarch);
+}
+
 void
 _initialize_cli_cmds (void)
 {
@@ -2049,6 +2242,44 @@  abbreviations for commands and/or values.  E.g.:\n\
   set_cmd_completer_handle_brkchars (c, with_command_completer);
   add_com_alias ("w", "with", class_vars, 1);
 
+  add_internal_function ("_gdb_setting_str", _("\
+$_gdb_setting_str - returns the value of a GDB setting as a string.\n\
+Usage: $_gdb_setting_str (setting)\n\
+\n\
+auto-boolean values are \"off\", \"on\", \"auto\".\n\
+boolean values are \"off\", \"on\".\n\
+The unlimited value for integer, uinteger, zuinteger-unlimited\n\
+settings is represented as \"unlimited\"."),
+			 gdb_setting_str_internal_fn, NULL);
+
+  add_internal_function ("_gdb_setting", _("\
+$_gdb_setting - returns the value of a GDB setting.\n\
+Usage: $_gdb_setting (setting)\n\
+auto-boolean values are \"off\", \"on\", \"auto\".\n\
+boolean values are \"off\", \"on\".\n\
+The unlimited value for integer, uinteger settings is 0.\n\
+The unlimited value for zuinteger-unlimited is -1."),
+			 gdb_setting_internal_fn, NULL);
+
+  add_internal_function ("_gdb_maint_setting_str", _("\
+$_gdb_maint_setting_str - returns the value of a GDB maintenance setting as a string.\n\
+Usage: $_gdb_maint_setting_str (setting)\n\
+\n\
+auto-boolean values are \"off\", \"on\", \"auto\".\n\
+boolean values are \"off\", \"on\".\n\
+The unlimited value for integer, uinteger, zuinteger-unlimited\n\
+settings is represented as \"unlimited\"."),
+			 gdb_maint_setting_str_internal_fn, NULL);
+
+  add_internal_function ("_gdb_maint_setting", _("\
+$_gdb_maint_setting - returns the value of a GDB maintenance setting.\n\
+Usage: $_gdb_maint_setting (setting)\n\
+auto-boolean values are \"off\", \"on\", \"auto\".\n\
+boolean values are \"off\", \"on\".\n\
+The unlimited value for integer, uinteger settings is 0.\n\
+The unlimited value for zuinteger-unlimited is -1."),
+			 gdb_maint_setting_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\
diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c
index 40538832b5..9e3332bf51 100644
--- a/gdb/cli/cli-setshow.c
+++ b/gdb/cli/cli-setshow.c
@@ -627,7 +627,7 @@  do_set_command (const char *arg, int from_tty, struct cmd_list_element *c)
 /* See cli/cli-setshow.h.  */
 
 std::string
-get_setshow_command_value_string (cmd_list_element *c)
+get_setshow_command_value_string (const cmd_list_element *c)
 {
   string_file stb;
 
diff --git a/gdb/cli/cli-setshow.h b/gdb/cli/cli-setshow.h
index 8bfe7e89f0..dbb3e9fdff 100644
--- a/gdb/cli/cli-setshow.h
+++ b/gdb/cli/cli-setshow.h
@@ -58,7 +58,7 @@  extern void do_show_command (const char *arg, int from_tty,
 			     struct cmd_list_element *c);
 
 /* Get a string version of C's current value.  */
-extern std::string get_setshow_command_value_string (cmd_list_element *c);
+extern std::string get_setshow_command_value_string (const cmd_list_element *c);
 
 extern void cmd_show_list (struct cmd_list_element *list, int from_tty,
 			   const char *prefix);