Bump version
[iotop.git] / iotop / ioprio.py
blob99b8ecdc0f633f3f9e0964a8db04c709720f728e
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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
23 import time
25 # From http://git.kernel.org/?p=utils/util-linux-ng/util-linux-ng.git;a=blob;
26 # f=configure.ac;h=770eb45ae85d32757fc3cff1d70a7808a627f9f7;hb=HEAD#l354
27 # i386 bit userspace under an x86_64 kernel will have its uname() appear as
28 # 'x86_64' but it will use the i386 syscall number, that's why we consider both
29 # the architecture name and the word size.
30 IOPRIO_GET_ARCH_SYSCALL = [
31 ('alpha', '*', 443),
32 ('i*86', '*', 290),
33 ('ia64*', '*', 1275),
34 ('powerpc*', '*', 274),
35 ('s390*', '*', 283),
36 ('sparc*', '*', 218),
37 ('sh*', '*', 289),
38 ('x86_64*', '32bit', 290),
39 ('x86_64*', '64bit', 252),
42 IOPRIO_SET_ARCH_SYSCALL = [
43 ('alpha', '*', 442),
44 ('i*86', '*', 289),
45 ('ia64*', '*', 1274),
46 ('powerpc*', '*', 273),
47 ('s390*', '*', 282),
48 ('sparc*', '*', 196),
49 ('sh*', '*', 288),
50 ('x86_64*', '32bit', 289),
51 ('x86_64*', '64bit', 251),
54 def find_ioprio_syscall_number(syscall_list):
55 arch = os.uname()[4]
56 bits = platform.architecture()[0]
58 for candidate_arch, candidate_bits, syscall_nr in syscall_list:
59 if fnmatch.fnmatch(arch, candidate_arch) and \
60 fnmatch.fnmatch(bits, candidate_bits):
61 return syscall_nr
63 class IoprioSetError(Exception):
64 def __init__(self, err):
65 try:
66 self.err = os.strerror(err)
67 except TypeError:
68 self.err = err
70 __NR_ioprio_get = find_ioprio_syscall_number(IOPRIO_GET_ARCH_SYSCALL)
71 __NR_ioprio_set = find_ioprio_syscall_number(IOPRIO_SET_ARCH_SYSCALL)
73 try:
74 ctypes_handle = ctypes.CDLL(None, use_errno=True)
75 except TypeError:
76 ctypes_handle = ctypes.CDLL(None)
78 syscall = ctypes_handle.syscall
80 PRIORITY_CLASSES = [None, 'rt', 'be', 'idle']
82 IOPRIO_WHO_PROCESS = 1
83 IOPRIO_CLASS_SHIFT = 13
84 IOPRIO_PRIO_MASK = (1 << IOPRIO_CLASS_SHIFT) - 1
86 def ioprio_value(ioprio_class, ioprio_data):
87 try:
88 ioprio_class = PRIORITY_CLASSES.index(ioprio_class)
89 except ValueError:
90 ioprio_class = PRIORITY_CLASSES.index(None)
91 return (ioprio_class << IOPRIO_CLASS_SHIFT) | ioprio_data
93 def ioprio_class(ioprio):
94 return PRIORITY_CLASSES[ioprio >> IOPRIO_CLASS_SHIFT]
96 def ioprio_data(ioprio):
97 return ioprio & IOPRIO_PRIO_MASK
99 sched_getscheduler = ctypes_handle.sched_getscheduler
100 SCHED_OTHER, SCHED_FIFO, SCHED_RR, SCHED_BATCH, SCHED_ISO, SCHED_IDLE = range(6)
102 getpriority = ctypes_handle.getpriority
103 PRIO_PROCESS = 0
105 def get_ioprio_from_sched(pid):
106 scheduler = sched_getscheduler(pid)
107 nice = getpriority(PRIO_PROCESS, pid)
108 ioprio_nice = (nice + 20) / 5
110 if scheduler in (SCHED_FIFO, SCHED_RR):
111 return 'rt/%d' % ioprio_nice
112 elif scheduler == SCHED_IDLE:
113 return 'idle'
114 else:
115 return 'be/%d' % ioprio_nice
117 def get(pid):
118 if __NR_ioprio_get is None:
119 return '?sys'
121 ioprio = syscall(__NR_ioprio_get, IOPRIO_WHO_PROCESS, pid)
122 if ioprio < 0:
123 return '?err'
125 prio_class = ioprio_class(ioprio)
126 if not prio_class:
127 return get_ioprio_from_sched(pid)
128 if prio_class == 'idle':
129 return prio_class
130 return '%s/%d' % (prio_class, ioprio_data(ioprio))
132 def set_ioprio(which, who, ioprio_class, ioprio_data):
133 if __NR_ioprio_set is None:
134 raise IoprioSetError('No ioprio_set syscall found')
136 ioprio_val = ioprio_value(ioprio_class, ioprio_data)
137 ret = syscall(__NR_ioprio_set, which, who, ioprio_val, use_errno=True)
138 if ret < 0:
139 try:
140 err = ctypes.get_errno()
141 except AttributeError:
142 err = 'Unknown error (errno support not available before Python2.6)'
143 raise IoprioSetError(err)
145 def sort_key(key):
146 if key[0] == '?':
147 return -ord(key[1])
149 if '/' in key:
150 if key.startswith('rt/'):
151 shift = 0
152 elif key.startswith('be/'):
153 shift = 1
154 prio = int(key.split('/')[1])
155 elif key == 'idle':
156 shift = 2
157 prio = 0
159 return (1 << (shift * IOPRIO_CLASS_SHIFT)) + prio
161 def to_class_and_data(ioprio_str):
162 if '/' in ioprio_str:
163 split = ioprio_str.split('/')
164 return (split[0], int(split[1]))
165 elif ioprio_str == 'idle':
166 return ('idle', 0)
167 return (None, None)
169 if __name__ == '__main__':
170 import sys
171 if len(sys.argv) == 2:
172 pid = int(sys.argv[1])
173 else:
174 pid = os.getpid()
175 print 'pid:', pid
176 print 'ioprio:', get(pid)