diff mbox

[doc,RFA] New feature: extension language post-initializers

Message ID 047d7b15b28fee2e17051ce557fd@google.com
State New
Headers show

Commit Message

Doug Evans Aug. 9, 2015, 6:50 p.m. UTC
Hi.

I have a need to run some python code from system.gdbinit after options
-ix, -iex, -cd are processed and before the inferior program is loaded.

Regression tested on amd64-linux.

2015-08-09  Doug Evans  <dje@google.com>

	* NEWS: Document new post-initializer support for Python.
	* extension-priv.h (extension_language_ops) <post_initialization>:
	New member.
	* extension.c (post_ext_lang_initialization): New function.
	* extension.h (post_ext_lang_initialization): Declare.
	* main.c: #include "extension.h".
	(captured_main): Call post_ext_lang_initialization.
	* python/lib/gdb/__init__.py (post_initializers): Define.
	* python/python.c (python_extension_ops): Update.
	(gdbpy_post_initialization): New function.
	* guile/guile.c (guile_extension_ops): Update.

	doc/
	* python.texi (Python API): Add entry for Startup Post-Initialization.
	(Startup Post-Initialization): New node.

	testsuite/
	* gdb.python/py-post-init.c: New file.
	* gdb.python/py-post-init.exp: New file.
	* gdb.python/py-post-init.py: New file.

Comments

Eli Zaretskii Aug. 9, 2015, 7:07 p.m. UTC | #1
> Date: Sun, 09 Aug 2015 18:50:31 +0000
> From: Doug Evans <dje@google.com>
> 
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,11 @@
> 
>   *** Changes since GDB 7.10
> 
> +* Python Scripting
> +
> +  ** Python code can now be run after -ix/-iex processing and before any
> +     program is loaded by appending functions to gdb.post_initializers.
> +
>   * Support for tracepoints on aarch64-linux was added in GDBserver.

OK.

> +@node Startup Post-Initialization
> +@subsubsection Startup Post-Initialization
> +@cindex running python at the end of GDB startup
                                        ^^^
@value{GDBN}

> +@value{GDBN} provides a way to run Python code at the end of startup,
> +after @code{-iex}, @code{-ix}, and @code{-cd} option processing,
> +and before it loads any specified program.
> +Such initialization may depend on various startup options thus we want
> +to defer it until after all such options are processed.

"thus we want" sounds strange in this context.  How about the
following alternative:

  Such initialization may depend on various startup options, so
  @value{GDBN} invokes them after all such options are processed.

The documentation parts are OK with those fixed.

Thanks.
diff mbox

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 3fe6036..a8d1bff 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,11 @@ 

  *** Changes since GDB 7.10

+* Python Scripting
+
+  ** Python code can now be run after -ix/-iex processing and before any
+     program is loaded by appending functions to gdb.post_initializers.
+
  * Support for tracepoints on aarch64-linux was added in GDBserver.

  * The 'record instruction-history' command now indicates speculative  
execution
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index a2df254..d9e68a8 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -135,6 +135,7 @@  optional arguments while skipping others.  Example:
  @menu
  * Basic Python::                Basic Python Functions.
  * Exception Handling::          How Python exceptions are translated.
+* Startup Post-Initialization:: Running python at the end of GDB startup.
  * Values From Inferior::        Python representation of values.
  * Types In Python::             Python representation of types.
  * Pretty Printing API::         Pretty-printing values.
@@ -512,6 +513,48 @@  to handle this case.  Example:
  hello-world takes no arguments
  @end smallexample

+@node Startup Post-Initialization
+@subsubsection Startup Post-Initialization
+@cindex running python at the end of GDB startup
+
+@value{GDBN} provides a way to run Python code at the end of startup,
+after @code{-iex}, @code{-ix}, and @code{-cd} option processing,
+and before it loads any specified program.
+Such initialization may depend on various startup options thus we want
+to defer it until after all such options are processed.
+
+The Python list @code{gdb.post_initializers} contains an array of
+functions or callable objects that have been registered.
+Registration is done via appending to the list.
+
+As an example, suppose the @file{system.gdbinit} file provides a set of
+features that are loaded, if present, from the current directory.
+The features must be loaded before the program is loaded,
+and must take into account the @code{-cd} option.
+Thus loading must be delayed until after processing the @code{-cd}
+option but before the inferior program is loaded.
+This is achieved with a ``post-initializer'' function.
+The function is registered by @file{system.gdbinit} and is called
+at the correct time by @value{GDBN}.
+
+Here is a small example:
+
+@smallexample
+$ cd $HOME
+$ cat post-init.py
+def post_init():
+  print "Hi, this is post_init."
+  gdb.execute("pwd")
+import gdb
+gdb.post_initializers.append(post_init)
+$ gdb -quiet -cd /tmp -iex pwd -ix post-init.py $HOME/myprogram
+Working directory /home/user.
+Hi, this is post_init.
+Working directory /tmp.
+Reading symbols from /home/user/myprogram...done.
+(gdb)
+@end smallexample
+
  @node Values From Inferior
  @subsubsection Values From Inferior
  @cindex values from inferior, with Python
diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h
index d0242e2..b785432 100644
--- a/gdb/extension-priv.h
+++ b/gdb/extension-priv.h
@@ -136,6 +136,10 @@  struct extension_language_ops
       This method is required.  */
    int (*initialized) (const struct extension_language_defn *);

+  /* Called after -iex, -ix, -cd and -d options are processed to perform
+     any final user-specified initialization prior to loading the binary.   
*/
+  void (*post_initialization) (const struct extension_language_defn *);
+
    /* Process a sequence of commands embedded in GDB's own scripting  
language.
       E.g.,
       python
diff --git a/gdb/extension.c b/gdb/extension.c
index dac203b..bd7eea0 100644
--- a/gdb/extension.c
+++ b/gdb/extension.c
@@ -337,6 +337,22 @@  finish_ext_lang_initialization (void)
      }
  }

+/* Wrapper to call the extension_language_ops.post_initialization "method"
+   for each compiled-in extension language.  */
+
+void
+post_ext_lang_initialization (void)
+{
+  int i;
+  const struct extension_language_defn *extlang;
+
+  ALL_ENABLED_EXTENSION_LANGUAGES (i, extlang)
+    {
+      if (extlang->ops->post_initialization != NULL)
+	extlang->ops->post_initialization (extlang);
+    }
+}
+
  /* Invoke the appropriate extension_language_ops.eval_from_control_command
     method to perform CMD, which is a list of commands in an extension  
language.

diff --git a/gdb/extension.h b/gdb/extension.h
index ea30035..7f9ac7a 100644
--- a/gdb/extension.h
+++ b/gdb/extension.h
@@ -213,6 +213,8 @@  extern int ext_lang_auto_load_enabled (const struct  
extension_language_defn *);

  extern void finish_ext_lang_initialization (void);

+extern void post_ext_lang_initialization (void);
+
  extern void eval_ext_lang_from_control_command (struct command_line *cmd);

  extern void auto_load_ext_lang_scripts_for_objfile (struct objfile *);
diff --git a/gdb/guile/guile.c b/gdb/guile/guile.c
index 4abf5c5..dbb2836 100644
--- a/gdb/guile/guile.c
+++ b/gdb/guile/guile.c
@@ -138,6 +138,7 @@  const struct extension_language_ops guile_extension_ops  
=
  {
    gdbscm_finish_initialization,
    gdbscm_initialized,
+  NULL, /* gdbscm_post_initialization */

    gdbscm_eval_from_control_command,

diff --git a/gdb/main.c b/gdb/main.c
index aecd60a..084a1f5 100644
--- a/gdb/main.c
+++ b/gdb/main.c
@@ -44,6 +44,7 @@ 
  #include <signal.h>
  #include "event-top.h"
  #include "infrun.h"
+#include "extension.h"

  /* The selected interpreter.  This will be used as a set command
     variable, so it should always be malloc'ed - since
@@ -1035,6 +1036,12 @@  captured_main (void *data)
      catch_command_errors (directory_switch, dirarg[i], 0);
    xfree (dirarg);

+  /* We're about to load the program (if specified).
+     Given extension languages a chance to do any final preprocessing,
+     after -ix, -iex, -cd, and -d parameters are processed, and prior to
+     the program being loaded.  */
+  post_ext_lang_initialization ();
+
    /* Skip auto-loading section-specified scripts until we've sourced
       local_gdbinit (which is often used to augment the source search
       path).  */
diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
index 81789e5..03f9b18 100644
--- a/gdb/python/lib/gdb/__init__.py
+++ b/gdb/python/lib/gdb/__init__.py
@@ -62,6 +62,9 @@  prompt_hook = None
  # We do not use PySys_SetArgvEx because it did not appear until 2.6.6.
  sys.argv = ['']

+# Initial post-initializers.
+post_initializers = []
+
  # Initial pretty printers.
  pretty_printers = []

diff --git a/gdb/python/python.c b/gdb/python/python.c
index 4f88b0e..7c839b9 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -135,6 +135,8 @@  static objfile_script_executor_func  
gdbpy_execute_objfile_script;
  static void gdbpy_finish_initialization
    (const struct extension_language_defn *);
  static int gdbpy_initialized (const struct extension_language_defn *);
