Merge pull request #2854 from lbryio/tracemalloc_api

Adds tracemalloc api for memory troubleshooting
This commit is contained in:
Lex Berezhny 2020-03-16 22:15:41 -04:00 committed by GitHub
commit bea94ce8ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 98 additions and 0 deletions

View file

@ -1,3 +1,4 @@
import linecache
import os import os
import re import re
import asyncio import asyncio
@ -8,6 +9,7 @@ import inspect
import typing import typing
import random import random
import hashlib import hashlib
import tracemalloc
from urllib.parse import urlencode, quote from urllib.parse import urlencode, quote
from typing import Callable, Optional, List from typing import Callable, Optional, List
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
@ -4531,6 +4533,85 @@ class Daemon(metaclass=JSONRPCServerType):
result['node_id'] = hexlify(self.dht_node.protocol.node_id).decode() result['node_id'] = hexlify(self.dht_node.protocol.node_id).decode()
return result return result
TRACEMALLOC_DOC = """
Controls and queries tracemalloc memory tracing tools for troubleshooting.
"""
def jsonrpc_tracemalloc_enable(self): # pylint: disable=no-self-use
"""
Enable tracemalloc memory tracing
Usage:
jsonrpc_tracemalloc_enable
Options:
None
Returns:
(bool) is it tracing?
"""
tracemalloc.start()
return tracemalloc.is_tracing()
def jsonrpc_tracemalloc_disable(self): # pylint: disable=no-self-use
"""
Disable tracemalloc memory tracing
Usage:
jsonrpc_tracemalloc_disable
Options:
None
Returns:
(bool) is it tracing?
"""
tracemalloc.stop()
return tracemalloc.is_tracing()
def jsonrpc_tracemalloc_top(self, items: int = 10): # pylint: disable=no-self-use
"""
Show most common objects, the place that created them and their size.
Usage:
jsonrpc_tracemalloc_top [(<items> | --items=<items>)]
Options:
--items=<items> : (int) maximum items to return, from the most common
Returns:
(dict) dictionary containing most common objects in memory
{
"line": (str) filename and line number where it was created,
"code": (str) code that created it,
"size": (int) size in bytes, for each "memory block",
"count" (int) number of memory blocks
}
"""
if not tracemalloc.is_tracing():
raise Exception("Enable tracemalloc first! See 'tracemalloc set' command.")
stats = tracemalloc.take_snapshot().filter_traces((
tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
tracemalloc.Filter(False, "<unknown>"),
# tracemalloc and linecache here use some memory, but thats not relevant
tracemalloc.Filter(False, tracemalloc.__file__),
tracemalloc.Filter(False, linecache.__file__),
)).statistics('lineno', True)
results = []
for stat in stats:
frame = stat.traceback[0]
filename = os.sep.join(frame.filename.split(os.sep)[-2:])
line = linecache.getline(frame.filename, frame.lineno).strip()
results.append({
"line": f"{filename}:{frame.lineno}",
"code": line,
"size": stat.size,
"count": stat.count
})
if len(results) == items:
break
return results
COMMENT_DOC = """ COMMENT_DOC = """
View, create and abandon comments. View, create and abandon comments.
""" """

View file

@ -38,3 +38,20 @@ class SettingsManagement(CommandTestCase):
self.assertTrue(self.daemon.analytics_manager.enabled) self.assertTrue(self.daemon.analytics_manager.enabled)
self.assertTrue(loggly.enabled) self.assertTrue(loggly.enabled)
self.daemon.jsonrpc_settings_set('share_usage_data', False) self.daemon.jsonrpc_settings_set('share_usage_data', False)
class TroubleshootingCommands(CommandTestCase):
async def test_tracemalloc_commands(self):
self.addCleanup(self.daemon.jsonrpc_tracemalloc_disable)
self.assertFalse(self.daemon.jsonrpc_tracemalloc_disable())
self.assertTrue(self.daemon.jsonrpc_tracemalloc_enable())
class WeirdObject():
pass
hold_em = [WeirdObject() for _ in range(500)]
top = self.daemon.jsonrpc_tracemalloc_top(1)
self.assertEqual(1, len(top))
self.assertEqual('hold_em = [WeirdObject() for _ in range(500)]', top[0]['code'])
self.assertTrue(top[0]['line'].startswith('other/test_other_commands.py:'))
self.assertGreaterEqual(top[0]['count'], 500)
self.assertGreater(top[0]['size'], 0) # just matters that its a positive integer