Test Failed
Pull Request — develop (#3395)
by
unknown
03:12
created

glances.cpu_percent.CpuPercent.__get_cpu_name()   C

Complexity

Conditions 9

Size

Total Lines 25
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 19
nop 0
dl 0
loc 25
rs 6.6666
c 0
b 0
f 0
1
#
2
# This file is part of Glances.
3
#
4
# SPDX-FileCopyrightText: 2022 Nicolas Hennion <[email protected]>
5
#
6
# SPDX-License-Identifier: LGPL-3.0-only
7
#
8
9
"""CPU percent stats shared between CPU and Quicklook plugins."""
10
11
import platform
12
from typing import TypedDict
13
14
import psutil
15
16
from glances.logger import logger
17
from glances.timer import Timer
18
19
__all__ = ["cpu_percent"]
20
21
CPU_IMPLEMENTERS = {
22
    0x41: 'ARM Limited',
23
    0x42: 'Broadcom',
24
    0x43: 'Cavium',
25
    0x44: 'DEC',
26
    0x46: 'Fujitsu',
27
    0x48: 'HiSilicon',
28
    0x49: 'Infineon Technologies',
29
    0x4D: 'Motorola/Freescale',
30
    0x4E: 'NVIDIA',
31
    0x50: 'Applied Micro (APM)',
32
    0x51: 'Qualcomm',
33
    0x53: 'Samsung',
34
    0x56: 'Marvell',
35
    0x61: 'Apple',
36
    0x66: 'Faraday',
37
    0x69: 'Intel',
38
    0x6D: 'Microsoft',
39
    0x70: 'Phytium',
40
    0xC0: 'Ampere Computing',
41
}
42
43
CPU_PARTS = {
44
    # ARM Limited (0x41)
45
    0x41: {
46
        0xD03: 'Cortex-A53',
47
        0xD04: 'Cortex-A35',
48
        0xD05: 'Cortex-A55',
49
        0xD06: 'Cortex-A65',
50
        0xD07: 'Cortex-A57',
51
        0xD08: 'Cortex-A72',
52
        0xD09: 'Cortex-A73',
53
        0xD0A: 'Cortex-A75',
54
        0xD0B: 'Cortex-A76',
55
        0xD0C: 'Neoverse N1',
56
        0xD0D: 'Cortex-A77',
57
        0xD0E: 'Cortex-A76AE',
58
        0xD13: 'Cortex-R52',
59
        0xD20: 'Cortex-M23',
60
        0xD21: 'Cortex-M33',
61
        0xD40: 'Neoverse V1',
62
        0xD41: 'Cortex-A78',
63
        0xD42: 'Cortex-A78AE',
64
        0xD43: 'Cortex-A65AE',
65
        0xD44: 'Cortex-X1',
66
        0xD46: 'Cortex-A510',
67
        0xD47: 'Cortex-A710',
68
        0xD48: 'Cortex-X2',
69
        0xD49: 'Neoverse N2',
70
        0xD4A: 'Neoverse E1',
71
        0xD4B: 'Cortex-A78C',
72
        0xD4C: 'Cortex-X1C',
73
        0xD4D: 'Cortex-A715',
74
        0xD4E: 'Cortex-X3',
75
        0xD4F: 'Neoverse V2',
76
        0xD80: 'Cortex-A520',
77
        0xD81: 'Cortex-A720',
78
        0xD82: 'Cortex-X4',
79
        0xD84: 'Neoverse V3',
80
        0xD85: 'Cortex-X925',
81
        0xD87: 'Cortex-A725',
82
    },
83
    # Apple (0x61)
84
    0x61: {
85
        0x000: 'Swift',
86
        0x001: 'Cyclone',
87
        0x002: 'Typhoon',
88
        0x003: 'Twister',
89
        0x004: 'Hurricane',
90
        0x005: 'Monsoon/Mistral',
91
        0x006: 'Vortex/Tempest',
92
        0x007: 'Lightning/Thunder',
93
        0x008: 'Firestorm/Icestorm (M1)',
94
        0x009: 'Avalanche/Blizzard (M2)',
95
        0x00E: 'Everest/Sawtooth (M3)',
96
        0x010: 'Blizzard/Avalanche (A16)',
97
        0x011: 'Coll (M4)',
98
    },
99
    # Qualcomm (0x51)
100
    0x51: {
101
        0x00F: 'Scorpion',
102
        0x02D: 'Scorpion',
103
        0x04D: 'Krait',
104
        0x06F: 'Krait',
105
        0x201: 'Kryo',
106
        0x205: 'Kryo',
107
        0x211: 'Kryo',
108
        0x800: 'Kryo 260/280 Gold (Cortex-A73)',
109
        0x801: 'Kryo 260/280 Silver (Cortex-A53)',
110
        0x802: 'Kryo 385 Gold (Cortex-A75)',
111
        0x803: 'Kryo 385 Silver (Cortex-A55)',
112
        0x804: 'Kryo 485 Gold (Cortex-A76)',
113
        0x805: 'Kryo 485 Silver (Cortex-A55)',
114
        0xC00: 'Falkor',
115
        0xC01: 'Saphira',
116
    },
117
    # Samsung (0x53)
118
    0x53: {
119
        0x001: 'Exynos M1/M2',
120
        0x002: 'Exynos M3',
121
        0x003: 'Exynos M4',
122
        0x004: 'Exynos M5',
123
    },
124
    # NVIDIA (0x4e)
125
    0x4E: {
126
        0x000: 'Denver',
127
        0x003: 'Denver 2',
128
        0x004: 'Carmel',
129
    },
130
    # Marvell (0x56)
131
    0x56: {
132
        0x131: 'Feroceon 88FR131',
133
        0x581: 'PJ4/PJ4b',
134
        0x584: 'PJ4B-MP',
135
    },
136
    # Cavium (0x43)
137
    0x43: {
138
        0x0A0: 'ThunderX',
139
        0x0A1: 'ThunderX 88XX',
140
        0x0A2: 'ThunderX 81XX',
141
        0x0A3: 'ThunderX 83XX',
142
        0x0AF: 'ThunderX2 99xx',
143
        0x0B0: 'OcteonTX2',
144
        0x0B1: 'OcteonTX2 T98',
145
        0x0B2: 'OcteonTX2 T96',
146
        0x0B3: 'OcteonTX2 F95',
147
        0x0B4: 'OcteonTX2 F95N',
148
        0x0B5: 'OcteonTX2 F95MM',
149
    },
150
    # Broadcom (0x42)
151
    0x42: {
152
        0x00F: 'Brahma B15',
153
        0x100: 'Brahma B53',
154
        0x516: 'Vulcan',
155
    },
156
    # HiSilicon (0x48)
157
    0x48: {
158
        0xD01: 'Kunpeng-920',
159
        0xD40: 'Cortex-A76 (Kirin)',
160
    },
161
    # Ampere (0xc0)
162
    0xC0: {
163
        0xAC3: 'Ampere-1',
164
        0xAC4: 'Ampere-1a',
165
    },
166
    # Fujitsu (0x46)
167
    0x46: {
168
        0x001: 'A64FX',
169
    },
170
    # Intel (0x69) - ARM-based chips
171
    0x69: {
172
        0x200: 'i80200',
173
        0x210: 'PXA250A',
174
        0x212: 'PXA210A',
175
        0x242: 'i80321-400',
176
        0x243: 'i80321-600',
177
        0x290: 'PXA250B/PXA26x',
178
        0x292: 'PXA210B',
179
        0x2C2: 'i80321-400-B0',
180
        0x2C3: 'i80321-600-B0',
181
        0x2D0: 'PXA250C/PXA255/PXA26x',
182
        0x2D2: 'PXA210C',
183
        0x411: 'PXA27x',
184
        0x41C: 'IPX425-533',
185
        0x41D: 'IPX425-400',
186
        0x41F: 'IPX425-266',
187
        0x682: 'PXA32x',
188
        0x683: 'PXA930/PXA935',
189
        0x688: 'PXA30x',
190
        0x689: 'PXA31x',
191
    },
192
}
193
194
195
class CpuInfo(TypedDict):
196
    cpu_name: str
197
    cpu_hz: float | None
198
    cpu_hz_current: float | None
199
200
201
class PerCpuPercentInfo(TypedDict):
202
    key: str
203
    cpu_number: int
204
    total: float
205
    user: float
206
    system: float
207
    idle: float
208
    nice: float | None
209
    iowait: float | None
210
    irq: float | None
211
    softirq: float | None
212
    steal: float | None
213
    guest: float | None
214
    guest_nice: float | None
215
    dpc: float | None
216
    interrupt: float | None
217
218
219
class CpuPercent:
220
    """Get and store the CPU percent."""
221
222
    def __init__(self, cached_timer_cpu: int = 2):
223
        # cached_timer_cpu is the minimum time interval between stats updates
224
        # since last update is passed (will retrieve old cached info instead)
225
        self.cached_timer_cpu = cached_timer_cpu
226
        # psutil.cpu_freq() consumes lots of CPU
227
        # So refresh CPU frequency stats every refresh * 2
228
        self.cached_timer_cpu_info = cached_timer_cpu * 2
229
230
        # Get CPU name
231
        self.timer_cpu_info = Timer(0)
232
        self.cpu_info: CpuInfo = {'cpu_name': self.__get_cpu_name(), 'cpu_hz_current': None, 'cpu_hz': None}
233
234
        # Warning from PsUtil documentation
235
        # The first time this function is called with interval = 0.0 or None
236
        # it will return a meaningless 0.0 value which you are supposed to ignore.
237
        self.timer_cpu = Timer(0)
238
        self.cpu_percent = self._compute_cpu()
239
        self.timer_percpu = Timer(0)
240
        self.percpu_percent = self._compute_percpu()
241
242
    def get_key(self):
243
        """Return the key of the per CPU list."""
244
        return 'cpu_number'
245
246
    def get_info(self) -> CpuInfo:
247
        """Get additional information about the CPU"""
248
        # Never update more than 1 time per cached_timer_cpu_info
249
        if self.timer_cpu_info.finished() and hasattr(psutil, 'cpu_freq'):
250
            # Get the CPU freq current/max
251
            try:
252
                cpu_freq = psutil.cpu_freq()
253
            except Exception as e:
254
                logger.debug(f'Can not grab CPU information ({e})')
255
            else:
256
                if hasattr(cpu_freq, 'current'):
257
                    self.cpu_info['cpu_hz_current'] = cpu_freq.current
258
                else:
259
                    self.cpu_info['cpu_hz_current'] = None
260
                if hasattr(cpu_freq, 'max') and cpu_freq.max != 0.0:
261
                    self.cpu_info['cpu_hz'] = cpu_freq.max
262
                else:
263
                    self.cpu_info['cpu_hz'] = None
264
                # Reset timer for cache
265
                self.timer_cpu_info.reset(duration=self.cached_timer_cpu_info)
266
        return self.cpu_info
267
268
    @staticmethod
269
    def __get_cpu_name() -> str:
270
        # Get the CPU name once from the /proc/cpuinfo file
271
        # Read the first line with the "model name" ("Model" for Raspberry Pi)
272
        ret = f'CPU {platform.processor()}'
273
        try:
274
            cpuinfo_lines = open('/proc/cpuinfo').readlines()
275
        except (FileNotFoundError, PermissionError):
276
            logger.debug("No permission to read '/proc/cpuinfo'")
277
            return ret
278
279
        cpu_implementer = None
280
        for line in cpuinfo_lines:
281
            # Look for the CPU name
282
            if line.startswith('model name') or line.startswith('Model') or line.startswith('cpu model'):
283
                return line.split(':')[1].strip()
284
            # Look for the CPU name on ARM architecture (see #3127)
285
            if line.startswith('CPU implementer'):
286
                cpu_implementer = CPU_IMPLEMENTERS.get(int(line.split(':')[1].strip(), 16), ret)
287
                ret = cpu_implementer
288
            if line.startswith('CPU part') and cpu_implementer in CPU_PARTS:
289
                cpu_part = CPU_PARTS[cpu_implementer].get(int(line.split(':')[1].strip(), 16), 'Unknown')
290
                ret = f'{cpu_implementer} {cpu_part}'
291
292
        return ret
293
294
    def get_cpu(self) -> float:
295
        """Update and/or return the CPU using the psutil library."""
296
        # Never update more than 1 time per cached_timer_cpu
297
        if self.timer_cpu.finished():
298
            # Reset timer for cache
299
            self.timer_cpu.reset(duration=self.cached_timer_cpu)
300
            # Update the stats
301
            self.cpu_percent = self._compute_cpu()
302
        return self.cpu_percent
303
304
    @staticmethod
305
    def _compute_cpu() -> float:
306
        return psutil.cpu_percent(interval=0.0)
307
308
    def get_percpu(self) -> list[PerCpuPercentInfo]:
309
        """Update and/or return the per CPU list using the psutil library."""
310
        # Never update more than 1 time per cached_timer_cpu
311
        if self.timer_percpu.finished():
312
            # Reset timer for cache
313
            self.timer_percpu.reset(duration=self.cached_timer_cpu)
314
            # Update stats
315
            self.percpu_percent = self._compute_percpu()
316
        return self.percpu_percent
317
318
    def _compute_percpu(self) -> list[PerCpuPercentInfo]:
319
        psutil_percpu = enumerate(psutil.cpu_times_percent(interval=0.0, percpu=True))
320
        return [
321
            {
322
                'key': self.get_key(),
323
                'cpu_number': cpu_number,
324
                'total': round(100 - cpu_times.idle, 1),
325
                'user': cpu_times.user,
326
                'system': cpu_times.system,
327
                'idle': cpu_times.idle,
328
                'nice': cpu_times.nice if hasattr(cpu_times, 'nice') else None,
329
                'iowait': cpu_times.iowait if hasattr(cpu_times, 'iowait') else None,
330
                'irq': cpu_times.irq if hasattr(cpu_times, 'irq') else None,
331
                'softirq': cpu_times.softirq if hasattr(cpu_times, 'softirq') else None,
332
                'steal': cpu_times.steal if hasattr(cpu_times, 'steal') else None,
333
                'guest': cpu_times.guest if hasattr(cpu_times, 'guest') else None,
334
                'guest_nice': cpu_times.steal if hasattr(cpu_times, 'guest_nice') else None,
335
                'dpc': cpu_times.dpc if hasattr(cpu_times, 'dpc') else None,
336
                'interrupt': cpu_times.interrupt if hasattr(cpu_times, 'interrupt') else None,
337
            }
338
            for cpu_number, cpu_times in psutil_percpu
339
        ]
340
341
342
# CpuPercent instance shared between plugins
343
cpu_percent = CpuPercent()
344