[gimp/gimp-2-10] tools: in performance-log-viewer.py, add thread filter to profile
- From: Ell <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/gimp-2-10] tools: in performance-log-viewer.py, add thread filter to profile
- Date: Sun, 23 Sep 2018 04:54:00 +0000 (UTC)
commit b5d628bec2614d62fc7ef90e43e3855820ffc254
Author: Ell <ell_se yahoo com>
Date: Sun Sep 23 00:44:50 2018 -0400
tools: in performance-log-viewer.py, add thread filter to profile
In the performance-log viewer, add an option to filter which
threads, and which states of each thread, are included in the
profile. By default, all threads in the RUNNING state are
included.
(cherry picked from commit 3f630378b0697b37d305165c0cc31065a65133c2)
tools/performance-log-viewer.py | 176 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 175 insertions(+), 1 deletion(-)
---
diff --git a/tools/performance-log-viewer.py b/tools/performance-log-viewer.py
index 791fb6bc28..3cfbaa452e 100755
--- a/tools/performance-log-viewer.py
+++ b/tools/performance-log-viewer.py
@@ -1859,6 +1859,144 @@ class BacktraceViewer (Gtk.Box):
return False
class ProfileViewer (Gtk.ScrolledWindow):
+ class ThreadFilter (Gtk.TreeView):
+ class Store (Gtk.ListStore):
+ VISIBLE = 0
+ ID = 1
+ NAME = 2
+ STATE = {list (ThreadState)[i]: 3 + i
+ for i in range (len (ThreadState))}
+
+ def __init__ (self):
+ Gtk.ListStore.__init__ (self,
+ bool, int, str,
+ *(len (self.STATE) * (bool,)))
+
+ threads = list ({thread.id
+ for sample in samples
+ for thread in sample.backtrace or ()})
+ threads.sort ()
+
+ states = [state == ThreadState.RUNNING for state in self.STATE]
+
+ for id in threads:
+ self.append ((False, id, None, *states))
+
+ def get_filter (self):
+ return {row[self.ID]: {state
+ for state, column in self.STATE.items ()
+ if row[column]}
+ for row in self}
+
+ def __init__ (self, *args, **kwargs):
+ Gtk.TreeView.__init__ (self, *args, **kwargs)
+
+ self.needs_update = True
+
+ store = self.Store ()
+ self.store = store
+
+ filter = Gtk.TreeModelFilter (child_model = store)
+ filter.set_visible_column (store.VISIBLE)
+ self.set_model (filter)
+
+ col = Gtk.TreeViewColumn (title = "ID")
+ self.append_column (col)
+
+ cell = Gtk.CellRendererText (xalign = 1)
+ col.pack_start (cell, False)
+ col.add_attribute (cell, "text", store.ID)
+
+ col = Gtk.TreeViewColumn (title = "Name")
+ self.append_column (col)
+
+ cell = Gtk.CellRendererText ()
+ col.pack_start (cell, False)
+ col.add_attribute (cell, "text", store.NAME)
+
+ for state in store.STATE:
+ col = Gtk.TreeViewColumn (title = str (state))
+ col.column = store.STATE[state]
+ self.append_column (col)
+ col.set_alignment (0.5)
+ col.set_clickable (True)
+
+ def col_clicked (col):
+ active = not all (row[col.column] for row in filter)
+
+ for row in filter:
+ row[col.column] = active
+
+ col.connect ("clicked", col_clicked)
+
+ cell = Gtk.CellRendererToggle ()
+ cell.column = store.STATE[state]
+ col.pack_start (cell, False)
+ col.add_attribute (cell, "active", store.STATE[state])
+
+ def cell_toggled (cell, path):
+ store[path][cell.column] = not cell.get_property ("active")
+
+ cell.connect ("toggled", cell_toggled)
+
+ selection.connect ("change-complete",
+ self.selection_change_complete)
+
+ def update (self):
+ if not self.needs_update:
+ return
+
+ self.needs_update = False
+
+ sel = selection.get_effective_selection ()
+
+ threads = {thread.id: thread.name
+ for i in sel
+ for thread in samples[i].backtrace or ()}
+
+ for row in self.store:
+ id = row[self.store.ID]
+
+ if id in threads:
+ row[self.store.VISIBLE] = True
+ row[self.store.NAME] = threads[id]
+ else:
+ row[self.store.VISIBLE] = False
+ row[self.store.NAME] = None
+
+ def do_map (self):
+ self.update ()
+
+ Gtk.TreeView.do_map (self)
+
+ def selection_change_complete (self, selection):
+ self.needs_update = True
+
+ if self.get_mapped ():
+ self.update ()
+
+ class ThreadPopover (Gtk.Popover):
+ def __init__ (self, *args, **kwargs):
+ Gtk.Popover.__init__ (self, *args, border_width = 4, **kwargs)
+
+ frame = Gtk.Frame (shadow_type = Gtk.ShadowType.IN)
+ self.add (frame)
+ frame.show ()
+
+ scrolled = Gtk.ScrolledWindow (
+ hscrollbar_policy = Gtk.PolicyType.NEVER,
+ vscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
+ propagate_natural_height = True,
+ max_content_height = 400
+ )
+ frame.add (scrolled)
+ scrolled.show ()
+
+ thread_filter = ProfileViewer.ThreadFilter ()
+ self.thread_filter = thread_filter
+ scrolled.add (thread_filter)
+ thread_filter.show ()
+
class Profile (Gtk.Box):
ProfileFrame = namedtuple ("ProfileFrame", ("sample", "stack", "i"))
@@ -1914,6 +2052,33 @@ class ProfileViewer (Gtk.ScrolledWindow):
header.show ()
if not id:
+ popover = ProfileViewer.ThreadPopover ()
+
+ thread_filter_store = popover.thread_filter.store
+
+ self.thread_filter_store = thread_filter_store
+ self.thread_filter = thread_filter_store.get_filter ()
+
+ button = Gtk.MenuButton (popover = popover)
+ header.pack_end (button)
+ button.show ()
+
+ button.connect ("toggled", self.thread_filter_button_toggled)
+
+ hbox = Gtk.Box (orientation = Gtk.Orientation.HORIZONTAL,
+ spacing = 4)
+ button.add (hbox)
+ hbox.show ()
+
+ label = Gtk.Label ("Threads")
+ hbox.pack_start (label, False, False, 0)
+ label.show ()
+
+ image = Gtk.Image.new_from_icon_name ("pan-down-symbolic",
+ Gtk.IconSize.BUTTON)
+ hbox.pack_start (image, False, False, 0)
+ image.show ()
+
button = Gtk.Button (tooltip_text = "Call-graph direction")
header.pack_end (button)
button.show ()
@@ -2020,7 +2185,7 @@ class ProfileViewer (Gtk.ScrolledWindow):
for i in selection.get_effective_selection ():
for thread in samples[i].backtrace or []:
- if thread.state == ThreadState.RUNNING:
+ if thread.state in self.thread_filter[thread.id]:
thread_frames = thread.frames
if self.direction == self.Direction.CALLERS:
@@ -2162,6 +2327,15 @@ class ProfileViewer (Gtk.ScrolledWindow):
self.emit ("subprofile-removed", subprofile)
+ def thread_filter_button_toggled (self, button):
+ if not button.get_active ():
+ thread_filter = self.thread_filter_store.get_filter ()
+
+ if thread_filter != self.thread_filter:
+ self.thread_filter = thread_filter
+
+ self.update ()
+
def direction_button_clicked (self, button):
if self.direction == self.Direction.CALLEES:
self.direction = self.Direction.CALLERS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]