snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
This commit is contained in:
117
zerver/lib/debug.py
Normal file
117
zerver/lib/debug.py
Normal file
@@ -0,0 +1,117 @@
|
||||
import code
|
||||
import gc
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import socket
|
||||
import threading
|
||||
import traceback
|
||||
import tracemalloc
|
||||
from types import FrameType
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.timezone import now as timezone_now
|
||||
|
||||
logger = logging.getLogger("zulip.debug")
|
||||
|
||||
|
||||
# Interactive debugging code from
|
||||
# https://stackoverflow.com/questions/132058/showing-the-stack-trace-from-a-running-python-application
|
||||
# (that link also points to code for an interactive remote debugger
|
||||
# setup, which we might want if we move Tornado to run in a daemon
|
||||
# rather than via screen).
|
||||
def interactive_debug(sig: int, frame: FrameType | None) -> None:
|
||||
"""Interrupt running process, and provide a python prompt for
|
||||
interactive debugging."""
|
||||
d = {"_frame": frame} # Allow access to frame object.
|
||||
if frame is not None:
|
||||
d.update(frame.f_globals) # Unless shadowed by global
|
||||
d.update(frame.f_locals)
|
||||
|
||||
message = "Signal received : entering python shell.\nTraceback:\n"
|
||||
message += "".join(traceback.format_stack(frame))
|
||||
i = code.InteractiveConsole(d)
|
||||
i.interact(message)
|
||||
|
||||
|
||||
# SIGUSR1 => Just print the stack
|
||||
# SIGUSR2 => Print stack + open interactive debugging shell
|
||||
def interactive_debug_listen() -> None:
|
||||
signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))
|
||||
signal.signal(signal.SIGUSR2, interactive_debug)
|
||||
|
||||
|
||||
def tracemalloc_dump() -> None:
|
||||
if not tracemalloc.is_tracing():
|
||||
logger.warning("pid %s: tracemalloc off, nothing to dump", os.getpid())
|
||||
return
|
||||
# Despite our name for it, `timezone_now` always deals in UTC.
|
||||
basename = "snap.{}.{}".format(
|
||||
os.getpid(), timezone_now().replace(tzinfo=None).isoformat("-", "seconds")
|
||||
)
|
||||
path = os.path.join(settings.TRACEMALLOC_DUMP_DIR, basename)
|
||||
os.makedirs(settings.TRACEMALLOC_DUMP_DIR, exist_ok=True)
|
||||
|
||||
gc.collect()
|
||||
tracemalloc.take_snapshot().dump(path)
|
||||
|
||||
with open(f"/proc/{os.getpid()}/stat", "rb") as f:
|
||||
procstat = f.read().split()
|
||||
rss_pages = int(procstat[23])
|
||||
logger.info(
|
||||
"tracemalloc dump: tracing %s MiB (%s MiB peak), using %s MiB; rss %s MiB; dumped %s",
|
||||
tracemalloc.get_traced_memory()[0] // 1048576,
|
||||
tracemalloc.get_traced_memory()[1] // 1048576,
|
||||
tracemalloc.get_tracemalloc_memory() // 1048576,
|
||||
rss_pages // 256,
|
||||
basename,
|
||||
)
|
||||
|
||||
|
||||
def tracemalloc_listen_sock(sock: socket.socket) -> None:
|
||||
logger.debug("pid %s: tracemalloc_listen_sock started!", os.getpid())
|
||||
while True:
|
||||
sock.recv(1)
|
||||
tracemalloc_dump()
|
||||
|
||||
|
||||
listener_pid: int | None = None
|
||||
|
||||
|
||||
def tracemalloc_listen() -> None:
|
||||
global listener_pid
|
||||
if listener_pid == os.getpid():
|
||||
# Already set up -- and in this process, not just its parent.
|
||||
return
|
||||
logger.debug("pid %s: tracemalloc_listen working...", os.getpid())
|
||||
listener_pid = os.getpid()
|
||||
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||
os.makedirs(settings.TRACEMALLOC_DUMP_DIR, exist_ok=True)
|
||||
path = os.path.join(settings.TRACEMALLOC_DUMP_DIR, f"tracemalloc.{os.getpid()}")
|
||||
sock.bind(path)
|
||||
thread = threading.Thread(target=lambda: tracemalloc_listen_sock(sock), daemon=True)
|
||||
thread.start()
|
||||
logger.debug("pid %s: tracemalloc_listen done: %s", os.getpid(), path)
|
||||
|
||||
|
||||
def maybe_tracemalloc_listen() -> None:
|
||||
"""If tracemalloc tracing enabled, listen for requests to dump a snapshot.
|
||||
|
||||
To trigger once this is listening:
|
||||
echo | socat -u stdin unix-sendto:/var/log/zulip/tracemalloc/tracemalloc.$pid
|
||||
|
||||
To enable in the Zulip web server: edit /etc/zulip/uwsgi.ini ,
|
||||
and add e.g. ` PYTHONTRACEMALLOC=5` to the `env=` line.
|
||||
This function is called in middleware, so the process will
|
||||
automatically start listening.
|
||||
|
||||
To enable in other contexts: see upstream docs
|
||||
https://docs.python.org/3/library/tracemalloc .
|
||||
You may also have to add a call to this function somewhere.
|
||||
|
||||
"""
|
||||
if os.environ.get("PYTHONTRACEMALLOC"):
|
||||
# If the server was started with `tracemalloc` tracing on, then
|
||||
# listen for a signal to dump `tracemalloc` snapshots.
|
||||
tracemalloc_listen()
|
||||
Reference in New Issue
Block a user