def track_memory_wrapper(*args, **kwargs): memory_info = {} tracker = ClassTracker() for cls in apps.get_models() + [Context, Template]: # track all models from registered apps, plus some standard Django ones tracker.track_class(cls) try: tracker.create_snapshot("before") memory_info["before"] = ProcessMemoryInfo() result = fn(*args, **kwargs) memory_info["after"] = ProcessMemoryInfo() tracker.create_snapshot("after") memory_info["stats"] = tracker.stats memory_info["stats"].annotate() return result finally: # record a whole bunch of memory statistics... resources = [ ("resident set size", memory_info["after"].rss), ("virtual size", memory_info["after"].vsz), ] resources.extend(memory_info["after"] - memory_info["before"]) resources = [(k, pp(v)) for k, v in resources] resources.extend(memory_info["after"].os_specific) # record each tracked class as of the final snapshot... classes_stats = [] snapshot = memory_info["stats"].snapshots[-1] for class_name in memory_info["stats"].tracked_classes: # history is a list of tuples that is updated on every creation/deletions: (timestamp, n_instances) history = [ n for _, n in memory_info["stats"].history[class_name] ] if history: classes_stats.append({ "name": class_name, "n_instances": len(history), "min_instances": min(history), "max_instances": max(history), "size": pp( snapshot.classes.get(class_name, {}).get("sum", 0)), }) if not path: stream = sys.stdout else: stream = open(path, "w") print("\nRESOURCES", file=stream) for k, v in resources: print(f"{k:<26}: {v:>10}", file=stream) print("\nCLASSES", file=stream) for class_stats in classes_stats: print( "{name}: created/deleted {n_instances} times for a min/max of {min_instances}/{max_instances} instances: {size:>10}" .format(**class_stats), file=stream, ) stream.closed tracker.detach_all_classes()
class MemoryPanel(Panel): name = 'pympler' title = 'Memory' template = 'memory_panel.html' classes = [Context, Template] def process_request(self, request): self._tracker = ClassTracker() for cls in apps.get_models() + self.classes: self._tracker.track_class(cls) self._tracker.create_snapshot('before') self.record_stats({'before': ProcessMemoryInfo()}) def process_response(self, request, response): self.record_stats({'after': ProcessMemoryInfo()}) self._tracker.create_snapshot('after') stats = self._tracker.stats stats.annotate() self.record_stats({'stats': stats}) def enable_instrumentation(self): self._tracker = ClassTracker() for cls in apps.get_models() + self.classes: self._tracker.track_class(cls) def disable_instrumentation(self): self._tracker.detach_all_classes() def nav_subtitle(self): context = self.get_stats() before = context['before'] after = context['after'] rss = after.rss delta = rss - before.rss delta = ('(+%s)' % pp(delta)) if delta > 0 else '' return "%s %s" % (pp(rss), delta) @property def content(self): context = self.get_stats() before = context['before'] after = context['after'] stats = context['stats'] rows = [ ('Resident set size', after.rss), ('Virtual size', after.vsz), ] rows.extend(after - before) rows = [(key, pp(value)) for key, value in rows] rows.extend(after.os_specific) classes = [] snapshot = stats.snapshots[-1] for model in stats.tracked_classes: history = [cnt for _, cnt in stats.history[model]] size = snapshot.classes.get(model, {}).get('sum', 0) if history and history[-1] > 0: classes.append((model, history, pp(size))) context.update({'rows': rows, 'classes': classes}) return render_to_string(self.template, context)
class MemoryPanel(Panel): name = 'pympler' title = 'Memory' template = 'memory_panel.html' classes = [Context, Template] def process_request(self, request): self._tracker = ClassTracker() for cls in get_models() + self.classes: self._tracker.track_class(cls) self._tracker.create_snapshot('before') self.record_stats({'before': ProcessMemoryInfo()}) def process_response(self, request, response): self.record_stats({'after': ProcessMemoryInfo()}) self._tracker.create_snapshot('after') stats = self._tracker.stats stats.annotate() self.record_stats({'stats': stats}) def enable_instrumentation(self): self._tracker = ClassTracker() for cls in get_models() + self.classes: self._tracker.track_class(cls) def disable_instrumentation(self): self._tracker.detach_all_classes() def nav_subtitle(self): context = self.get_stats() before = context['before'] after = context['after'] rss = after.rss delta = rss - before.rss delta = ('(+%s)' % pp(delta)) if delta > 0 else '' return "%s %s" % (pp(rss), delta) @property def content(self): context = self.get_stats() before = context['before'] after = context['after'] stats = context['stats'] rows = [('Resident set size', after.rss), ('Virtual size', after.vsz), ] rows.extend(after - before) rows = [(key, pp(value)) for key, value in rows] rows.extend(after.os_specific) classes = [] snapshot = stats.snapshots[-1] for model in stats.tracked_classes: history = [cnt for _, cnt in stats.history[model]] size = snapshot.classes.get(model, {}).get('sum', 0) if cnt > 0: classes.append((model, history, pp(size))) context.update({'rows': rows, 'classes': classes}) return render_to_string(self.template, context)