[1/2,gdb/python] Handle normalized exception returned by PyErr_Fetch
Checks
Context |
Check |
Description |
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 |
fail
|
Testing failed
|
linaro-tcwg-bot/tcwg_gdb_build--master-arm |
fail
|
Testing failed
|
Commit Message
With python 3.12 and test-case gdb.python/py-block.exp, before commit
a207f6b3a38 ("Rewrite "python" command exception handling"), we had:
...
(gdb) python print (block['nonexistent'])^M
Traceback (most recent call last):^M
File "<string>", line 1, in <module>^M
KeyError: 'nonexistent'^M
Error while executing Python code.^M
(gdb) PASS: gdb.python/py-block.exp: check nonexistent variable
...
but after the commit we have:
...
(gdb) python print (block['nonexistent'])^M
Python Exception <class 'KeyError'>: 'nonexistent'^M
Error occurred in Python: 'nonexistent'^M
(gdb) FAIL: gdb.python/py-block.exp: check nonexistent variable
...
In contrast, with python 3.6 we have:
...
(gdb) python print (block['nonexistent'])^M
Python Exception <class 'KeyError'>: nonexistent^M
Error occurred in Python: nonexistent^M
(gdb) PASS: gdb.python/py-block.exp: check nonexistent variable
...
The change in the test-case is:
...
-gdb_test "python print (block\['nonexistent'\])" ".*KeyError: 'nonexistent'.*" \
+gdb_test "python print (block\['nonexistent'\])" ".*KeyError.*: nonexistent.*" \
"check nonexistent variable"
...
which drops the single quotes around the nonexistent string, which matches the
output with python 3.6, but not with python 3.12.
The difference is caused by a difference in the result of PyErr_Fetch.
[ PyErr_Fetch is deprecated in python 3.12, this will be addressed in a
follow-up commit. ]
With python 3.6, we have PyErr_Fetch returning:
...
(gdb) p PyObject_Print(error_value, stderr, 0)
'nonexistent'$3 = 0
(gdb) p PyObject_Print(error_value, stderr, 1)
nonexistent$4 = 0
...
but with python 3.12, we have instead:
...
(gdb) p (int)PyObject_Print(error_value,stderr, 0)
KeyError('nonexistent')$3 = 0
(gdb) p (int)PyObject_Print(error_value,stderr, 1)
'nonexistent'$4 = 0
...
In more detail, with python 3.12 we get a normalized exception, so the
error_value is an object of class KeyError. With python 3.6, error_value is
an unnormalized exception, meaning not of class KeyError.
Apparantly we rely here on PyErr_Fetch to return an unnormalized exception.
Fix this by pretending to have an unnormalized exception in
gdbpy_err_fetch::to_string.
Tested on aarch64-linux.
PR python/31425
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31425
---
gdb/python/py-utils.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
base-commit: 1485a3fb63619cced99dd7a4a043cf01a0f423d9
Comments
>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
Tom> + /* Detected a normalized exception. */
Tom> + PyObject *args = PyException_GetArgs (m_error_value.get ());
According to:
https://docs.python.org/3/c-api/exceptions.html
... PyException_GetArgs returns a new reference; so this code leaks
memory.
I think args should be a gdbpy_ref<> here.
Tom
@@ -196,7 +196,19 @@ gdbpy_err_fetch::to_string () const
gdb.GdbError ("message"). */
if (m_error_value.get () != nullptr && m_error_value.get () != Py_None)
- return gdbpy_obj_to_string (m_error_value.get ());
+ {
+ if ((PyObject *)Py_TYPE (m_error_value.get ()) == m_error_type.get ())
+ {
+ /* Detected a normalized exception. */
+ PyObject *args = PyException_GetArgs (m_error_value.get ());
+ if (PyTuple_Size (args) == 1)
+ {
+ /* Pretend to be looking at an unnormalized exception. */
+ return gdbpy_obj_to_string (PyTuple_GetItem (args, 0));
+ }
+ }
+ return gdbpy_obj_to_string (m_error_value.get ());
+ }
else
return gdbpy_obj_to_string (m_error_type.get ());
}