Skip to content

Commit

Permalink
Linux / CPU freq, fixes #1481
Browse files Browse the repository at this point in the history
  • Loading branch information
amanusk authored and giampaolo committed Apr 5, 2019
1 parent 36b5ab0 commit 05d5164
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 49 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ syntax: glob
*.rej
*.so
*.swp
.failed-tests.txt
.cache/
.idea/
.tox/
Expand Down
26 changes: 11 additions & 15 deletions psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
81 changes: 47 additions & 34 deletions psutil/tests/test_linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import collections
import contextlib
import errno
import glob
import io
import os
import re
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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")
Expand All @@ -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.
Expand All @@ -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)


Expand Down

0 comments on commit 05d5164

Please sign in to comment.