From 05d51649ca709c6626d84cc710c2470d64829848 Mon Sep 17 00:00:00 2001 From: Alex Manuskin Date: Fri, 5 Apr 2019 21:32:37 +0300 Subject: [PATCH] Linux / CPU freq, fixes #1481 --- .gitignore | 1 + psutil/_pslinux.py | 26 ++++++------ psutil/tests/test_linux.py | 81 ++++++++++++++++++++++---------------- 3 files changed, 59 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index 99d0d5457..3d22b0b35 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ syntax: glob *.rej *.so *.swp +.failed-tests.txt .cache/ .idea/ .tox/ diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index bf815e87a..1f8ddb93c 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -688,23 +688,19 @@ def cpu_freq(): Contrarily to other OSes, Linux updates these values in real-time. """ - # scaling_* files seem preferable to cpuinfo_*, see: - # http://unix.stackexchange.com/a/87537/168884 + def get_path(num): + for p in ("/sys/devices/system/cpu/cpufreq/policy%s" % num, + "/sys/devices/system/cpu/cpu%s/cpufreq" % num): + if os.path.exists(p): + return p + ret = [] - ls = glob.glob("/sys/devices/system/cpu/cpufreq/policy*") - if ls: - # Sort the list so that '10' comes after '2'. This should - # ensure the CPU order is consistent with other CPU functions - # having a 'percpu' argument and returning results for multiple - # CPUs (cpu_times(), cpu_percent(), cpu_times_percent()). - ls.sort(key=lambda x: int(os.path.basename(x)[6:])) - else: - # https://github.com/giampaolo/psutil/issues/981 - ls = glob.glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq") - ls.sort(key=lambda x: int(re.search('[0-9]+', x).group(0))) + for n in range(cpu_count_logical()): + path = get_path(n) + if not path: + continue - pjoin = os.path.join - for path in ls: + pjoin = os.path.join curr = cat(pjoin(path, "scaling_cur_freq"), fallback=None) if curr is None: # Likely an old RedHat, see: diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 66c50aa68..85cf33c44 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -10,7 +10,6 @@ import collections import contextlib import errno -import glob import io import os import re @@ -698,27 +697,26 @@ class TestSystemCPUFrequency(unittest.TestCase): @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_emulate_no_files(self): - with mock.patch("psutil._pslinux.glob.glob", return_value=[]): + with mock.patch("os.path.exists", return_value=False): self.assertIsNone(psutil.cpu_freq()) @unittest.skipIf(TRAVIS, "fails on Travis") @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_emulate_use_second_file(self): # https://github.com/giampaolo/psutil/issues/981 - def glob_mock(pattern): - if pattern.startswith("/sys/devices/system/cpu/cpufreq/policy"): - flags.append(None) - return [] + def path_exists_mock(path): + if path.startswith("/sys/devices/system/cpu/cpufreq/policy"): + return False else: flags.append(None) - return orig_glob(pattern) + return orig_exists(path) flags = [] - orig_glob = glob.glob - with mock.patch("psutil._pslinux.glob.glob", side_effect=glob_mock, + orig_exists = os.path.exists + with mock.patch("os.path.exists", side_effect=path_exists_mock, create=True): assert psutil.cpu_freq() - self.assertEqual(len(flags), 2) + self.assertEqual(len(flags), psutil.cpu_count(logical=True)) @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_emulate_use_cpuinfo(self): @@ -752,11 +750,14 @@ def path_exists_mock(path): @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_emulate_data(self): def open_mock(name, *args, **kwargs): - if name.endswith('/scaling_cur_freq'): + if (name.endswith('/scaling_cur_freq') and + name.startswith("/sys/devices/system/cpu/cpufreq/policy")): return io.BytesIO(b"500000") - elif name.endswith('/scaling_min_freq'): + elif (name.endswith('/scaling_min_freq') and + name.startswith("/sys/devices/system/cpu/cpufreq/policy")): return io.BytesIO(b"600000") - elif name.endswith('/scaling_max_freq'): + elif (name.endswith('/scaling_max_freq') and + name.startswith("/sys/devices/system/cpu/cpufreq/policy")): return io.BytesIO(b"700000") else: return orig_open(name, *args, **kwargs) @@ -765,8 +766,7 @@ def open_mock(name, *args, **kwargs): patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): with mock.patch( - 'glob.glob', - return_value=['/sys/devices/system/cpu/cpufreq/policy0']): + 'os.path.exists', return_value=True): freq = psutil.cpu_freq() self.assertEqual(freq.current, 500.0) self.assertEqual(freq.min, 600.0) @@ -775,26 +775,41 @@ def open_mock(name, *args, **kwargs): @unittest.skipIf(not HAS_CPU_FREQ, "not supported") def test_emulate_multi_cpu(self): def open_mock(name, *args, **kwargs): - if name.endswith('/scaling_cur_freq'): + n = name + if (n.endswith('/scaling_cur_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy0")): return io.BytesIO(b"100000") - elif name.endswith('/scaling_min_freq'): + elif (n.endswith('/scaling_min_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy0")): return io.BytesIO(b"200000") - elif name.endswith('/scaling_max_freq'): + elif (n.endswith('/scaling_max_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy0")): return io.BytesIO(b"300000") + elif (n.endswith('/scaling_cur_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy1")): + return io.BytesIO(b"400000") + elif (n.endswith('/scaling_min_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy1")): + return io.BytesIO(b"500000") + elif (n.endswith('/scaling_max_freq') and + n.startswith("/sys/devices/system/cpu/cpufreq/policy1")): + return io.BytesIO(b"600000") else: return orig_open(name, *args, **kwargs) orig_open = open patch_point = 'builtins.open' if PY3 else '__builtin__.open' - policies = ['/sys/devices/system/cpu/cpufreq/policy0', - '/sys/devices/system/cpu/cpufreq/policy1', - '/sys/devices/system/cpu/cpufreq/policy2'] with mock.patch(patch_point, side_effect=open_mock): - with mock.patch('glob.glob', return_value=policies): - freq = psutil.cpu_freq() - self.assertEqual(freq.current, 100.0) - self.assertEqual(freq.min, 200.0) - self.assertEqual(freq.max, 300.0) + with mock.patch('os.path.exists', return_value=True): + with mock.patch('psutil._pslinux.cpu_count_logical', + return_value=2): + freq = psutil.cpu_freq(percpu=True) + self.assertEqual(freq[0].current, 100.0) + self.assertEqual(freq[0].min, 200.0) + self.assertEqual(freq[0].max, 300.0) + self.assertEqual(freq[1].current, 400.0) + self.assertEqual(freq[1].min, 500.0) + self.assertEqual(freq[1].max, 600.0) @unittest.skipIf(TRAVIS, "fails on Travis") @unittest.skipIf(not HAS_CPU_FREQ, "not supported") @@ -810,14 +825,12 @@ def open_mock(name, *args, **kwargs): orig_open = open patch_point = 'builtins.open' if PY3 else '__builtin__.open' - policies = ['/sys/devices/system/cpu/cpufreq/policy0', - '/sys/devices/system/cpu/cpufreq/policy1', - '/sys/devices/system/cpu/cpufreq/policy2'] - with mock.patch(patch_point, side_effect=open_mock): - with mock.patch('glob.glob', return_value=policies): - freq = psutil.cpu_freq() - self.assertEqual(freq.current, 200) + with mock.patch('os.path.exists', return_value=True): + with mock.patch('psutil._pslinux.cpu_count_logical', + return_value=1): + freq = psutil.cpu_freq() + self.assertEqual(freq.current, 200) # Also test that NotImplementedError is raised in case no # current freq file is present. @@ -833,7 +846,7 @@ def open_mock(name, *args, **kwargs): orig_open = open patch_point = 'builtins.open' if PY3 else '__builtin__.open' with mock.patch(patch_point, side_effect=open_mock): - with mock.patch('glob.glob', return_value=policies): + with mock.patch('os.path.exists', return_value=True): self.assertRaises(NotImplementedError, psutil.cpu_freq)