-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Duplicate frame in traceback of exception raised inside trace function #102818
Comments
cc @markshannon |
This actually will reproduce before the commit given. The reason the example code was working was because the trace function happens to raise an exception on "call" event, which happened in def trace(frame, event, arg):
if event == "line":
raise ValueError()
return trace It will reproduce this issue even on e028ae9. The fundamental issue here is - who is responsible to set the traceback when the trace function raises an exception. Currently, In this specific case, The solution is not trivial here - multiple functions are relying on I'm not familiar with the structure enough to make the call, but I guess this would be a good question to @markshannon - can we do that? There is definitely performance hit(on a very rare case), but is that even valid? To go through I can help implementing this if needed, just need to confirm the solution. |
@gaogaotiantian Yes, I overlooked the case on 3e43fac when trace function raises during My first rough attempt to fix this issue was to make This draft patch works as expected and passes all tests. It's not a final fix but something that can prove the concept. diff --git a/Python/ceval.c b/Python/ceval.c
index 7d60cf987e..0b70c7dc8b 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -835,6 +835,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
}
DISPATCH();
+ bool tracing_error = false;
+
{
/* Start instructions */
#if !USE_COMPUTED_GOTOS
@@ -896,6 +898,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
// instruction. Increment it before handling the error,
// so that it looks the same as a "normal" instruction:
next_instr++;
+ tracing_error = true;
goto error;
}
// Reload next_instr. Don't increment it, though, since
@@ -982,13 +985,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
/* Log traceback info. */
assert(frame != &entry_frame);
- if (!_PyFrame_IsIncomplete(frame)) {
+ if (!_PyFrame_IsIncomplete(frame) && !tracing_error) {
PyFrameObject *f = _PyFrame_GetFrameObject(frame);
if (f != NULL) {
PyTraceBack_Here(f);
}
}
+ tracing_error = false;
+
if (tstate->c_tracefunc != NULL) {
/* Make sure state is set to FRAME_UNWINDING for tracing */
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
@@ -996,6 +1001,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
}
exception_unwind:
+
{
/* We can't use frame->f_lasti here, as RERAISE may have set it */
int offset = INSTR_OFFSET()-1;
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index c2257515a3..107a5724e0 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -312,6 +312,7 @@ GETITEM(PyObject *v, Py_ssize_t i) {
stack_pointer = _PyFrame_GetStackPointer(frame); \
frame->stacktop = -1; \
if (err) { \
+ tracing_error = true; \
goto error; \
} \
}
|
Well, even though the fix is easy to understand, I'm not sure if it's elegant enough. Introducing a state variable makes the code less robust and harder to maintain in the future. But I agree that it proves the logic of the problem. I guess we still need to wait for @markshannon for the actual path to fix this. |
I think we can get rid of variable by placing the code that sets traceback just after diff --git a/Python/ceval.c b/Python/ceval.c
index 7d60cf987e..9d4f25a58b 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -896,7 +896,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
// instruction. Increment it before handling the error,
// so that it looks the same as a "normal" instruction:
next_instr++;
- goto error;
+ goto trace_error;
}
// Reload next_instr. Don't increment it, though, since
// we're going to re-dispatch to the "true" instruction now:
@@ -969,6 +969,16 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
pop_1_error:
STACK_SHRINK(1);
error:
+ /* Log traceback info. */
+ assert(frame != &entry_frame);
+ if (!_PyFrame_IsIncomplete(frame)) {
+ PyFrameObject *f = _PyFrame_GetFrameObject(frame);
+ if (f != NULL) {
+ PyTraceBack_Here(f);
+ }
+ }
+
+trace_error:
kwnames = NULL;
/* Double-check exception status. */
#ifdef NDEBUG
@@ -980,15 +990,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
assert(_PyErr_Occurred(tstate));
#endif
- /* Log traceback info. */
- assert(frame != &entry_frame);
- if (!_PyFrame_IsIncomplete(frame)) {
- PyFrameObject *f = _PyFrame_GetFrameObject(frame);
- if (f != NULL) {
- PyTraceBack_Here(f);
- }
- }
-
if (tstate->c_tracefunc != NULL) {
/* Make sure state is set to FRAME_UNWINDING for tracing */
call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h
index c2257515a3..c8a077a9a6 100644
--- a/Python/ceval_macros.h
+++ b/Python/ceval_macros.h
@@ -312,7 +312,7 @@ GETITEM(PyObject *v, Py_ssize_t i) {
stack_pointer = _PyFrame_GetStackPointer(frame); \
frame->stacktop = -1; \
if (err) { \
- goto error; \
+ goto trace_error; \
} \
} |
It is a bit strange that |
The documentation for So, |
* main: (30 commits) pythongh-103987: fix several crashes in mmap module (python#103990) docs: fix wrong indentation causing rendering error in dis page (python#104661) pythongh-94906: Support multiple steps in math.nextafter (python#103881) pythongh-104472: Skip `test_subprocess.ProcessTestCase.test_empty_env` if ASAN is enabled (python#104667) pythongh-103839: Allow building Tkinter against Tcl 8.7 without external libtommath (pythonGH-103842) pythongh-85984: New additions and improvements to the tty library. (python#101832) pythongh-104659: Consolidate python examples in enum documentation (python#104665) pythongh-92248: Deprecate `type`, `choices`, `metavar` parameters of `argparse.BooleanOptionalAction` (python#103678) pythongh-104645: fix error handling in marshal tests (python#104646) pythongh-104600: Make type.__type_params__ writable (python#104634) pythongh-104602: Add additional test for listcomp with lambda (python#104639) pythongh-104640: Disallow walrus in comprehension within type scopes (python#104641) pythongh-103921: Rename "type" header in argparse docs (python#104654) Improve readability of `typing._ProtocolMeta.__instancecheck__` (python#104649) pythongh-96522: Fix deadlock in pty.spawn (python#96639) pythonGH-102818: Do not call `PyTraceBack_Here` in sys.settrace trampoline. (pythonGH-104579) pythonGH-103545: Add macOS specific constants for ``os.setpriority`` to ``os`` (python#104606) pythongh-104623: Update macOS installer to SQLite 3.42.0 (pythonGH-104624) pythongh-104619: never leak comprehension locals to outer locals() (python#104637) pythongh-104602: ensure all cellvars are known up front (python#104603) ...
I'm closing this one as it is fixed and the fix is backported. |
First appeared in e028ae9.
Reproducer:
Before 'bad' commit (3e43fac):
After 'bad' commit (e028ae9):
3.11.0 release and main (039714d) also lack pointers to error locations, but this probably needs a different issue:
Linked PRs
PyTraceBack_Here
in sys.settrace trampoline. #104579The text was updated successfully, but these errors were encountered: