| # -*- python -*- |
| # -*- coding: utf-8 -*- |
| |
| import pygtk |
| pygtk.require("2.0") |
| |
| import gtk, gobject, math, os, procfs, schedutils |
| from tuna import sysfs, tuna, gui |
| |
| def set_affinity_warning(tid, affinity): |
| dialog = gtk.MessageDialog(None, |
| gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, |
| gtk.MESSAGE_WARNING, |
| gtk.BUTTONS_OK, |
| _("Couldn't change the affinity of %(tid)d to %(affinity)s!") % \ |
| {"tid": tid, "affinity": affinity}) |
| dialog.run() |
| dialog.destroy() |
| |
| def drop_handler_move_threads_to_cpu(new_affinity, data): |
| pid_list = [ int(pid) for pid in data.split(",") ] |
| |
| return tuna.move_threads_to_cpu(new_affinity, pid_list, |
| set_affinity_warning) |
| |
| def drop_handler_move_irqs_to_cpu(cpus, data): |
| irq_list = [ int(irq) for irq in data.split(",") ] |
| new_affinity = [ reduce(lambda a, b: a | b, |
| map(lambda cpu: 1 << cpu, cpus)), ] |
| |
| for irq in irq_list: |
| tuna.set_irq_affinity(irq, new_affinity) |
| |
| # FIXME: check if we really changed the affinity, but |
| # its only an optimization to avoid a needless refresh |
| # in the irqview, now we always refresh. |
| return True |
| |
| class cpu_socket_frame(gtk.Frame): |
| |
| ( COL_FILTER, COL_CPU, COL_USAGE ) = range(3) |
| |
| def __init__(self, socket, cpus, creator): |
| |
| if creator.nr_sockets > 1: |
| gtk.Frame.__init__(self, _("Socket %s") % socket) |
| else: |
| gtk.Frame.__init__(self) |
| |
| self.socket = socket |
| self.cpus = cpus |
| self.nr_cpus = len(cpus) |
| self.creator = creator |
| |
| self.list_store = gtk.ListStore(gobject.TYPE_BOOLEAN, |
| gobject.TYPE_UINT, |
| gobject.TYPE_UINT) |
| |
| self.treeview = gtk.TreeView(self.list_store) |
| |
| # Filter column |
| renderer = gtk.CellRendererToggle() |
| renderer.connect('toggled', self.filter_toggled, self.list_store) |
| column = gtk.TreeViewColumn(_('Filter'), renderer, active = self.COL_FILTER) |
| self.treeview.append_column(column) |
| |
| # CPU# column |
| column = gtk.TreeViewColumn(_('CPU'), gtk.CellRendererText(), |
| text = self.COL_CPU) |
| self.treeview.append_column(column) |
| |
| # CPU usage column |
| try: |
| column = gtk.TreeViewColumn(_('Usage'), gtk.CellRendererProgress(), |
| text = self.COL_USAGE, value = self.COL_USAGE) |
| except: |
| # CellRendererProgress needs pygtk2 >= 2.6 |
| column = gtk.TreeViewColumn(_('Usage'), gtk.CellRendererText(), |
| text = self.COL_USAGE) |
| self.treeview.append_column(column) |
| |
| self.add(self.treeview) |
| |
| self.treeview.enable_model_drag_dest(gui.DND_TARGETS, |
| gtk.gdk.ACTION_DEFAULT) |
| self.treeview.connect("drag_data_received", |
| self.on_drag_data_received_data) |
| self.treeview.connect("button_press_event", |
| self.on_cpu_socket_frame_button_press_event) |
| |
| self.drop_handlers = { "pid": (drop_handler_move_threads_to_cpu, self.creator.procview), |
| "irq": (drop_handler_move_irqs_to_cpu, self.creator.irqview), } |
| |
| self.drag_dest_set(gtk.DEST_DEFAULT_ALL, gui.DND_TARGETS, |
| gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE) |
| self.connect("drag_data_received", |
| self.on_frame_drag_data_received_data) |
| |
| def on_frame_drag_data_received_data(self, w, context, x, y, |
| selection, info, etime): |
| # Move to all CPUs in this socket |
| cpus = [ int(cpu.name[3:]) for cpu in self.cpus ] |
| # pid list, a irq list, etc |
| source, data = selection.data.split(":") |
| |
| if self.drop_handlers.has_key(source): |
| if self.drop_handlers[source][0](cpus, data): |
| self.drop_handlers[source][1].refresh() |
| else: |
| print "cpu_socket_frame: unhandled drag source '%s'" % source |
| |
| def on_drag_data_received_data(self, treeview, context, x, y, |
| selection, info, etime): |
| drop_info = treeview.get_dest_row_at_pos(x, y) |
| |
| # pid list, a irq list, etc |
| source, data = selection.data.split(":") |
| |
| if drop_info: |
| model = treeview.get_model() |
| path, position = drop_info |
| iter = model.get_iter(path) |
| cpus = [ model.get_value(iter, self.COL_CPU), ] |
| else: |
| # Move to all CPUs in this socket |
| cpus = [ int(cpu.name[3:]) for cpu in self.cpus ] |
| |
| if self.drop_handlers.has_key(source): |
| if self.drop_handlers[source][0](cpus, data): |
| self.drop_handlers[source][1].refresh() |
| else: |
| print "cpu_socket_frame: unhandled drag source '%s'" % source |
| |
| def refresh(self): |
| self.list_store.clear() |
| for i in range(self.nr_cpus): |
| cpu = self.cpus[i] |
| cpunr = int(cpu.name[3:]) |
| usage = self.creator.cpustats[cpunr + 1].usage |
| |
| iter = self.list_store.append() |
| self.list_store.set(iter, |
| self.COL_FILTER, cpunr not in self.creator.cpus_filtered, |
| self.COL_CPU, cpunr, |
| self.COL_USAGE, int(usage)) |
| self.treeview.show_all() |
| |
| def isolate_cpu(self, a): |
| ret = self.treeview.get_path_at_pos(self.last_x, self.last_y) |
| if not ret: |
| return |
| path, col, xpos, ypos = ret |
| if not path: |
| return |
| row = self.list_store.get_iter(path) |
| cpu = self.list_store.get_value(row, self.COL_CPU) |
| |
| self.creator.isolate_cpus([cpu,]) |
| |
| def include_cpu(self, a): |
| ret = self.treeview.get_path_at_pos(self.last_x, self.last_y) |
| if not ret: |
| return |
| path, col, xpos, ypos = ret |
| if not path: |
| return |
| row = self.list_store.get_iter(path) |
| cpu = self.list_store.get_value(row, self.COL_CPU) |
| |
| self.creator.include_cpus([cpu,]) |
| |
| def restore_cpu(self, a): |
| |
| self.creator.restore_cpu() |
| |
| def isolate_cpu_socket(self, a): |
| |
| # Isolate all CPUs in this socket |
| cpus = [ int(cpu.name[3:]) for cpu in self.cpus ] |
| self.creator.isolate_cpus(cpus) |
| |
| def include_cpu_socket(self, a): |
| |
| # Include all CPUs in this socket |
| cpus = [ int(cpu.name[3:]) for cpu in self.cpus ] |
| self.creator.include_cpus(cpus) |
| |
| def on_cpu_socket_frame_button_press_event(self, treeview, event): |
| if event.type != gtk.gdk.BUTTON_PRESS or event.button != 3: |
| return |
| |
| self.last_x = int(event.x) |
| self.last_y = int(event.y) |
| |
| menu = gtk.Menu() |
| |
| include = gtk.MenuItem(_("I_nclude CPU")) |
| isolate = gtk.MenuItem(_("_Isolate CPU")) |
| if self.creator.nr_sockets > 1: |
| include_socket = gtk.MenuItem(_("I_nclude CPU Socket")) |
| isolate_socket = gtk.MenuItem(_("_Isolate CPU Socket")) |
| restore = gtk.MenuItem(_("_Restore CPU")) |
| |
| menu.add(include) |
| menu.add(isolate) |
| if self.creator.nr_sockets > 1: |
| menu.add(include_socket) |
| menu.add(isolate_socket) |
| menu.add(restore) |
| |
| include.connect_object('activate', self.include_cpu, event) |
| isolate.connect_object('activate', self.isolate_cpu, event) |
| if self.creator.nr_sockets > 1: |
| include_socket.connect_object('activate', self.include_cpu_socket, event) |
| isolate_socket.connect_object('activate', self.isolate_cpu_socket, event) |
| if not (self.creator.previous_pid_affinities or \ |
| self.creator.previous_irq_affinities): |
| restore.set_sensitive(False) |
| restore.connect_object('activate', self.restore_cpu, event) |
| |
| include.show() |
| isolate.show() |
| if self.creator.nr_sockets > 1: |
| include_socket.show() |
| isolate_socket.show() |
| restore.show() |
| |
| menu.popup(None, None, None, event.button, event.time) |
| |
| def filter_toggled(self, cell, path, model): |
| # get toggled iter |
| iter = model.get_iter((int(path),)) |
| enabled = model.get_value(iter, self.COL_FILTER) |
| cpu = model.get_value(iter, self.COL_CPU) |
| |
| enabled = not enabled |
| self.creator.toggle_mask_cpu(cpu, enabled) |
| |
| # set new value |
| model.set(iter, self.COL_FILTER, enabled) |
| |
| class cpuview: |
| |
| def __init__(self, vpaned, hpaned, window, procview, irqview, cpus_filtered): |
| self.cpus = sysfs.cpus() |
| self.cpustats = procfs.cpusstats() |
| self.socket_frames = {} |
| |
| self.procview = procview |
| self.irqview = irqview |
| |
| vbox = window.get_child().get_child() |
| socket_ids = [ int(id) for id in self.cpus.sockets.keys() ] |
| socket_ids.sort() |
| |
| self.nr_sockets = len(socket_ids) |
| if self.nr_sockets > 1: |
| columns = math.ceil(math.sqrt(self.nr_sockets)) |
| rows = math.ceil(self.nr_sockets / columns) |
| box = gtk.HBox() |
| vbox.pack_start(box, True, True) |
| else: |
| box = vbox |
| |
| column = 1 |
| for socket_id in socket_ids: |
| frame = cpu_socket_frame(socket_id, |
| self.cpus.sockets[str(socket_id)], |
| self) |
| box.pack_start(frame, False, False) |
| self.socket_frames[socket_id] = frame |
| if self.nr_sockets > 1: |
| if column == columns: |
| box = gtk.HBox() |
| vbox.pack_start(box, True, True) |
| column = 1 |
| else: |
| column += 1 |
| |
| window.show_all() |
| |
| self.cpus_filtered = cpus_filtered |
| self.refresh() |
| |
| self.previous_pid_affinities = None |
| self.previous_irq_affinities = None |
| |
| req = frame.size_request() |
| # FIXME: what is the slack we have |
| # to add to every row and column? |
| width = req[0] + 16 |
| height = req[1] + 20 |
| if self.nr_sockets > 1: |
| width *= columns |
| height *= rows |
| vpaned.set_position(int(height)) |
| hpaned.set_position(int(width)) |
| |
| self.timer = gobject.timeout_add(3000, self.refresh) |
| |
| def isolate_cpus(self, cpus): |
| self.previous_pid_affinities, \ |
| self.previous_irq_affinities = tuna.isolate_cpus(cpus, self.cpus.nr_cpus) |
| |
| if self.previous_pid_affinities: |
| self.procview.refresh() |
| |
| if self.previous_irq_affinities: |
| self.irqview.refresh() |
| |
| def include_cpus(self, cpus): |
| self.previous_pid_affinities, \ |
| self.previous_irq_affinities = tuna.include_cpus(cpus, self.cpus.nr_cpus) |
| |
| if self.previous_pid_affinities: |
| self.procview.refresh() |
| |
| if self.previous_irq_affinities: |
| self.irqview.refresh() |
| |
| def restore_cpu(self): |
| if not (self.previous_pid_affinities or \ |
| self.previous_irq_affinities): |
| return |
| affinities = self.previous_pid_affinities |
| for pid in affinities.keys(): |
| try: |
| schedutils.set_affinity(pid, affinities[pid]) |
| except: |
| pass |
| |
| affinities = self.previous_irq_affinities |
| for irq in affinities.keys(): |
| tuna.set_irq_affinity(int(irq), |
| procfs.hexbitmask(affinities[irq], |
| self.cpus.nr_cpus)) |
| |
| self.previous_pid_affinities = None |
| self.previous_irq_affinities = None |
| |
| def toggle_mask_cpu(self, cpu, enabled): |
| if enabled: |
| if cpu in self.cpus_filtered: |
| self.cpus_filtered.remove(cpu) |
| else: |
| if cpu not in self.cpus_filtered: |
| self.cpus_filtered.append(cpu) |
| |
| self.procview.toggle_mask_cpu(cpu, enabled) |
| self.irqview.toggle_mask_cpu(cpu, enabled) |
| |
| def refresh(self): |
| self.cpustats.reload() |
| for frame in self.socket_frames.keys(): |
| self.socket_frames[frame].refresh() |
| return True |