Commit 33269cc8 authored by Ell's avatar Ell
Browse files

tools: in, allow viewing source files ...

... in backtraces

In the performance-log viewer's backtrace viewer, show a document
icon next to stack frames with source-location information, whose
source file is found locally.  Clicking the icon opens the source
file in a text editor at the relevant line.

Two environment variables control this feature:

  - PERFORMANCE_LOG_VIEWER_PATH is a list of colon-separated
    directories in which to look for source files.  If this
    variable is undefined, the current directory is used.

  - PERFORMANCE_LOG_VIEWER_EDITOR is the command to use to launch
    the text editor, for editing a specific file at a specific
    line.  The special strings "{file}" and "{line}" are replaced
    with the filename and line-number, respectively.  If this
    variable is undefined, "xdg-open {file}" is used.

(cherry picked from commit 0f387092)
parent 788eb090
......@@ -21,7 +21,7 @@ along with this program. If not, see <>.
Usage: < infile
import builtins, sys, math, statistics, functools, enum, re
import builtins, sys, os, math, statistics, functools, enum, re, subprocess
from collections import namedtuple
from xml.etree import ElementTree
......@@ -72,6 +72,39 @@ def get_basename (path):
return match[1] if match else path
search_path = list (filter (
os.environ.get ("PERFORMANCE_LOG_VIEWER_PATH", ".").split (":")
editor_command = os.environ.get ("PERFORMANCE_LOG_VIEWER_EDITOR",
"xdg-open {file}")
editor_command += " &"
def find_file (filename):
filename = re.sub ("[\\\\/]", GLib.DIR_SEPARATOR_S, filename)
if GLib.path_is_absolute (filename):
file = Gio.File.new_for_path (filename)
if file.query_exists ():
return file
for path in search_path:
rest = filename
while rest:
file = Gio.File.new_for_path (GLib.build_filenamev ((path, rest)))
if file.query_exists ():
return file
sep = rest.find (GLib.DIR_SEPARATOR_S)
rest = rest[sep + 1:] if sep >= 0 else ""
return None
VariableType = namedtuple ("VariableType",
("parse", "format", "format_numeric"))
......@@ -1737,6 +1770,36 @@ class BacktraceViewer (Gtk.Box):
def __init__ (self):
Gtk.ListStore.__init__ (self, int, str, str, str, str, str, str)
class CellRendererViewSource (Gtk.CellRendererPixbuf):
file = GObject.Property (type = Gio.File, default = None)
line = GObject.Property (type = int, default = 0)
def __init__ (self, *args, **kwargs):
Gtk.CellRendererPixbuf.__init__ (
icon_name = "text-x-generic-symbolic",
mode = Gtk.CellRendererMode.ACTIVATABLE,
self.connect ("notify::file",
lambda *args:
self.set_property ("visible", bool (self.file)))
def do_activate (self, event, widget, path, *args):
if self.file: (
editor_command.format (
file = "\"%s\"" % self.file.get_path (),
line = self.line
shell = True
return True
return False
def __init__ (self, *args, **kwargs):
Gtk.Box.__init__ (self,
......@@ -1893,9 +1956,33 @@ class BacktraceViewer (Gtk.Box):
col.pack_start (cell, False)
col.add_attribute (cell, "text", self.FrameStore.LINE)
def format_view_source_col (tree_col, cell, model, iter, cols):
filename = model[iter][cols[0]] or None
line = model[iter][cols[1]] or "0"
cell.set_property ("file", filename and find_file (filename))
cell.set_property ("line", int (line))
def format_view_source_tooltip (row):
filename = row[store.SOURCE]
if filename:
file = find_file (filename)
if file:
return file.get_path ()
return None
col = Gtk.TreeViewColumn ()
self.tooltip_columns[col] = format_view_source_tooltip
tree.append_column (col)
cell = self.CellRendererViewSource (xalign = 0)
col.pack_start (cell, False)
col.set_cell_data_func (cell, format_view_source_col, (store.SOURCE,
selection.connect ("change-complete", self.selection_change_complete)
@GObject.Property (type = bool, default = False)
......@@ -2030,7 +2117,7 @@ class BacktraceViewer (Gtk.Box):
if hit:
column = -1
column = None
if keyboard_mode:
cursor_path, cursor_col = tree.get_cursor ()
......@@ -2047,8 +2134,13 @@ class BacktraceViewer (Gtk.Box):
if column >= 0:
value = model[iter][column]
if column is not None:
value = None
if type (column) == int:
value = model[iter][column]
value = column (model[iter])
if value:
tooltip.set_text (str (value))
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