mirror of https://github.com/nicolargo/glances.git
Merge pull request #3029 from ariel-anieli/glances-processes
Refactored `glances.processes.GlancesProcesses.update`, version 2
This commit is contained in:
commit
8258101d35
|
|
@ -58,7 +58,7 @@ try:
|
|||
year = datetime.utcfromtimestamp(int(os.environ['SOURCE_DATE_EPOCH'])).year
|
||||
except (KeyError, ValueError):
|
||||
year = datetime.now().year
|
||||
copyright = '%d, %s' % (year, author)
|
||||
copyright = f'{year}, {author}'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
|
|
|||
|
|
@ -424,6 +424,118 @@ class GlancesProcesses:
|
|||
and not self.args.disable_cursor
|
||||
)
|
||||
|
||||
def build_process_list(self, sorted_attrs):
|
||||
# Build the processes stats list (it is why we need psutil>=5.3.0) (see issue #2755)
|
||||
processlist = list(
|
||||
filter(
|
||||
lambda p: not (BSD and p.info['name'] == 'idle')
|
||||
and not (WINDOWS and p.info['name'] == 'System Idle Process')
|
||||
and not (MACOS and p.info['name'] == 'kernel_task')
|
||||
and not (self.no_kernel_threads and LINUX and p.info['gids'].real == 0),
|
||||
psutil.process_iter(attrs=sorted_attrs, ad_value=None),
|
||||
)
|
||||
)
|
||||
|
||||
# Only get the info key
|
||||
# PsUtil 6+ no longer check PID reused #2755 so use is_running in the loop
|
||||
# Note: not sure it is realy needed but CPU consumption look the same with or without it
|
||||
processlist = [p.info for p in processlist if p.is_running()]
|
||||
|
||||
# Sort the processes list by the current sort_key
|
||||
return sort_stats(processlist, sorted_by=self.sort_key, reverse=True)
|
||||
|
||||
def get_sorted_attrs(self):
|
||||
defaults = ['cpu_percent', 'cpu_times', 'memory_percent', 'name', 'status', 'num_threads']
|
||||
optional = ['io_counters'] if not self.disable_io_counters else []
|
||||
|
||||
return defaults + optional
|
||||
|
||||
def get_displayed_attr(self):
|
||||
defaults = ['memory_info', 'nice', 'pid']
|
||||
optional = ['gids'] if not self.disable_gids else []
|
||||
|
||||
return defaults + optional
|
||||
|
||||
def get_cached_attrs(self):
|
||||
return ['cmdline', 'username']
|
||||
|
||||
def maybe_add_cached_attrs(self, sorted_attrs, cached_attrs):
|
||||
# Some stats are not sort key
|
||||
# An optimisation can be done be only grabbed displayed_attr
|
||||
# for displayed processes (but only in standalone mode...)
|
||||
sorted_attrs.extend(self.get_displayed_attr())
|
||||
# Some stats are cached (not necessary to be refreshed every time)
|
||||
if self.cache_timer.finished():
|
||||
sorted_attrs += cached_attrs
|
||||
self.cache_timer.set(self.cache_timeout)
|
||||
self.cache_timer.reset()
|
||||
is_cached = False
|
||||
else:
|
||||
is_cached = True
|
||||
|
||||
return is_cached, sorted_attrs
|
||||
|
||||
def get_pid_time_and_status(self, time_since_update, proc):
|
||||
# PID is the key
|
||||
proc['key'] = 'pid'
|
||||
|
||||
# Time since last update (for disk_io rate computation)
|
||||
proc['time_since_update'] = time_since_update
|
||||
|
||||
# Process status (only keep the first char)
|
||||
proc['status'] = str(proc.get('status', '?'))[:1].upper()
|
||||
|
||||
return proc
|
||||
|
||||
def get_io_counters(self, proc):
|
||||
# procstat['io_counters'] is a list:
|
||||
# [read_bytes, write_bytes, read_bytes_old, write_bytes_old, io_tag]
|
||||
# If io_tag = 0 > Access denied or first time (display "?")
|
||||
# If io_tag = 1 > No access denied (display the IO rate)
|
||||
if 'io_counters' in proc and proc['io_counters'] is not None:
|
||||
io_new = [proc['io_counters'][2], proc['io_counters'][3]]
|
||||
# For IO rate computation
|
||||
# Append saved IO r/w bytes
|
||||
try:
|
||||
proc['io_counters'] = io_new + self.io_old[proc['pid']]
|
||||
io_tag = 1
|
||||
except KeyError:
|
||||
proc['io_counters'] = io_new + [0, 0]
|
||||
io_tag = 0
|
||||
# then save the IO r/w bytes
|
||||
self.io_old[proc['pid']] = io_new
|
||||
else:
|
||||
proc['io_counters'] = [0, 0] + [0, 0]
|
||||
io_tag = 0
|
||||
# Append the IO tag (for display)
|
||||
proc['io_counters'] += [io_tag]
|
||||
|
||||
return proc
|
||||
|
||||
def maybe_add_cached_stats(self, is_cached, cached_attrs, proc):
|
||||
if is_cached:
|
||||
# Grab cached values (in case of a new incoming process)
|
||||
if proc['pid'] not in self.processlist_cache:
|
||||
try:
|
||||
self.processlist_cache[proc['pid']] = psutil.Process(pid=proc['pid']).as_dict(
|
||||
attrs=cached_attrs, ad_value=None
|
||||
)
|
||||
except psutil.NoSuchProcess:
|
||||
pass
|
||||
# Add cached value to current stat
|
||||
try:
|
||||
proc.update(self.processlist_cache[proc['pid']])
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
# Save values to cache
|
||||
try:
|
||||
self.processlist_cache[proc['pid']] = {cached: proc[cached] for cached in cached_attrs}
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return proc
|
||||
|
||||
def update(self):
|
||||
"""Update the processes stats."""
|
||||
# Init new processes stats
|
||||
|
|
@ -438,49 +550,17 @@ class GlancesProcesses:
|
|||
|
||||
# Grab standard stats
|
||||
#####################
|
||||
sorted_attrs = ['cpu_percent', 'cpu_times', 'memory_percent', 'name', 'status', 'num_threads']
|
||||
displayed_attr = ['memory_info', 'nice', 'pid']
|
||||
sorted_attrs = self.get_sorted_attrs()
|
||||
|
||||
# The following attributes are cached and only retrieve every self.cache_timeout seconds
|
||||
# Warning: 'name' can not be cached because it is used for filtering
|
||||
cached_attrs = ['cmdline', 'username']
|
||||
cached_attrs = self.get_cached_attrs()
|
||||
|
||||
# Some stats are optional
|
||||
if not self.disable_io_counters:
|
||||
sorted_attrs.append('io_counters')
|
||||
if not self.disable_gids:
|
||||
displayed_attr.append('gids')
|
||||
# Some stats are not sort key
|
||||
# An optimisation can be done be only grabbed displayed_attr
|
||||
# for displayed processes (but only in standalone mode...)
|
||||
sorted_attrs.extend(displayed_attr)
|
||||
# Some stats are cached (not necessary to be refreshed every time)
|
||||
if self.cache_timer.finished():
|
||||
sorted_attrs += cached_attrs
|
||||
self.cache_timer.set(self.cache_timeout)
|
||||
self.cache_timer.reset()
|
||||
is_cached = False
|
||||
else:
|
||||
is_cached = True
|
||||
is_cached, sorted_attrs = self.maybe_add_cached_attrs(sorted_attrs, cached_attrs)
|
||||
|
||||
# Remove attributes set by the user in the config file (see #1524)
|
||||
sorted_attrs = [i for i in sorted_attrs if i not in self.disable_stats]
|
||||
|
||||
# Build the processes stats list (it is why we need psutil>=5.3.0) (see issue #2755)
|
||||
processlist = list(
|
||||
filter(
|
||||
lambda p: not (BSD and p.info['name'] == 'idle')
|
||||
and not (WINDOWS and p.info['name'] == 'System Idle Process')
|
||||
and not (MACOS and p.info['name'] == 'kernel_task')
|
||||
and not (self.no_kernel_threads and LINUX and p.info['gids'].real == 0),
|
||||
psutil.process_iter(attrs=sorted_attrs, ad_value=None),
|
||||
)
|
||||
)
|
||||
# Only get the info key
|
||||
# PsUtil 6+ no longer check PID reused #2755 so use is_running in the loop
|
||||
# Note: not sure it is realy needed but CPU consumption look the same with or without it
|
||||
processlist = [p.info for p in processlist if p.is_running()]
|
||||
# Sort the processes list by the current sort_key
|
||||
processlist = sort_stats(processlist, sorted_by=self.sort_key, reverse=True)
|
||||
processlist = self.build_process_list(sorted_attrs)
|
||||
|
||||
# Update the processcount
|
||||
self.update_processcount(processlist)
|
||||
|
|
@ -503,67 +583,16 @@ class GlancesProcesses:
|
|||
|
||||
# Meta data
|
||||
###########
|
||||
|
||||
# PID is the key
|
||||
proc['key'] = 'pid'
|
||||
|
||||
# Time since last update (for disk_io rate computation)
|
||||
proc['time_since_update'] = time_since_update
|
||||
|
||||
# Process status (only keep the first char)
|
||||
proc['status'] = str(proc.get('status', '?'))[:1].upper()
|
||||
proc = self.get_pid_time_and_status(time_since_update, proc)
|
||||
|
||||
# Process IO
|
||||
# procstat['io_counters'] is a list:
|
||||
# [read_bytes, write_bytes, read_bytes_old, write_bytes_old, io_tag]
|
||||
# If io_tag = 0 > Access denied or first time (display "?")
|
||||
# If io_tag = 1 > No access denied (display the IO rate)
|
||||
if 'io_counters' in proc and proc['io_counters'] is not None:
|
||||
io_new = [proc['io_counters'][2], proc['io_counters'][3]]
|
||||
# For IO rate computation
|
||||
# Append saved IO r/w bytes
|
||||
try:
|
||||
proc['io_counters'] = io_new + self.io_old[proc['pid']]
|
||||
io_tag = 1
|
||||
except KeyError:
|
||||
proc['io_counters'] = io_new + [0, 0]
|
||||
io_tag = 0
|
||||
# then save the IO r/w bytes
|
||||
self.io_old[proc['pid']] = io_new
|
||||
else:
|
||||
proc['io_counters'] = [0, 0] + [0, 0]
|
||||
io_tag = 0
|
||||
# Append the IO tag (for display)
|
||||
proc['io_counters'] += [io_tag]
|
||||
proc = self.get_io_counters(proc)
|
||||
|
||||
# Manage cached information
|
||||
if is_cached:
|
||||
# Grab cached values (in case of a new incoming process)
|
||||
if proc['pid'] not in self.processlist_cache:
|
||||
try:
|
||||
self.processlist_cache[proc['pid']] = psutil.Process(pid=proc['pid']).as_dict(
|
||||
attrs=cached_attrs, ad_value=None
|
||||
)
|
||||
except psutil.NoSuchProcess:
|
||||
pass
|
||||
# Add cached value to current stat
|
||||
try:
|
||||
proc.update(self.processlist_cache[proc['pid']])
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
# Save values to cache
|
||||
try:
|
||||
self.processlist_cache[proc['pid']] = {cached: proc[cached] for cached in cached_attrs}
|
||||
except KeyError:
|
||||
pass
|
||||
proc = self.maybe_add_cached_stats(is_cached, cached_attrs, proc)
|
||||
|
||||
# Remove non running process from the cache (avoid issue #2976)
|
||||
pids_running = [p['pid'] for p in processlist]
|
||||
pids_cached = list(self.processlist_cache.keys()).copy()
|
||||
for pid in pids_cached:
|
||||
if pid not in pids_running:
|
||||
self.processlist_cache.pop(pid, None)
|
||||
self.remove_non_running_procs(processlist)
|
||||
|
||||
# Filter and transform process export list
|
||||
self.processlist_export = self.update_export_list(processlist)
|
||||
|
|
@ -573,16 +602,26 @@ class GlancesProcesses:
|
|||
|
||||
# Compute the maximum value for keys in self._max_values_list: CPU, MEM
|
||||
# Useful to highlight the processes with maximum values
|
||||
for k in [i for i in self._max_values_list if i not in self.disable_stats]:
|
||||
values_list = [i[k] for i in processlist if i[k] is not None]
|
||||
if values_list:
|
||||
self.set_max_values(k, max(values_list))
|
||||
self.compute_max_value(processlist)
|
||||
|
||||
# Update the stats
|
||||
self.processlist = processlist
|
||||
|
||||
return self.processlist
|
||||
|
||||
def compute_max_value(self, processlist):
|
||||
for k in [i for i in self._max_values_list if i not in self.disable_stats]:
|
||||
values_list = [i[k] for i in processlist if i[k] is not None]
|
||||
if values_list:
|
||||
self.set_max_values(k, max(values_list))
|
||||
|
||||
def remove_non_running_procs(self, processlist):
|
||||
pids_running = [p['pid'] for p in processlist]
|
||||
pids_cached = list(self.processlist_cache.keys()).copy()
|
||||
for pid in pids_cached:
|
||||
if pid not in pids_running:
|
||||
self.processlist_cache.pop(pid, None)
|
||||
|
||||
def update_list(self, processlist):
|
||||
"""Return the process list after filtering and transformation (namedtuple to dict)."""
|
||||
if self._filter.filter is None:
|
||||
|
|
|
|||
Loading…
Reference in New Issue