[gimp] tools: in performance-log-viewer.py, add markers view



commit dafb63fd661d02779b2e9011b5916fb26a8df4b9
Author: Ell <ell_se yahoo com>
Date:   Wed Jan 23 16:29:10 2019 -0500

    tools: in performance-log-viewer.py, add markers view
    
    Add a "markers" page to the performance-log viewer, which lists
    the event markers contained in the log, and allows navigating
    between them.
    
    Update docs accordingly.

 devel-docs/performance-logs/performance-logs.md |  55 +++++----
 tools/performance-log-viewer.py                 | 142 ++++++++++++++++++++++--
 2 files changed, 167 insertions(+), 30 deletions(-)
---
diff --git a/devel-docs/performance-logs/performance-logs.md b/devel-docs/performance-logs/performance-logs.md
index efcf433be6..6efff2897e 100644
--- a/devel-docs/performance-logs/performance-logs.md
+++ b/devel-docs/performance-logs/performance-logs.md
@@ -18,16 +18,17 @@ report performance-related issues.
       - [4.1.1. Selecting Samples](#411-selecting-samples)
     - [4.2. Information Area](#42-information-area)
       - [4.2.1. Information Page](#421-information-page)
-      - [4.2.2. Variables Page](#422-variables-page)
-      - [4.2.3. Backtrace Page](#423-backtrace-page)
-        - [4.2.3.1. Threads Pane](#4231-threads-pane)
-        - [4.2.3.2. Stack Pane](#4232-stack-pane)
-      - [4.2.4. Profile Page](#424-profile-page)
-        - [4.2.4.1. Root Column](#4241-root-column)
-          - [4.2.4.1.1. Thread Filter](#42411-thread-filter)
-          - [4.2.4.1.2. Call-Graph Direction](#42412-call-graph-direction)
-        - [4.2.4.2. Function Columns](#4242-function-columns)
-        - [4.2.4.3. Source Columns](#4243-source-columns)
+      - [4.2.2. Markers Page](#422-markers-page)
+      - [4.2.3. Variables Page](#423-variables-page)
+      - [4.2.4. Backtrace Page](#424-backtrace-page)
+        - [4.2.4.1. Threads Pane](#4241-threads-pane)
+        - [4.2.4.2. Stack Pane](#4242-stack-pane)
+      - [4.2.5. Profile Page](#425-profile-page)
+        - [4.2.5.1. Root Column](#4251-root-column)
+          - [4.2.5.1.1. Thread Filter](#42511-thread-filter)
+          - [4.2.5.1.2. Call-Graph Direction](#42512-call-graph-direction)
+        - [4.2.5.2. Function Columns](#4252-function-columns)
+        - [4.2.5.3. Source Columns](#4253-source-columns)
     - [4.3. Selection Modifiers](#43-selection-modifiers)
       - [4.3.1. Searching Samples](#431-searching-samples)
     - [4.4. History Navigation](#44-history-navigation)
@@ -249,7 +250,17 @@ associated with any sample, including:
 
 The key/value lists are searchable by key name.
 
-#### 4.2.2. Variables Page
+#### 4.2.4. Markers Page
+
+The *markers page* lists the event markers contained in the log, displaying
+their number, relative time, and description.
+It is only present in logs containing event markers.
+
+If the current selection contains samples corresponding to any markers, the
+markers are selected in the markers-page list.  Conversely, if any markers are
+selected in the markers-page list, the corresponding samples are selected.
+
+#### 4.2.3. Variables Page
 
 The *variables page* shows instrumentation-variable statistics for the current
 selection.
@@ -267,13 +278,13 @@ standard deviation.
 The variable list is searchable by variable name, and its tooltip shows the
 variable descriptions.
 
-#### 4.2.3. Backtrace Page
+#### 4.2.4. Backtrace Page
 
 The *backtrace page* shows the program backtrace at the current sample.
 It is only available when a single sample is selected, in logs containing
 backtraces.
 
-##### 4.2.3.1. Threads Pane
+##### 4.2.4.1. Threads Pane
 
 The *threads pane*, on the left side of the page, lists all active threads at
 the time of the sample, displaying the following information:
@@ -306,7 +317,7 @@ The thread list is searchable by thread name.
 Double-clicking on a thread selects all samples at which the thread is in the
 running state.
 
-##### 4.2.3.2. Stack Pane
+##### 4.2.4.2. Stack Pane
 
 The *stack pane*, on the right side of the page, shows the selected thread's
 call stack at the time of the sample, displaying the following information:
@@ -350,7 +361,7 @@ The frame list is searchable by function name.
 Double-clicking on a frame selects all samples at which the corresponding
 function is present in the backtrace.
 
-#### 4.2.4. Profile Page
+#### 4.2.5. Profile Page
 
 The *profile page* shows a fully context-sensitive *call graph*, annotated with
 frequency information, for the current selection.
@@ -368,7 +379,7 @@ Each non-root column lists the direct *descendants* (*callers* or *callees*) of
 a given function; selecting a descendant opens a new column to the right of the
 current column, showing the descendants of the selected function, and so on.
 
-##### 4.2.4.1. Root Column
+##### 4.2.5.1. Root Column
 
 The *root column* of the call graph shows a list of all functions included in
 the graph.
@@ -398,14 +409,14 @@ Pressing *Escape* while the list has focus deselects the current item.
 The root-column header buttons allow controlling the structure of the call
 graph:
 
-###### 4.2.4.1.1. Thread Filter
+###### 4.2.5.1.1. Thread Filter
 
 The *Threads* button opens the *thread filter*, allowing control over which
 threads, and which states of each thread, are included in the graph.
 
 The thread filter lists all threads included in the current selection.
 Each thread is identified by ID and name, as described in
-[section *4.2.3.1*](#4231-threads-pane).
+[section *4.2.4.1*](#4241-threads-pane).
 Next to each thread is a row of toggles, corresponding to the different thread
 states; only call stacks during which the thread was in one of the active
 states are included in the graph.
@@ -413,7 +424,7 @@ Clicking on a thread-state column title toggles the entire column.
 
 The thread list can be searched by thread name.
 
-###### 4.2.4.1.2. Call-Graph Direction
+###### 4.2.5.1.2. Call-Graph Direction
 
 By default, the graph direction is *caller β†’ callee*β€”the direct descendants of
 each function are its callees.
@@ -421,7 +432,7 @@ The *Call-Graph Direction* button allows toggling the graph between the *caller
 β†’ callee* direction, and the reverse *callee β†’ caller* direction, in which the
 direct descendants of each function are its callers.
 
-##### 4.2.4.2. Function Columns
+##### 4.2.5.2. Function Columns
 
 When a function from the root column is selected, a new *function column* opens
 to the right of the root column, listing the direct descendants of the
@@ -472,7 +483,7 @@ corresponding to the current column, that is, all the samples whose call stacks
 contribute to column.
 The button's tooltip shows a textual description of the samples.
 
-##### 4.2.4.3. Source Columns
+##### 4.2.5.3. Source Columns
 
 When the *[Self]* item of a function column is selected, if the log contains
 source-location information for the function, and the corresponding source file
@@ -535,7 +546,7 @@ A number of sample-dependent variables and functions are provided:
     thread name.
 
     The optional `state` argument, if not `None`, may specify a thread state
-    (see [section *4.2.3.1*](#4231-threads-pane)).
+    (see [section *4.2.4.1*](#4241-threads-pane)).
     Only samples at which the thread is in the given state are matched.
     The argument may be a regular expression, which should fully match the
     thread state.
diff --git a/tools/performance-log-viewer.py b/tools/performance-log-viewer.py
index a0f78f2840..50cd7e8a7a 100755
--- a/tools/performance-log-viewer.py
+++ b/tools/performance-log-viewer.py
@@ -288,15 +288,16 @@ Frame  = namedtuple ("Frame",  ("id", "address", "info"))
 Sample = namedtuple ("Sample", ("t", "vars", "markers", "backtrace"))
 Marker = namedtuple ("Marker", ("id", "t", "description"))
 
-samples = []
-markers = []
+samples     = []
+markers     = []
+last_marker = 0
 
 for element in log.find ("samples"):
     if element.tag == "sample":
         sample = Sample (
             t         = int (element.get ("t")),
             vars      = {},
-            markers   = markers,
+            markers   = markers[last_marker:],
             backtrace = []
         )
 
@@ -347,7 +348,7 @@ for element in log.find ("samples"):
 
         samples.append (sample)
 
-        markers = []
+        last_marker = len (markers)
     elif element.tag == "marker":
         marker = Marker (
             id          = int (element.get ("id")),
@@ -357,10 +358,8 @@ for element in log.find ("samples"):
 
         markers.append (marker)
 
-if samples and markers:
-    samples[-1].markers += markers
-
-markers = None
+if samples:
+    samples[-1].markers.extend (markers[last_marker:])
 
 DELTA_SAME = __builtins__.object ()
 
@@ -1675,6 +1674,128 @@ class InformationViewer (Gtk.ScrolledWindow):
             for element in info:
                 add_element (element)
 
+class MarkersViewer (Gtk.ScrolledWindow):
+    class Store (Gtk.ListStore):
+        ID   = 0
+        TIME = 1
+        DESC = 2
+
+        def __init__ (self):
+            Gtk.ListStore.__init__ (self, int, int, str)
+
+            for marker in markers:
+                self.append ((marker.id, marker.t, marker.description))
+
+    def __init__ (self, *args, **kwargs):
+        Gtk.Box.__init__ (self,
+                          *args,
+                          hscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
+                          vscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
+                          **kwargs)
+
+        self.needs_update = True
+
+        store = self.Store ()
+        self.store = store
+
+        tree = Gtk.TreeView (model = store)
+        self.tree = tree
+        self.add (tree)
+        tree.show ()
+
+        tree.get_selection ().set_mode (Gtk.SelectionMode.MULTIPLE)
+
+        self.tree_selection_changed_handler = tree.get_selection ().connect (
+            "changed", self.tree_selection_changed
+        )
+
+        col = Gtk.TreeViewColumn (title = "#")
+        tree.append_column (col)
+        col.set_resizable (True)
+
+        cell = Gtk.CellRendererText (xalign = 1)
+        col.pack_start (cell, False)
+        col.add_attribute (cell, "text", store.ID)
+
+        def format_time_col (tree_col, cell, model, iter, col):
+            time = model[iter][col]
+
+            cell.set_property ("text", format_duration (time / 1000000))
+
+        col = Gtk.TreeViewColumn (title = "Time")
+        tree.append_column (col)
+        col.set_resizable (True)
+        col.set_alignment (0.5)
+
+        cell = Gtk.CellRendererText (xalign = 1)
+        col.pack_start (cell, False)
+        col.set_cell_data_func (cell, format_time_col, store.TIME)
+
+        col = Gtk.TreeViewColumn (title = "Description")
+        tree.append_column (col)
+        col.set_resizable (True)
+        col.set_alignment (0.5)
+
+        cell = Gtk.CellRendererText ()
+        col.pack_start (cell, False)
+        col.add_attribute (cell, "text", store.DESC)
+
+        col = Gtk.TreeViewColumn ()
+        tree.append_column (col)
+
+        selection.connect ("change-complete", self.selection_change_complete)
+
+    def update (self):
+        markers = set ()
+
+        if not self.needs_update:
+            return
+
+        self.needs_update = False
+
+        for i in selection.selection:
+            markers.update (marker.id for marker in samples[i].markers)
+
+        tree_sel = self.tree.get_selection ()
+
+        GObject.signal_handler_block (tree_sel,
+                                      self.tree_selection_changed_handler)
+
+        tree_sel.unselect_all ()
+
+        for row in self.store:
+            if row[self.store.ID] in markers:
+                tree_sel.select_iter (row.iter)
+
+        GObject.signal_handler_unblock (tree_sel,
+                                        self.tree_selection_changed_handler)
+
+    def do_map (self):
+        self.update ()
+
+        Gtk.ScrolledWindow.do_map (self)
+
+    def selection_change_complete (self, selection):
+        self.needs_update = True
+
+        if self.get_mapped ():
+            self.update ()
+
+    def tree_selection_changed (self, tree_sel):
+        sel = set ()
+
+        for row in self.store:
+            if tree_sel.iter_is_selected (row.iter):
+                id = row[self.store.ID]
+
+                for i in range (len (samples)):
+                    if any (marker.id == id for marker in samples[i].markers):
+                        sel.add (i)
+
+        selection.select (sel)
+
+        selection.change_complete ()
+
 class VariablesViewer (Gtk.ScrolledWindow):
     class Store (Gtk.ListStore):
         NAME        =  0
@@ -3466,6 +3587,11 @@ class LogViewer (Gtk.Window):
         stack.add_titled (info_viewer, "information", "Information")
         info_viewer.show ()
 
+        if markers:
+            markers_viewer = MarkersViewer ()
+            stack.add_titled (markers_viewer, "markers", "Markers")
+            markers_viewer.show ()
+
         vars_viewer = VariablesViewer ()
         stack.add_titled (vars_viewer, "variables", "Variables")
         vars_viewer.show ()


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]