Issue 23012: RuntimeError: settrace/setprofile function gets lost (original) (raw)

It's not possible to write a settrace() or setprofile() function that remains active if we're about to run out of stack. If we are, we get a RuntimeError when the function is called. The RuntimeError is normally propagated, but in case it is eaten (e.g. see example) then the program continues to run normally --- but the trace/profile function is disabled from now on.

I try to track down this.

sys_settrace calls PyEval_SetTrace with trace_trampoline and the function given to it. The trace_trampoline is important because it checks the result and if result is NULL (for example like f() recursion in your code) it sets c_tracefunc and c_traceobj to NULL. It is why it doesnt work.

if (result == NULL) { PyEval_SetTrace(NULL, NULL); Py_CLEAR(frame->f_trace); return -1; }

We can create a simple reset function for resetting everything and then setting c_tracefunc, c_traceobj variables back to the thread state. https://github.com/isidentical/cpython/commit/3bafbf3a89e09cc573ddbcd13f9334e164f7dd8b

But then a set of tests will fail with raw ValueError produced by tracer.

class RaisingTraceFuncTestCase(unittest.TestCase): ...

def trace(self, frame, event, arg):
    """A trace function that raises an exception in response to a
    specific trace event."""
    if event == self.raiseOnEvent:
        raise ValueError # just something that isn't RuntimeError
    else:
        return self.trace

This should be catched in

def run_test_for_event(self, event):
    try:
        for i in range(sys.getrecursionlimit() + 1):
            sys.settrace(self.trace)
            try:
                self.f()
            except ValueError:
                pass
            else:
                self.fail("exception not raised!")
    except RuntimeError:
        self.fail("recursion counter not reset")

but after resetting, it doesn't work. I'm missing something but i dont know what i'm missing.