<div dir="ltr"><br><div class="gmail_extra"><br><br><div class="gmail_quote">On Wed, Aug 28, 2013 at 8:16 PM, Victor Stinner <span dir="ltr"><<a href="mailto:victor.stinner@gmail.com" target="_blank">victor.stinner@gmail.com</a>></span> wrote:<br>

<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hi,<br>
<br>
Thanks to the PEP 445, it becomes possible to trace easily memory<br>
allocations. I propose to add a new tracemalloc module computing the<br>
memory usage per file and per line number. It has also a private<br>
method to retrieve the location (filename and line number) of a memory<br>
allocation of an object.<br>
<br>
tracemalloc is different than Heapy or PySizer because it is focused<br>
on the location of a memory allocation rather that the object type or<br>
object content.<br>
<br>
I have an implementation of the module for Python 2.5-3.4, but it<br>
requires to patch and recompile Python:<br>
<a href="https://pypi.python.org/pypi/pytracemalloc" target="_blank">https://pypi.python.org/pypi/pytracemalloc</a><br>
<br>
<br>
My proposed implementation for Python 3.4 is different:<br>
<br>
* reuse the PEP 445 to hook memory allocators<br>
<br>
* use a simple C implementation of an hash table called "cfuhash"<br>
(coming from the libcfu project, BSD license) instead of depending on<br>
the glib library. I simplified and adapted cfuhash for my usage<br>
<br>
* no enable() / disable() function: tracemalloc can only be enabled<br>
before startup by setting PYTHONTRACEMALLOC=1 environment variable<br>
<br>
* traces (size of the memory block, Python filename, Python line<br>
number) are stored directly in the memory block, not in a separated<br>
hash table<br>
<br>
I chose PYTHONTRACEMALLOC env var instead of enable()/disable()<br>
functions to be able to really trace *all* memory allocated by Python,<br>
especially memory allocated at startup, during Python initialization.<br>
<br>
<br>
The (high-level) API should be reviewed and discussed. The most<br>
interesting part is to take "snapshots" and compare snapshots. The<br>
module can load snapshots from disk and compare them later for deeper<br>
analysis (ex: ignore some files).<br>
<br>
For the documentation, see the following page:<br>
<a href="https://pypi.python.org/pypi/pytracemalloc" target="_blank">https://pypi.python.org/pypi/pytracemalloc</a><br>
<br>
I created the following issue to track the implementation:<br>
<a href="http://bugs.python.org/issue18874" target="_blank">http://bugs.python.org/issue18874</a><br>
<br>
The implementation:<br>
<a href="http://hg.python.org/features/tracemalloc" target="_blank">http://hg.python.org/features/tracemalloc</a></blockquote><div><br></div><div>Without looking at the code or docs I can the concept sounds very cool!</div>

