This repository has been archived by the owner on Jul 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #812 from mozilla-services/feat/802
add a /_memusage API on a separate (internal port)
- Loading branch information
Showing
8 changed files
with
262 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
#! /usr/bin/env python | ||
""" | ||
Prints a human-readable total out of a dumpfile produced | ||
by gc.dump_rpy_heap(), and optionally a typeids.txt. | ||
Syntax: dump.py <dumpfile> [<typeids.txt>] | ||
By default, typeids.txt is loaded from the same dir as dumpfile. | ||
""" | ||
import array | ||
import os | ||
import struct | ||
import sys | ||
|
||
|
||
class Stat(object): | ||
summary = {} | ||
typeids = {0: '<GCROOT>'} | ||
|
||
def summarize(self, filename, stream=None): | ||
a = self.load_dump_file(filename) | ||
self.summary = {} # {typenum: [count, totalsize]} | ||
for obj in self.walk(a, stream=stream): | ||
self.add_object_summary(obj[2], obj[3]) | ||
|
||
def load_typeids(self, filename_or_iter): | ||
self.typeids = Stat.typeids.copy() | ||
if isinstance(filename_or_iter, str): | ||
iter = open(filename_or_iter) | ||
else: | ||
iter = filename_or_iter | ||
for num, line in enumerate(iter): | ||
if num == 0: | ||
continue | ||
if not line: | ||
continue | ||
words = line.split() | ||
if words[0].startswith('member'): | ||
del words[0] | ||
if words[0] == 'GcStruct': | ||
del words[0] | ||
self.typeids[num] = ' '.join(words) | ||
|
||
def get_type_name(self, num): | ||
return self.typeids.get(num, '<typenum %d>' % num) | ||
|
||
def print_summary(self, stream): | ||
items = self.summary.items() | ||
items.sort(key=lambda (typenum, stat): stat[1]) # sort by totalsize | ||
totalsize = 0 | ||
for typenum, stat in items: | ||
totalsize += stat[1] | ||
stream.write('%8d %8.2fM %s\n' % | ||
(stat[0], | ||
stat[1] / (1024.0*1024.0), | ||
self.get_type_name(typenum))) | ||
stream.write('total %.1fM\n' % (totalsize / (1024.0*1024.0))) | ||
|
||
def load_dump_file(self, filename): | ||
f = open(filename, 'rb') | ||
f.seek(0, 2) | ||
end = f.tell() | ||
f.seek(0) | ||
a = array.array('l') | ||
a.fromfile(f, end / struct.calcsize('l')) | ||
f.close() | ||
return a | ||
|
||
def add_object_summary(self, typenum, sizeobj): | ||
try: | ||
stat = self.summary[typenum] | ||
except KeyError: | ||
stat = self.summary[typenum] = [0, 0] | ||
stat[0] += 1 | ||
stat[1] += sizeobj | ||
|
||
def walk(self, a, start=0, stop=None, stream=None): | ||
assert a[-1] == -1, "invalid or truncated dump file (or 32/64-bit mix)" | ||
assert a[-2] != -1, "invalid or truncated dump file (or 32/64-bit mix)" | ||
if stream: | ||
stream.write('walking...') | ||
i = start | ||
if stop is None: | ||
stop = len(a) | ||
while i < stop: | ||
j = i + 3 | ||
while a[j] != -1: | ||
j += 1 | ||
yield (i, a[i], a[i+1], a[i+2], a[i+3:j]) | ||
i = j + 1 | ||
if stream: | ||
stream.write('done\n') | ||
|
||
|
||
if __name__ == '__main__': | ||
if len(sys.argv) <= 1: | ||
print >> sys.stderr, __doc__ | ||
sys.exit(2) | ||
stat = Stat() | ||
stat.summarize(sys.argv[1], stream=sys.stderr) | ||
# | ||
if len(sys.argv) > 2: | ||
typeid_name = sys.argv[2] | ||
else: | ||
typeid_name = os.path.join(os.path.dirname(sys.argv[1]), 'typeids.txt') | ||
if os.path.isfile(typeid_name): | ||
stat.load_typeids(typeid_name) | ||
else: | ||
import gc | ||
import zlib | ||
stat.load_typeids(zlib.decompress(gc.get_typeids_z()).split("\n")) | ||
# | ||
stat.print_summary(sys.stdout) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
"""Produces memory usage information""" | ||
import gc | ||
import objgraph | ||
import os | ||
import resource | ||
import subprocess | ||
import tempfile | ||
import zlib | ||
from StringIO import StringIO | ||
|
||
from autopush.gcdump import Stat | ||
|
||
|
||
def memusage(): | ||
"""Returning a str of memory usage stats""" | ||
# type() -> str | ||
def trap_err(func, *args, **kwargs): | ||
try: | ||
return func(*args, **kwargs) | ||
except Exception as e: | ||
# include both __str/repr__, sometimes one's useless | ||
buf.writelines([func.__name__, ': ', repr(e), ': ', str(e)]) | ||
|
||
buf = StringIO() | ||
rusage = trap_err(resource.getrusage, resource.RUSAGE_SELF) | ||
buf.writelines([repr(rusage), '\n\n']) | ||
trap_err(objgraph.show_most_common_types, limit=0, file=buf) | ||
buf.write('\n\n') | ||
pmap = trap_err(subprocess.check_output, ['pmap', '-x', str(os.getpid())], | ||
stderr=subprocess.STDOUT) | ||
buf.writelines([pmap, '\n\n']) | ||
trap_err(dump_rpy_heap, buf) | ||
return buf.getvalue() | ||
|
||
|
||
def dump_rpy_heap(stream): | ||
"""Write PyPy's gcdump to the specified stream""" | ||
if not hasattr(gc, '_dump_rpy_heap'): | ||
# not PyPy | ||
return | ||
|
||
with tempfile.NamedTemporaryFile('wb') as fp: | ||
gc._dump_rpy_heap(fp.fileno()) | ||
stream.write("{} size: {}\n".format(fp.name, os.stat(fp.name).st_size)) | ||
stat = Stat() | ||
stat.summarize(fp.name, stream=None) | ||
stat.load_typeids(zlib.decompress(gc.get_typeids_z()).split("\n")) | ||
stream.write('\n\n') | ||
stat.print_summary(stream) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters