tools: in performance-log-viewer.py, 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 0f38709259
)
This commit is contained in:
@ -21,7 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
Usage: performance-log-viewer.py < infile
|
Usage: performance-log-viewer.py < infile
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import builtins, sys, math, statistics, functools, enum, re
|
import builtins, sys, os, math, statistics, functools, enum, re, subprocess
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
@ -72,6 +72,39 @@ def get_basename (path):
|
|||||||
|
|
||||||
return match[1] if match else path
|
return match[1] if match else path
|
||||||
|
|
||||||
|
search_path = list (filter (
|
||||||
|
bool,
|
||||||
|
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",
|
VariableType = namedtuple ("VariableType",
|
||||||
("parse", "format", "format_numeric"))
|
("parse", "format", "format_numeric"))
|
||||||
|
|
||||||
@ -1737,6 +1770,36 @@ class BacktraceViewer (Gtk.Box):
|
|||||||
def __init__ (self):
|
def __init__ (self):
|
||||||
Gtk.ListStore.__init__ (self, int, str, str, str, str, str, str)
|
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__ (
|
||||||
|
self,
|
||||||
|
*args,
|
||||||
|
icon_name = "text-x-generic-symbolic",
|
||||||
|
mode = Gtk.CellRendererMode.ACTIVATABLE,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
self.connect ("notify::file",
|
||||||
|
lambda *args:
|
||||||
|
self.set_property ("visible", bool (self.file)))
|
||||||
|
|
||||||
|
def do_activate (self, event, widget, path, *args):
|
||||||
|
if self.file:
|
||||||
|
subprocess.call (
|
||||||
|
editor_command.format (
|
||||||
|
file = "\"%s\"" % self.file.get_path (),
|
||||||
|
line = self.line
|
||||||
|
),
|
||||||
|
shell = True
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def __init__ (self, *args, **kwargs):
|
def __init__ (self, *args, **kwargs):
|
||||||
Gtk.Box.__init__ (self,
|
Gtk.Box.__init__ (self,
|
||||||
*args,
|
*args,
|
||||||
@ -1893,9 +1956,33 @@ class BacktraceViewer (Gtk.Box):
|
|||||||
col.pack_start (cell, False)
|
col.pack_start (cell, False)
|
||||||
col.add_attribute (cell, "text", self.FrameStore.LINE)
|
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 ()
|
col = Gtk.TreeViewColumn ()
|
||||||
|
self.tooltip_columns[col] = format_view_source_tooltip
|
||||||
tree.append_column (col)
|
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,
|
||||||
|
store.LINE))
|
||||||
|
|
||||||
selection.connect ("change-complete", self.selection_change_complete)
|
selection.connect ("change-complete", self.selection_change_complete)
|
||||||
|
|
||||||
@GObject.Property (type = bool, default = False)
|
@GObject.Property (type = bool, default = False)
|
||||||
@ -2030,7 +2117,7 @@ class BacktraceViewer (Gtk.Box):
|
|||||||
keyboard_mode)
|
keyboard_mode)
|
||||||
|
|
||||||
if hit:
|
if hit:
|
||||||
column = -1
|
column = None
|
||||||
|
|
||||||
if keyboard_mode:
|
if keyboard_mode:
|
||||||
cursor_path, cursor_col = tree.get_cursor ()
|
cursor_path, cursor_col = tree.get_cursor ()
|
||||||
@ -2047,8 +2134,13 @@ class BacktraceViewer (Gtk.Box):
|
|||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
if column >= 0:
|
if column is not None:
|
||||||
|
value = None
|
||||||
|
|
||||||
|
if type (column) == int:
|
||||||
value = model[iter][column]
|
value = model[iter][column]
|
||||||
|
else:
|
||||||
|
value = column (model[iter])
|
||||||
|
|
||||||
if value:
|
if value:
|
||||||
tooltip.set_text (str (value))
|
tooltip.set_text (str (value))
|
||||||
|
Reference in New Issue
Block a user