Skip to content

Commit

Permalink
use setup_singnals
Browse files Browse the repository at this point in the history
  • Loading branch information
slicklash committed Jul 15, 2024
1 parent 2e6b85e commit 4a229e4
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 37 deletions.
30 changes: 2 additions & 28 deletions gprofiler/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@
import logging.handlers
import os
import shutil
import signal
import sys
import time
import traceback
from pathlib import Path
from threading import Event
from types import FrameType, TracebackType
from types import TracebackType
from typing import Iterable, List, Optional, Type, cast

import configargparse
Expand Down Expand Up @@ -74,6 +73,7 @@
reset_umask,
resource_path,
run_process,
setup_signals,
)
from gprofiler.utils.fs import escape_filename, mkdir_owned_root
from gprofiler.utils.proxy import get_https_proxy
Expand All @@ -98,21 +98,6 @@

UPLOAD_FILE_SUBCOMMAND = "upload-file"

# 1 KeyboardInterrupt raised per this many seconds, no matter how many SIGINTs we get.
SIGINT_RATELIMIT = 0.5

last_signal_ts: Optional[float] = None


def sigint_handler(sig: int, frame: Optional[FrameType]) -> None:
global last_signal_ts
ts = time.monotonic()
# no need for atomicity here: we can't get another SIGINT before this one returns.
# https://www.gnu.org/software/libc/manual/html_node/Signals-in-Handler.html#Signals-in-Handler
if last_signal_ts is None or ts > last_signal_ts + SIGINT_RATELIMIT:
last_signal_ts = ts
raise KeyboardInterrupt


class GProfiler:
def __init__(
Expand Down Expand Up @@ -939,17 +924,6 @@ def verify_preconditions(args: configargparse.Namespace, processes_to_profile: O
sys.exit(1)


def setup_signals() -> None:
# When we run under staticx & PyInstaller, both of them forward (some of the) signals to gProfiler.
# We catch SIGINTs and ratelimit them, to avoid being interrupted again during the handling of the
# first INT.
# See my commit message for more information.
signal.signal(signal.SIGINT, sigint_handler)
# handle SIGTERM in the same manner - gracefully stop gProfiler.
# SIGTERM is also forwarded by staticx & PyInstaller, so we need to ratelimit it.
signal.signal(signal.SIGTERM, sigint_handler)


def log_system_info() -> None:
system_info = get_static_system_info()
logger.info(f"gProfiler Python version: {system_info.python_version}")
Expand Down
34 changes: 25 additions & 9 deletions gprofiler/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from subprocess import CompletedProcess, Popen, TimeoutExpired
from tempfile import TemporaryDirectory
from threading import Event
from types import FrameType
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union, cast

import importlib_resources
Expand Down Expand Up @@ -71,8 +72,10 @@

gprofiler_mutex: Optional[socket.socket] = None

STATUS_KILL = 137
STATUS_INTERRUPT = 130
# 1 KeyboardInterrupt raised per this many seconds, no matter how many SIGINTs we get.
SIGINT_RATELIMIT = 0.5

_last_signal_ts: Optional[float] = None
_processes: List[Popen] = []


Expand Down Expand Up @@ -522,10 +525,23 @@ def _exit_handler() -> None:
process.kill()


def _kill_handler(*args: Any) -> None:
sys.exit(STATUS_KILL if args[0] == signal.SIGTERM else STATUS_INTERRUPT)


atexit.register(_exit_handler)
signal.signal(signal.SIGINT, _kill_handler)
signal.signal(signal.SIGTERM, _kill_handler)
def _sigint_handler(sig: int, frame: Optional[FrameType]) -> None:
global _last_signal_ts
ts = time.monotonic()
# no need for atomicity here: we can't get another SIGINT before this one returns.
# https://www.gnu.org/software/libc/manual/html_node/Signals-in-Handler.html#Signals-in-Handler
if _last_signal_ts is None or ts > _last_signal_ts + SIGINT_RATELIMIT:
_last_signal_ts = ts
raise KeyboardInterrupt


def setup_signals() -> None:
atexit.register(_exit_handler)
# When we run under staticx & PyInstaller, both of them forward (some of the) signals to gProfiler.
# We catch SIGINTs and ratelimit them, to avoid being interrupted again during the handling of the
# first INT.
# See my commit message for more information.
signal.signal(signal.SIGINT, _sigint_handler)
# handle SIGTERM in the same manner - gracefully stop gProfiler.
# SIGTERM is also forwarded by staticx & PyInstaller, so we need to ratelimit it.
signal.signal(signal.SIGTERM, _sigint_handler)

0 comments on commit 4a229e4

Please sign in to comment.