Blame SOURCES/gdb-python-gil.patch

4c2ad1
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
4c2ad1
From: Fedora GDB patches <invalid@email.com>
4c2ad1
Date: Fri, 27 Oct 2017 21:07:50 +0200
4c2ad1
Subject: gdb-python-gil.patch
4c2ad1
4c2ad1
;; Fix Python GIL with gdb.execute("continue") (Phil Muldoon, BZ 1116957).
4c2ad1
;;=push
4c2ad1
4c2ad1
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
4c2ad1
--- a/gdb/doc/python.texi
4c2ad1
+++ b/gdb/doc/python.texi
4c2ad1
@@ -232,6 +232,14 @@ returned as a string.  The default is @code{False}, in which case the
4c2ad1
 return value is @code{None}.  If @var{to_string} is @code{True}, the
4c2ad1
 @value{GDBN} virtual terminal will be temporarily set to unlimited width
4c2ad1
 and height, and its pagination will be disabled; @pxref{Screen Size}.
4c2ad1
+
4c2ad1
+The @var{release_gil} flag specifies whether @value{GDBN} ought to
4c2ad1
+release the Python GIL before executing the command.  This is useful
4c2ad1
+in multi-threaded Python programs where by default the Python
4c2ad1
+interpreter will acquire the GIL and lock other threads from
4c2ad1
+executing.  After the command has completed executing in @value{GDBN}
4c2ad1
+the Python GIL is reacquired. This flag must be a boolean value.  If
4c2ad1
+omitted, it defaults to @code{False}.
4c2ad1
 @end defun
4c2ad1
 
4c2ad1
 @findex gdb.breakpoints
4c2ad1
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
4c2ad1
--- a/gdb/python/python-internal.h
4c2ad1
+++ b/gdb/python/python-internal.h
4c2ad1
@@ -148,6 +148,8 @@ typedef int Py_ssize_t;
4c2ad1
 #define PyGILState_Release(ARG) ((void)(ARG))
4c2ad1
 #define PyEval_InitThreads()
4c2ad1
 #define PyThreadState_Swap(ARG) ((void)(ARG))
4c2ad1
+#define PyEval_SaveThread() ((void)(ARG))
4c2ad1
+#define PyEval_RestoreThread(ARG) ((void)(ARG))
4c2ad1
 #define PyEval_ReleaseLock()
4c2ad1
 #endif
4c2ad1
 
4c2ad1
diff --git a/gdb/python/python.c b/gdb/python/python.c
4c2ad1
--- a/gdb/python/python.c
4c2ad1
+++ b/gdb/python/python.c
4c2ad1
@@ -556,12 +556,16 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
4c2ad1
 {
4c2ad1
   const char *arg;
4c2ad1
   PyObject *from_tty_obj = NULL, *to_string_obj = NULL;
4c2ad1
-  int from_tty, to_string;
4c2ad1
-  static const char *keywords[] = { "command", "from_tty", "to_string", NULL };
4c2ad1
+  int from_tty, to_string, release_gil;
4c2ad1
+  static const char *keywords[] = {"command", "from_tty", "to_string", "release_gil", NULL };
4c2ad1
+  PyObject *release_gil_obj = NULL;
4c2ad1
+  /* Initialize it just to avoid a GCC false warning.  */
4c2ad1
+  PyThreadState *state = NULL;
4c2ad1
 
4c2ad1
-  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!", keywords, &arg,
4c2ad1
+  if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!O!", keywords, &arg,
4c2ad1
 					&PyBool_Type, &from_tty_obj,
4c2ad1
-					&PyBool_Type, &to_string_obj))
4c2ad1
+					&PyBool_Type, &to_string_obj,
4c2ad1
+					&PyBool_Type, &release_gil_obj))
4c2ad1
     return NULL;
4c2ad1
 
4c2ad1
   from_tty = 0;
4c2ad1
@@ -582,6 +586,15 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
4c2ad1
       to_string = cmp;
4c2ad1
     }
4c2ad1
 
4c2ad1
+  release_gil = 0;
4c2ad1
+  if (release_gil_obj)
4c2ad1
+    {
4c2ad1
+      int cmp = PyObject_IsTrue (release_gil_obj);
4c2ad1
+      if (cmp < 0)
4c2ad1
+	return NULL;
4c2ad1
+      release_gil = cmp;
4c2ad1
+    }
4c2ad1
+
4c2ad1
   std::string to_string_res;
4c2ad1
 
4c2ad1
   TRY
4c2ad1
@@ -602,6 +615,13 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
4c2ad1
 
4c2ad1
       counted_command_line lines = read_command_lines_1 (reader, 1, nullptr);
4c2ad1
 
4c2ad1
+      /* In the case of long running GDB commands, allow the user to
4c2ad1
+	 release the Python GIL acquired by Python.  Restore the GIL
4c2ad1
+	 after the command has completed before handing back to
4c2ad1
+	 Python.  */
4c2ad1
+      if (release_gil)
4c2ad1
+	state = PyEval_SaveThread();
4c2ad1
+
4c2ad1
       scoped_restore save_async = make_scoped_restore (&current_ui->async, 0);
4c2ad1
 
4c2ad1
       scoped_restore save_uiout = make_scoped_restore (&current_uiout);
4c2ad1
@@ -617,10 +637,22 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
4c2ad1
 							    from_tty);
4c2ad1
       else
4c2ad1
 	execute_control_commands (lines.get (), from_tty);
4c2ad1
+
4c2ad1
+      /* Reacquire the GIL if it was released earlier.  */
4c2ad1
+      if (release_gil)
4c2ad1
+	PyEval_RestoreThread (state);
4c2ad1
     }
4c2ad1
   CATCH (except, RETURN_MASK_ALL)
4c2ad1
     {
4c2ad1
-      GDB_PY_HANDLE_EXCEPTION (except);
4c2ad1
+      if (except.reason < 0)
4c2ad1
+	{
4c2ad1
+	  /* Reacquire the GIL if it was released earlier.  */
4c2ad1
+	  if (release_gil)
4c2ad1
+	    PyEval_RestoreThread (state);
4c2ad1
+
4c2ad1
+	  gdbpy_convert_exception (except);
4c2ad1
+	  return NULL;
4c2ad1
+	}
4c2ad1
     }
4c2ad1
   END_CATCH
4c2ad1
 