+static void gdbpy_post_initialization
+  (const struct extension_language_defn *);
  static void gdbpy_eval_from_control_command
    (const struct extension_language_defn *, struct command_line *cmd);
  static void gdbpy_start_type_printers (const struct  
extension_language_defn *,
@@ -166,6 +168,7 @@  const struct extension_language_ops  
python_extension_ops =
  {
    gdbpy_finish_initialization,
    gdbpy_initialized,
+  gdbpy_post_initialization,

    gdbpy_eval_from_control_command,

@@ -1967,6 +1970,54 @@  gdbpy_initialized (const struct  
extension_language_defn *extlang)
    return gdb_python_initialized;
  }

+/* Called after -iex, -ix, -cd and -d options are processed to perform
+   any final user-specified initialization prior to loading the binary.  */
+
+static void
+gdbpy_post_initialization (const struct extension_language_defn *extlang)
+{
+  PyObject *pi_list, *function, *result;
+  Py_ssize_t pi_list_size, list_index;
+  struct cleanup *cleanup;
+
+  cleanup = ensure_python_env (get_current_arch (), current_language);
+
+  /* Fetch the global post-initializer list.  */
+  if (gdb_python_module == NULL
+      || ! PyObject_HasAttrString (gdb_python_module, "post_initializers"))
+    {
+      do_cleanups (cleanup);
+      return;
+    }
+  pi_list = PyObject_GetAttrString  
(gdb_python_module, "post_initializers");
+  if (pi_list == NULL || ! PyList_Check (pi_list))
+    {
+      Py_XDECREF (pi_list);
+      do_cleanups (cleanup);
+      return;
+    }
+
+  pi_list_size = PyList_Size (pi_list);
+  for (list_index = 0; list_index < pi_list_size; list_index++)
+    {
+      function = PyList_GetItem (pi_list, list_index);
+      result = NULL;
+      if (function != NULL)
+	result = PyObject_CallFunctionObjArgs (function, NULL);
+      if (result == NULL)
+	{
+	  /* Print the trace here, but keep going -- we want to
+	     call all of the callbacks even if one is broken.  */
+	  gdbpy_print_stack ();
+	}
+      Py_XDECREF (function);
+      Py_XDECREF (result);
+    }
+
+  Py_DECREF (pi_list);
+  do_cleanups (cleanup);
+}
+
  #endif /* HAVE_PYTHON */

  
diff --git a/gdb/testsuite/gdb.python/py-post-init.c  
b/gdb/testsuite/gdb.python/py-post-init.c
new file mode 100644
index 0000000..d4206cb
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-post-init.c
@@ -0,0 +1,22 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2015 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.   
*/
+
+int
+main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.python/py-post-init.exp  
b/gdb/testsuite/gdb.python/py-post-init.exp
new file mode 100644
index 0000000..5b575af
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-post-init.exp
@@ -0,0 +1,58 @@ 
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests the post_initializers
+# mechanism.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if {[build_executable ${testfile}.exp ${testfile} ${srcfile} debug] == -1}  
{
+    return -1
+}
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+
+# Skip all tests if Python scripting is not enabled.
+# Note that this requires a running gdb.
+if { [skip_python_tests] } { continue }
+
+gdb_exit
+
+# Make the program and  python scripts available to gdb
+# (in case it is remote).
+set program_file \
+    [gdb_remote_download host ${binfile}]
+set post_init_script \
+    [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+
+# We want to pass the program to gdb at startup so that we can test that
+# post_init functions are called after -iex options (and -ix and others)
+# and before the program is loaded by gdb.
+
+set options "--ix ${post_init_script}"
+append options " -iex \"py post_init_test = 0\""
+append options " ${program_file}"
+set res [gdb_spawn_with_cmdline_opts $options]
+set test "starting with post-init script"
+gdb_test_multiple "" $test {
+    -re "Hi, I'm post_init.*Reading symbols from.*$gdb_prompt $" {
+	pass "$test"
+    }
+}
+gdb_test "py print post_init_test" "10"
diff --git a/gdb/testsuite/gdb.python/py-post-init.py  
b/gdb/testsuite/gdb.python/py-post-init.py
new file mode 100644
index 0000000..013aa8e
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-post-init.py
@@ -0,0 +1,23 @@ 
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import gdb
+
+def post_init():
+  print "Hi, I'm post_init."
+  global post_init_test
+  post_init_test += 10
+
+gdb.post_initializers.append(post_init)