summaryrefslogtreecommitdiffstats
path: root/glances/plugins/processlist/model.py
diff options
context:
space:
mode:
Diffstat (limited to 'glances/plugins/processlist/model.py')
-rw-r--r--glances/plugins/processlist/model.py833
1 files changed, 0 insertions, 833 deletions
diff --git a/glances/plugins/processlist/model.py b/glances/plugins/processlist/model.py
deleted file mode 100644
index 82bf494a..00000000
--- a/glances/plugins/processlist/model.py
+++ /dev/null
@@ -1,833 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file is part of Glances.
-#
-# SPDX-FileCopyrightText: 2023 Nicolas Hennion <nicolas@nicolargo.com>
-#
-# SPDX-License-Identifier: LGPL-3.0-only
-#
-
-"""Process list plugin."""
-
-import os
-import copy
-
-from glances.logger import logger
-from glances.globals import WINDOWS, key_exist_value_not_none_not_v
-from glances.processes import glances_processes, sort_stats
-from glances.outputs.glances_unicode import unicode_message
-from glances.plugins.core.model import PluginModel as CorePluginModel
-from glances.plugins.plugin.model import GlancesPluginModel
-
-
-def seconds_to_hms(input_seconds):
- """Convert seconds to human-readable time."""
- minutes, seconds = divmod(input_seconds, 60)
- hours, minutes = divmod(minutes, 60)
-
- hours = int(hours)
- minutes = int(minutes)
- seconds = str(int(seconds)).zfill(2)
-
- return hours, minutes, seconds
-
-
-def split_cmdline(bare_process_name, cmdline):
- """Return path, cmd and arguments for a process cmdline based on bare_process_name.
-
- If first argument of cmdline starts with the bare_process_name then
- cmdline will just be considered cmd and path will be empty (see https://github.com/nicolargo/glances/issues/1795)
-
- :param bare_process_name: Name of the process from psutil
- :param cmdline: cmdline from psutil
- :return: Tuple with three strings, which are path, cmd and arguments of the process
- """
- if cmdline[0].startswith(bare_process_name):
- path, cmd = "", cmdline[0]
- else:
- path, cmd = os.path.split(cmdline[0])
- arguments = ' '.join(cmdline[1:])
- return path, cmd, arguments
-
-
-class PluginModel(GlancesPluginModel):
- """Glances' processes plugin.
-
- stats is a list
- """
-
- # Define the header layout of the processes list columns
- layout_header = {
- 'cpu': '{:<6} ',
- 'mem': '{:<5} ',
- 'virt': '{:<5} ',
- 'res': '{:<5} ',
- 'pid': '{:>{width}} ',
- 'user': '{:<10} ',
- 'time': '{:>8} ',
- 'thread': '{:<3} ',
- 'nice': '{:>3} ',
- 'status': '{:>1} ',
- 'ior': '{:>4} ',
- 'iow': '{:<4} ',
- 'command': '{} {}',
- }
-
- # Define the stat layout of the processes list columns
- layout_stat = {
- 'cpu': '{:<6.1f}',
- 'cpu_no_digit': '{:<6.0f}',
- 'mem': '{:<5.1f} ',
- 'virt': '{:<5} ',
- 'res': '{:<5} ',
- 'pid': '{:>{width}} ',
- 'user': '{:<10} ',
- 'time': '{:>8} ',
- 'thread': '{:<3} ',
- 'nice': '{:>3} ',
- 'status': '{:>1} ',
- 'ior': '{:>4} ',
- 'iow': '{:<4} ',
- 'command': '{}',
- 'name': '[{}]',
- }
-
- def __init__(self, args=None, config=None):
- """Init the plugin."""
- super(PluginModel, self).__init__(args=args, config=config, stats_init_value=[])
-
- # We want to display the stat in the curse interface
- self.display_curse = True
-
- # Trying to display proc time
- self.tag_proc_time = True
-
- # Call CorePluginModel to get the core number (needed when not in IRIX mode / Solaris mode)
- try:
- self.nb_log_core = CorePluginModel(args=self.args).update()["log"]
- except Exception:
- self.nb_log_core = 0
-
- # Get the max values (dict)
- self.max_values = copy.deepcopy(glances_processes.max_values())
-
- # Get the maximum PID number
- # Use to optimize space (see https://github.com/nicolargo/glances/issues/959)
- self.pid_max = glances_processes.pid_max
-
- # Set the default sort key if it is defined in the configuration file
- if config is not None:
- if 'processlist' in config.as_dict() and 'sort_key' in config.as_dict()['processlist']:
- logger.debug(
- 'Configuration overwrites processes sort key by {}'.format(
- config.as_dict()['processlist']['sort_key']
- )
- )
- glances_processes.set_sort_key(config.as_dict()['processlist']['sort_key'], False)
-
- # The default sort key could also be overwrite by command line (see #1903)
- if args.sort_processes_key is not None:
- glances_processes.set_sort_key(args.sort_processes_key, False)
-
- # Note: 'glances_processes' is already init in the processes.py script
-
- def get_key(self):
- """Return the key of the list."""
- return 'pid'
-
- def update(self):
- """Update processes stats using the input method."""
- # Init new stats
- stats = self.get_init_value()
-
- if self.input_method == 'local':
- # Update stats using the standard system lib
- # Note: Update is done in the processcount plugin
- # Just return the processes list
- if self.args.programs:
- stats = glances_processes.getlist(as_programs=True)
- else:
- stats = glances_processes.getlist()
-
- elif self.input_method == 'snmp':
- # No SNMP grab for processes
- pass
-
- # Update the stats
- self.stats = stats
-
- # Get the max values (dict)
- # Use Deep copy to avoid change between update and display
- self.max_values = copy.deepcopy(glances_processes.max_values())
-
- return self.stats
-
- def get_nice_alert(self, value):
- """Return the alert relative to the Nice configuration list"""
- value = str(value)
- try:
- if value in self.get_limit('nice_critical'):
- return 'CRITICAL'
- except KeyError:
- pass
- try:
- if value in self.get_limit('nice_warning'):
- return 'WARNING'
- except KeyError:
- pass
- try:
- if value in self.get_limit('nice_careful'):
- return 'CAREFUL'
- except KeyError:
- pass
- return 'DEFAULT'
-
- def _get_process_curses_cpu(self, p, selected, args):
- """Return process CPU curses"""
- if key_exist_value_not_none_not_v('cpu_percent', p, ''):
- cpu_layout = self.layout_stat['cpu'] if p['cpu_percent'] < 100 else self.layout_stat['cpu_no_digit']
- if args.disable_irix and self.nb_log_core != 0:
- msg = cpu_layout.format(p['cpu_percent'] / float(self.nb_log_core))
- else:
- msg = cpu_layout.format(p['cpu_percent'])
- alert = self.get_alert(
- p['cpu_percent'],
- highlight_zero=False,
- is_max=(p['cpu_percent'] == self.max_values['cpu_percent']),
- header="cpu",
- )
- ret = self.curse_add_line(msg, alert)
- else:
- msg = self.layout_header['cpu'].format('?')
- ret = self.curse_add_line(msg)
- return ret
-
- def _get_process_curses_mem(self, p, selected, args):
- """Return process MEM curses"""
- if key_exist_value_not_none_not_v('memory_percent', p, ''):
- msg = self.layout_stat['mem'].format(p['memory_percent'])
- alert = self.get_alert(
- p['memory_percent'],
- highlight_zero=False,
- is_max=(p['memory_percent'] == self.max_values['memory_percent']),
- header="mem",
- )
- ret = self.curse_add_line(msg, alert)
- else:
- msg = self.layout_header['mem'].format('?')
- ret = self.curse_add_line(msg)
- return ret
-
- def _get_process_curses_vms(self, p, selected, args):
- """Return process VMS curses"""
- if key_exist_value_not_none_not_v('memory_info', p, '', 1):
- msg = self.layout_stat['virt'].format(self.auto_unit(p['memory_info'][1], low_precision=False))
- ret = self.curse_add_line(msg, optional=True)
- else:
- msg = self.layout_header['virt'].format('?')
- ret = self.curse_add_line(msg)
- return ret
-
- def _get_process_curses_rss(self, p, selected, args):
- """Return process RSS curses"""
- if key_exist_value_not_none_not_v('memory_info', p, '', 0):
- msg = self.layout_stat['res'].format(self.auto_unit(p['memory_info'][0], low_precision=False))
- ret = self.curse_add_line(msg, optional=True)
- else:
- msg = self.layout_header['res'].format('?')
- ret = self.curse_add_line(msg)
- return ret
-
- def _get_process_curses_username(self, p, selected, args):
- """Return process username curses"""
- if 'username' in p:
- # docker internal users are displayed as ints only, therefore str()
- # Correct issue #886 on Windows OS
- msg = self.layout_stat['user'].format(str(p['username'])[:9])
- ret = self.curse_add_line(msg)
- else:
- msg = self.layout_header['user'].format('?')
- ret = self.curse_add_line(msg)
- return ret
-
- def _get_process_curses_time(self, p, selected, args):
- """Return process time curses"""
- try:
- # Sum user and system time
- user_system_time = p['cpu_times'][0] + p['cpu_times'][1]
- except (OverflowError, TypeError):
- # Catch OverflowError on some Amazon EC2 server
- # See https://github.com/nicolargo/glances/issues/87
- # Also catch TypeError on macOS
- # See: https://github.com/nicolargo/glances/issues/622
- # logger.debug("Cannot get TIME+ ({})".format(e))
- msg = self.layout_header['time'].format('?')
- ret = self.curse_add_line(msg, optional=True)
- else:
- hours, minutes, seconds = seconds_to_hms(user_system_time)
- if hours > 99:
- msg = '{:<7}h'.format(hours)
- elif 0 < hours < 100:
- msg = '{}h{}:{}'.format(hours, minutes, seconds)
- else:
- msg = '{}:{}'.format(minutes, seconds)
- msg = self.layout_stat['time'].format(msg)
- if hours > 0:
- ret = self.curse_add_line(msg, decoration='CPU_TIME', optional=True)
- else:
- ret = self.curse_add_line(msg, optional=True)
- return ret
-
- def _get_process_curses_thread(self, p, selected, args):
- """Return process thread curses"""
- if 'num_threads' in p:
- num_threads = p['num_threads']
- if num_threads is None:
- num_threads = '?'
- msg = self.layout_stat['thread'].format(num_threads)
- ret = self.curse_add_line(msg)
- else:
- msg = self.layout_header['thread'].format('?')
- ret = self.curse_add_line(msg)
- return ret
-
- def _get_process_curses_nice(self, p, selected, args):
- """Return process nice curses"""
- if 'nice' in p:
- nice = p['nice']
- if nice is None:
- nice = '?'
- msg = self.layout_stat['nice'].format(nice)
- ret = self.curse_add_line(msg, decoration=self.get_nice_alert(nice))
- else:
- msg = self.layout_header['nice'].format('?')
- ret = self.curse_add_line(msg)
- return ret
-
- def _get_process_curses_status(self, p, selected, args):
- """Return process status curses"""
- if 'status' in p:
- status = p['status']
- msg = self.layout_stat['status'].format(status)
- if status == 'R':
- ret = self.curse_add_line(msg, decoration='STATUS')
- else:
- ret = self.curse_add_line(msg)
- else:
- msg = self.layout_header['status'].format('?')
- ret = self.curse_add_line(msg)
- return ret
-
- def _get_process_curses_io(self, p, selected, args, rorw='ior'):
- """Return process IO Read or Write curses"""
- if 'io_counters' in p and p['io_counters'][4] == 1 and p['time_since_update'] != 0:
- # Display rate if stats is available and io_tag ([4]) == 1
- # IO
- io = int(
- (p['io_counters'][0 if rorw == 'ior' else 1] - p['io_counters'][2 if rorw == 'ior' else 3])
- / p['time_since_update']
- )
- if io == 0:
- msg = self.layout_stat[rorw].format("0")
- else:
- msg = self.layout_stat[rorw].format(self.auto_unit(io, low_precision=True))
- ret = self.curse_add_line(msg, optional=True, additional=True)
- else:
- msg = self.layout_header[rorw].format("?")
- ret = self.curse_add_line(msg, optional=True, additional=True)
- return ret
-
- def _get_process_curses_io_read(self, p, selected, args):
- """Return process IO Read curses"""
- return self._get_process_curses_io(p, selected, args, rorw='ior')
-
- def _get_process_curses_io_write(self, p, selected, args):
- """Return process IO Write curses"""
- return self._get_process_curses_io(p, selected, args, rorw='iow')
-
- def get_process_curses_data(self, p, selected, args):
- """Get curses data to display for a process.
-
- - p is the process to display
- - selected is a tag=True if p is the selected process
- """
- ret = [self.curse_new_line()]
-
- # When a process is selected:
- # * display a special character at the beginning of the line
- # * underline the command name
- ret.append(
- self.curse_add_line(
- unicode_message('PROCESS_SELECTOR') if (selected and not args.disable_cursor) else ' ', 'SELECTED'
- )
- )
-
- # CPU
- ret.append(self._get_process_curses_cpu(p, selected, args))
-
- # MEM
- ret.append(self._get_process_curses_mem(p, selected, args))
- ret.append(self._get_process_curses_vms(p, selected, args))
- ret.append(self._get_process_curses_rss(p, selected, args))
-
- # PID
- if not self.args.programs:
- # Display processes, so the PID should be displayed
- msg = self.layout_stat['pid'].format(p['pid'], width=self.__max_pid_size())
- else:
- # Display programs, so the PID should not be displayed
- # Instead displays the number of children
- msg = self.layout_stat['pid'].format(
- len(p['childrens']) if 'childrens' in p else '_', width=self.__max_pid_size()
- )
- ret.append(self.curse_add_line(msg))
-
- # USER
- ret.append(self._get_process_curses_username(p, selected, args))
-
- # TIME+
- ret.append(self._get_process_curses_time(p, selected, args))
-
- # THREAD
- ret.append(self._get_process_curses_thread(p, selected, args))
-
- # NICE
- ret.append(self._get_process_curses_nice(p, selected, args))
-
- # STATUS
- ret.append(self._get_process_curses_status(p, selected, args))
-
- # IO read/write
- ret.append(self._get_process_curses_io_read(p, selected, args))
- ret.append(self._get_process_curses_io_write(p, selected, args))
-
- # Command line
- # If no command line for the process is available, fallback to the bare process name instead
- bare_process_name = p['name']
- cmdline = p.get('cmdline', '?')
-
- try:
- process_decoration = 'PROCESS_SELECTED' if (selected and not args.disable_cursor) else 'PROCESS'
- if cmdline:
- path, cmd, arguments = split_cmdline(bare_process_name, cmdline)
- # Manage end of line in arguments (see #1692)
- arguments = arguments.replace('\r\n', ' ')
- arguments = arguments.replace('\n', ' ')
- arguments = arguments.replace('\t', ' ')
- if os.path.isdir(path) and not args.process_short_name:
- msg = self.layout_stat['command'].format(path) + os.sep
- ret.append(self.curse_add_line(msg, splittable=True))
- ret.append(self.curse_add_line(cmd, decoration=process_decoration, splittable=True))
- else:
- msg = self.layout_stat['command'].format(cmd)
- ret.append(self.curse_add_line(msg, decoration=process_decoration, splittable=True))
- if arguments:
- msg = ' ' + self.layout_stat['command'].format(arguments)
- ret.append(self.curse_add_line(msg, splittable=True))
- else:
- msg = self.layout_stat['name'].format(bare_process_name)
- ret.append(self.curse_add_line(msg, decoration=process_decoration, splittable=True))
- except (TypeError, UnicodeEncodeError) as e:
- # Avoid crash after running fine for several hours #1335
- logger.debug("Can not decode command line '{}' ({})".format(cmdline, e))
- ret.append(self.curse_add_line('', splittable=True))
-
- return ret
-
- def is_selected_process(self, args):
- return (
- args.is_standalone
- and self.args.enable_process_extended
- and args.cursor_position is not None
- and glances_processes.extended_process is not None
- )
-
- def msg_curse(self, args=None, max_width=None):
- """Return the dict to display in the curse interface."""
- # Init the return message
- ret = []
-
- # Only process if stats exist and display plugin enable...
- if not self.stats or args.disable_process:
- return ret
-
- # Compute the sort key
- process_sort_key = glances_processes.sort_key
- processes_list_sorted = self.__sort_stats(process_sort_key)
-
- # Display extended stats for selected process
- #############################################
-
- if self.is_selected_process(args):
- self.__msg_curse_extended_process(ret, glances_processes.extended_process)
-
- # Display others processes list
- ###############################
-
- # Header
- self.__msg_curse_header(ret, process_sort_key, args)
-
- # Process list
- # Loop over processes (sorted by the sort key previously compute)
- # This is a Glances bottleneck (see flame graph),
- # get_process_curses_data should be optimzed
- for position, process in enumerate(processes_list_sorted):
- ret.extend(self.get_process_curses_data(process, position == args.cursor_position, args))
-
- # A filter is set Display the stats summaries
- if glances_processes.process_filter is not None:
- if args.reset_minmax_tag:
- args.reset_minmax_tag = not args.reset_minmax_tag
- self.__mmm_reset()
- self.__msg_curse_sum(ret, args=args)
- self.__msg_curse_sum(ret, mmm='min', args=args)
- self.__msg_curse_sum(ret, mmm='max', args=args)
-
- # Return the message with decoration
- return ret
-
- def __msg_curse_extended_process(self, ret, p):
- """Get extended curses data for the selected process (see issue #2225)
-
- The result depends of the process type (process or thread).
-
- Input p is a dict with the following keys:
- {'status': 'S',
- 'memory_info': pmem(rss=466890752, vms=3365347328, shared=68153344,
- text=659456, lib=0, data=774647808, dirty=0),
- 'pid': 4980,
- 'io_counters': [165385216, 0, 165385216, 0, 1],
- 'num_threads': 20,
- 'nice': 0,
- 'memory_percent': 5.958135664449709,
- 'cpu_percent': 0.0,
- 'gids': pgids(real=1000, effective=1000, saved=1000),
- 'cpu_times': pcputimes(user=696.38, system=119.98, children_user=0.0, children_system=0.0, iowait=0.0),
- 'name': 'WebExtensions',
- 'key': 'pid',
- 'time_since_update': 2.1997854709625244,
- 'cmdline': ['/snap/firefox/2154/usr/lib/firefox/firefox', '-contentproc', '-childID', '...'],
- 'username': 'nicolargo',
- 'cpu_min': 0.0,
- 'cpu_max': 7.0,
- 'cpu_mean': 3.2}
- """
- if self.args.programs:
- self.__msg_curse_extended_process_program(ret, p)
- else:
- self.__msg_curse_extended_process_thread(ret, p)
-
- def __msg_curse_extended_process_program(self, ret, p):
- # Title
- msg = "Pinned program {} ('e' to unpin)".format(p['name'])
- ret.append(self.curse_add_line(msg, "TITLE"))
-
- ret.append(self.curse_new_line())
- ret.append(self.curse_new_line())
-
- def __msg_curse_extended_process_thread(self, ret, p):
- # Title
- ret.append(self.curse_add_line("Pinned thread ", "TITLE"))
- ret.append(self.curse_add_line(p['name'], "UNDERLINE"))
- ret.append(self.curse_add_line(" ('e' to unpin)"))
-
- # First line is CPU affinity
- ret.append(self.curse_new_line())
- ret.append(self.curse_add_line(' CPU Min/Max/Mean: '))
- msg = '{: >7.1f}{: >7.1f}{: >7.1f}%'.format(p['cpu_min'], p['cpu_max'], p['cpu_mean'])
- ret.append(self.curse_add_line(msg, decoration='INFO'))
- if 'cpu_affinity' in p and p['cpu_affinity'] is not None:
- ret.append(self.curse_add_line(' Affinity: '))
- ret.append(self.curse_add_line(str(len(p['cpu_affinity'])), decoration='INFO'))
- ret.append(self.curse_add_line(' cores', decoration='INFO'))
- if 'ionice' in p and p['ionice'] is not None and hasattr(p['ionice'], 'ioclass'):
- msg = ' IO nice: '
- k = 'Class is '
- v = p['ionice'].ioclass
- # Linux: The scheduling class. 0 for none, 1 for real time, 2 for best-effort, 3 for idle.
- # Windows: On Windows only ioclass is used and it can be set to 2 (normal), 1 (low) or 0 (very low).
- if WINDOWS:
- if v == 0:
- msg += k + 'Very Low'
- elif v == 1:
- msg += k + 'Low'
- elif v == 2:
- msg += 'No specific I/O priority'
- else:
- msg += k + str(v)
- else:
- if v == 0:
- msg += 'No specific I/O priority'
- elif v == 1:
- msg += k + 'Real Time'
- elif v == 2:
- msg += k + 'Best Effort'
- elif v == 3:
- msg += k + 'IDLE'
- else:
- msg += k + str(v)
- # value is a number which goes from 0 to 7.
- # The higher the value, the lower the I/O priority of the process.
- if hasattr(p['ionice'], 'value') and p['ionice'].value != 0:
- msg += ' (value %s/7)' % str(p['ionice'].value)
- ret.append(self.curse_add_line(msg, splittable=True))
-
- # Second line is memory info
- ret.append(self.curse_new_line())
- ret.append(self.curse_add_line(' MEM Min/Max/Mean: '))
- msg = '{: >7.1f}{: >7.1f}{: >7.1f}%'.format(p['memory_min'], p['memory_max'], p['memory_mean'])
- ret.append(self.curse_add_line(msg, decoration='INFO'))
- if 'memory_info' in p and p['memory_info'] is not None:
- ret.append(self.curse_add_line(' Memory info: '))
- for k in p['memory_info']._asdict():
- ret.append(
- self.curse_add_line(
- self.auto_unit(p['memory_info']._asdict()[k], low_precision=False),
- decoration='INFO',
- splittable=True,
- )
- )
- ret.append(self.curse_add_line(' ' + k + ' ', splittable=True))
- if 'memory_swap' in p and p['memory_swap'] is not None:
- ret.append(
- self.curse_add_line(
- self.auto_unit(p['memory_swap'], low_precision=False), decoration='INFO', splittable=True
- )
- )
- ret.append(self.curse_add_line(' swap ', splittable=True))
-
- # Third line is for open files/network sessions
- ret.append(self.curse_new_line())
- ret.append(self.curse_add_line(' Open: '))
- for stat_prefix in ['num_threads', 'num_fds', 'num_handles', 'tcp', 'udp']:
- if stat_prefix in p and p[stat_prefix] is not None:
- ret.append(self.curse_add_line(str(p[stat_prefix]), decoration='INFO'))
- ret.append(self.curse_add_line(' {} '.format(stat_prefix.replace('num_', ''))))
-
- ret.append(self.curse_new_line())
- ret.append(self.curse_new_line())
-
- def __msg_curse_header(self, ret, process_sort_key, args=None):
- """Build the header and add it to the ret dict."""
- sort_style = 'SORT'
-
- if args.disable_irix and 0 < self.nb_log_core < 10:
- msg = self.layout_header['cpu'].format('CPU%/' + str(self.nb_log_core))
- elif args.disable_irix and self.nb_log_core != 0:
- msg = self.layout_header['cpu'].format('CPU%/C')
- else:
- msg = self.layout_header['cpu'].format('CPU%')
- ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_percent' else 'DEFAULT'))
- msg = self.layout_header['mem'].format('MEM%')
- ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'memory_percent' else 'DEFAULT'))
- msg = self.layout_header['virt'].format('VIRT')
- ret.append(self.curse_add_line(msg, optional=True))
- msg = self.layout_header['res'].format('RES')
- ret.append(self.curse_add_line(msg, optional=True))
- if not self.args.programs:
- msg = self.layout_header['pid'].format('PID', width=self.__max_pid_size())
- else:
- msg = self.layout_header['pid'].format('NPROCS', width=self.__max_pid_size())
- ret.append(self.curse_add_line(msg))
- msg = self.layout_header['user'].format('USER')
- ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'username' else 'DEFAULT'))
- msg = self.layout_header['time'].format('TIME+')
- ret.append(
- self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_times' else 'DEFAULT', optional=True)
- )
- msg = self.layout_header['thread'].format('THR')
- ret.append(self.curse_add_line(msg))
- msg = self.layout_header['nice'].format('NI')
- ret.append(self.curse_add_line(msg))
- msg = self.layout_header['status'].format('S')
- ret.append(self.curse_add_line(msg))
- msg = self.layout_header['ior'].format('R/s')
- ret.append(
- self.curse_add_line(
- msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
- )
- )
- msg = self.layout_header['iow'].format('W/s')
- ret.append(
- self.curse_add_line(
- msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True
- )
- )
- if args.is_standalone and not args.disable_cursor:
- if self.args.programs:
- shortkey = "('k' to kill)"
- else:
- shortkey = "('e' to pin | 'k' to kill)"
- else:
- shortkey = ""
- msg = self.layout_header['command'].format("Programs" if self.args.programs else "Command", shortkey)
- ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'name' else 'DEFAULT'))
-
- def __msg_curse_sum(self, ret, sep_char='_', mmm=None, args=None):
- """
- Build the sum message (only when filter is on) and add it to the ret dict.
-
- :param ret: list of string where the message is added
- :param sep_char: define the line separation char
- :param mmm: display min, max, mean or current (if mmm=None)
- :param args: Glances args
- """
- ret.append(self.curse_new_line())
- if mmm is None:
- ret.append(self.curse_add_line(sep_char * 69))
- ret.append(self.curse_new_line())
- # CPU percent sum
- msg = self.layout_stat['cpu'].format(self.__sum_stats('cpu_percent', mmm=mmm))
- ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm)))
- # MEM percent sum
- msg = self.layout_stat['mem'].format(self.__sum_stats('memory_percent', mmm=mmm))
- ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm)))
- # VIRT and RES memory sum
- if (
- 'memory_info' in self.stats[0]
- and self.stats[0]['memory_info'] is not None
- and self.stats[0]['memory_info'] != ''
- ):
- # VMS
- msg = self.layout_stat['virt'].format(
- self.auto_unit(self.__sum_stats('memory_info', indice=1, mmm=mmm), low_precision=False)
- )
- ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm), optional=True))
- # RSS
- msg = self.layout_stat['res'].format(
- self.auto_unit(self.__sum_stats('memory_info', indice=0, mmm=mmm), low_precision=False)
- )
- ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm), optional=True))
- else:
- msg = self.layout_header['virt'].format('')
- ret.append(self.curse_add_line(msg))
- msg = self.layout_header['res'].format('')
- ret.append(self.curse_add_line(msg))
- # PID
- msg = self.layout_header['pid'].format('', width=self.__max_pid_size())
- ret.append(self.curse_add_line(msg))
- # USER
- msg = self.layout_header['user'].format('')
- ret.append(self.curse_add_line(msg))
- # TIME+
- msg = self.layout_header['time'].format('')
- ret.append(self.curse_add_line(msg, optional=True))
- # THREAD
- msg = self.layout_header['thread'].format('')
- ret.append(self.curse_add_line(msg))
- # NICE
- msg = self.layout_header['nice'].format('')
- ret.append(self.curse_add_line(msg))
- # STATUS
- msg = self.layout_header['status'].format('')
- ret.append(self.curse_add_line(msg))
- # IO read/write
- if 'io_counters' in self.stats[0] and mmm is None:
- # IO read
- io_rs = int(
- (self.__sum_stats('io_counters', 0) - self.__sum_stats('io_counters', indice=2, mmm=mmm))
- / self.stats[0]['time_since_update']
- )
- if io_rs == 0:
- msg = self.layout_stat['ior'].format('0')
- else:
- msg = self.layout_stat['ior'].format(self.auto_unit(io_rs, low_precision=True))
- ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm), optional=True, additional=True))
- # IO write
- io_ws = int(
- (self.__sum_stats('io_counters', 1) - self.__sum_stats('io_counters', indice=3, mmm=mmm))
- / self.stats[0]['time_since_update']
- )
- if io_ws == 0:
- msg = self.layout_stat['iow'].format('0')
- else:
- msg = self.layout_stat['iow'].format(self.auto_unit(io_ws, low_precision=True))
- ret.append(self.curse_add_line(msg, decoration=self.__mmm_deco(mmm), optional=True, additional=True))
- else:
- msg = self.layout_header['ior'].format('')
- ret.append(self.curse_add_line(msg, optional=True, additional=True))
- msg = self.layout_header['iow'].format('')
- ret.append(self.curse_add_line(msg, optional=True, additional=True))
- if mmm is None:
- msg = ' < {}'.format('current')
- ret.append(self.curse_add_line(msg, optional=True))
- else:
- msg = ' < {}'.format(mmm)
- ret.append(self.curse_add_line(msg, optional=True))
- msg = ' (\'M\' to reset)'
- ret.append(self.curse_add_line(msg, optional=True))
-
- def __mmm_deco(self, mmm):
- """Return the decoration string for the current mmm status."""
- if mmm is not None:
- return 'DEFAULT'
- else:
- return 'FILTER'
-
- def __mmm_reset(self):
- """Reset the MMM stats."""
- self.mmm_min = {}
- self.mmm_max = {}
-
- def __sum_stats(self, key, indice=None, mmm=None):
- """Return the sum of the stats value for the given key.
-
- :param indice: If indice is set, get the p[key][indice]
- :param mmm: display min, max, mean or current (if mmm=None)
- """
- # Compute stats summary
- ret = 0
- for p in self.stats:
- if key not in p:
- # Correct issue #1188
- continue
- if p[key] is None:
- # Correct https://github.com/nicolargo/glances/issues/1105#issuecomment-363553788
- continue
- if indice is None:
- ret += p[key]
- else:
- ret += p[key][indice]
-
- # Manage Min/Max/Mean
- mmm_key = self.__mmm_key(key, indice)
- if mmm == 'min':
- try:
- if self.mmm_min[mmm_key] > ret:
- self.mmm_min[mmm_key] = ret
- except AttributeError:
- self.mmm_min = {}
- return 0
- except KeyError:
- self.mmm_min[mmm_key] = ret
- ret = self.mmm_min[mmm_key]
- elif mmm == 'max':
- try:
- if self.mmm_max[mmm_key] < ret:
- self.mmm_max[mmm_key] = ret
- except AttributeError:
- self.mmm_max = {}
- return 0
- except KeyError:
- self.mmm_max[mmm_key] = ret
- ret = self.mmm_max[mmm_key]
-
- return ret
-
- def __mmm_key(self, key, indice):
- ret = key
- if indice is not None:
- ret += str(indice)
- return ret
-
- def __sort_stats(self, sorted_by=None):
- """Return the stats (dict) sorted by (sorted_by)."""
- return sort_stats(self.stats, sorted_by, reverse=glances_processes.sort_reverse)
-
- def __max_pid_size(self):
- """Return the maximum PID size in number of char."""
- if self.pid_max is not None:
- return len(str(self.pid_max))
- else:
- # By default return 5 (corresponding to 99999 PID number)
- return 5