diff options
author | John Kacur <jkacur@redhat.com> | 2020-08-01 02:30:51 -0400 |
---|---|---|
committer | John Kacur <jkacur@redhat.com> | 2020-08-01 02:30:51 -0400 |
commit | c003f25438af2caaa6d6beea38fdd5de02075bcf (patch) | |
tree | 8bb7ddb056ad7e1867e84e7051b11cd90fa6ad5a | |
parent | 04eee9a483bafc8ca6099f5b9c6bb0a6d27d7277 (diff) | |
download | tuna-c003f25438af2caaa6d6beea38fdd5de02075bcf.tar.gz |
tuna: procview.py: Update the spacing and style
Update the spacing and style.
Signed-off-by: John Kacur <jkacur@redhat.com>
-rwxr-xr-x | tuna/gui/procview.py | 1333 |
1 files changed, 661 insertions, 672 deletions
diff --git a/tuna/gui/procview.py b/tuna/gui/procview.py index 62ba9cf..fa70ab4 100755 --- a/tuna/gui/procview.py +++ b/tuna/gui/procview.py @@ -9,686 +9,675 @@ import re import schedutils try: - import perf + import perf except: - pass + pass def N_(s): - """gettext_noop""" - return s + """gettext_noop""" + return s class process_druid: - ( PROCESS_COL_PID, PROCESS_COL_NAME ) = list(range(2)) - - def __init__(self, ps, pid, pid_info, nr_cpus, gladefile): - self.ps = ps - self.pid = pid - self.pid_info = pid_info - self.nr_cpus = nr_cpus - self.window = Gtk.glade.XML(gladefile, "set_process_attributes", "tuna") - self.dialog = self.window.get_widget("set_process_attributes") - pixbuf = self.dialog.render_icon(Gtk.STOCK_PREFERENCES, - Gtk.IconSize.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 pid not in ps or "threads" not in ps[pid]: - 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 ) = list(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.pid_info) - 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 list(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(self.ps[match_pid], - 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 list(threads.keys()): - if gui.thread_set_attributes(threads[tid], new_policy, - new_prio, new_affinity, - self.nr_cpus): - changed = True - - return changed - - def run(self): - changed = False - if self.dialog.run() == Gtk.ResponseType.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_info, - new_policy, - new_prio, - new_affinity, - self.nr_cpus) - elif self.all_these_threads.get_active(): - if gui.thread_set_attributes(self.pid_info, - 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 + (PROCESS_COL_PID, PROCESS_COL_NAME) = list(range(2)) + + def __init__(self, ps, pid, pid_info, nr_cpus, gladefile): + self.ps = ps + self.pid = pid + self.pid_info = pid_info + self.nr_cpus = nr_cpus + self.window = Gtk.glade.XML(gladefile, "set_process_attributes", "tuna") + self.dialog = self.window.get_widget("set_process_attributes") + pixbuf = self.dialog.render_icon(Gtk.STOCK_PREFERENCES, + Gtk.IconSize.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 pid not in ps or "threads" not in ps[pid]: + 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) = list(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.pid_info) + 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 list(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(self.ps[match_pid], + 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 list(threads.keys()): + if gui.thread_set_attributes(threads[tid], new_policy, + new_prio, new_affinity, self.nr_cpus): + changed = True + + return changed + + def run(self): + changed = False + if self.dialog.run() == Gtk.ResponseType.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_info, new_policy, + new_prio, new_affinity, + self.nr_cpus) + elif self.all_these_threads.get_active(): + if gui.thread_set_attributes(self.pid_info, 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 = 8 - ( COL_PID, COL_POL, COL_PRI, COL_AFF, COL_VOLCTXT, COL_NONVOLCTXT, COL_CGROUP, COL_CMDLINE ) = list(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(_("CGroup"), GObject.TYPE_STRING), - 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 - - self.evlist = None - try: - self.perf_init() - except: # No perf, poll /proc baby, poll - pass - - if "voluntary_ctxt_switches" not in ps[1]["status"]: - self.nr_columns = 5 - else: - self.nr_columns = 7 - try: - if ps[1]["cgroups"]: - self.nr_columns = self.nr_columns + 1 - except: - pass - - 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)) - - if self.nr_columns==5: - ( self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, self.COL_CMDLINE ) = list(range(self.nr_columns)) - self.columns = self.columns + (gui.list_store_column(_("Command Line"), GObject.TYPE_STRING)) - - elif self.nr_columns==6: - ( self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, self.COL_CGROUP, self.COL_CMDLINE ) = list(range(self.nr_columns)) - self.columns = self.columns + (gui.list_store_column(_("CGroup"), GObject.TYPE_STRING), - gui.list_store_column(_("Command Line"), GObject.TYPE_STRING)) - - elif self.nr_columns==7: - ( self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, self.COL_VOLCTXT, - self.NONVOLCTXT, self.COL_CMDLINE ) = list(range(self.nr_columns)) - self.columns = self.columns + (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)) - - elif self.nr_columns==8: - ( self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, self.COL_VOLCTXT, - self.COL_NONVOLCTXT, self.COL_CGROUP, self.COL_CMDLINE ) = list(range(self.nr_columns)) - self.columns = self.columns + (gui.list_store_column(_("VolCtxtSwitch"), GObject.TYPE_UINT), - gui.list_store_column(_("NonVolCtxtSwitch"), GObject.TYPE_UINT), - gui.list_store_column(_("CGroup"), 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.SelectionMode.MULTIPLE) - - # Allow enable drag and drop of rows - self.treeview.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, - gui.DND_TARGETS, - Gdk.DragAction.DEFAULT | Gdk.DragAction.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) - if(col == self.COL_CGROUP): - column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) - column.set_fixed_width(130) - try: - self.treeview.set_tooltip_column(col) - except: - # old versions of pygtk2+ doesn't have this signal - pass - column.set_resizable(True) - self.treeview.append_column(column) - - self.show_kthreads = show_kthreads - self.show_uthreads = show_uthreads - self.cpus_filtered = cpus_filtered - self.refreshing = True - - def perf_process_events(self, source, condition): - had_events = True - while had_events: - had_events = False - for cpu in self.cpu_map: - event = self.evlist.read_on_cpu(cpu) - if event: - had_events = True - if event.type == perf.RECORD_FORK: - if event.pid == event.tid: - try: - self.ps.processes[event.pid] = procfs.process(event.pid) - except: # short lived thread - pass - else: - try: - self.ps.processes[event.pid].threads.processes[event.tid] = procfs.process(event.tid) - except AttributeError: - self.ps.processes[event.pid].threads = procfs.pidstats("/proc/%d/task/" % event.pid) - elif event.type == perf.RECORD_EXIT: - del self.ps[int(event.tid)] - elif event.type == perf.RECORD_SAMPLE: - tid = event.sample_tid - if tid in self.perf_counter: - self.perf_counter[tid] += event.sample_period - else: - self.perf_counter[tid] = event.sample_period - - self.show() - return True - - def perf_init(self): - self.cpu_map = perf.cpu_map() - self.thread_map = perf.thread_map() - self.evsel_cycles = perf.evsel(task = 1, comm = 1, - wakeup_events = 1, - watermark = 1, - sample_type = perf.SAMPLE_CPU | - perf.SAMPLE_TID) - self.evsel_cycles.open(cpus = self.cpu_map, threads = self.thread_map); - self.evlist = perf.evlist(self.cpu_map, self.thread_map) - self.evlist.add(self.evsel_cycles) - self.evlist.mmap() - self.pollfd = self.evlist.get_pollfd() - for f in self.pollfd: - GObject.io_add_watch(f, Gdk.INPUT_READ, self.perf_process_events) - self.perf_counter = {} - - 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] - help = tuna.kthread_help(cmdline) - tooltip.set_markup("<b>%s %d(%s)</b>\n%s" % \ + nr_columns = 8 + (COL_PID, COL_POL, COL_PRI, COL_AFF, COL_VOLCTXT, COL_NONVOLCTXT, + COL_CGROUP, COL_CMDLINE) = list(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(_("CGroup"), GObject.TYPE_STRING), + 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 + + self.evlist = None + try: + self.perf_init() + except: # No perf, poll /proc baby, poll + pass + + if "voluntary_ctxt_switches" not in ps[1]["status"]: + self.nr_columns = 5 + else: + self.nr_columns = 7 + try: + if ps[1]["cgroups"]: + self.nr_columns = self.nr_columns + 1 + except: + pass + + 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)) + + if self.nr_columns == 5: + (self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, + self.COL_CMDLINE) = list(range(self.nr_columns)) + self.columns = self.columns \ + + (gui.list_store_column(_("Command Line"), GObject.TYPE_STRING)) + + elif self.nr_columns == 6: + (self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, + self.COL_CGROUP, self.COL_CMDLINE) = list(range(self.nr_columns)) + self.columns = self.columns \ + + (gui.list_store_column(_("CGroup"), GObject.TYPE_STRING), + gui.list_store_column(_("Command Line"), GObject.TYPE_STRING)) + elif self.nr_columns == 7: + (self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, \ + self.COL_VOLCTXT, self.NONVOLCTXT, self.COL_CMDLINE) \ + = list(range(self.nr_columns)) + self.columns = self.columns \ + + (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)) + + elif self.nr_columns == 8: + (self.COL_PID, self.COL_POL, self.COL_PRI, self.COL_AFF, \ + self.COL_VOLCTXT, self.COL_NONVOLCTXT, self.COL_CGROUP, \ + self.COL_CMDLINE) = list(range(self.nr_columns)) + self.columns = self.columns \ + + (gui.list_store_column(_("VolCtxtSwitch"), GObject.TYPE_UINT), + gui.list_store_column(_("NonVolCtxtSwitch"), GObject.TYPE_UINT), + gui.list_store_column(_("CGroup"), 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.SelectionMode.MULTIPLE) + + # Allow enable drag and drop of rows + self.treeview.enable_model_drag_source( + Gdk.ModifierType.BUTTON1_MASK, gui.DND_TARGETS, + Gdk.DragAction.DEFAULT | Gdk.DragAction.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) + if col == self.COL_CGROUP: + column.set_sizing(Gtk.TreeViewColumnSizing.FIXED) + column.set_fixed_width(130) + try: + self.treeview.set_tooltip_column(col) + except: + # old versions of pygtk2+ doesn't have this signal + pass + column.set_resizable(True) + self.treeview.append_column(column) + + self.show_kthreads = show_kthreads + self.show_uthreads = show_uthreads + self.cpus_filtered = cpus_filtered + self.refreshing = True + + def perf_process_events(self, source, condition): + had_events = True + while had_events: + had_events = False + for cpu in self.cpu_map: + event = self.evlist.read_on_cpu(cpu) + if event: + had_events = True + if event.type == perf.RECORD_FORK: + if event.pid == event.tid: + try: + self.ps.processes[event.pid] = procfs.process(event.pid) + except: # short lived thread + pass + else: + try: + self.ps.processes[event.pid].threads.processes[event.tid] = procfs.process(event.tid) + except AttributeError: + self.ps.processes[event.pid].threads = procfs.pidstats("/proc/%d/task/" % event.pid) + elif event.type == perf.RECORD_EXIT: + del self.ps[int(event.tid)] + elif event.type == perf.RECORD_SAMPLE: + tid = event.sample_tid + if tid in self.perf_counter: + self.perf_counter[tid] += event.sample_period + else: + self.perf_counter[tid] = event.sample_period + + self.show() + return True + + def perf_init(self): + self.cpu_map = perf.cpu_map() + self.thread_map = perf.thread_map() + self.evsel_cycles = perf.evsel(task=1, comm=1, wakeup_events=1, \ + watermark=1, sample_type=perf.SAMPLE_CPU | perf.SAMPLE_TID) + self.evsel_cycles.open(cpus=self.cpu_map, threads=self.thread_map) + self.evlist = perf.evlist(self.cpu_map, self.thread_map) + self.evlist.add(self.evsel_cycles) + self.evlist.mmap() + self.pollfd = self.evlist.get_pollfd() + for f in self.pollfd: + GObject.io_add_watch(f, Gdk.INPUT_READ, self.perf_process_events) + self.perf_counter = {} + + 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] + help = tuna.kthread_help(cmdline) + tooltip.set_markup("<b>%s %d(%s)</b>\n%s" % \ (_("Kernel Thread"), pid, cmdline, _(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"]) - new_value[self.COL_CGROUP] = thread_info["cgroups"] - 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 = list(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 tid not in threads: - 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" in threads[tid]: - 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" in threads[tid]: - children = threads[tid]["threads"] - children_list = 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 pid in self.ps: - pid_info = self.ps[pid] - else: - parent = self.tree_store.iter_parent(row) - ppid = self.tree_store.get_value(parent, self.COL_PID) - pid_info = self.ps[ppid].threads[pid] - - dialog = process_druid(self.ps, pid, pid_info, 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 pid not in self.ps: - 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.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, - Gtk.MessageType.INFO, - Gtk.ButtonsType.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.FileChooserAction.SAVE, - (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, - Gtk.STOCK_OK, Gtk.ResponseType.OK)) - dialog.set_default_response(Gtk.ResponseType.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.ResponseType.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.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, - Gtk.MessageType.INFO, - Gtk.ButtonsType.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.ResponseType.YES: - filename = "/etc/rtgroups" - tuna.generate_rtgroups(filename, kthreads, self.nr_cpus) - - dialog = Gtk.MessageDialog(None, - Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, - Gtk.MessageType.INFO, - Gtk.ButtonsType.OK, - _("Kernel thread tunings saved to %s!") % filename) - dialog.run() - dialog.destroy() - - def on_processlist_button_press_event(self, treeview, event): - if event.type != Gdk.EventType.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 = Gtk.MenuItem(_("Sto_p refreshing the process list")) - else: - refresh = Gtk.MenuItem(_("_Refresh the process list")) - - if self.show_kthreads: - kthreads = Gtk.MenuItem(_("_Hide kernel threads")) - else: - kthreads = Gtk.MenuItem(_("_Show kernel threads")) - - if self.show_uthreads: - uthreads = Gtk.MenuItem(_("_Hide user threads")) - else: - uthreads = Gtk.MenuItem(_("_Show 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', + 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"]) + new_value[self.COL_CGROUP] = thread_info["cgroups"] + 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 = list(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 tid not in threads: + 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" in threads[tid]: + 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" in threads[tid]: + children = threads[tid]["threads"] + children_list = 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 pid in self.ps: + pid_info = self.ps[pid] + else: + parent = self.tree_store.iter_parent(row) + ppid = self.tree_store.get_value(parent, self.COL_PID) + pid_info = self.ps[ppid].threads[pid] + + dialog = process_druid(self.ps, pid, pid_info, 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 pid not in self.ps: + 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.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ + Gtk.MessageType.INFO, Gtk.ButtonsType.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.FileChooserAction.SAVE, \ + (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, \ + Gtk.STOCK_OK, Gtk.ResponseType.OK)) + dialog.set_default_response(Gtk.ResponseType.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.ResponseType.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.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ + Gtk.MessageType.INFO, Gtk.ButtonsType.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.ResponseType.YES: + filename = "/etc/rtgroups" + tuna.generate_rtgroups(filename, kthreads, self.nr_cpus) + + dialog = Gtk.MessageDialog(None, \ + Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, \ + Gtk.MessageType.INFO, Gtk.ButtonsType.OK, \ + _("Kernel thread tunings saved to %s!") % filename) + dialog.run() + dialog.destroy() + + def on_processlist_button_press_event(self, treeview, event): + if event.type != Gdk.EventType.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 = Gtk.MenuItem(_("Sto_p refreshing the process list")) + else: + refresh = Gtk.MenuItem(_("_Refresh the process list")) + + if self.show_kthreads: + kthreads = Gtk.MenuItem(_("_Hide kernel threads")) + else: + kthreads = Gtk.MenuItem(_("_Show kernel threads")) + + if self.show_uthreads: + uthreads = Gtk.MenuItem(_("_Hide user threads")) + else: + uthreads = Gtk.MenuItem(_("_Show 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) + 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) |