blob: a0037f81b88f277e21541a943793969bcf570ffe [file] [log] [blame]
Victor Stinnered3b0bc2013-11-23 12:27:24 +01001import contextlib
Victor Stinnered3b0bc2013-11-23 12:27:24 +01002import os
3import sys
4import tracemalloc
5import unittest
6from unittest.mock import patch
Berker Peksagce643912015-05-06 06:33:17 +03007from test.support.script_helper import (assert_python_ok, assert_python_failure,
8 interpreter_requires_environment)
9from test import support
Hai Shia7f5d932020-08-04 00:41:24 +080010from test.support import os_helper
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020011
Victor Stinner10b73e12016-03-22 13:39:05 +010012try:
13 import _testcapi
14except ImportError:
15 _testcapi = None
16
Victor Stinnered3b0bc2013-11-23 12:27:24 +010017
18EMPTY_STRING_SIZE = sys.getsizeof(b'')
Victor Stinner60b04c92018-07-25 19:23:53 +020019INVALID_NFRAME = (-1, 2**30)
Victor Stinnered3b0bc2013-11-23 12:27:24 +010020
Victor Stinner10b73e12016-03-22 13:39:05 +010021
Victor Stinnered3b0bc2013-11-23 12:27:24 +010022def get_frames(nframe, lineno_delta):
23 frames = []
24 frame = sys._getframe(1)
25 for index in range(nframe):
26 code = frame.f_code
27 lineno = frame.f_lineno + lineno_delta
28 frames.append((code.co_filename, lineno))
29 lineno_delta = 0
30 frame = frame.f_back
31 if frame is None:
32 break
33 return tuple(frames)
34
35def allocate_bytes(size):
36 nframe = tracemalloc.get_traceback_limit()
37 bytes_len = (size - EMPTY_STRING_SIZE)
38 frames = get_frames(nframe, 1)
39 data = b'x' * bytes_len
Julien Danjou8d59eb12019-10-15 14:00:16 +020040 return data, tracemalloc.Traceback(frames, min(len(frames), nframe))
Victor Stinnered3b0bc2013-11-23 12:27:24 +010041
42def create_snapshots():
43 traceback_limit = 2
44
Victor Stinnere492ae52016-03-22 12:58:23 +010045 # _tracemalloc._get_traces() returns a list of (domain, size,
46 # traceback_frames) tuples. traceback_frames is a tuple of (filename,
47 # line_number) tuples.
Victor Stinnered3b0bc2013-11-23 12:27:24 +010048 raw_traces = [
Julien Danjou8d59eb12019-10-15 14:00:16 +020049 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
50 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
51 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010052
Julien Danjou8d59eb12019-10-15 14:00:16 +020053 (1, 2, (('a.py', 5), ('b.py', 4)), 3),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010054
Julien Danjou8d59eb12019-10-15 14:00:16 +020055 (2, 66, (('b.py', 1),), 1),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010056
Julien Danjou8d59eb12019-10-15 14:00:16 +020057 (3, 7, (('<unknown>', 0),), 1),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010058 ]
59 snapshot = tracemalloc.Snapshot(raw_traces, traceback_limit)
60
61 raw_traces2 = [
Julien Danjou8d59eb12019-10-15 14:00:16 +020062 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
63 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
64 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010065
Julien Danjou8d59eb12019-10-15 14:00:16 +020066 (2, 2, (('a.py', 5), ('b.py', 4)), 3),
67 (2, 5000, (('a.py', 5), ('b.py', 4)), 3),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010068
Julien Danjou8d59eb12019-10-15 14:00:16 +020069 (4, 400, (('c.py', 578),), 1),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010070 ]
71 snapshot2 = tracemalloc.Snapshot(raw_traces2, traceback_limit)
72
73 return (snapshot, snapshot2)
74
75def frame(filename, lineno):
76 return tracemalloc._Frame((filename, lineno))
77
78def traceback(*frames):
79 return tracemalloc.Traceback(frames)
80
81def traceback_lineno(filename, lineno):
82 return traceback((filename, lineno))
83
84def traceback_filename(filename):
85 return traceback_lineno(filename, 0)
86
87
88class TestTracemallocEnabled(unittest.TestCase):
89 def setUp(self):
90 if tracemalloc.is_tracing():
91 self.skipTest("tracemalloc must be stopped before the test")
92
Victor Stinner3728d6c2013-11-23 12:37:20 +010093 tracemalloc.start(1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +010094
95 def tearDown(self):
96 tracemalloc.stop()
97
98 def test_get_tracemalloc_memory(self):
99 data = [allocate_bytes(123) for count in range(1000)]
100 size = tracemalloc.get_tracemalloc_memory()
101 self.assertGreaterEqual(size, 0)
102
103 tracemalloc.clear_traces()
104 size2 = tracemalloc.get_tracemalloc_memory()
105 self.assertGreaterEqual(size2, 0)
106 self.assertLessEqual(size2, size)
107
108 def test_get_object_traceback(self):
109 tracemalloc.clear_traces()
110 obj_size = 12345
111 obj, obj_traceback = allocate_bytes(obj_size)
112 traceback = tracemalloc.get_object_traceback(obj)
113 self.assertEqual(traceback, obj_traceback)
114
Victor Stinner9e00e802018-10-25 13:31:16 +0200115 def test_new_reference(self):
116 tracemalloc.clear_traces()
117 # gc.collect() indirectly calls PyList_ClearFreeList()
118 support.gc_collect()
119
120 # Create a list and "destroy it": put it in the PyListObject free list
121 obj = []
122 obj = None
123
124 # Create a list which should reuse the previously created empty list
125 obj = []
126
127 nframe = tracemalloc.get_traceback_limit()
128 frames = get_frames(nframe, -3)
Julien Danjou8d59eb12019-10-15 14:00:16 +0200129 obj_traceback = tracemalloc.Traceback(frames, min(len(frames), nframe))
Victor Stinner9e00e802018-10-25 13:31:16 +0200130
131 traceback = tracemalloc.get_object_traceback(obj)
132 self.assertIsNotNone(traceback)
133 self.assertEqual(traceback, obj_traceback)
134
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100135 def test_set_traceback_limit(self):
136 obj_size = 10
137
Victor Stinner3728d6c2013-11-23 12:37:20 +0100138 tracemalloc.stop()
139 self.assertRaises(ValueError, tracemalloc.start, -1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100140
Victor Stinner3728d6c2013-11-23 12:37:20 +0100141 tracemalloc.stop()
142 tracemalloc.start(10)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100143 obj2, obj2_traceback = allocate_bytes(obj_size)
144 traceback = tracemalloc.get_object_traceback(obj2)
145 self.assertEqual(len(traceback), 10)
146 self.assertEqual(traceback, obj2_traceback)
147
Victor Stinner3728d6c2013-11-23 12:37:20 +0100148 tracemalloc.stop()
149 tracemalloc.start(1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100150 obj, obj_traceback = allocate_bytes(obj_size)
151 traceback = tracemalloc.get_object_traceback(obj)
152 self.assertEqual(len(traceback), 1)
153 self.assertEqual(traceback, obj_traceback)
154
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100155 def find_trace(self, traces, traceback):
156 for trace in traces:
Victor Stinnere492ae52016-03-22 12:58:23 +0100157 if trace[2] == traceback._frames:
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100158 return trace
159
160 self.fail("trace not found")
161
162 def test_get_traces(self):
163 tracemalloc.clear_traces()
164 obj_size = 12345
165 obj, obj_traceback = allocate_bytes(obj_size)
166
167 traces = tracemalloc._get_traces()
168 trace = self.find_trace(traces, obj_traceback)
169
170 self.assertIsInstance(trace, tuple)
Julien Danjou8d59eb12019-10-15 14:00:16 +0200171 domain, size, traceback, length = trace
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100172 self.assertEqual(size, obj_size)
173 self.assertEqual(traceback, obj_traceback._frames)
174
175 tracemalloc.stop()
176 self.assertEqual(tracemalloc._get_traces(), [])
177
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100178 def test_get_traces_intern_traceback(self):
179 # dummy wrappers to get more useful and identical frames in the traceback
180 def allocate_bytes2(size):
181 return allocate_bytes(size)
182 def allocate_bytes3(size):
183 return allocate_bytes2(size)
184 def allocate_bytes4(size):
185 return allocate_bytes3(size)
186
187 # Ensure that two identical tracebacks are not duplicated
Victor Stinner3728d6c2013-11-23 12:37:20 +0100188 tracemalloc.stop()
189 tracemalloc.start(4)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100190 obj_size = 123
191 obj1, obj1_traceback = allocate_bytes4(obj_size)
192 obj2, obj2_traceback = allocate_bytes4(obj_size)
193
194 traces = tracemalloc._get_traces()
195
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100196 obj1_traceback._frames = tuple(reversed(obj1_traceback._frames))
197 obj2_traceback._frames = tuple(reversed(obj2_traceback._frames))
198
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100199 trace1 = self.find_trace(traces, obj1_traceback)
200 trace2 = self.find_trace(traces, obj2_traceback)
Julien Danjou8d59eb12019-10-15 14:00:16 +0200201 domain1, size1, traceback1, length1 = trace1
202 domain2, size2, traceback2, length2 = trace2
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100203 self.assertIs(traceback2, traceback1)
204
205 def test_get_traced_memory(self):
206 # Python allocates some internals objects, so the test must tolerate
207 # a small difference between the expected size and the real usage
208 max_error = 2048
209
210 # allocate one object
211 obj_size = 1024 * 1024
212 tracemalloc.clear_traces()
213 obj, obj_traceback = allocate_bytes(obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100214 size, peak_size = tracemalloc.get_traced_memory()
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100215 self.assertGreaterEqual(size, obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100216 self.assertGreaterEqual(peak_size, size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100217
218 self.assertLessEqual(size - obj_size, max_error)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100219 self.assertLessEqual(peak_size - size, max_error)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100220
221 # destroy the object
222 obj = None
Victor Stinner3c0481d2013-11-27 21:39:49 +0100223 size2, peak_size2 = tracemalloc.get_traced_memory()
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100224 self.assertLess(size2, size)
225 self.assertGreaterEqual(size - size2, obj_size - max_error)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100226 self.assertGreaterEqual(peak_size2, peak_size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100227
228 # clear_traces() must reset traced memory counters
229 tracemalloc.clear_traces()
230 self.assertEqual(tracemalloc.get_traced_memory(), (0, 0))
231
232 # allocate another object
233 obj, obj_traceback = allocate_bytes(obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100234 size, peak_size = tracemalloc.get_traced_memory()
235 self.assertGreaterEqual(size, obj_size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100236
Victor Stinnera89ecc72013-11-25 09:29:45 +0100237 # stop() also resets traced memory counters
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100238 tracemalloc.stop()
239 self.assertEqual(tracemalloc.get_traced_memory(), (0, 0))
240
241 def test_clear_traces(self):
242 obj, obj_traceback = allocate_bytes(123)
243 traceback = tracemalloc.get_object_traceback(obj)
244 self.assertIsNotNone(traceback)
245
246 tracemalloc.clear_traces()
247 traceback2 = tracemalloc.get_object_traceback(obj)
248 self.assertIsNone(traceback2)
249
Huon Wilson8b626442020-05-23 00:18:51 +1000250 def test_reset_peak(self):
251 # Python allocates some internals objects, so the test must tolerate
252 # a small difference between the expected size and the real usage
253 tracemalloc.clear_traces()
254
255 # Example: allocate a large piece of memory, temporarily
256 large_sum = sum(list(range(100000)))
257 size1, peak1 = tracemalloc.get_traced_memory()
258
259 # reset_peak() resets peak to traced memory: peak2 < peak1
260 tracemalloc.reset_peak()
261 size2, peak2 = tracemalloc.get_traced_memory()
262 self.assertGreaterEqual(peak2, size2)
263 self.assertLess(peak2, peak1)
264
265 # check that peak continue to be updated if new memory is allocated:
266 # peak3 > peak2
267 obj_size = 1024 * 1024
268 obj, obj_traceback = allocate_bytes(obj_size)
269 size3, peak3 = tracemalloc.get_traced_memory()
270 self.assertGreaterEqual(peak3, size3)
271 self.assertGreater(peak3, peak2)
272 self.assertGreaterEqual(peak3 - peak2, obj_size)
273
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100274 def test_is_tracing(self):
275 tracemalloc.stop()
276 self.assertFalse(tracemalloc.is_tracing())
277
278 tracemalloc.start()
279 self.assertTrue(tracemalloc.is_tracing())
280
281 def test_snapshot(self):
282 obj, source = allocate_bytes(123)
283
284 # take a snapshot
285 snapshot = tracemalloc.take_snapshot()
286
Julien Danjou8d59eb12019-10-15 14:00:16 +0200287 # This can vary
288 self.assertGreater(snapshot.traces[1].traceback.total_nframe, 10)
289
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100290 # write on disk
Hai Shia7f5d932020-08-04 00:41:24 +0800291 snapshot.dump(os_helper.TESTFN)
292 self.addCleanup(os_helper.unlink, os_helper.TESTFN)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100293
294 # load from disk
Hai Shia7f5d932020-08-04 00:41:24 +0800295 snapshot2 = tracemalloc.Snapshot.load(os_helper.TESTFN)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100296 self.assertEqual(snapshot2.traces, snapshot.traces)
297
298 # tracemalloc must be tracing memory allocations to take a snapshot
299 tracemalloc.stop()
300 with self.assertRaises(RuntimeError) as cm:
301 tracemalloc.take_snapshot()
302 self.assertEqual(str(cm.exception),
303 "the tracemalloc module must be tracing memory "
304 "allocations to take a snapshot")
305
306 def test_snapshot_save_attr(self):
307 # take a snapshot with a new attribute
308 snapshot = tracemalloc.take_snapshot()
309 snapshot.test_attr = "new"
Hai Shia7f5d932020-08-04 00:41:24 +0800310 snapshot.dump(os_helper.TESTFN)
311 self.addCleanup(os_helper.unlink, os_helper.TESTFN)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100312
Martin Panter8f265652016-04-19 04:03:41 +0000313 # load() should recreate the attribute
Hai Shia7f5d932020-08-04 00:41:24 +0800314 snapshot2 = tracemalloc.Snapshot.load(os_helper.TESTFN)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100315 self.assertEqual(snapshot2.test_attr, "new")
316
317 def fork_child(self):
318 if not tracemalloc.is_tracing():
319 return 2
320
321 obj_size = 12345
322 obj, obj_traceback = allocate_bytes(obj_size)
323 traceback = tracemalloc.get_object_traceback(obj)
324 if traceback is None:
325 return 3
326
327 # everything is fine
328 return 0
329
330 @unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork()')
331 def test_fork(self):
332 # check that tracemalloc is still working after fork
333 pid = os.fork()
334 if not pid:
335 # child
336 exitcode = 1
337 try:
338 exitcode = self.fork_child()
339 finally:
340 os._exit(exitcode)
341 else:
Victor Stinner278c1e12020-03-31 20:08:12 +0200342 support.wait_process(pid, exitcode=0)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100343
344
345class TestSnapshot(unittest.TestCase):
346 maxDiff = 4000
347
348 def test_create_snapshot(self):
Julien Danjou8d59eb12019-10-15 14:00:16 +0200349 raw_traces = [(0, 5, (('a.py', 2),), 10)]
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100350
351 with contextlib.ExitStack() as stack:
352 stack.enter_context(patch.object(tracemalloc, 'is_tracing',
353 return_value=True))
354 stack.enter_context(patch.object(tracemalloc, 'get_traceback_limit',
355 return_value=5))
356 stack.enter_context(patch.object(tracemalloc, '_get_traces',
357 return_value=raw_traces))
358
359 snapshot = tracemalloc.take_snapshot()
360 self.assertEqual(snapshot.traceback_limit, 5)
361 self.assertEqual(len(snapshot.traces), 1)
362 trace = snapshot.traces[0]
363 self.assertEqual(trace.size, 5)
Julien Danjou8d59eb12019-10-15 14:00:16 +0200364 self.assertEqual(trace.traceback.total_nframe, 10)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100365 self.assertEqual(len(trace.traceback), 1)
366 self.assertEqual(trace.traceback[0].filename, 'a.py')
367 self.assertEqual(trace.traceback[0].lineno, 2)
368
369 def test_filter_traces(self):
370 snapshot, snapshot2 = create_snapshots()
371 filter1 = tracemalloc.Filter(False, "b.py")
372 filter2 = tracemalloc.Filter(True, "a.py", 2)
373 filter3 = tracemalloc.Filter(True, "a.py", 5)
374
375 original_traces = list(snapshot.traces._traces)
376
377 # exclude b.py
378 snapshot3 = snapshot.filter_traces((filter1,))
379 self.assertEqual(snapshot3.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200380 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
381 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
382 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
383 (1, 2, (('a.py', 5), ('b.py', 4)), 3),
384 (3, 7, (('<unknown>', 0),), 1),
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100385 ])
386
387 # filter_traces() must not touch the original snapshot
388 self.assertEqual(snapshot.traces._traces, original_traces)
389
390 # only include two lines of a.py
391 snapshot4 = snapshot3.filter_traces((filter2, filter3))
392 self.assertEqual(snapshot4.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200393 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
394 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
395 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
396 (1, 2, (('a.py', 5), ('b.py', 4)), 3),
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100397 ])
398
399 # No filter: just duplicate the snapshot
400 snapshot5 = snapshot.filter_traces(())
401 self.assertIsNot(snapshot5, snapshot)
402 self.assertIsNot(snapshot5.traces, snapshot.traces)
403 self.assertEqual(snapshot5.traces, snapshot.traces)
404
Victor Stinner8ce8ff92014-03-10 11:05:07 +0100405 self.assertRaises(TypeError, snapshot.filter_traces, filter1)
406
Victor Stinnere492ae52016-03-22 12:58:23 +0100407 def test_filter_traces_domain(self):
408 snapshot, snapshot2 = create_snapshots()
409 filter1 = tracemalloc.Filter(False, "a.py", domain=1)
410 filter2 = tracemalloc.Filter(True, "a.py", domain=1)
411
412 original_traces = list(snapshot.traces._traces)
413
414 # exclude a.py of domain 1
415 snapshot3 = snapshot.filter_traces((filter1,))
416 self.assertEqual(snapshot3.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200417 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
418 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
419 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
420 (2, 66, (('b.py', 1),), 1),
421 (3, 7, (('<unknown>', 0),), 1),
Victor Stinnere492ae52016-03-22 12:58:23 +0100422 ])
423
424 # include domain 1
425 snapshot3 = snapshot.filter_traces((filter1,))
426 self.assertEqual(snapshot3.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200427 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
428 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
429 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
430 (2, 66, (('b.py', 1),), 1),
431 (3, 7, (('<unknown>', 0),), 1),
Victor Stinnere492ae52016-03-22 12:58:23 +0100432 ])
433
434 def test_filter_traces_domain_filter(self):
435 snapshot, snapshot2 = create_snapshots()
436 filter1 = tracemalloc.DomainFilter(False, domain=3)
437 filter2 = tracemalloc.DomainFilter(True, domain=3)
438
439 # exclude domain 2
440 snapshot3 = snapshot.filter_traces((filter1,))
441 self.assertEqual(snapshot3.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200442 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
443 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
444 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
445 (1, 2, (('a.py', 5), ('b.py', 4)), 3),
446 (2, 66, (('b.py', 1),), 1),
Victor Stinnere492ae52016-03-22 12:58:23 +0100447 ])
448
449 # include domain 2
450 snapshot3 = snapshot.filter_traces((filter2,))
451 self.assertEqual(snapshot3.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200452 (3, 7, (('<unknown>', 0),), 1),
Victor Stinnere492ae52016-03-22 12:58:23 +0100453 ])
454
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100455 def test_snapshot_group_by_line(self):
456 snapshot, snapshot2 = create_snapshots()
457 tb_0 = traceback_lineno('<unknown>', 0)
458 tb_a_2 = traceback_lineno('a.py', 2)
459 tb_a_5 = traceback_lineno('a.py', 5)
460 tb_b_1 = traceback_lineno('b.py', 1)
461 tb_c_578 = traceback_lineno('c.py', 578)
462
463 # stats per file and line
464 stats1 = snapshot.statistics('lineno')
465 self.assertEqual(stats1, [
466 tracemalloc.Statistic(tb_b_1, 66, 1),
467 tracemalloc.Statistic(tb_a_2, 30, 3),
468 tracemalloc.Statistic(tb_0, 7, 1),
469 tracemalloc.Statistic(tb_a_5, 2, 1),
470 ])
471
472 # stats per file and line (2)
473 stats2 = snapshot2.statistics('lineno')
474 self.assertEqual(stats2, [
475 tracemalloc.Statistic(tb_a_5, 5002, 2),
476 tracemalloc.Statistic(tb_c_578, 400, 1),
477 tracemalloc.Statistic(tb_a_2, 30, 3),
478 ])
479
480 # stats diff per file and line
481 statistics = snapshot2.compare_to(snapshot, 'lineno')
482 self.assertEqual(statistics, [
483 tracemalloc.StatisticDiff(tb_a_5, 5002, 5000, 2, 1),
484 tracemalloc.StatisticDiff(tb_c_578, 400, 400, 1, 1),
485 tracemalloc.StatisticDiff(tb_b_1, 0, -66, 0, -1),
486 tracemalloc.StatisticDiff(tb_0, 0, -7, 0, -1),
487 tracemalloc.StatisticDiff(tb_a_2, 30, 0, 3, 0),
488 ])
489
490 def test_snapshot_group_by_file(self):
491 snapshot, snapshot2 = create_snapshots()
492 tb_0 = traceback_filename('<unknown>')
493 tb_a = traceback_filename('a.py')
494 tb_b = traceback_filename('b.py')
495 tb_c = traceback_filename('c.py')
496
497 # stats per file
498 stats1 = snapshot.statistics('filename')
499 self.assertEqual(stats1, [
500 tracemalloc.Statistic(tb_b, 66, 1),
501 tracemalloc.Statistic(tb_a, 32, 4),
502 tracemalloc.Statistic(tb_0, 7, 1),
503 ])
504
505 # stats per file (2)
506 stats2 = snapshot2.statistics('filename')
507 self.assertEqual(stats2, [
508 tracemalloc.Statistic(tb_a, 5032, 5),
509 tracemalloc.Statistic(tb_c, 400, 1),
510 ])
511
512 # stats diff per file
513 diff = snapshot2.compare_to(snapshot, 'filename')
514 self.assertEqual(diff, [
515 tracemalloc.StatisticDiff(tb_a, 5032, 5000, 5, 1),
516 tracemalloc.StatisticDiff(tb_c, 400, 400, 1, 1),
517 tracemalloc.StatisticDiff(tb_b, 0, -66, 0, -1),
518 tracemalloc.StatisticDiff(tb_0, 0, -7, 0, -1),
519 ])
520
521 def test_snapshot_group_by_traceback(self):
522 snapshot, snapshot2 = create_snapshots()
523
524 # stats per file
525 tb1 = traceback(('a.py', 2), ('b.py', 4))
526 tb2 = traceback(('a.py', 5), ('b.py', 4))
527 tb3 = traceback(('b.py', 1))
528 tb4 = traceback(('<unknown>', 0))
529 stats1 = snapshot.statistics('traceback')
530 self.assertEqual(stats1, [
531 tracemalloc.Statistic(tb3, 66, 1),
532 tracemalloc.Statistic(tb1, 30, 3),
533 tracemalloc.Statistic(tb4, 7, 1),
534 tracemalloc.Statistic(tb2, 2, 1),
535 ])
536
537 # stats per file (2)
538 tb5 = traceback(('c.py', 578))
539 stats2 = snapshot2.statistics('traceback')
540 self.assertEqual(stats2, [
541 tracemalloc.Statistic(tb2, 5002, 2),
542 tracemalloc.Statistic(tb5, 400, 1),
543 tracemalloc.Statistic(tb1, 30, 3),
544 ])
545
546 # stats diff per file
547 diff = snapshot2.compare_to(snapshot, 'traceback')
548 self.assertEqual(diff, [
549 tracemalloc.StatisticDiff(tb2, 5002, 5000, 2, 1),
550 tracemalloc.StatisticDiff(tb5, 400, 400, 1, 1),
551 tracemalloc.StatisticDiff(tb3, 0, -66, 0, -1),
552 tracemalloc.StatisticDiff(tb4, 0, -7, 0, -1),
553 tracemalloc.StatisticDiff(tb1, 30, 0, 3, 0),
554 ])
555
556 self.assertRaises(ValueError,
557 snapshot.statistics, 'traceback', cumulative=True)
558
559 def test_snapshot_group_by_cumulative(self):
560 snapshot, snapshot2 = create_snapshots()
561 tb_0 = traceback_filename('<unknown>')
562 tb_a = traceback_filename('a.py')
563 tb_b = traceback_filename('b.py')
564 tb_a_2 = traceback_lineno('a.py', 2)
565 tb_a_5 = traceback_lineno('a.py', 5)
566 tb_b_1 = traceback_lineno('b.py', 1)
567 tb_b_4 = traceback_lineno('b.py', 4)
568
569 # per file
570 stats = snapshot.statistics('filename', True)
571 self.assertEqual(stats, [
572 tracemalloc.Statistic(tb_b, 98, 5),
573 tracemalloc.Statistic(tb_a, 32, 4),
574 tracemalloc.Statistic(tb_0, 7, 1),
575 ])
576
577 # per line
578 stats = snapshot.statistics('lineno', True)
579 self.assertEqual(stats, [
580 tracemalloc.Statistic(tb_b_1, 66, 1),
581 tracemalloc.Statistic(tb_b_4, 32, 4),
582 tracemalloc.Statistic(tb_a_2, 30, 3),
583 tracemalloc.Statistic(tb_0, 7, 1),
584 tracemalloc.Statistic(tb_a_5, 2, 1),
585 ])
586
587 def test_trace_format(self):
588 snapshot, snapshot2 = create_snapshots()
589 trace = snapshot.traces[0]
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100590 self.assertEqual(str(trace), 'b.py:4: 10 B')
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100591 traceback = trace.traceback
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100592 self.assertEqual(str(traceback), 'b.py:4')
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100593 frame = traceback[0]
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100594 self.assertEqual(str(frame), 'b.py:4')
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100595
596 def test_statistic_format(self):
597 snapshot, snapshot2 = create_snapshots()
598 stats = snapshot.statistics('lineno')
599 stat = stats[0]
600 self.assertEqual(str(stat),
601 'b.py:1: size=66 B, count=1, average=66 B')
602
603 def test_statistic_diff_format(self):
604 snapshot, snapshot2 = create_snapshots()
605 stats = snapshot2.compare_to(snapshot, 'lineno')
606 stat = stats[0]
607 self.assertEqual(str(stat),
608 'a.py:5: size=5002 B (+5000 B), count=2 (+1), average=2501 B')
609
Victor Stinner524be302014-02-01 04:07:02 +0100610 def test_slices(self):
611 snapshot, snapshot2 = create_snapshots()
612 self.assertEqual(snapshot.traces[:2],
613 (snapshot.traces[0], snapshot.traces[1]))
614
615 traceback = snapshot.traces[0].traceback
616 self.assertEqual(traceback[:2],
617 (traceback[0], traceback[1]))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100618
Victor Stinner23f628d2014-02-16 23:53:38 +0100619 def test_format_traceback(self):
620 snapshot, snapshot2 = create_snapshots()
621 def getline(filename, lineno):
622 return ' <%s, %s>' % (filename, lineno)
623 with unittest.mock.patch('tracemalloc.linecache.getline',
624 side_effect=getline):
625 tb = snapshot.traces[0].traceback
626 self.assertEqual(tb.format(),
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100627 [' File "b.py", line 4',
628 ' <b.py, 4>',
629 ' File "a.py", line 2',
630 ' <a.py, 2>'])
Victor Stinner23f628d2014-02-16 23:53:38 +0100631
632 self.assertEqual(tb.format(limit=1),
633 [' File "a.py", line 2',
634 ' <a.py, 2>'])
635
636 self.assertEqual(tb.format(limit=-1),
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100637 [' File "b.py", line 4',
638 ' <b.py, 4>'])
639
640 self.assertEqual(tb.format(most_recent_first=True),
641 [' File "a.py", line 2',
642 ' <a.py, 2>',
643 ' File "b.py", line 4',
644 ' <b.py, 4>'])
645
646 self.assertEqual(tb.format(limit=1, most_recent_first=True),
647 [' File "a.py", line 2',
648 ' <a.py, 2>'])
649
650 self.assertEqual(tb.format(limit=-1, most_recent_first=True),
651 [' File "b.py", line 4',
652 ' <b.py, 4>'])
Victor Stinner23f628d2014-02-16 23:53:38 +0100653
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100654
655class TestFilters(unittest.TestCase):
656 maxDiff = 2048
657
658 def test_filter_attributes(self):
659 # test default values
660 f = tracemalloc.Filter(True, "abc")
661 self.assertEqual(f.inclusive, True)
662 self.assertEqual(f.filename_pattern, "abc")
663 self.assertIsNone(f.lineno)
664 self.assertEqual(f.all_frames, False)
665
666 # test custom values
667 f = tracemalloc.Filter(False, "test.py", 123, True)
668 self.assertEqual(f.inclusive, False)
669 self.assertEqual(f.filename_pattern, "test.py")
670 self.assertEqual(f.lineno, 123)
671 self.assertEqual(f.all_frames, True)
672
673 # parameters passed by keyword
674 f = tracemalloc.Filter(inclusive=False, filename_pattern="test.py", lineno=123, all_frames=True)
675 self.assertEqual(f.inclusive, False)
676 self.assertEqual(f.filename_pattern, "test.py")
677 self.assertEqual(f.lineno, 123)
678 self.assertEqual(f.all_frames, True)
679
680 # read-only attribute
681 self.assertRaises(AttributeError, setattr, f, "filename_pattern", "abc")
682
683 def test_filter_match(self):
684 # filter without line number
685 f = tracemalloc.Filter(True, "abc")
686 self.assertTrue(f._match_frame("abc", 0))
687 self.assertTrue(f._match_frame("abc", 5))
688 self.assertTrue(f._match_frame("abc", 10))
689 self.assertFalse(f._match_frame("12356", 0))
690 self.assertFalse(f._match_frame("12356", 5))
691 self.assertFalse(f._match_frame("12356", 10))
692
693 f = tracemalloc.Filter(False, "abc")
694 self.assertFalse(f._match_frame("abc", 0))
695 self.assertFalse(f._match_frame("abc", 5))
696 self.assertFalse(f._match_frame("abc", 10))
697 self.assertTrue(f._match_frame("12356", 0))
698 self.assertTrue(f._match_frame("12356", 5))
699 self.assertTrue(f._match_frame("12356", 10))
700
701 # filter with line number > 0
702 f = tracemalloc.Filter(True, "abc", 5)
703 self.assertFalse(f._match_frame("abc", 0))
704 self.assertTrue(f._match_frame("abc", 5))
705 self.assertFalse(f._match_frame("abc", 10))
706 self.assertFalse(f._match_frame("12356", 0))
707 self.assertFalse(f._match_frame("12356", 5))
708 self.assertFalse(f._match_frame("12356", 10))
709
710 f = tracemalloc.Filter(False, "abc", 5)
711 self.assertTrue(f._match_frame("abc", 0))
712 self.assertFalse(f._match_frame("abc", 5))
713 self.assertTrue(f._match_frame("abc", 10))
714 self.assertTrue(f._match_frame("12356", 0))
715 self.assertTrue(f._match_frame("12356", 5))
716 self.assertTrue(f._match_frame("12356", 10))
717
718 # filter with line number 0
719 f = tracemalloc.Filter(True, "abc", 0)
720 self.assertTrue(f._match_frame("abc", 0))
721 self.assertFalse(f._match_frame("abc", 5))
722 self.assertFalse(f._match_frame("abc", 10))
723 self.assertFalse(f._match_frame("12356", 0))
724 self.assertFalse(f._match_frame("12356", 5))
725 self.assertFalse(f._match_frame("12356", 10))
726
727 f = tracemalloc.Filter(False, "abc", 0)
728 self.assertFalse(f._match_frame("abc", 0))
729 self.assertTrue(f._match_frame("abc", 5))
730 self.assertTrue(f._match_frame("abc", 10))
731 self.assertTrue(f._match_frame("12356", 0))
732 self.assertTrue(f._match_frame("12356", 5))
733 self.assertTrue(f._match_frame("12356", 10))
734
735 def test_filter_match_filename(self):
736 def fnmatch(inclusive, filename, pattern):
737 f = tracemalloc.Filter(inclusive, pattern)
738 return f._match_frame(filename, 0)
739
740 self.assertTrue(fnmatch(True, "abc", "abc"))
741 self.assertFalse(fnmatch(True, "12356", "abc"))
742 self.assertFalse(fnmatch(True, "<unknown>", "abc"))
743
744 self.assertFalse(fnmatch(False, "abc", "abc"))
745 self.assertTrue(fnmatch(False, "12356", "abc"))
746 self.assertTrue(fnmatch(False, "<unknown>", "abc"))
747
748 def test_filter_match_filename_joker(self):
749 def fnmatch(filename, pattern):
750 filter = tracemalloc.Filter(True, pattern)
751 return filter._match_frame(filename, 0)
752
753 # empty string
754 self.assertFalse(fnmatch('abc', ''))
755 self.assertFalse(fnmatch('', 'abc'))
756 self.assertTrue(fnmatch('', ''))
757 self.assertTrue(fnmatch('', '*'))
758
759 # no *
760 self.assertTrue(fnmatch('abc', 'abc'))
761 self.assertFalse(fnmatch('abc', 'abcd'))
762 self.assertFalse(fnmatch('abc', 'def'))
763
764 # a*
765 self.assertTrue(fnmatch('abc', 'a*'))
766 self.assertTrue(fnmatch('abc', 'abc*'))
767 self.assertFalse(fnmatch('abc', 'b*'))
768 self.assertFalse(fnmatch('abc', 'abcd*'))
769
770 # a*b
771 self.assertTrue(fnmatch('abc', 'a*c'))
772 self.assertTrue(fnmatch('abcdcx', 'a*cx'))
773 self.assertFalse(fnmatch('abb', 'a*c'))
774 self.assertFalse(fnmatch('abcdce', 'a*cx'))
775
776 # a*b*c
777 self.assertTrue(fnmatch('abcde', 'a*c*e'))
778 self.assertTrue(fnmatch('abcbdefeg', 'a*bd*eg'))
779 self.assertFalse(fnmatch('abcdd', 'a*c*e'))
780 self.assertFalse(fnmatch('abcbdefef', 'a*bd*eg'))
781
Brett Cannonf299abd2015-04-13 14:21:02 -0400782 # replace .pyc suffix with .py
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100783 self.assertTrue(fnmatch('a.pyc', 'a.py'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100784 self.assertTrue(fnmatch('a.py', 'a.pyc'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100785
786 if os.name == 'nt':
787 # case insensitive
788 self.assertTrue(fnmatch('aBC', 'ABc'))
789 self.assertTrue(fnmatch('aBcDe', 'Ab*dE'))
790
791 self.assertTrue(fnmatch('a.pyc', 'a.PY'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100792 self.assertTrue(fnmatch('a.py', 'a.PYC'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100793 else:
794 # case sensitive
795 self.assertFalse(fnmatch('aBC', 'ABc'))
796 self.assertFalse(fnmatch('aBcDe', 'Ab*dE'))
797
798 self.assertFalse(fnmatch('a.pyc', 'a.PY'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100799 self.assertFalse(fnmatch('a.py', 'a.PYC'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100800
801 if os.name == 'nt':
802 # normalize alternate separator "/" to the standard separator "\"
803 self.assertTrue(fnmatch(r'a/b', r'a\b'))
804 self.assertTrue(fnmatch(r'a\b', r'a/b'))
805 self.assertTrue(fnmatch(r'a/b\c', r'a\b/c'))
806 self.assertTrue(fnmatch(r'a/b/c', r'a\b\c'))
807 else:
808 # there is no alternate separator
809 self.assertFalse(fnmatch(r'a/b', r'a\b'))
810 self.assertFalse(fnmatch(r'a\b', r'a/b'))
811 self.assertFalse(fnmatch(r'a/b\c', r'a\b/c'))
812 self.assertFalse(fnmatch(r'a/b/c', r'a\b\c'))
813
Zachary Wared9b25bb2015-05-13 00:27:01 -0500814 # as of 3.5, .pyo is no longer munged to .py
815 self.assertFalse(fnmatch('a.pyo', 'a.py'))
816
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100817 def test_filter_match_trace(self):
818 t1 = (("a.py", 2), ("b.py", 3))
819 t2 = (("b.py", 4), ("b.py", 5))
820 t3 = (("c.py", 5), ('<unknown>', 0))
821 unknown = (('<unknown>', 0),)
822
823 f = tracemalloc.Filter(True, "b.py", all_frames=True)
824 self.assertTrue(f._match_traceback(t1))
825 self.assertTrue(f._match_traceback(t2))
826 self.assertFalse(f._match_traceback(t3))
827 self.assertFalse(f._match_traceback(unknown))
828
829 f = tracemalloc.Filter(True, "b.py", all_frames=False)
830 self.assertFalse(f._match_traceback(t1))
831 self.assertTrue(f._match_traceback(t2))
832 self.assertFalse(f._match_traceback(t3))
833 self.assertFalse(f._match_traceback(unknown))
834
835 f = tracemalloc.Filter(False, "b.py", all_frames=True)
836 self.assertFalse(f._match_traceback(t1))
837 self.assertFalse(f._match_traceback(t2))
838 self.assertTrue(f._match_traceback(t3))
839 self.assertTrue(f._match_traceback(unknown))
840
841 f = tracemalloc.Filter(False, "b.py", all_frames=False)
842 self.assertTrue(f._match_traceback(t1))
843 self.assertFalse(f._match_traceback(t2))
844 self.assertTrue(f._match_traceback(t3))
845 self.assertTrue(f._match_traceback(unknown))
846
847 f = tracemalloc.Filter(False, "<unknown>", all_frames=False)
848 self.assertTrue(f._match_traceback(t1))
849 self.assertTrue(f._match_traceback(t2))
850 self.assertTrue(f._match_traceback(t3))
851 self.assertFalse(f._match_traceback(unknown))
852
853 f = tracemalloc.Filter(True, "<unknown>", all_frames=True)
854 self.assertFalse(f._match_traceback(t1))
855 self.assertFalse(f._match_traceback(t2))
856 self.assertTrue(f._match_traceback(t3))
857 self.assertTrue(f._match_traceback(unknown))
858
859 f = tracemalloc.Filter(False, "<unknown>", all_frames=True)
860 self.assertTrue(f._match_traceback(t1))
861 self.assertTrue(f._match_traceback(t2))
862 self.assertFalse(f._match_traceback(t3))
863 self.assertFalse(f._match_traceback(unknown))
864
865
866class TestCommandLine(unittest.TestCase):
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800867 def test_env_var_disabled_by_default(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100868 # not tracing by default
869 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
870 ok, stdout, stderr = assert_python_ok('-c', code)
871 stdout = stdout.rstrip()
872 self.assertEqual(stdout, b'False')
873
Berker Peksagce643912015-05-06 06:33:17 +0300874 @unittest.skipIf(interpreter_requires_environment(),
Gregory P. Smithb9a3dd92015-02-04 00:59:40 -0800875 'Cannot run -E tests when PYTHON env vars are required.')
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800876 def test_env_var_ignored_with_E(self):
877 """PYTHON* environment variables must be ignored when -E is present."""
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100878 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
879 ok, stdout, stderr = assert_python_ok('-E', '-c', code, PYTHONTRACEMALLOC='1')
880 stdout = stdout.rstrip()
881 self.assertEqual(stdout, b'False')
882
Victor Stinner60b04c92018-07-25 19:23:53 +0200883 def test_env_var_disabled(self):
884 # tracing at startup
885 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
886 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='0')
887 stdout = stdout.rstrip()
888 self.assertEqual(stdout, b'False')
889
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800890 def test_env_var_enabled_at_startup(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100891 # tracing at startup
892 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
893 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='1')
894 stdout = stdout.rstrip()
895 self.assertEqual(stdout, b'True')
896
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800897 def test_env_limit(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100898 # start and set the number of frames
899 code = 'import tracemalloc; print(tracemalloc.get_traceback_limit())'
900 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='10')
901 stdout = stdout.rstrip()
902 self.assertEqual(stdout, b'10')
903
Victor Stinnera7368ac2017-11-15 18:11:45 -0800904 def check_env_var_invalid(self, nframe):
905 with support.SuppressCrashReport():
906 ok, stdout, stderr = assert_python_failure(
907 '-c', 'pass',
908 PYTHONTRACEMALLOC=str(nframe))
909
910 if b'ValueError: the number of frames must be in range' in stderr:
911 return
912 if b'PYTHONTRACEMALLOC: invalid number of frames' in stderr:
913 return
Min ho Kim39d87b52019-08-31 06:21:19 +1000914 self.fail(f"unexpected output: {stderr!a}")
Victor Stinnera7368ac2017-11-15 18:11:45 -0800915
916
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100917 def test_env_var_invalid(self):
Victor Stinner60b04c92018-07-25 19:23:53 +0200918 for nframe in INVALID_NFRAME:
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100919 with self.subTest(nframe=nframe):
Victor Stinnera7368ac2017-11-15 18:11:45 -0800920 self.check_env_var_invalid(nframe)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100921
922 def test_sys_xoptions(self):
923 for xoptions, nframe in (
924 ('tracemalloc', 1),
925 ('tracemalloc=1', 1),
926 ('tracemalloc=15', 15),
927 ):
928 with self.subTest(xoptions=xoptions, nframe=nframe):
929 code = 'import tracemalloc; print(tracemalloc.get_traceback_limit())'
930 ok, stdout, stderr = assert_python_ok('-X', xoptions, '-c', code)
931 stdout = stdout.rstrip()
932 self.assertEqual(stdout, str(nframe).encode('ascii'))
933
Victor Stinnera7368ac2017-11-15 18:11:45 -0800934 def check_sys_xoptions_invalid(self, nframe):
935 args = ('-X', 'tracemalloc=%s' % nframe, '-c', 'pass')
936 with support.SuppressCrashReport():
937 ok, stdout, stderr = assert_python_failure(*args)
938
939 if b'ValueError: the number of frames must be in range' in stderr:
940 return
941 if b'-X tracemalloc=NFRAME: invalid number of frames' in stderr:
942 return
Min ho Kim39d87b52019-08-31 06:21:19 +1000943 self.fail(f"unexpected output: {stderr!a}")
Victor Stinnera7368ac2017-11-15 18:11:45 -0800944
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100945 def test_sys_xoptions_invalid(self):
Victor Stinner60b04c92018-07-25 19:23:53 +0200946 for nframe in INVALID_NFRAME:
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100947 with self.subTest(nframe=nframe):
Victor Stinnera7368ac2017-11-15 18:11:45 -0800948 self.check_sys_xoptions_invalid(nframe)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100949
Serhiy Storchaka24c738a2017-03-19 20:20:10 +0200950 @unittest.skipIf(_testcapi is None, 'need _testcapi')
Victor Stinner8dd49fe2014-06-02 21:36:59 +0200951 def test_pymem_alloc0(self):
952 # Issue #21639: Check that PyMem_Malloc(0) with tracemalloc enabled
953 # does not crash.
954 code = 'import _testcapi; _testcapi.test_pymem_alloc0(); 1'
955 assert_python_ok('-X', 'tracemalloc', '-c', code)
956
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100957
Victor Stinner10b73e12016-03-22 13:39:05 +0100958@unittest.skipIf(_testcapi is None, 'need _testcapi')
959class TestCAPI(unittest.TestCase):
960 maxDiff = 80 * 20
961
962 def setUp(self):
963 if tracemalloc.is_tracing():
964 self.skipTest("tracemalloc must be stopped before the test")
965
966 self.domain = 5
967 self.size = 123
968 self.obj = allocate_bytes(self.size)[0]
969
970 # for the type "object", id(obj) is the address of its memory block.
971 # This type is not tracked by the garbage collector
972 self.ptr = id(self.obj)
973
974 def tearDown(self):
975 tracemalloc.stop()
976
977 def get_traceback(self):
978 frames = _testcapi.tracemalloc_get_traceback(self.domain, self.ptr)
979 if frames is not None:
980 return tracemalloc.Traceback(frames)
981 else:
982 return None
983
984 def track(self, release_gil=False, nframe=1):
Serhiy Storchakada8d72c2018-09-17 15:17:29 +0300985 frames = get_frames(nframe, 1)
Victor Stinner10b73e12016-03-22 13:39:05 +0100986 _testcapi.tracemalloc_track(self.domain, self.ptr, self.size,
987 release_gil)
988 return frames
989
990 def untrack(self):
991 _testcapi.tracemalloc_untrack(self.domain, self.ptr)
992
993 def get_traced_memory(self):
994 # Get the traced size in the domain
995 snapshot = tracemalloc.take_snapshot()
996 domain_filter = tracemalloc.DomainFilter(True, self.domain)
997 snapshot = snapshot.filter_traces([domain_filter])
998 return sum(trace.size for trace in snapshot.traces)
999
1000 def check_track(self, release_gil):
1001 nframe = 5
1002 tracemalloc.start(nframe)
1003
1004 size = tracemalloc.get_traced_memory()[0]
1005
1006 frames = self.track(release_gil, nframe)
1007 self.assertEqual(self.get_traceback(),
1008 tracemalloc.Traceback(frames))
1009
1010 self.assertEqual(self.get_traced_memory(), self.size)
1011
1012 def test_track(self):
1013 self.check_track(False)
1014
1015 def test_track_without_gil(self):
1016 # check that calling _PyTraceMalloc_Track() without holding the GIL
1017 # works too
1018 self.check_track(True)
1019
1020 def test_track_already_tracked(self):
1021 nframe = 5
1022 tracemalloc.start(nframe)
1023
1024 # track a first time
1025 self.track()
1026
1027 # calling _PyTraceMalloc_Track() must remove the old trace and add
1028 # a new trace with the new traceback
1029 frames = self.track(nframe=nframe)
1030 self.assertEqual(self.get_traceback(),
1031 tracemalloc.Traceback(frames))
1032
1033 def test_untrack(self):
1034 tracemalloc.start()
1035
1036 self.track()
1037 self.assertIsNotNone(self.get_traceback())
1038 self.assertEqual(self.get_traced_memory(), self.size)
1039
1040 # untrack must remove the trace
1041 self.untrack()
1042 self.assertIsNone(self.get_traceback())
1043 self.assertEqual(self.get_traced_memory(), 0)
1044
1045 # calling _PyTraceMalloc_Untrack() multiple times must not crash
1046 self.untrack()
1047 self.untrack()
1048
1049 def test_stop_track(self):
1050 tracemalloc.start()
1051 tracemalloc.stop()
1052
1053 with self.assertRaises(RuntimeError):
1054 self.track()
1055 self.assertIsNone(self.get_traceback())
1056
1057 def test_stop_untrack(self):
1058 tracemalloc.start()
1059 self.track()
1060
1061 tracemalloc.stop()
1062 with self.assertRaises(RuntimeError):
1063 self.untrack()
1064
1065
Victor Stinnered3b0bc2013-11-23 12:27:24 +01001066def test_main():
1067 support.run_unittest(
1068 TestTracemallocEnabled,
1069 TestSnapshot,
1070 TestFilters,
1071 TestCommandLine,
Victor Stinner10b73e12016-03-22 13:39:05 +01001072 TestCAPI,
Victor Stinnered3b0bc2013-11-23 12:27:24 +01001073 )
1074
1075if __name__ == "__main__":
1076 test_main()