Skip to content

Commit a207f6b

Browse files
committed
Rewrite "python" command exception handling
The "python" command (and the Python implementation of the gdb "source" command) does not handle Python exceptions in the same way as other gdb-facing Python code. In particular, exceptions are turned into a generic error rather than being routed through gdbpy_handle_exception, which takes care of converting to 'quit' as appropriate. I think this was done this way because PyRun_SimpleFile and friends do not propagate the Python exception -- they simply indicate that one occurred. This patch reimplements these functions to respect the general gdb convention here. As a bonus, some Windows-specific code can be removed, as can the _execute_file function. The bulk of this change is tweaking the test suite to match the new way that exceptions are displayed. These changes are largely uninteresting. However, it's worth pointing out the py-error.exp change. Here, the failure changes because the test changes the host charset to something that isn't supported by Python. This then results in a weird error in the new setup. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31354 Acked-By: Tom de Vries <tdevries@suse.de> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
1 parent 8ee6f71 commit a207f6b

34 files changed

+237
-278
lines changed

gdb/doc/python.texi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -721,9 +721,9 @@ When executing the @code{python} command, Python exceptions
721721
uncaught within the Python code are translated to calls to
722722
@value{GDBN} error-reporting mechanism. If the command that called
723723
@code{python} does not handle the error, @value{GDBN} will
724-
terminate it and print an error message containing the Python
725-
exception name, the associated value, and the Python call stack
726-
backtrace at the point where the exception was raised. Example:
724+
terminate it and print an error message. Exactly what will be printed
725+
depends on @code{set python print-stack} (@pxref{Python Commands}).
726+
Example:
727727

728728
@smallexample
729729
(@value{GDBP}) python print foo

gdb/python/lib/gdb/__init__.py

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -127,33 +127,6 @@ def _execute_unwinders(pending_frame):
127127
return None
128128

129129

130-
def _execute_file(filepath):
131-
"""This function is used to replace Python 2's PyRun_SimpleFile.
132-
133-
Loads and executes the given file.
134-
135-
We could use the runpy module, but its documentation says:
136-
"Furthermore, any functions and classes defined by the executed code are
137-
not guaranteed to work correctly after a runpy function has returned."
138-
"""
139-
globals = sys.modules["__main__"].__dict__
140-
set_file = False
141-
# Set file (if not set) so that the imported file can use it (e.g. to
142-
# access file-relative paths). This matches what PyRun_SimpleFile does.
143-
if not hasattr(globals, "__file__"):
144-
globals["__file__"] = filepath
145-
set_file = True
146-
try:
147-
with open(filepath, "rb") as file:
148-
# We pass globals also as locals to match what Python does
149-
# in PyRun_SimpleFile.
150-
compiled = compile(file.read(), filepath, "exec")
151-
exec(compiled, globals, globals)
152-
finally:
153-
if set_file:
154-
del globals["__file__"]
155-
156-
157130
# Convenience variable to GDB's python directory
158131
PYTHONDIR = os.path.dirname(os.path.dirname(__file__))
159132

gdb/python/python.c

Lines changed: 45 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -284,12 +284,14 @@ gdbpy_check_quit_flag (const struct extension_language_defn *extlang)
284284
return PyOS_InterruptOccurred ();
285285
}
286286

287-
/* Evaluate a Python command like PyRun_SimpleString, but uses
288-
Py_single_input which prints the result of expressions, and does
289-
not automatically print the stack on errors. */
287+
/* Evaluate a Python command like PyRun_SimpleString, but takes a
288+
Python start symbol, and does not automatically print the stack on
289+
errors. FILENAME is used to set the file name in error
290+
messages. */
290291

