From eebd769c4610f1a3ca9f3aa1e4fe7843aeeafc26 Mon Sep 17 00:00:00 2001 From: nicolargo Date: Sun, 23 Jun 2024 10:22:37 +0200 Subject: [PATCH] perCPU and CPU consumption display time to time a total of 100% #2849 --- glances/cpu_percent.py | 53 +++++++++++++-------------- glances/plugins/cpu/__init__.py | 4 +- glances/plugins/percpu/__init__.py | 10 ++--- glances/plugins/quicklook/__init__.py | 4 +- glances/stats.py | 12 ++---- test-data/issues/issue2849.py | 25 +++++++++++++ 6 files changed, 60 insertions(+), 48 deletions(-) create mode 100644 test-data/issues/issue2849.py diff --git a/glances/cpu_percent.py b/glances/cpu_percent.py index 690f9fd1..f8b24e61 100644 --- a/glances/cpu_percent.py +++ b/glances/cpu_percent.py @@ -17,36 +17,30 @@ from glances.timer import Timer class CpuPercent: """Get and store the CPU percent.""" - def __init__(self, cached_timer_cpu=3): - self.cpu_info = {'cpu_name': None, 'cpu_hz_current': None, 'cpu_hz': None} - self.cpu_percent = 0 - self.percpu_percent = [] - - # Get CPU name - self.cpu_info['cpu_name'] = self.__get_cpu_name() - + def __init__(self, cached_timer_cpu=2): # cached_timer_cpu is the minimum time interval between stats updates # since last update is passed (will retrieve old cached info instead) self.cached_timer_cpu = cached_timer_cpu - self.timer_cpu = Timer(0) - self.timer_percpu = Timer(0) - # psutil.cpu_freq() consumes lots of CPU - # So refresh the stats every refresh*2 (6 seconds) + # So refresh CPU frequency stats every refresh * 2 self.cached_timer_cpu_info = cached_timer_cpu * 2 + + # Get CPU name self.timer_cpu_info = Timer(0) + self.cpu_info = {'cpu_name': self.__get_cpu_name(), 'cpu_hz_current': None, 'cpu_hz': None} + + # Warning from PsUtil documentation + # The first time this function is called with interval = 0.0 or None + # it will return a meaningless 0.0 value which you are supposed to ignore. + self.timer_cpu = Timer(0) + self.cpu_percent = self.get_cpu() + self.timer_percpu = Timer(0) + self.percpu_percent = self.get_percpu() def get_key(self): """Return the key of the per CPU list.""" return 'cpu_number' - def get(self, percpu=False): - """Update and/or return the CPU using the psutil library. - If percpu, return the percpu stats""" - if percpu: - return self.__get_percpu() - return self.__get_cpu() - def get_info(self): """Get additional information about the CPU""" # Never update more than 1 time per cached_timer_cpu_info @@ -84,21 +78,26 @@ class CpuPercent: break return ret if ret else 'CPU' - def __get_cpu(self): + def get_cpu(self): """Update and/or return the CPU using the psutil library.""" # Never update more than 1 time per cached_timer_cpu if self.timer_cpu.finished(): - self.cpu_percent = psutil.cpu_percent(interval=0.0) # Reset timer for cache self.timer_cpu.reset(duration=self.cached_timer_cpu) + # Update the stats + self.cpu_percent = psutil.cpu_percent(interval=0.0) return self.cpu_percent - def __get_percpu(self): + def get_percpu(self): """Update and/or return the per CPU list using the psutil library.""" # Never update more than 1 time per cached_timer_cpu if self.timer_percpu.finished(): - self.percpu_percent = [] - for cpu_number, cputimes in enumerate(psutil.cpu_times_percent(interval=0.0, percpu=True)): + # Reset timer for cache + self.timer_percpu.reset(duration=self.cached_timer_cpu) + # Get stats + percpu_percent = [] + psutil_percpu = enumerate(psutil.cpu_times_percent(interval=0.0, percpu=True)) + for cpu_number, cputimes in psutil_percpu: cpu = { 'key': self.get_key(), 'cpu_number': cpu_number, @@ -123,9 +122,9 @@ class CpuPercent: if hasattr(cputimes, 'guest_nice'): cpu['guest_nice'] = cputimes.guest_nice # Append new CPU to the list - self.percpu_percent.append(cpu) - # Reset timer for cache - self.timer_percpu.reset(duration=self.cached_timer_cpu) + percpu_percent.append(cpu) + # Update stats + self.percpu_percent = percpu_percent return self.percpu_percent diff --git a/glances/plugins/cpu/__init__.py b/glances/plugins/cpu/__init__.py index 63d051d6..ecc30a23 100644 --- a/glances/plugins/cpu/__init__.py +++ b/glances/plugins/cpu/__init__.py @@ -165,8 +165,6 @@ class PluginModel(GlancesPluginModel): stats = self.update_local() elif self.input_method == 'snmp': stats = self.update_snmp() - else: - stats = self.get_init_value() # Update the stats self.stats = stats @@ -185,7 +183,7 @@ class PluginModel(GlancesPluginModel): # Init new stats stats = self.get_init_value() - stats['total'] = cpu_percent.get() + stats['total'] = cpu_percent.get_cpu() # Standards stats # - user: time spent by normal processes executing in user mode; on Linux this also includes guest time diff --git a/glances/plugins/percpu/__init__.py b/glances/plugins/percpu/__init__.py index 66e2c438..bf52446d 100644 --- a/glances/plugins/percpu/__init__.py +++ b/glances/plugins/percpu/__init__.py @@ -120,16 +120,12 @@ class PluginModel(GlancesPluginModel): @GlancesPluginModel._log_result_decorator def update(self): """Update per-CPU stats using the input method.""" - # Init new stats - stats = self.get_init_value() - - # Grab per-CPU stats using psutil's cpu_percent(percpu=True) and - # cpu_times_percent(percpu=True) methods + # Grab per-CPU stats using psutil's if self.input_method == 'local': - stats = cpu_percent.get(percpu=True) + stats = cpu_percent.get_percpu() else: # Update stats using SNMP - pass + stats = self.get_init_value() # Update the stats self.stats = stats diff --git a/glances/plugins/quicklook/__init__.py b/glances/plugins/quicklook/__init__.py index 091e4963..b36bca74 100644 --- a/glances/plugins/quicklook/__init__.py +++ b/glances/plugins/quicklook/__init__.py @@ -118,8 +118,8 @@ class PluginModel(GlancesPluginModel): # Get the CPU percent value (global and per core) # Stats is shared across all plugins - stats['cpu'] = cpu_percent.get() - stats['percpu'] = cpu_percent.get(percpu=True) + stats['cpu'] = cpu_percent.get_cpu() + stats['percpu'] = cpu_percent.get_percpu() # Get the virtual and swap memory stats['mem'] = psutil.virtual_memory().percent diff --git a/glances/stats.py b/glances/stats.py index 921c3da8..e033723f 100644 --- a/glances/stats.py +++ b/glances/stats.py @@ -260,19 +260,13 @@ class GlancesStats: self._plugins[p].update_views() def update(self): - """Wrapper method to update the stats. + """Wrapper method to update all stats. Only called by standalone and server modes """ - threads = [] # Start update of all enable plugins - for p in self.getPluginsList(): - thread = threading.Thread(target=self.__update_plugin, args=(p,)) - thread.start() - threads.append(thread) - # Wait the end of the update - for t in threads: - t.join() + for p in self.getPluginsList(enable=True): + self.__update_plugin(p) def export(self, input_stats=None): """Export all the stats. diff --git a/test-data/issues/issue2849.py b/test-data/issues/issue2849.py new file mode 100644 index 00000000..34691a97 --- /dev/null +++ b/test-data/issues/issue2849.py @@ -0,0 +1,25 @@ +import sys +import time + +sys.path.insert(0, '../glances') + +########### + +# from glances.cpu_percent import cpu_percent + +# for _ in range(0, 5): +# print([i['total'] for i in cpu_percent.get_percpu()]) +# time.sleep(2) + +########### + +from glances.main import GlancesMain +from glances.stats import GlancesStats + +core = GlancesMain() +stats = GlancesStats(config=core.get_config(), args=core.get_args()) + +for _ in range(0, 5): + stats.update() + print([i['total'] for i in stats.get_plugin('percpu').get_raw()]) + time.sleep(2)