<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
<br>
* * *<br>
<br>
I also created a "pyfailmalloc" project based on the PEP 445 to inject<br>
MemoryError exceptions. I used this module to check if Python handles<br>
correctly memory allocation failures. The answer is no, I fixed many<br>
bugs (see issue #18408).<br>
<br>
Project homepage:<br>
<a href="https://bitbucket.org/haypo/pyfailmalloc" target="_blank">https://bitbucket.org/haypo/pyfailmalloc</a><br>
<br>
Charles-François Natali and Serhiy Storchaka asked me to add this<br>
module somewhere in Python 3.4: "how about adding pyfailmalloc to the<br>
main repo (maybe under Tools), with a script making it easy to run the<br>
tests suite with it enabled?"<br>
<br>
What is the best place for such module? Add it to Modules/ directory<br>
but as a private module: "_failmalloc"?<br></blockquote><div><br></div><div>Would extension module authors find it useful? If so maybe we need a malloc package with trace and fail submodules?</div><div><br></div>

<div>And if we add it we might want to add to running the tool as part of the devguide something people can work on.</div><div><br></div><div>-Brett</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">


<br>
* * *<br>
<br>
Example of tracemalloc output (it is more readable with colors, try in<br>
a terminal). The first top is sorted by total size, whereas the second<br>
top is sorted (automatically) with the size difference. You can see<br>
for example that the linecache module likes caching data (1.5 MB after<br>
10 seconds of tests).<br>
<br>
<br>
$ PYTHONTRACEMALLOC=1 ./python -m test<br>
...<br>
== CPython 3.4.0a1+ (default:2ce9e5f6b47c+, Aug 29 2013, 02:03:02)<br>
[GCC 4.7.2 20121109 (Red Hat 4.7.2-8)]<br>
==   Linux-3.9.4-200.fc18.x86_64-x86_64-with-fedora-18-Spherical_Cow<br>
little-endian<br>
==   /home/haypo/prog/python/tracemalloc/build/test_python_11087<br>
...<br>
[  1/380] test_grammar<br>
[  2/380] test_opcodes<br>
[  3/380] test_dict<br>
[  4/380] test_builtin<br>
[  5/380] test_exceptions<br>
[  6/380] test_types<br>
[  7/380] test_unittest<br>
<br>
2013-08-29 02:06:22: Top 25 allocations per file and line<br>
#1: <frozen importlib._bootstrap>:704: size=5 MiB, count=56227, average=105 B<br>
#2: .../tracemalloc/Lib/linecache.py:127: size=1004 KiB, count=8706,<br>
average=118 B<br>
#3: .../Lib/unittest/mock.py:1764: size=895 KiB, count=7841, average=116 B<br>
#4: .../Lib/unittest/mock.py:1805: size=817 KiB, count=15101, average=55 B<br>
#5: .../Lib/test/test_dict.py:35: size=768 KiB, count=8, average=96 KiB<br>
#6: <frozen importlib._bootstrap>:274: size=703 KiB, count=4604, average=156 B<br>
#7: ???:?: size=511 KiB, count=4445, average=117 B<br>
#8: .../Lib/unittest/mock.py:350: size=370 KiB, count=1227, average=308 B<br>
#9: .../Lib/unittest/case.py:306: size=343 KiB, count=1390, average=253 B<br>
#10: .../Lib/unittest/case.py:496: size=330 KiB, count=650, average=521 B<br>
#11: .../Lib/unittest/case.py:327: size=291 KiB, count=717, average=416 B<br>
#12: .../Lib/collections/__init__.py:368: size=239 KiB, count=2170,<br>
average=113 B<br>
#13: .../Lib/test/test_grammar.py:132: size=195 KiB, count=1250, average=159 B<br>
#14: .../Lib/unittest/mock.py:379: size=118 KiB, count=152, average=800 B<br>
#15: .../tracemalloc/Lib/contextlib.py:37: size=102 KiB, count=672,<br>
average=156 B<br>
#16: <frozen importlib._bootstrap>:1430: size=91 KiB, count=1193, average=78 B<br>
#17: .../tracemalloc/Lib/inspect.py:1399: size=79 KiB, count=104, average=784 B<br>
#18: .../tracemalloc/Lib/abc.py:133: size=77 KiB, count=275, average=289 B<br>
#19: .../Lib/unittest/case.py:43: size=73 KiB, count=593, average=127 B<br>
#20: .../Lib/unittest/mock.py:491: size=67 KiB, count=153, average=450 B<br>
#21: <frozen importlib._bootstrap>:1438: size=64 KiB, count=20, average=3321 B<br>
#22: .../Lib/unittest/case.py:535: size=56 KiB, count=76, average=766 B<br>
#23: .../tracemalloc/Lib/sre_compile.py:508: size=54 KiB, count=115,<br>
average=485 B<br>
#24: .../Lib/unittest/case.py:300: size=48 KiB, count=616, average=80 B<br>
#25: .../Lib/test/regrtest.py:1207: size=48 KiB, count=2, average=24 KiB<br>
7333 more: size=4991 KiB, count=28051, average=182 B<br>
Total Python memory: size=17 MiB, count=136358, average=136 B<br>
Total process memory: size=42 MiB (ignore tracemalloc: 22 KiB)<br>
<br>
[  8/380] test_doctest<br>
[  9/380] test_doctest2<br>
[ 10/380] test_support<br>
[ 11/380] test___all__<br>
<br>
2013-08-29 02:08:44: Top 25 allocations per file and line (compared to<br>
2013-08-29 02:08:39)<br>
#1: <frozen importlib._bootstrap>:704: size=8 MiB (+2853 KiB),<br>
count=80879 (+24652), average=109 B<br>
#2: .../tracemalloc/Lib/linecache.py:127: size=1562 KiB (+557 KiB),<br>
count=13669 (+4964), average=117 B<br>
#3: <frozen importlib._bootstrap>:274: size=955 KiB (+252 KiB),<br>
count=6415 (+1811), average=152 B<br>
#4: .../Lib/collections/__init__.py:368: size=333 KiB (+93 KiB),<br>
count=3136 (+966), average=108 B<br>
#5: .../tracemalloc/Lib/abc.py:133: size=148 KiB (+71 KiB), count=483<br>
(+211), average=314 B<br>
#6: .../Lib/urllib/parse.py:476: size=71 KiB (+71 KiB), count=969<br>
(+969), average=75 B<br>
#7: .../tracemalloc/Lib/base64.py:143: size=59 KiB (+59 KiB),<br>
count=1025 (+1025), average=59 B<br>
#8: .../tracemalloc/Lib/doctest.py:1283: size=56 KiB (+56 KiB),<br>
count=507 (+507), average=113 B<br>
#9: .../tracemalloc/Lib/sre_compile.py:508: size=89 KiB (+36 KiB),<br>
count=199 (+86), average=460 B<br>
#10: <frozen importlib._bootstrap>:53: size=67 KiB (+32 KiB),<br>
count=505 (+242), average=136 B<br>
#11: <frozen importlib._bootstrap>:1048: size=61 KiB (+27 KiB),<br>
count=332 (+138), average=188 B<br>
#12: .../Lib/unittest/case.py:496: size=351 KiB (+25 KiB), count=688<br>
(+48), average=522 B<br>
#13: .../tracemalloc/Lib/_weakrefset.py:38: size=55 KiB (+24 KiB),<br>
count=521 (+236), average=109 B<br>
#14: .../Lib/email/quoprimime.py:56: size=24 KiB (+24 KiB), count=190<br>
(+190), average=132 B<br>
#15: .../Lib/email/quoprimime.py:57: size=24 KiB (+24 KiB), count=1 (+1)<br>
#16: .../test/support/__init__.py:1055: size=24 KiB (+24 KiB), count=1 (+1)<br>
#17: .../Lib/test/test___all__.py:48: size=23 KiB (+23 KiB), count=283<br>
(+283), average=84 B<br>
#18: .../tracemalloc/Lib/_weakrefset.py:37: size=60 KiB (+22 KiB),<br>
count=438 (+174), average=140 B<br>
#19: .../tracemalloc/Lib/sre_parse.py:73: size=48 KiB (+20 KiB),<br>
count=263 (+114), average=189 B<br>
#20: <string>:5: size=61 KiB (+18 KiB), count=173 (+57), average=364 B<br>
#21: .../Lib/test/test___all__.py:34: size=16 KiB (+16 KiB), count=164<br>
(+164), average=104 B<br>
#22: .../Lib/unittest/mock.py:491: size=79 KiB (+16 KiB), count=145<br>
(+3), average=560 B<br>
#23: .../Lib/collections/__init__.py:362: size=50 KiB (+16 KiB),<br>
count=23 (+7), average=2255 B<br>
#24: .../Lib/test/test___all__.py:59: size=13 KiB (+13 KiB), count=165<br>
(+165), average=84 B<br>
#25: .../tracemalloc/Lib/doctest.py:1291: size=13 KiB (+13 KiB),<br>
count=170 (+170), average=81 B<br>
10788 more: size=7 MiB (-830 KiB), count=36291 (-16379), average=220 B<br>
Total Python memory: size=20 MiB (+3567 KiB), count=147635 (+20805),<br>
average=143 B<br>
Total process memory: size=49 MiB (+7 MiB) (ignore tracemalloc: 1669 KiB)<br>
<br>
Victor<br>
_______________________________________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org">Python-Dev@python.org</a><br>
<a href="http://mail.python.org/mailman/listinfo/python-dev" target="_blank">http://mail.python.org/mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="http://mail.python.org/mailman/options/python-dev/brett%40python.org" target="_blank">http://mail.python.org/mailman/options/python-dev/brett%40python.org</a><br>
</blockquote></div><br></div></div>