291292
static int
292-
eval_python_command (const char *command)
293+
eval_python_command (const char *command, int start_symbol,
294+
const char *filename = "<string>")
293295
{
294296
PyObject *m, *d;
295297

@@ -300,8 +302,15 @@ eval_python_command (const char *command)
300302
d = PyModule_GetDict (m);
301303
if (d == NULL)
302304
return -1;
303-
gdbpy_ref<> v (PyRun_StringFlags (command, Py_single_input, d, d, NULL));
304-
if (v == NULL)
305+
306+
/* Use this API because it is in Python 3.2. */
307+
gdbpy_ref<> code (Py_CompileStringExFlags (command, filename, start_symbol,
308+
nullptr, -1));
309+
if (code == nullptr)
310+
return -1;
311+
312+
gdbpy_ref<> result (PyEval_EvalCode (code.get (), d, d));
313+
if (result == nullptr)
305314
return -1;
306315

307316
return 0;
@@ -324,7 +333,8 @@ python_interactive_command (const char *arg, int from_tty)
324333
if (arg && *arg)
325334
{
326335
std::string script = std::string (arg) + "\n";
327-
err = eval_python_command (script.c_str ());
336+
/* Py_single_input causes the result to be displayed. */
337+
err = eval_python_command (script.c_str (), Py_single_input);
328338
}
329339
else
330340
{
@@ -333,14 +343,12 @@ python_interactive_command (const char *arg, int from_tty)
333343
}
334344

335345
if (err)
336-
{
337-
gdbpy_print_stack ();
338-
error (_("Error while executing Python code."));
339-
}
346+
gdbpy_handle_exception ();
340347
}
341348

342-
/* A wrapper around PyRun_SimpleFile. FILE is the Python script to run
343-
named FILENAME.
349+
/* Like PyRun_SimpleFile, but if there is an exception, it is not
350+
automatically displayed. FILE is the Python script to run named
351+
FILENAME.
344352
345353
On Windows hosts few users would build Python themselves (this is no
346354
trivial task on this platform), and thus use binaries built by
@@ -349,39 +357,13 @@ python_interactive_command (const char *arg, int from_tty)
349357
library. Python, being built with VC, would use one version of the
350358
msvcr DLL (Eg. msvcr100.dll), while MinGW uses msvcrt.dll.
351359
A FILE * from one runtime does not necessarily operate correctly in
352-
the other runtime.
360+
the other runtime. */
353361

354-
To work around this potential issue, we run code in Python to load
355-
the script. */
356-
357-
static void
362+
static int
358363
python_run_simple_file (FILE *file, const char *filename)
359364
{
360-
#ifndef _WIN32
361-
362-
PyRun_SimpleFile (file, filename);
363-
364-
#else /* _WIN32 */
365-
366-
/* Because we have a string for a filename, and are using Python to
367-
open the file, we need to expand any tilde in the path first. */
368-
gdb::unique_xmalloc_ptr<char> full_path (tilde_expand (filename));
369-
370-
if (gdb_python_module == nullptr
371-
|| ! PyObject_HasAttrString (gdb_python_module, "_execute_file"))
372-
error (_("Installation error: gdb._execute_file function is missing"));
373-
374-
gdbpy_ref<> return_value
375-
(PyObject_CallMethod (gdb_python_module, "_execute_file", "s",
376-
full_path.get ()));
377-
if (return_value == nullptr)
378-
{
379-
/* Use PyErr_PrintEx instead of gdbpy_print_stack to better match the
380-
behavior of the non-Windows codepath. */
381-
PyErr_PrintEx(0);
382-
}
383-
384-
#endif /* _WIN32 */
365+
std::string contents = read_remainder_of_file (file);
366+
return eval_python_command (contents.c_str (), Py_file_input, filename);
385367
}
386368

