This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Memory allocation fault-injection?
Type: enhancement Stage:
Components: Tests Versions: Python 3.5
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: benjamin.peterson, dmalcolm, pitrou, vstinner
Priority: normal Keywords:

Created on 2010-10-26 00:23 by dmalcolm, last changed 2022-04-11 14:57 by admin.

Files
File name Uploaded Description Edit
py3k-inject-malloc-failure.txt dmalcolm, 2010-10-26 00:23
py3k-inject-malloc-failure-002.txt dmalcolm, 2010-10-26 17:50
Messages (11)
msg119586 - (view) Author: Dave Malcolm (dmalcolm) (Python committer) Date: 2010-10-26 00:23
We were chatting on #python-dev on possible ways of testing the correct handling of "MemoryError".

Attached is one idea: adding a sys._inject_malloc_failure() hook, letting you inject a memory-allocation (or reallocation) failure some number of allocations in the future:

>>> import sys
[52733 refs]
>>> 2 + 2
4
[52733 refs]
>>> sys._inject_malloc_failure(50)
[52733 refs]
>>> 2 + 2
MemoryError
[52747 refs]
>>> 2 + 2
4
[52747 refs]

I'm not sure how to make this useful; perhaps it could instead compare with the "serialno" in Objects/obmalloc.c so that you could set up a specific numbered allocation and have it fail (or perhaps a range of serial numbers, and expose "serialno" within the "sys" module?)

Another idea might be to randomly have some proportion of allocations fail.  Perhaps the test suite could have an option where it runs each set of tests in a separate subprocess, and sets a command-line switch to inject "random" malloc failures? (storing the seed, so that they're reproducible, on one machine at least).
msg119588 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2010-10-26 03:42
We should do something like SQLite: http://sqlite.org/testing.html
msg119595 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2010-10-26 08:47
Unless we want to test manually each memory allocation in the interpreter, the only reasonable way seems to be some kind of fuzzing (perhaps using a reproducible random seed).
msg119600 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2010-10-26 11:50
Don't you know http://www.nongnu.org/failmalloc/?
msg119602 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2010-10-26 11:58
> Don't you know http://www.nongnu.org/failmalloc/?

This doesn't answer the question of what and how to test.
msg119625 - (view) Author: Dave Malcolm (dmalcolm) (Python committer) Date: 2010-10-26 17:50
Attached is a new approach to doing this, based on "Out-Of-Memory Testing" within http://sqlite.org/testing.html

This reads environment variables, and injects a fault at the given value of "serialno", and (optionally) ongoing failures afterwards.

I used this to find a specific bug in Python/pythonrun.c (fix is the first hunk of the patch): if moduleName is NULL, then Py_DECREF will read through NULL.

Potentially this gives the low-level machinery to support adding SQLite's approach to be added to regrtest.  Doing so would imply running each test many tens of thousands of times, so perhaps we could run "-c pass" to establish at what serialno the interpreter has fully started up, then use that as a starting point when testing other scripts/modules.

I implemented a toy version of this as Lib/test/test_malloc_fault.py, which sits in an infinite loop injecting individual allocation failures when starting up sys.executable as a subprocess.

For low numbers, this throws up segfaults within _Py_ReadyTypes' call to PyType_Ready(&PyType_Type), where PyExc_MemoryError is set but has not yet been initialized (its ob_type is NULL):
        /* this will probably fail since there's no memory and hee,
           hee, we have to instantiate this class
        */

Running this interactively with a large value for PYTHONMALLOCINJECTFAULTSAT leads to an interesting failure mode within PyRun_InteractiveLoopFlags(): every call to PyRun_InteractiveOneFlags immediately returns (the PyUnicode_FromString("stdin") in within PySys_GetObject() fails to allocate memory); this leads to a tight loop sending the total refcount to stderr:
[52812 refs]
[52812 refs]
[52812 refs]
(etc)
msg119636 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2010-10-26 18:49
> Doing so would imply running each test many tens of thousands of
> times, so perhaps we could run "-c pass" to establish at what serialno
> the interpreter has fully started up, then use that as a starting
> point when testing other scripts/modules.

Well, this seems to suggest it's not an optimal approach. IMHO, your
sys._inject_malloc_failure() proposal sounded better.

(I don't think running "-c pass" is an appropriate solution; it's not
obvious that the number of memory allocations at startup is totally
deterministic - it could depend on e.g. I/O conditions -, and I don't
think we want to ensure it is)

> Running this interactively with a large value for
> PYTHONMALLOCINJECTFAULTSAT

I don't think an env variable is useful for this, since you probably
don't want to set it permanently or even semi-permanently (and
"PYTHONLONGUNAMBIGUOUSVARIABLENAME"s are always painful :-)). The new -X
option support could be used instead, if a sys function isn't enough.
msg119651 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2010-10-26 21:07
> ... every call to PyRun_InteractiveOneFlags immediately returns 
>(the PyUnicode_FromString("stdin") in within PySys_GetObject() fails 
> to allocate memory); this leads to a tight loop sending the total
> refcount to stderr:

I think that it is the issue #8070.
msg221700 - (view) Author: Mark Lawrence (BreamoreBoy) * Date: 2014-06-27 17:57
Do you folks want to pick this up again as it seems a handy thing to have in our toolbox?
msg221702 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-06-27 18:23
This feature is implemented in my external project:
https://bitbucket.org/haypo/pyfailmalloc

It was discussed to integrate it in Python 3.4, but I foscused my efforts on the PEP 445 (malloc API) and 454 (tracemalloc).
msg221720 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-06-27 20:32
Related issues: #19817 "tracemalloc add a memory limit feature" and #19835 "Add a MemoryError singleton to fix an unlimited loop when the memory is exhausted".
History
Date User Action Args
2022-04-11 14:57:07adminsetgithub: 54404
2019-03-15 23:57:22BreamoreBoysetnosy: - BreamoreBoy
2014-06-27 20:32:27vstinnersetmessages: + msg221720
2014-06-27 18:23:55vstinnersetmessages: + msg221702
2014-06-27 17:57:11BreamoreBoysetnosy: + BreamoreBoy

messages: + msg221700
versions: + Python 3.5, - Python 3.2
2010-10-26 21:07:51vstinnersetmessages: + msg119651
2010-10-26 18:49:55pitrousetmessages: + msg119636
2010-10-26 17:50:51dmalcolmsetfiles: + py3k-inject-malloc-failure-002.txt

messages: + msg119625
2010-10-26 11:58:49pitrousetmessages: + msg119602
2010-10-26 11:50:14vstinnersetmessages: + msg119600
2010-10-26 08:47:45pitrousetnosy: + vstinner
messages: + msg119595
2010-10-26 03:42:49benjamin.petersonsetnosy: + benjamin.peterson
messages: + msg119588
2010-10-26 00:23:47dmalcolmcreate