Commit 34c732df authored by Ell's avatar Ell

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.

(cherry picked from commit dafb63fd)
parent dcc48167
...@@ -18,16 +18,17 @@ report performance-related issues. ...@@ -18,16 +18,17 @@ report performance-related issues.
- [4.1.1. Selecting Samples](#411-selecting-samples) - [4.1.1. Selecting Samples](#411-selecting-samples)
- [4.2. Information Area](#42-information-area) - [4.2. Information Area](#42-information-area)
- [4.2.1. Information Page](#421-information-page) - [4.2.1. Information Page](#421-information-page)
- [4.2.2. Variables Page](#422-variables-page) - [4.2.2. Markers Page](#422-markers-page)
- [4.2.3. Backtrace Page](#423-backtrace-page) - [4.2.3. Variables Page](#423-variables-page)
- [4.2.3.1. Threads Pane](#4231-threads-pane) - [4.2.4. Backtrace Page](#424-backtrace-page)
- [4.2.3.2. Stack Pane](#4232-stack-pane) - [4.2.4.1. Threads Pane](#4241-threads-pane)
- [4.2.4. Profile Page](#424-profile-page) - [4.2.4.2. Stack Pane](#4242-stack-pane)
- [4.2.4.1. Root Column](#4241-root-column) - [4.2.5. Profile Page](#425-profile-page)
- [4.2.4.1.1. Thread Filter](#42411-thread-filter) - [4.2.5.1. Root Column](#4251-root-column)
- [4.2.4.1.2. Call-Graph Direction](#42412-call-graph-direction) - [4.2.5.1.1. Thread Filter](#42511-thread-filter)
- [4.2.4.2. Function Columns](#4242-function-columns) - [4.2.5.1.2. Call-Graph Direction](#42512-call-graph-direction)
- [4.2.4.3. Source Columns](#4243-source-columns) - [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. Selection Modifiers](#43-selection-modifiers)
- [4.3.1. Searching Samples](#431-searching-samples) - [4.3.1. Searching Samples](#431-searching-samples)
- [4.4. History Navigation](#44-history-navigation) - [4.4. History Navigation](#44-history-navigation)
...@@ -249,7 +250,17 @@ associated with any sample, including: ...@@ -249,7 +250,17 @@ associated with any sample, including:
The key/value lists are searchable by key name. 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 The *variables page* shows instrumentation-variable statistics for the current
selection. selection.
...@@ -267,13 +278,13 @@ standard deviation. ...@@ -267,13 +278,13 @@ standard deviation.
The variable list is searchable by variable name, and its tooltip shows the The variable list is searchable by variable name, and its tooltip shows the
variable descriptions. variable descriptions.
#### 4.2.3. Backtrace Page #### 4.2.4. Backtrace Page
The *backtrace page* shows the program backtrace at the current sample. The *backtrace page* shows the program backtrace at the current sample.
It is only available when a single sample is selected, in logs containing It is only available when a single sample is selected, in logs containing
backtraces. 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 *threads pane*, on the left side of the page, lists all active threads at
the time of the sample, displaying the following information: the time of the sample, displaying the following information:
...@@ -306,7 +317,7 @@ The thread list is searchable by thread name. ...@@ -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 Double-clicking on a thread selects all samples at which the thread is in the
running state. 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 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: call stack at the time of the sample, displaying the following information:
...@@ -350,7 +361,7 @@ The frame list is searchable by function name. ...@@ -350,7 +361,7 @@ The frame list is searchable by function name.
Double-clicking on a frame selects all samples at which the corresponding Double-clicking on a frame selects all samples at which the corresponding
function is present in the backtrace. 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 The *profile page* shows a fully context-sensitive *call graph*, annotated with
frequency information, for the current selection. frequency information, for the current selection.
...@@ -368,7 +379,7 @@ Each non-root column lists the direct *descendants* (*callers* or *callees*) of ...@@ -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 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. 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 *root column* of the call graph shows a list of all functions included in
the graph. the graph.
...@@ -398,14 +409,14 @@ Pressing *Escape* while the list has focus deselects the current item. ...@@ -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 The root-column header buttons allow controlling the structure of the call
graph: 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 The *Threads* button opens the *thread filter*, allowing control over which
threads, and which states of each thread, are included in the graph. threads, and which states of each thread, are included in the graph.
The thread filter lists all threads included in the current selection. The thread filter lists all threads included in the current selection.
Each thread is identified by ID and name, as described in 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 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; only call stacks during which the thread was in one of the active
states are included in the graph. states are included in the graph.
...@@ -413,7 +424,7 @@ Clicking on a thread-state column title toggles the entire column. ...@@ -413,7 +424,7 @@ Clicking on a thread-state column title toggles the entire column.
The thread list can be searched by thread name. 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 By default, the graph direction is *caller → callee*—the direct descendants of
each function are its callees. each function are its callees.
...@@ -421,7 +432,7 @@ The *Call-Graph Direction* button allows toggling the graph between the *caller ...@@ -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 → callee* direction, and the reverse *callee → caller* direction, in which the
direct descendants of each function are its callers. 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 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 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 ...@@ -472,7 +483,7 @@ corresponding to the current column, that is, all the samples whose call stacks
contribute to column. contribute to column.
The button's tooltip shows a textual description of the samples. 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 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 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: ...@@ -535,7 +546,7 @@ A number of sample-dependent variables and functions are provided:
thread name. thread name.
The optional `state` argument, if not `None`, may specify a thread state 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. 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 The argument may be a regular expression, which should fully match the
thread state. thread state.
......
...@@ -288,15 +288,16 @@ Frame = namedtuple ("Frame", ("id", "address", "info")) ...@@ -288,15 +288,16 @@ Frame = namedtuple ("Frame", ("id", "address", "info"))
Sample = namedtuple ("Sample", ("t", "vars", "markers", "backtrace")) Sample = namedtuple ("Sample", ("t", "vars", "markers", "backtrace"))
Marker = namedtuple ("Marker", ("id", "t", "description")) Marker = namedtuple ("Marker", ("id", "t", "description"))
samples = [] samples = []
markers = [] markers = []
last_marker = 0
for element in log.find ("samples"): for element in log.find ("samples"):
if element.tag == "sample": if element.tag == "sample":
sample = Sample ( sample = Sample (
t = int (element.get ("t")), t = int (element.get ("t")),
vars = {}, vars = {},
markers = markers, markers = markers[last_marker:],
backtrace = [] backtrace = []
) )
...@@ -347,7 +348,7 @@ for element in log.find ("samples"): ...@@ -347,7 +348,7 @@ for element in log.find ("samples"):
samples.append (sample) samples.append (sample)
markers = [] last_marker = len (markers)
elif element.tag == "marker": elif element.tag == "marker":
marker = Marker ( marker = Marker (
id = int (element.get ("id")), id = int (element.get ("id")),
...@@ -357,10 +358,8 @@ for element in log.find ("samples"): ...@@ -357,10 +358,8 @@ for element in log.find ("samples"):
markers.append (marker) markers.append (marker)
if samples and markers: if samples:
samples[-1].markers += markers samples[-1].markers.extend (markers[last_marker:])
markers = None
DELTA_SAME = __builtins__.object () DELTA_SAME = __builtins__.object ()
...@@ -1675,6 +1674,128 @@ class InformationViewer (Gtk.ScrolledWindow): ...@@ -1675,6 +1674,128 @@ class InformationViewer (Gtk.ScrolledWindow):
for element in info: for element in info:
add_element (element) 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 VariablesViewer (Gtk.ScrolledWindow):
class Store (Gtk.ListStore): class Store (Gtk.ListStore):
NAME = 0 NAME = 0
...@@ -3466,6 +3587,11 @@ class LogViewer (Gtk.Window): ...@@ -3466,6 +3587,11 @@ class LogViewer (Gtk.Window):
stack.add_titled (info_viewer, "information", "Information") stack.add_titled (info_viewer, "information", "Information")
info_viewer.show () info_viewer.show ()
if markers:
markers_viewer = MarkersViewer ()
stack.add_titled (markers_viewer, "markers", "Markers")
markers_viewer.show ()
vars_viewer = VariablesViewer () vars_viewer = VariablesViewer ()
stack.add_titled (vars_viewer, "variables", "Variables") stack.add_titled (vars_viewer, "variables", "Variables")
vars_viewer.show () vars_viewer.show ()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment