Add basic Python API for convenience variables
authorTom Tromey <tom@tromey.com>
Sun, 22 Apr 2018 21:13:09 +0000 (15:13 -0600)
committerTom Tromey <tom@tromey.com>
Thu, 31 May 2018 21:00:40 +0000 (15:00 -0600)
This adds a basic Python API for accessing convenience variables.
With this, convenience variables can be read and set from Python.
Although gdb supports convenience variables whose value changes at
each call, this is not exposed to Python; it could be, but I think
it's just as good to write a convenience function in this situation.

This is PR python/23080.

Tested on x86-64 Fedora 26.

2018-04-22  Tom Tromey  <tom@tromey.com>

PR python/23080:
* NEWS: Update for new functions.
* python/py-value.c (gdbpy_set_convenience_variable)
(gdbpy_convenience_variable): New functions.
* python/python-internal.h (gdbpy_convenience_variable)
(gdbpy_set_convenience_variable): Declare.
* python/python.c (python_GdbMethods): Add convenience_variable,
set_convenience_variable.

doc/ChangeLog
2018-04-22  Tom Tromey  <tom@tromey.com>

PR python/23080:
* python.texi (Basic Python): Document gdb.convenience_variable,
gdb.set_convenience_variable.

testsuite/ChangeLog
2018-04-22  Tom Tromey  <tom@tromey.com>

PR python/23080:
* gdb.python/python.exp: Add convenience variable tests.

gdb/NEWS
gdb/doc/python.texi
gdb/python/py-value.c
gdb/python/python-internal.h
gdb/python/python.c
gdb/testsuite/gdb.python/python.exp

index 392e2994e15bc46546b526c761dd91fe9504f50e..5b5c467dfc2f8335d81e70b97da8443d979a686d 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -42,6 +42,10 @@ set|show record btrace cpu
 
   ** gdb.execute can now execute multi-line gdb commands.
 
+  ** The new functions gdb.convenience_variable and
+     gdb.set_convenience_variable can be used to get and set the value
+     of convenience variables.
+
 * New targets
 
 RiscV ELF                      riscv*-*-elf
index 4182b60ca3e92825cc116fea514376b43a4ef370..60e016ad957dd85a2974d83963580f730b3cb287 100644 (file)
@@ -288,6 +288,26 @@ If no exception is raised, the return value is always an instance of
 @code{gdb.Value} (@pxref{Values From Inferior}).
 @end defun
 
+@findex gdb.convenience_variable
+@defun gdb.convenience_variable (name)
+Return the value of the convenience variable (@pxref{Convenience
+Vars}) named @var{name}.  @var{name} must be a string.  The name
+should not include the @samp{$} that is used to mark a convenience
+variable in an expression.  If the convenience variable does not
+exist, then @code{None} is returned.
+@end defun
+
+@findex gdb.set_convenience_variable
+@defun gdb.set_convenience_variable (name, value)
+Set the value of the convenience variable (@pxref{Convenience Vars})
+named @var{name}.  @var{name} must be a string.  The name should not
+include the @samp{$} that is used to mark a convenience variable in an
+expression.  If @var{value} is @code{None}, then the convenience
+variable is removed.  Otherwise, if @var{value} is not a
+@code{gdb.Value} (@pxref{Values From Inferior}), it is is converted
+using the @code{gdb.Value} constructor.
+@end defun
+
 @findex gdb.parse_and_eval
 @defun gdb.parse_and_eval (expression)
 Parse @var{expression}, which must be a string, as an expression in
@@ -297,8 +317,7 @@ the current language, evaluate it, and return the result as a
 This function can be useful when implementing a new command
 (@pxref{Commands In Python}), as it provides a way to parse the
 command's argument as an expression.  It is also useful simply to
-compute values, for example, it is the only way to get the value of a
-convenience variable (@pxref{Convenience Vars}) as a @code{gdb.Value}.
+compute values.
 @end defun
 
 @findex gdb.find_pc_line
index bba6d0b8a48cdff663cb977ca83a5b96ab92a071..30a0082da85e4c119b8638cbd4b4323fc1524584 100644 (file)
@@ -1746,6 +1746,83 @@ gdbpy_history (PyObject *self, PyObject *args)
   return value_to_value_object (res_val);
 }
 
