blob: 780942a80615563203731afb73f5880c54725f96 [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'')
18
Victor Stinner10b73e12016-03-22 13:39:05 +010019
Victor Stinnered3b0bc2013-11-23 12:27:24 +010020def get_frames(nframe, lineno_delta):
21 frames = []
22 frame = sys._getframe(1)
23 for index in range(nframe):
24 code = frame.f_code
25 lineno = frame.f_lineno + lineno_delta
26 frames.append((code.co_filename, lineno))
27 lineno_delta = 0
28 frame = frame.f_back
29 if frame is None:
30 break
31 return tuple(frames)
32
33def allocate_bytes(size):
34 nframe = tracemalloc.get_traceback_limit()
35 bytes_len = (size - EMPTY_STRING_SIZE)
36 frames = get_frames(nframe, 1)
37 data = b'x' * bytes_len
38 return data, tracemalloc.Traceback(frames)
39
40def create_snapshots():
41 traceback_limit = 2
42
Victor Stinnere492ae52016-03-22 12:58:23 +010043 # _tracemalloc._get_traces() returns a list of (domain, size,
44 # traceback_frames) tuples. traceback_frames is a tuple of (filename,
45 # line_number) tuples.
Victor Stinnered3b0bc2013-11-23 12:27:24 +010046 raw_traces = [
Victor Stinnere492ae52016-03-22 12:58:23 +010047 (0, 10, (('a.py', 2), ('b.py', 4))),
48 (0, 10, (('a.py', 2), ('b.py', 4))),
49 (0, 10, (('a.py', 2), ('b.py', 4))),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010050
Victor Stinnere492ae52016-03-22 12:58:23 +010051 (1, 2, (('a.py', 5), ('b.py', 4))),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010052
Victor Stinnere492ae52016-03-22 12:58:23 +010053 (2, 66, (('b.py', 1),)),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010054
Victor Stinnere492ae52016-03-22 12:58:23 +010055 (3, 7, (('<unknown>', 0),)),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010056 ]
57 snapshot = tracemalloc.Snapshot(raw_traces, traceback_limit)
58
59 raw_traces2 = [
Victor Stinnere492ae52016-03-22 12:58:23 +010060 (0, 10, (('a.py', 2), ('b.py', 4))),
61 (0, 10, (('a.py', 2), ('b.py', 4))),
62 (0, 10, (('a.py', 2), ('b.py', 4))),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010063
Victor Stinnere492ae52016-03-22 12:58:23 +010064 (2, 2, (('a.py', 5), ('b.py', 4))),
65 (2, 5000, (('a.py', 5), ('b.py', 4))),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010066
Victor Stinnere492ae52016-03-22 12:58:23 +010067 (4, 400, (('c.py', 578),)),
Victor Stinnered3b0bc2013-11-23 12:27:24 +010068 ]
69 snapshot2 = tracemalloc.Snapshot(raw_traces2, traceback_limit)
70
71 return (snapshot, snapshot2)
72
73def frame(filename, lineno):
74 return tracemalloc._Frame((filename, lineno))
75
76def traceback(*frames):
77 return tracemalloc.Traceback(frames)
78
79def traceback_lineno(filename, lineno):
80 return traceback((filename, lineno))
81
82def traceback_filename(filename):
83 return traceback_lineno(filename, 0)
84
85
86class TestTracemallocEnabled(unittest.TestCase):
87 def setUp(self):
88 if tracemalloc.is_tracing():
89 self.skipTest("tracemalloc must be stopped before the test")
90
Victor Stinner3728d6c2013-11-23 12:37:20 +010091 tracemalloc.start(1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +010092
93 def tearDown(self):
94 tracemalloc.stop()
95
96 def test_get_tracemalloc_memory(self):
97 data = [allocate_bytes(123) for count in range(1000)]
98 size = tracemalloc.get_tracemalloc_memory()
99 self.assertGreaterEqual(size, 0)
100
101 tracemalloc.clear_traces()
102 size2 = tracemalloc.get_tracemalloc_memory()
103 self.assertGreaterEqual(size2, 0)
104 self.assertLessEqual(size2, size)
105
106 def test_get_object_traceback(self):
107 tracemalloc.clear_traces()
108 obj_size = 12345
109 obj, obj_traceback = allocate_bytes(obj_size)
110 traceback = tracemalloc.get_object_traceback(obj)
111 self.assertEqual(traceback, obj_traceback)
112
113 def test_set_traceback_limit(self):
114 obj_size = 10
115
Victor Stinner3728d6c2013-11-23 12:37:20 +0100116 tracemalloc.stop()
117 self.assertRaises(ValueError, tracemalloc.start, -1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100118
Victor Stinner3728d6c2013-11-23 12:37:20 +0100119 tracemalloc.stop()
120 tracemalloc.start(10)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100121 obj2, obj2_traceback = allocate_bytes(obj_size)
122 traceback = tracemalloc.get_object_traceback(obj2)
123 self.assertEqual(len(traceback), 10)
124 self.assertEqual(traceback, obj2_traceback)
125
Victor Stinner3728d6c2013-11-23 12:37:20 +0100126 tracemalloc.stop()
127 tracemalloc.start(1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100128 obj, obj_traceback = allocate_bytes(obj_size)
129 traceback = tracemalloc.get_object_traceback(obj)
130 self.assertEqual(len(traceback), 1)
131 self.assertEqual(traceback, obj_traceback)
132
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100133 def find_trace(self, traces, traceback):
134 for trace in traces:
Victor Stinnere492ae52016-03-22 12:58:23 +0100135 if trace[2] == traceback._frames:
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100136 return trace
137
138 self.fail("trace not found")
139
140 def test_get_traces(self):
141 tracemalloc.clear_traces()
142 obj_size = 12345
143 obj, obj_traceback = allocate_bytes(obj_size)
144
145 traces = tracemalloc._get_traces()
146 trace = self.find_trace(traces, obj_traceback)
147
148 self.assertIsInstance(trace, tuple)
Victor Stinnere492ae52016-03-22 12:58:23 +0100149 domain, size, traceback = trace
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100150 self.assertEqual(size, obj_size)
151 self.assertEqual(traceback, obj_traceback._frames)
152
153 tracemalloc.stop()
154 self.assertEqual(tracemalloc._get_traces(), [])
155
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100156 def test_get_traces_intern_traceback(self):
157 # dummy wrappers to get more useful and identical frames in the traceback
158 def allocate_bytes2(size):
159 return allocate_bytes(size)
160 def allocate_bytes3(size):
161 return allocate_bytes2(size)
162 def allocate_bytes4(size):
163 return allocate_bytes3(size)
164
165 # Ensure that two identical tracebacks are not duplicated
Victor Stinner3728d6c2013-11-23 12:37:20 +0100166 tracemalloc.stop()
167 tracemalloc.start(4)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100168 obj_size = 123
169 obj1, obj1_traceback = allocate_bytes4(obj_size)
170 obj2, obj2_traceback = allocate_bytes4(obj_size)
171
172 traces = tracemalloc._get_traces()
173
174 trace1 = self.find_trace(traces, obj1_traceback)
175 trace2 = self.find_trace(traces, obj2_traceback)
Victor Stinnere492ae52016-03-22 12:58:23 +0100176 domain1, size1, traceback1 = trace1
177 domain2, size2, traceback2 = trace2
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100178 self.assertIs(traceback2, traceback1)
179
180 def test_get_traced_memory(self):
181 # Python allocates some internals objects, so the test must tolerate
182 # a small difference between the expected size and the real usage
183 max_error = 2048
184
185 # allocate one object
186 obj_size = 1024 * 1024
187 tracemalloc.clear_traces()
188 obj, obj_traceback = allocate_bytes(obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100189 size, peak_size = tracemalloc.get_traced_memory()
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100190 self.assertGreaterEqual(size, obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100191 self.assertGreaterEqual(peak_size, size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100192
193 self.assertLessEqual(size - obj_size, max_error)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100194 self.assertLessEqual(peak_size - size, max_error)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100195
196 # destroy the object
197 obj = None
Victor Stinner3c0481d2013-11-27 21:39:49 +0100198 size2, peak_size2 = tracemalloc.get_traced_memory()
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100199 self.assertLess(size2, size)
200 self.assertGreaterEqual(size - size2, obj_size - max_error)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100201 self.assertGreaterEqual(peak_size2, peak_size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100202
203 # clear_traces() must reset traced memory counters
204 tracemalloc.clear_traces()
205 self.assertEqual(tracemalloc.get_traced_memory(), (0, 0))
206
207 # allocate another object
208 obj, obj_traceback = allocate_bytes(obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100209 size, peak_size = tracemalloc.get_traced_memory()
210 self.assertGreaterEqual(size, obj_size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100211
Victor Stinnera89ecc72013-11-25 09:29:45 +0100212 # stop() also resets traced memory counters
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100213 tracemalloc.stop()
214 self.assertEqual(tracemalloc.get_traced_memory(), (0, 0))
215
216 def test_clear_traces(self):
217 obj, obj_traceback = allocate_bytes(123)
218 traceback = tracemalloc.get_object_traceback(obj)
219 self.assertIsNotNone(traceback)
220
221 tracemalloc.clear_traces()
222 traceback2 = tracemalloc.get_object_traceback(obj)
223 self.assertIsNone(traceback2)
224
225 def test_is_tracing(self):
226 tracemalloc.stop()
227 self.assertFalse(tracemalloc.is_tracing())
228
229 tracemalloc.start()
230 self.assertTrue(tracemalloc.is_tracing())
231
232 def test_snapshot(self):
233 obj, source = allocate_bytes(123)
234
235 # take a snapshot
236 snapshot = tracemalloc.take_snapshot()
237
238 # write on disk
239 snapshot.dump(support.TESTFN)
240 self.addCleanup(support.unlink, support.TESTFN)
241
242 # load from disk
243 snapshot2 = tracemalloc.Snapshot.load(support.TESTFN)
244 self.assertEqual(snapshot2.traces, snapshot.traces)
245
246 # tracemalloc must be tracing memory allocations to take a snapshot
247 tracemalloc.stop()
248 with self.assertRaises(RuntimeError) as cm:
249 tracemalloc.take_snapshot()
250 self.assertEqual(str(cm.exception),
251 "the tracemalloc module must be tracing memory "
252 "allocations to take a snapshot")
253
254 def test_snapshot_save_attr(self):
255 # take a snapshot with a new attribute
256 snapshot = tracemalloc.take_snapshot()
257 snapshot.test_attr = "new"
258 snapshot.dump(support.TESTFN)
259 self.addCleanup(support.unlink, support.TESTFN)
260
Martin Panter8f265652016-04-19 04:03:41 +0000261 # load() should recreate the attribute
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100262 snapshot2 = tracemalloc.Snapshot.load(support.TESTFN)
263 self.assertEqual(snapshot2.test_attr, "new")
264
265 def fork_child(self):
266 if not tracemalloc.is_tracing():
267 return 2
268
269 obj_size = 12345
270 obj, obj_traceback = allocate_bytes(obj_size)
271 traceback = tracemalloc.get_object_traceback(obj)
272 if traceback is None:
273 return 3
274
275 # everything is fine
276 return 0
277
278 @unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork()')
279 def test_fork(self):
280 # check that tracemalloc is still working after fork
281 pid = os.fork()
282 if not pid:
283 # child
284 exitcode = 1
285 try:
286 exitcode = self.fork_child()
287 finally:
288 os._exit(exitcode)
289 else:
290 pid2, status = os.waitpid(pid, 0)
291 self.assertTrue(os.WIFEXITED(status))
292 exitcode = os.WEXITSTATUS(status)
293 self.assertEqual(exitcode, 0)
294
295
296class TestSnapshot(unittest.TestCase):
297 maxDiff = 4000
298
299 def test_create_snapshot(self):
Victor Stinnere492ae52016-03-22 12:58:23 +0100300 raw_traces = [(0, 5, (('a.py', 2),))]
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100301
302 with contextlib.ExitStack() as stack:
303 stack.enter_context(patch.object(tracemalloc, 'is_tracing',
304 return_value=True))
305 stack.enter_context(patch.object(tracemalloc, 'get_traceback_limit',
306 return_value=5))
307 stack.enter_context(patch.object(tracemalloc, '_get_traces',
308 return_value=raw_traces))
309
310 snapshot = tracemalloc.take_snapshot()
311 self.assertEqual(snapshot.traceback_limit, 5)
312 self.assertEqual(len(snapshot.traces), 1)
313 trace = snapshot.traces[0]
314 self.assertEqual(trace.size, 5)
315 self.assertEqual(len(trace.traceback), 1)
316 self.assertEqual(trace.traceback[0].filename, 'a.py')
317 self.assertEqual(trace.traceback[0].lineno, 2)
318
319 def test_filter_traces(self):
320 snapshot, snapshot2 = create_snapshots()
321 filter1 = tracemalloc.Filter(False, "b.py")
322 filter2 = tracemalloc.Filter(True, "a.py", 2)
323 filter3 = tracemalloc.Filter(True, "a.py", 5)
324
325 original_traces = list(snapshot.traces._traces)
326
327 # exclude b.py
328 snapshot3 = snapshot.filter_traces((filter1,))
329 self.assertEqual(snapshot3.traces._traces, [
Victor Stinnere492ae52016-03-22 12:58:23 +0100330 (0, 10, (('a.py', 2), ('b.py', 4))),
331 (0, 10, (('a.py', 2), ('b.py', 4))),
332 (0, 10, (('a.py', 2), ('b.py', 4))),
333 (1, 2, (('a.py', 5), ('b.py', 4))),
334 (3, 7, (('<unknown>', 0),)),
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100335 ])
336
337 # filter_traces() must not touch the original snapshot
338 self.assertEqual(snapshot.traces._traces, original_traces)
339
340 # only include two lines of a.py
341 snapshot4 = snapshot3.filter_traces((filter2, filter3))
342 self.assertEqual(snapshot4.traces._traces, [
Victor Stinnere492ae52016-03-22 12:58:23 +0100343 (0, 10, (('a.py', 2), ('b.py', 4))),
344 (0, 10, (('a.py', 2), ('b.py', 4))),
345 (0, 10, (('a.py', 2), ('b.py', 4))),
346 (1, 2, (('a.py', 5), ('b.py', 4))),
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100347 ])
348
349 # No filter: just duplicate the snapshot
350 snapshot5 = snapshot.filter_traces(())
351 self.assertIsNot(snapshot5, snapshot)
352 self.assertIsNot(snapshot5.traces, snapshot.traces)
353 self.assertEqual(snapshot5.traces, snapshot.traces)
354
Victor Stinner8ce8ff92014-03-10 11:05:07 +0100355 self.assertRaises(TypeError, snapshot.filter_traces, filter1)
356
Victor Stinnere492ae52016-03-22 12:58:23 +0100357 def test_filter_traces_domain(self):
358 snapshot, snapshot2 = create_snapshots()
359 filter1 = tracemalloc.Filter(False, "a.py", domain=1)
360 filter2 = tracemalloc.Filter(True, "a.py", domain=1)
361
362 original_traces = list(snapshot.traces._traces)
363
364 # exclude a.py of domain 1
365 snapshot3 = snapshot.filter_traces((filter1,))
366 self.assertEqual(snapshot3.traces._traces, [
367 (0, 10, (('a.py', 2), ('b.py', 4))),
368 (0, 10, (('a.py', 2), ('b.py', 4))),
369 (0, 10, (('a.py', 2), ('b.py', 4))),
370 (2, 66, (('b.py', 1),)),
371 (3, 7, (('<unknown>', 0),)),
372 ])
373
374 # include domain 1
375 snapshot3 = snapshot.filter_traces((filter1,))
376 self.assertEqual(snapshot3.traces._traces, [
377 (0, 10, (('a.py', 2), ('b.py', 4))),
378 (0, 10, (('a.py', 2), ('b.py', 4))),
379 (0, 10, (('a.py', 2), ('b.py', 4))),
380 (2, 66, (('b.py', 1),)),
381 (3, 7, (('<unknown>', 0),)),
382 ])
383
384 def test_filter_traces_domain_filter(self):
385 snapshot, snapshot2 = create_snapshots()
386 filter1 = tracemalloc.DomainFilter(False, domain=3)
387 filter2 = tracemalloc.DomainFilter(True, domain=3)
388
389 # exclude domain 2
390 snapshot3 = snapshot.filter_traces((filter1,))
391 self.assertEqual(snapshot3.traces._traces, [
392 (0, 10, (('a.py', 2), ('b.py', 4))),
393 (0, 10, (('a.py', 2), ('b.py', 4))),
394 (0, 10, (('a.py', 2), ('b.py', 4))),
395 (1, 2, (('a.py', 5), ('b.py', 4))),
396 (2, 66, (('b.py', 1),)),
397 ])
398
399 # include domain 2
400 snapshot3 = snapshot.filter_traces((filter2,))
401 self.assertEqual(snapshot3.traces._traces, [
402 (3, 7, (('<unknown>', 0),)),
403 ])
404
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100405 def test_snapshot_group_by_line(self):
406 snapshot, snapshot2 = create_snapshots()
407 tb_0 = traceback_lineno('<unknown>', 0)
408 tb_a_2 = traceback_lineno('a.py', 2)
409 tb_a_5 = traceback_lineno('a.py', 5)
410 tb_b_1 = traceback_lineno('b.py', 1)
411 tb_c_578 = traceback_lineno('c.py', 578)
412
413 # stats per file and line
414 stats1 = snapshot.statistics('lineno')
415 self.assertEqual(stats1, [
416 tracemalloc.Statistic(tb_b_1, 66, 1),
417 tracemalloc.Statistic(tb_a_2, 30, 3),
418 tracemalloc.Statistic(tb_0, 7, 1),
419 tracemalloc.Statistic(tb_a_5, 2, 1),
420 ])
421
422 # stats per file and line (2)
423 stats2 = snapshot2.statistics('lineno')
424 self.assertEqual(stats2, [
425 tracemalloc.Statistic(tb_a_5, 5002, 2),
426 tracemalloc.Statistic(tb_c_578, 400, 1),
427 tracemalloc.Statistic(tb_a_2, 30, 3),
428 ])
429
430 # stats diff per file and line
431 statistics = snapshot2.compare_to(snapshot, 'lineno')
432 self.assertEqual(statistics, [
433 tracemalloc.StatisticDiff(tb_a_5, 5002, 5000, 2, 1),
434 tracemalloc.StatisticDiff(tb_c_578, 400, 400, 1, 1),
435 tracemalloc.StatisticDiff(tb_b_1, 0, -66, 0, -1),
436 tracemalloc.StatisticDiff(tb_0, 0, -7, 0, -1),
437 tracemalloc.StatisticDiff(tb_a_2, 30, 0, 3, 0),
438 ])
439
440 def test_snapshot_group_by_file(self):
441 snapshot, snapshot2 = create_snapshots()
442 tb_0 = traceback_filename('<unknown>')
443 tb_a = traceback_filename('a.py')
444 tb_b = traceback_filename('b.py')
445 tb_c = traceback_filename('c.py')
446
447 # stats per file
448 stats1 = snapshot.statistics('filename')
449 self.assertEqual(stats1, [
450 tracemalloc.Statistic(tb_b, 66, 1),
451 tracemalloc.Statistic(tb_a, 32, 4),
452 tracemalloc.Statistic(tb_0, 7, 1),
453 ])
454
455 # stats per file (2)
456 stats2 = snapshot2.statistics('filename')
457 self.assertEqual(stats2, [
458 tracemalloc.Statistic(tb_a, 5032, 5),
459 tracemalloc.Statistic(tb_c, 400, 1),
460 ])
461
462 # stats diff per file
463 diff = snapshot2.compare_to(snapshot, 'filename')
464 self.assertEqual(diff, [
465 tracemalloc.StatisticDiff(tb_a, 5032, 5000, 5, 1),
466 tracemalloc.StatisticDiff(tb_c, 400, 400, 1, 1),
467 tracemalloc.StatisticDiff(tb_b, 0, -66, 0, -1),
468 tracemalloc.StatisticDiff(tb_0, 0, -7, 0, -1),
469 ])
470
471 def test_snapshot_group_by_traceback(self):
472 snapshot, snapshot2 = create_snapshots()
473
474 # stats per file
475 tb1 = traceback(('a.py', 2), ('b.py', 4))
476 tb2 = traceback(('a.py', 5), ('b.py', 4))
477 tb3 = traceback(('b.py', 1))
478 tb4 = traceback(('<unknown>', 0))
479 stats1 = snapshot.statistics('traceback')
480 self.assertEqual(stats1, [
481 tracemalloc.Statistic(tb3, 66, 1),
482 tracemalloc.Statistic(tb1, 30, 3),
483 tracemalloc.Statistic(tb4, 7, 1),
484 tracemalloc.Statistic(tb2, 2, 1),
485 ])
486
487 # stats per file (2)
488 tb5 = traceback(('c.py', 578))
489 stats2 = snapshot2.statistics('traceback')
490 self.assertEqual(stats2, [
491 tracemalloc.Statistic(tb2, 5002, 2),
492 tracemalloc.Statistic(tb5, 400, 1),
493 tracemalloc.Statistic(tb1, 30, 3),
494 ])
495
496 # stats diff per file
497 diff = snapshot2.compare_to(snapshot, 'traceback')
498 self.assertEqual(diff, [
499 tracemalloc.StatisticDiff(tb2, 5002, 5000, 2, 1),
500 tracemalloc.StatisticDiff(tb5, 400, 400, 1, 1),
501 tracemalloc.StatisticDiff(tb3, 0, -66, 0, -1),
502 tracemalloc.StatisticDiff(tb4, 0, -7, 0, -1),
503 tracemalloc.StatisticDiff(tb1, 30, 0, 3, 0),
504 ])
505
506 self.assertRaises(ValueError,
507 snapshot.statistics, 'traceback', cumulative=True)
508
509 def test_snapshot_group_by_cumulative(self):
510 snapshot, snapshot2 = create_snapshots()
511 tb_0 = traceback_filename('<unknown>')
512 tb_a = traceback_filename('a.py')
513 tb_b = traceback_filename('b.py')
514 tb_a_2 = traceback_lineno('a.py', 2)
515 tb_a_5 = traceback_lineno('a.py', 5)
516 tb_b_1 = traceback_lineno('b.py', 1)
517 tb_b_4 = traceback_lineno('b.py', 4)
518
519 # per file
520 stats = snapshot.statistics('filename', True)
521 self.assertEqual(stats, [
522 tracemalloc.Statistic(tb_b, 98, 5),
523 tracemalloc.Statistic(tb_a, 32, 4),
524 tracemalloc.Statistic(tb_0, 7, 1),
525 ])
526
527 # per line
528 stats = snapshot.statistics('lineno', True)
529 self.assertEqual(stats, [
530 tracemalloc.Statistic(tb_b_1, 66, 1),
531 tracemalloc.Statistic(tb_b_4, 32, 4),
532 tracemalloc.Statistic(tb_a_2, 30, 3),
533 tracemalloc.Statistic(tb_0, 7, 1),
534 tracemalloc.Statistic(tb_a_5, 2, 1),
535 ])
536
537 def test_trace_format(self):
538 snapshot, snapshot2 = create_snapshots()
539 trace = snapshot.traces[0]
540 self.assertEqual(str(trace), 'a.py:2: 10 B')
541 traceback = trace.traceback
542 self.assertEqual(str(traceback), 'a.py:2')
543 frame = traceback[0]
544 self.assertEqual(str(frame), 'a.py:2')
545
546 def test_statistic_format(self):
547 snapshot, snapshot2 = create_snapshots()
548 stats = snapshot.statistics('lineno')
549 stat = stats[0]
550 self.assertEqual(str(stat),
551 'b.py:1: size=66 B, count=1, average=66 B')
552
553 def test_statistic_diff_format(self):
554 snapshot, snapshot2 = create_snapshots()
555 stats = snapshot2.compare_to(snapshot, 'lineno')
556 stat = stats[0]
557 self.assertEqual(str(stat),
558 'a.py:5: size=5002 B (+5000 B), count=2 (+1), average=2501 B')
559
Victor Stinner524be302014-02-01 04:07:02 +0100560 def test_slices(self):
561 snapshot, snapshot2 = create_snapshots()
562 self.assertEqual(snapshot.traces[:2],
563 (snapshot.traces[0], snapshot.traces[1]))
564
565 traceback = snapshot.traces[0].traceback
566 self.assertEqual(traceback[:2],
567 (traceback[0], traceback[1]))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100568
Victor Stinner23f628d2014-02-16 23:53:38 +0100569 def test_format_traceback(self):
570 snapshot, snapshot2 = create_snapshots()
571 def getline(filename, lineno):
572 return ' <%s, %s>' % (filename, lineno)
573 with unittest.mock.patch('tracemalloc.linecache.getline',
574 side_effect=getline):
575 tb = snapshot.traces[0].traceback
576 self.assertEqual(tb.format(),
577 [' File "a.py", line 2',
578 ' <a.py, 2>',
579 ' File "b.py", line 4',
580 ' <b.py, 4>'])
581
582 self.assertEqual(tb.format(limit=1),
583 [' File "a.py", line 2',
584 ' <a.py, 2>'])
585
586 self.assertEqual(tb.format(limit=-1),
587 [])
588
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100589
590class TestFilters(unittest.TestCase):
591 maxDiff = 2048
592
593 def test_filter_attributes(self):
594 # test default values
595 f = tracemalloc.Filter(True, "abc")
596 self.assertEqual(f.inclusive, True)
597 self.assertEqual(f.filename_pattern, "abc")
598 self.assertIsNone(f.lineno)
599 self.assertEqual(f.all_frames, False)
600
601 # test custom values
602 f = tracemalloc.Filter(False, "test.py", 123, True)
603 self.assertEqual(f.inclusive, False)
604 self.assertEqual(f.filename_pattern, "test.py")
605 self.assertEqual(f.lineno, 123)
606 self.assertEqual(f.all_frames, True)
607
608 # parameters passed by keyword
609 f = tracemalloc.Filter(inclusive=False, filename_pattern="test.py", lineno=123, all_frames=True)
610 self.assertEqual(f.inclusive, False)
611 self.assertEqual(f.filename_pattern, "test.py")
612 self.assertEqual(f.lineno, 123)
613 self.assertEqual(f.all_frames, True)
614
615 # read-only attribute
616 self.assertRaises(AttributeError, setattr, f, "filename_pattern", "abc")
617
618 def test_filter_match(self):
619 # filter without line number
620 f = tracemalloc.Filter(True, "abc")
621 self.assertTrue(f._match_frame("abc", 0))
622 self.assertTrue(f._match_frame("abc", 5))
623 self.assertTrue(f._match_frame("abc", 10))
624 self.assertFalse(f._match_frame("12356", 0))
625 self.assertFalse(f._match_frame("12356", 5))
626 self.assertFalse(f._match_frame("12356", 10))
627
628 f = tracemalloc.Filter(False, "abc")
629 self.assertFalse(f._match_frame("abc", 0))
630 self.assertFalse(f._match_frame("abc", 5))
631 self.assertFalse(f._match_frame("abc", 10))
632 self.assertTrue(f._match_frame("12356", 0))
633 self.assertTrue(f._match_frame("12356", 5))
634 self.assertTrue(f._match_frame("12356", 10))
635
636 # filter with line number > 0
637 f = tracemalloc.Filter(True, "abc", 5)
638 self.assertFalse(f._match_frame("abc", 0))
639 self.assertTrue(f._match_frame("abc", 5))
640 self.assertFalse(f._match_frame("abc", 10))
641 self.assertFalse(f._match_frame("12356", 0))
642 self.assertFalse(f._match_frame("12356", 5))
643 self.assertFalse(f._match_frame("12356", 10))
644
645 f = tracemalloc.Filter(False, "abc", 5)
646 self.assertTrue(f._match_frame("abc", 0))
647 self.assertFalse(f._match_frame("abc", 5))
648 self.assertTrue(f._match_frame("abc", 10))
649 self.assertTrue(f._match_frame("12356", 0))
650 self.assertTrue(f._match_frame("12356", 5))
651 self.assertTrue(f._match_frame("12356", 10))
652
653 # filter with line number 0
654 f = tracemalloc.Filter(True, "abc", 0)
655 self.assertTrue(f._match_frame("abc", 0))
656 self.assertFalse(f._match_frame("abc", 5))
657 self.assertFalse(f._match_frame("abc", 10))
658 self.assertFalse(f._match_frame("12356", 0))
659 self.assertFalse(f._match_frame("12356", 5))
660 self.assertFalse(f._match_frame("12356", 10))
661
662 f = tracemalloc.Filter(False, "abc", 0)
663 self.assertFalse(f._match_frame("abc", 0))
664 self.assertTrue(f._match_frame("abc", 5))
665 self.assertTrue(f._match_frame("abc", 10))
666 self.assertTrue(f._match_frame("12356", 0))
667 self.assertTrue(f._match_frame("12356", 5))
668 self.assertTrue(f._match_frame("12356", 10))
669
670 def test_filter_match_filename(self):
671 def fnmatch(inclusive, filename, pattern):
672 f = tracemalloc.Filter(inclusive, pattern)
673 return f._match_frame(filename, 0)
674
675 self.assertTrue(fnmatch(True, "abc", "abc"))
676 self.assertFalse(fnmatch(True, "12356", "abc"))
677 self.assertFalse(fnmatch(True, "<unknown>", "abc"))
678
679 self.assertFalse(fnmatch(False, "abc", "abc"))
680 self.assertTrue(fnmatch(False, "12356", "abc"))
681 self.assertTrue(fnmatch(False, "<unknown>", "abc"))
682
683 def test_filter_match_filename_joker(self):
684 def fnmatch(filename, pattern):
685 filter = tracemalloc.Filter(True, pattern)
686 return filter._match_frame(filename, 0)
687
688 # empty string
689 self.assertFalse(fnmatch('abc', ''))
690 self.assertFalse(fnmatch('', 'abc'))
691 self.assertTrue(fnmatch('', ''))
692 self.assertTrue(fnmatch('', '*'))
693
694 # no *
695 self.assertTrue(fnmatch('abc', 'abc'))
696 self.assertFalse(fnmatch('abc', 'abcd'))
697 self.assertFalse(fnmatch('abc', 'def'))
698
699 # a*
700 self.assertTrue(fnmatch('abc', 'a*'))
701 self.assertTrue(fnmatch('abc', 'abc*'))
702 self.assertFalse(fnmatch('abc', 'b*'))
703 self.assertFalse(fnmatch('abc', 'abcd*'))
704
705 # a*b
706 self.assertTrue(fnmatch('abc', 'a*c'))
707 self.assertTrue(fnmatch('abcdcx', 'a*cx'))
708 self.assertFalse(fnmatch('abb', 'a*c'))
709 self.assertFalse(fnmatch('abcdce', 'a*cx'))
710
711 # a*b*c
712 self.assertTrue(fnmatch('abcde', 'a*c*e'))
713 self.assertTrue(fnmatch('abcbdefeg', 'a*bd*eg'))
714 self.assertFalse(fnmatch('abcdd', 'a*c*e'))
715 self.assertFalse(fnmatch('abcbdefef', 'a*bd*eg'))
716
Brett Cannonf299abd2015-04-13 14:21:02 -0400717 # replace .pyc suffix with .py
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100718 self.assertTrue(fnmatch('a.pyc', 'a.py'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100719 self.assertTrue(fnmatch('a.py', 'a.pyc'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100720
721 if os.name == 'nt':
722 # case insensitive
723 self.assertTrue(fnmatch('aBC', 'ABc'))
724 self.assertTrue(fnmatch('aBcDe', 'Ab*dE'))
725
726 self.assertTrue(fnmatch('a.pyc', 'a.PY'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100727 self.assertTrue(fnmatch('a.py', 'a.PYC'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100728 else:
729 # case sensitive
730 self.assertFalse(fnmatch('aBC', 'ABc'))
731 self.assertFalse(fnmatch('aBcDe', 'Ab*dE'))
732
733 self.assertFalse(fnmatch('a.pyc', 'a.PY'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100734 self.assertFalse(fnmatch('a.py', 'a.PYC'))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100735
736 if os.name == 'nt':
737 # normalize alternate separator "/" to the standard separator "\"
738 self.assertTrue(fnmatch(r'a/b', r'a\b'))
739 self.assertTrue(fnmatch(r'a\b', r'a/b'))
740 self.assertTrue(fnmatch(r'a/b\c', r'a\b/c'))
741 self.assertTrue(fnmatch(r'a/b/c', r'a\b\c'))
742 else:
743 # there is no alternate separator
744 self.assertFalse(fnmatch(r'a/b', r'a\b'))
745 self.assertFalse(fnmatch(r'a\b', r'a/b'))
746 self.assertFalse(fnmatch(r'a/b\c', r'a\b/c'))
747 self.assertFalse(fnmatch(r'a/b/c', r'a\b\c'))
748
Zachary Wared9b25bb2015-05-13 00:27:01 -0500749 # as of 3.5, .pyo is no longer munged to .py
750 self.assertFalse(fnmatch('a.pyo', 'a.py'))
751
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100752 def test_filter_match_trace(self):
753 t1 = (("a.py", 2), ("b.py", 3))
754 t2 = (("b.py", 4), ("b.py", 5))
755 t3 = (("c.py", 5), ('<unknown>', 0))
756 unknown = (('<unknown>', 0),)
757
758 f = tracemalloc.Filter(True, "b.py", all_frames=True)
759 self.assertTrue(f._match_traceback(t1))
760 self.assertTrue(f._match_traceback(t2))
761 self.assertFalse(f._match_traceback(t3))
762 self.assertFalse(f._match_traceback(unknown))
763
764 f = tracemalloc.Filter(True, "b.py", all_frames=False)
765 self.assertFalse(f._match_traceback(t1))
766 self.assertTrue(f._match_traceback(t2))
767 self.assertFalse(f._match_traceback(t3))
768 self.assertFalse(f._match_traceback(unknown))
769
770 f = tracemalloc.Filter(False, "b.py", all_frames=True)
771 self.assertFalse(f._match_traceback(t1))
772 self.assertFalse(f._match_traceback(t2))
773 self.assertTrue(f._match_traceback(t3))
774 self.assertTrue(f._match_traceback(unknown))
775
776 f = tracemalloc.Filter(False, "b.py", all_frames=False)
777 self.assertTrue(f._match_traceback(t1))
778 self.assertFalse(f._match_traceback(t2))
779 self.assertTrue(f._match_traceback(t3))
780 self.assertTrue(f._match_traceback(unknown))
781
782 f = tracemalloc.Filter(False, "<unknown>", all_frames=False)
783 self.assertTrue(f._match_traceback(t1))
784 self.assertTrue(f._match_traceback(t2))
785 self.assertTrue(f._match_traceback(t3))
786 self.assertFalse(f._match_traceback(unknown))
787
788 f = tracemalloc.Filter(True, "<unknown>", all_frames=True)
789 self.assertFalse(f._match_traceback(t1))
790 self.assertFalse(f._match_traceback(t2))
791 self.assertTrue(f._match_traceback(t3))
792 self.assertTrue(f._match_traceback(unknown))
793
794 f = tracemalloc.Filter(False, "<unknown>", all_frames=True)
795 self.assertTrue(f._match_traceback(t1))
796 self.assertTrue(f._match_traceback(t2))
797 self.assertFalse(f._match_traceback(t3))
798 self.assertFalse(f._match_traceback(unknown))
799
800
801class TestCommandLine(unittest.TestCase):
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800802 def test_env_var_disabled_by_default(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100803 # not tracing by default
804 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
805 ok, stdout, stderr = assert_python_ok('-c', code)
806 stdout = stdout.rstrip()
807 self.assertEqual(stdout, b'False')
808
Berker Peksagce643912015-05-06 06:33:17 +0300809 @unittest.skipIf(interpreter_requires_environment(),
Gregory P. Smithb9a3dd92015-02-04 00:59:40 -0800810 'Cannot run -E tests when PYTHON env vars are required.')
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800811 def test_env_var_ignored_with_E(self):
812 """PYTHON* environment variables must be ignored when -E is present."""
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100813 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
814 ok, stdout, stderr = assert_python_ok('-E', '-c', code, PYTHONTRACEMALLOC='1')
815 stdout = stdout.rstrip()
816 self.assertEqual(stdout, b'False')
817
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800818 def test_env_var_enabled_at_startup(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100819 # tracing at startup
820 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
821 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='1')
822 stdout = stdout.rstrip()
823 self.assertEqual(stdout, b'True')
824
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800825 def test_env_limit(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100826 # start and set the number of frames
827 code = 'import tracemalloc; print(tracemalloc.get_traceback_limit())'
828 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='10')
829 stdout = stdout.rstrip()
830 self.assertEqual(stdout, b'10')
831
832 def test_env_var_invalid(self):
Victor Stinnerf28ce602013-11-27 22:27:13 +0100833 for nframe in (-1, 0, 2**30):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100834 with self.subTest(nframe=nframe):
835 with support.SuppressCrashReport():
836 ok, stdout, stderr = assert_python_failure(
837 '-c', 'pass',
838 PYTHONTRACEMALLOC=str(nframe))
Victor Stinnerf28ce602013-11-27 22:27:13 +0100839 self.assertIn(b'PYTHONTRACEMALLOC: invalid '
840 b'number of frames',
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100841 stderr)
842
843 def test_sys_xoptions(self):
844 for xoptions, nframe in (
845 ('tracemalloc', 1),
846 ('tracemalloc=1', 1),
847 ('tracemalloc=15', 15),
848 ):
849 with self.subTest(xoptions=xoptions, nframe=nframe):
850 code = 'import tracemalloc; print(tracemalloc.get_traceback_limit())'
851 ok, stdout, stderr = assert_python_ok('-X', xoptions, '-c', code)
852 stdout = stdout.rstrip()
853 self.assertEqual(stdout, str(nframe).encode('ascii'))
854
855 def test_sys_xoptions_invalid(self):
Victor Stinnerf28ce602013-11-27 22:27:13 +0100856 for nframe in (-1, 0, 2**30):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100857 with self.subTest(nframe=nframe):
858 with support.SuppressCrashReport():
859 args = ('-X', 'tracemalloc=%s' % nframe, '-c', 'pass')
860 ok, stdout, stderr = assert_python_failure(*args)
Victor Stinnerf28ce602013-11-27 22:27:13 +0100861 self.assertIn(b'-X tracemalloc=NFRAME: invalid '
862 b'number of frames',
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100863 stderr)
864
Serhiy Storchaka24c738a2017-03-19 20:20:10 +0200865 @unittest.skipIf(_testcapi is None, 'need _testcapi')
Victor Stinner8dd49fe2014-06-02 21:36:59 +0200866 def test_pymem_alloc0(self):
867 # Issue #21639: Check that PyMem_Malloc(0) with tracemalloc enabled
868 # does not crash.
869 code = 'import _testcapi; _testcapi.test_pymem_alloc0(); 1'
870 assert_python_ok('-X', 'tracemalloc', '-c', code)
871
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100872
Victor Stinner10b73e12016-03-22 13:39:05 +0100873@unittest.skipIf(_testcapi is None, 'need _testcapi')
874class TestCAPI(unittest.TestCase):
875 maxDiff = 80 * 20
876
877 def setUp(self):
878 if tracemalloc.is_tracing():
879 self.skipTest("tracemalloc must be stopped before the test")
880
881 self.domain = 5
882 self.size = 123
883 self.obj = allocate_bytes(self.size)[0]
884
885 # for the type "object", id(obj) is the address of its memory block.
886 # This type is not tracked by the garbage collector
887 self.ptr = id(self.obj)
888
889 def tearDown(self):
890 tracemalloc.stop()
891
892 def get_traceback(self):
893 frames = _testcapi.tracemalloc_get_traceback(self.domain, self.ptr)
894 if frames is not None:
895 return tracemalloc.Traceback(frames)
896 else:
897 return None
898
899 def track(self, release_gil=False, nframe=1):
900 frames = get_frames(nframe, 2)
901 _testcapi.tracemalloc_track(self.domain, self.ptr, self.size,
902 release_gil)
903 return frames
904
905 def untrack(self):
906 _testcapi.tracemalloc_untrack(self.domain, self.ptr)
907
908 def get_traced_memory(self):
909 # Get the traced size in the domain
910 snapshot = tracemalloc.take_snapshot()
911 domain_filter = tracemalloc.DomainFilter(True, self.domain)
912 snapshot = snapshot.filter_traces([domain_filter])
913 return sum(trace.size for trace in snapshot.traces)
914
915 def check_track(self, release_gil):
916 nframe = 5
917 tracemalloc.start(nframe)
918
919 size = tracemalloc.get_traced_memory()[0]
920
921 frames = self.track(release_gil, nframe)
922 self.assertEqual(self.get_traceback(),
923 tracemalloc.Traceback(frames))
924
925 self.assertEqual(self.get_traced_memory(), self.size)
926
927 def test_track(self):
928 self.check_track(False)
929
930 def test_track_without_gil(self):
931 # check that calling _PyTraceMalloc_Track() without holding the GIL
932 # works too
933 self.check_track(True)
934
935 def test_track_already_tracked(self):
936 nframe = 5
937 tracemalloc.start(nframe)
938
939 # track a first time
940 self.track()
941
942 # calling _PyTraceMalloc_Track() must remove the old trace and add
943 # a new trace with the new traceback
944 frames = self.track(nframe=nframe)
945 self.assertEqual(self.get_traceback(),
946 tracemalloc.Traceback(frames))
947
948 def test_untrack(self):
949 tracemalloc.start()
950
951 self.track()
952 self.assertIsNotNone(self.get_traceback())
953 self.assertEqual(self.get_traced_memory(), self.size)
954
955 # untrack must remove the trace
956 self.untrack()
957 self.assertIsNone(self.get_traceback())
958 self.assertEqual(self.get_traced_memory(), 0)
959
960 # calling _PyTraceMalloc_Untrack() multiple times must not crash
961 self.untrack()
962 self.untrack()
963
964 def test_stop_track(self):
965 tracemalloc.start()
966 tracemalloc.stop()
967
968 with self.assertRaises(RuntimeError):
969 self.track()
970 self.assertIsNone(self.get_traceback())
971
972 def test_stop_untrack(self):
973 tracemalloc.start()
974 self.track()
975
976 tracemalloc.stop()
977 with self.assertRaises(RuntimeError):
978 self.untrack()
979
980
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100981def test_main():
982 support.run_unittest(
983 TestTracemallocEnabled,
984 TestSnapshot,
985 TestFilters,
986 TestCommandLine,
Victor Stinner10b73e12016-03-22 13:39:05 +0100987 TestCAPI,
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100988 )
989
990if __name__ == "__main__":
991 test_main()