summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2009-01-08 19:17:41 -0200
committerArnaldo Carvalho de Melo <acme@redhat.com>2009-01-08 19:17:41 -0200
commite07d4a8133f2a09a994c584387b41ea47afb1fb3 (patch)
treee88a1c2d27adb217dd3ad1238c1ee289c69a62c5
parent33de5fbd8dcca05fe167839ff725f958c0647b9e (diff)
downloadtuna-e07d4a8133f2a09a994c584387b41ea47afb1fb3.tar.gz
gui: move procview to a separate file
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--MANIFEST1
-rw-r--r--tuna/gui/procview.py596
-rw-r--r--tuna/tuna_gui.py597
3 files changed, 601 insertions, 593 deletions
diff --git a/MANIFEST b/MANIFEST
index a3e1389..08650c4 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -9,6 +9,7 @@ tuna/tuna_gui.glade
tuna/gui/__init__.py
tuna/gui/cpuview.py
tuna/gui/irqview.py
+tuna/gui/procview.py
setup.py
rpm/SPECS/tuna.spec
MANIFEST
diff --git a/tuna/gui/procview.py b/tuna/gui/procview.py
new file mode 100644
index 0000000..bfe2a86
--- /dev/null
+++ b/tuna/gui/procview.py
@@ -0,0 +1,596 @@
+import pygtk
+pygtk.require("2.0")
+
+from tuna import tuna, gui
+import gobject, gtk, procfs, re, schedutils
+
+class process_druid:
+
+ ( PROCESS_COL_PID, PROCESS_COL_NAME ) = range(2)
+
+ def __init__(self, ps, pid, nr_cpus, gladefile):
+ self.ps = ps
+ self.pid = pid
+ self.nr_cpus = nr_cpus
+ pid_info = self.ps[pid]
+ self.window = gtk.glade.XML(gladefile, "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)
+ 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)
+
+ processes.set_model(self.process_list_store)
+
+ def create_policy_model(self, policy):
+ ( COL_TEXT, COL_SCHED ) = range(2)
+ list_store = gtk.ListStore(gobject.TYPE_STRING,
+ gobject.TYPE_UINT)
+ 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)
+ policy.set_model(list_store)
+
+ 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 ( schedutils.SCHED_FIFO, schedutils.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):
+ gui.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 gui.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 gui.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 = gui.thread_set_attributes(self.pid,
+ self.ps,
+ new_policy,
+ new_prio,
+ new_affinity,
+ self.nr_cpus)
+ elif self.all_these_threads.get_active():
+ if gui.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 = (gui.list_store_column("PID"),
+ gui.list_store_column("Policy", gobject.TYPE_STRING),
+ gui.list_store_column("Priority"),
+ gui.list_store_column("Affinity", gobject.TYPE_STRING),
+ gui.list_store_column("VolCtxtSwitch", gobject.TYPE_UINT),
+ gui.list_store_column("NonVolCtxtSwitch", gobject.TYPE_UINT),
+ gui.list_store_column("Command Line", gobject.TYPE_STRING))
+
+ def __init__(self, treeview, ps,
+ show_kthreads, show_uthreads,
+ cpus_filtered, gladefile):
+ self.ps = ps
+ self.treeview = treeview
+ self.nr_cpus = procfs.cpuinfo().nr_cpus
+ self.gladefile = gladefile
+
+ 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 = (gui.list_store_column("PID"),
+ gui.list_store_column("Policy", gobject.TYPE_STRING),
+ gui.list_store_column("Priority"),
+ gui.list_store_column("Affinity", gobject.TYPE_STRING),
+ gui.list_store_column("Command Line", gobject.TYPE_STRING))
+
+ self.tree_store = gtk.TreeStore(*gui.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,
+ gui.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):
+ x, y = treeview.convert_widget_to_bin_window_coords(x, y)
+ ret = treeview.get_path_at_pos(x, y)
+ 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"])
+ new_value[self.COL_POL] = schedutils.schedstr(schedutils.get_scheduler(tid))[6:]
+ thread_affinity_list = schedutils.get_affinity(tid)
+
+ new_value[self.COL_PID] = 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)
+
+ gui.set_store_columns(self.tree_store, iter, new_value)
+
+ 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 everything 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 update_rows(self, threads, row, parent_row):
+ new_tids = threads.keys()
+ previous_row = None
+ while row:
+ tid = self.tree_store.get_value(row, self.COL_PID)
+ if previous_row:
+ previous_tid = self.tree_store.get_value(previous_row, self.COL_PID)
+ if previous_tid == tid:
+ # print "WARNING: tree_store dup %d, fixing..." % tid
+ self.tree_store.remove(previous_row)
+ 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 tuna.thread_filtered(tid, self.cpus_filtered,
+ self.show_kthreads,
+ self.show_uthreads):
+ if self.tree_store.remove(row):
+ # removed and now row is the next one
+ continue
+ # removed and its the last one
+ break
+ else:
+ try:
+ 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)
+ except: # thread doesn't exists anymore
+ if self.tree_store.remove(row):
+ # removed and now row is the next one
+ continue
+ # removed and its the last one
+ break
+
+ previous_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 tuna.thread_filtered(tid, self.cpus_filtered,
+ self.show_kthreads,
+ self.show_uthreads):
+ continue
+
+ row = self.tree_store.append(parent_row)
+
+ try:
+ self.set_thread_columns(row, tid, threads[tid])
+ except: # 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)
+ try:
+ self.set_thread_columns(child_row,
+ child,
+ children[child])
+ except: # Thread doesn't exists anymore
+ self.tree_store.remove(child_row)
+
+ def refresh(self):
+ self.ps.reload()
+ self.ps.reload_threads()
+
+ 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,
+ self.gladefile)
+ 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 save_kthreads_tunings(self, a):
+ dialog = gtk.FileChooserDialog("Save As",
+ None,
+ gtk.FILE_CHOOSER_ACTION_SAVE,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OK, gtk.RESPONSE_OK))
+ dialog.set_default_response(gtk.RESPONSE_OK)
+
+ try:
+ dialog.set_do_overwrite_confirmation(True)
+ except:
+ pass
+
+ filter = gtk.FileFilter()
+ filter.set_name("rtctl config files")
+ filter.add_pattern("*.rtctl")
+ filter.add_pattern("*.tuna")
+ filter.add_pattern("*rtgroup*")
+ dialog.add_filter(filter)
+
+ filter = gtk.FileFilter()
+ filter.set_name("All files")
+ filter.add_pattern("*")
+ dialog.add_filter(filter)
+
+ response = dialog.run()
+
+ filename = dialog.get_filename()
+ dialog.destroy()
+
+ if response != gtk.RESPONSE_OK:
+ return
+
+ self.refresh()
+ kthreads = tuna.get_kthread_sched_tunings(self.ps)
+ tuna.generate_rtgroups(filename, kthreads, self.nr_cpus)
+
+ if filename != "/etc/rtgroups":
+ dialog = gtk.MessageDialog(None,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_INFO,
+ gtk.BUTTONS_YES_NO,
+ "Kernel thread tunings saved!\n\n"
+ "Now you can use it with rtctl:\n\n"
+ "rtctl --file %s reset\n\n"
+ "If you want the changes to be in "
+ "effect every time you boot the system "
+ "please move %s to /etc/rtgroups\n\n"
+ "Do you want to do that now?" % (filename, filename))
+ response = dialog.run()
+ dialog.destroy()
+ if response == gtk.RESPONSE_YES:
+ filename = "/etc/rtgroups"
+ tuna.generate_rtgroups(filename, kthreads, self.nr_cpus)
+
+ dialog = gtk.MessageDialog(None,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_INFO,
+ gtk.BUTTONS_OK,
+ "Kernel thread tunings saved to %s!" % filename)
+ dialog.run()
+ dialog.destroy()
+
+ 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?")
+
+ save_kthreads_tunings = gtk.MenuItem("_Save kthreads tunings")
+
+ menu.add(save_kthreads_tunings)
+ menu.add(setattr)
+ menu.add(refresh)
+ menu.add(kthreads)
+ menu.add(uthreads)
+ menu.add(help)
+
+ save_kthreads_tunings.connect_object('activate',
+ self.save_kthreads_tunings, event)
+ 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)
+
+ save_kthreads_tunings.show()
+ 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)
diff --git a/tuna/tuna_gui.py b/tuna/tuna_gui.py
index d96f9a6..083d1be 100644
--- a/tuna/tuna_gui.py
+++ b/tuna/tuna_gui.py
@@ -5,605 +5,15 @@
import pygtk
pygtk.require("2.0")
-import gtk, gobject, os, procfs, re, schedutils, sys
+import gtk, gobject, os, procfs, sys
import gtk.glade
-import gui
from gui.cpuview import cpuview
from gui.irqview import irqview
-import tuna
+from gui.procview import procview
tuna_glade_dirs = [ ".", "tuna", "/usr/share/tuna" ]
tuna_glade = None
-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)
- 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)
-
- processes.set_model(self.process_list_store)
-
- def create_policy_model(self, policy):
- ( COL_TEXT, COL_SCHED ) = range(2)
- list_store = gtk.ListStore(gobject.TYPE_STRING,
- gobject.TYPE_UINT)
- 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)
- policy.set_model(list_store)
-
- 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 (schedutils.SCHED_FIFO, schedutils.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):
- gui.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 gui.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 gui.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 = gui.thread_set_attributes(self.pid,
- self.ps,
- new_policy,
- new_prio,
- new_affinity,
- self.nr_cpus)
- elif self.all_these_threads.get_active():
- if gui.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 = (gui.list_store_column("PID"),
- gui.list_store_column("Policy", gobject.TYPE_STRING),
- gui.list_store_column("Priority"),
- gui.list_store_column("Affinity", gobject.TYPE_STRING),
- gui.list_store_column("VolCtxtSwitch", gobject.TYPE_UINT),
- gui.list_store_column("NonVolCtxtSwitch", gobject.TYPE_UINT),
- gui.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 = (gui.list_store_column("PID"),
- gui.list_store_column("Policy", gobject.TYPE_STRING),
- gui.list_store_column("Priority"),
- gui.list_store_column("Affinity", gobject.TYPE_STRING),
- gui.list_store_column("Command Line", gobject.TYPE_STRING))
-
- self.tree_store = gtk.TreeStore(*gui.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,
- gui.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):
- x, y = treeview.convert_widget_to_bin_window_coords(x, y)
- ret = treeview.get_path_at_pos(x, y)
- 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"])
- new_value[self.COL_POL] = schedutils.schedstr(schedutils.get_scheduler(tid))[6:]
- thread_affinity_list = schedutils.get_affinity(tid)
-
- new_value[self.COL_PID] = 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)
-
- gui.set_store_columns(self.tree_store, iter, new_value)
-
- 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 everything 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 update_rows(self, threads, row, parent_row):
- new_tids = threads.keys()
- previous_row = None
- while row:
- tid = self.tree_store.get_value(row, self.COL_PID)
- if previous_row:
- previous_tid = self.tree_store.get_value(previous_row, self.COL_PID)
- if previous_tid == tid:
- # print "WARNING: tree_store dup %d, fixing..." % tid
- self.tree_store.remove(previous_row)
- 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 tuna.thread_filtered(tid, self.cpus_filtered,
- self.show_kthreads,
- self.show_uthreads):
- if self.tree_store.remove(row):
- # removed and now row is the next one
- continue
- # removed and its the last one
- break
- else:
- try:
- 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)
- except: # thread doesn't exists anymore
- if self.tree_store.remove(row):
- # removed and now row is the next one
- continue
- # removed and its the last one
- break
-
- previous_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 tuna.thread_filtered(tid, self.cpus_filtered,
- self.show_kthreads,
- self.show_uthreads):
- continue
-
- row = self.tree_store.append(parent_row)
-
- try:
- self.set_thread_columns(row, tid, threads[tid])
- except: # 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)
- try:
- self.set_thread_columns(child_row,
- child,
- children[child])
- except: # Thread doesn't exists anymore
- self.tree_store.remove(child_row)
-
- def refresh(self):
- self.ps.reload()
- self.ps.reload_threads()
-
- 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 save_kthreads_tunings(self, a):
- dialog = gtk.FileChooserDialog("Save As",
- None,
- gtk.FILE_CHOOSER_ACTION_SAVE,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
- gtk.STOCK_OK, gtk.RESPONSE_OK))
- dialog.set_default_response(gtk.RESPONSE_OK)
-
- try:
- dialog.set_do_overwrite_confirmation(True)
- except:
- pass
-
- filter = gtk.FileFilter()
- filter.set_name("rtctl config files")
- filter.add_pattern("*.rtctl")
- filter.add_pattern("*.tuna")
- filter.add_pattern("*rtgroup*")
- dialog.add_filter(filter)
-
- filter = gtk.FileFilter()
- filter.set_name("All files")
- filter.add_pattern("*")
- dialog.add_filter(filter)
-
- response = dialog.run()
-
- filename = dialog.get_filename()
- dialog.destroy()
-
- if response != gtk.RESPONSE_OK:
- return
-
- self.refresh()
- kthreads = tuna.get_kthread_sched_tunings(self.ps)
- tuna.generate_rtgroups(filename, kthreads, self.nr_cpus)
-
- if filename != "/etc/rtgroups":
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_INFO,
- gtk.BUTTONS_YES_NO,
- "Kernel thread tunings saved!\n\n"
- "Now you can use it with rtctl:\n\n"
- "rtctl --file %s reset\n\n"
- "If you want the changes to be in "
- "effect every time you boot the system "
- "please move %s to /etc/rtgroups\n\n"
- "Do you want to do that now?" % (filename, filename))
- response = dialog.run()
- dialog.destroy()
- if response == gtk.RESPONSE_YES:
- filename = "/etc/rtgroups"
- tuna.generate_rtgroups(filename, kthreads, self.nr_cpus)
-
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_INFO,
- gtk.BUTTONS_OK,
- "Kernel thread tunings saved to %s!" % filename)
- dialog.run()
- dialog.destroy()
-
- 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?")
-
- save_kthreads_tunings = gtk.MenuItem("_Save kthreads tunings")
-
- menu.add(save_kthreads_tunings)
- menu.add(setattr)
- menu.add(refresh)
- menu.add(kthreads)
- menu.add(uthreads)
- menu.add(help)
-
- save_kthreads_tunings.connect_object('activate',
- self.save_kthreads_tunings, event)
- 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)
-
- save_kthreads_tunings.show()
- 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 main_gui:
def __init__(self, show_kthreads = True, show_uthreads = True, cpus_filtered = []):
@@ -621,7 +31,8 @@ class main_gui:
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.ps, show_kthreads, show_uthreads,
+ cpus_filtered, tuna_glade)
self.irqview = irqview(self.wtree.get_widget("irqlist"),
self.irqs, self.ps, cpus_filtered,
tuna_glade)