diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2009-01-08 19:17:41 -0200 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2009-01-08 19:17:41 -0200 |
commit | e07d4a8133f2a09a994c584387b41ea47afb1fb3 (patch) | |
tree | e88a1c2d27adb217dd3ad1238c1ee289c69a62c5 | |
parent | 33de5fbd8dcca05fe167839ff725f958c0647b9e (diff) | |
download | tuna-e07d4a8133f2a09a994c584387b41ea47afb1fb3.tar.gz |
gui: move procview to a separate file
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | MANIFEST | 1 | ||||
-rw-r--r-- | tuna/gui/procview.py | 596 | ||||
-rw-r--r-- | tuna/tuna_gui.py | 597 |
3 files changed, 601 insertions, 593 deletions
@@ -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) |