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.
This commit is contained in:
Ell 2019-01-23 16:29:10 -05:00
parent 5a6548a4b6
commit dafb63fd66
2 changed files with 167 additions and 30 deletions

View File

@ -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.

View File

@ -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 ()