387369
/* Given a command_line, return a command string suitable for passing
@@ -408,17 +390,15 @@ static void
408390
gdbpy_eval_from_control_command (const struct extension_language_defn *extlang,
409391
struct command_line *cmd)
410392
{
411-
int ret;
412-
413393
if (cmd->body_list_1 != nullptr)
414394
error (_("Invalid \"python\" block structure."));
415395

416396
gdbpy_enter enter_py;
417397

418398
std::string script = compute_python_string (cmd->body_list_0.get ());
419-
ret = PyRun_SimpleString (script.c_str ());
420-
if (ret)
421-
error (_("Error while executing Python code."));
399+
int ret = eval_python_command (script.c_str (), Py_file_input);
400+
if (ret != 0)
401+
gdbpy_handle_exception ();
422402
}
423403

424404
/* Implementation of the gdb "python" command. */
@@ -433,8 +413,9 @@ python_command (const char *arg, int from_tty)
433413
arg = skip_spaces (arg);
434414
if (arg && *arg)
435415
{
436-
if (PyRun_SimpleString (arg))
437-
error (_("Error while executing Python code."));
416+
int ret = eval_python_command (arg, Py_file_input);
417+
if (ret != 0)
418+
gdbpy_handle_exception ();
438419
}
439420
else
440421
{
@@ -1050,7 +1031,9 @@ gdbpy_source_script (const struct extension_language_defn *extlang,
10501031
FILE *file, const char *filename)
10511032
{
10521033
gdbpy_enter enter_py;
1053-
python_run_simple_file (file, filename);
1034+
int result = python_run_simple_file (file, filename);
1035+
if (result != 0)
1036+
gdbpy_handle_exception ();
10541037
}
10551038

10561039

@@ -1682,7 +1665,9 @@ gdbpy_source_objfile_script (const struct extension_language_defn *extlang,
16821665
scoped_restore restire_current_objfile
16831666
= make_scoped_restore (&gdbpy_current_objfile, objfile);
16841667

1685-
python_run_simple_file (file, filename);
1668+
int result = python_run_simple_file (file, filename);
1669+
if (result != 0)
1670+
gdbpy_print_stack ();
16861671
}
16871672

16881673
/* Set the current objfile to OBJFILE and then execute SCRIPT
@@ -1703,7 +1688,9 @@ gdbpy_execute_objfile_script (const struct extension_language_defn *extlang,
17031688
scoped_restore restire_current_objfile
17041689
= make_scoped_restore (&gdbpy_current_objfile, objfile);
17051690

1706-
PyRun_SimpleString (script);
1691+
int ret = eval_python_command (script, Py_file_input);
1692+
if (ret != 0)
1693+
gdbpy_print_stack ();
17071694
}
17081695

17091696
/* Return the current Objfile, or None if there isn't one. */
@@ -2361,21 +2348,15 @@ test_python ()
23612348
{
23622349
CMD (output);
23632350
}
2364-
catch (const gdb_exception &e)
2351+
catch (const gdb_exception_quit &e)
23652352
{
23662353
saw_exception = true;
2367-
SELF_CHECK (e.reason == RETURN_ERROR);
2368-
SELF_CHECK (e.error == GENERIC_ERROR);
2369-
SELF_CHECK (*e.message == "Error while executing Python code.");
2354+
SELF_CHECK (e.reason == RETURN_QUIT);
2355+
SELF_CHECK (e.error == GDB_NO_ERROR);
2356+
SELF_CHECK (*e.message == "Quit");
23702357
}
23712358
SELF_CHECK (saw_exception);
2372-
std::string ref_output_0 ("Traceback (most recent call last):\n"
2373-
" File \"<string>\", line 0, in <module>\n"
2374-
"KeyboardInterrupt\n");
2375-
std::string ref_output_1 ("Traceback (most recent call last):\n"
2376-
" File \"<string>\", line 1, in <module>\n"
2377-
"KeyboardInterrupt\n");
2378-
SELF_CHECK (output == ref_output_0 || output == ref_output_1);
2359+
SELF_CHECK (output.empty ());
23792360
}
23802361

23812362
#undef CMD

