blob: 48ccab28b8089ca42c62d3941992d32aa86fc7ad [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
7from test.script_helper import assert_python_ok, assert_python_failure
Gregory P. Smithb9a3dd92015-02-04 00:59:40 -08008from test import script_helper, support
Victor Stinnered3b0bc2013-11-23 12:27:24 +01009try:
10 import threading
11except ImportError:
12 threading = None
13
14EMPTY_STRING_SIZE = sys.getsizeof(b'')
15
16def get_frames(nframe, lineno_delta):
17 frames = []
18 frame = sys._getframe(1)
19 for index in range(nframe):
20 code = frame.f_code
21 lineno = frame.f_lineno + lineno_delta
22 frames.append((code.co_filename, lineno))
23 lineno_delta = 0
24 frame = frame.f_back
25 if frame is None:
26 break
27 return tuple(frames)
28
29def allocate_bytes(size):
30 nframe = tracemalloc.get_traceback_limit()
31 bytes_len = (size - EMPTY_STRING_SIZE)
32 frames = get_frames(nframe, 1)
33 data = b'x' * bytes_len
34 return data, tracemalloc.Traceback(frames)
35
36def create_snapshots():
37 traceback_limit = 2
38
39 raw_traces = [
40 (10, (('a.py', 2), ('b.py', 4))),
41 (10, (('a.py', 2), ('b.py', 4))),
42 (10, (('a.py', 2), ('b.py', 4))),
43
44 (2, (('a.py', 5), ('b.py', 4))),
45
46 (66, (('b.py', 1),)),
47
48 (7, (('<unknown>', 0),)),
49 ]
50 snapshot = tracemalloc.Snapshot(raw_traces, traceback_limit)
51
52 raw_traces2 = [
53 (10, (('a.py', 2), ('b.py', 4))),
54 (10, (('a.py', 2), ('b.py', 4))),
55 (10, (('a.py', 2), ('b.py', 4))),
56
57 (2, (('a.py', 5), ('b.py', 4))),
58 (5000, (('a.py', 5), ('b.py', 4))),
59
60 (400, (('c.py', 578),)),
61 ]
62 snapshot2 = tracemalloc.Snapshot(raw_traces2, traceback_limit)
63
64 return (snapshot, snapshot2)
65
66def frame(filename, lineno):
67 return tracemalloc._Frame((filename, lineno))
68
69def traceback(*frames):
70 return tracemalloc.Traceback(frames)
71
72def traceback_lineno(filename, lineno):
73 return traceback((filename, lineno))
74
75def traceback_filename(filename):
76 return traceback_lineno(filename, 0)
77
78
79class TestTracemallocEnabled(unittest.TestCase):
80 def setUp(self):
81 if tracemalloc.is_tracing():
82 self.skipTest("tracemalloc must be stopped before the test")
83
Victor Stinner3728d6c2013-11-23 12:37:20 +010084 tracemalloc.start(1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +010085
86 def tearDown(self):
87 tracemalloc.stop()
88
89 def test_get_tracemalloc_memory(self):
90 data = [allocate_bytes(123) for count in range(1000)]
91 size = tracemalloc.get_tracemalloc_memory()
92 self.assertGreaterEqual(size, 0)
93
94 tracemalloc.clear_traces()
95 size2 = tracemalloc.get_tracemalloc_memory()
96 self.assertGreaterEqual(size2, 0)
97 self.assertLessEqual(size2, size)
98
99 def test_get_object_traceback(self):
100 tracemalloc.clear_traces()
101 obj_size = 12345
102 obj, obj_traceback = allocate_bytes(obj_size)
103 traceback = tracemalloc.get_object_traceback(obj)
104 self.assertEqual(traceback, obj_traceback)
105
106 def test_set_traceback_limit(self):
107 obj_size = 10
108
Victor Stinner3728d6c2013-11-23 12:37:20 +0100109 tracemalloc.stop()
110 self.assertRaises(ValueError, tracemalloc.start, -1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100111
Victor Stinner3728d6c2013-11-23 12:37:20 +0100112 tracemalloc.stop()
113 tracemalloc.start(10)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100114 obj2, obj2_traceback = allocate_bytes(obj_size)
115 traceback = tracemalloc.get_object_traceback(obj2)
116 self.assertEqual(len(traceback), 10)
117 self.assertEqual(traceback, obj2_traceback)
118
Victor Stinner3728d6c2013-11-23 12:37:20 +0100119 tracemalloc.stop()
120 tracemalloc.start(1)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100121 obj, obj_traceback = allocate_bytes(obj_size)
122 traceback = tracemalloc.get_object_traceback(obj)
123 self.assertEqual(len(traceback), 1)
124 self.assertEqual(traceback, obj_traceback)
125
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100126 def find_trace(self, traces, traceback):
127 for trace in traces:
128 if trace[1] == traceback._frames:
129 return trace
130
131 self.fail("trace not found")
132
133 def test_get_traces(self):
134 tracemalloc.clear_traces()
135 obj_size = 12345
136 obj, obj_traceback = allocate_bytes(obj_size)
137
138 traces = tracemalloc._get_traces()
139 trace = self.find_trace(traces, obj_traceback)
140
141 self.assertIsInstance(trace, tuple)
142 size, traceback = trace
143 self.assertEqual(size, obj_size)
144 self.assertEqual(traceback, obj_traceback._frames)
145
146 tracemalloc.stop()
147 self.assertEqual(tracemalloc._get_traces(), [])
148
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100149 def test_get_traces_intern_traceback(self):
150 # dummy wrappers to get more useful and identical frames in the traceback
151 def allocate_bytes2(size):
152 return allocate_bytes(size)
153 def allocate_bytes3(size):
154 return allocate_bytes2(size)
155 def allocate_bytes4(size):
156 return allocate_bytes3(size)
157
158 # Ensure that two identical tracebacks are not duplicated
Victor Stinner3728d6c2013-11-23 12:37:20 +0100159 tracemalloc.stop()
160 tracemalloc.start(4)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100161 obj_size = 123
162 obj1, obj1_traceback = allocate_bytes4(obj_size)
163 obj2, obj2_traceback = allocate_bytes4(obj_size)
164
165 traces = tracemalloc._get_traces()
166
167 trace1 = self.find_trace(traces, obj1_traceback)
168 trace2 = self.find_trace(traces, obj2_traceback)
169 size1, traceback1 = trace1
170 size2, traceback2 = trace2
171 self.assertEqual(traceback2, traceback1)
172 self.assertIs(traceback2, traceback1)
173
174 def test_get_traced_memory(self):
175 # Python allocates some internals objects, so the test must tolerate
176 # a small difference between the expected size and the real usage
177 max_error = 2048
178
179 # allocate one object
180 obj_size = 1024 * 1024
181 tracemalloc.clear_traces()
182 obj, obj_traceback = allocate_bytes(obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100183 size, peak_size = tracemalloc.get_traced_memory()
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100184 self.assertGreaterEqual(size, obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100185 self.assertGreaterEqual(peak_size, size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100186
187 self.assertLessEqual(size - obj_size, max_error)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100188 self.assertLessEqual(peak_size - size, max_error)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100189
190 # destroy the object
191 obj = None
Victor Stinner3c0481d2013-11-27 21:39:49 +0100192 size2, peak_size2 = tracemalloc.get_traced_memory()
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100193 self.assertLess(size2, size)
194 self.assertGreaterEqual(size - size2, obj_size - max_error)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100195 self.assertGreaterEqual(peak_size2, peak_size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100196
197 # clear_traces() must reset traced memory counters
198 tracemalloc.clear_traces()
199 self.assertEqual(tracemalloc.get_traced_memory(), (0, 0))
200
201 # allocate another object
202 obj, obj_traceback = allocate_bytes(obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100203 size, peak_size = tracemalloc.get_traced_memory()
204 self.assertGreaterEqual(size, obj_size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100205
Victor Stinnera89ecc72013-11-25 09:29:45 +0100206 # stop() also resets traced memory counters
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100207 tracemalloc.stop()
208 self.assertEqual(tracemalloc.get_traced_memory(), (0, 0))
209
210 def test_clear_traces(self):
211 obj, obj_traceback = allocate_bytes(123)
212 traceback = tracemalloc.get_object_traceback(obj)
213 self.assertIsNotNone(traceback)
214
215 tracemalloc.clear_traces()
216 traceback2 = tracemalloc.get_object_traceback(obj)
217 self.assertIsNone(traceback2)
218
219 def test_is_tracing(self):
220 tracemalloc.stop()
221 self.assertFalse(tracemalloc.is_tracing())
222
223 tracemalloc.start()
224 self.assertTrue(tracemalloc.is_tracing())
225
226 def test_snapshot(self):
227 obj, source = allocate_bytes(123)
228
229 # take a snapshot
230 snapshot = tracemalloc.take_snapshot()
231
232 # write on disk
233 snapshot.dump(support.TESTFN)
234 self.addCleanup(support.unlink, support.TESTFN)
235
236 # load from disk
237 snapshot2 = tracemalloc.Snapshot.load(support.TESTFN)
238 self.assertEqual(snapshot2.traces, snapshot.traces)
239
240 # tracemalloc must be tracing memory allocations to take a snapshot
241 tracemalloc.stop()
242 with self.assertRaises(RuntimeError) as cm:
243 tracemalloc.take_snapshot()
244 self.assertEqual(str(cm.exception),
245 "the tracemalloc module must be tracing memory "
246 "allocations to take a snapshot")
247
248 def test_snapshot_save_attr(self):
249 # take a snapshot with a new attribute
250 snapshot = tracemalloc.take_snapshot()
251 snapshot.test_attr = "new"
252 snapshot.dump(support.TESTFN)
253 self.addCleanup(support.unlink, support.TESTFN)
254
255 # load() should recreates the attribute
256 snapshot2 = tracemalloc.Snapshot.load(support.TESTFN)
257 self.assertEqual(snapshot2.test_attr, "new")
258
259 def fork_child(self):
260 if not tracemalloc.is_tracing():
261 return 2
262
263 obj_size = 12345
264 obj, obj_traceback = allocate_bytes(obj_size)
265 traceback = tracemalloc.get_object_traceback(obj)
266 if traceback is None:
267 return 3
268
269 # everything is fine
270 return 0
271
272 @unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork()')
273 def test_fork(self):
274 # check that tracemalloc is still working after fork
275 pid = os.fork()
276 if not pid:
277 # child
278 exitcode = 1
279 try:
280 exitcode = self.fork_child()
281 finally:
282 os._exit(exitcode)
283 else:
284 pid2, status = os.waitpid(pid, 0)
285 self.assertTrue(os.WIFEXITED(status))
286 exitcode = os.WEXITSTATUS(status)
287 self.assertEqual(exitcode, 0)
288
289
290class TestSnapshot(unittest.TestCase):
291 maxDiff = 4000
292
293 def test_create_snapshot(self):
294 raw_traces = [(5, (('a.py', 2),))]
295
296 with contextlib.ExitStack() as stack:
297 stack.enter_context(patch.object(tracemalloc, 'is_tracing',
298 return_value=True))
299 stack.enter_context(patch.object(tracemalloc, 'get_traceback_limit',
300 return_value=5))
301 stack.enter_context(patch.object(tracemalloc, '_get_traces',
302 return_value=raw_traces))
303
304 snapshot = tracemalloc.take_snapshot()
305 self.assertEqual(snapshot.traceback_limit, 5)
306 self.assertEqual(len(snapshot.traces), 1)
307 trace = snapshot.traces[0]
308 self.assertEqual(trace.size, 5)
309 self.assertEqual(len(trace.traceback), 1)
310 self.assertEqual(trace.traceback[0].filename, 'a.py')
311 self.assertEqual(trace.traceback[0].lineno, 2)
312
313 def test_filter_traces(self):
314 snapshot, snapshot2 = create_snapshots()
315 filter1 = tracemalloc.Filter(False, "b.py")
316 filter2 = tracemalloc.Filter(True, "a.py", 2)
317 filter3 = tracemalloc.Filter(True, "a.py", 5)
318
319 original_traces = list(snapshot.traces._traces)
320
321 # exclude b.py
322 snapshot3 = snapshot.filter_traces((filter1,))
323 self.assertEqual(snapshot3.traces._traces, [
324 (10, (('a.py', 2), ('b.py', 4))),
325 (10, (('a.py', 2), ('b.py', 4))),
326 (10, (('a.py', 2), ('b.py', 4))),
327 (2, (('a.py', 5), ('b.py', 4))),
328 (7, (('<unknown>', 0),)),
329 ])
330
331 # filter_traces() must not touch the original snapshot
332 self.assertEqual(snapshot.traces._traces, original_traces)
333
334 # only include two lines of a.py
335 snapshot4 = snapshot3.filter_traces((filter2, filter3))
336 self.assertEqual(snapshot4.traces._traces, [
337 (10, (('a.py', 2), ('b.py', 4))),
338 (10, (('a.py', 2), ('b.py', 4))),
339 (10, (('a.py', 2), ('b.py', 4))),
340 (2, (('a.py', 5), ('b.py', 4))),
341 ])
342
343 # No filter: just duplicate the snapshot
344 snapshot5 = snapshot.filter_traces(())
345 self.assertIsNot(snapshot5, snapshot)
346 self.assertIsNot(snapshot5.traces, snapshot.traces)
347 self.assertEqual(snapshot5.traces, snapshot.traces)
348
Victor Stinner8ce8ff92014-03-10 11:05:07 +0100349 self.assertRaises(TypeError, snapshot.filter_traces, filter1)
350
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100351 def test_snapshot_group_by_line(self):
352 snapshot, snapshot2 = create_snapshots()
353 tb_0 = traceback_lineno('<unknown>', 0)
354 tb_a_2 = traceback_lineno('a.py', 2)
355 tb_a_5 = traceback_lineno('a.py', 5)
356 tb_b_1 = traceback_lineno('b.py', 1)
357 tb_c_578 = traceback_lineno('c.py', 578)
358
359 # stats per file and line
360 stats1 = snapshot.statistics('lineno')
361 self.assertEqual(stats1, [
362 tracemalloc.Statistic(tb_b_1, 66, 1),
363 tracemalloc.Statistic(tb_a_2, 30, 3),
364 tracemalloc.Statistic(tb_0, 7, 1),
365 tracemalloc.Statistic(tb_a_5, 2, 1),
366 ])
367
368 # stats per file and line (2)
369 stats2 = snapshot2.statistics('lineno')
370 self.assertEqual(stats2, [
371 tracemalloc.Statistic(tb_a_5, 5002, 2),
372 tracemalloc.Statistic(tb_c_578, 400, 1),
373 tracemalloc.Statistic(tb_a_2, 30, 3),
374 ])
375
376 # stats diff per file and line
377 statistics = snapshot2.compare_to(snapshot, 'lineno')
378 self.assertEqual(statistics, [
379 tracemalloc.StatisticDiff(tb_a_5, 5002, 5000, 2, 1),
380 tracemalloc.StatisticDiff(tb_c_578, 400, 400, 1, 1),
381 tracemalloc.StatisticDiff(tb_b_1, 0, -66, 0, -1),
382 tracemalloc.StatisticDiff(tb_0, 0, -7, 0, -1),
383 tracemalloc.StatisticDiff(tb_a_2, 30, 0, 3, 0),
384 ])
385
386 def test_snapshot_group_by_file(self):
387 snapshot, snapshot2 = create_snapshots()
388 tb_0 = traceback_filename('<unknown>')
389 tb_a = traceback_filename('a.py')
390 tb_b = traceback_filename('b.py')
391 tb_c = traceback_filename('c.py')
392
393 # stats per file
394 stats1 = snapshot.statistics('filename')
395 self.assertEqual(stats1, [
396 tracemalloc.Statistic(tb_b, 66, 1),
397 tracemalloc.Statistic(tb_a, 32, 4),
398 tracemalloc.Statistic(tb_0, 7, 1),
399 ])
400
401 # stats per file (2)
402 stats2 = snapshot2.statistics('filename')
403 self.assertEqual(stats2, [
404 tracemalloc.Statistic(tb_a, 5032, 5),
405 tracemalloc.Statistic(tb_c, 400, 1),
406 ])
407
408 # stats diff per file
409 diff = snapshot2.compare_to(snapshot, 'filename')
410 self.assertEqual(diff, [
411 tracemalloc.StatisticDiff(tb_a, 5032, 5000, 5, 1),
412 tracemalloc.StatisticDiff(tb_c, 400, 400, 1, 1),
413 tracemalloc.StatisticDiff(tb_b, 0, -66, 0, -1),
414 tracemalloc.StatisticDiff(tb_0, 0, -7, 0, -1),
415 ])
416
417 def test_snapshot_group_by_traceback(self):
418 snapshot, snapshot2 = create_snapshots()
419
420 # stats per file
421 tb1 = traceback(('a.py', 2), ('b.py', 4))
422 tb2 = traceback(('a.py', 5), ('b.py', 4))
423 tb3 = traceback(('b.py', 1))
424 tb4 = traceback(('<unknown>', 0))
425 stats1 = snapshot.statistics('traceback')
426 self.assertEqual(stats1, [
427 tracemalloc.Statistic(tb3, 66, 1),
428 tracemalloc.Statistic(tb1, 30, 3),
429 tracemalloc.Statistic(tb4, 7, 1),
430 tracemalloc.Statistic(tb2, 2, 1),
431 ])
432
433 # stats per file (2)
434 tb5 = traceback(('c.py', 578))
435 stats2 = snapshot2.statistics('traceback')
436 self.assertEqual(stats2, [
437 tracemalloc.Statistic(tb2, 5002, 2),
438 tracemalloc.Statistic(tb5, 400, 1),
439 tracemalloc.Statistic(tb1, 30, 3),
440 ])
441
442 # stats diff per file
443 diff = snapshot2.compare_to(snapshot, 'traceback')
444 self.assertEqual(diff, [
445 tracemalloc.StatisticDiff(tb2, 5002, 5000, 2, 1),
446 tracemalloc.StatisticDiff(tb5, 400, 400, 1, 1),
447 tracemalloc.StatisticDiff(tb3, 0, -66, 0, -1),
448 tracemalloc.StatisticDiff(tb4, 0, -7, 0, -1),
449 tracemalloc.StatisticDiff(tb1, 30, 0, 3, 0),
450 ])
451
452 self.assertRaises(ValueError,
453 snapshot.statistics, 'traceback', cumulative=True)
454
455 def test_snapshot_group_by_cumulative(self):
456 snapshot, snapshot2 = create_snapshots()
457 tb_0 = traceback_filename('<unknown>')
458 tb_a = traceback_filename('a.py')
459 tb_b = traceback_filename('b.py')
460 tb_a_2 = traceback_lineno('a.py', 2)
461 tb_a_5 = traceback_lineno('a.py', 5)
462 tb_b_1 = traceback_lineno('b.py', 1)
463 tb_b_4 = traceback_lineno('b.py', 4)
464
465 # per file
466 stats = snapshot.statistics('filename', True)
467 self.assertEqual(stats, [
468 tracemalloc.Statistic(tb_b, 98, 5),
469 tracemalloc.Statistic(tb_a, 32, 4),
470 tracemalloc.Statistic(tb_0, 7, 1),
471 ])
472
473 # per line
474 stats = snapshot.statistics('lineno', True)
475 self.assertEqual(stats, [
476 tracemalloc.Statistic(tb_b_1, 66, 1),
477 tracemalloc.Statistic(tb_b_4, 32, 4),
478 tracemalloc.Statistic(tb_a_2, 30, 3),
479 tracemalloc.Statistic(tb_0, 7, 1),
480 tracemalloc.Statistic(tb_a_5, 2, 1),
481 ])
482
483 def test_trace_format(self):
484 snapshot, snapshot2 = create_snapshots()
485 trace = snapshot.traces[0]
486 self.assertEqual(str(trace), 'a.py:2: 10 B')
487 traceback = trace.traceback
488 self.assertEqual(str(traceback), 'a.py:2')
489 frame = traceback[0]
490 self.assertEqual(str(frame), 'a.py:2')
491
492 def test_statistic_format(self):
493 snapshot, snapshot2 = create_snapshots()
494 stats = snapshot.statistics('lineno')
495 stat = stats[0]
496 self.assertEqual(str(stat),
497 'b.py:1: size=66 B, count=1, average=66 B')
498
499 def test_statistic_diff_format(self):
500 snapshot, snapshot2 = create_snapshots()
501 stats = snapshot2.compare_to(snapshot, 'lineno')
502 stat = stats[0]
503 self.assertEqual(str(stat),
504 'a.py:5: size=5002 B (+5000 B), count=2 (+1), average=2501 B')
505
Victor Stinner524be302014-02-01 04:07:02 +0100506 def test_slices(self):
507 snapshot, snapshot2 = create_snapshots()
508 self.assertEqual(snapshot.traces[:2],
509 (snapshot.traces[0], snapshot.traces[1]))
510
511 traceback = snapshot.traces[0].traceback
512 self.assertEqual(traceback[:2],
513 (traceback[0], traceback[1]))
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100514
Victor Stinner23f628d2014-02-16 23:53:38 +0100515 def test_format_traceback(self):
516 snapshot, snapshot2 = create_snapshots()
517 def getline(filename, lineno):
518 return ' <%s, %s>' % (filename, lineno)
519 with unittest.mock.patch('tracemalloc.linecache.getline',
520 side_effect=getline):
521 tb = snapshot.traces[0].traceback
522 self.assertEqual(tb.format(),
523 [' File "a.py", line 2',
524 ' <a.py, 2>',
525 ' File "b.py", line 4',
526 ' <b.py, 4>'])
527
528 self.assertEqual(tb.format(limit=1),
529 [' File "a.py", line 2',
530 ' <a.py, 2>'])
531
532 self.assertEqual(tb.format(limit=-1),
533 [])
534
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100535
536class TestFilters(unittest.TestCase):
537 maxDiff = 2048
538
539 def test_filter_attributes(self):
540 # test default values
541 f = tracemalloc.Filter(True, "abc")
542 self.assertEqual(f.inclusive, True)
543 self.assertEqual(f.filename_pattern, "abc")
544 self.assertIsNone(f.lineno)
545 self.assertEqual(f.all_frames, False)
546
547 # test custom values
548 f = tracemalloc.Filter(False, "test.py", 123, True)
549 self.assertEqual(f.inclusive, False)
550 self.assertEqual(f.filename_pattern, "test.py")
551 self.assertEqual(f.lineno, 123)
552 self.assertEqual(f.all_frames, True)
553
554 # parameters passed by keyword
555 f = tracemalloc.Filter(inclusive=False, filename_pattern="test.py", lineno=123, all_frames=True)
556 self.assertEqual(f.inclusive, False)
557 self.assertEqual(f.filename_pattern, "test.py")
558 self.assertEqual(f.lineno, 123)
559 self.assertEqual(f.all_frames, True)
560
561 # read-only attribute
562 self.assertRaises(AttributeError, setattr, f, "filename_pattern", "abc")
563
564 def test_filter_match(self):
565 # filter without line number
566 f = tracemalloc.Filter(True, "abc")
567 self.assertTrue(f._match_frame("abc", 0))
568 self.assertTrue(f._match_frame("abc", 5))
569 self.assertTrue(f._match_frame("abc", 10))
570 self.assertFalse(f._match_frame("12356", 0))
571 self.assertFalse(f._match_frame("12356", 5))
572 self.assertFalse(f._match_frame("12356", 10))
573
574 f = tracemalloc.Filter(False, "abc")
575 self.assertFalse(f._match_frame("abc", 0))
576 self.assertFalse(f._match_frame("abc", 5))
577 self.assertFalse(f._match_frame("abc", 10))
578 self.assertTrue(f._match_frame("12356", 0))
579 self.assertTrue(f._match_frame("12356", 5))
580 self.assertTrue(f._match_frame("12356", 10))
581
582 # filter with line number > 0
583 f = tracemalloc.Filter(True, "abc", 5)
584 self.assertFalse(f._match_frame("abc", 0))
585 self.assertTrue(f._match_frame("abc", 5))
586 self.assertFalse(f._match_frame("abc", 10))
587 self.assertFalse(f._match_frame("12356", 0))
588 self.assertFalse(f._match_frame("12356", 5))
589 self.assertFalse(f._match_frame("12356", 10))
590
591 f = tracemalloc.Filter(False, "abc", 5)
592 self.assertTrue(f._match_frame("abc", 0))
593 self.assertFalse(f._match_frame("abc", 5))
594 self.assertTrue(f._match_frame("abc", 10))
595 self.assertTrue(f._match_frame("12356", 0))
596 self.assertTrue(f._match_frame("12356", 5))
597 self.assertTrue(f._match_frame("12356", 10))
598
599 # filter with line number 0
600 f = tracemalloc.Filter(True, "abc", 0)
601 self.assertTrue(f._match_frame("abc", 0))
602 self.assertFalse(f._match_frame("abc", 5))
603 self.assertFalse(f._match_frame("abc", 10))
604 self.assertFalse(f._match_frame("12356", 0))
605 self.assertFalse(f._match_frame("12356", 5))
606 self.assertFalse(f._match_frame("12356", 10))
607
608 f = tracemalloc.Filter(False, "abc", 0)
609 self.assertFalse(f._match_frame("abc", 0))
610 self.assertTrue(f._match_frame("abc", 5))
611 self.assertTrue(f._match_frame("abc", 10))
612 self.assertTrue(f._match_frame("12356", 0))
613 self.assertTrue(f._match_frame("12356", 5))
614 self.assertTrue(f._match_frame("12356", 10))
615
616 def test_filter_match_filename(self):
617 def fnmatch(inclusive, filename, pattern):
618 f = tracemalloc.Filter(inclusive, pattern)
619 return f._match_frame(filename, 0)
620
621 self.assertTrue(fnmatch(True, "abc", "abc"))
622 self.assertFalse(fnmatch(True, "12356", "abc"))
623 self.assertFalse(fnmatch(True, "<unknown>", "abc"))
624
625 self.assertFalse(fnmatch(False, "abc", "abc"))
626 self.assertTrue(fnmatch(False, "12356", "abc"))
627 self.assertTrue(fnmatch(False, "<unknown>", "abc"))
628
629 def test_filter_match_filename_joker(self):
630 def fnmatch(filename, pattern):
631 filter = tracemalloc.Filter(True, pattern)
632 return filter._match_frame(filename, 0)
633
634 # empty string
635 self.assertFalse(fnmatch('abc', ''))
636 self.assertFalse(fnmatch('', 'abc'))
637 self.assertTrue(fnmatch('', ''))
638 self.assertTrue(fnmatch('', '*'))
639
640 # no *
641 self.assertTrue(fnmatch('abc', 'abc'))
642 self.assertFalse(fnmatch('abc', 'abcd'))
643 self.assertFalse(fnmatch('abc', 'def'))
644
645 # a*
646 self.assertTrue(fnmatch('abc', 'a*'))
647 self.assertTrue(fnmatch('abc', 'abc*'))
648 self.assertFalse(fnmatch('abc', 'b*'))
649 self.assertFalse(fnmatch('abc', 'abcd*'))
650
651 # a*b
652 self.assertTrue(fnmatch('abc', 'a*c'))
653 self.assertTrue(fnmatch('abcdcx', 'a*cx'))
654 self.assertFalse(fnmatch('abb', 'a*c'))
655 self.assertFalse(fnmatch('abcdce', 'a*cx'))
656
657 # a*b*c
658 self.assertTrue(fnmatch('abcde', 'a*c*e'))
659 self.assertTrue(fnmatch('abcbdefeg', 'a*bd*eg'))
660 self.assertFalse(fnmatch('abcdd', 'a*c*e'))
661 self.assertFalse(fnmatch('abcbdefef', 'a*bd*eg'))
662
663 # replace .pyc and .pyo suffix with .py
664 self.assertTrue(fnmatch('a.pyc', 'a.py'))
665 self.assertTrue(fnmatch('a.pyo', 'a.py'))
666 self.assertTrue(fnmatch('a.py', 'a.pyc'))
667 self.assertTrue(fnmatch('a.py', 'a.pyo'))
668
669 if os.name == 'nt':
670 # case insensitive
671 self.assertTrue(fnmatch('aBC', 'ABc'))
672 self.assertTrue(fnmatch('aBcDe', 'Ab*dE'))
673
674 self.assertTrue(fnmatch('a.pyc', 'a.PY'))
675 self.assertTrue(fnmatch('a.PYO', 'a.py'))
676 self.assertTrue(fnmatch('a.py', 'a.PYC'))
677 self.assertTrue(fnmatch('a.PY', 'a.pyo'))
678 else:
679 # case sensitive
680 self.assertFalse(fnmatch('aBC', 'ABc'))
681 self.assertFalse(fnmatch('aBcDe', 'Ab*dE'))
682
683 self.assertFalse(fnmatch('a.pyc', 'a.PY'))
684 self.assertFalse(fnmatch('a.PYO', 'a.py'))
685 self.assertFalse(fnmatch('a.py', 'a.PYC'))
686 self.assertFalse(fnmatch('a.PY', 'a.pyo'))
687
688 if os.name == 'nt':
689 # normalize alternate separator "/" to the standard separator "\"
690 self.assertTrue(fnmatch(r'a/b', r'a\b'))
691 self.assertTrue(fnmatch(r'a\b', r'a/b'))
692 self.assertTrue(fnmatch(r'a/b\c', r'a\b/c'))
693 self.assertTrue(fnmatch(r'a/b/c', r'a\b\c'))
694 else:
695 # there is no alternate separator
696 self.assertFalse(fnmatch(r'a/b', r'a\b'))
697 self.assertFalse(fnmatch(r'a\b', r'a/b'))
698 self.assertFalse(fnmatch(r'a/b\c', r'a\b/c'))
699 self.assertFalse(fnmatch(r'a/b/c', r'a\b\c'))
700
701 def test_filter_match_trace(self):
702 t1 = (("a.py", 2), ("b.py", 3))
703 t2 = (("b.py", 4), ("b.py", 5))
704 t3 = (("c.py", 5), ('<unknown>', 0))
705 unknown = (('<unknown>', 0),)
706
707 f = tracemalloc.Filter(True, "b.py", all_frames=True)
708 self.assertTrue(f._match_traceback(t1))
709 self.assertTrue(f._match_traceback(t2))
710 self.assertFalse(f._match_traceback(t3))
711 self.assertFalse(f._match_traceback(unknown))
712
713 f = tracemalloc.Filter(True, "b.py", all_frames=False)
714 self.assertFalse(f._match_traceback(t1))
715 self.assertTrue(f._match_traceback(t2))
716 self.assertFalse(f._match_traceback(t3))
717 self.assertFalse(f._match_traceback(unknown))
718
719 f = tracemalloc.Filter(False, "b.py", all_frames=True)
720 self.assertFalse(f._match_traceback(t1))
721 self.assertFalse(f._match_traceback(t2))
722 self.assertTrue(f._match_traceback(t3))
723 self.assertTrue(f._match_traceback(unknown))
724
725 f = tracemalloc.Filter(False, "b.py", all_frames=False)
726 self.assertTrue(f._match_traceback(t1))
727 self.assertFalse(f._match_traceback(t2))
728 self.assertTrue(f._match_traceback(t3))
729 self.assertTrue(f._match_traceback(unknown))
730
731 f = tracemalloc.Filter(False, "<unknown>", all_frames=False)
732 self.assertTrue(f._match_traceback(t1))
733 self.assertTrue(f._match_traceback(t2))
734 self.assertTrue(f._match_traceback(t3))
735 self.assertFalse(f._match_traceback(unknown))
736
737 f = tracemalloc.Filter(True, "<unknown>", all_frames=True)
738 self.assertFalse(f._match_traceback(t1))
739 self.assertFalse(f._match_traceback(t2))
740 self.assertTrue(f._match_traceback(t3))
741 self.assertTrue(f._match_traceback(unknown))
742
743 f = tracemalloc.Filter(False, "<unknown>", all_frames=True)
744 self.assertTrue(f._match_traceback(t1))
745 self.assertTrue(f._match_traceback(t2))
746 self.assertFalse(f._match_traceback(t3))
747 self.assertFalse(f._match_traceback(unknown))
748
749
750class TestCommandLine(unittest.TestCase):
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800751 def test_env_var_disabled_by_default(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100752 # not tracing by default
753 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
754 ok, stdout, stderr = assert_python_ok('-c', code)
755 stdout = stdout.rstrip()
756 self.assertEqual(stdout, b'False')
757
Gregory P. Smithb9a3dd92015-02-04 00:59:40 -0800758 @unittest.skipIf(script_helper._interpreter_requires_environment(),
759 'Cannot run -E tests when PYTHON env vars are required.')
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800760 def test_env_var_ignored_with_E(self):
761 """PYTHON* environment variables must be ignored when -E is present."""
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100762 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
763 ok, stdout, stderr = assert_python_ok('-E', '-c', code, PYTHONTRACEMALLOC='1')
764 stdout = stdout.rstrip()
765 self.assertEqual(stdout, b'False')
766
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800767 def test_env_var_enabled_at_startup(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100768 # tracing at startup
769 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
770 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='1')
771 stdout = stdout.rstrip()
772 self.assertEqual(stdout, b'True')
773
Gregory P. Smith34cd2ae2015-01-22 14:38:00 -0800774 def test_env_limit(self):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100775 # start and set the number of frames
776 code = 'import tracemalloc; print(tracemalloc.get_traceback_limit())'
777 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='10')
778 stdout = stdout.rstrip()
779 self.assertEqual(stdout, b'10')
780
781 def test_env_var_invalid(self):
Victor Stinnerf28ce602013-11-27 22:27:13 +0100782 for nframe in (-1, 0, 2**30):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100783 with self.subTest(nframe=nframe):
784 with support.SuppressCrashReport():
785 ok, stdout, stderr = assert_python_failure(
786 '-c', 'pass',
787 PYTHONTRACEMALLOC=str(nframe))
Victor Stinnerf28ce602013-11-27 22:27:13 +0100788 self.assertIn(b'PYTHONTRACEMALLOC: invalid '
789 b'number of frames',
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100790 stderr)
791
792 def test_sys_xoptions(self):
793 for xoptions, nframe in (
794 ('tracemalloc', 1),
795 ('tracemalloc=1', 1),
796 ('tracemalloc=15', 15),
797 ):
798 with self.subTest(xoptions=xoptions, nframe=nframe):
799 code = 'import tracemalloc; print(tracemalloc.get_traceback_limit())'
800 ok, stdout, stderr = assert_python_ok('-X', xoptions, '-c', code)
801 stdout = stdout.rstrip()
802 self.assertEqual(stdout, str(nframe).encode('ascii'))
803
804 def test_sys_xoptions_invalid(self):
Victor Stinnerf28ce602013-11-27 22:27:13 +0100805 for nframe in (-1, 0, 2**30):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100806 with self.subTest(nframe=nframe):
807 with support.SuppressCrashReport():
808 args = ('-X', 'tracemalloc=%s' % nframe, '-c', 'pass')
809 ok, stdout, stderr = assert_python_failure(*args)
Victor Stinnerf28ce602013-11-27 22:27:13 +0100810 self.assertIn(b'-X tracemalloc=NFRAME: invalid '
811 b'number of frames',
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100812 stderr)
813
Victor Stinner8dd49fe2014-06-02 21:36:59 +0200814 def test_pymem_alloc0(self):
815 # Issue #21639: Check that PyMem_Malloc(0) with tracemalloc enabled
816 # does not crash.
817 code = 'import _testcapi; _testcapi.test_pymem_alloc0(); 1'
818 assert_python_ok('-X', 'tracemalloc', '-c', code)
819
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100820
821def test_main():
822 support.run_unittest(
823 TestTracemallocEnabled,
824 TestSnapshot,
825 TestFilters,
826 TestCommandLine,
827 )
828
829if __name__ == "__main__":
830 test_main()