This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


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.


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)

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]