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