diff options
author | nicolargo <nicolashennion@gmail.com> | 2024-07-03 19:47:05 +0200 |
---|---|---|
committer | nicolargo <nicolashennion@gmail.com> | 2024-07-03 19:47:05 +0200 |
commit | ca41d0984571a38e62928ef92976077566417424 (patch) | |
tree | 837629a58ea1e43cf674d6ff3df5d2c6ada3f0d5 | |
parent | 90f4934c62eac8c42ab4df82e5e735901113570f (diff) | |
parent | fd99c8008d87bcb1d20d117576dedeba90b67b54 (diff) |
Glances 4.1.2
-rw-r--r-- | glances/cpu_percent.py | 125 | ||||
-rw-r--r-- | glances/plugins/percpu/__init__.py | 35 |
2 files changed, 99 insertions, 61 deletions
diff --git a/glances/cpu_percent.py b/glances/cpu_percent.py index baa3b359..0f76e7d8 100644 --- a/glances/cpu_percent.py +++ b/glances/cpu_percent.py @@ -8,16 +8,44 @@ """CPU percent stats shared between CPU and Quicklook plugins.""" +from typing import List, Optional, TypedDict + import psutil from glances.logger import logger from glances.timer import Timer +__all__ = ["cpu_percent"] + + +class CpuInfo(TypedDict): + cpu_name: str + cpu_hz: Optional[float] + cpu_hz_current: Optional[float] + + +class PerCpuPercentInfo(TypedDict): + key: str + cpu_number: int + total: float + user: float + system: float + idle: float + nice: Optional[float] + iowait: Optional[float] + irq: Optional[float] + softirq: Optional[float] + steal: Optional[float] + guest: Optional[float] + guest_nice: Optional[float] + dpc: Optional[float] + interrupt: Optional[float] + class CpuPercent: """Get and store the CPU percent.""" - def __init__(self, cached_timer_cpu=2): + def __init__(self, cached_timer_cpu: int = 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 @@ -27,21 +55,21 @@ class CpuPercent: # 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} + self.cpu_info: CpuInfo = {'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.cpu_percent = self._compute_cpu() self.timer_percpu = Timer(0) - self.percpu_percent = self.get_percpu() + self.percpu_percent = self._compute_percpu() def get_key(self): """Return the key of the per CPU list.""" return 'cpu_number' - def get_info(self): + def get_info(self) -> CpuInfo: """Get additional information about the CPU""" # Never update more than 1 time per cached_timer_cpu_info if self.timer_cpu_info.finished() and hasattr(psutil, 'cpu_freq'): @@ -63,70 +91,69 @@ class CpuPercent: self.timer_cpu_info.reset(duration=self.cached_timer_cpu_info) return self.cpu_info - def __get_cpu_name(self): + @staticmethod + def __get_cpu_name() -> str: # Get the CPU name once from the /proc/cpuinfo file # Read the first line with the "model name" ("Model" for Raspberry Pi) - ret = None try: - cpuinfo_file = open('/proc/cpuinfo').readlines() + cpuinfo_lines = open('/proc/cpuinfo').readlines() except (FileNotFoundError, PermissionError): - pass - else: - for line in cpuinfo_file: - if line.startswith('model name') or line.startswith('Model') or line.startswith('cpu model'): - ret = line.split(':')[1].strip() - break - return ret if ret else 'CPU' - - def get_cpu(self): + logger.debug("No permission to read '/proc/cpuinfo'") + return 'CPU' + + for line in cpuinfo_lines: + if line.startswith('model name') or line.startswith('Model') or line.startswith('cpu model'): + return line.split(':')[1].strip() + + return 'CPU' + + def get_cpu(self) -> float: """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(): # 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) + self.cpu_percent = self._compute_cpu() return self.cpu_percent - def get_percpu(self): + @staticmethod + def _compute_cpu() -> float: + return psutil.cpu_percent(interval=0.0) + + def get_percpu(self) -> List[PerCpuPercentInfo]: """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(): # 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, - 'total': round(100 - cputimes.idle, 1), - 'user': cputimes.user, - 'system': cputimes.system, - 'idle': cputimes.idle, - } - # The following stats are for API purposes only - if hasattr(cputimes, 'nice'): - cpu['nice'] = cputimes.nice - if hasattr(cputimes, 'iowait'): - cpu['iowait'] = cputimes.iowait - if hasattr(cputimes, 'irq'): - cpu['irq'] = cputimes.irq - if hasattr(cputimes, 'softirq'): - cpu['softirq'] = cputimes.softirq - if hasattr(cputimes, 'steal'): - cpu['steal'] = cputimes.steal - if hasattr(cputimes, 'guest'): - cpu['guest'] = cputimes.guest - if hasattr(cputimes, 'guest_nice'): - cpu['guest_nice'] = cputimes.guest_nice - # Append new CPU to the list - percpu_percent.append(cpu) # Update stats - self.percpu_percent = percpu_percent + self.percpu_percent = self._compute_percpu() return self.percpu_percent + def _compute_percpu(self) -> List[PerCpuPercentInfo]: + psutil_percpu = enumerate(psutil.cpu_times_percent(interval=0.0, percpu=True)) + return [ + { + 'key': self.get_key(), + 'cpu_number': cpu_number, + 'total': round(100 - cpu_times.idle, 1), + 'user': cpu_times.user, + 'system': cpu_times.system, + 'idle': cpu_times.idle, + 'nice': cpu_times.nice if hasattr(cpu_times, 'nice') else None, + 'iowait': cpu_times.iowait if hasattr(cpu_times, 'iowait') else None, + 'irq': cpu_times.irq if hasattr(cpu_times, 'irq') else None, + 'softirq': cpu_times.softirq if hasattr(cpu_times, 'softirq') else None, + 'steal': cpu_times.steal if hasattr(cpu_times, 'steal') else None, + 'guest': cpu_times.guest if hasattr(cpu_times, 'guest') else None, + 'guest_nice': cpu_times.steal if hasattr(cpu_times, 'guest_nice') else None, + 'dpc': cpu_times.dpc if hasattr(cpu_times, 'dpc') else None, + 'interrupt': cpu_times.interrupt if hasattr(cpu_times, 'interrupt') else None, + } + for cpu_number, cpu_times in psutil_percpu + ] + # CpuPercent instance shared between plugins cpu_percent = CpuPercent() diff --git a/glances/plugins/percpu/__init__.py b/glances/plugins/percpu/__init__.py index bf52446d..2c6aa79d 100644 --- a/glances/plugins/percpu/__init__.py +++ b/glances/plugins/percpu/__init__.py @@ -9,6 +9,7 @@ """Per-CPU plugin.""" from glances.cpu_percent import cpu_percent +from glances.globals import BSD, LINUX, MACOS, WINDOWS from glances.plugins.plugin.model import GlancesPluginModel # Fields description @@ -76,9 +77,16 @@ guest operating systems under the control of the Linux kernel.', 'description': '*(Linux)*: percent of time spent handling software interrupts.', 'unit': 'percent', }, + 'dpc': { + 'description': '*(Windows)*: percent of time spent handling deferred procedure calls.', + 'unit': 'percent', + }, + 'interrupt': { + 'description': '*(Windows)*: percent of time spent handling software interrupts.', + 'unit': 'percent', + }, } - # Define the history items list items_history_list = [ {'name': 'user', 'description': 'User CPU usage', 'y_unit': '%'}, @@ -141,8 +149,17 @@ class PluginModel(GlancesPluginModel): if not self.stats or not self.args.percpu or self.is_disabled(): return ret - # Define the default header - header = ['user', 'system', 'idle', 'iowait', 'steal'] + # Define the headers based on OS + header = ['user', 'system'] + + if LINUX: + header.extend(['iowait', 'idle', 'irq', 'nice', 'steal', 'guest']) + elif MACOS: + header.extend(['idle', 'nice']) + elif BSD: + header.extend(['idle', 'irq', 'nice']) + elif WINDOWS: + header.extend(['dpc', 'interrupt']) # Build the string message if self.is_disabled('quicklook'): @@ -152,8 +169,6 @@ class PluginModel(GlancesPluginModel): # Per CPU stats displayed per line for stat in header: - if stat not in self.stats[0]: - continue msg = f'{stat:>7}' ret.append(self.curse_add_line(msg)) @@ -179,8 +194,6 @@ class PluginModel(GlancesPluginModel): msg = '{:4} '.format('?') ret.append(self.curse_add_line(msg)) for stat in header: - if stat not in self.stats[0]: - continue try: msg = f'{cpu[stat]:6.1f}%' except TypeError: @@ -192,12 +205,10 @@ class PluginModel(GlancesPluginModel): ret.append(self.curse_new_line()) if self.is_disabled('quicklook'): ret.append(self.curse_add_line('CPU* ')) + for stat in header: - if stat not in self.stats[0]: - continue - cpu_stat = sum([i[stat] for i in percpu_list[0 : self.max_cpu_display]]) / len( - [i[stat] for i in percpu_list[0 : self.max_cpu_display]] - ) + percpu_stats = [i[stat] for i in percpu_list[0 : self.max_cpu_display]] + cpu_stat = sum(percpu_stats) / len(percpu_stats) try: msg = f'{cpu_stat:6.1f}%' except TypeError: |