[tor-commits] [stem/master] Add a size_of function to get memory usage
atagar at torproject.org
atagar at torproject.org
Mon Aug 21 19:07:57 UTC 2017
commit eaba27d496609a4fdd9b8270793f621d219ad323
Author: Damian Johnson <atagar at torproject.org>
Date: Mon Aug 21 12:08:05 2017 -0700
Add a size_of function to get memory usage
Presently investigating nyx's memory usage but turns out sys.getsizeof doesn't
understand how to recurse collections. Adding a simple helper to do so.
---
docs/change_log.rst | 1 +
stem/util/system.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++++
test/unit/util/system.py | 9 +++++++++
3 files changed, 59 insertions(+)
diff --git a/docs/change_log.rst b/docs/change_log.rst
index 59b546ad..204d5718 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -80,6 +80,7 @@ The following are only available within Stem's `git repository
* Supporing pid arguments in :func:`~stem.util.system.is_running`
* Normalized :func:`~stem.util.term.format` to return unicode
* Don't load vim swap files as configurations
+ * Added :func:`~stem.util.system.size_of`
* **Interpreter**
diff --git a/stem/util/system.py b/stem/util/system.py
index b3b57da9..0569992b 100644
--- a/stem/util/system.py
+++ b/stem/util/system.py
@@ -25,6 +25,7 @@ best-effort, providing **None** if the lookup fails.
is_available - determines if a command is available on this system
is_running - determines if a given process is running
+ size_of - provides the memory usage of an object
call - runs the given system command and provides back the results
name_by_pid - gets the name for a process by the given pid
@@ -62,15 +63,18 @@ best-effort, providing **None** if the lookup fails.
==================== ===========
"""
+import collections
import ctypes
import ctypes.util
import distutils.spawn
+import itertools
import mimetypes
import multiprocessing
import os
import platform
import re
import subprocess
+import sys
import tarfile
import threading
import time
@@ -89,6 +93,17 @@ State = stem.util.enum.UppercaseEnum(
'FAILED',
)
+DEFAULT_SIZE = sys.getsizeof(0) # estimate if object lacks a __sizeof__
+
+SIZE_RECURSES = {
+ tuple: iter,
+ list: iter,
+ collections.deque: iter,
+ dict: lambda d: itertools.chain.from_iterable(d.items()),
+ set: iter,
+ frozenset: iter,
+}
+
# Mapping of commands to if they're available or not.
CMD_AVAILABLE_CACHE = {}
@@ -422,6 +437,40 @@ def is_running(command):
return None
+def size_of(obj, exclude = None):
+ """
+ Provides the `approximate memory usage of an object
+ <https://code.activestate.com/recipes/577504/>`_. This can recurse tuples,
+ lists, deques, dicts, and sets. To teach this function to inspect additional
+ object types expand SIZE_RECURSES...
+
+ ::
+
+ stem.util.system.SIZE_RECURSES[SomeClass] = SomeClass.get_elements
+
+ .. versionadded:: 1.6.0
+
+ :param object obj: object to provide the size of
+ :param set exclude: object ids to exclude from size estimation
+
+ :returns: **int** with the size of the object in bytes
+ """
+
+ if exclude is None:
+ exclude = set()
+ elif id(obj) in exclude:
+ return 0
+
+ size = sys.getsizeof(obj, DEFAULT_SIZE)
+ exclude.add(id(obj))
+
+ if type(obj) in SIZE_RECURSES:
+ for entry in SIZE_RECURSES[type(obj)](obj):
+ size += size_of(obj, exclude)
+
+ return size
+
+
def name_by_pid(pid):
"""
Attempts to determine the name a given process is running under (not
diff --git a/test/unit/util/system.py b/test/unit/util/system.py
index e519aec8..a331aab7 100644
--- a/test/unit/util/system.py
+++ b/test/unit/util/system.py
@@ -148,6 +148,15 @@ class TestSystem(unittest.TestCase):
self.assertFalse(system.is_running('irssi'))
self.assertEqual(None, system.is_running('irssi'))
+ def test_size_of(self):
+ """
+ Exercises the size_of function.
+ """
+
+ self.assertTrue(10 < system.size_of('hello') < 50)
+ self.assertTrue(10 < system.size_of([]) < 50)
+ self.assertTrue(system.size_of([]) < system.size_of(['hello']) < system.size_of(['hello', 'world']))
+
@patch('stem.util.system.call')
@patch('stem.util.proc.is_available', Mock(return_value = False))
@patch('stem.util.system.is_available', Mock(return_value = True))
More information about the tor-commits
mailing list