Skip to content

Commit 1757ad3

Browse files
ivanivanov884jeffmahoney
authored andcommitted
gdb-python-gil.patch
;; Fix Python GIL with gdb.execute("continue") (Phil Muldoon, BZ 1116957). ;;=push
1 parent 64a34f3 commit 1757ad3

File tree

6 files changed

+157
-6
lines changed

6 files changed

+157
-6
lines changed

gdb/doc/python.texi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,14 @@ returned as a string. The default is @code{False}, in which case the
234234
return value is @code{None}. If @var{to_string} is @code{True}, the
235235
@value{GDBN} virtual terminal will be temporarily set to unlimited width
236236
and height, and its pagination will be disabled; @pxref{Screen Size}.
237+
238+
The @var{release_gil} flag specifies whether @value{GDBN} ought to
239+
release the Python GIL before executing the command. This is useful
240+
in multi-threaded Python programs where by default the Python
241+
interpreter will acquire the GIL and lock other threads from
242+
executing. After the command has completed executing in @value{GDBN}
243+
the Python GIL is reacquired. This flag must be a boolean value. If
244+
omitted, it defaults to @code{False}.
237245
@end defun
238246

239247
@findex gdb.breakpoints

gdb/python/python-internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@
115115
#define PyGILState_Release(ARG) ((void)(ARG))
116116
#define PyEval_InitThreads()
117117
#define PyThreadState_Swap(ARG) ((void)(ARG))
118+
#define PyEval_SaveThread() ((void)(ARG))
119+
#define PyEval_RestoreThread(ARG) ((void)(ARG))
118120
#define PyEval_ReleaseLock()
119121
#endif
120122

gdb/python/python.c

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -543,12 +543,16 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
543543
{
544544
const char *arg;
545545
PyObject *from_tty_obj = NULL, *to_string_obj = NULL;
546-
int from_tty, to_string;
547-
static const char *keywords[] = { "command", "from_tty", "to_string", NULL };
546+
int from_tty, to_string, release_gil;
547+
static const char *keywords[] = {"command", "from_tty", "to_string", "release_gil", NULL };
548+
PyObject *release_gil_obj = NULL;
549+
/* Initialize it just to avoid a GCC false warning. */
550+
PyThreadState *state = NULL;
548551

549-
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!", keywords, &arg,
552+
if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!O!", keywords, &arg,
550553
&PyBool_Type, &from_tty_obj,
551-
&PyBool_Type, &to_string_obj))
554+
&PyBool_Type, &to_string_obj,
555+
&PyBool_Type, &release_gil_obj))
552556
return NULL;
553557

554558
from_tty = 0;
@@ -569,6 +573,15 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
569573
to_string = cmp;
570574
}
571575

576+
release_gil = 0;
577+
if (release_gil_obj)
578+
{
579+
int cmp = PyObject_IsTrue (release_gil_obj);
580+
if (cmp < 0)
581+
return NULL;
582+
release_gil = cmp;
583+
}
584+
572585
std::string to_string_res;
573586

574587
scoped_restore preventer = prevent_dont_repeat ();
@@ -593,10 +606,16 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
593606

594607
counted_command_line lines = read_command_lines_1 (reader, 1, nullptr);
595608

609+
/* In the case of long running GDB commands, allow the user to
610+
release the Python GIL acquired by Python. Restore the GIL
611+
after the command has completed before handing back to
612+
Python. */
613+
if (release_gil)
614+
state = PyEval_SaveThread();
615+
596616
{
597617
scoped_restore save_async = make_scoped_restore (&current_ui->async,
598618
0);
599-
600619
scoped_restore save_uiout = make_scoped_restore (&current_uiout);
601620

602621
/* Use the console interpreter uiout to have the same print format
@@ -611,12 +630,24 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
611630
execute_control_commands (lines.get (), from_tty);
612631
}
613632

633+
/* Reacquire the GIL if it was released earlier. */
634+
if (release_gil)
635+
PyEval_RestoreThread (state);
636+
614637
/* Do any commands attached to breakpoint we stopped at. */
615638
bpstat_do_actions ();
616639
}
617640
catch (const gdb_exception &except)
618641
{
619-
GDB_PY_HANDLE_EXCEPTION (except);
642+
if (except.reason < 0)
643+
{
644+
/* Reacquire the GIL if it was released earlier. */
645+
if (release_gil)
646+
PyEval_RestoreThread (state);
647+
648+
gdbpy_convert_exception (except);
649+
return NULL;
650+
}
620651
}
621652

622653
if (to_string)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <stdio.h>
2+
#include <unistd.h>
3+
4+
int
5+
main (void)
6+
{
7+
int i;
8+
for (i = 0; i < 10; i++)
9+
{
10+
sleep (1); /* break-here */
11+
printf ("Sleeping %d\n", i);
12+
}
13+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Copyright (C) 2014 Free Software Foundation, Inc.
2+
3+
# This program is free software; you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation; either version 3 of the License, or
6+
# (at your option) any later version.
7+
#
8+
# This program is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
# GNU General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU General Public License
14+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
16+
standard_testfile .c .py
17+
set executable $testfile
18+
19+
if { [prepare_for_testing $testfile.exp $executable $srcfile] } {
20+
return -1
21+
}
22+
23+
# Skip all tests if Python scripting is not enabled.
24+
if { [skip_python_tests] } { continue }
25+
26+
if ![runto_main] {
27+
return -1
28+
}
29+
30+
gdb_breakpoint $srcfile:[gdb_get_line_number "break-here"] temporary
31+
gdb_continue_to_breakpoint "break-here" ".* break-here .*"
32+
33+
set test "response"
34+
set timeout 60
35+
set sleeping_last -1
36+
set hello_last 0
37+
set minimal 5
38+
gdb_test_multiple "python exec (open ('$srcdir/$subdir/$srcfile2').read ())" $test {
39+
-re "Error: unable to start thread\r\n" {
40+
fail $test
41+
# Not $gdb_prompt-synced!
42+
}
43+
-re "Sleeping (\[0-9\]+)\r\n" {
44+
set n $expect_out(1,string)
45+
if { $sleeping_last + 1 != $n } {
46+
fail $test
47+
} else {
48+
set sleeping_last $n
49+
if { $sleeping_last >= $minimal && $hello_last >= $minimal } {
50+
pass $test
51+
} else {
52+
exp_continue
53+
}
54+
}
55+
}
56+
-re "Hello \\( (\[0-9\]+) \\)\r\n" {
57+
set n $expect_out(1,string)
58+
if { $hello_last + 1 != $n } {
59+
fail $test
60+
} else {
61+
set hello_last $n
62+
if { $sleeping_last >= $minimal && $hello_last >= $minimal } {
63+
pass $test
64+
} else {
65+
exp_continue
66+
}
67+
}
68+
}
69+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
try:
2+
import thread
3+
except:
4+
import _thread
5+
import time
6+
import gdb
7+
8+
# Define a function for the thread
9+
def print_thread_hello():
10+
count = 0
11+
while count < 10:
12+
time.sleep(1)
13+
count += 1
14+
print ("Hello ( %d )" % count)
15+
16+
# Create a threads a continue
17+
try:
18+
thread.start_new_thread (print_thread_hello, ())
19+
gdb.execute ("continue", release_gil=True)
20+
except:
21+
try:
22+
_thread.start_new_thread (print_thread_hello, ())
23+
gdb.execute ("continue", release_gil=True)
24+
except:
25+
print ("Error: unable to start thread")
26+
27+
while 1:
28+
pass

0 commit comments

Comments
 (0)