diff options
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/test_tracemalloc.py | 90 | ||||
-rw-r--r-- | Lib/tracemalloc.py | 26 |
2 files changed, 65 insertions, 51 deletions
diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index 4b9bf4e..7283d9c 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -36,7 +36,7 @@ def allocate_bytes(size): bytes_len = (size - EMPTY_STRING_SIZE) frames = get_frames(nframe, 1) data = b'x' * bytes_len - return data, tracemalloc.Traceback(frames) + return data, tracemalloc.Traceback(frames, min(len(frames), nframe)) def create_snapshots(): traceback_limit = 2 @@ -45,27 +45,27 @@ def create_snapshots(): # traceback_frames) tuples. traceback_frames is a tuple of (filename, # line_number) tuples. raw_traces = [ - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), - (1, 2, (('a.py', 5), ('b.py', 4))), + (1, 2, (('a.py', 5), ('b.py', 4)), 3), - (2, 66, (('b.py', 1),)), + (2, 66, (('b.py', 1),), 1), - (3, 7, (('<unknown>', 0),)), + (3, 7, (('<unknown>', 0),), 1), ] snapshot = tracemalloc.Snapshot(raw_traces, traceback_limit) raw_traces2 = [ - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), - (2, 2, (('a.py', 5), ('b.py', 4))), - (2, 5000, (('a.py', 5), ('b.py', 4))), + (2, 2, (('a.py', 5), ('b.py', 4)), 3), + (2, 5000, (('a.py', 5), ('b.py', 4)), 3), - (4, 400, (('c.py', 578),)), + (4, 400, (('c.py', 578),), 1), ] snapshot2 = tracemalloc.Snapshot(raw_traces2, traceback_limit) @@ -125,7 +125,7 @@ class TestTracemallocEnabled(unittest.TestCase): nframe = tracemalloc.get_traceback_limit() frames = get_frames(nframe, -3) - obj_traceback = tracemalloc.Traceback(frames) + obj_traceback = tracemalloc.Traceback(frames, min(len(frames), nframe)) traceback = tracemalloc.get_object_traceback(obj) self.assertIsNotNone(traceback) @@ -167,7 +167,7 @@ class TestTracemallocEnabled(unittest.TestCase): trace = self.find_trace(traces, obj_traceback) self.assertIsInstance(trace, tuple) - domain, size, traceback = trace + domain, size, traceback, length = trace self.assertEqual(size, obj_size) self.assertEqual(traceback, obj_traceback._frames) @@ -197,8 +197,8 @@ class TestTracemallocEnabled(unittest.TestCase): trace1 = self.find_trace(traces, obj1_traceback) trace2 = self.find_trace(traces, obj2_traceback) - domain1, size1, traceback1 = trace1 - domain2, size2, traceback2 = trace2 + domain1, size1, traceback1, length1 = trace1 + domain2, size2, traceback2, length2 = trace2 self.assertIs(traceback2, traceback1) def test_get_traced_memory(self): @@ -259,6 +259,9 @@ class TestTracemallocEnabled(unittest.TestCase): # take a snapshot snapshot = tracemalloc.take_snapshot() + # This can vary + self.assertGreater(snapshot.traces[1].traceback.total_nframe, 10) + # write on disk snapshot.dump(support.TESTFN) self.addCleanup(support.unlink, support.TESTFN) @@ -321,7 +324,7 @@ class TestSnapshot(unittest.TestCase): maxDiff = 4000 def test_create_snapshot(self): - raw_traces = [(0, 5, (('a.py', 2),))] + raw_traces = [(0, 5, (('a.py', 2),), 10)] with contextlib.ExitStack() as stack: stack.enter_context(patch.object(tracemalloc, 'is_tracing', @@ -336,6 +339,7 @@ class TestSnapshot(unittest.TestCase): self.assertEqual(len(snapshot.traces), 1) trace = snapshot.traces[0] self.assertEqual(trace.size, 5) + self.assertEqual(trace.traceback.total_nframe, 10) self.assertEqual(len(trace.traceback), 1) self.assertEqual(trace.traceback[0].filename, 'a.py') self.assertEqual(trace.traceback[0].lineno, 2) @@ -351,11 +355,11 @@ class TestSnapshot(unittest.TestCase): # exclude b.py snapshot3 = snapshot.filter_traces((filter1,)) self.assertEqual(snapshot3.traces._traces, [ - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (1, 2, (('a.py', 5), ('b.py', 4))), - (3, 7, (('<unknown>', 0),)), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (1, 2, (('a.py', 5), ('b.py', 4)), 3), + (3, 7, (('<unknown>', 0),), 1), ]) # filter_traces() must not touch the original snapshot @@ -364,10 +368,10 @@ class TestSnapshot(unittest.TestCase): # only include two lines of a.py snapshot4 = snapshot3.filter_traces((filter2, filter3)) self.assertEqual(snapshot4.traces._traces, [ - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (1, 2, (('a.py', 5), ('b.py', 4))), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (1, 2, (('a.py', 5), ('b.py', 4)), 3), ]) # No filter: just duplicate the snapshot @@ -388,21 +392,21 @@ class TestSnapshot(unittest.TestCase): # exclude a.py of domain 1 snapshot3 = snapshot.filter_traces((filter1,)) self.assertEqual(snapshot3.traces._traces, [ - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (2, 66, (('b.py', 1),)), - (3, 7, (('<unknown>', 0),)), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (2, 66, (('b.py', 1),), 1), + (3, 7, (('<unknown>', 0),), 1), ]) # include domain 1 snapshot3 = snapshot.filter_traces((filter1,)) self.assertEqual(snapshot3.traces._traces, [ - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (2, 66, (('b.py', 1),)), - (3, 7, (('<unknown>', 0),)), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (2, 66, (('b.py', 1),), 1), + (3, 7, (('<unknown>', 0),), 1), ]) def test_filter_traces_domain_filter(self): @@ -413,17 +417,17 @@ class TestSnapshot(unittest.TestCase): # exclude domain 2 snapshot3 = snapshot.filter_traces((filter1,)) self.assertEqual(snapshot3.traces._traces, [ - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (0, 10, (('a.py', 2), ('b.py', 4))), - (1, 2, (('a.py', 5), ('b.py', 4))), - (2, 66, (('b.py', 1),)), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (0, 10, (('a.py', 2), ('b.py', 4)), 3), + (1, 2, (('a.py', 5), ('b.py', 4)), 3), + (2, 66, (('b.py', 1),), 1), ]) # include domain 2 snapshot3 = snapshot.filter_traces((filter2,)) self.assertEqual(snapshot3.traces._traces, [ - (3, 7, (('<unknown>', 0),)), + (3, 7, (('<unknown>', 0),), 1), ]) def test_snapshot_group_by_line(self): diff --git a/Lib/tracemalloc.py b/Lib/tracemalloc.py index 80b521c..69b4170 100644 --- a/Lib/tracemalloc.py +++ b/Lib/tracemalloc.py @@ -182,15 +182,20 @@ class Traceback(Sequence): Sequence of Frame instances sorted from the oldest frame to the most recent frame. """ - __slots__ = ("_frames",) + __slots__ = ("_frames", '_total_nframe') - def __init__(self, frames): + def __init__(self, frames, total_nframe=None): Sequence.__init__(self) # frames is a tuple of frame tuples: see Frame constructor for the # format of a frame tuple; it is reversed, because _tracemalloc # returns frames sorted from most recent to oldest, but the # Python API expects oldest to most recent self._frames = tuple(reversed(frames)) + self._total_nframe = total_nframe + + @property + def total_nframe(self): + return self._total_nframe def __len__(self): return len(self._frames) @@ -221,7 +226,12 @@ class Traceback(Sequence): return str(self[0]) def __repr__(self): - return "<Traceback %r>" % (tuple(self),) + s = "<Traceback %r" % tuple(self) + if self._total_nframe is None: + s += ">" + else: + s += f" total_nframe={self.total_nframe}>" + return s def format(self, limit=None, most_recent_first=False): lines = [] @@ -280,7 +290,7 @@ class Trace: @property def traceback(self): - return Traceback(self._trace[2]) + return Traceback(*self._trace[2:]) def __eq__(self, other): if not isinstance(other, Trace): @@ -378,7 +388,7 @@ class Filter(BaseFilter): return self._match_frame(filename, lineno) def _match(self, trace): - domain, size, traceback = trace + domain, size, traceback, total_nframe = trace res = self._match_traceback(traceback) if self.domain is not None: if self.inclusive: @@ -398,7 +408,7 @@ class DomainFilter(BaseFilter): return self._domain def _match(self, trace): - domain, size, traceback = trace + domain, size, traceback, total_nframe = trace return (domain == self.domain) ^ (not self.inclusive) @@ -475,7 +485,7 @@ class Snapshot: tracebacks = {} if not cumulative: for trace in self.traces._traces: - domain, size, trace_traceback = trace + domain, size, trace_traceback, total_nframe = trace try: traceback = tracebacks[trace_traceback] except KeyError: @@ -496,7 +506,7 @@ class Snapshot: else: # cumulative statistics for trace in self.traces._traces: - domain, size, trace_traceback = trace + domain, size, trace_traceback, total_nframe = trace for frame in trace_traceback: try: traceback = tracebacks[frame] |