blob: 7283d9c31db4eeafb7613c9579052e89345e0fbb [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
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020010
Victor Stinner10b73e12016-03-22 13:39:05 +010011try:
12 import _testcapi
13except ImportError:
14 _testcapi = None
15
Victor Stinnered3b0bc2013-11-23 12:27:24 +010016
17EMPTY_STRING_SIZE = sys.getsizeof(b'')
Victor Stinner60b04c92018-07-25 19:23:53 +020018INVALID_NFRAME = (-1, 2**30)
Victor Stinnered3b0bc2013-11-23 12:27:24 +010019
Victor Stinner10b73e12016-03-22 13:39:05 +010020
Victor Stinnered3b0bc2013-11-23 12:27:24 +010021def get_frames(nframe, lineno_delta):
22 frames = []
23 frame = sys._getframe(1)
24 for index in range(nframe):
25 code = frame.f_code
26 lineno = frame.f_lineno + lineno_delta
27 frames.append((code.co_filename, lineno))
28 lineno_delta = 0
29 frame = frame.f_back
30 if frame is None:
31 break
32 return tuple(frames)
33
34def allocate_bytes(size):
35 nframe = tracemalloc.get_traceback_limit()
36 bytes_len = (size - EMPTY_STRING_SIZE)
37 frames = get_frames(nframe, 1)
38 data = b'x' * bytes_len
Julien Danjou8d59eb12019-10-15 14:00:16 +020039 return data, tracemalloc.Traceback(frames, min(len(frames), nframe))
Victor Stinnered3b0bc2013-11-23 12:27:24 +010040
41def create_snapshots():
42 traceback_limit = 2
43
Victor Stinnere492ae52016-03-22 12:58:23 +010044 # _tracemalloc._get_traces() returns a list of (domain, size,
45 # traceback_frames) tuples. traceback_frames is a tuple of (filename,
46 # line_number) tuples.
Victor Stinnered3b0bc2013-11-23 12:27:24 +010047 raw_traces = [
Julien Danjou8d59eb12019-10-15 14:00:16 +020048 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
49 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
50 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010051
Julien Danjou8d59eb12019-10-15 14:00:16 +020052 (1, 2, (('a.py', 5), ('b.py', 4)), 3),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010053
Julien Danjou8d59eb12019-10-15 14:00:16 +020054 (2, 66, (('b.py', 1),), 1),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010055
Julien Danjou8d59eb12019-10-15 14:00:16 +020056 (3, 7, (('<unknown>', 0),), 1),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010057 ]
58 snapshot = tracemalloc.Snapshot(raw_traces, traceback_limit)
59
60 raw_traces2 = [
Julien Danjou8d59eb12019-10-15 14:00:16 +020061 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
62 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
63 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010064
Julien Danjou8d59eb12019-10-15 14:00:16 +020065 (2, 2, (('a.py', 5), ('b.py', 4)), 3),
66 (2, 5000, (('a.py', 5), ('b.py', 4)), 3),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010067
Julien Danjou8d59eb12019-10-15 14:00:16 +020068 (4, 400, (('c.py', 578),), 1),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010069 ]
70 snapshot2 = tracemalloc.Snapshot(raw_traces2, traceback_limit)
71
72 return (snapshot, snapshot2)
73
74def frame(filename, lineno):
75 return tracemalloc._Frame((filename, lineno))
76
77def traceback(*frames):
78 return tracemalloc.Traceback(frames)
79
80def traceback_lineno(filename, lineno):
81 return traceback((filename, lineno))
82
83def traceback_filename(filename):
84 return traceback_lineno(filename, 0)
85
86
87class TestTracemallocEnabled(unittest.TestCase):
88 def setUp(self):
89 if tracemalloc.is_tracing():
90 self.skipTest("tracemalloc must be stopped before the test")
91
Victor Stinner3728d6c2013-11-23 12:37:20 +010092 tracemalloc.start(1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +010093
94 def tearDown(self):
95 tracemalloc.stop()
96
97 def test_get_tracemalloc_memory(self):
98 data = [allocate_bytes(123) for count in range(1000)]
99 size = tracemalloc.get_tracemalloc_memory()
100 self.assertGreaterEqual(size, 0)
101
102 tracemalloc.clear_traces()
103 size2 = tracemalloc.get_tracemalloc_memory()
104 self.assertGreaterEqual(size2, 0)
105 self.assertLessEqual(size2, size)
106
107 def test_get_object_traceback(self):
108 tracemalloc.clear_traces()
109 obj_size = 12345
110 obj, obj_traceback = allocate_bytes(obj_size)
111 traceback = tracemalloc.get_object_traceback(obj)
112 self.assertEqual(traceback, obj_traceback)
113
Victor Stinner9e00e802018-10-25 13:31:16 +0200114 def test_new_reference(self):
115 tracemalloc.clear_traces()
116 # gc.collect() indirectly calls PyList_ClearFreeList()
117 support.gc_collect()
118
119 # Create a list and "destroy it": put it in the PyListObject free list
120 obj = []
121 obj = None
122
123 # Create a list which should reuse the previously created empty list
124 obj = []
125
126 nframe = tracemalloc.get_traceback_limit()
127 frames = get_frames(nframe, -3)
Julien Danjou8d59eb12019-10-15 14:00:16 +0200128 obj_traceback = tracemalloc.Traceback(frames, min(len(frames), nframe))
Victor Stinner9e00e802018-10-25 13:31:16 +0200129
130 traceback = tracemalloc.get_object_traceback(obj)
131 self.assertIsNotNone(traceback)
132 self.assertEqual(traceback, obj_traceback)
133
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100134 def test_set_traceback_limit(self):
135 obj_size = 10
136
Victor Stinner3728d6c2013-11-23 12:37:20 +0100137 tracemalloc.stop()
138 self.assertRaises(ValueError, tracemalloc.start, -1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100139
Victor Stinner3728d6c2013-11-23 12:37:20 +0100140 tracemalloc.stop()
141 tracemalloc.start(10)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100142 obj2, obj2_traceback = allocate_bytes(obj_size)
143 traceback = tracemalloc.get_object_traceback(obj2)
144 self.assertEqual(len(traceback), 10)
145 self.assertEqual(traceback, obj2_traceback)
146
Victor Stinner3728d6c2013-11-23 12:37:20 +0100147 tracemalloc.stop()
148 tracemalloc.start(1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100149 obj, obj_traceback = allocate_bytes(obj_size)
150 traceback = tracemalloc.get_object_traceback(obj)
151 self.assertEqual(len(traceback), 1)
152 self.assertEqual(traceback, obj_traceback)
153
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100154 def find_trace(self, traces, traceback):
155 for trace in traces:
Victor Stinnere492ae52016-03-22 12:58:23 +0100156 if trace[2] == traceback._frames:
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100157 return trace
158
159 self.fail("trace not found")
160
161 def test_get_traces(self):
162 tracemalloc.clear_traces()
163 obj_size = 12345
164 obj, obj_traceback = allocate_bytes(obj_size)
165
166 traces = tracemalloc._get_traces()
167 trace = self.find_trace(traces, obj_traceback)
168
169 self.assertIsInstance(trace, tuple)
Julien Danjou8d59eb12019-10-15 14:00:16 +0200170 domain, size, traceback, length = trace
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100171 self.assertEqual(size, obj_size)
172 self.assertEqual(traceback, obj_traceback._frames)
173
174 tracemalloc.stop()
175 self.assertEqual(tracemalloc._get_traces(), [])
176
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100177 def test_get_traces_intern_traceback(self):
178 # dummy wrappers to get more useful and identical frames in the traceback
179 def allocate_bytes2(size):
180 return allocate_bytes(size)
181 def allocate_bytes3(size):
182 return allocate_bytes2(size)
183 def allocate_bytes4(size):
184 return allocate_bytes3(size)
185
186 # Ensure that two identical tracebacks are not duplicated
Victor Stinner3728d6c2013-11-23 12:37:20 +0100187 tracemalloc.stop()
188 tracemalloc.start(4)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100189 obj_size = 123
190 obj1, obj1_traceback = allocate_bytes4(obj_size)
191 obj2, obj2_traceback = allocate_bytes4(obj_size)
192
193 traces = tracemalloc._get_traces()
194
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100195 obj1_traceback._frames = tuple(reversed(obj1_traceback._frames))
196 obj2_traceback._frames = tuple(reversed(obj2_traceback._frames))
197
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100198 trace1 = self.find_trace(traces, obj1_traceback)
199 trace2 = self.find_trace(traces, obj2_traceback)
Julien Danjou8d59eb12019-10-15 14:00:16 +0200200 domain1, size1, traceback1, length1 = trace1
201 domain2, size2, traceback2, length2 = trace2
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100202 self.assertIs(traceback2, traceback1)
203
204 def test_get_traced_memory(self):
205 # Python allocates some internals objects, so the test must tolerate
206 # a small difference between the expected size and the real usage
207 max_error = 2048
208
209 # allocate one object
210 obj_size = 1024 * 1024
211 tracemalloc.clear_traces()
212 obj, obj_traceback = allocate_bytes(obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100213 size, peak_size = tracemalloc.get_traced_memory()
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100214 self.assertGreaterEqual(size, obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100215 self.assertGreaterEqual(peak_size, size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100216
217 self.assertLessEqual(size - obj_size, max_error)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100218 self.assertLessEqual(peak_size - size, max_error)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100219
220 # destroy the object
221 obj = None
Victor Stinner3c0481d2013-11-27 21:39:49 +0100222 size2, peak_size2 = tracemalloc.get_traced_memory()
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100223 self.assertLess(size2, size)
224 self.assertGreaterEqual(size - size2, obj_size - max_error)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100225 self.assertGreaterEqual(peak_size2, peak_size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100226
227 # clear_traces() must reset traced memory counters
228 tracemalloc.clear_traces()
229 self.assertEqual(tracemalloc.get_traced_memory(), (0, 0))
230
231 # allocate another object
232 obj, obj_traceback = allocate_bytes(obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100233 size, peak_size = tracemalloc.get_traced_memory()
234 self.assertGreaterEqual(size, obj_size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100235
Victor Stinnera89ecc72013-11-25 09:29:45 +0100236 # stop() also resets traced memory counters
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100237 tracemalloc.stop()
238 self.assertEqual(tracemalloc.get_traced_memory(), (0, 0))
239
240 def test_clear_traces(self):
241 obj, obj_traceback = allocate_bytes(123)
242 traceback = tracemalloc.get_object_traceback(obj)
243 self.assertIsNotNone(traceback)
244
245 tracemalloc.clear_traces()
246 traceback2 = tracemalloc.get_object_traceback(obj)
247 self.assertIsNone(traceback2)
248
249 def test_is_tracing(self):
250 tracemalloc.stop()
251 self.assertFalse(tracemalloc.is_tracing())
252
253 tracemalloc.start()
254 self.assertTrue(tracemalloc.is_tracing())
255
256 def test_snapshot(self):
257 obj, source = allocate_bytes(123)
258
259 # take a snapshot
260 snapshot = tracemalloc.take_snapshot()
261
Julien Danjou8d59eb12019-10-15 14:00:16 +0200262 # This can vary
263 self.assertGreater(snapshot.traces[1].traceback.total_nframe, 10)
264
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100265 # write on disk
266 snapshot.dump(support.TESTFN)
267 self.addCleanup(support.unlink, support.TESTFN)
268
269 # load from disk
270 snapshot2 = tracemalloc.Snapshot.load(support.TESTFN)
271 self.assertEqual(snapshot2.traces, snapshot.traces)
272
273 # tracemalloc must be tracing memory allocations to take a snapshot
274 tracemalloc.stop()
275 with self.assertRaises(RuntimeError) as cm:
276 tracemalloc.take_snapshot()
277 self.assertEqual(str(cm.exception),
278 "the tracemalloc module must be tracing memory "
279 "allocations to take a snapshot")
280
281 def test_snapshot_save_attr(self):
282 # take a snapshot with a new attribute
283 snapshot = tracemalloc.take_snapshot()
284 snapshot.test_attr = "new"
285 snapshot.dump(support.TESTFN)
286 self.addCleanup(support.unlink, support.TESTFN)
287
Martin Panter8f265652016-04-19 04:03:41 +0000288 # load() should recreate the attribute
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100289 snapshot2 = tracemalloc.Snapshot.load(support.TESTFN)
290 self.assertEqual(snapshot2.test_attr, "new")
291
292 def fork_child(self):
293 if not tracemalloc.is_tracing():
294 return 2
295
296 obj_size = 12345
297 obj, obj_traceback = allocate_bytes(obj_size)
298 traceback = tracemalloc.get_object_traceback(obj)
299 if traceback is None:
300 return 3
301
302 # everything is fine
303 return 0
304
305 @unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork()')
306 def test_fork(self):
307 # check that tracemalloc is still working after fork
308 pid = os.fork()
309 if not pid:
310 # child
311 exitcode = 1
312 try:
313 exitcode = self.fork_child()
314 finally:
315 os._exit(exitcode)
316 else:
317 pid2, status = os.waitpid(pid, 0)
318 self.assertTrue(os.WIFEXITED(status))
319 exitcode = os.WEXITSTATUS(status)
320 self.assertEqual(exitcode, 0)
321
322
323class TestSnapshot(unittest.TestCase):
324 maxDiff = 4000
325
326 def test_create_snapshot(self):
Julien Danjou8d59eb12019-10-15 14:00:16 +0200327 raw_traces = [(0, 5, (('a.py', 2),), 10)]
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100328
329 with contextlib.ExitStack() as stack:
330 stack.enter_context(patch.object(tracemalloc, 'is_tracing',
331 return_value=True))
332 stack.enter_context(patch.object(tracemalloc, 'get_traceback_limit',
333 return_value=5))
334 stack.enter_context(patch.object(tracemalloc, '_get_traces',
335 return_value=raw_traces))
336
337 snapshot = tracemalloc.take_snapshot()
338 self.assertEqual(snapshot.traceback_limit, 5)
339 self.assertEqual(len(snapshot.traces), 1)
340 trace = snapshot.traces[0]
341 self.assertEqual(trace.size, 5)
Julien Danjou8d59eb12019-10-15 14:00:16 +0200342 self.assertEqual(trace.traceback.total_nframe, 10)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100343 self.assertEqual(len(trace.traceback), 1)
344 self.assertEqual(trace.traceback[0].filename, 'a.py')
345 self.assertEqual(trace.traceback[0].lineno, 2)
346
347 def test_filter_traces(self):
348 snapshot, snapshot2 = create_snapshots()
349 filter1 = tracemalloc.Filter(False, "b.py")
350 filter2 = tracemalloc.Filter(True, "a.py", 2)
351 filter3 = tracemalloc.Filter(True, "a.py", 5)
352
353 original_traces = list(snapshot.traces._traces)
354
355 # exclude b.py
356 snapshot3 = snapshot.filter_traces((filter1,))
357 self.assertEqual(snapshot3.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200358 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
359 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
360 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
361 (1, 2, (('a.py', 5), ('b.py', 4)), 3),
362 (3, 7, (('<unknown>', 0),), 1),
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100363 ])
364
365 # filter_traces() must not touch the original snapshot
366 self.assertEqual(snapshot.traces._traces, original_traces)
367
368 # only include two lines of a.py
369 snapshot4 = snapshot3.filter_traces((filter2, filter3))
370 self.assertEqual(snapshot4.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200371 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
372 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
373 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
374 (1, 2, (('a.py', 5), ('b.py', 4)), 3),
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100375 ])
376
377 # No filter: just duplicate the snapshot
378 snapshot5 = snapshot.filter_traces(())
379 self.assertIsNot(snapshot5, snapshot)
380 self.assertIsNot(snapshot5.traces, snapshot.traces)
381 self.assertEqual(snapshot5.traces, snapshot.traces)
382
Victor Stinner8ce8ff92014-03-10 11:05:07 +0100383 self.assertRaises(TypeError, snapshot.filter_traces, filter1)
384
Victor Stinnere492ae52016-03-22 12:58:23 +0100385 def test_filter_traces_domain(self):
386 snapshot, snapshot2 = create_snapshots()
387 filter1 = tracemalloc.Filter(False, "a.py", domain=1)
388 filter2 = tracemalloc.Filter(True, "a.py", domain=1)
389
390 original_traces = list(snapshot.traces._traces)
391
392 # exclude a.py of domain 1
393 snapshot3 = snapshot.filter_traces((filter1,))
394 self.assertEqual(snapshot3.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200395 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
396 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
397 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
398 (2, 66, (('b.py', 1),), 1),
399 (3, 7, (('<unknown>', 0),), 1),
Victor Stinnere492ae52016-03-22 12:58:23 +0100400 ])
401
402 # include domain 1
403 snapshot3 = snapshot.filter_traces((filter1,))
404 self.assertEqual(snapshot3.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200405 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
406 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
407 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
408 (2, 66, (('b.py', 1),), 1),
409 (3, 7, (('<unknown>', 0),), 1),
Victor Stinnere492ae52016-03-22 12:58:23 +0100410 ])
411
412 def test_filter_traces_domain_filter(self):
413 snapshot, snapshot2 = create_snapshots()
414 filter1 = tracemalloc.DomainFilter(False, domain=3)
415 filter2 = tracemalloc.DomainFilter(True, domain=3)
416
417 # exclude domain 2
418 snapshot3 = snapshot.filter_traces((filter1,))
419 self.assertEqual(snapshot3.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200420 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
421 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
422 (0, 10, (('a.py', 2), ('b.py', 4)), 3),
423 (1, 2, (('a.py', 5), ('b.py', 4)), 3),
424 (2, 66, (('b.py', 1),), 1),
Victor Stinnere492ae52016-03-22 12:58:23 +0100425 ])
426
427 # include domain 2
428 snapshot3 = snapshot.filter_traces((filter2,))
429 self.assertEqual(snapshot3.traces._traces, [
Julien Danjou8d59eb12019-10-15 14:00:16 +0200430 (3, 7, (('<unknown>', 0),), 1),
Victor Stinnere492ae52016-03-22 12:58:23 +0100431 ])
432
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100433 def test_snapshot_group_by_line(self):
434 snapshot, snapshot2 = create_snapshots()
435 tb_0 = traceback_lineno('<unknown>', 0)
436 tb_a_2 = traceback_lineno('a.py', 2)
437 tb_a_5 = traceback_lineno('a.py', 5)
438 tb_b_1 = traceback_lineno('b.py', 1)
439 tb_c_578 = traceback_lineno('c.py', 578)
440
441 # stats per file and line
442 stats1 = snapshot.statistics('lineno')
443 self.assertEqual(stats1, [
444 tracemalloc.Statistic(tb_b_1, 66, 1),
445 tracemalloc.Statistic(tb_a_2, 30, 3),
446 tracemalloc.Statistic(tb_0, 7, 1),
447 tracemalloc.Statistic(tb_a_5, 2, 1),
448 ])
449
450 # stats per file and line (2)
451 stats2 = snapshot2.statistics('lineno')
452 self.assertEqual(stats2, [
453 tracemalloc.Statistic(tb_a_5, 5002, 2),
454 tracemalloc.Statistic(tb_c_578, 400, 1),
455 tracemalloc.Statistic(tb_a_2, 30, 3),
456 ])
457
458 # stats diff per file and line
459 statistics = snapshot2.compare_to(snapshot, 'lineno')
460 self.assertEqual(statistics, [
461 tracemalloc.StatisticDiff(tb_a_5, 5002, 5000, 2, 1),
462 tracemalloc.StatisticDiff(tb_c_578, 400, 400, 1, 1),
463 tracemalloc.StatisticDiff(tb_b_1, 0, -66, 0, -1),
464 tracemalloc.StatisticDiff(tb_0, 0, -7, 0, -1),
465 tracemalloc.StatisticDiff(tb_a_2, 30, 0, 3, 0),
466 ])
467
468 def test_snapshot_group_by_file(self):
469 snapshot, snapshot2 = create_snapshots()
470 tb_0 = traceback_filename('<unknown>')
471 tb_a = traceback_filename('a.py')
472 tb_b = traceback_filename('b.py')
473 tb_c = traceback_filename('c.py')
474
475 # stats per file
476 stats1 = snapshot.statistics('filename')
477 self.assertEqual(stats1, [
478 tracemalloc.Statistic(tb_b, 66, 1),
479 tracemalloc.Statistic(tb_a, 32, 4),
480 tracemalloc.Statistic(tb_0, 7, 1),
481 ])
482
483 # stats per file (2)
484 stats2 = snapshot2.statistics('filename')
485 self.assertEqual(stats2, [
486 tracemalloc.Statistic(tb_a, 5032, 5),
487 tracemalloc.Statistic(tb_c, 400, 1),
488 ])
489
490 # stats diff per file
491 diff = snapshot2.compare_to(snapshot, 'filename')
492 self.assertEqual(diff, [
493 tracemalloc.StatisticDiff(tb_a, 5032, 5000, 5, 1),
494 tracemalloc.StatisticDiff(tb_c, 400, 400, 1, 1),
495 tracemalloc.StatisticDiff(tb_b, 0, -66, 0, -1),
496 tracemalloc.StatisticDiff(tb_0, 0, -7, 0, -1),
497 ])
498
499 def test_snapshot_group_by_traceback(self):
500 snapshot, snapshot2 = create_snapshots()
501
502 # stats per file
503 tb1 = traceback(('a.py', 2), ('b.py', 4))
504 tb2 = traceback(('a.py', 5), ('b.py', 4))
505 tb3 = traceback(('b.py', 1))
506 tb4 = traceback(('<unknown>', 0))
507 stats1 = snapshot.statistics('traceback')
508 self.assertEqual(stats1, [
509 tracemalloc.Statistic(tb3, 66, 1),
510 tracemalloc.Statistic(tb1, 30, 3),
511 tracemalloc.Statistic(tb4, 7, 1),
512 tracemalloc.Statistic(tb2, 2, 1),
513 ])
514
515 # stats per file (2)
516 tb5 = traceback(('c.py', 578))
517 stats2 = snapshot2.statistics('traceback')
518 self.assertEqual(stats2, [
519 tracemalloc.Statistic(tb2, 5002, 2),
520 tracemalloc.Statistic(tb5, 400, 1),
521 tracemalloc.Statistic(tb1, 30, 3),
522 ])
523
524 # stats diff per file
525 diff = snapshot2.compare_to(snapshot, 'traceback')
526 self.assertEqual(diff, [
527 tracemalloc.StatisticDiff(tb2, 5002, 5000, 2, 1),
528 tracemalloc.StatisticDiff(tb5, 400, 400, 1, 1),
529 tracemalloc.StatisticDiff(tb3, 0, -66, 0, -1),
530 tracemalloc.StatisticDiff(tb4, 0, -7, 0, -1),
531 tracemalloc.StatisticDiff(tb1, 30, 0, 3, 0),
532 ])
533
534 self.assertRaises(ValueError,
535 snapshot.statistics, 'traceback', cumulative=True)
536
537 def test_snapshot_group_by_cumulative(self):
538 snapshot, snapshot2 = create_snapshots()
539 tb_0 = traceback_filename('<unknown>')
540 tb_a = traceback_filename('a.py')
541 tb_b = traceback_filename('b.py')
542 tb_a_2 = traceback_lineno('a.py', 2)
543 tb_a_5 = traceback_lineno('a.py', 5)
544 tb_b_1 = traceback_lineno('b.py', 1)
545 tb_b_4 = traceback_lineno('b.py', 4)
546
547 # per file
548 stats = snapshot.statistics('filename', True)
549 self.assertEqual(stats, [
550 tracemalloc.Statistic(tb_b, 98, 5),
551 tracemalloc.Statistic(tb_a, 32, 4),
552 tracemalloc.Statistic(tb_0, 7, 1),
553 ])
554
555 # per line
556 stats = snapshot.statistics('lineno', True)
557 self.assertEqual(stats, [
558 tracemalloc.Statistic(tb_b_1, 66, 1),
559 tracemalloc.Statistic(tb_b_4, 32, 4),
560 tracemalloc.Statistic(tb_a_2, 30, 3),
561 tracemalloc.Statistic(tb_0, 7, 1),
562 tracemalloc.Statistic(tb_a_5, 2, 1),
563 ])
564
565 def test_trace_format(self):
566 snapshot, snapshot2 = create_snapshots()
567 trace = snapshot.traces[0]
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100568 self.assertEqual(str(trace), 'b.py:4: 10 B')
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100569 traceback = trace.traceback
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100570 self.assertEqual(str(traceback), 'b.py:4')
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100571 frame = traceback[0]
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100572 self.assertEqual(str(frame), 'b.py:4')
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100573
574 def test_statistic_format(self):
575 snapshot, snapshot2 = create_snapshots()
576 stats = snapshot.statistics('lineno')
577 stat = stats[0]
578 self.assertEqual(str(stat),
579 'b.py:1: size=66 B, count=1, average=66 B')
580
581 def test_statistic_diff_format(self):
582 snapshot, snapshot2 = create_snapshots()
583 stats = snapshot2.compare_to(snapshot, 'lineno')
584 stat = stats[0]
585 self.assertEqual(str(stat),
586 'a.py:5: size=5002 B (+5000 B), count=2 (+1), average=2501 B')
587
Victor Stinner524be302014-02-01 04:07:02 +0100588 def test_slices(self):
589 snapshot, snapshot2 = create_snapshots()
590 self.assertEqual(snapshot.traces[:2],
591 (snapshot.traces[0], snapshot.traces[1]))
592
593 traceback = snapshot.traces[0].traceback
594 self.assertEqual(traceback[:2],
595 (traceback[0], traceback[1]))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100596
Victor Stinner23f628d2014-02-16 23:53:38 +0100597 def test_format_traceback(self):
598 snapshot, snapshot2 = create_snapshots()
599 def getline(filename, lineno):
600 return ' <%s, %s>' % (filename, lineno)
601 with unittest.mock.patch('tracemalloc.linecache.getline',
602 side_effect=getline):
603 tb = snapshot.traces[0].traceback
604 self.assertEqual(tb.format(),
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100605 [' File "b.py", line 4',
606 ' <b.py, 4>',
607 ' File "a.py", line 2',
608 ' <a.py, 2>'])
Victor Stinner23f628d2014-02-16 23:53:38 +0100609
610 self.assertEqual(tb.format(limit=1),
611 [' File "a.py", line 2',
612 ' <a.py, 2>'])
613
614 self.assertEqual(tb.format(limit=-1),
Jesse-Bakker706e10b2017-11-30 00:05:07 +0100615 [' File "b.py", line 4',
616 ' <b.py, 4>'])
617
618 self.assertEqual(tb.format(most_recent_first=True),
619 [' File "a.py", line 2',
620 ' <a.py, 2>',
621 ' File "b.py", line 4',
622 ' <b.py, 4>'])
623
624 self.assertEqual(tb.format(limit=1, most_recent_first=True),
625 [' File "a.py", line 2',
626 ' <a.py, 2>'])
627
628 self.assertEqual(tb.format(limit=-1, most_recent_first=True),
629 [' File "b.py", line 4',
630 ' <b.py, 4>'])
Victor Stinner23f628d2014-02-16 23:53:38 +0100631
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100632
633class TestFilters(unittest.TestCase):
634 maxDiff = 2048
635
636 def test_filter_attributes(self):
637 # test default values
638 f = tracemalloc.Filter(True, "abc")
639 self.assertEqual(f.inclusive, True)
640 self.assertEqual(f.filename_pattern, "abc")
641 self.assertIsNone(f.lineno)
642 self.assertEqual(f.all_frames, False)
643
644 # test custom values
645 f = tracemalloc.Filter(False, "test.py", 123, True)
646 self.assertEqual(f.inclusive, False)
647 self.assertEqual(f.filename_pattern, "test.py")
648 self.assertEqual(f.lineno, 123)
649 self.assertEqual(f.all_frames, True)
650
651 # parameters passed by keyword
652 f = tracemalloc.Filter(inclusive=False, filename_pattern="test.py", lineno=123, all_frames=True)
653 self.assertEqual(f.inclusive, False)
654 self.assertEqual(f.filename_pattern, "test.py")
655 self.assertEqual(f.lineno, 123)
656 self.assertEqual(f.all_frames, True)
657
658 # read-only attribute
659 self.assertRaises(AttributeError, setattr, f, "filename_pattern", "abc")
660
661 def test_filter_match(self):
662 # filter without line number
663 f = tracemalloc.Filter(True, "abc")
664 self.assertTrue(f._match_frame("abc", 0))
665 self.assertTrue(f._match_frame("abc", 5))
666 self.assertTrue(f._match_frame("abc", 10))
667 self.assertFalse(f._match_frame("12356", 0))
668 self.assertFalse(f._match_frame("12356", 5))
669 self.assertFalse(f._match_frame("12356", 10))
670
671 f = tracemalloc.Filter(False, "abc")
672 self.assertFalse(f._match_frame("abc", 0))
673 self.assertFalse(f._match_frame("abc", 5))
674 self.assertFalse(f._match_frame("abc", 10))
675 self.assertTrue(f._match_frame("12356", 0))
676 self.assertTrue(f._match_frame("12356", 5))
677 self.assertTrue(f._match_frame("12356", 10))
678
679 # filter with line number > 0
680 f = tracemalloc.Filter(True, "abc", 5)
681 self.assertFalse(f._match_frame("abc", 0))
682 self.assertTrue(f._match_frame("abc", 5))
683 self.assertFalse(f._match_frame("abc", 10))
684 self.assertFalse(f._match_frame("12356", 0))
685 self.assertFalse(f._match_frame("12356", 5))
686 self.assertFalse(f._match_frame("12356", 10))
687
688 f = tracemalloc.Filter(False, "abc", 5)
689 self.assertTrue(f._match_frame("abc", 0))
690 self.assertFalse(f._match_frame("abc", 5))
691 self.assertTrue(f._match_frame("abc", 10))
692 self.assertTrue(f._match_frame("12356", 0))
693 self.assertTrue(f._match_frame("12356", 5))
694 self.assertTrue(f._match_frame("12356", 10))
695
696 # filter with line number 0
697 f = tracemalloc.Filter(True, "abc", 0)
698 self.assertTrue(f._match_frame("abc", 0))
699 self.assertFalse(f._match_frame("abc", 5))
700 self.assertFalse(f._match_frame("abc", 10))
701 self.assertFalse(f._match_frame("12356", 0))
702 self.assertFalse(f._match_frame("12356", 5))
703 self.assertFalse(f._match_frame("12356", 10))
704
705 f = tracemalloc.Filter(False, "abc", 0)
706 self.assertFalse(f._match_frame("abc", 0))
707 self.assertTrue(f._match_frame("abc", 5))
708 self.assertTrue(f._match_frame("abc", 10))
709 self.assertTrue(f._match_frame("12356", 0))
710 self.assertTrue(f._match_frame("12356", 5))
711 self.assertTrue(f._match_frame("12356", 10))
712
713 def test_filter_match_filename(self):
714 def fnmatch(inclusive, filename, pattern):
715 f = tracemalloc.Filter(inclusive, pattern)
716 return f._match_frame(filename, 0)
717
718 self.assertTrue(fnmatch(True, "abc", "abc"))
719 self.assertFalse(fnmatch(True, "12356", "abc"))
720 self.assertFalse(fnmatch(True, "<unknown>", "abc"))
721
722 self.assertFalse(fnmatch(False, "abc", "abc"))
723 self.assertTrue(fnmatch(False, "12356", "abc"))
724 self.assertTrue(fnmatch(False, "<unknown>", "abc"))
725
726 def test_filter_match_filename_joker(self):
727 def fnmatch(filename, pattern):
728 filter = tracemalloc.Filter(True, pattern)
729 return filter._match_frame(filename, 0)
730
731 # empty string
732 self.assertFalse(fnmatch('abc', ''))
733 self.assertFalse(fnmatch('', 'abc'))
734 self.assertTrue(fnmatch('', ''))
735 self.assertTrue(fnmatch('', '*'))
736
737 # no *
738 self.assertTrue(fnmatch('abc', 'abc'))
739 self.assertFalse(fnmatch('abc', 'abcd'))
740 self.assertFalse(fnmatch('abc', 'def'))
741
742 # a*
743 self.assertTrue(fnmatch('abc', 'a*'))
744 self.assertTrue(fnmatch('abc', 'abc*'))
745 self.assertFalse(fnmatch('abc', 'b*'))
746 self.assertFalse(fnmatch('abc', 'abcd*'))
747
748 # a*b
749 self.assertTrue(fnmatch('abc', 'a*c'))
750 self.assertTrue(fnmatch('abcdcx', 'a*cx'))
751 self.assertFalse(fnmatch('abb', 'a*c'))
752 self.assertFalse(fnmatch('abcdce', 'a*cx'))
753
754 # a*b*c
755 self.assertTrue(fnmatch('abcde', 'a*c*e'))
756 self.assertTrue(fnmatch('abcbdefeg', 'a*bd*eg'))
757 self.assertFalse(fnmatch('abcdd', 'a*c*e'))
758 self.assertFalse(fnmatch('abcbdefef', 'a*bd*eg'))
759
Brett Cannonf299abd2015-04-13 14:21:02 -0400760 # replace .pyc suffix with .py
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100761 self.assertTrue(fnmatch('a.pyc', 'a.py'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100762 self.assertTrue(fnmatch('a.py', 'a.pyc'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100763
764 if os.name == 'nt':
765 # case insensitive
766 self.assertTrue(fnmatch('aBC', 'ABc'))
767 self.assertTrue(fnmatch('aBcDe', 'Ab*dE'))
768
769 self.assertTrue(fnmatch('a.pyc', 'a.PY'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100770 self.assertTrue(fnmatch('a.py', 'a.PYC'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100771 else:
772 # case sensitive
773 self.assertFalse(fnmatch('aBC', 'ABc'))
774 self.assertFalse(fnmatch('aBcDe', 'Ab*dE'))
775
776 self.assertFalse(fnmatch('a.pyc', 'a.PY'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100777 self.assertFalse(fnmatch('a.py', 'a.PYC'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100778
779 if os.name == 'nt':
780 # normalize alternate separator "/" to the standard separator "\"
781 self.assertTrue(fnmatch(r'a/b', r'a\b'))
782 self.assertTrue(fnmatch(r'a\b', r'a/b'))
783 self.assertTrue(fnmatch(r'a/b\c', r'a\b/c'))
784 self.assertTrue(fnmatch(r'a/b/c', r'a\b\c'))
785 else:
786 # there is no alternate separator
787 self.assertFalse(fnmatch(r'a/b', r'a\b'))
788 self.assertFalse(fnmatch(r'a\b', r'a/b'))
789 self.assertFalse(fnmatch(r'a/b\c', r'a\b/c'))
790 self.assertFalse(fnmatch(r'a/b/c', r'a\b\c'))
791
Zachary Wared9b25bb2015-05-13 00:27:01 -0500792 # as of 3.5, .pyo is no longer munged to .py
793 self.assertFalse(fnmatch('a.pyo', 'a.py'))
794
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100795 def test_filter_match_trace(self):
796 t1 = (("a.py", 2), ("b.py", 3))
797 t2 = (("b.py", 4), ("b.py", 5))
798 t3 = (("c.py", 5), ('<unknown>', 0))
799 unknown = (('<unknown>', 0),)
800
801 f = tracemalloc.Filter(True, "b.py", all_frames=True)
802 self.assertTrue(f._match_traceback(t1))
803 self.assertTrue(f._match_traceback(t2))
804 self.assertFalse(f._match_traceback(t3))
805 self.assertFalse(f._match_traceback(unknown))
806
807 f = tracemalloc.Filter(True, "b.py", all_frames=False)
808 self.assertFalse(f._match_traceback(t1))
809 self.assertTrue(f._match_traceback(t2))
810 self.assertFalse(f._match_traceback(t3))
811 self.assertFalse(f._match_traceback(unknown))
812
813 f = tracemalloc.Filter(False, "b.py", all_frames=True)
814 self.assertFalse(f._match_traceback(t1))
815 self.assertFalse(f._match_traceback(t2))
816 self.assertTrue(f._match_traceback(t3))
817 self.assertTrue(f._match_traceback(unknown))
818
819 f = tracemalloc.Filter(False, "b.py", all_frames=False)
820 self.assertTrue(f._match_traceback(t1))
821 self.assertFalse(f._match_traceback(t2))
822 self.assertTrue(f._match_traceback(t3))
823 self.assertTrue(f._match_traceback(unknown))
824
825 f = tracemalloc.Filter(False, "<unknown>", all_frames=False)
826 self.assertTrue(f._match_traceback(t1))
827 self.assertTrue(f._match_traceback(t2))
828 self.assertTrue(f._match_traceback(t3))
829 self.assertFalse(f._match_traceback(unknown))
830
831 f = tracemalloc.Filter(True, "<unknown>", all_frames=True)
832 self.assertFalse(f._match_traceback(t1))
833 self.assertFalse(f._match_traceback(t2))
834 self.assertTrue(f._match_traceback(t3))
835 self.assertTrue(f._match_traceback(unknown))
836
837 f = tracemalloc.Filter(False, "<unknown>", all_frames=True)
838 self.assertTrue(f._match_traceback(t1))
839 self.assertTrue(f._match_traceback(t2))
840 self.assertFalse(f._match_traceback(t3))
841 self.assertFalse(f._match_traceback(unknown))
842
843
844class TestCommandLine(unittest.TestCase):
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800845 def test_env_var_disabled_by_default(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100846 # not tracing by default
847 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
848 ok, stdout, stderr = assert_python_ok('-c', code)
849 stdout = stdout.rstrip()
850 self.assertEqual(stdout, b'False')
851
Berker Peksagce643912015-05-06 06:33:17 +0300852 @unittest.skipIf(interpreter_requires_environment(),
Gregory P. Smithb9a3dd92015-02-04 00:59:40 -0800853 'Cannot run -E tests when PYTHON env vars are required.')
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800854 def test_env_var_ignored_with_E(self):
855 """PYTHON* environment variables must be ignored when -E is present."""
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100856 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
857 ok, stdout, stderr = assert_python_ok('-E', '-c', code, PYTHONTRACEMALLOC='1')
858 stdout = stdout.rstrip()
859 self.assertEqual(stdout, b'False')
860
Victor Stinner60b04c92018-07-25 19:23:53 +0200861 def test_env_var_disabled(self):
862 # tracing at startup
863 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
864 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='0')
865 stdout = stdout.rstrip()
866 self.assertEqual(stdout, b'False')
867
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800868 def test_env_var_enabled_at_startup(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100869 # tracing at startup
870 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
871 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='1')
872 stdout = stdout.rstrip()
873 self.assertEqual(stdout, b'True')
874
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800875 def test_env_limit(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100876 # start and set the number of frames
877 code = 'import tracemalloc; print(tracemalloc.get_traceback_limit())'
878 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='10')
879 stdout = stdout.rstrip()
880 self.assertEqual(stdout, b'10')
881
Victor Stinnera7368ac2017-11-15 18:11:45 -0800882 def check_env_var_invalid(self, nframe):
883 with support.SuppressCrashReport():
884 ok, stdout, stderr = assert_python_failure(
885 '-c', 'pass',
886 PYTHONTRACEMALLOC=str(nframe))
887
888 if b'ValueError: the number of frames must be in range' in stderr:
889 return
890 if b'PYTHONTRACEMALLOC: invalid number of frames' in stderr:
891 return
Min ho Kim39d87b52019-08-31 06:21:19 +1000892 self.fail(f"unexpected output: {stderr!a}")
Victor Stinnera7368ac2017-11-15 18:11:45 -0800893
894
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100895 def test_env_var_invalid(self):
Victor Stinner60b04c92018-07-25 19:23:53 +0200896 for nframe in INVALID_NFRAME:
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100897 with self.subTest(nframe=nframe):
Victor Stinnera7368ac2017-11-15 18:11:45 -0800898 self.check_env_var_invalid(nframe)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100899
900 def test_sys_xoptions(self):
901 for xoptions, nframe in (
902 ('tracemalloc', 1),
903 ('tracemalloc=1', 1),
904 ('tracemalloc=15', 15),
905 ):
906 with self.subTest(xoptions=xoptions, nframe=nframe):
907 code = 'import tracemalloc; print(tracemalloc.get_traceback_limit())'
908 ok, stdout, stderr = assert_python_ok('-X', xoptions, '-c', code)
909 stdout = stdout.rstrip()
910 self.assertEqual(stdout, str(nframe).encode('ascii'))
911
Victor Stinnera7368ac2017-11-15 18:11:45 -0800912 def check_sys_xoptions_invalid(self, nframe):
913 args = ('-X', 'tracemalloc=%s' % nframe, '-c', 'pass')
914 with support.SuppressCrashReport():
915 ok, stdout, stderr = assert_python_failure(*args)
916
917 if b'ValueError: the number of frames must be in range' in stderr:
918 return
919 if b'-X tracemalloc=NFRAME: invalid number of frames' in stderr:
920 return
Min ho Kim39d87b52019-08-31 06:21:19 +1000921 self.fail(f"unexpected output: {stderr!a}")
Victor Stinnera7368ac2017-11-15 18:11:45 -0800922
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100923 def test_sys_xoptions_invalid(self):
Victor Stinner60b04c92018-07-25 19:23:53 +0200924 for nframe in INVALID_NFRAME:
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100925 with self.subTest(nframe=nframe):
Victor Stinnera7368ac2017-11-15 18:11:45 -0800926 self.check_sys_xoptions_invalid(nframe)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100927
Serhiy Storchaka24c738a2017-03-19 20:20:10 +0200928 @unittest.skipIf(_testcapi is None, 'need _testcapi')
Victor Stinner8dd49fe2014-06-02 21:36:59 +0200929 def test_pymem_alloc0(self):
930 # Issue #21639: Check that PyMem_Malloc(0) with tracemalloc enabled
931 # does not crash.
932 code = 'import _testcapi; _testcapi.test_pymem_alloc0(); 1'
933 assert_python_ok('-X', 'tracemalloc', '-c', code)
934
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100935
Victor Stinner10b73e12016-03-22 13:39:05 +0100936@unittest.skipIf(_testcapi is None, 'need _testcapi')
937class TestCAPI(unittest.TestCase):
938 maxDiff = 80 * 20
939
940 def setUp(self):
941 if tracemalloc.is_tracing():
942 self.skipTest("tracemalloc must be stopped before the test")
943
944 self.domain = 5
945 self.size = 123
946 self.obj = allocate_bytes(self.size)[0]
947
948 # for the type "object", id(obj) is the address of its memory block.
949 # This type is not tracked by the garbage collector
950 self.ptr = id(self.obj)
951
952 def tearDown(self):
953 tracemalloc.stop()
954
955 def get_traceback(self):
956 frames = _testcapi.tracemalloc_get_traceback(self.domain, self.ptr)
957 if frames is not None:
958 return tracemalloc.Traceback(frames)
959 else:
960 return None
961
962 def track(self, release_gil=False, nframe=1):
Serhiy Storchakada8d72c2018-09-17 15:17:29 +0300963 frames = get_frames(nframe, 1)
Victor Stinner10b73e12016-03-22 13:39:05 +0100964 _testcapi.tracemalloc_track(self.domain, self.ptr, self.size,
965 release_gil)
966 return frames
967
968 def untrack(self):
969 _testcapi.tracemalloc_untrack(self.domain, self.ptr)
970
971 def get_traced_memory(self):
972 # Get the traced size in the domain
973 snapshot = tracemalloc.take_snapshot()
974 domain_filter = tracemalloc.DomainFilter(True, self.domain)
975 snapshot = snapshot.filter_traces([domain_filter])
976 return sum(trace.size for trace in snapshot.traces)
977
978 def check_track(self, release_gil):
979 nframe = 5
980 tracemalloc.start(nframe)
981
982 size = tracemalloc.get_traced_memory()[0]
983
984 frames = self.track(release_gil, nframe)
985 self.assertEqual(self.get_traceback(),
986 tracemalloc.Traceback(frames))
987
988 self.assertEqual(self.get_traced_memory(), self.size)
989
990 def test_track(self):
991 self.check_track(False)
992
993 def test_track_without_gil(self):
994 # check that calling _PyTraceMalloc_Track() without holding the GIL
995 # works too
996 self.check_track(True)
997
998 def test_track_already_tracked(self):
999 nframe = 5
1000 tracemalloc.start(nframe)
1001
1002 # track a first time
1003 self.track()
1004
1005 # calling _PyTraceMalloc_Track() must remove the old trace and add
1006 # a new trace with the new traceback
1007 frames = self.track(nframe=nframe)
1008 self.assertEqual(self.get_traceback(),
1009 tracemalloc.Traceback(frames))
1010
1011 def test_untrack(self):
1012 tracemalloc.start()
1013
1014 self.track()
1015 self.assertIsNotNone(self.get_traceback())
1016 self.assertEqual(self.get_traced_memory(), self.size)
1017
1018 # untrack must remove the trace
1019 self.untrack()
1020 self.assertIsNone(self.get_traceback())
1021 self.assertEqual(self.get_traced_memory(), 0)
1022
1023 # calling _PyTraceMalloc_Untrack() multiple times must not crash
1024 self.untrack()
1025 self.untrack()
1026
1027 def test_stop_track(self):
1028 tracemalloc.start()
1029 tracemalloc.stop()
1030
1031 with self.assertRaises(RuntimeError):
1032 self.track()
1033 self.assertIsNone(self.get_traceback())
1034
1035 def test_stop_untrack(self):
1036 tracemalloc.start()
1037 self.track()
1038
1039 tracemalloc.stop()
1040 with self.assertRaises(RuntimeError):
1041 self.untrack()
1042
1043
Victor Stinnered3b0bc2013-11-23 12:27:24 +01001044def test_main():
1045 support.run_unittest(
1046 TestTracemallocEnabled,
1047 TestSnapshot,
1048 TestFilters,
1049 TestCommandLine,
Victor Stinner10b73e12016-03-22 13:39:05 +01001050 TestCAPI,
Victor Stinnered3b0bc2013-11-23 12:27:24 +01001051 )
1052
1053if __name__ == "__main__":
1054 test_main()