mirror of https://github.com/nicolargo/glances.git
192 lines
5.7 KiB
Python
192 lines
5.7 KiB
Python
#
|
|
# This file is part of Glances.
|
|
#
|
|
# SPDX-FileCopyrightText: 2024 Nicolas Hennion <nicolas@nicolargo.com>
|
|
#
|
|
# SPDX-License-Identifier: LGPL-3.0-only
|
|
#
|
|
#
|
|
|
|
"""Init the Glances software."""
|
|
|
|
# Import system libs
|
|
import locale
|
|
import platform
|
|
import signal
|
|
import sys
|
|
import tracemalloc
|
|
|
|
# Global name
|
|
# Version should start and end with a numerical char
|
|
# See https://packaging.python.org/specifications/core-metadata/#version
|
|
# Examples: 1.0.0, 1.0.0rc1, 1.1.0_dev1
|
|
__version__ = "4.4.1"
|
|
__apiversion__ = '4'
|
|
__author__ = 'Nicolas Hennion <nicolas@nicolargo.com>'
|
|
__license__ = 'LGPLv3'
|
|
|
|
# Import psutil
|
|
try:
|
|
from psutil import __version__ as psutil_version
|
|
except ImportError:
|
|
print('psutil library not found. Glances cannot start.')
|
|
sys.exit(1)
|
|
|
|
# Import Glances libs
|
|
# Note: others Glances libs will be imported optionally
|
|
from glances.logger import logger
|
|
from glances.main import GlancesMain
|
|
from glances.timer import Counter
|
|
|
|
# Check locale
|
|
try:
|
|
locale.setlocale(locale.LC_ALL, '')
|
|
except locale.Error:
|
|
print("Warning: Unable to set locale. Expect encoding problems.")
|
|
|
|
# Check psutil version
|
|
psutil_min_version = (5, 3, 0)
|
|
psutil_version_info = tuple([int(num) for num in psutil_version.split('.')])
|
|
if psutil_version_info < psutil_min_version:
|
|
print('psutil 5.3.0 or higher is needed. Glances cannot start.')
|
|
sys.exit(1)
|
|
|
|
|
|
# Trac malloc is only available on Python 3.4 or higher
|
|
def __signal_handler(sig, frame):
|
|
logger.debug(f"Signal {sig} caught")
|
|
# Avoid Glances hang when killing process with muliple CTRL-C See #3264
|
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
end()
|
|
|
|
|
|
def end():
|
|
"""Stop Glances."""
|
|
try:
|
|
mode.end()
|
|
except (NameError, KeyError):
|
|
# NameError: name 'mode' is not defined in case of interrupt shortly...
|
|
# ...after starting the server mode (issue #1175)
|
|
pass
|
|
|
|
logger.info("Glances stopped gracefully")
|
|
|
|
# The end...
|
|
sys.exit(0)
|
|
|
|
|
|
def start_main_loop(args, start_duration):
|
|
logger.debug(f"Glances started in {start_duration.get()} seconds")
|
|
if args.stop_after:
|
|
logger.info(f'Glances will be stopped in ~{args.stop_after * args.time} seconds')
|
|
|
|
|
|
def check_memleak(args, mode):
|
|
if args.memory_leak:
|
|
wait = args.stop_after * args.time * args.memory_leak * 2
|
|
print(f'Memory leak detection, please wait ~{wait} seconds...')
|
|
# First run without dump to fill the memory
|
|
mode.serve_n(args.stop_after)
|
|
# Then start the memory-leak loop
|
|
snapshot_begin = tracemalloc.take_snapshot()
|
|
else:
|
|
snapshot_begin = None
|
|
|
|
return snapshot_begin
|
|
|
|
|
|
def setup_server_mode(args, mode):
|
|
if args.stdout_issue or args.stdout_api_restful_doc or args.stdout_api_doc:
|
|
# Serve once for issue and API documentation modes
|
|
mode.serve_issue()
|
|
else:
|
|
# Serve forever
|
|
mode.serve_forever()
|
|
|
|
|
|
def maybe_trace_memleak(args, snapshot_begin):
|
|
if args.trace_malloc or args.memory_leak:
|
|
snapshot_end = tracemalloc.take_snapshot()
|
|
if args.memory_leak:
|
|
snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
|
|
memory_leak = sum([s.size_diff for s in snapshot_diff])
|
|
print(f"Memory consumption: {memory_leak / 1000:.1f}KB (see log for details)")
|
|
logger.info("Memory consumption (top 5):")
|
|
for stat in snapshot_diff[:5]:
|
|
logger.info(stat)
|
|
if args.trace_malloc:
|
|
# See more options here: https://docs.python.org/3/library/tracemalloc.html
|
|
top_stats = snapshot_end.statistics("filename")
|
|
print("[ Trace malloc - Top 10 ]")
|
|
for stat in top_stats[:10]:
|
|
print(stat)
|
|
|
|
|
|
def start(config, args):
|
|
"""Start Glances."""
|
|
|
|
# Load mode
|
|
global mode
|
|
|
|
if args.trace_malloc or args.memory_leak:
|
|
tracemalloc.start()
|
|
|
|
start_duration = Counter()
|
|
|
|
if core.is_standalone():
|
|
from glances.standalone import GlancesStandalone as GlancesMode
|
|
elif core.is_client():
|
|
if core.is_client_browser():
|
|
from glances.client_browser import GlancesClientBrowser as GlancesMode
|
|
else:
|
|
from glances.client import GlancesClient as GlancesMode
|
|
elif core.is_server():
|
|
from glances.server import GlancesServer as GlancesMode
|
|
elif core.is_webserver():
|
|
from glances.webserver import GlancesWebServer as GlancesMode
|
|
|
|
# Init the mode
|
|
logger.info(f"Start {GlancesMode.__name__} mode")
|
|
mode = GlancesMode(config=config, args=args)
|
|
|
|
start_main_loop(args, start_duration)
|
|
snapshot_begin = check_memleak(args, mode)
|
|
setup_server_mode(args, mode)
|
|
maybe_trace_memleak(args, snapshot_begin)
|
|
|
|
# Shutdown
|
|
mode.end()
|
|
|
|
|
|
def main():
|
|
"""Main entry point for Glances.
|
|
|
|
Select the mode (standalone, client or server)
|
|
Run it...
|
|
"""
|
|
# SIGHUP not available on Windows (see issue #2408)
|
|
if sys.platform.startswith('win'):
|
|
signal_list = (signal.SIGTERM, signal.SIGINT)
|
|
else:
|
|
signal_list = (signal.SIGTERM, signal.SIGINT, signal.SIGHUP)
|
|
# Catch the kill signal
|
|
for sig in signal_list:
|
|
signal.signal(sig, __signal_handler)
|
|
|
|
# Log Glances and psutil version
|
|
logger.info(f'Start Glances {__version__}')
|
|
python_impl = platform.python_implementation()
|
|
python_ver = platform.python_version()
|
|
logger.info(f'{python_impl} {python_ver} ({sys.executable}) and psutil {psutil_version} detected')
|
|
|
|
# Share global var
|
|
global core
|
|
|
|
# Create the Glances main instance
|
|
# Glances options from the command line are read first (in __init__)
|
|
# then the options from the config file (in parse_args)
|
|
core = GlancesMain()
|
|
|
|
# Glances can be ran in standalone, client or server mode
|
|
start(config=core.get_config(), args=core.get_args())
|