blob: 484e313a1a977ddd866b337cce15084af010705f [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
8from test import support
9try:
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
126
127 def find_trace(self, traces, traceback):
128 for trace in traces:
129 if trace[1] == traceback._frames:
130 return trace
131
132 self.fail("trace not found")
133
134 def test_get_traces(self):
135 tracemalloc.clear_traces()
136 obj_size = 12345
137 obj, obj_traceback = allocate_bytes(obj_size)
138
139 traces = tracemalloc._get_traces()
140 trace = self.find_trace(traces, obj_traceback)
141
142 self.assertIsInstance(trace, tuple)
143 size, traceback = trace
144 self.assertEqual(size, obj_size)
145 self.assertEqual(traceback, obj_traceback._frames)
146
147 tracemalloc.stop()
148 self.assertEqual(tracemalloc._get_traces(), [])
149
150
151 def test_get_traces_intern_traceback(self):
152 # dummy wrappers to get more useful and identical frames in the traceback
153 def allocate_bytes2(size):
154 return allocate_bytes(size)
155 def allocate_bytes3(size):
156 return allocate_bytes2(size)
157 def allocate_bytes4(size):
158 return allocate_bytes3(size)
159
160 # Ensure that two identical tracebacks are not duplicated
Victor Stinner3728d6c2013-11-23 12:37:20 +0100161 tracemalloc.stop()
162 tracemalloc.start(4)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100163 obj_size = 123
164 obj1, obj1_traceback = allocate_bytes4(obj_size)
165 obj2, obj2_traceback = allocate_bytes4(obj_size)
166
167 traces = tracemalloc._get_traces()
168
169 trace1 = self.find_trace(traces, obj1_traceback)
170 trace2 = self.find_trace(traces, obj2_traceback)
171 size1, traceback1 = trace1
172 size2, traceback2 = trace2
173 self.assertEqual(traceback2, traceback1)
174 self.assertIs(traceback2, traceback1)
175
176 def test_get_traced_memory(self):
177 # Python allocates some internals objects, so the test must tolerate
178 # a small difference between the expected size and the real usage
179 max_error = 2048
180
181 # allocate one object
182 obj_size = 1024 * 1024
183 tracemalloc.clear_traces()
184 obj, obj_traceback = allocate_bytes(obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100185 size, peak_size = tracemalloc.get_traced_memory()
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100186 self.assertGreaterEqual(size, obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100187 self.assertGreaterEqual(peak_size, size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100188
189 self.assertLessEqual(size - obj_size, max_error)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100190 self.assertLessEqual(peak_size - size, max_error)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100191
192 # destroy the object
193 obj = None
Victor Stinner3c0481d2013-11-27 21:39:49 +0100194 size2, peak_size2 = tracemalloc.get_traced_memory()
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100195 self.assertLess(size2, size)
196 self.assertGreaterEqual(size - size2, obj_size - max_error)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100197 self.assertGreaterEqual(peak_size2, peak_size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100198
199 # clear_traces() must reset traced memory counters
200 tracemalloc.clear_traces()
201 self.assertEqual(tracemalloc.get_traced_memory(), (0, 0))
202
203 # allocate another object
204 obj, obj_traceback = allocate_bytes(obj_size)
Victor Stinner3c0481d2013-11-27 21:39:49 +0100205 size, peak_size = tracemalloc.get_traced_memory()
206 self.assertGreaterEqual(size, obj_size)
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100207
Victor Stinnera89ecc72013-11-25 09:29:45 +0100208 # stop() also resets traced memory counters
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100209 tracemalloc.stop()
210 self.assertEqual(tracemalloc.get_traced_memory(), (0, 0))
211
212 def test_clear_traces(self):
213 obj, obj_traceback = allocate_bytes(123)
214 traceback = tracemalloc.get_object_traceback(obj)
215 self.assertIsNotNone(traceback)
216
217 tracemalloc.clear_traces()
218 traceback2 = tracemalloc.get_object_traceback(obj)
219 self.assertIsNone(traceback2)
220
221 def test_is_tracing(self):
222 tracemalloc.stop()
223 self.assertFalse(tracemalloc.is_tracing())
224
225 tracemalloc.start()
226 self.assertTrue(tracemalloc.is_tracing())
227
228 def test_snapshot(self):
229 obj, source = allocate_bytes(123)
230
231 # take a snapshot
232 snapshot = tracemalloc.take_snapshot()
233
234 # write on disk
235 snapshot.dump(support.TESTFN)
236 self.addCleanup(support.unlink, support.TESTFN)
237
238 # load from disk
239 snapshot2 = tracemalloc.Snapshot.load(support.TESTFN)
240 self.assertEqual(snapshot2.traces, snapshot.traces)
241
242 # tracemalloc must be tracing memory allocations to take a snapshot
243 tracemalloc.stop()
244 with self.assertRaises(RuntimeError) as cm:
245 tracemalloc.take_snapshot()
246 self.assertEqual(str(cm.exception),
247 "the tracemalloc module must be tracing memory "
248 "allocations to take a snapshot")
249
250 def test_snapshot_save_attr(self):
251 # take a snapshot with a new attribute
252 snapshot = tracemalloc.take_snapshot()
253 snapshot.test_attr = "new"
254 snapshot.dump(support.TESTFN)
255 self.addCleanup(support.unlink, support.TESTFN)
256
257 # load() should recreates the attribute
258 snapshot2 = tracemalloc.Snapshot.load(support.TESTFN)
259 self.assertEqual(snapshot2.test_attr, "new")
260
261 def fork_child(self):
262 if not tracemalloc.is_tracing():
263 return 2
264
265 obj_size = 12345
266 obj, obj_traceback = allocate_bytes(obj_size)
267 traceback = tracemalloc.get_object_traceback(obj)
268 if traceback is None:
269 return 3
270
271 # everything is fine
272 return 0
273
274 @unittest.skipUnless(hasattr(os, 'fork'), 'need os.fork()')
275 def test_fork(self):
276 # check that tracemalloc is still working after fork
277 pid = os.fork()
278 if not pid:
279 # child
280 exitcode = 1
281 try:
282 exitcode = self.fork_child()
283 finally:
284 os._exit(exitcode)
285 else:
286 pid2, status = os.waitpid(pid, 0)
287 self.assertTrue(os.WIFEXITED(status))
288 exitcode = os.WEXITSTATUS(status)
289 self.assertEqual(exitcode, 0)
290
291
292class TestSnapshot(unittest.TestCase):
293 maxDiff = 4000
294
295 def test_create_snapshot(self):
296 raw_traces = [(5, (('a.py', 2),))]
297
298 with contextlib.ExitStack() as stack:
299 stack.enter_context(patch.object(tracemalloc, 'is_tracing',
300 return_value=True))
301 stack.enter_context(patch.object(tracemalloc, 'get_traceback_limit',
302 return_value=5))
303 stack.enter_context(patch.object(tracemalloc, '_get_traces',
304 return_value=raw_traces))
305
306 snapshot = tracemalloc.take_snapshot()
307 self.assertEqual(snapshot.traceback_limit, 5)
308 self.assertEqual(len(snapshot.traces), 1)
309 trace = snapshot.traces[0]
310 self.assertEqual(trace.size, 5)
311 self.assertEqual(len(trace.traceback), 1)
312 self.assertEqual(trace.traceback[0].filename, 'a.py')
313 self.assertEqual(trace.traceback[0].lineno, 2)
314
315 def test_filter_traces(self):
316 snapshot, snapshot2 = create_snapshots()
317 filter1 = tracemalloc.Filter(False, "b.py")
318 filter2 = tracemalloc.Filter(True, "a.py", 2)
319 filter3 = tracemalloc.Filter(True, "a.py", 5)
320
321 original_traces = list(snapshot.traces._traces)
322
323 # exclude b.py
324 snapshot3 = snapshot.filter_traces((filter1,))
325 self.assertEqual(snapshot3.traces._traces, [
326 (10, (('a.py', 2), ('b.py', 4))),
327 (10, (('a.py', 2), ('b.py', 4))),
328 (10, (('a.py', 2), ('b.py', 4))),
329 (2, (('a.py', 5), ('b.py', 4))),
330 (7, (('<unknown>', 0),)),
331 ])
332
333 # filter_traces() must not touch the original snapshot
334 self.assertEqual(snapshot.traces._traces, original_traces)
335
336 # only include two lines of a.py
337 snapshot4 = snapshot3.filter_traces((filter2, filter3))
338 self.assertEqual(snapshot4.traces._traces, [
339 (10, (('a.py', 2), ('b.py', 4))),
340 (10, (('a.py', 2), ('b.py', 4))),
341 (10, (('a.py', 2), ('b.py', 4))),
342 (2, (('a.py', 5), ('b.py', 4))),
343 ])
344
345 # No filter: just duplicate the snapshot
346 snapshot5 = snapshot.filter_traces(())
347 self.assertIsNot(snapshot5, snapshot)
348 self.assertIsNot(snapshot5.traces, snapshot.traces)
349 self.assertEqual(snapshot5.traces, snapshot.traces)
350
351 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
506
507
508class TestFilters(unittest.TestCase):
509 maxDiff = 2048
510
511 def test_filter_attributes(self):
512 # test default values
513 f = tracemalloc.Filter(True, "abc")
514 self.assertEqual(f.inclusive, True)
515 self.assertEqual(f.filename_pattern, "abc")
516 self.assertIsNone(f.lineno)
517 self.assertEqual(f.all_frames, False)
518
519 # test custom values
520 f = tracemalloc.Filter(False, "test.py", 123, True)
521 self.assertEqual(f.inclusive, False)
522 self.assertEqual(f.filename_pattern, "test.py")
523 self.assertEqual(f.lineno, 123)
524 self.assertEqual(f.all_frames, True)
525
526 # parameters passed by keyword
527 f = tracemalloc.Filter(inclusive=False, filename_pattern="test.py", lineno=123, all_frames=True)
528 self.assertEqual(f.inclusive, False)
529 self.assertEqual(f.filename_pattern, "test.py")
530 self.assertEqual(f.lineno, 123)
531 self.assertEqual(f.all_frames, True)
532
533 # read-only attribute
534 self.assertRaises(AttributeError, setattr, f, "filename_pattern", "abc")
535
536 def test_filter_match(self):
537 # filter without line number
538 f = tracemalloc.Filter(True, "abc")
539 self.assertTrue(f._match_frame("abc", 0))
540 self.assertTrue(f._match_frame("abc", 5))
541 self.assertTrue(f._match_frame("abc", 10))
542 self.assertFalse(f._match_frame("12356", 0))
543 self.assertFalse(f._match_frame("12356", 5))
544 self.assertFalse(f._match_frame("12356", 10))
545
546 f = tracemalloc.Filter(False, "abc")
547 self.assertFalse(f._match_frame("abc", 0))
548 self.assertFalse(f._match_frame("abc", 5))
549 self.assertFalse(f._match_frame("abc", 10))
550 self.assertTrue(f._match_frame("12356", 0))
551 self.assertTrue(f._match_frame("12356", 5))
552 self.assertTrue(f._match_frame("12356", 10))
553
554 # filter with line number > 0
555 f = tracemalloc.Filter(True, "abc", 5)
556 self.assertFalse(f._match_frame("abc", 0))
557 self.assertTrue(f._match_frame("abc", 5))
558 self.assertFalse(f._match_frame("abc", 10))
559 self.assertFalse(f._match_frame("12356", 0))
560 self.assertFalse(f._match_frame("12356", 5))
561 self.assertFalse(f._match_frame("12356", 10))
562
563 f = tracemalloc.Filter(False, "abc", 5)
564 self.assertTrue(f._match_frame("abc", 0))
565 self.assertFalse(f._match_frame("abc", 5))
566 self.assertTrue(f._match_frame("abc", 10))
567 self.assertTrue(f._match_frame("12356", 0))
568 self.assertTrue(f._match_frame("12356", 5))
569 self.assertTrue(f._match_frame("12356", 10))
570
571 # filter with line number 0
572 f = tracemalloc.Filter(True, "abc", 0)
573 self.assertTrue(f._match_frame("abc", 0))
574 self.assertFalse(f._match_frame("abc", 5))
575 self.assertFalse(f._match_frame("abc", 10))
576 self.assertFalse(f._match_frame("12356", 0))
577 self.assertFalse(f._match_frame("12356", 5))
578 self.assertFalse(f._match_frame("12356", 10))
579
580 f = tracemalloc.Filter(False, "abc", 0)
581 self.assertFalse(f._match_frame("abc", 0))
582 self.assertTrue(f._match_frame("abc", 5))
583 self.assertTrue(f._match_frame("abc", 10))
584 self.assertTrue(f._match_frame("12356", 0))
585 self.assertTrue(f._match_frame("12356", 5))
586 self.assertTrue(f._match_frame("12356", 10))
587
588 def test_filter_match_filename(self):
589 def fnmatch(inclusive, filename, pattern):
590 f = tracemalloc.Filter(inclusive, pattern)
591 return f._match_frame(filename, 0)
592
593 self.assertTrue(fnmatch(True, "abc", "abc"))
594 self.assertFalse(fnmatch(True, "12356", "abc"))
595 self.assertFalse(fnmatch(True, "<unknown>", "abc"))
596
597 self.assertFalse(fnmatch(False, "abc", "abc"))
598 self.assertTrue(fnmatch(False, "12356", "abc"))
599 self.assertTrue(fnmatch(False, "<unknown>", "abc"))
600
601 def test_filter_match_filename_joker(self):
602 def fnmatch(filename, pattern):
603 filter = tracemalloc.Filter(True, pattern)
604 return filter._match_frame(filename, 0)
605
606 # empty string
607 self.assertFalse(fnmatch('abc', ''))
608 self.assertFalse(fnmatch('', 'abc'))
609 self.assertTrue(fnmatch('', ''))
610 self.assertTrue(fnmatch('', '*'))
611
612 # no *
613 self.assertTrue(fnmatch('abc', 'abc'))
614 self.assertFalse(fnmatch('abc', 'abcd'))
615 self.assertFalse(fnmatch('abc', 'def'))
616
617 # a*
618 self.assertTrue(fnmatch('abc', 'a*'))
619 self.assertTrue(fnmatch('abc', 'abc*'))
620 self.assertFalse(fnmatch('abc', 'b*'))
621 self.assertFalse(fnmatch('abc', 'abcd*'))
622
623 # a*b
624 self.assertTrue(fnmatch('abc', 'a*c'))
625 self.assertTrue(fnmatch('abcdcx', 'a*cx'))
626 self.assertFalse(fnmatch('abb', 'a*c'))
627 self.assertFalse(fnmatch('abcdce', 'a*cx'))
628
629 # a*b*c
630 self.assertTrue(fnmatch('abcde', 'a*c*e'))
631 self.assertTrue(fnmatch('abcbdefeg', 'a*bd*eg'))
632 self.assertFalse(fnmatch('abcdd', 'a*c*e'))
633 self.assertFalse(fnmatch('abcbdefef', 'a*bd*eg'))
634
635 # replace .pyc and .pyo suffix with .py
636 self.assertTrue(fnmatch('a.pyc', 'a.py'))
637 self.assertTrue(fnmatch('a.pyo', 'a.py'))
638 self.assertTrue(fnmatch('a.py', 'a.pyc'))
639 self.assertTrue(fnmatch('a.py', 'a.pyo'))
640
641 if os.name == 'nt':
642 # case insensitive
643 self.assertTrue(fnmatch('aBC', 'ABc'))
644 self.assertTrue(fnmatch('aBcDe', 'Ab*dE'))
645
646 self.assertTrue(fnmatch('a.pyc', 'a.PY'))
647 self.assertTrue(fnmatch('a.PYO', 'a.py'))
648 self.assertTrue(fnmatch('a.py', 'a.PYC'))
649 self.assertTrue(fnmatch('a.PY', 'a.pyo'))
650 else:
651 # case sensitive
652 self.assertFalse(fnmatch('aBC', 'ABc'))
653 self.assertFalse(fnmatch('aBcDe', 'Ab*dE'))
654
655 self.assertFalse(fnmatch('a.pyc', 'a.PY'))
656 self.assertFalse(fnmatch('a.PYO', 'a.py'))
657 self.assertFalse(fnmatch('a.py', 'a.PYC'))
658 self.assertFalse(fnmatch('a.PY', 'a.pyo'))
659
660 if os.name == 'nt':
661 # normalize alternate separator "/" to the standard separator "\"
662 self.assertTrue(fnmatch(r'a/b', r'a\b'))
663 self.assertTrue(fnmatch(r'a\b', r'a/b'))
664 self.assertTrue(fnmatch(r'a/b\c', r'a\b/c'))
665 self.assertTrue(fnmatch(r'a/b/c', r'a\b\c'))
666 else:
667 # there is no alternate separator
668 self.assertFalse(fnmatch(r'a/b', r'a\b'))
669 self.assertFalse(fnmatch(r'a\b', r'a/b'))
670 self.assertFalse(fnmatch(r'a/b\c', r'a\b/c'))
671 self.assertFalse(fnmatch(r'a/b/c', r'a\b\c'))
672
673 def test_filter_match_trace(self):
674 t1 = (("a.py", 2), ("b.py", 3))
675 t2 = (("b.py", 4), ("b.py", 5))
676 t3 = (("c.py", 5), ('<unknown>', 0))
677 unknown = (('<unknown>', 0),)
678
679 f = tracemalloc.Filter(True, "b.py", all_frames=True)
680 self.assertTrue(f._match_traceback(t1))
681 self.assertTrue(f._match_traceback(t2))
682 self.assertFalse(f._match_traceback(t3))
683 self.assertFalse(f._match_traceback(unknown))
684
685 f = tracemalloc.Filter(True, "b.py", all_frames=False)
686 self.assertFalse(f._match_traceback(t1))
687 self.assertTrue(f._match_traceback(t2))
688 self.assertFalse(f._match_traceback(t3))
689 self.assertFalse(f._match_traceback(unknown))
690
691 f = tracemalloc.Filter(False, "b.py", all_frames=True)
692 self.assertFalse(f._match_traceback(t1))
693 self.assertFalse(f._match_traceback(t2))
694 self.assertTrue(f._match_traceback(t3))
695 self.assertTrue(f._match_traceback(unknown))
696
697 f = tracemalloc.Filter(False, "b.py", all_frames=False)
698 self.assertTrue(f._match_traceback(t1))
699 self.assertFalse(f._match_traceback(t2))
700 self.assertTrue(f._match_traceback(t3))
701 self.assertTrue(f._match_traceback(unknown))
702
703 f = tracemalloc.Filter(False, "<unknown>", all_frames=False)
704 self.assertTrue(f._match_traceback(t1))
705 self.assertTrue(f._match_traceback(t2))
706 self.assertTrue(f._match_traceback(t3))
707 self.assertFalse(f._match_traceback(unknown))
708
709 f = tracemalloc.Filter(True, "<unknown>", all_frames=True)
710 self.assertFalse(f._match_traceback(t1))
711 self.assertFalse(f._match_traceback(t2))
712 self.assertTrue(f._match_traceback(t3))
713 self.assertTrue(f._match_traceback(unknown))
714
715 f = tracemalloc.Filter(False, "<unknown>", all_frames=True)
716 self.assertTrue(f._match_traceback(t1))
717 self.assertTrue(f._match_traceback(t2))
718 self.assertFalse(f._match_traceback(t3))
719 self.assertFalse(f._match_traceback(unknown))
720
721
722class TestCommandLine(unittest.TestCase):
723 def test_env_var(self):
724 # not tracing by default
725 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
726 ok, stdout, stderr = assert_python_ok('-c', code)
727 stdout = stdout.rstrip()
728 self.assertEqual(stdout, b'False')
729
Victor Stinnera89ecc72013-11-25 09:29:45 +0100730 # PYTHON* environment variables must be ignored when -E option is
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100731 # present
732 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
733 ok, stdout, stderr = assert_python_ok('-E', '-c', code, PYTHONTRACEMALLOC='1')
734 stdout = stdout.rstrip()
735 self.assertEqual(stdout, b'False')
736
737 # tracing at startup
738 code = 'import tracemalloc; print(tracemalloc.is_tracing())'
739 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='1')
740 stdout = stdout.rstrip()
741 self.assertEqual(stdout, b'True')
742
743 # start and set the number of frames
744 code = 'import tracemalloc; print(tracemalloc.get_traceback_limit())'
745 ok, stdout, stderr = assert_python_ok('-c', code, PYTHONTRACEMALLOC='10')
746 stdout = stdout.rstrip()
747 self.assertEqual(stdout, b'10')
748
749 def test_env_var_invalid(self):
Victor Stinnerf28ce602013-11-27 22:27:13 +0100750 for nframe in (-1, 0, 2**30):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100751 with self.subTest(nframe=nframe):
752 with support.SuppressCrashReport():
753 ok, stdout, stderr = assert_python_failure(
754 '-c', 'pass',
755 PYTHONTRACEMALLOC=str(nframe))
Victor Stinnerf28ce602013-11-27 22:27:13 +0100756 self.assertIn(b'PYTHONTRACEMALLOC: invalid '
757 b'number of frames',
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100758 stderr)
759
760 def test_sys_xoptions(self):
761 for xoptions, nframe in (
762 ('tracemalloc', 1),
763 ('tracemalloc=1', 1),
764 ('tracemalloc=15', 15),
765 ):
766 with self.subTest(xoptions=xoptions, nframe=nframe):
767 code = 'import tracemalloc; print(tracemalloc.get_traceback_limit())'
768 ok, stdout, stderr = assert_python_ok('-X', xoptions, '-c', code)
769 stdout = stdout.rstrip()
770 self.assertEqual(stdout, str(nframe).encode('ascii'))
771
772 def test_sys_xoptions_invalid(self):
Victor Stinnerf28ce602013-11-27 22:27:13 +0100773 for nframe in (-1, 0, 2**30):
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100774 with self.subTest(nframe=nframe):
775 with support.SuppressCrashReport():
776 args = ('-X', 'tracemalloc=%s' % nframe, '-c', 'pass')
777 ok, stdout, stderr = assert_python_failure(*args)
Victor Stinnerf28ce602013-11-27 22:27:13 +0100778 self.assertIn(b'-X tracemalloc=NFRAME: invalid '
779 b'number of frames',
Victor Stinnered3b0bc2013-11-23 12:27:24 +0100780 stderr)
781
782
783def test_main():
784 support.run_unittest(
785 TestTracemallocEnabled,
786 TestSnapshot,
787 TestFilters,
788 TestCommandLine,
789 )
790
791if __name__ == "__main__":
792 test_main()