[gdb] Fix Python GIL with gdb.execute("continue") (Phil Muldoon, BZ 1116957).

Jan Kratochvil jankratochvil at fedoraproject.org
Wed Aug 13 20:56:01 UTC 2014


commit 0d2fda651f1e54756a48a2a618cd4bee6706e236
Author: Jan Kratochvil <jan.kratochvil at redhat.com>
Date:   Wed Aug 13 22:56:01 2014 +0200

    Fix Python GIL with gdb.execute("continue") (Phil Muldoon, BZ 1116957).

 gdb-python-gil.patch |  223 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb.spec             |    9 ++-
 2 files changed, 231 insertions(+), 1 deletions(-)
---
diff --git a/gdb-python-gil.patch b/gdb-python-gil.patch
new file mode 100644
index 0000000..d839b6b
--- /dev/null
+++ b/gdb-python-gil.patch
@@ -0,0 +1,223 @@
+diff -dup -ruNp gdb-7.8-orig/gdb/doc/python.texi gdb-7.8/gdb/doc/python.texi
+--- gdb-7.8-orig/gdb/doc/python.texi	2014-08-13 22:04:14.162441271 +0200
++++ gdb-7.8/gdb/doc/python.texi	2014-08-13 22:07:20.894643853 +0200
+@@ -228,6 +228,14 @@ returned as a string.  The default is @c
+ return value is @code{None}.  If @var{to_string} is @code{True}, the
+ @value{GDBN} virtual terminal will be temporarily set to unlimited width
+ and height, and its pagination will be disabled; @pxref{Screen Size}.
++
++The @var{release_gil} flag specifies whether @value{GDBN} ought to
++release the Python GIL before executing the command.  This is useful
++in multi-threaded Python programs where by default the Python
++interpreter will acquire the GIL and lock other threads from
++executing.  After the command has completed executing in @value{GDBN}
++the Python GIL is reacquired. This flag must be a boolean value.  If
++omitted, it defaults to @code{False}.
+ @end defun
+ 
+ @findex gdb.breakpoints
+diff -dup -ruNp gdb-7.8-orig/gdb/python/python-internal.h gdb-7.8/gdb/python/python-internal.h
+--- gdb-7.8-orig/gdb/python/python-internal.h	2014-08-13 22:04:14.835441977 +0200
++++ gdb-7.8/gdb/python/python-internal.h	2014-08-13 22:07:20.895643867 +0200
+@@ -143,6 +143,8 @@ typedef int Py_ssize_t;
+ #define PyGILState_Release(ARG) ((void)(ARG))
+ #define PyEval_InitThreads()
+ #define PyThreadState_Swap(ARG) ((void)(ARG))
++#define PyEval_SaveThread() ((void)(ARG))
++#define PyEval_RestoreThread(ARG) ((void)(ARG))
+ #define PyEval_ReleaseLock()
+ #endif
+ 
+diff -dup -ruNp gdb-7.8-orig/gdb/python/python.c gdb-7.8/gdb/python/python.c
+--- gdb-7.8-orig/gdb/python/python.c	2014-08-13 22:04:14.164441273 +0200
++++ gdb-7.8/gdb/python/python.c	2014-08-13 22:07:20.895643867 +0200
+@@ -620,14 +620,18 @@ execute_gdb_command (PyObject *self, PyO
+ {
+   const char *arg;
+   PyObject *from_tty_obj = NULL, *to_string_obj = NULL;
+-  int from_tty, to_string;
++  PyObject *release_gil_obj = NULL;
++  int from_tty, to_string, release_gil;
+   volatile struct gdb_exception except;
+-  static char *keywords[] = {"command", "from_tty", "to_string", NULL };
++  static char *keywords[] = {"command", "from_tty", "to_string",
++			     "release_gil", NULL };
+   char *result = NULL;
++  PyThreadState *state;
+ 
+-  if (! PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!", keywords, &arg,
++  if (! PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!O!", keywords, &arg,
+ 				     &PyBool_Type, &from_tty_obj,
+-				     &PyBool_Type, &to_string_obj))
++				     &PyBool_Type, &to_string_obj,
++				     &PyBool_Type, &release_gil_obj))
+     return NULL;
+ 
+   from_tty = 0;
+@@ -648,12 +652,28 @@ execute_gdb_command (PyObject *self, PyO
+       to_string = cmp;
+     }
+ 
++  release_gil = 0;
++  if (release_gil_obj)
++    {
++      int cmp = PyObject_IsTrue (release_gil_obj);
++      if (cmp < 0)
++	return NULL;
++      release_gil = cmp;
++    }
++
+   TRY_CATCH (except, RETURN_MASK_ALL)
+     {
+       /* Copy the argument text in case the command modifies it.  */
+       char *copy = xstrdup (arg);
+       struct cleanup *cleanup = make_cleanup (xfree, copy);
+ 
++      /* In the case of long running GDB commands, allow the user to
++	 release the Python GIL acquired by Python.  Restore the GIL
++	 after the command has completed before handing back to
++	 Python.  */
++      if (release_gil)
++	state = PyEval_SaveThread();
++
+       make_cleanup_restore_integer (&interpreter_async);
+       interpreter_async = 0;
+ 
+@@ -666,9 +686,21 @@ execute_gdb_command (PyObject *self, PyO
+ 	  execute_command (copy, from_tty);
+ 	}
+ 
++      /* Reacquire the GIL if it was released earlier.  */
++      if (release_gil)
++	PyEval_RestoreThread (state);
++
+       do_cleanups (cleanup);
+     }
+-  GDB_PY_HANDLE_EXCEPTION (except);
++  if (except.reason < 0)
++    {
++      /* Reacquire the GIL if it was released earlier.  */
++      if (release_gil)
++	PyEval_RestoreThread (state);
++
++      gdbpy_convert_exception (except);
++      return NULL;
++    }
+ 
+   /* Do any commands attached to breakpoint we stopped at.  */
+   bpstat_do_actions ();
+diff -dup -ruNp gdb-7.8-orig/gdb/testsuite/gdb.python/py-gil-mthread.c gdb-7.8/gdb/testsuite/gdb.python/py-gil-mthread.c
+--- gdb-7.8-orig/gdb/testsuite/gdb.python/py-gil-mthread.c	1970-01-01 01:00:00.000000000 +0100
++++ gdb-7.8/gdb/testsuite/gdb.python/py-gil-mthread.c	2014-08-13 22:33:05.052648912 +0200
+@@ -0,0 +1,12 @@
++#include <stdio.h>
++
++int
++main (void)
++{
++  int i;
++  for (i = 0; i < 10; i++)
++    {
++      sleep (1); /* break-here */
++      printf ("Sleeping %d\n", i);
++    }
++}
+diff -dup -ruNp gdb-7.8-orig/gdb/testsuite/gdb.python/py-gil-mthread.exp gdb-7.8/gdb/testsuite/gdb.python/py-gil-mthread.exp
+--- gdb-7.8-orig/gdb/testsuite/gdb.python/py-gil-mthread.exp	1970-01-01 01:00:00.000000000 +0100
++++ gdb-7.8/gdb/testsuite/gdb.python/py-gil-mthread.exp	2014-08-13 22:33:00.660641300 +0200
+@@ -0,0 +1,69 @@
++# Copyright (C) 2014 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/>.
++
++standard_testfile .c .py
++set executable $testfile
++
++if { [prepare_for_testing $testfile.exp $executable $srcfile] } {
++    return -1
++}
++
++# Skip all tests if Python scripting is not enabled.
++if { [skip_python_tests] } { continue }
++
++if ![runto_main] {
++    return -1
++}
++
++gdb_breakpoint $srcfile:[gdb_get_line_number "break-here"] temporary
++gdb_continue_to_breakpoint "break-here" ".* break-here .*"
++
++set test "response"
++set timeout 60
++set sleeping_last -1
++set hello_last 0
++set minimal 5
++gdb_test_multiple "python execfile('$srcdir/$subdir/$srcfile2')" $test {
++    -re "Error: unable to start thread\r\n" {
++	fail $test
++	# Not $gdb_prompt-synced!
++    }
++    -re "Sleeping (\[0-9\]+)\r\n" {
++	set n $expect_out(1,string)
++	if { $sleeping_last + 1 != $n } {
++	    fail $test
++	} else {
++	    set sleeping_last $n
++	    if { $sleeping_last >= $minimal && $hello_last >= $minimal } {
++		pass $test
++	    } else {
++		exp_continue
++	    }
++	}
++    }
++    -re "Hello \\( (\[0-9\]+) \\)\r\n" {
++	set n $expect_out(1,string)
++	if { $hello_last + 1 != $n } {
++	    fail $test
++	} else {
++	    set hello_last $n
++	    if { $sleeping_last >= $minimal && $hello_last >= $minimal } {
++		pass $test
++	    } else {
++		exp_continue
++	    }
++	}
++    }
++}
+diff -dup -ruNp gdb-7.8-orig/gdb/testsuite/gdb.python/py-gil-mthread.py gdb-7.8/gdb/testsuite/gdb.python/py-gil-mthread.py
+--- gdb-7.8-orig/gdb/testsuite/gdb.python/py-gil-mthread.py	1970-01-01 01:00:00.000000000 +0100
++++ gdb-7.8/gdb/testsuite/gdb.python/py-gil-mthread.py	2014-08-13 22:33:08.996654320 +0200
+@@ -0,0 +1,22 @@
++import thread
++import time
++import gdb
++
++# Define a function for the thread
++def print_thread_hello():
++   count = 0
++   while count < 10:
++      time.sleep(1)
++      count += 1
++      print "Hello (", count, ")"
++
++# Create a threads a continue
++try:
++   thread.start_new_thread( print_thread_hello, ())
++   gdb.execute ("continue", release_gil=True)
++   
++except:
++   print "Error: unable to start thread"
++
++while 1:
++   pass
diff --git a/gdb.spec b/gdb.spec
index 41cdc11..a073c81 100644
--- a/gdb.spec
+++ b/gdb.spec
@@ -26,7 +26,7 @@ Version: 7.8
 
 # The release always contains a leading reserved number, start it at 1.
 # `upstream' is not a part of `name' to stay fully rpm dependencies compatible for the testing.
