xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/tracemalloc.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Workerfrom collections.abc import Sequence, Iterable
2*cda5da8dSAndroid Build Coastguard Workerfrom functools import total_ordering
3*cda5da8dSAndroid Build Coastguard Workerimport fnmatch
4*cda5da8dSAndroid Build Coastguard Workerimport linecache
5*cda5da8dSAndroid Build Coastguard Workerimport os.path
6*cda5da8dSAndroid Build Coastguard Workerimport pickle
7*cda5da8dSAndroid Build Coastguard Worker
8*cda5da8dSAndroid Build Coastguard Worker# Import types and functions implemented in C
9*cda5da8dSAndroid Build Coastguard Workerfrom _tracemalloc import *
10*cda5da8dSAndroid Build Coastguard Workerfrom _tracemalloc import _get_object_traceback, _get_traces
11*cda5da8dSAndroid Build Coastguard Worker
12*cda5da8dSAndroid Build Coastguard Worker
13*cda5da8dSAndroid Build Coastguard Workerdef _format_size(size, sign):
14*cda5da8dSAndroid Build Coastguard Worker    for unit in ('B', 'KiB', 'MiB', 'GiB', 'TiB'):
15*cda5da8dSAndroid Build Coastguard Worker        if abs(size) < 100 and unit != 'B':
16*cda5da8dSAndroid Build Coastguard Worker            # 3 digits (xx.x UNIT)
17*cda5da8dSAndroid Build Coastguard Worker            if sign:
18*cda5da8dSAndroid Build Coastguard Worker                return "%+.1f %s" % (size, unit)
19*cda5da8dSAndroid Build Coastguard Worker            else:
20*cda5da8dSAndroid Build Coastguard Worker                return "%.1f %s" % (size, unit)
21*cda5da8dSAndroid Build Coastguard Worker        if abs(size) < 10 * 1024 or unit == 'TiB':
22*cda5da8dSAndroid Build Coastguard Worker            # 4 or 5 digits (xxxx UNIT)
23*cda5da8dSAndroid Build Coastguard Worker            if sign:
24*cda5da8dSAndroid Build Coastguard Worker                return "%+.0f %s" % (size, unit)
25*cda5da8dSAndroid Build Coastguard Worker            else:
26*cda5da8dSAndroid Build Coastguard Worker                return "%.0f %s" % (size, unit)
27*cda5da8dSAndroid Build Coastguard Worker        size /= 1024
28*cda5da8dSAndroid Build Coastguard Worker
29*cda5da8dSAndroid Build Coastguard Worker
30*cda5da8dSAndroid Build Coastguard Workerclass Statistic:
31*cda5da8dSAndroid Build Coastguard Worker    """
32*cda5da8dSAndroid Build Coastguard Worker    Statistic difference on memory allocations between two Snapshot instance.
33*cda5da8dSAndroid Build Coastguard Worker    """
34*cda5da8dSAndroid Build Coastguard Worker
35*cda5da8dSAndroid Build Coastguard Worker    __slots__ = ('traceback', 'size', 'count')
36*cda5da8dSAndroid Build Coastguard Worker
37*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, traceback, size, count):
38*cda5da8dSAndroid Build Coastguard Worker        self.traceback = traceback
39*cda5da8dSAndroid Build Coastguard Worker        self.size = size
40*cda5da8dSAndroid Build Coastguard Worker        self.count = count
41*cda5da8dSAndroid Build Coastguard Worker
42*cda5da8dSAndroid Build Coastguard Worker    def __hash__(self):
43*cda5da8dSAndroid Build Coastguard Worker        return hash((self.traceback, self.size, self.count))
44*cda5da8dSAndroid Build Coastguard Worker
45*cda5da8dSAndroid Build Coastguard Worker    def __eq__(self, other):
46*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(other, Statistic):
47*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
48*cda5da8dSAndroid Build Coastguard Worker        return (self.traceback == other.traceback
49*cda5da8dSAndroid Build Coastguard Worker                and self.size == other.size
50*cda5da8dSAndroid Build Coastguard Worker                and self.count == other.count)
51*cda5da8dSAndroid Build Coastguard Worker
52*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
53*cda5da8dSAndroid Build Coastguard Worker        text = ("%s: size=%s, count=%i"
54*cda5da8dSAndroid Build Coastguard Worker                 % (self.traceback,
55*cda5da8dSAndroid Build Coastguard Worker                    _format_size(self.size, False),
56*cda5da8dSAndroid Build Coastguard Worker                    self.count))
57*cda5da8dSAndroid Build Coastguard Worker        if self.count:
58*cda5da8dSAndroid Build Coastguard Worker            average = self.size / self.count
59*cda5da8dSAndroid Build Coastguard Worker            text += ", average=%s" % _format_size(average, False)
60*cda5da8dSAndroid Build Coastguard Worker        return text
61*cda5da8dSAndroid Build Coastguard Worker
62*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
63*cda5da8dSAndroid Build Coastguard Worker        return ('<Statistic traceback=%r size=%i count=%i>'
64*cda5da8dSAndroid Build Coastguard Worker                % (self.traceback, self.size, self.count))
65*cda5da8dSAndroid Build Coastguard Worker
66*cda5da8dSAndroid Build Coastguard Worker    def _sort_key(self):
67*cda5da8dSAndroid Build Coastguard Worker        return (self.size, self.count, self.traceback)
68*cda5da8dSAndroid Build Coastguard Worker
69*cda5da8dSAndroid Build Coastguard Worker
70*cda5da8dSAndroid Build Coastguard Workerclass StatisticDiff:
71*cda5da8dSAndroid Build Coastguard Worker    """
72*cda5da8dSAndroid Build Coastguard Worker    Statistic difference on memory allocations between an old and a new
73*cda5da8dSAndroid Build Coastguard Worker    Snapshot instance.
74*cda5da8dSAndroid Build Coastguard Worker    """
75*cda5da8dSAndroid Build Coastguard Worker    __slots__ = ('traceback', 'size', 'size_diff', 'count', 'count_diff')
76*cda5da8dSAndroid Build Coastguard Worker
77*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, traceback, size, size_diff, count, count_diff):
78*cda5da8dSAndroid Build Coastguard Worker        self.traceback = traceback
79*cda5da8dSAndroid Build Coastguard Worker        self.size = size
80*cda5da8dSAndroid Build Coastguard Worker        self.size_diff = size_diff
81*cda5da8dSAndroid Build Coastguard Worker        self.count = count
82*cda5da8dSAndroid Build Coastguard Worker        self.count_diff = count_diff
83*cda5da8dSAndroid Build Coastguard Worker
84*cda5da8dSAndroid Build Coastguard Worker    def __hash__(self):
85*cda5da8dSAndroid Build Coastguard Worker        return hash((self.traceback, self.size, self.size_diff,
86*cda5da8dSAndroid Build Coastguard Worker                     self.count, self.count_diff))
87*cda5da8dSAndroid Build Coastguard Worker
88*cda5da8dSAndroid Build Coastguard Worker    def __eq__(self, other):
89*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(other, StatisticDiff):
90*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
91*cda5da8dSAndroid Build Coastguard Worker        return (self.traceback == other.traceback
92*cda5da8dSAndroid Build Coastguard Worker                and self.size == other.size
93*cda5da8dSAndroid Build Coastguard Worker                and self.size_diff == other.size_diff
94*cda5da8dSAndroid Build Coastguard Worker                and self.count == other.count
95*cda5da8dSAndroid Build Coastguard Worker                and self.count_diff == other.count_diff)
96*cda5da8dSAndroid Build Coastguard Worker
97*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
98*cda5da8dSAndroid Build Coastguard Worker        text = ("%s: size=%s (%s), count=%i (%+i)"
99*cda5da8dSAndroid Build Coastguard Worker                % (self.traceback,
100*cda5da8dSAndroid Build Coastguard Worker                   _format_size(self.size, False),
101*cda5da8dSAndroid Build Coastguard Worker                   _format_size(self.size_diff, True),
102*cda5da8dSAndroid Build Coastguard Worker                   self.count,
103*cda5da8dSAndroid Build Coastguard Worker                   self.count_diff))
104*cda5da8dSAndroid Build Coastguard Worker        if self.count:
105*cda5da8dSAndroid Build Coastguard Worker            average = self.size / self.count
106*cda5da8dSAndroid Build Coastguard Worker            text += ", average=%s" % _format_size(average, False)
107*cda5da8dSAndroid Build Coastguard Worker        return text
108*cda5da8dSAndroid Build Coastguard Worker
109*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
110*cda5da8dSAndroid Build Coastguard Worker        return ('<StatisticDiff traceback=%r size=%i (%+i) count=%i (%+i)>'
111*cda5da8dSAndroid Build Coastguard Worker                % (self.traceback, self.size, self.size_diff,
112*cda5da8dSAndroid Build Coastguard Worker                   self.count, self.count_diff))
113*cda5da8dSAndroid Build Coastguard Worker
114*cda5da8dSAndroid Build Coastguard Worker    def _sort_key(self):
115*cda5da8dSAndroid Build Coastguard Worker        return (abs(self.size_diff), self.size,
116*cda5da8dSAndroid Build Coastguard Worker                abs(self.count_diff), self.count,
117*cda5da8dSAndroid Build Coastguard Worker                self.traceback)
118*cda5da8dSAndroid Build Coastguard Worker
119*cda5da8dSAndroid Build Coastguard Worker
120*cda5da8dSAndroid Build Coastguard Workerdef _compare_grouped_stats(old_group, new_group):
121*cda5da8dSAndroid Build Coastguard Worker    statistics = []
122*cda5da8dSAndroid Build Coastguard Worker    for traceback, stat in new_group.items():
123*cda5da8dSAndroid Build Coastguard Worker        previous = old_group.pop(traceback, None)
124*cda5da8dSAndroid Build Coastguard Worker        if previous is not None:
125*cda5da8dSAndroid Build Coastguard Worker            stat = StatisticDiff(traceback,
126*cda5da8dSAndroid Build Coastguard Worker                                 stat.size, stat.size - previous.size,
127*cda5da8dSAndroid Build Coastguard Worker                                 stat.count, stat.count - previous.count)
128*cda5da8dSAndroid Build Coastguard Worker        else:
129*cda5da8dSAndroid Build Coastguard Worker            stat = StatisticDiff(traceback,
130*cda5da8dSAndroid Build Coastguard Worker                                 stat.size, stat.size,
131*cda5da8dSAndroid Build Coastguard Worker                                 stat.count, stat.count)
132*cda5da8dSAndroid Build Coastguard Worker        statistics.append(stat)
133*cda5da8dSAndroid Build Coastguard Worker
134*cda5da8dSAndroid Build Coastguard Worker    for traceback, stat in old_group.items():
135*cda5da8dSAndroid Build Coastguard Worker        stat = StatisticDiff(traceback, 0, -stat.size, 0, -stat.count)
136*cda5da8dSAndroid Build Coastguard Worker        statistics.append(stat)
137*cda5da8dSAndroid Build Coastguard Worker    return statistics
138*cda5da8dSAndroid Build Coastguard Worker
139*cda5da8dSAndroid Build Coastguard Worker
140*cda5da8dSAndroid Build Coastguard Worker@total_ordering
141*cda5da8dSAndroid Build Coastguard Workerclass Frame:
142*cda5da8dSAndroid Build Coastguard Worker    """
143*cda5da8dSAndroid Build Coastguard Worker    Frame of a traceback.
144*cda5da8dSAndroid Build Coastguard Worker    """
145*cda5da8dSAndroid Build Coastguard Worker    __slots__ = ("_frame",)
146*cda5da8dSAndroid Build Coastguard Worker
147*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, frame):
148*cda5da8dSAndroid Build Coastguard Worker        # frame is a tuple: (filename: str, lineno: int)
149*cda5da8dSAndroid Build Coastguard Worker        self._frame = frame
150*cda5da8dSAndroid Build Coastguard Worker
151*cda5da8dSAndroid Build Coastguard Worker    @property
152*cda5da8dSAndroid Build Coastguard Worker    def filename(self):
153*cda5da8dSAndroid Build Coastguard Worker        return self._frame[0]
154*cda5da8dSAndroid Build Coastguard Worker
155*cda5da8dSAndroid Build Coastguard Worker    @property
156*cda5da8dSAndroid Build Coastguard Worker    def lineno(self):
157*cda5da8dSAndroid Build Coastguard Worker        return self._frame[1]
158*cda5da8dSAndroid Build Coastguard Worker
159*cda5da8dSAndroid Build Coastguard Worker    def __eq__(self, other):
160*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(other, Frame):
161*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
162*cda5da8dSAndroid Build Coastguard Worker        return (self._frame == other._frame)
163*cda5da8dSAndroid Build Coastguard Worker
164*cda5da8dSAndroid Build Coastguard Worker    def __lt__(self, other):
165*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(other, Frame):
166*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
167*cda5da8dSAndroid Build Coastguard Worker        return (self._frame < other._frame)
168*cda5da8dSAndroid Build Coastguard Worker
169*cda5da8dSAndroid Build Coastguard Worker    def __hash__(self):
170*cda5da8dSAndroid Build Coastguard Worker        return hash(self._frame)
171*cda5da8dSAndroid Build Coastguard Worker
172*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
173*cda5da8dSAndroid Build Coastguard Worker        return "%s:%s" % (self.filename, self.lineno)
174*cda5da8dSAndroid Build Coastguard Worker
175*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
176*cda5da8dSAndroid Build Coastguard Worker        return "<Frame filename=%r lineno=%r>" % (self.filename, self.lineno)
177*cda5da8dSAndroid Build Coastguard Worker
178*cda5da8dSAndroid Build Coastguard Worker
179*cda5da8dSAndroid Build Coastguard Worker@total_ordering
180*cda5da8dSAndroid Build Coastguard Workerclass Traceback(Sequence):
181*cda5da8dSAndroid Build Coastguard Worker    """
182*cda5da8dSAndroid Build Coastguard Worker    Sequence of Frame instances sorted from the oldest frame
183*cda5da8dSAndroid Build Coastguard Worker    to the most recent frame.
184*cda5da8dSAndroid Build Coastguard Worker    """
185*cda5da8dSAndroid Build Coastguard Worker    __slots__ = ("_frames", '_total_nframe')
186*cda5da8dSAndroid Build Coastguard Worker
187*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, frames, total_nframe=None):
188*cda5da8dSAndroid Build Coastguard Worker        Sequence.__init__(self)
189*cda5da8dSAndroid Build Coastguard Worker        # frames is a tuple of frame tuples: see Frame constructor for the
190*cda5da8dSAndroid Build Coastguard Worker        # format of a frame tuple; it is reversed, because _tracemalloc
191*cda5da8dSAndroid Build Coastguard Worker        # returns frames sorted from most recent to oldest, but the
192*cda5da8dSAndroid Build Coastguard Worker        # Python API expects oldest to most recent
193*cda5da8dSAndroid Build Coastguard Worker        self._frames = tuple(reversed(frames))
194*cda5da8dSAndroid Build Coastguard Worker        self._total_nframe = total_nframe
195*cda5da8dSAndroid Build Coastguard Worker
196*cda5da8dSAndroid Build Coastguard Worker    @property
197*cda5da8dSAndroid Build Coastguard Worker    def total_nframe(self):
198*cda5da8dSAndroid Build Coastguard Worker        return self._total_nframe
199*cda5da8dSAndroid Build Coastguard Worker
200*cda5da8dSAndroid Build Coastguard Worker    def __len__(self):
201*cda5da8dSAndroid Build Coastguard Worker        return len(self._frames)
202*cda5da8dSAndroid Build Coastguard Worker
203*cda5da8dSAndroid Build Coastguard Worker    def __getitem__(self, index):
204*cda5da8dSAndroid Build Coastguard Worker        if isinstance(index, slice):
205*cda5da8dSAndroid Build Coastguard Worker            return tuple(Frame(trace) for trace in self._frames[index])
206*cda5da8dSAndroid Build Coastguard Worker        else:
207*cda5da8dSAndroid Build Coastguard Worker            return Frame(self._frames[index])
208*cda5da8dSAndroid Build Coastguard Worker
209*cda5da8dSAndroid Build Coastguard Worker    def __contains__(self, frame):
210*cda5da8dSAndroid Build Coastguard Worker        return frame._frame in self._frames
211*cda5da8dSAndroid Build Coastguard Worker
212*cda5da8dSAndroid Build Coastguard Worker    def __hash__(self):
213*cda5da8dSAndroid Build Coastguard Worker        return hash(self._frames)
214*cda5da8dSAndroid Build Coastguard Worker
215*cda5da8dSAndroid Build Coastguard Worker    def __eq__(self, other):
216*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(other, Traceback):
217*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
218*cda5da8dSAndroid Build Coastguard Worker        return (self._frames == other._frames)
219*cda5da8dSAndroid Build Coastguard Worker
220*cda5da8dSAndroid Build Coastguard Worker    def __lt__(self, other):
221*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(other, Traceback):
222*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
223*cda5da8dSAndroid Build Coastguard Worker        return (self._frames < other._frames)
224*cda5da8dSAndroid Build Coastguard Worker
225*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
226*cda5da8dSAndroid Build Coastguard Worker        return str(self[0])
227*cda5da8dSAndroid Build Coastguard Worker
228*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
229*cda5da8dSAndroid Build Coastguard Worker        s = f"<Traceback {tuple(self)}"
230*cda5da8dSAndroid Build Coastguard Worker        if self._total_nframe is None:
231*cda5da8dSAndroid Build Coastguard Worker            s += ">"
232*cda5da8dSAndroid Build Coastguard Worker        else:
233*cda5da8dSAndroid Build Coastguard Worker            s += f" total_nframe={self.total_nframe}>"
234*cda5da8dSAndroid Build Coastguard Worker        return s
235*cda5da8dSAndroid Build Coastguard Worker
236*cda5da8dSAndroid Build Coastguard Worker    def format(self, limit=None, most_recent_first=False):
237*cda5da8dSAndroid Build Coastguard Worker        lines = []
238*cda5da8dSAndroid Build Coastguard Worker        if limit is not None:
239*cda5da8dSAndroid Build Coastguard Worker            if limit > 0:
240*cda5da8dSAndroid Build Coastguard Worker                frame_slice = self[-limit:]
241*cda5da8dSAndroid Build Coastguard Worker            else:
242*cda5da8dSAndroid Build Coastguard Worker                frame_slice = self[:limit]
243*cda5da8dSAndroid Build Coastguard Worker        else:
244*cda5da8dSAndroid Build Coastguard Worker            frame_slice = self
245*cda5da8dSAndroid Build Coastguard Worker
246*cda5da8dSAndroid Build Coastguard Worker        if most_recent_first:
247*cda5da8dSAndroid Build Coastguard Worker            frame_slice = reversed(frame_slice)
248*cda5da8dSAndroid Build Coastguard Worker        for frame in frame_slice:
249*cda5da8dSAndroid Build Coastguard Worker            lines.append('  File "%s", line %s'
250*cda5da8dSAndroid Build Coastguard Worker                         % (frame.filename, frame.lineno))
251*cda5da8dSAndroid Build Coastguard Worker            line = linecache.getline(frame.filename, frame.lineno).strip()
252*cda5da8dSAndroid Build Coastguard Worker            if line:
253*cda5da8dSAndroid Build Coastguard Worker                lines.append('    %s' % line)
254*cda5da8dSAndroid Build Coastguard Worker        return lines
255*cda5da8dSAndroid Build Coastguard Worker
256*cda5da8dSAndroid Build Coastguard Worker
257*cda5da8dSAndroid Build Coastguard Workerdef get_object_traceback(obj):
258*cda5da8dSAndroid Build Coastguard Worker    """
259*cda5da8dSAndroid Build Coastguard Worker    Get the traceback where the Python object *obj* was allocated.
260*cda5da8dSAndroid Build Coastguard Worker    Return a Traceback instance.
261*cda5da8dSAndroid Build Coastguard Worker
262*cda5da8dSAndroid Build Coastguard Worker    Return None if the tracemalloc module is not tracing memory allocations or
263*cda5da8dSAndroid Build Coastguard Worker    did not trace the allocation of the object.
264*cda5da8dSAndroid Build Coastguard Worker    """
265*cda5da8dSAndroid Build Coastguard Worker    frames = _get_object_traceback(obj)
266*cda5da8dSAndroid Build Coastguard Worker    if frames is not None:
267*cda5da8dSAndroid Build Coastguard Worker        return Traceback(frames)
268*cda5da8dSAndroid Build Coastguard Worker    else:
269*cda5da8dSAndroid Build Coastguard Worker        return None
270*cda5da8dSAndroid Build Coastguard Worker
271*cda5da8dSAndroid Build Coastguard Worker
272*cda5da8dSAndroid Build Coastguard Workerclass Trace:
273*cda5da8dSAndroid Build Coastguard Worker    """
274*cda5da8dSAndroid Build Coastguard Worker    Trace of a memory block.
275*cda5da8dSAndroid Build Coastguard Worker    """
276*cda5da8dSAndroid Build Coastguard Worker    __slots__ = ("_trace",)
277*cda5da8dSAndroid Build Coastguard Worker
278*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, trace):
279*cda5da8dSAndroid Build Coastguard Worker        # trace is a tuple: (domain: int, size: int, traceback: tuple).
280*cda5da8dSAndroid Build Coastguard Worker        # See Traceback constructor for the format of the traceback tuple.
281*cda5da8dSAndroid Build Coastguard Worker        self._trace = trace
282*cda5da8dSAndroid Build Coastguard Worker
283*cda5da8dSAndroid Build Coastguard Worker    @property
284*cda5da8dSAndroid Build Coastguard Worker    def domain(self):
285*cda5da8dSAndroid Build Coastguard Worker        return self._trace[0]
286*cda5da8dSAndroid Build Coastguard Worker
287*cda5da8dSAndroid Build Coastguard Worker    @property
288*cda5da8dSAndroid Build Coastguard Worker    def size(self):
289*cda5da8dSAndroid Build Coastguard Worker        return self._trace[1]
290*cda5da8dSAndroid Build Coastguard Worker
291*cda5da8dSAndroid Build Coastguard Worker    @property
292*cda5da8dSAndroid Build Coastguard Worker    def traceback(self):
293*cda5da8dSAndroid Build Coastguard Worker        return Traceback(*self._trace[2:])
294*cda5da8dSAndroid Build Coastguard Worker
295*cda5da8dSAndroid Build Coastguard Worker    def __eq__(self, other):
296*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(other, Trace):
297*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
298*cda5da8dSAndroid Build Coastguard Worker        return (self._trace == other._trace)
299*cda5da8dSAndroid Build Coastguard Worker
300*cda5da8dSAndroid Build Coastguard Worker    def __hash__(self):
301*cda5da8dSAndroid Build Coastguard Worker        return hash(self._trace)
302*cda5da8dSAndroid Build Coastguard Worker
303*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
304*cda5da8dSAndroid Build Coastguard Worker        return "%s: %s" % (self.traceback, _format_size(self.size, False))
305*cda5da8dSAndroid Build Coastguard Worker
306*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
307*cda5da8dSAndroid Build Coastguard Worker        return ("<Trace domain=%s size=%s, traceback=%r>"
308*cda5da8dSAndroid Build Coastguard Worker                % (self.domain, _format_size(self.size, False), self.traceback))
309*cda5da8dSAndroid Build Coastguard Worker
310*cda5da8dSAndroid Build Coastguard Worker
311*cda5da8dSAndroid Build Coastguard Workerclass _Traces(Sequence):
312*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, traces):
313*cda5da8dSAndroid Build Coastguard Worker        Sequence.__init__(self)
314*cda5da8dSAndroid Build Coastguard Worker        # traces is a tuple of trace tuples: see Trace constructor
315*cda5da8dSAndroid Build Coastguard Worker        self._traces = traces
316*cda5da8dSAndroid Build Coastguard Worker
317*cda5da8dSAndroid Build Coastguard Worker    def __len__(self):
318*cda5da8dSAndroid Build Coastguard Worker        return len(self._traces)
319*cda5da8dSAndroid Build Coastguard Worker
320*cda5da8dSAndroid Build Coastguard Worker    def __getitem__(self, index):
321*cda5da8dSAndroid Build Coastguard Worker        if isinstance(index, slice):
322*cda5da8dSAndroid Build Coastguard Worker            return tuple(Trace(trace) for trace in self._traces[index])
323*cda5da8dSAndroid Build Coastguard Worker        else:
324*cda5da8dSAndroid Build Coastguard Worker            return Trace(self._traces[index])
325*cda5da8dSAndroid Build Coastguard Worker
326*cda5da8dSAndroid Build Coastguard Worker    def __contains__(self, trace):
327*cda5da8dSAndroid Build Coastguard Worker        return trace._trace in self._traces
328*cda5da8dSAndroid Build Coastguard Worker
329*cda5da8dSAndroid Build Coastguard Worker    def __eq__(self, other):
330*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(other, _Traces):
331*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
332*cda5da8dSAndroid Build Coastguard Worker        return (self._traces == other._traces)
333*cda5da8dSAndroid Build Coastguard Worker
334*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
335*cda5da8dSAndroid Build Coastguard Worker        return "<Traces len=%s>" % len(self)
336*cda5da8dSAndroid Build Coastguard Worker
337*cda5da8dSAndroid Build Coastguard Worker
338*cda5da8dSAndroid Build Coastguard Workerdef _normalize_filename(filename):
339*cda5da8dSAndroid Build Coastguard Worker    filename = os.path.normcase(filename)
340*cda5da8dSAndroid Build Coastguard Worker    if filename.endswith('.pyc'):
341*cda5da8dSAndroid Build Coastguard Worker        filename = filename[:-1]
342*cda5da8dSAndroid Build Coastguard Worker    return filename
343*cda5da8dSAndroid Build Coastguard Worker
344*cda5da8dSAndroid Build Coastguard Worker
345*cda5da8dSAndroid Build Coastguard Workerclass BaseFilter:
346*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, inclusive):
347*cda5da8dSAndroid Build Coastguard Worker        self.inclusive = inclusive
348*cda5da8dSAndroid Build Coastguard Worker
349*cda5da8dSAndroid Build Coastguard Worker    def _match(self, trace):
350*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError
351*cda5da8dSAndroid Build Coastguard Worker
352*cda5da8dSAndroid Build Coastguard Worker
353*cda5da8dSAndroid Build Coastguard Workerclass Filter(BaseFilter):
354*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, inclusive, filename_pattern,
355*cda5da8dSAndroid Build Coastguard Worker                 lineno=None, all_frames=False, domain=None):
356*cda5da8dSAndroid Build Coastguard Worker        super().__init__(inclusive)
357*cda5da8dSAndroid Build Coastguard Worker        self.inclusive = inclusive
358*cda5da8dSAndroid Build Coastguard Worker        self._filename_pattern = _normalize_filename(filename_pattern)
359*cda5da8dSAndroid Build Coastguard Worker        self.lineno = lineno
360*cda5da8dSAndroid Build Coastguard Worker        self.all_frames = all_frames
361*cda5da8dSAndroid Build Coastguard Worker        self.domain = domain
362*cda5da8dSAndroid Build Coastguard Worker
363*cda5da8dSAndroid Build Coastguard Worker    @property
364*cda5da8dSAndroid Build Coastguard Worker    def filename_pattern(self):
365*cda5da8dSAndroid Build Coastguard Worker        return self._filename_pattern
366*cda5da8dSAndroid Build Coastguard Worker
367*cda5da8dSAndroid Build Coastguard Worker    def _match_frame_impl(self, filename, lineno):
368*cda5da8dSAndroid Build Coastguard Worker        filename = _normalize_filename(filename)
369*cda5da8dSAndroid Build Coastguard Worker        if not fnmatch.fnmatch(filename, self._filename_pattern):
370*cda5da8dSAndroid Build Coastguard Worker            return False
371*cda5da8dSAndroid Build Coastguard Worker        if self.lineno is None:
372*cda5da8dSAndroid Build Coastguard Worker            return True
373*cda5da8dSAndroid Build Coastguard Worker        else:
374*cda5da8dSAndroid Build Coastguard Worker            return (lineno == self.lineno)
375*cda5da8dSAndroid Build Coastguard Worker
376*cda5da8dSAndroid Build Coastguard Worker    def _match_frame(self, filename, lineno):
377*cda5da8dSAndroid Build Coastguard Worker        return self._match_frame_impl(filename, lineno) ^ (not self.inclusive)
378*cda5da8dSAndroid Build Coastguard Worker
379*cda5da8dSAndroid Build Coastguard Worker    def _match_traceback(self, traceback):
380*cda5da8dSAndroid Build Coastguard Worker        if self.all_frames:
381*cda5da8dSAndroid Build Coastguard Worker            if any(self._match_frame_impl(filename, lineno)
382*cda5da8dSAndroid Build Coastguard Worker                   for filename, lineno in traceback):
383*cda5da8dSAndroid Build Coastguard Worker                return self.inclusive
384*cda5da8dSAndroid Build Coastguard Worker            else:
385*cda5da8dSAndroid Build Coastguard Worker                return (not self.inclusive)
386*cda5da8dSAndroid Build Coastguard Worker        else:
387*cda5da8dSAndroid Build Coastguard Worker            filename, lineno = traceback[0]
388*cda5da8dSAndroid Build Coastguard Worker            return self._match_frame(filename, lineno)
389*cda5da8dSAndroid Build Coastguard Worker
390*cda5da8dSAndroid Build Coastguard Worker    def _match(self, trace):
391*cda5da8dSAndroid Build Coastguard Worker        domain, size, traceback, total_nframe = trace
392*cda5da8dSAndroid Build Coastguard Worker        res = self._match_traceback(traceback)
393*cda5da8dSAndroid Build Coastguard Worker        if self.domain is not None:
394*cda5da8dSAndroid Build Coastguard Worker            if self.inclusive:
395*cda5da8dSAndroid Build Coastguard Worker                return res and (domain == self.domain)
396*cda5da8dSAndroid Build Coastguard Worker            else:
397*cda5da8dSAndroid Build Coastguard Worker                return res or (domain != self.domain)
398*cda5da8dSAndroid Build Coastguard Worker        return res
399*cda5da8dSAndroid Build Coastguard Worker
400*cda5da8dSAndroid Build Coastguard Worker
401*cda5da8dSAndroid Build Coastguard Workerclass DomainFilter(BaseFilter):
402*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, inclusive, domain):
403*cda5da8dSAndroid Build Coastguard Worker        super().__init__(inclusive)
404*cda5da8dSAndroid Build Coastguard Worker        self._domain = domain
405*cda5da8dSAndroid Build Coastguard Worker
406*cda5da8dSAndroid Build Coastguard Worker    @property
407*cda5da8dSAndroid Build Coastguard Worker    def domain(self):
408*cda5da8dSAndroid Build Coastguard Worker        return self._domain
409*cda5da8dSAndroid Build Coastguard Worker
410*cda5da8dSAndroid Build Coastguard Worker    def _match(self, trace):
411*cda5da8dSAndroid Build Coastguard Worker        domain, size, traceback, total_nframe = trace
412*cda5da8dSAndroid Build Coastguard Worker        return (domain == self.domain) ^ (not self.inclusive)
413*cda5da8dSAndroid Build Coastguard Worker
414*cda5da8dSAndroid Build Coastguard Worker
415*cda5da8dSAndroid Build Coastguard Workerclass Snapshot:
416*cda5da8dSAndroid Build Coastguard Worker    """
417*cda5da8dSAndroid Build Coastguard Worker    Snapshot of traces of memory blocks allocated by Python.
418*cda5da8dSAndroid Build Coastguard Worker    """
419*cda5da8dSAndroid Build Coastguard Worker
420*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, traces, traceback_limit):
421*cda5da8dSAndroid Build Coastguard Worker        # traces is a tuple of trace tuples: see _Traces constructor for
422*cda5da8dSAndroid Build Coastguard Worker        # the exact format
423*cda5da8dSAndroid Build Coastguard Worker        self.traces = _Traces(traces)
424*cda5da8dSAndroid Build Coastguard Worker        self.traceback_limit = traceback_limit
425*cda5da8dSAndroid Build Coastguard Worker
426*cda5da8dSAndroid Build Coastguard Worker    def dump(self, filename):
427*cda5da8dSAndroid Build Coastguard Worker        """
428*cda5da8dSAndroid Build Coastguard Worker        Write the snapshot into a file.
429*cda5da8dSAndroid Build Coastguard Worker        """
430*cda5da8dSAndroid Build Coastguard Worker        with open(filename, "wb") as fp:
431*cda5da8dSAndroid Build Coastguard Worker            pickle.dump(self, fp, pickle.HIGHEST_PROTOCOL)
432*cda5da8dSAndroid Build Coastguard Worker
433*cda5da8dSAndroid Build Coastguard Worker    @staticmethod
434*cda5da8dSAndroid Build Coastguard Worker    def load(filename):
435*cda5da8dSAndroid Build Coastguard Worker        """
436*cda5da8dSAndroid Build Coastguard Worker        Load a snapshot from a file.
437*cda5da8dSAndroid Build Coastguard Worker        """
438*cda5da8dSAndroid Build Coastguard Worker        with open(filename, "rb") as fp:
439*cda5da8dSAndroid Build Coastguard Worker            return pickle.load(fp)
440*cda5da8dSAndroid Build Coastguard Worker
441*cda5da8dSAndroid Build Coastguard Worker    def _filter_trace(self, include_filters, exclude_filters, trace):
442*cda5da8dSAndroid Build Coastguard Worker        if include_filters:
443*cda5da8dSAndroid Build Coastguard Worker            if not any(trace_filter._match(trace)
444*cda5da8dSAndroid Build Coastguard Worker                       for trace_filter in include_filters):
445*cda5da8dSAndroid Build Coastguard Worker                return False
446*cda5da8dSAndroid Build Coastguard Worker        if exclude_filters:
447*cda5da8dSAndroid Build Coastguard Worker            if any(not trace_filter._match(trace)
448*cda5da8dSAndroid Build Coastguard Worker                   for trace_filter in exclude_filters):
449*cda5da8dSAndroid Build Coastguard Worker                return False
450*cda5da8dSAndroid Build Coastguard Worker        return True
451*cda5da8dSAndroid Build Coastguard Worker
452*cda5da8dSAndroid Build Coastguard Worker    def filter_traces(self, filters):
453*cda5da8dSAndroid Build Coastguard Worker        """
454*cda5da8dSAndroid Build Coastguard Worker        Create a new Snapshot instance with a filtered traces sequence, filters
455*cda5da8dSAndroid Build Coastguard Worker        is a list of Filter or DomainFilter instances.  If filters is an empty
456*cda5da8dSAndroid Build Coastguard Worker        list, return a new Snapshot instance with a copy of the traces.
457*cda5da8dSAndroid Build Coastguard Worker        """
458*cda5da8dSAndroid Build Coastguard Worker        if not isinstance(filters, Iterable):
459*cda5da8dSAndroid Build Coastguard Worker            raise TypeError("filters must be a list of filters, not %s"
460*cda5da8dSAndroid Build Coastguard Worker                            % type(filters).__name__)
461*cda5da8dSAndroid Build Coastguard Worker        if filters:
462*cda5da8dSAndroid Build Coastguard Worker            include_filters = []
463*cda5da8dSAndroid Build Coastguard Worker            exclude_filters = []
464*cda5da8dSAndroid Build Coastguard Worker            for trace_filter in filters:
465*cda5da8dSAndroid Build Coastguard Worker                if trace_filter.inclusive:
466*cda5da8dSAndroid Build Coastguard Worker                    include_filters.append(trace_filter)
467*cda5da8dSAndroid Build Coastguard Worker                else:
468*cda5da8dSAndroid Build Coastguard Worker                    exclude_filters.append(trace_filter)
469*cda5da8dSAndroid Build Coastguard Worker            new_traces = [trace for trace in self.traces._traces
470*cda5da8dSAndroid Build Coastguard Worker                          if self._filter_trace(include_filters,
471*cda5da8dSAndroid Build Coastguard Worker                                                exclude_filters,
472*cda5da8dSAndroid Build Coastguard Worker                                                trace)]
473*cda5da8dSAndroid Build Coastguard Worker        else:
474*cda5da8dSAndroid Build Coastguard Worker            new_traces = self.traces._traces.copy()
475*cda5da8dSAndroid Build Coastguard Worker        return Snapshot(new_traces, self.traceback_limit)
476*cda5da8dSAndroid Build Coastguard Worker
477*cda5da8dSAndroid Build Coastguard Worker    def _group_by(self, key_type, cumulative):
478*cda5da8dSAndroid Build Coastguard Worker        if key_type not in ('traceback', 'filename', 'lineno'):
479*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("unknown key_type: %r" % (key_type,))
480*cda5da8dSAndroid Build Coastguard Worker        if cumulative and key_type not in ('lineno', 'filename'):
481*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("cumulative mode cannot by used "
482*cda5da8dSAndroid Build Coastguard Worker                             "with key type %r" % key_type)
483*cda5da8dSAndroid Build Coastguard Worker
484*cda5da8dSAndroid Build Coastguard Worker        stats = {}
485*cda5da8dSAndroid Build Coastguard Worker        tracebacks = {}
486*cda5da8dSAndroid Build Coastguard Worker        if not cumulative:
487*cda5da8dSAndroid Build Coastguard Worker            for trace in self.traces._traces:
488*cda5da8dSAndroid Build Coastguard Worker                domain, size, trace_traceback, total_nframe = trace
489*cda5da8dSAndroid Build Coastguard Worker                try:
490*cda5da8dSAndroid Build Coastguard Worker                    traceback = tracebacks[trace_traceback]
491*cda5da8dSAndroid Build Coastguard Worker                except KeyError:
492*cda5da8dSAndroid Build Coastguard Worker                    if key_type == 'traceback':
493*cda5da8dSAndroid Build Coastguard Worker                        frames = trace_traceback
494*cda5da8dSAndroid Build Coastguard Worker                    elif key_type == 'lineno':
495*cda5da8dSAndroid Build Coastguard Worker                        frames = trace_traceback[:1]
496*cda5da8dSAndroid Build Coastguard Worker                    else: # key_type == 'filename':
497*cda5da8dSAndroid Build Coastguard Worker                        frames = ((trace_traceback[0][0], 0),)
498*cda5da8dSAndroid Build Coastguard Worker                    traceback = Traceback(frames)
499*cda5da8dSAndroid Build Coastguard Worker                    tracebacks[trace_traceback] = traceback
500*cda5da8dSAndroid Build Coastguard Worker                try:
501*cda5da8dSAndroid Build Coastguard Worker                    stat = stats[traceback]
502*cda5da8dSAndroid Build Coastguard Worker                    stat.size += size
503*cda5da8dSAndroid Build Coastguard Worker                    stat.count += 1
504*cda5da8dSAndroid Build Coastguard Worker                except KeyError:
505*cda5da8dSAndroid Build Coastguard Worker                    stats[traceback] = Statistic(traceback, size, 1)
506*cda5da8dSAndroid Build Coastguard Worker        else:
507*cda5da8dSAndroid Build Coastguard Worker            # cumulative statistics
508*cda5da8dSAndroid Build Coastguard Worker            for trace in self.traces._traces:
509*cda5da8dSAndroid Build Coastguard Worker                domain, size, trace_traceback, total_nframe = trace
510*cda5da8dSAndroid Build Coastguard Worker                for frame in trace_traceback:
511*cda5da8dSAndroid Build Coastguard Worker                    try:
512*cda5da8dSAndroid Build Coastguard Worker                        traceback = tracebacks[frame]
513*cda5da8dSAndroid Build Coastguard Worker                    except KeyError:
514*cda5da8dSAndroid Build Coastguard Worker                        if key_type == 'lineno':
515*cda5da8dSAndroid Build Coastguard Worker                            frames = (frame,)
516*cda5da8dSAndroid Build Coastguard Worker                        else: # key_type == 'filename':
517*cda5da8dSAndroid Build Coastguard Worker                            frames = ((frame[0], 0),)
518*cda5da8dSAndroid Build Coastguard Worker                        traceback = Traceback(frames)
519*cda5da8dSAndroid Build Coastguard Worker                        tracebacks[frame] = traceback
520*cda5da8dSAndroid Build Coastguard Worker                    try:
521*cda5da8dSAndroid Build Coastguard Worker                        stat = stats[traceback]
522*cda5da8dSAndroid Build Coastguard Worker                        stat.size += size
523*cda5da8dSAndroid Build Coastguard Worker                        stat.count += 1
524*cda5da8dSAndroid Build Coastguard Worker                    except KeyError:
525*cda5da8dSAndroid Build Coastguard Worker                        stats[traceback] = Statistic(traceback, size, 1)
526*cda5da8dSAndroid Build Coastguard Worker        return stats
527*cda5da8dSAndroid Build Coastguard Worker
528*cda5da8dSAndroid Build Coastguard Worker    def statistics(self, key_type, cumulative=False):
529*cda5da8dSAndroid Build Coastguard Worker        """
530*cda5da8dSAndroid Build Coastguard Worker        Group statistics by key_type. Return a sorted list of Statistic
531*cda5da8dSAndroid Build Coastguard Worker        instances.
532*cda5da8dSAndroid Build Coastguard Worker        """
533*cda5da8dSAndroid Build Coastguard Worker        grouped = self._group_by(key_type, cumulative)
534*cda5da8dSAndroid Build Coastguard Worker        statistics = list(grouped.values())
535*cda5da8dSAndroid Build Coastguard Worker        statistics.sort(reverse=True, key=Statistic._sort_key)
536*cda5da8dSAndroid Build Coastguard Worker        return statistics
537*cda5da8dSAndroid Build Coastguard Worker
538*cda5da8dSAndroid Build Coastguard Worker    def compare_to(self, old_snapshot, key_type, cumulative=False):
539*cda5da8dSAndroid Build Coastguard Worker        """
540*cda5da8dSAndroid Build Coastguard Worker        Compute the differences with an old snapshot old_snapshot. Get
541*cda5da8dSAndroid Build Coastguard Worker        statistics as a sorted list of StatisticDiff instances, grouped by
542*cda5da8dSAndroid Build Coastguard Worker        group_by.
543*cda5da8dSAndroid Build Coastguard Worker        """
544*cda5da8dSAndroid Build Coastguard Worker        new_group = self._group_by(key_type, cumulative)
545*cda5da8dSAndroid Build Coastguard Worker        old_group = old_snapshot._group_by(key_type, cumulative)
546*cda5da8dSAndroid Build Coastguard Worker        statistics = _compare_grouped_stats(old_group, new_group)
547*cda5da8dSAndroid Build Coastguard Worker        statistics.sort(reverse=True, key=StatisticDiff._sort_key)
548*cda5da8dSAndroid Build Coastguard Worker        return statistics
549*cda5da8dSAndroid Build Coastguard Worker
550*cda5da8dSAndroid Build Coastguard Worker
551*cda5da8dSAndroid Build Coastguard Workerdef take_snapshot():
552*cda5da8dSAndroid Build Coastguard Worker    """
553*cda5da8dSAndroid Build Coastguard Worker    Take a snapshot of traces of memory blocks allocated by Python.
554*cda5da8dSAndroid Build Coastguard Worker    """
555*cda5da8dSAndroid Build Coastguard Worker    if not is_tracing():
556*cda5da8dSAndroid Build Coastguard Worker        raise RuntimeError("the tracemalloc module must be tracing memory "
557*cda5da8dSAndroid Build Coastguard Worker                           "allocations to take a snapshot")
558*cda5da8dSAndroid Build Coastguard Worker    traces = _get_traces()
559*cda5da8dSAndroid Build Coastguard Worker    traceback_limit = get_traceback_limit()
560*cda5da8dSAndroid Build Coastguard Worker    return Snapshot(traces, traceback_limit)
561