Update URLs for redirects and other location changes
[iotop.git] / iotop / ioprio.py
blobc76e665c10878f1612f378655ae06a3088a2d50c
1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; either version 2 of the License, or
4 # (at your option) any later version.
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # GNU Library General Public License for more details.
11 # You should have received a copy of the GNU General Public License
12 # along with this program; if not, write to the Free Software
13 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
15 # See the COPYING file for license information.
17 # Copyright (c) 2007 Guillaume Chazarain <guichaz@gmail.com>
19 import ctypes
20 import fnmatch
21 import os
22 import platform
24 # From https://git.kernel.org/pub/scm/utils/util-linux/util-linux.git/tree/configure.ac#n2289
25 # i386 bit userspace under an x86_64 kernel will have its uname() appear as
26 # 'x86_64' but it will use the i386 syscall number, that's why we consider both
27 # the architecture name and the word size.
28 IOPRIO_GET_ARCH_SYSCALL = [
29 ('alpha', '*', 443),
30 ('arm*', '*', 315),
31 ('i*86', '*', 290),
32 ('ia64*', '*', 1275),
33 ('mips*', '32bit', 4315),
34 ('mips*', '64bit', 5274),
35 ('parisc*', '*', 268),
36 ('powerpc*', '*', 274),
37 ('s390*', '*', 283),
38 ('sparc*', '*', 218),
39 ('sh*', '*', 289),
40 ('x86_64*', '32bit', 290),
41 ('x86_64*', '64bit', 252),
44 IOPRIO_SET_ARCH_SYSCALL = [
45 ('alpha', '*', 442),
46 ('arm*', '*', 314),
47 ('i*86', '*', 289),
48 ('ia64*', '*', 1274),
49 ('mips*', '32bit', 4314),
50 ('mips*', '64bit', 5273),
51 ('parisc*', '*', 267),
52 ('powerpc*', '*', 273),
53 ('s390*', '*', 282),
54 ('sparc*', '*', 196),
55 ('sh*', '*', 288),
56 ('x86_64*', '32bit', 289),
57 ('x86_64*', '64bit', 251),
61 def find_ioprio_syscall_number(syscall_list):
62 arch = os.uname()[4]
63 bits = platform.architecture()[0]
65 for candidate_arch, candidate_bits, syscall_nr in syscall_list:
66 if fnmatch.fnmatch(arch, candidate_arch) and \
67 fnmatch.fnmatch(bits, candidate_bits):
68 return syscall_nr
71 class IoprioSetError(Exception):
72 def __init__(self, err):
73 try:
74 self.err = os.strerror(err)
75 except TypeError:
76 self.err = err
78 try:
79 from iotop import _ioprio
80 __NR_ioprio_get = _ioprio.SYS_ioprio_get
81 __NR_ioprio_set = _ioprio.SYS_ioprio_set
82 except (ImportError, AttributeError):
83 __NR_ioprio_get = find_ioprio_syscall_number(IOPRIO_GET_ARCH_SYSCALL)
84 __NR_ioprio_set = find_ioprio_syscall_number(IOPRIO_SET_ARCH_SYSCALL)
86 try:
87 ctypes_handle = ctypes.CDLL(None, use_errno=True)
88 except TypeError:
89 ctypes_handle = ctypes.CDLL(None)
91 syscall = ctypes_handle.syscall
93 PRIORITY_CLASSES = [None, 'rt', 'be', 'idle']
95 IOPRIO_WHO_PROCESS = 1
96 IOPRIO_CLASS_SHIFT = 13
97 IOPRIO_PRIO_MASK = (1 << IOPRIO_CLASS_SHIFT) - 1
100 def ioprio_value(ioprio_class, ioprio_data):
101 try:
102 ioprio_class = PRIORITY_CLASSES.index(ioprio_class)
103 except ValueError:
104 ioprio_class = PRIORITY_CLASSES.index(None)
105 return (ioprio_class << IOPRIO_CLASS_SHIFT) | ioprio_data
108 def ioprio_class(ioprio):
109 return PRIORITY_CLASSES[ioprio >> IOPRIO_CLASS_SHIFT]
112 def ioprio_data(ioprio):
113 return ioprio & IOPRIO_PRIO_MASK
115 sched_getscheduler = ctypes_handle.sched_getscheduler
116 SCHED_OTHER, SCHED_FIFO, SCHED_RR, SCHED_BATCH, SCHED_ISO, SCHED_IDLE = \
117 range(6)
119 getpriority = ctypes_handle.getpriority
120 PRIO_PROCESS = 0
123 def get_ioprio_from_sched(pid):
124 scheduler = sched_getscheduler(pid)
125 nice = getpriority(PRIO_PROCESS, pid)
126 ioprio_nice = (nice + 20) / 5
128 if scheduler in (SCHED_FIFO, SCHED_RR):
129 return 'rt/%d' % ioprio_nice
130 elif scheduler == SCHED_IDLE:
131 return 'idle'
132 else:
133 return 'be/%d' % ioprio_nice
136 def get(pid):
137 if __NR_ioprio_get is None:
138 return '?sys'
140 ioprio = syscall(__NR_ioprio_get, IOPRIO_WHO_PROCESS, pid)
141 if ioprio < 0:
142 return '?err'
144 prio_class = ioprio_class(ioprio)
145 if not prio_class:
146 return get_ioprio_from_sched(pid)
147 if prio_class == 'idle':
148 return prio_class
149 return '%s/%d' % (prio_class, ioprio_data(ioprio))
152 def set_ioprio(which, who, ioprio_class, ioprio_data):
153 if __NR_ioprio_set is None:
154 raise IoprioSetError('No ioprio_set syscall found')
156 ioprio_val = ioprio_value(ioprio_class, ioprio_data)
157 ret = syscall(__NR_ioprio_set, which, who, ioprio_val, use_errno=True)
158 if ret < 0:
159 try:
160 err = ctypes.get_errno()
161 except AttributeError:
162 err = \
163 'Unknown error (errno support not available before Python2.6)'
164 raise IoprioSetError(err)
167 def sort_key(key):
168 if key[0] == '?':
169 return -ord(key[1])
171 if '/' in key:
172 if key.startswith('rt/'):
173 shift = 0
174 elif key.startswith('be/'):
175 shift = 1
176 prio = int(key.split('/')[1])
177 elif key == 'idle':
178 shift = 2
179 prio = 0
181 return (1 << (shift * IOPRIO_CLASS_SHIFT)) + prio
184 def to_class_and_data(ioprio_str):
185 if '/' in ioprio_str:
186 split = ioprio_str.split('/')
187 return (split[0], int(split[1]))
188 elif ioprio_str == 'idle':
189 return ('idle', 0)
190 return (None, None)
192 if __name__ == '__main__':
193 import sys
194 if len(sys.argv) == 2:
195 pid = int(sys.argv[1])
196 else:
197 pid = os.getpid()
198 print('pid:', pid)
199 print('ioprio:', get(pid))