Test Failed
Push — master ( 73a01d...ef36eb )
by Nicolas
04:43
created

glances.maybe_trace_memleak()   B

Complexity

Conditions 7

Size

Total Lines 16
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 15
nop 2
dl 0
loc 16
rs 8
c 0
b 0
f 0
1
#
2
# This file is part of Glances.
3
#
4
# SPDX-FileCopyrightText: 2024 Nicolas Hennion <[email protected]>
5
#
6
# SPDX-License-Identifier: LGPL-3.0-only
7
#
8
#
9
10
"""Init the Glances software."""
11
12
# Import system libs
13
import locale
14
import platform
15
import signal
16
import sys
17
import tracemalloc
18
19
# Global name
20
# Version should start and end with a numerical char
21
# See https://packaging.python.org/specifications/core-metadata/#version
22
# Examples: 1.0.0, 1.0.0rc1, 1.1.0_dev1
23
__version__ = "4.4.0"
24
__apiversion__ = '4'
25
__author__ = 'Nicolas Hennion <[email protected]>'
26
__license__ = 'LGPLv3'
27
28
# Import psutil
29
try:
30
    from psutil import __version__ as psutil_version
31
except ImportError:
32
    print('psutil library not found. Glances cannot start.')
33
    sys.exit(1)
34
35
# Import Glances libs
36
# Note: others Glances libs will be imported optionally
37
from glances.logger import logger
38
from glances.main import GlancesMain
39
from glances.timer import Counter
40
41
# Check locale
42
try:
43
    locale.setlocale(locale.LC_ALL, '')
44
except locale.Error:
45
    print("Warning: Unable to set locale. Expect encoding problems.")
46
47
# Check psutil version
48
psutil_min_version = (5, 3, 0)
49
psutil_version_info = tuple([int(num) for num in psutil_version.split('.')])
50
if psutil_version_info < psutil_min_version:
51
    print('psutil 5.3.0 or higher is needed. Glances cannot start.')
52
    sys.exit(1)
53
54
55
# Trac malloc is only available on Python 3.4 or higher
56
def __signal_handler(sig, frame):
57
    logger.debug(f"Signal {sig} caught")
58
    # Avoid Glances hang when killing process with muliple CTRL-C See #3264
59
    signal.signal(signal.SIGINT, signal.SIG_IGN)
60
    end()
61
62
63
def end():
64
    """Stop Glances."""
65
    try:
66
        mode.end()
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable mode does not seem to be defined.
Loading history...
67
    except (NameError, KeyError):
68
        # NameError: name 'mode' is not defined in case of interrupt shortly...
69
        # ...after starting the server mode (issue #1175)
70
        pass
71
72
    logger.info("Glances stopped gracefully")
73
74
    # The end...
75
    sys.exit(0)
76
77
78
def start_main_loop(args, start_duration):
79
    logger.debug(f"Glances started in {start_duration.get()} seconds")
80
    if args.stop_after:
81
        logger.info(f'Glances will be stopped in ~{args.stop_after * args.time} seconds')
82
83
84
def check_memleak(args, mode):
85
    if args.memory_leak:
86
        wait = args.stop_after * args.time * args.memory_leak * 2
87
        print(f'Memory leak detection, please wait ~{wait} seconds...')
88
        # First run without dump to fill the memory
89
        mode.serve_n(args.stop_after)
90
        # Then start the memory-leak loop
91
        snapshot_begin = tracemalloc.take_snapshot()
92
    else:
93
        snapshot_begin = None
94
95
    return snapshot_begin
96
97
98
def setup_server_mode(args, mode):
99
    if args.stdout_issue or args.stdout_api_restful_doc or args.stdout_api_doc:
100
        # Serve once for issue and API documentation modes
101
        mode.serve_issue()
102
    else:
103
        # Serve forever
104
        mode.serve_forever()
105
106
107
def maybe_trace_memleak(args, snapshot_begin):
108
    if args.trace_malloc or args.memory_leak:
109
        snapshot_end = tracemalloc.take_snapshot()
110
    if args.memory_leak:
111
        snapshot_diff = snapshot_end.compare_to(snapshot_begin, 'filename')
0 ignored issues
show
introduced by
The variable snapshot_end does not seem to be defined in case args.trace_malloc or args.memory_leak on line 108 is False. Are you sure this can never be the case?
Loading history...
112
        memory_leak = sum([s.size_diff for s in snapshot_diff])
113
        print(f"Memory consumption: {memory_leak / 1000:.1f}KB (see log for details)")
114
        logger.info("Memory consumption (top 5):")
115
        for stat in snapshot_diff[:5]:
116
            logger.info(stat)
117
    if args.trace_malloc:
118
        # See more options here: https://docs.python.org/3/library/tracemalloc.html
119
        top_stats = snapshot_end.statistics("filename")
120
        print("[ Trace malloc - Top 10 ]")
121
        for stat in top_stats[:10]:
122
            print(stat)
123
124
125
def start(config, args):
126
    """Start Glances."""
127
128
    # Load mode
129
    global mode
130
131
    if args.trace_malloc or args.memory_leak:
132
        tracemalloc.start()
133
134
    start_duration = Counter()
135
136
    if core.is_standalone():
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable core does not seem to be defined.
Loading history...
137
        from glances.standalone import GlancesStandalone as GlancesMode
138
    elif core.is_client():
139
        if core.is_client_browser():
140
            from glances.client_browser import GlancesClientBrowser as GlancesMode
141
        else:
142
            from glances.client import GlancesClient as GlancesMode
143
    elif core.is_server():
144
        from glances.server import GlancesServer as GlancesMode
145
    elif core.is_webserver():
146
        from glances.webserver import GlancesWebServer as GlancesMode
147
148
    # Init the mode
149
    logger.info(f"Start {GlancesMode.__name__} mode")
150
    mode = GlancesMode(config=config, args=args)
0 ignored issues
show
introduced by
The variable GlancesMode does not seem to be defined for all execution paths.
Loading history...
151
152
    start_main_loop(args, start_duration)
153
    snapshot_begin = check_memleak(args, mode)
154
    setup_server_mode(args, mode)
155
    maybe_trace_memleak(args, snapshot_begin)
156
157
    # Shutdown
158
    mode.end()
159
160
161
def main():
162
    """Main entry point for Glances.
163
164
    Select the mode (standalone, client or server)
165
    Run it...
166
    """
167
    # SIGHUP not available on Windows (see issue #2408)
168
    if sys.platform.startswith('win'):
169
        signal_list = (signal.SIGTERM, signal.SIGINT)
170
    else:
171
        signal_list = (signal.SIGTERM, signal.SIGINT, signal.SIGHUP)
172
    # Catch the kill signal
173
    for sig in signal_list:
174
        signal.signal(sig, __signal_handler)
175
176
    # Log Glances and psutil version
177
    logger.info(f'Start Glances {__version__}')
178
    python_impl = platform.python_implementation()
179
    python_ver = platform.python_version()
180
    logger.info(f'{python_impl} {python_ver} ({sys.executable}) and psutil {psutil_version} detected')
181
182
    # Share global var
183
    global core
184
185
    # Create the Glances main instance
186
    # Glances options from the command line are read first (in __init__)
187
    # then the options from the config file (in parse_args)
188
    core = GlancesMain()
189
190
    # Glances can be ran in standalone, client or server mode
191
    start(config=core.get_config(), args=core.get_args())
192