#! /usr/bin/env python3.3
"""Tools for logging space consumption.
"""
from collections import defaultdict
import logging
import operator
import os
import resource
import p4gf_util
# Linux seems to report in KB while Mac uses bytes.
MAXRSS_KB = 2**10 if os.uname()[0] == "Darwin" else 2**1
MAXRSS_MB = 2**20 if os.uname()[0] == "Darwin" else 2**10
class MemPerFunc:
"""Instrumentation to report how much memory certain functions
allocate.
"""
def __init__(self
, log = None
, log_level = logging.DEBUG3
, report_period_record_ct = 1000
):
self.log = log
self.log_level = log_level
self._report_period_record_ct = report_period_record_ct
self._report_ct = 0
self._current_mem_kb = 0
self.data = defaultdict(int)
if not self.log:
self.log = logging.getLogger(__name__)
def record(self, func_name):
"""Assign memory growth to funct_name.
Assigns all memory growth since the previous call to record().
"""
curr_mem_kb = current_memory_kb()
# First call? No base from which to diff.
if not self._current_mem_kb:
self._current_mem_kb = curr_mem_kb
return
diff = curr_mem_kb - self._current_mem_kb
self.data[func_name] += diff
self._current_mem_kb = curr_mem_kb
self._report_ct += 1
if not self._report_ct % self._report_period_record_ct:
self.report()
def report(self):
"""Dump our data to log."""
if not self.log.isEnabledFor(self.log_level):
return
kv = sorted(self.data.items(), key=operator.itemgetter(1), reverse=True)
(kl, vl) = zip(*kv)
lines = p4gf_util.tabular(kl, vl)
self.log.log(self.log_level, "Memory by function (KB):\n" + "\n".join(lines))
# -- module-wide -------------------------------------------------------------
def current_memory_kb():
"""How much memory does Python consume right now?"""
return int(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / MAXRSS_KB)