ยปCore Development>Code coverage>Lib/test/test_tracemalloc.py

Python code coverage for Lib/test/test_tracemalloc.py

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