gdb/testsuite/gdb.ada/tasks.exp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,15 @@ if {[allow_python_tests]} {
9898
gdb_test "python print(bp.thread)" "None"
9999
gdb_test "python bp.thread = 1" \
100100
[multi_line \
101-
"RuntimeError: Cannot set both task and thread attributes\\." \
102-
"Error while executing Python code\\."] \
101+
"RuntimeError.*: Cannot set both task and thread attributes\\." \
102+
"Error occurred in Python.*"] \
103103
"try setting the thread, but expect an error"
104104
gdb_test_no_output "python bp.task = None"
105105
gdb_test_no_output "python bp.thread = 1"
106106
gdb_test "python bp.task = 3" \
107107
[multi_line \
108-
"RuntimeError: Cannot set both task and thread attributes\\." \
109-
"Error while executing Python code\\."] \
108+
"RuntimeError.*: Cannot set both task and thread attributes\\." \
109+
"Error occurred in Python.*"] \
110110
"try setting the task, but expect an error"
111111

112112
# Reset the breakpoint to the state required for the rest of this

gdb/testsuite/gdb.python/py-arch.exp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ foreach size {0 1 2 3 4 8 16} {
9191
}
9292

9393
gdb_test "python arch.integer_type(95)" \
94-
".*ValueError: no integer type of that size is available.*" \
94+
".*ValueError.* no integer type of that size is available.*" \
9595
"call integer_type with invalid size"
9696

9797
# Test for gdb.architecture_names(). First we're going to grab the

gdb/testsuite/gdb.python/py-block.exp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ gdb_test "python print (block.function)" "None" "first anonymous block"
4444
gdb_test "python print (block.start)" "${decimal}" "check start not None"
4545
gdb_test "python print (block.end)" "${decimal}" "check end not None"
4646
gdb_test "python print (block\['f'\].name == 'f')" "True" "check variable access"
47-
gdb_test "python print (block\['nonexistent'\])" ".*KeyError: 'nonexistent'.*" \
47+
gdb_test "python print (block\['nonexistent'\])" ".*KeyError.*: nonexistent.*" \
4848
"check nonexistent variable"
49-
gdb_test "python print (block\[42\])" ".*TypeError: Expected a string.*" \
49+
gdb_test "python print (block\[42\])" ".*TypeError.*: Expected a string.*" \
5050
"check non-string key"
5151

5252
# Test global/static blocks

gdb/testsuite/gdb.python/py-breakpoint.exp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ proc_with_prefix test_bkpt_deletion { } {
190190
gdb_py_test_silent_cmd "python dp1.delete()" \
191191
"Delete Breakpoint" 0
192192
gdb_test "python print (dp1.number)" \
193-
"RuntimeError: Breakpoint 2 is invalid.*" \
193+
"RuntimeError.*: Breakpoint 2 is invalid.*" \
194194
"Check breakpoint invalidated"
195195
gdb_py_test_silent_cmd "python del_list = gdb.breakpoints()" \
196196
"Get Breakpoint List" 0
@@ -275,7 +275,7 @@ proc_with_prefix test_bkpt_thread_and_inferior { } {
275275
gdb_test "python print(bp.thread)" "1"
276276
gdb_test "python print(bp.inferior)" "None"
277277
gdb_test "python bp.inferior = 1" \
278-
"RuntimeError: Cannot have both 'thread' and 'inferior' conditions on a breakpoint.*"
278+
"RuntimeError.*: Cannot have both 'thread' and 'inferior' conditions on a breakpoint.*"
279279
gdb_test_no_output "python bp.thread = None"
280280
gdb_test_no_output "python bp.inferior = 1" \
281281
"set the inferior now the thread has been cleared"
@@ -289,7 +289,7 @@ proc_with_prefix test_bkpt_thread_and_inferior { } {
289289
gdb_test "python print(bp.thread)" "None"
290290
gdb_test "python print(bp.inferior)" "1"
291291
gdb_test "python bp.thread = 1" \
292-
"RuntimeError: Cannot have both 'thread' and 'inferior' conditions on a breakpoint.*"
292+
"RuntimeError.*: Cannot have both 'thread' and 'inferior' conditions on a breakpoint.*"
293293
gdb_test_no_output "python bp.inferior = None"
294294
gdb_test_no_output "python bp.thread = 1" \
295295
"set the thread now the inferior has been cleared"
@@ -522,7 +522,7 @@ proc_with_prefix test_bkpt_eval_funcs { } {
522522
"end" ""
523523

524524
gdb_test "python eval_bp2.stop = stop_func" \
525-
"RuntimeError: Only one stop condition allowed. There is currently a GDB.*" \
525+
"RuntimeError.*: Only one stop condition allowed. There is currently a GDB.*" \
526526
"assign stop function to a breakpoint that has a condition"
527527

528528
delete_breakpoints
@@ -589,7 +589,7 @@ proc_with_prefix test_bkpt_temporary { } {
589589
".*$srcfile:$ibp_location.*"
590590
gdb_test "python print (ibp.count)" "1" \
591591
"Check temporary stop callback executed before deletion."
592-
gdb_test "python print (ibp.temporary)" "RuntimeError: Breakpoint 2 is invalid.*" \
592+
gdb_test "python print (ibp.temporary)" "RuntimeError.*: Breakpoint 2 is invalid.*" \
593593
"Check temporary breakpoint is deleted after being hit"
594594
gdb_test "info breakpoints" "No breakpoints, watchpoints, tracepoints, or catchpoints.*" \
595595
"Check info breakpoints shows temporary breakpoint is deleted"
@@ -707,7 +707,7 @@ proc_with_prefix test_bkpt_explicit_loc {} {
707707

708708
delete_breakpoints
709709
gdb_test "python bp1 = gdb.Breakpoint (line=bp1)" \
710-
"RuntimeError: Line keyword should be an integer or a string.*" \
710+
"RuntimeError.*: Line keyword should be an integer or a string.*" \
711711
"set explicit breakpoint by invalid line type"
712712

713713
delete_breakpoints
@@ -736,7 +736,7 @@ proc_with_prefix test_bkpt_explicit_loc {} {
736736

737737
delete_breakpoints
738738
gdb_test "python bp1 = gdb.Breakpoint (source=\"$srcfile\")" \
739-
"RuntimeError: Specifying a source must also include a line, label or function.*" \
739+
"RuntimeError.*: Specifying a source must also include a line, label or function.*" \
740740
"set invalid explicit breakpoint by source only"
741741

742742
gdb_test "python bp1 = gdb.Breakpoint (source=\"foo.c\", line=\"5\")" \
@@ -859,8 +859,8 @@ proc_with_prefix test_catchpoints {} {
859859
# the python api.
860860
gdb_test "python gdb.Breakpoint (\"syscall\", type=gdb.BP_CATCHPOINT)" \
861861
[multi_line \
862-
"gdb.error: BP_CATCHPOINT not supported" \
863-
"Error while executing Python code\\."] \
862+
"gdb.error.*: BP_CATCHPOINT not supported" \
863+
"Error occurred in Python:.*"] \
864864
"create a catchpoint via the api"
865865

866866
# Setup a catchpoint.

gdb/testsuite/gdb.python/py-connection.exp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ gdb_test "python print(conn.is_valid())" "False" "is_valid returns False"
6565
# Now check that accessing properties of the invalid connection cases
6666
# an error.
6767
gdb_test "python print(conn.num)" \
68-
"RuntimeError: Connection no longer exists\\.\r\n.*"
68+
"RuntimeError.*: Connection no longer exists\\.\r\n.*"
6969
gdb_test "python print(conn.type)" \
70-
"RuntimeError: Connection no longer exists\\.\r\n.*"
70+
"RuntimeError.*: Connection no longer exists\\.\r\n.*"
7171
gdb_test "python print(conn.description)" \
72-
"RuntimeError: Connection no longer exists\\.\r\n.*"
72+
"RuntimeError.*: Connection no longer exists\\.\r\n.*"
7373
gdb_test "python print(conn.details)" \
74-
"RuntimeError: Connection no longer exists\\.\r\n.*"
74+
"RuntimeError.*: Connection no longer exists\\.\r\n.*"

0 commit comments

Comments
 (0)