-Release: 16%{?dist}
+Release: 17%{?dist}
 
 License: GPLv3+ and GPLv3+ with exceptions and GPLv2+ and GPLv2+ with exceptions and GPL+ and LGPLv2+ and BSD and Public Domain and GFDL
 Group: Development/Debuggers
@@ -527,6 +527,9 @@ Patch925: gdb-fortran-frame-string.patch
 # Fix -Werror=unused-variable error configuring babeltrace.
 Patch926: gdb-babeltrace-configure.patch
 
+# Fix Python GIL with gdb.execute("continue") (Phil Muldoon, BZ 1116957).
+Patch927: gdb-python-gil.patch
+
 %if 0%{!?rhel:1} || 0%{?rhel} > 6
 # RL_STATE_FEDORA_GDB would not be found for:
 # Patch642: gdb-readline62-ask-more-rh.patch
@@ -811,6 +814,7 @@ find -name "*.info*"|xargs rm -f
 %patch921 -p1
 %patch925 -p1
 %patch926 -p1
+%patch927 -p1
 
 %patch848 -p1
 %if 0%{!?el6:1}
@@ -1306,6 +1310,9 @@ then
 fi
 
 %changelog
+* Wed Aug 13 2014 Jan Kratochvil <jan.kratochvil at redhat.com> - 7.8-17.fc21
+- Fix Python GIL with gdb.execute("continue") (Phil Muldoon, BZ 1116957).
+
 * Mon Aug  4 2014 Jan Kratochvil <jan.kratochvil at redhat.com> - 7.8-16.fc21
 - Enable babeltrace compile-time feature.
 


More information about the scm-commits mailing list