classification
Title: reference kept in f_locals prevents the tracing/profiling of a destructor
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.2, Python 3.3, Python 3.4
process
Status: closed Resolution: duplicate
Dependencies: Superseder:
Assigned To: Nosy List: georg.brandl, jcea, nedbat, xdegaye
Priority: normal Keywords: patch

Created on 2012-12-09 22:01 by xdegaye, last changed 2018-05-08 18:56 by xdegaye. This issue is now closed.

Files
File name Uploaded Description Edit
issue_16653.diff xdegaye, 2012-12-09 22:07 review
Messages (4)
msg177240 - (view) Author: Xavier de Gaye (xdegaye) * (Python triager) Date: 2012-12-09 22:01
The following debugging session, run with python on the default branch, shows
that pdb does not stop in __del__ when it is invoked.

The reason is:

  - The destructor is not called when processing the 'c = 1' statement because
    foo frame.f_locals owns a reference to the C instance.
  - When the interpreter is about to invoke the trace function with the ensuing
    debug event, the call to PyFrame_LocalsToFast in call_trampoline causes the
    destructor to be invoked (as shown by gdb), and the destructor is not traced
    because at that time tstate->use_tracing is false.

This is confirmed by the fact that when 'c = 1' is replaced with
'c = 1; locals()' (on one single line so as not to trigger the trace function
before the call to locals()), then pdb stops in __del__.

===   foo.py   ================
class C:
    def __del__(self):
        print("Calling C destructor.")

def foo():
    c = C()
    import pdb; pdb.set_trace()
    c = 1

foo()
===============================
$ ./python /tmp/foo.py
> /tmp/foo.py(8)foo()
-> c = 1
(Pdb) step
Calling C destructor.
--Return--
> /tmp/foo.py(8)foo()->None
-> c = 1
(Pdb)
===============================
msg177244 - (view) Author: Xavier de Gaye (xdegaye) * (Python triager) Date: 2012-12-09 22:07
Tracing/profiling is disabled when tstate->tracing is true or
tstate->use_tracing is false. The proposed patch fixes the problem by reducing
the scope where this condition is true.

As a consequence call_trace, profile_trampoline, trace_trampoline and
call_trampoline may now be called recursively.

The patch includes a test case.
msg178700 - (view) Author: Xavier de Gaye (xdegaye) * (Python triager) Date: 2012-12-31 16:20
This patch breaks extension modules (for example Ned Batchelder's
coverage.py) that use PyEval_SetTrace to set the trace function.
msg316294 - (view) Author: Xavier de Gaye (xdegaye) * (Python triager) Date: 2018-05-08 18:56
Closing as a duplicate of issue 33446 which has a wider scope and a PR.
History
Date User Action Args
2018-05-08 18:56:24xdegayesetstatus: open -> closed
resolution: duplicate
messages: + msg316294

stage: resolved
2012-12-31 16:26:56nedbatsetnosy: + nedbat
2012-12-31 16:20:32xdegayesetmessages: + msg178700
2012-12-10 11:23:14jceasetnosy: + jcea
2012-12-09 22:38:31pitrousetnosy: + georg.brandl

versions: + Python 3.2, Python 3.3
2012-12-09 22:07:45xdegayesetfiles: + issue_16653.diff
keywords: + patch
messages: + msg177244
2012-12-09 22:01:58xdegayecreate