You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: reference leaks and incorrect C-API usage in _stacktrace.c [backport 2.0] (#7148)
Backport ac10377 from #7112 to 2.0.
I was just reading this file and found many problems and leaks so I
thought that a PR may be welcomed 😉
There are several problems and possible improvements with
"_stacktrace.c":
* The macros for 3.11+ have some nested calls such as:
#define GET_FILENAME(frame)
PyObject_GetAttrString(PyFrame_GetCode(frame), "co_filename")
The problem with these nested calls is that the nested functions
return strong references that are never cleaned and also the call can
fail, which will crash the interpreter or raise SystemError and doesn't
allow the code to properly do error management.
* The macros for 3.11+ that return PyObject* are strong references that
must be properly managed or otherwise the code will leak.
* There are many calls that are not checked for errors such as
PyBytes_AsString, PyFrame_GetBack, PyThreadState_GetFrame, ...
* When PyThreadState_Get or PyThreadState_GetFrame return NULL, that's
considered a hard error (for instance this can happen at interpreter
teardown) and returning None is incorrect. The error must be
propagated.
* There invalid paths are innecesarily obscured and the only situation
where None should be returned (although I would advise to return an
error instead) is if the argument passed is not a unicode/string object.
* The function just receives one argument, so it should use METH_O
instead of adding the extra complication of using METH_VARARGS.
The leaks can be confirmed using `memray` or a debug version of python.
Just use this example `setup.py` to compile the extension:
```
from setuptools import setup, Extension
extension_module = Extension(
'_stacktrace',
sources=['_stacktrace.c'],
)
setup(
name='_stacktrace',
version='1.0',
ext_modules=[extension_module],
)
```
To check for leaks one possibility is using a debug version of the
interpreter and print the total reference count bere and after:
```
import _stacktrace as s
import sys, gc
for _ in range(100):
gc.collect()
a = sys.gettotalrefcount()
s.get_info_frame(b"")
gc.collect()
print(sys.gettotalrefcount() -a)
```
This prints:
```
7
7
7
7
```
because there are 7 references being leaked (at least).
The other is using `memray` or another memory profieler. Use this drive
script:
```
# Test script
import _stacktrace as s
import gc
for _ in range(1_000_000):
s.get_info_frame(b"")
gc.collect()
```
Then use memray:
```
$ memray run --trace-python-allocators --native -o lel.bin -f test.py
$ python -m memray flamegraph lel.bin --leaks -f
```

Note that frame objects do not appear in `memray` because we only leak
~7 frames and they use a freelist:
https://github.com/python/cpython/blob/97ce15c5f8743fb8b8967c6a05d6aec9fef9cbc7/Objects/frameobject.c#L810
## Checklist
- [x] Change(s) are motivated and described in the PR description.
- [x] Testing strategy is described if automated tests are not included
in the PR.
- [x] Risk is outlined (performance impact, potential for breakage,
maintainability, etc).
- [x] Change is maintainable (easy to change, telemetry, documentation).
- [x] [Library release note
guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html)
are followed. If no release note is required, add label
`changelog/no-changelog`.
- [x] Documentation is included (in-code, generated user docs, [public
corp docs](https://github.com/DataDog/documentation/)).
- [x] Backport labels are set (if
[applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting))
## Reviewer Checklist
- [x] Title is accurate.
- [x] No unnecessary changes are introduced.
- [x] Description motivates each change.
- [x] Avoids breaking
[API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces)
changes unless absolutely necessary.
- [x] Testing strategy adequately addresses listed risk(s).
- [x] Change is maintainable (easy to change, telemetry, documentation).
- [x] Release note makes sense to a user of the library.
- [x] Reviewer has explicitly acknowledged and discussed the performance
implications of this PR as reported in the benchmarks PR comment.
- [x] Backport labels are set in a manner that is consistent with the
[release branch maintenance
policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
- [x] If this PR touches code that signs or publishes builds or
packages, or handles credentials of any kind, I've requested a review
from `@DataDog/security-design-and-guidance`.
- [x] This PR doesn't touch any of that.
Co-authored-by: Pablo Galindo Salgado <[email protected]>
Co-authored-by: Emmett Butler <[email protected]>
0 commit comments