4c2ad1
diff --git a/gdb/testsuite/gdb.python/py-gil-mthread.c b/gdb/testsuite/gdb.python/py-gil-mthread.c
4c2ad1
new file mode 100644
4c2ad1
--- /dev/null
4c2ad1
+++ b/gdb/testsuite/gdb.python/py-gil-mthread.c
4c2ad1
@@ -0,0 +1,13 @@
4c2ad1
+#include <stdio.h>
4c2ad1
+#include <unistd.h>
4c2ad1
+
4c2ad1
+int
4c2ad1
+main (void)
4c2ad1
+{
4c2ad1
+  int i;
4c2ad1
+  for (i = 0; i < 10; i++)
4c2ad1
+    {
4c2ad1
+      sleep (1); /* break-here */
4c2ad1
+      printf ("Sleeping %d\n", i);
4c2ad1
+    }
4c2ad1
+}
4c2ad1
diff --git a/gdb/testsuite/gdb.python/py-gil-mthread.exp b/gdb/testsuite/gdb.python/py-gil-mthread.exp
4c2ad1
new file mode 100644
4c2ad1
--- /dev/null
4c2ad1
+++ b/gdb/testsuite/gdb.python/py-gil-mthread.exp
4c2ad1
@@ -0,0 +1,69 @@
4c2ad1
+# Copyright (C) 2014 Free Software Foundation, Inc.
4c2ad1
+
4c2ad1
+# This program is free software; you can redistribute it and/or modify
4c2ad1
+# it under the terms of the GNU General Public License as published by
4c2ad1
+# the Free Software Foundation; either version 3 of the License, or
4c2ad1
+# (at your option) any later version.
4c2ad1
+#
4c2ad1
+# This program is distributed in the hope that it will be useful,
4c2ad1
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
4c2ad1
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4c2ad1
+# GNU General Public License for more details.
4c2ad1
+#
4c2ad1
+# You should have received a copy of the GNU General Public License
4c2ad1
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
4c2ad1
+
4c2ad1
+standard_testfile .c .py
4c2ad1
+set executable $testfile
4c2ad1
+
4c2ad1
+if { [prepare_for_testing $testfile.exp $executable $srcfile] } {
4c2ad1
+    return -1
4c2ad1
+}
4c2ad1
+
4c2ad1
+# Skip all tests if Python scripting is not enabled.
4c2ad1
+if { [skip_python_tests] } { continue }
4c2ad1
+
4c2ad1
+if ![runto_main] {
4c2ad1
+    return -1
4c2ad1
+}
4c2ad1
+
4c2ad1
+gdb_breakpoint $srcfile:[gdb_get_line_number "break-here"] temporary
4c2ad1
+gdb_continue_to_breakpoint "break-here" ".* break-here .*"
4c2ad1
+
4c2ad1
+set test "response"
4c2ad1
+set timeout 60
4c2ad1
+set sleeping_last -1
4c2ad1
+set hello_last 0
4c2ad1
+set minimal 5
4c2ad1
+gdb_test_multiple "python exec (open ('$srcdir/$subdir/$srcfile2').read ())" $test {
4c2ad1
+    -re "Error: unable to start thread\r\n" {
4c2ad1
+	fail $test
4c2ad1
+	# Not $gdb_prompt-synced!
4c2ad1
+    }
4c2ad1
+    -re "Sleeping (\[0-9\]+)\r\n" {
4c2ad1
+	set n $expect_out(1,string)
4c2ad1
+	if { $sleeping_last + 1 != $n } {
4c2ad1
+	    fail $test
4c2ad1
+	} else {
4c2ad1
+	    set sleeping_last $n
4c2ad1
+	    if { $sleeping_last >= $minimal && $hello_last >= $minimal } {
4c2ad1
+		pass $test
4c2ad1
+	    } else {
4c2ad1
+		exp_continue
4c2ad1
+	    }
4c2ad1
+	}
4c2ad1
+    }
4c2ad1
+    -re "Hello \\( (\[0-9\]+) \\)\r\n" {
4c2ad1
+	set n $expect_out(1,string)
4c2ad1
+	if { $hello_last + 1 != $n } {
4c2ad1
+	    fail $test
4c2ad1
+	} else {
4c2ad1
+	    set hello_last $n
4c2ad1
+	    if { $sleeping_last >= $minimal && $hello_last >= $minimal } {
4c2ad1
+		pass $test
4c2ad1
+	    } else {
4c2ad1
+		exp_continue
4c2ad1
+	    }
4c2ad1
+	}
4c2ad1
+    }
4c2ad1
+}
4c2ad1
diff --git a/gdb/testsuite/gdb.python/py-gil-mthread.py b/gdb/testsuite/gdb.python/py-gil-mthread.py
4c2ad1
new file mode 100644
4c2ad1
--- /dev/null
4c2ad1
+++ b/gdb/testsuite/gdb.python/py-gil-mthread.py
4c2ad1
@@ -0,0 +1,28 @@
4c2ad1
+try:
4c2ad1
+   import thread
4c2ad1
+except:
4c2ad1
+   import _thread
4c2ad1
+import time
4c2ad1
+import gdb
4c2ad1
+
4c2ad1
+# Define a function for the thread
4c2ad1
+def print_thread_hello():
4c2ad1
+   count = 0
4c2ad1
+   while count < 10:
4c2ad1
+      time.sleep(1)
4c2ad1
+      count += 1
4c2ad1
+      print ("Hello ( %d )" % count)
4c2ad1
+
4c2ad1
+# Create a threads a continue
4c2ad1
+try:
4c2ad1
+   thread.start_new_thread (print_thread_hello, ())
4c2ad1
+   gdb.execute ("continue", release_gil=True)
4c2ad1
+except:
4c2ad1
+   try:
4c2ad1
+      _thread.start_new_thread (print_thread_hello, ())
4c2ad1
+      gdb.execute ("continue", release_gil=True)
4c2ad1
+   except:
4c2ad1
+      print ("Error: unable to start thread")
4c2ad1
+
4c2ad1
+while 1:
4c2ad1
+   pass