GmCapsule [gsorg-style]
More memory debugging
[1mdiff --git a/gmcapsule/gemini.py b/gmcapsule/gemini.py[m
[1mindex a930c73..43b98a9 100644[m
[1m--- a/gmcapsule/gemini.py[m
[1m+++ b/gmcapsule/gemini.py[m
[36m@@ -2,6 +2,7 @@[m
# License: BSD 2-Clause[m
[m
import fnmatch[m
[32m+[m[32mimport gc[m
import hashlib[m
import queue[m
import socket[m
[36m@@ -18,6 +19,54 @@[m [mdef report_error(stream, code, msg):[m
stream.sendall(f'{code} {msg}\r\n'.encode('utf-8'))[m
[m
[m
[32m+[m[32mmemtrace_lock = threading.Lock()[m
[32m+[m
[32m+[m
[32m+[m[32mdef display_memtop(snapshot, prev_snapshot, key_type='lineno', limit=10):[m
[32m+[m[32m import tracemalloc[m
[32m+[m[32m import linecache[m
[32m+[m[32m filters = ([m
[32m+[m[32m tracemalloc.Filter(False, ""),[m
[32m+[m[32m tracemalloc.Filter(False, ""),[m
[32m+[m[32m tracemalloc.Filter(False, "*/linecache.py"),[m
[32m+[m[32m tracemalloc.Filter(False, "*/tracemalloc.py")[m
[32m+[m[32m )[m
[32m+[m[32m snapshot = snapshot.filter_traces(filters)[m
[32m+[m[32m if prev_snapshot:[m
[32m+[m[32m prev_snapshot = prev_snapshot.filter_traces(filters)[m
[32m+[m[32m top_stats = snapshot.compare_to(prev_snapshot, key_type)[m
[32m+[m[32m top_type = 'delta'[m
[32m+[m[32m limit = 200[m
[32m+[m[32m else:[m
[32m+[m[32m top_stats = snapshot.statistics(key_type)[m
[32m+[m[32m top_type = 'malloc'[m
[32m+[m
[32m+[m[32m with memtrace_lock:[m
[32m+[m[32m print("\n\nTop %s %s" % (limit, top_type))[m
[32m+[m[32m for index, stat in enumerate(top_stats[:limit], 1):[m
[32m+[m[32m frame = stat.traceback[0][m
[32m+[m[32m if prev_snapshot:[m
[32m+[m[32m if stat.size_diff <= 0:[m
[32m+[m[32m continue[m
[32m+[m[32m print("#%s: %35s:%-5s \x1b[1m%.1f\x1b[0m KiB (%+.1f KiB) count=%d (%+d)"[m
[32m+[m[32m % (index, frame.filename[-35:], str(frame.lineno) + ':',[m
[32m+[m[32m stat.size / 1024, stat.size_diff / 1024, stat.count, stat.count_diff))[m
[32m+[m[32m else:[m
[32m+[m[32m print("#%s: %35s:%-5s \x1b[1m%.1f\x1b[0m KiB count=%d"[m
[32m+[m[32m % (index, frame.filename[-35:], str(frame.lineno) + ':',[m
[32m+[m[32m stat.size / 1024, stat.count))[m
[32m+[m[32m line = linecache.getline(frame.filename, frame.lineno).strip()[m
[32m+[m[32m if line:[m
[32m+[m[32m print('\x1b[2m %s\x1b[0m' % line)[m
[32m+[m
[32m+[m[32m other = top_stats[limit:][m
[32m+[m[32m if other:[m
[32m+[m[32m size = sum(stat.size for stat in other)[m
[32m+[m[32m print("%s other: %.1f KiB" % (len(other), size / 1024))[m
[32m+[m[32m total = sum(stat.size for stat in top_stats)[m
[32m+[m[32m print("Total size: %.1f KiB\n\n" % (total / 1024))[m
[32m+[m
[32m+[m
class Identity:[m
def __init__(self, cert):[m
self.cert = cert[m
[36m@@ -81,7 +130,13 @@[m [mclass Worker(threading.Thread):[m
self.jobs = server.work_queue[m
[m
def run(self):[m
[32m+[m[32m #if self.server.memtrace:[m
[32m+[m[32m # import tracemalloc[m
[32m+[m[32m #prev_malloc_snapshot = None[m
while True:[m
[32m+[m[32m #if self.server.memtrace:[m
[32m+[m[32m # malloc_snapshot = tracemalloc.take_snapshot()[m
[32m+[m
job = self.jobs.get()[m
if job is None:[m
break[m
[36m@@ -92,20 +147,28 @@[m [mclass Worker(threading.Thread):[m
except OpenSSL.SSL.SysCallError as error:[m
self.log(f'OpenSSL error: ' + str(error))[m
except Exception as error:[m
[31m- import traceback[m
[31m- traceback.print_exc()[m
[32m+[m[32m #import traceback[m
[32m+[m[32m #traceback.print_exc()[m
try:[m
report_error(stream, 42, str(error))[m
except:[m
pass[m
[31m- finally:[m
[31m- try:[m
[31m- stream.shutdown()[m
[31m- stream.close()[m
[31m- except:[m
[31m- pass[m
[32m+[m
[32m+[m[32m try:[m
[32m+[m[32m stream.shutdown()[m
[32m+[m[32m stream.close()[m
[32m+[m[32m except:[m
[32m+[m[32m pass[m
del from_addr[m
del stream[m
[32m+[m[32m gc.collect()[m
[32m+[m
[32m+[m[32m # if self.server.memtrace:[m
[32m+[m[32m # malloc_snapshot = tracemalloc.take_snapshot()[m
[32m+[m[32m # if prev_malloc_snapshot:[m
[32m+[m[32m # display_memtop(malloc_snapshot, prev_malloc_snapshot)[m
[32m+[m[32m # prev_malloc_snapshot = tracemalloc.take_snapshot()[m
[32m+[m
[m
def log(self, *args):[m
print(time.strftime('%Y-%m-%d %H:%M:%S'), f'[{self.id}]', '--', *args)[m
[36m@@ -253,34 +316,6 @@[m [mclass Worker(threading.Thread):[m
report_error(stream, 50, 'Permanent failure')[m
[m
[m
[31m-def display_memtop(snapshot, key_type='lineno', limit=10):[m
[31m- import tracemalloc[m
[31m- import linecache[m
[31m- snapshot = snapshot.filter_traces(([m
[31m- tracemalloc.Filter(False, ""),[m
[31m- tracemalloc.Filter(False, ""),[m
[31m- tracemalloc.Filter(False, "*/linecache.py"),[m
[31m- tracemalloc.Filter(False, "*/tracemalloc.py")[m
[31m- ))[m
[31m- top_stats = snapshot.statistics(key_type)[m
[31m-[m
[31m- print("\n\nTop %s lines" % limit)[m
[31m- for index, stat in enumerate(top_stats[:limit], 1):[m
[31m- frame = stat.traceback[0][m
[31m- print("#%s: %35s:%-5s \x1b[1m%.1f\x1b[0m KiB"[m
[31m- % (index, frame.filename[-35:], str(frame.lineno) + ':', stat.size / 1024))[m
[31m- line = linecache.getline(frame.filename, frame.lineno).strip()[m
[31m- if line:[m
[31m- print('\x1b[2m %s\x1b[0m' % line)[m
[31m-[m
[31m- other = top_stats[limit:][m
[31m- if other:[m
[31m- size = sum(stat.size for stat in other)[m
[31m- print("%s other: %.1f KiB" % (len(other), size / 1024))[m
[31m- total = sum(stat.size for stat in top_stats)[m
[31m- print("Total allocated size: %.1f KiB\n\n" % (total / 1024))[m
[31m-[m
[31m-[m
class Server:[m
def __init__(self, hostname_or_hostnames, cert_path, key_path,[m
address='localhost', port=1965,[m
[36m@@ -331,6 +366,10 @@[m [mclass Server:[m
self.add_entrypoint('gemini', hostname, key, value)[m
[m
def run(self, memtrace=False):[m
[32m+[m[32m self.memtrace = memtrace[m
[32m+[m[32m if self.memtrace:[m
[32m+[m[32m import tracemalloc[m
[32m+[m[32m tracemalloc.start(10)[m
attempts = 60[m
print(f'Opening port {self.port}...')[m
while True:[m
[36m@@ -353,9 +392,8 @@[m [mclass Server:[m
worker.start()[m
print(len(self.workers), 'worker(s) started')[m
[m
[31m- if memtrace:[m
[31m- import tracemalloc[m
[31m- tracemalloc.start()[m
[32m+[m[32m snapshot = None[m
[32m+[m
while True:[m
try:[m
stream = None[m
[36m@@ -368,14 +406,30 @@[m [mclass Server:[m
print('\nStopping the server...')[m
break[m
except Exception as ex:[m
[31m- import traceback[m
[31m- traceback.print_exc()[m
[32m+[m[32m #import traceback[m
[32m+[m[32m #traceback.print_exc()[m
print(ex)[m
[32m+[m[32m #del traceback[m
except Exception as ex:[m
print(ex)[m
[m
[31m- if memtrace:[m
[31m- display_memtop(tracemalloc.take_snapshot())[m
[32m+[m[32m if self.memtrace:[m
[32m+[m[32m time.sleep(2)[m
[32m+[m[32m old_snapshot = snapshot[m
[32m+[m[32m gc.collect()[m
[32m+[m[32m snapshot = tracemalloc.take_snapshot()[m
[32m+[m[32m filters = ([m
[32m+[m[32m tracemalloc.Filter(False, ""),[m
[32m+[m[32m tracemalloc.Filter(False, ""),[m
[32m+[m[32m tracemalloc.Filter(False, "*/linecache.py"),[m
[32m+[m[32m tracemalloc.Filter(False, "*/tracemalloc.py"),[m
[32m+[m[32m tracemalloc.Filter(False, "*/mimetypes.py"),[m
[32m+[m[32m tracemalloc.Filter(False, "*/fnmatch.py")[m
[32m+[m[32m )[m
[32m+[m[32m snapshot = snapshot.filter_traces(filters)[m
[32m+[m[32m top_stats = snapshot.statistics('lineno')[m
[32m+[m[32m display_memtop(snapshot, old_snapshot)[m
[32m+[m
[m
# Close the server socket.[m
self.sv_conn = None[m