blob: 51f8a1010d321000bff0c2b9d8544c1db66e0741 [file] [log] [blame]
#! /usr/bin/python
# -*- python -*-
# -*- coding: utf-8 -*-
import pygtk
pygtk.require("2.0")
import copy, ethtool, gtk, gobject, os, pango, procfs, re, schedutils, sys, tuna
import gtk.glade
try:
from sets import Set as set
except:
# OK, we're modern, having sets as first class citizens
pass
# FIXME: should go to python-schedutils
( SCHED_OTHER, SCHED_FIFO, SCHED_RR, SCHED_BATCH ) = range(4)
DND_TARGET_STRING = 0
DND_TARGET_ROOTWIN = 1
DND_TARGETS = [ ('STRING', 0, DND_TARGET_STRING),
('text/plain', 0, DND_TARGET_STRING),
('application/x-rootwin-drop', 0, DND_TARGET_ROOTWIN) ]
tuna_glade = "/usr/share/tuna/tuna_gui.glade"
#tuna_glade = "tuna/tuna_gui.glade"
def set_affinity_warning(tid, affinity):
dialog = gtk.MessageDialog(None,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_WARNING,
gtk.BUTTONS_OK,
"Couldn't change the affinity of %d to %s!" % \
(tid, affinity))
dialog.run()
dialog.destroy()
def drop_handler_move_threads_to_cpu(cpu, data):
pid_list = [ int(pid) for pid in data.split(",") ]
if cpu >= 0:
new_affinity = [ cpu, ]
else:
new_affinity = range(-cpu)
tuna.move_threads_to_cpu(new_affinity, pid_list)
def drop_handler_move_irqs_to_cpu(cpu, data):
irq_list = [ int(irq) for irq in data.split(",") ]
if cpu >= 0:
new_affinity = [ 1 << cpu, ]
else:
cpu = -cpu
new_affinity = [ (1 << cpu) - 1, ]
for irq in irq_list:
tuna.set_irq_affinity(irq, new_affinity)
# FIXME: check if we really changed the affinity, but
# its only an optimization to avoid a needless refresh
# in the irqview, now we always refresh.
return True
def set_store_columns(store, row, new_value):
nr_columns = len(new_value)
for col in range(nr_columns):
col_weight = col + nr_columns
cur_value = store.get_value(row, col)
if cur_value == new_value[col]:
new_weight = pango.WEIGHT_NORMAL
else:
new_weight = pango.WEIGHT_BOLD
store.set(row, col, new_value[col], col_weight, new_weight)
class list_store_column:
def __init__(self, name, type = gobject.TYPE_UINT):
self.name = name
self.type = type
def generate_list_store_columns_with_attr(columns):
for column in columns:
yield column.type
for column in columns:
yield gobject.TYPE_UINT
class cpuview:
( COL_FILTER, COL_CPU, COL_USAGE ) = range(3)
def __init__(self, treeview, procview, irqview, cpus_filtered):
self.cpustats = procfs.cpusstats()
self.procview = procview
self.irqview = irqview
self.treeview = treeview
self.list_store = gtk.ListStore(gobject.TYPE_BOOLEAN,
gobject.TYPE_UINT,
gobject.TYPE_UINT)
self.treeview.set_model(self.list_store)
self.nr_cpus = len(self.cpustats) - 1
model = self.treeview.get_model()
# Filter column
renderer = gtk.CellRendererToggle()
renderer.connect('toggled', self.filter_toggled, model)
column = gtk.TreeViewColumn('Filter', renderer, active = self.COL_FILTER)
self.treeview.append_column(column)
# CPU# column
column = gtk.TreeViewColumn('CPU', gtk.CellRendererText(),
text = self.COL_CPU)
self.treeview.append_column(column)
# CPU usage column
try:
column = gtk.TreeViewColumn('Usage', gtk.CellRendererProgress(),
text = self.COL_USAGE, value = self.COL_USAGE)
except:
# CellRendererProgress needs pygtk2 >= 2.6
column = gtk.TreeViewColumn('Usage', gtk.CellRendererText(),
text = self.COL_USAGE)
self.treeview.append_column(column)
self.treeview.enable_model_drag_dest(DND_TARGETS,
gtk.gdk.ACTION_DEFAULT)
self.treeview.connect("drag_data_received",
self.on_drag_data_received_data)
self.timer = gobject.timeout_add(3000, self.refresh)
self.drop_handlers = { "pid": (drop_handler_move_threads_to_cpu, self.procview),
"irq": (drop_handler_move_irqs_to_cpu, self.irqview), }
self.previous_pid_affinities = None
self.previous_irq_affinities = None
self.cpus_filtered = cpus_filtered
def isolate_cpu(self, a):
ret = self.treeview.get_path_at_pos(self.last_x, self.last_y)
if not ret:
return
path, col, xpos, ypos = ret
if not path:
return
row = self.list_store.get_iter(path)
cpu = self.list_store.get_value(row, self.COL_CPU)
self.previous_pid_affinities, \
self.previous_irq_affinities = tuna.isolate_cpus([cpu,], self.nr_cpus)
if self.previous_pid_affinities:
self.procview.refresh()
if self.previous_irq_affinities:
self.irqview.refresh()
def include_cpu(self, a):
ret = self.treeview.get_path_at_pos(self.last_x, self.last_y)
if not ret:
return
path, col, xpos, ypos = ret
if not path:
return
row = self.list_store.get_iter(path)
cpu = self.list_store.get_value(row, self.COL_CPU)
self.previous_pid_affinities, \
self.previous_irq_affinities = tuna.include_cpu(cpu, self.nr_cpus)
if self.previous_pid_affinities:
self.procview.refresh()
if self.previous_irq_affinities:
self.irqview.refresh()
def restore_cpu(self, a):
if not (self.previous_pid_affinities or \
self.previous_irq_affinities):
return
affinities = self.previous_pid_affinities
for pid in affinities.keys():
try:
schedutils.set_affinity(pid, affinities[pid])
except:
pass
affinities = self.previous_irq_affinities
for irq in affinities.keys():
tuna.set_irq_affinity(int(irq),
procfs.hexbitmask(affinities[irq],
self.nr_cpus))
self.previous_pid_affinities = None
self.previous_irq_affinities = None
def on_cpuview_button_press_event(self, treeview, event):
if event.type != gtk.gdk.BUTTON_PRESS or event.button != 3:
return
self.last_x = int(event.x)
self.last_y = int(event.y)
menu = gtk.Menu()
include = gtk.MenuItem("I_nclude CPU")
isolate = gtk.MenuItem("_Isolate CPU")
restore = gtk.MenuItem("_Restore CPU")
menu.add(include)
menu.add(isolate)
menu.add(restore)
include.connect_object('activate', self.include_cpu, event)
isolate.connect_object('activate', self.isolate_cpu, event)
if not (self.previous_pid_affinities or \
self.previous_irq_affinities):
restore.set_sensitive(False)
restore.connect_object('activate', self.restore_cpu, event)
include.show()
isolate.show()
restore.show()
menu.popup(None, None, None, event.button, event.time)
def on_drag_data_received_data(self, treeview, context, x, y,
selection, info, etime):
drop_info = treeview.get_dest_row_at_pos(x, y)
# pid list, a irq list, etc
source, data = selection.data.split(":")
if drop_info:
model = treeview.get_model()
path, position = drop_info
iter = model.get_iter(path)
cpu = model.get_value(iter, self.COL_CPU)
else:
# Move to all CPUs
cpu = -self.nr_cpus
if self.drop_handlers.has_key(source):
if self.drop_handlers[source][0](cpu, data):
self.drop_handlers[source][1].refresh()
else:
print "cpuview: unhandled drag source '%s'" % source
def filter_toggled(self, cell, path, model):
# get toggled iter
iter = model.get_iter((int(path),))
enabled = model.get_value(iter, self.COL_FILTER)
cpu = model.get_value(iter, self.COL_CPU)
enabled = not enabled
if enabled:
if cpu in self.cpus_filtered:
self.cpus_filtered.remove(cpu)
else:
if cpu not in self.cpus_filtered:
self.cpus_filtered.append(cpu)
self.procview.toggle_mask_cpu(cpu, enabled)
self.irqview.toggle_mask_cpu(cpu, enabled)
# set new value
model.set(iter, self.COL_FILTER, enabled)
def refresh(self):
self.list_store.clear()
self.cpustats.reload()
for cpunr in range(self.nr_cpus):
cpu = self.list_store.append()
usage = self.cpustats[cpunr + 1].usage
self.list_store.set(cpu, self.COL_FILTER, cpunr not in self.cpus_filtered,
self.COL_CPU, cpunr,
self.COL_USAGE, int(usage))
self.treeview.show_all()
return True
def on_affinity_text_changed(self):
new_affinity_text = self.affinity.get_text().strip()
if self.affinity_text != new_affinity_text:
try:
for cpu in new_affinity_text.strip(",").split(","):
new_affinity_cpu_entry = int(cpu, 16)
except:
try:
new_affinity = tuna.cpustring_to_list(new_affinity_text)
except:
if len(new_affinity_text) > 0 and new_affinity_text[-1] != "-":
# print "not a hex number"
self.affinity.set_text(self.affinity_text)
return
self.affinity_text = new_affinity_text
def invalid_affinity():
dialog = gtk.MessageDialog(None,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_WARNING,
gtk.BUTTONS_OK,
"Invalid affinity, specify a list of CPUs!")
dialog.run()
dialog.destroy()
return False
def thread_set_attributes(pid, threads, new_policy, new_prio, new_affinity, nr_cpus):
changed = False
curr_policy = schedutils.get_scheduler(pid)
curr_prio = int(threads[pid]["stat"]["rt_priority"])
if curr_policy != new_policy or curr_prio != new_prio:
try:
schedutils.set_scheduler(pid, new_policy, new_prio)
except:
dialog = gtk.MessageDialog(None,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_WARNING,
gtk.BUTTONS_OK,
"Invalid parameters!")
dialog.run()
dialog.destroy()
return False
curr_policy = schedutils.get_scheduler(pid)
if curr_policy != new_policy:
print "couldn't change pid %d from %s(%d) to %s(%d)!" % \
( pid, schedutils.schedstr(curr_policy),
curr_prio,
schedutils.schedstr(new_policy),
new_prio)
else:
changed = True
curr_affinity = schedutils.get_affinity(pid)
try:
new_affinity = [ int(a) for a in new_affinity.split(",") ]
except:
try:
new_affinity = tuna.cpustring_to_list(new_affinity)
except:
new_affinity = procfs.bitmasklist(new_affinity, nr_cpus)
new_affinity.sort()
if curr_affinity != new_affinity:
try:
schedutils.set_affinity(pid, new_affinity)
except:
return invalid_affinity()
curr_affinity = schedutils.get_affinity(pid)
if curr_affinity != new_affinity:
print "couldn't change pid %d from %s to %s!" % \
( pid, curr_affinity, new_affinity )
else:
changed = True
return changed
class irq_druid:
def __init__(self, irqs, ps, irq):
self.irqs = irqs
self.ps = ps
self.irq = irq
self.window = gtk.glade.XML(tuna_glade, "set_irq_attributes")
self.dialog = self.window.get_widget("set_irq_attributes")
pixbuf = self.dialog.render_icon(gtk.STOCK_PREFERENCES,
gtk.ICON_SIZE_SMALL_TOOLBAR)
self.dialog.set_icon(pixbuf)
event_handlers = { "on_irq_affinity_text_changed" : self.on_irq_affinity_text_changed,
"on_sched_policy_combo_changed": self.on_sched_policy_combo_changed }
self.window.signal_autoconnect(event_handlers)
self.sched_pri = self.window.get_widget("irq_pri_spinbutton")
self.sched_policy = self.window.get_widget("irq_policy_combobox")
self.affinity = self.window.get_widget("irq_affinity_text")
text = self.window.get_widget("irq_text")
users = tuna.get_irq_users(irqs, irq)
self.affinity_text = tuna.get_irq_affinity_text(irqs, irq)
pids = ps.find_by_name("IRQ-%d" % irq)
if pids:
pid = pids[0]
prio = int(ps[pid]["stat"]["rt_priority"])
self.create_policy_model(self.sched_policy)
self.sched_policy.set_active(schedutils.get_scheduler(pid))
text.set_markup("IRQ <b>%u</b> (PID <b>%u</b>), pri <b>%u</b>, aff <b>%s</b>, <tt><b>%s</b></tt>" % \
( irq, pid, prio, self.affinity_text,
",".join(users)))
else:
self.sched_pri.set_sensitive(False)
self.sched_policy.set_sensitive(False)
text.set_markup("IRQ <b>%u</b>, aff <b>%s</b>, <tt><b>%s</b></tt>" % \
( irq, self.affinity_text,
",".join(users)))
self.affinity.set_text(self.affinity_text)
def create_policy_model(self, policy):
( COL_TEXT, COL_SCHED ) = range(2)
list_store = gtk.ListStore(gobject.TYPE_STRING,
gobject.TYPE_UINT)
policy.set_model(list_store)
renderer = gtk.CellRendererText()
policy.pack_start(renderer, True)
policy.add_attribute(renderer, "text", COL_TEXT)
for pol in range(4):
row = list_store.append()
list_store.set(row, COL_TEXT, schedutils.schedstr(pol),
COL_SCHED, pol)
def on_sched_policy_combo_changed(self, button):
new_policy = self.sched_policy.get_active()
if new_policy in ( SCHED_FIFO, SCHED_RR ):
can_change_pri = True
else:
can_change_pri = False
self.sched_pri.set_sensitive(can_change_pri)
def on_irq_affinity_text_changed(self, button):
on_affinity_text_changed(self)
def run(self):
changed = False
if self.dialog.run() == gtk.RESPONSE_OK:
new_policy = self.sched_policy.get_active()
new_prio = self.sched_pri.get_value()
new_affinity = self.affinity.get_text()
pids = self.ps.find_by_name("IRQ-%d" % self.irq)
if pids:
if thread_set_attributes(pids[0], self.ps,
new_policy,
new_prio,
new_affinity,
self.irqs.nr_cpus):
changed = True
try:
new_affinity = [ int(a) for a in new_affinity.split(",") ]
except:
try:
new_affinity = tuna.cpustring_to_list(new_affinity)
except:
new_affinity = procfs.bitmasklist(new_affinity,
self.irqs.nr_cpus)
new_affinity.sort()
curr_affinity = self.irqs[self.irq]["affinity"]
if curr_affinity != new_affinity:
tuna.set_irq_affinity(self.irq,
procfs.hexbitmask(new_affinity,
self.irqs.nr_cpus))
changed = True
self.dialog.destroy()
return changed
class irqview:
nr_columns = 7
( COL_NUM, COL_PID, COL_POL, COL_PRI,
COL_AFF, COL_EVENTS, COL_USERS ) = range(nr_columns)
columns = (list_store_column("IRQ"),
list_store_column("PID", gobject.TYPE_INT),
list_store_column("Policy", gobject.TYPE_STRING),
list_store_column("Priority", gobject.TYPE_INT),
list_store_column("Affinity", gobject.TYPE_STRING),
list_store_column("Events"),
list_store_column("Users", gobject.TYPE_STRING))
def __init__(self, treeview, irqs, ps, cpus_filtered):
self.is_root = os.getuid() == 0
self.irqs = irqs
self.ps = ps
self.treeview = treeview
self.has_threaded_irqs = tuna.has_threaded_irqs(irqs, ps)
if not self.has_threaded_irqs:
self.nr_columns = 4
( self.COL_NUM,
self.COL_AFF,
self.COL_EVENTS,
self.COL_USERS ) = range(self.nr_columns)
self.columns = (list_store_column("IRQ"),
list_store_column("Affinity", gobject.TYPE_STRING),
list_store_column("Events"),
list_store_column("Users", gobject.TYPE_STRING))
self.list_store = gtk.ListStore(*generate_list_store_columns_with_attr(self.columns))
self.treeview.set_model(self.list_store)
# Allow selecting multiple rows
selection = treeview.get_selection()
selection.set_mode(gtk.SELECTION_MULTIPLE)
# Allow enable drag and drop of rows
self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
DND_TARGETS,
gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE)
self.treeview.connect("drag_data_get", self.on_drag_data_get_data)
self.renderer = gtk.CellRendererText()
for col in range(self.nr_columns):
column = gtk.TreeViewColumn(self.columns[col].name,
self.renderer, text = col)
column.set_sort_column_id(col)
column.add_attribute(self.renderer, "weight",
col + self.nr_columns)
self.treeview.append_column(column)
self.cpus_filtered = cpus_filtered
self.refreshing = True
def foreach_selected_cb(self, model, path, iter, irq_list):
irq = model.get_value(iter, self.COL_NUM)
irq_list.append(str(irq))
def on_drag_data_get_data(self, treeview, context,
selection, target_id, etime):
treeselection = treeview.get_selection()
irq_list = []
treeselection.selected_foreach(self.foreach_selected_cb, irq_list)
selection.set(selection.target, 8, "irq:" + ",".join(irq_list))
def filtered(self, irq):
if self.cpus_filtered and self.is_root:
affinity = self.irqs[irq]["affinity"]
if set(self.cpus_filtered + affinity) == set(self.cpus_filtered):
return True
return False
def set_irq_columns(self, iter, irq, irq_info, nics):
new_value = [ None ] * self.nr_columns
users = tuna.get_irq_users(self.irqs, irq, nics)
if self.has_threaded_irqs:
pids = self.ps.find_by_name("IRQ-%d" % irq)
if pids:
pid = pids[0]
prio = int(self.ps[pid]["stat"]["rt_priority"])
sched = schedutils.schedstr(schedutils.get_scheduler(pid))[6:]
else:
sched = ""
pid = -1
prio = -1
new_value[self.COL_PID] = pid
new_value[self.COL_POL] = sched
new_value[self.COL_PRI] = prio
new_value[self.COL_NUM] = irq
new_value[self.COL_AFF] = tuna.get_irq_affinity_text(self.irqs, irq)
new_value[self.COL_EVENTS] = reduce(lambda a, b: a + b, irq_info["cpu"])
new_value[self.COL_USERS] = ",".join(users)
set_store_columns(self.list_store, iter, new_value)
def show(self):
new_irqs = []
for sirq in self.irqs.keys():
try:
new_irqs.append(int(sirq))
except:
continue
nics = ethtool.get_active_devices()
row = self.list_store.get_iter_first()
while row:
irq = self.list_store.get_value(row, self.COL_NUM)
# IRQ was unregistered? I.e. driver unloaded?
if not self.irqs.has_key(irq):
if self.list_store.remove(row):
# removed and row now its the next one
continue
# Was the last one
break
elif self.filtered(irq):
new_irqs.remove(irq)
if self.list_store.remove(row):
# removed and row now its the next one
continue
# Was the last one
break
else:
new_irqs.remove(irq)
irq_info = self.irqs[irq]
self.set_irq_columns(row, irq, irq_info, nics)
row = self.list_store.iter_next(row)
new_irqs.sort()
for irq in new_irqs:
if self.filtered(irq):
continue
row = self.list_store.append()
irq_info = self.irqs[irq]
self.set_irq_columns(row, irq, irq_info, nics)
self.treeview.show_all()
def refresh(self):
if not self.refreshing:
return
self.irqs.reload()
self.show()
def refresh_toggle(self, unused):
self.refreshing = not self.refreshing
def edit_attributes(self, a):
ret = self.treeview.get_path_at_pos(self.last_x, self.last_y)
if not ret:
return
path, col, xpos, ypos = ret
if not path:
return
row = self.list_store.get_iter(path)
irq = self.list_store.get_value(row, self.COL_NUM)
if not self.irqs.has_key(irq):
return
dialog = irq_druid(self.irqs, self.ps, irq)
if dialog.run():
self.refresh(self.ps)
def on_irqlist_button_press_event(self, treeview, event):
if event.type != gtk.gdk.BUTTON_PRESS or event.button != 3:
return
self.last_x = int(event.x)
self.last_y = int(event.y)
menu = gtk.Menu()
setattr = gtk.MenuItem("_Set IRQ attributes")
if self.refreshing:
refresh_prefix = "Sto_p refreshing the"
else:
refresh_prefix = "_Refresh"
refresh = gtk.MenuItem(refresh_prefix + " IRQ list")
menu.add(setattr)
menu.add(refresh)
setattr.connect_object('activate', self.edit_attributes, event)
refresh.connect_object('activate', self.refresh_toggle, event)
setattr.show()
refresh.show()
menu.popup(None, None, None, event.button, event.time)
def toggle_mask_cpu(self, cpu, enabled):
if not enabled:
if cpu not in self.cpus_filtered:
self.cpus_filtered.append(cpu)
self.show()
else:
if cpu in self.cpus_filtered:
self.cpus_filtered.remove(cpu)
self.show()
class process_druid:
( PROCESS_COL_PID, PROCESS_COL_NAME ) = range(2)
def __init__(self, ps, pid, nr_cpus):
self.ps = ps
self.pid = pid
self.nr_cpus = nr_cpus
pid_info = self.ps[pid]
self.window = gtk.glade.XML(tuna_glade, "set_process_attributes")
self.dialog = self.window.get_widget("set_process_attributes")
pixbuf = self.dialog.render_icon(gtk.STOCK_PREFERENCES,
gtk.ICON_SIZE_SMALL_TOOLBAR)
self.dialog.set_icon(pixbuf)
event_handlers = { "on_cmdline_regex_changed" : self.on_cmdline_regex_changed,
"on_affinity_text_changed" : self.on_affinity_text_changed,
"on_sched_policy_combo_changed" : self.on_sched_policy_combo_changed,
"on_command_regex_clicked" : self.on_command_regex_clicked,
"on_all_these_threads_clicked" : self.on_all_these_threads_clicked,
"on_just_this_thread_clicked" : self.on_just_this_thread_clicked }
self.window.signal_autoconnect(event_handlers)
self.sched_pri = self.window.get_widget("sched_pri_spin")
self.sched_policy = self.window.get_widget("sched_policy_combo")
self.regex_edit = self.window.get_widget("cmdline_regex")
self.affinity = self.window.get_widget("affinity_text")
self.just_this_thread = self.window.get_widget("just_this_thread")
self.all_these_threads = self.window.get_widget("all_these_threads")
processes = self.window.get_widget("matching_process_list")
self.sched_pri.set_value(int(pid_info["stat"]["rt_priority"]))
cmdline_regex = procfs.process_cmdline(pid_info)
self.affinity_text = tuna.list_to_cpustring(schedutils.get_affinity(pid))
self.affinity.set_text(self.affinity_text)
self.create_matching_process_model(processes)
self.create_policy_model(self.sched_policy)
self.sched_policy.set_active(schedutils.get_scheduler(pid))
self.regex_edit.set_text(cmdline_regex)
self.just_this_thread.set_active(True)
self.regex_edit.set_sensitive(False)
if not ps[pid].has_key("threads"):
self.all_these_threads.hide()
self.on_just_this_thread_clicked(None)
def refresh_match_pids(self, cmdline_regex):
self.process_list_store.clear()
for match_pid in self.ps.find_by_cmdline_regex(cmdline_regex):
info = self.process_list_store.append()
pid_info = self.ps[match_pid]
cmdline = procfs.process_cmdline(pid_info)
self.process_list_store.set(info, self.PROCESS_COL_PID, match_pid,
self.PROCESS_COL_NAME,
cmdline)
def create_matching_process_model(self, processes):
labels = [ "PID", "Name" ]
self.process_list_store = gtk.ListStore(gobject.TYPE_UINT,
gobject.TYPE_STRING)
processes.set_model(self.process_list_store)
renderer = gtk.CellRendererText()
for col in range(len(labels)):
column = gtk.TreeViewColumn(labels[col], renderer, text = col)
column.set_sort_column_id(col)
processes.append_column(column)
def create_policy_model(self, policy):
( COL_TEXT, COL_SCHED ) = range(2)
list_store = gtk.ListStore(gobject.TYPE_STRING,
gobject.TYPE_UINT)
policy.set_model(list_store)
renderer = gtk.CellRendererText()
policy.pack_start(renderer, True)
policy.add_attribute(renderer, "text", COL_TEXT)
for pol in range(4):
row = list_store.append()
list_store.set(row, COL_TEXT, schedutils.schedstr(pol),
COL_SCHED, pol)
def on_cmdline_regex_changed(self, entry):
process_regex_text = entry.get_text()
try:
cmdline_regex = re.compile(process_regex_text)
except:
self.process_list_store.clear()
return
self.refresh_match_pids(cmdline_regex)
def on_just_this_thread_clicked(self, button):
self.regex_edit.set_sensitive(False)
self.process_list_store.clear()
info = self.process_list_store.append()
cmdline = procfs.process_cmdline(self.ps[self.pid])
self.process_list_store.set(info,
self.PROCESS_COL_PID, self.pid,
self.PROCESS_COL_NAME, cmdline)
def on_command_regex_clicked(self, button):
self.regex_edit.set_sensitive(True)
self.on_cmdline_regex_changed(self.regex_edit)
def on_all_these_threads_clicked(self, button):
self.regex_edit.set_sensitive(False)
self.process_list_store.clear()
info = self.process_list_store.append()
cmdline = procfs.process_cmdline(self.ps[self.pid])
self.process_list_store.set(info,
self.PROCESS_COL_PID, self.pid,
self.PROCESS_COL_NAME, cmdline)
for tid in self.ps[self.pid]["threads"].keys():
child = self.process_list_store.append()
self.process_list_store.set(child,
self.PROCESS_COL_PID, tid,
self.PROCESS_COL_NAME, cmdline)
def on_sched_policy_combo_changed(self, button):
new_policy = self.sched_policy.get_active()
if new_policy in ( SCHED_FIFO, SCHED_RR ):
can_change_pri = True
else:
can_change_pri = False
self.sched_pri.set_sensitive(can_change_pri)
def on_affinity_text_changed(self, button):
on_affinity_text_changed(self)
def set_attributes_for_regex(self, regex, new_policy, new_prio, new_affinity):
changed = False
cmdline_regex = re.compile(regex)
for match_pid in self.ps.find_by_cmdline_regex(cmdline_regex):
if thread_set_attributes(match_pid, self.ps, new_policy,
new_prio, new_affinity,
self.nr_cpus):
changed = True
return changed
def set_attributes_for_threads(self, pid, new_policy, new_prio, new_affinity):
changed = False
threads = self.ps[pid]["threads"]
for tid in threads.keys():
if thread_set_attributes(tid, threads, new_policy, new_prio,
new_affinity, self.nr_cpus):
changed = True
return changed
def run(self):
changed = False
if self.dialog.run() == gtk.RESPONSE_OK:
new_policy = int(self.sched_policy.get_active())
new_prio = int(self.sched_pri.get_value())
new_affinity = self.affinity.get_text()
if self.just_this_thread.get_active():
changed = thread_set_attributes(self.pid,
self.ps,
new_policy,
new_prio,
new_affinity,
self.nr_cpus)
elif self.all_these_threads.get_active():
if thread_set_attributes(self.pid, self.ps,
new_policy, new_prio,
new_affinity,
self.nr_cpus):
changed = True
if self.set_attributes_for_threads(self.pid,
new_policy,
new_prio,
new_affinity):
changed = True
else:
changed = self.set_attributes_for_regex(self.regex_edit.get_text(),
new_policy,
new_prio,
new_affinity)
self.dialog.destroy()
return changed
class procview:
nr_columns = 7
( COL_PID, COL_POL, COL_PRI, COL_AFF, COL_VOLCTXT, COL_NONVOLCTXT, COL_CMDLINE ) = range(nr_columns)
columns = (list_store_column("PID"),
list_store_column("Policy", gobject.TYPE_STRING),
list_store_column("Priority"),
list_store_column("Affinity", gobject.TYPE_STRING),
list_store_column("VolCtxtSwitch", gobject.TYPE_INT),
list_store_column("NonVolCtxtSwitch", gobject.TYPE_INT),
list_store_column("Command Line", gobject.TYPE_STRING))
def __init__(self, treeview, ps,
show_kthreads = True, show_uthreads = True,
cpus_filtered = None):
self.ps = ps
self.treeview = treeview
self.nr_cpus = procfs.cpuinfo().nr_cpus
if not ps[1]["status"].has_key("voluntary_ctxt_switches"):
self.nr_columns = 5
( self.COL_PID, self.COL_POL, self.COL_PRI,
self.COL_AFF, self.COL_CMDLINE ) = range(self.nr_columns)
self.columns = (list_store_column("PID"),
list_store_column("Policy", gobject.TYPE_STRING),
list_store_column("Priority"),
list_store_column("Affinity", gobject.TYPE_STRING),
list_store_column("Command Line", gobject.TYPE_STRING))
self.tree_store = gtk.TreeStore(*generate_list_store_columns_with_attr(self.columns))
self.treeview.set_model(self.tree_store)
# Allow selecting multiple rows
selection = treeview.get_selection()
selection.set_mode(gtk.SELECTION_MULTIPLE)
# Allow enable drag and drop of rows
self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
DND_TARGETS,
gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE)
self.treeview.connect("drag_data_get", self.on_drag_data_get_data)
try:
self.treeview.connect("query-tooltip", self.on_query_tooltip)
except:
# old versions of pygtk2+ doesn't have this signal
pass
self.renderer = gtk.CellRendererText()
for col in range(self.nr_columns):
column = gtk.TreeViewColumn(self.columns[col].name,
self.renderer, text = col)
column.add_attribute(self.renderer, "weight",
col + self.nr_columns)
column.set_sort_column_id(col)
try:
self.treeview.set_tooltip_column(col)
except:
# old versions of pygtk2+ doesn't have this signal
pass
self.treeview.append_column(column)
self.show_kthreads = show_kthreads
self.show_uthreads = show_uthreads
self.cpus_filtered = cpus_filtered
self.refreshing = True
def on_query_tooltip(self, treeview, x, y, keyboard_mode, tooltip):
# FIXME: Why is that it is off by a row?
ret = treeview.get_path_at_pos(x, y - 30)
tooltip.set_text(None)
if not ret:
return True
path, col, xpos, ypos = ret
if not path:
return True
col_id = col.get_sort_column_id()
if col_id != self.COL_CMDLINE:
return True
row = self.tree_store.get_iter(path)
if not row:
return True
pid = int(self.tree_store.get_value(row, self.COL_PID))
if not tuna.iskthread(pid):
return True
cmdline = self.tree_store.get_value(row, self.COL_CMDLINE).split(' ')[0]
try:
index = cmdline.index("/")
key = cmdline[:index + 1]
suffix_help = "\n<i>One per CPU</i>"
except:
key = cmdline
suffix_help = ""
help = tuna.kthread_help(key)
tooltip.set_markup("<b>Kernel Thread %d (%s):</b>\n%s%s" % (pid, cmdline, help, suffix_help))
return True
def foreach_selected_cb(self, model, path, iter, pid_list):
pid = model.get_value(iter, self.COL_PID)
pid_list.append(str(pid))
def on_drag_data_get_data(self, treeview, context,
selection, target_id, etime):
treeselection = treeview.get_selection()
pid_list = []
treeselection.selected_foreach(self.foreach_selected_cb, pid_list)
selection.set(selection.target, 8, "pid:" + ",".join(pid_list))
def set_thread_columns(self, iter, tid, thread_info):
new_value = [ None ] * self.nr_columns
new_value[self.COL_PRI] = int(thread_info["stat"]["rt_priority"])
try:
new_value[self.COL_POL] = schedutils.schedstr(schedutils.get_scheduler(tid))[6:]
except SystemError:
return True
new_value[self.COL_PID] = tid
thread_affinity_list = schedutils.get_affinity(tid)
new_value[self.COL_AFF] = tuna.list_to_cpustring(thread_affinity_list)
try:
new_value[self.COL_VOLCTXT] = int(thread_info["status"]["voluntary_ctxt_switches"])
new_value[self.COL_NONVOLCTXT] = int(thread_info["status"]["nonvoluntary_ctxt_switches"])
except:
pass
new_value[self.COL_CMDLINE] = procfs.process_cmdline(thread_info)
set_store_columns(self.tree_store, iter, new_value)
return False
def show(self, force_refresh = False):
# Start with the first row, if there is one, on the
# process list. If the first time update_rows will just
# have everthing in new_tids and append_new_tids will
# create the rows.
if not self.refreshing and not force_refresh:
return
row = self.tree_store.get_iter_first()
self.update_rows(self.ps, row, None)
self.treeview.show_all()
def filtered(self, pid):
if self.cpus_filtered:
affinity = schedutils.get_affinity(pid)
if set(self.cpus_filtered + affinity) == set(self.cpus_filtered):
return True
if not (self.show_kthreads and self.show_uthreads):
kthread = tuna.iskthread(pid)
if ((not self.show_kthreads) and kthread) or \
((not self.show_uthreads) and not kthread):
return True
return False
def update_rows(self, threads, row, parent_row):
new_tids = threads.keys()
while row:
tid = self.tree_store.get_value(row, self.COL_PID)
if not threads.has_key(tid):
if self.tree_store.remove(row):
# removed and now row is the next one
continue
# removed and its the last one
break
else:
try:
new_tids.remove(tid)
except:
# FIXME: understand in what situation this
# can happen, seems harmless from visual
# inspection.
pass
if self.filtered(tid):
if self.tree_store.remove(row):
# removed and now row is the next one
continue
# removed and its the last one
break
else:
self.set_thread_columns(row, tid, threads[tid])
if threads[tid].has_key("threads"):
children = threads[tid]["threads"]
else:
children = {}
child_row = self.tree_store.iter_children(row)
self.update_rows(children, child_row, row)
row = self.tree_store.iter_next(row)
new_tids.sort()
self.append_new_tids(parent_row, threads, new_tids)
def append_new_tids(self, parent_row, threads, tid_list):
for tid in tid_list:
if self.filtered(tid):
continue
row = self.tree_store.append(parent_row)
if self.set_thread_columns(row, tid, threads[tid]):
# Thread doesn't exists anymore
self.tree_store.remove(row)
continue
if threads[tid].has_key("threads"):
children = threads[tid]["threads"]
children_list = children.keys()
children_list.sort()
for child in children_list:
child_row = self.tree_store.append(row)
if self.set_thread_columns(child_row,
child,
children[child]):
# Thread doesn't exists anymore
self.tree_store.remove(child_row)
def refresh(self):
self.ps.reload()
self.ps.reload_threads()
self.ps.load_cmdline()
self.show(True)
def edit_attributes(self, a):
ret = self.treeview.get_path_at_pos(self.last_x, self.last_y)
if not ret:
return
path, col, xpos, ypos = ret
if not path:
return
row = self.tree_store.get_iter(path)
pid = self.tree_store.get_value(row, self.COL_PID)
if not self.ps.has_key(pid):
return
dialog = process_druid(self.ps, pid, self.nr_cpus)
if dialog.run():
self.refresh()
def kthreads_view_toggled(self, a):
self.show_kthreads = not self.show_kthreads
self.show(True)
def uthreads_view_toggled(self, a):
self.show_uthreads = not self.show_uthreads
self.show(True)
def help_dialog(self, a):
ret = self.treeview.get_path_at_pos(self.last_x, self.last_y)
if not ret:
return
path, col, xpos, ypos = ret
if not path:
return
row = self.tree_store.get_iter(path)
pid = self.tree_store.get_value(row, self.COL_PID)
if not self.ps.has_key(pid):
return
cmdline = self.tree_store.get_value(row, self.COL_CMDLINE)
help, title = tuna.kthread_help_plain_text(pid, cmdline)
dialog = gtk.MessageDialog(None,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_INFO,
gtk.BUTTONS_OK, help)
dialog.set_title(title)
ret = dialog.run()
dialog.destroy()
def refresh_toggle(self, a):
self.refreshing = not self.refreshing
def on_processlist_button_press_event(self, treeview, event):
if event.type != gtk.gdk.BUTTON_PRESS or event.button != 3:
return
self.last_x = int(event.x)
self.last_y = int(event.y)
menu = gtk.Menu()
setattr = gtk.MenuItem("_Set process attributes")
if self.refreshing:
refresh_prefix = "Sto_p refreshing the"
else:
refresh_prefix = "_Refresh the "
refresh = gtk.MenuItem(refresh_prefix + " process list")
if self.show_kthreads:
kthreads_prefix = "_Hide"
else:
kthreads_prefix = "_Show"
kthreads = gtk.MenuItem(kthreads_prefix + " kernel threads")
if self.show_uthreads:
uthreads_prefix = "_Hide"
else:
uthreads_prefix = "_Show"
uthreads = gtk.MenuItem(uthreads_prefix + " user threads")
help = gtk.MenuItem("_What is this?")
menu.add(setattr)
menu.add(refresh)
menu.add(kthreads)
menu.add(uthreads)
menu.add(help)
setattr.connect_object('activate', self.edit_attributes, event)
refresh.connect_object('activate', self.refresh_toggle, event)
kthreads.connect_object('activate', self.kthreads_view_toggled, event)
uthreads.connect_object('activate', self.uthreads_view_toggled, event)
help.connect_object('activate', self.help_dialog, event)
setattr.show()
refresh.show()
kthreads.show()
uthreads.show()
help.show()
menu.popup(None, None, None, event.button, event.time)
def toggle_mask_cpu(self, cpu, enabled):
if not enabled:
if cpu not in self.cpus_filtered:
self.cpus_filtered.append(cpu)
self.show(True)
else:
if cpu in self.cpus_filtered:
self.cpus_filtered.remove(cpu)
self.show(True)
class gui:
def __init__(self, show_kthreads = True, show_uthreads = True, cpus_filtered = []):
if self.check_root():
sys.exit(1)
self.ps = procfs.pidstats()
self.irqs = procfs.interrupts()
self.wtree = gtk.glade.XML(tuna_glade, "mainbig_window")
self.window = self.wtree.get_widget("mainbig_window")
self.procview = procview(self.wtree.get_widget("processlist"),
self.ps, show_kthreads, show_uthreads, cpus_filtered)
self.irqview = irqview(self.wtree.get_widget("irqlist"),
self.irqs, self.ps, cpus_filtered)
self.cpuview = cpuview(self.wtree.get_widget("cpuview"),
self.procview, self.irqview, cpus_filtered)
event_handlers = { "on_mainbig_window_delete_event" : self.on_mainbig_window_delete_event,
"on_processlist_button_press_event" : self.procview.on_processlist_button_press_event,
"on_irqlist_button_press_event" : self.irqview.on_irqlist_button_press_event,
"on_cpuview_button_press_event" : self.cpuview.on_cpuview_button_press_event }
self.wtree.signal_autoconnect(event_handlers)
self.ps.reload_threads()
self.ps.load_cmdline()
self.show()
self.timer = gobject.timeout_add(2500, self.refresh)
try:
self.icon = gtk.status_icon_new_from_stock(gtk.STOCK_PREFERENCES)
self.icon.connect("activate", self.on_status_icon_activate)
self.icon.connect("popup-menu", self.on_status_icon_popup_menu)
except AttributeError:
# Old pygtk2
pass
pixbuf = self.window.render_icon(gtk.STOCK_PREFERENCES,
gtk.ICON_SIZE_SMALL_TOOLBAR)
self.window.set_icon(pixbuf)
def on_status_icon_activate(self, status_icon):
if self.window.is_active():
self.window.hide()
else:
self.window.present()
def on_status_icon_popup_menu(self, icon, event_button, event_time):
menu = gtk.Menu()
quit = gtk.MenuItem("_Quit")
menu.add(quit)
quit.connect_object('activate', self.on_mainbig_window_delete_event, icon)
quit.show()
menu.popup(None, None, None, event_button, event_time)
def on_mainbig_window_delete_event(self, obj, event = None):
gtk.main_quit()
def show(self):
self.cpuview.refresh()
self.irqview.show()
self.procview.show()
def refresh(self):
self.ps.reload()
self.ps.reload_threads()
self.irqview.refresh()
self.ps.load_cmdline()
self.procview.show()
return True
def check_root(self):
if os.getuid() == 0:
return False
dialog = gtk.MessageDialog(None,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_WARNING,
gtk.BUTTONS_YES_NO,
"Root priviledge required\n\n" + \
"Some functions will not work without root " + \
"privilege.\nDo you want to continue?")
ret = dialog.run()
dialog.destroy()
if ret == gtk.RESPONSE_NO:
return True
return False
def run(self):
gtk.main()