+/* Return the value of a convenience variable.  */
+PyObject *
+gdbpy_convenience_variable (PyObject *self, PyObject *args)
+{
+  const char *varname;
+  struct value *res_val = NULL;
+
+  if (!PyArg_ParseTuple (args, "s", &varname))
+    return NULL;
+
+  TRY
+    {
+      struct internalvar *var = lookup_only_internalvar (varname);
+
+      if (var != NULL)
+       {
+         res_val = value_of_internalvar (python_gdbarch, var);
+         if (TYPE_CODE (value_type (res_val)) == TYPE_CODE_VOID)
+           res_val = NULL;
+       }
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
+  if (res_val == NULL)
+    Py_RETURN_NONE;
+
+  return value_to_value_object (res_val);
+}
+
+/* Set the value of a convenience variable.  */
+PyObject *
+gdbpy_set_convenience_variable (PyObject *self, PyObject *args)
+{
+  const char *varname;
+  PyObject *value_obj;
+  struct value *value = NULL;
+
+  if (!PyArg_ParseTuple (args, "sO", &varname, &value_obj))
+    return NULL;
+
+  /* None means to clear the variable.  */
+  if (value_obj != Py_None)
+    {
+      value = convert_value_from_python (value_obj);
+      if (value == NULL)
+       return NULL;
+    }
+
+  TRY
+    {
+      if (value == NULL)
+       {
+         struct internalvar *var = lookup_only_internalvar (varname);
+
+         if (var != NULL)
+           clear_internalvar (var);
+       }
+      else
+       {
+         struct internalvar *var = lookup_internalvar (varname);
+
+         set_internalvar (var, value);
+       }
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
+  Py_RETURN_NONE;
+}
+
 /* Returns 1 in OBJ is a gdb.Value object, 0 otherwise.  */
 
 int
index 26400f4fbaab655e3462717286a7836311731626..495655e759253819abbbeb805d6f893686d02651 100644 (file)
@@ -477,6 +477,8 @@ extern enum ext_lang_rc gdbpy_get_matching_xmethod_workers
 
 \f
 PyObject *gdbpy_history (PyObject *self, PyObject *args);
+PyObject *gdbpy_convenience_variable (PyObject *self, PyObject *args);
+PyObject *gdbpy_set_convenience_variable (PyObject *self, PyObject *args);
 PyObject *gdbpy_breakpoints (PyObject *, PyObject *);
 PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *);
 PyObject *gdbpy_lookup_symbol (PyObject *self, PyObject *args, PyObject *kw);
index c29e7d7a6b27e9cc21eae176144c5685131ce9ba..1805c90628455be92b6788be2a531f302cef7fe9 100644 (file)
@@ -2132,6 +2132,14 @@ Return a tuple containing all inferiors." },
 Invalidate any cached frame objects in gdb.\n\
 Intended for internal use only." },
 
+  { "convenience_variable", gdbpy_convenience_variable, METH_VARARGS,
+    "convenience_variable (NAME) -> value.\n\
+Return the value of the convenience variable $NAME,\n\
+or None if not set." },
+  { "set_convenience_variable", gdbpy_set_convenience_variable, METH_VARARGS,
+    "convenience_variable (NAME, VALUE) -> None.\n\
+Set the value of the convenience variable $NAME." },
+
   {NULL, NULL, 0, NULL}
 };
 
index f6bf93add0d346259d4bbaccf3b598829d3b9e7d..2780b783bfc826cda1a7963c96c3847b955b2097 100644 (file)
@@ -477,3 +477,23 @@ gdb_py_test_silent_cmd "step" "Step into func2" 1
 gdb_py_test_silent_cmd "up" "Step out of func2" 1
 
 gdb_test "python print (gdb.find_pc_line(gdb.selected_frame().pc()).line > line)" "True" "test find_pc_line with resume address"
+
+gdb_test_no_output "set variable \$cvar1 = 23" "set convenience variable"
+gdb_test "python print(gdb.convenience_variable('cvar1'))" "23"
+gdb_test "python print(gdb.convenience_variable('cvar2'))" "None"
+gdb_test_no_output "python gdb.set_convenience_variable('cvar1', 89)" \
+    "change convenience variable from python"
+gdb_test "python print(gdb.convenience_variable('cvar1'))" "89" \
+    "print new value of convenience variable from python"
+gdb_test "print \$cvar1" " = 89" \
+    "print new value of convenience variable from CLI"
+gdb_test_no_output "python gdb.set_convenience_variable('cvar3', -5)" \
+    "make convenience variable from python"
+gdb_test "python print(gdb.convenience_variable('cvar3'))" "-5" \
+    "print value of new convenience variable from python"
+gdb_test_no_output "python gdb.set_convenience_variable('cvar3', None)" \
+    "reset convenience variable from python"
+gdb_test "python print(gdb.convenience_variable('cvar3'))" "None" \
+    "print reset convenience variable from python"
+gdb_test "print \$cvar3" "= void" \
+    "print reset convenience variable from CLI"