mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-03 09:23:24 +00:00
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.
This commit is contained in:
parent
a7afbe13ec
commit
3f630378b0
1 changed files with 175 additions and 1 deletions
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue