tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
performance-log-viewer.py -- GIMP performance log viewer
|
|
|
|
|
Copyright (C) 2018 Ell
|
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Usage: performance-log-viewer.py < infile
|
|
|
|
|
"""
|
|
|
|
|
|
2018-09-30 20:54:34 +08:00
|
|
|
|
import builtins, sys, os, math, statistics, bisect, functools, enum, re, \
|
|
|
|
|
subprocess
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
from collections import namedtuple
|
|
|
|
|
from xml.etree import ElementTree
|
|
|
|
|
|
|
|
|
|
import gi
|
|
|
|
|
gi.require_version ("Gdk", "3.0")
|
|
|
|
|
gi.require_version ("Gtk", "3.0")
|
|
|
|
|
from gi.repository import GLib, GObject, Gio, Gdk, Gtk, Pango
|
|
|
|
|
|
|
|
|
|
def compose (head = None, *tail):
|
|
|
|
|
return (
|
|
|
|
|
lambda *args, **kwargs: head (compose (*tail) (*args, **kwargs))
|
|
|
|
|
) if tail else head or (lambda x: x)
|
|
|
|
|
|
|
|
|
|
def div (x, y):
|
|
|
|
|
return x / y if y else \
|
|
|
|
|
+math.inf if x > 0 else \
|
|
|
|
|
-math.inf if x < 0 else \
|
|
|
|
|
None
|
|
|
|
|
|
|
|
|
|
def format_float (x):
|
|
|
|
|
return "%g" % (round (100 * x) / 100)
|
|
|
|
|
|
|
|
|
|
def format_percentage (x, digits = 0):
|
2018-10-19 22:02:04 +08:00
|
|
|
|
return "%%.%df%%%%" % digits % (100 * x)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
def format_size (size):
|
|
|
|
|
return GLib.format_size_full (size, GLib.FormatSizeFlags.IEC_UNITS)
|
|
|
|
|
|
|
|
|
|
def format_duration (t):
|
|
|
|
|
return "%02d:%02d:%02d.%02d" % (int (t / 3600),
|
|
|
|
|
int (t / 60) % 60,
|
|
|
|
|
int (t % 60),
|
|
|
|
|
round (100 * t) % 100)
|
|
|
|
|
|
|
|
|
|
def format_color (color):
|
|
|
|
|
return "#%02x%02x%02x" % tuple (
|
|
|
|
|
map (lambda x: min (max (round (255 * x), 0), 255), color)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def is_bright_color (color):
|
|
|
|
|
return max (tuple (color)[0:3]) > 0.5
|
|
|
|
|
|
2018-10-19 22:02:04 +08:00
|
|
|
|
def blend_colors (color1, color2, amount):
|
|
|
|
|
color1 = tuple (color1)
|
|
|
|
|
color2 = tuple (color2)
|
|
|
|
|
|
|
|
|
|
a1 = color1[-1]
|
|
|
|
|
a2 = color2[-1]
|
|
|
|
|
|
|
|
|
|
a = (1 - amount) * a1 + amount * a2
|
|
|
|
|
|
|
|
|
|
return tuple (a and ((1 - amount) * a1 * c1 + amount * a2 * c2) / a
|
|
|
|
|
for c1, c2 in zip (color1[:-1], color2[:-1])) + (a,)
|
|
|
|
|
|
|
|
|
|
def rounded_rectangle (cr, x, y, width, height, radius):
|
|
|
|
|
radius = min (radius, width / 2, height / 2)
|
|
|
|
|
|
|
|
|
|
cr.arc (x + radius, y + radius, radius, -math.pi, -math.pi / 2)
|
|
|
|
|
cr.rel_line_to (width - 2 * radius, 0)
|
|
|
|
|
|
|
|
|
|
cr.arc (x + width - radius, y + radius, radius, -math.pi / 2, 0)
|
|
|
|
|
cr.rel_line_to (0, height - 2 * radius)
|
|
|
|
|
|
|
|
|
|
cr.arc (x + width - radius, y + height - radius, radius, 0, math.pi / 2)
|
|
|
|
|
cr.rel_line_to (-(width - 2 * radius), 0)
|
|
|
|
|
|
|
|
|
|
cr.arc (x + radius, y + height - radius, radius, math.pi / 2, math.pi)
|
|
|
|
|
cr.rel_line_to (0, -(height - 2 * radius))
|
|
|
|
|
|
|
|
|
|
cr.close_path ()
|
|
|
|
|
|
2018-09-21 20:23:59 +08:00
|
|
|
|
def get_basename (path):
|
|
|
|
|
match = re.fullmatch (".*[\\\\/](.+?)[\\\\/]?", path)
|
|
|
|
|
|
|
|
|
|
return match[1] if match else path
|
|
|
|
|
|
2018-09-30 16:51:19 +08:00
|
|
|
|
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):
|
2018-09-30 17:34:59 +08:00
|
|
|
|
def lookup (filename):
|
|
|
|
|
filename = re.sub ("[\\\\/]", GLib.DIR_SEPARATOR_S, filename)
|
2018-09-30 16:51:19 +08:00
|
|
|
|
|
2018-09-30 17:34:59 +08:00
|
|
|
|
if GLib.path_is_absolute (filename):
|
|
|
|
|
file = Gio.File.new_for_path (filename)
|
2018-09-30 16:51:19 +08:00
|
|
|
|
|
2018-09-30 17:34:59 +08:00
|
|
|
|
if file.query_exists ():
|
|
|
|
|
return file
|
2018-09-30 16:51:19 +08:00
|
|
|
|
|
2018-09-30 17:34:59 +08:00
|
|
|
|
for path in search_path:
|
|
|
|
|
rest = filename
|
2018-09-30 16:51:19 +08:00
|
|
|
|
|
2018-09-30 17:34:59 +08:00
|
|
|
|
while rest:
|
|
|
|
|
file = Gio.File.new_for_path (GLib.build_filenamev ((path, rest)))
|
2018-09-30 16:51:19 +08:00
|
|
|
|
|
2018-09-30 17:34:59 +08:00
|
|
|
|
if file.query_exists ():
|
|
|
|
|
return file
|
|
|
|
|
|
|
|
|
|
sep = rest.find (GLib.DIR_SEPARATOR_S)
|
|
|
|
|
|
|
|
|
|
rest = rest[sep + 1:] if sep >= 0 else ""
|
|
|
|
|
|
|
|
|
|
return None
|
2018-09-30 16:51:19 +08:00
|
|
|
|
|
2018-09-30 17:34:59 +08:00
|
|
|
|
if filename not in find_file.cache:
|
|
|
|
|
find_file.cache[filename] = lookup (filename)
|
2018-09-30 16:51:19 +08:00
|
|
|
|
|
2018-09-30 17:34:59 +08:00
|
|
|
|
return find_file.cache[filename]
|
2018-09-30 16:51:19 +08:00
|
|
|
|
|
2018-09-30 17:34:59 +08:00
|
|
|
|
find_file.cache = {}
|
2018-09-30 16:51:19 +08:00
|
|
|
|
|
2018-09-30 20:54:34 +08:00
|
|
|
|
def run_editor (file, line):
|
|
|
|
|
subprocess.call (editor_command.format (file = "\"%s\"" % file.get_path (),
|
|
|
|
|
line = line),
|
|
|
|
|
shell = True)
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
VariableType = namedtuple ("VariableType",
|
|
|
|
|
("parse", "format", "format_numeric"))
|
|
|
|
|
|
|
|
|
|
var_types = {
|
|
|
|
|
"boolean": VariableType (
|
|
|
|
|
parse = int,
|
|
|
|
|
format = compose (str, bool),
|
|
|
|
|
format_numeric = format_float
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
"integer": VariableType (
|
|
|
|
|
parse = int,
|
|
|
|
|
format = format_float,
|
|
|
|
|
format_numeric = None
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
"size": VariableType (
|
|
|
|
|
parse = int,
|
|
|
|
|
format = format_size,
|
|
|
|
|
format_numeric = None
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
"size-ratio": VariableType (
|
|
|
|
|
parse = lambda x: div (*map (int, x.split ("/"))),
|
|
|
|
|
format = format_percentage,
|
|
|
|
|
format_numeric = None
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
"int-ratio": VariableType (
|
|
|
|
|
parse = lambda x: div (*map (int, x.split (":"))),
|
|
|
|
|
format = lambda x: "%g:%g" % (
|
2018-09-27 13:09:46 +08:00
|
|
|
|
(0, 0) if math.isnan (x) else
|
|
|
|
|
(1, 0) if x == math.inf else
|
|
|
|
|
(-1, 0) if x == -math.inf else
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
(0, 1) if x == 0 else
|
2018-09-27 13:09:46 +08:00
|
|
|
|
(round (100 * x) / 100, 1) if abs (x) > 1 else
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
(1, round (100 / x) / 100)
|
|
|
|
|
),
|
|
|
|
|
format_numeric = None
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
"percentage": VariableType (
|
|
|
|
|
parse = float,
|
|
|
|
|
format = format_percentage,
|
|
|
|
|
format_numeric = None
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
"duration": VariableType (
|
|
|
|
|
parse = float,
|
|
|
|
|
format = format_duration,
|
|
|
|
|
format_numeric = None
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
"rate-of-change": VariableType (
|
|
|
|
|
parse = float,
|
|
|
|
|
format = lambda x: "%s/s" % format_size (x),
|
|
|
|
|
format_numeric = None
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
var_types = {
|
|
|
|
|
type: VariableType (
|
|
|
|
|
parse = parse,
|
|
|
|
|
format = lambda x, f = format: \
|
|
|
|
|
f (x) if x is not None else "N/A",
|
|
|
|
|
format_numeric = lambda x, f = format_numeric or format:
|
|
|
|
|
f (x) if x is not None else "N/A"
|
|
|
|
|
)
|
|
|
|
|
for type, (parse, format, format_numeric) in var_types.items ()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Read performance log from STDIN
|
|
|
|
|
log = ElementTree.fromstring (sys.stdin.buffer.read ())
|
|
|
|
|
|
|
|
|
|
Variable = namedtuple ("Variable", ("type", "color"))
|
|
|
|
|
Value = namedtuple ("Value", ("value", "raw"))
|
|
|
|
|
|
|
|
|
|
var_colors = [
|
|
|
|
|
(0.8, 0.4, 0.4),
|
|
|
|
|
(0.8, 0.6, 0.4),
|
|
|
|
|
(0.4, 0.8, 0.4),
|
|
|
|
|
(0.8, 0.8, 0.4),
|
|
|
|
|
(0.4, 0.4, 0.8),
|
|
|
|
|
(0.4, 0.8, 0.8),
|
|
|
|
|
(0.8, 0.4, 0.8),
|
|
|
|
|
(0.8, 0.8, 0.8)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
var_defs = {}
|
|
|
|
|
|
|
|
|
|
for var in log.find ("var-defs"):
|
|
|
|
|
color = var_colors[len (var_defs) % len (var_colors)]
|
|
|
|
|
|
|
|
|
|
var_defs[var.get ("name")] = Variable (var.get ("type"), color)
|
|
|
|
|
|
|
|
|
|
AddressInfo = namedtuple ("AddressInfo", ("id",
|
|
|
|
|
"name",
|
|
|
|
|
"object",
|
|
|
|
|
"symbol",
|
|
|
|
|
"offset",
|
|
|
|
|
"source",
|
|
|
|
|
"line"))
|
|
|
|
|
|
|
|
|
|
address_map = {}
|
|
|
|
|
|
|
|
|
|
if log.find ("address-map"):
|
|
|
|
|
for address in log.find ("address-map").iterfind ("address"):
|
|
|
|
|
value = int (address.get ("value"), 0)
|
|
|
|
|
|
|
|
|
|
object = address.find ("object").text
|
|
|
|
|
symbol = address.find ("symbol").text
|
|
|
|
|
base = address.find ("base").text
|
|
|
|
|
source = address.find ("source").text
|
|
|
|
|
line = address.find ("line").text
|
|
|
|
|
|
|
|
|
|
address_map[value] = AddressInfo (
|
|
|
|
|
id = int (base, 0) if base else value,
|
|
|
|
|
name = symbol or base or hex (value),
|
|
|
|
|
object = object,
|
|
|
|
|
symbol = symbol,
|
|
|
|
|
offset = value - int (base, 0) if base else None,
|
|
|
|
|
source = source,
|
|
|
|
|
line = int (line) if line else None
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
class ThreadState (enum.Enum):
|
|
|
|
|
SUSPENDED = enum.auto ()
|
|
|
|
|
RUNNING = enum.auto ()
|
|
|
|
|
|
|
|
|
|
def __str__ (self):
|
|
|
|
|
return {
|
|
|
|
|
ThreadState.SUSPENDED: "S",
|
|
|
|
|
ThreadState.RUNNING: "R"
|
|
|
|
|
}[self]
|
|
|
|
|
|
|
|
|
|
Thread = namedtuple ("Thread", ("id", "name", "state", "frames"))
|
|
|
|
|
Frame = namedtuple ("Frame", ("id", "address", "info"))
|
|
|
|
|
|
|
|
|
|
Sample = namedtuple ("Sample", ("t", "vars", "markers", "backtrace"))
|
|
|
|
|
Marker = namedtuple ("Marker", ("id", "t", "description"))
|
|
|
|
|
|
|
|
|
|
samples = []
|
|
|
|
|
markers = []
|
|
|
|
|
|
|
|
|
|
for element in log.find ("samples"):
|
|
|
|
|
if element.tag == "sample":
|
|
|
|
|
sample = Sample (
|
|
|
|
|
t = int (element.get ("t")),
|
|
|
|
|
vars = {},
|
|
|
|
|
markers = markers,
|
|
|
|
|
backtrace = []
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for var in element.find ("vars"):
|
|
|
|
|
sample.vars[var.tag] = Value (
|
|
|
|
|
value = var_types[var_defs[var.tag].type].parse (var.text) \
|
|
|
|
|
if var.text else None,
|
|
|
|
|
raw = var.text.strip () if var.text else None
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if element.find ("backtrace"):
|
|
|
|
|
for thread in element.find ("backtrace").iterfind ("thread"):
|
|
|
|
|
id = thread.get ("id")
|
|
|
|
|
name = thread.get ("name")
|
|
|
|
|
running = thread.get ("running")
|
|
|
|
|
|
|
|
|
|
t = Thread (
|
|
|
|
|
id = int (id),
|
|
|
|
|
name = name,
|
|
|
|
|
state = ThreadState.RUNNING if running and int (running) \
|
|
|
|
|
else ThreadState.SUSPENDED,
|
|
|
|
|
frames = []
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for frame in thread.iterfind ("frame"):
|
|
|
|
|
address = int (frame.get ("address"), 0)
|
|
|
|
|
|
|
|
|
|
info = address_map.get (address, None)
|
|
|
|
|
|
|
|
|
|
if not info:
|
|
|
|
|
info = AddressInfo (
|
|
|
|
|
id = address,
|
|
|
|
|
name = hex (address),
|
|
|
|
|
object = None,
|
|
|
|
|
symbol = None,
|
|
|
|
|
offset = None,
|
|
|
|
|
source = None,
|
|
|
|
|
line = None
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
t.frames.append (Frame (
|
|
|
|
|
id = len (t.frames),
|
|
|
|
|
address = address,
|
|
|
|
|
info = info
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
sample.backtrace.append (t)
|
|
|
|
|
|
|
|
|
|
samples.append (sample)
|
|
|
|
|
|
|
|
|
|
markers = []
|
|
|
|
|
elif element.tag == "marker":
|
|
|
|
|
marker = Marker (
|
|
|
|
|
id = int (element.get ("id")),
|
|
|
|
|
t = int (element.get ("t")),
|
|
|
|
|
description = element.text.strip () if element.text else None
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
markers.append (marker)
|
|
|
|
|
|
|
|
|
|
if samples and markers:
|
|
|
|
|
samples[-1].markers += markers
|
|
|
|
|
|
|
|
|
|
markers = None
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
DELTA_SAME = __builtins__.object ()
|
|
|
|
|
|
|
|
|
|
def delta_encode (dest, src):
|
|
|
|
|
if type (dest) == type (src):
|
|
|
|
|
if dest == src:
|
|
|
|
|
return DELTA_SAME
|
|
|
|
|
elif type (dest) == tuple:
|
|
|
|
|
return tuple (delta_encode (d, s) for d, s in zip (dest, src)) + \
|
|
|
|
|
dest[len (src):]
|
|
|
|
|
|
|
|
|
|
return dest
|
|
|
|
|
|
|
|
|
|
def delta_decode (dest, src):
|
|
|
|
|
if dest == DELTA_SAME:
|
|
|
|
|
return src
|
|
|
|
|
elif type (dest) == type (src):
|
|
|
|
|
if type (dest) == tuple:
|
|
|
|
|
return tuple (delta_decode (d, s) for d, s in zip (dest, src)) + \
|
|
|
|
|
dest[len (src):]
|
|
|
|
|
|
|
|
|
|
return dest
|
|
|
|
|
|
|
|
|
|
class History (GObject.GObject):
|
|
|
|
|
Source = namedtuple ("HistorySource", ("get", "set"))
|
|
|
|
|
|
|
|
|
|
def __init__ (self):
|
|
|
|
|
GObject.GObject.__init__ (self)
|
|
|
|
|
|
|
|
|
|
self.sources = []
|
|
|
|
|
|
|
|
|
|
self.state = None
|
|
|
|
|
|
|
|
|
|
self.undo_stack = []
|
|
|
|
|
self.redo_stack = []
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.blocked = 0
|
2018-09-29 04:17:28 +08:00
|
|
|
|
self.n_groups = 0
|
|
|
|
|
self.pending_record = False
|
|
|
|
|
|
|
|
|
|
@GObject.Property (type = bool, default = False)
|
|
|
|
|
def can_undo (self):
|
|
|
|
|
return bool (self.undo_stack)
|
|
|
|
|
|
|
|
|
|
@GObject.Property (type = bool, default = False)
|
|
|
|
|
def can_redo (self):
|
|
|
|
|
return bool (self.redo_stack)
|
|
|
|
|
|
|
|
|
|
def add_source (self, get, set):
|
|
|
|
|
self.sources.append (self.Source (get, set))
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
def block (self):
|
|
|
|
|
self.blocked += 1
|
|
|
|
|
|
|
|
|
|
def unblock (self):
|
|
|
|
|
self.blocked -= 1
|
|
|
|
|
|
|
|
|
|
def is_blocked (self):
|
|
|
|
|
return self.blocked > 0
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
def start_group (self):
|
|
|
|
|
self.n_groups += 1
|
|
|
|
|
|
|
|
|
|
def end_group (self):
|
|
|
|
|
self.n_groups -= 1
|
|
|
|
|
|
|
|
|
|
if self.n_groups == 0 and self.pending_record:
|
|
|
|
|
self.record ()
|
|
|
|
|
|
|
|
|
|
def record (self):
|
2018-09-29 12:14:38 +08:00
|
|
|
|
if self.is_blocked ():
|
2018-09-29 04:17:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if self.n_groups == 0:
|
|
|
|
|
state = tuple (source.get () for source in self.sources)
|
|
|
|
|
|
|
|
|
|
if self.state is None:
|
|
|
|
|
self.state = state
|
|
|
|
|
else:
|
|
|
|
|
self.pending_record = False
|
|
|
|
|
|
|
|
|
|
delta = delta_encode (self.state, state)
|
|
|
|
|
|
|
|
|
|
if delta == DELTA_SAME:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.undo_stack.append (delta_encode (self.state, state))
|
|
|
|
|
self.redo_stack = []
|
|
|
|
|
|
|
|
|
|
self.state = state
|
|
|
|
|
|
|
|
|
|
self.notify ("can-undo")
|
|
|
|
|
self.notify ("can-redo")
|
|
|
|
|
else:
|
|
|
|
|
self.pending_record = True
|
|
|
|
|
|
2018-09-30 20:54:34 +08:00
|
|
|
|
def update (self):
|
|
|
|
|
if self.is_blocked ():
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if self.n_groups == 0:
|
|
|
|
|
state = tuple (source.get () for source in self.sources)
|
|
|
|
|
|
|
|
|
|
for stack in self.undo_stack, self.redo_stack:
|
|
|
|
|
if stack:
|
|
|
|
|
stack[-1] = delta_encode (delta_decode (stack[-1],
|
|
|
|
|
self.state),
|
|
|
|
|
state)
|
|
|
|
|
|
|
|
|
|
self.state = state
|
|
|
|
|
else:
|
|
|
|
|
self.pending_record = True
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
def move (self, src, dest):
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.block ()
|
2018-09-29 04:17:28 +08:00
|
|
|
|
|
|
|
|
|
state = src.pop ()
|
|
|
|
|
|
|
|
|
|
for source, substate, prev_substate in \
|
|
|
|
|
zip (self.sources, self.state, state):
|
|
|
|
|
if prev_substate != DELTA_SAME:
|
|
|
|
|
source.set (delta_decode (prev_substate, substate))
|
|
|
|
|
|
|
|
|
|
state = delta_decode (state, self.state)
|
|
|
|
|
|
|
|
|
|
dest.append (delta_encode (self.state, state))
|
|
|
|
|
|
|
|
|
|
self.state = state
|
|
|
|
|
|
|
|
|
|
self.notify ("can-undo")
|
|
|
|
|
self.notify ("can-redo")
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.unblock ()
|
2018-09-29 04:17:28 +08:00
|
|
|
|
|
|
|
|
|
def undo (self):
|
|
|
|
|
self.move (self.undo_stack, self.redo_stack)
|
|
|
|
|
|
|
|
|
|
def redo (self):
|
|
|
|
|
self.move (self.redo_stack, self.undo_stack)
|
|
|
|
|
|
|
|
|
|
history = History ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
class SelectionOp (enum.Enum):
|
|
|
|
|
REPLACE = enum.auto ()
|
|
|
|
|
ADD = enum.auto ()
|
|
|
|
|
SUBTRACT = enum.auto ()
|
|
|
|
|
INTERSECT = enum.auto ()
|
|
|
|
|
XOR = enum.auto ()
|
|
|
|
|
|
|
|
|
|
class Selection (GObject.GObject):
|
|
|
|
|
__gsignals__ = {
|
|
|
|
|
"changed": (GObject.SIGNAL_RUN_FIRST, None, ()),
|
|
|
|
|
"change-complete": (GObject.SIGNAL_RUN_FIRST, None, ()),
|
|
|
|
|
"highlight-changed": (GObject.SIGNAL_RUN_FIRST, None, ())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def __init__ (self, iter = ()):
|
|
|
|
|
GObject.GObject.__init__ (self)
|
|
|
|
|
|
|
|
|
|
self.selection = set (iter)
|
|
|
|
|
self.highlight = None
|
|
|
|
|
self.cursor = None
|
|
|
|
|
self.cursor_dir = 0
|
|
|
|
|
|
|
|
|
|
self.pending_change_completion = False
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
def __eq__ (self, other):
|
|
|
|
|
return type (self) == type (other) and \
|
|
|
|
|
self.selection == other.selection and \
|
|
|
|
|
self.cursor == other.cursor and \
|
|
|
|
|
self.cursor_dir == other.cursor_dir
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
def __str__ (self):
|
|
|
|
|
n_sel = len (self.selection)
|
|
|
|
|
|
|
|
|
|
if n_sel == 0 or n_sel == len (samples):
|
|
|
|
|
return "All Samples"
|
|
|
|
|
elif n_sel == 1:
|
|
|
|
|
i, = self.selection
|
|
|
|
|
|
|
|
|
|
return "Sample %d" % i
|
|
|
|
|
else:
|
|
|
|
|
sel = list (self.selection)
|
|
|
|
|
|
|
|
|
|
sel.sort ()
|
|
|
|
|
|
|
|
|
|
if all (sel[i] + 1 == sel[i + 1] for i in range (n_sel - 1)):
|
|
|
|
|
return "Samples %d–%d" % (sel[0], sel[-1])
|
|
|
|
|
else:
|
|
|
|
|
return "%d Samples" % n_sel
|
|
|
|
|
|
|
|
|
|
def copy (self):
|
|
|
|
|
selection = Selection ()
|
|
|
|
|
|
|
|
|
|
selection.highlight = self.highlight
|
|
|
|
|
selection.cursor = self.cursor
|
|
|
|
|
selection.cursor_dir = self.cursor_dir
|
|
|
|
|
selection.selection = self.selection.copy ()
|
|
|
|
|
|
|
|
|
|
return selection
|
|
|
|
|
|
|
|
|
|
def get_effective_selection (self):
|
|
|
|
|
if self.selection:
|
|
|
|
|
return self.selection
|
|
|
|
|
else:
|
|
|
|
|
return set (range (len (samples)))
|
|
|
|
|
|
|
|
|
|
def select (self, selection, op = SelectionOp.REPLACE):
|
|
|
|
|
if op == SelectionOp.REPLACE:
|
|
|
|
|
self.selection = selection.copy ()
|
|
|
|
|
elif op == SelectionOp.ADD:
|
|
|
|
|
self.selection |= selection
|
|
|
|
|
elif op == SelectionOp.SUBTRACT:
|
|
|
|
|
self.selection -= selection
|
|
|
|
|
elif op == SelectionOp.INTERSECT:
|
|
|
|
|
self.selection &= selection
|
|
|
|
|
elif op == SelectionOp.XOR:
|
|
|
|
|
self.selection.symmetric_difference_update (selection)
|
|
|
|
|
|
|
|
|
|
if len (self.selection) == 1:
|
|
|
|
|
(self.cursor,) = self.selection
|
|
|
|
|
else:
|
|
|
|
|
self.cursor = None
|
|
|
|
|
|
|
|
|
|
self.cursor_dir = 0
|
|
|
|
|
|
|
|
|
|
self.pending_change_completion = True
|
|
|
|
|
|
|
|
|
|
self.emit ("changed")
|
|
|
|
|
|
|
|
|
|
def select_range (self, first, last, op = SelectionOp.REPLACE):
|
|
|
|
|
if first > last:
|
|
|
|
|
temp = first
|
|
|
|
|
first = last
|
|
|
|
|
last = temp
|
|
|
|
|
|
|
|
|
|
first = max (first, 0)
|
|
|
|
|
last = min (last, len (samples) - 1)
|
|
|
|
|
|
|
|
|
|
if first <= last:
|
|
|
|
|
self.select (set (range (first, last + 1)), op)
|
|
|
|
|
else:
|
|
|
|
|
self.select (set (), op)
|
|
|
|
|
|
|
|
|
|
def change_complete (self):
|
|
|
|
|
if self.pending_change_completion:
|
|
|
|
|
self.pending_change_completion = False
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.start_group ()
|
|
|
|
|
|
|
|
|
|
history.record ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
self.emit ("change-complete")
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.end_group ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
def set_highlight (self, highlight):
|
|
|
|
|
self.highlight = highlight
|
|
|
|
|
|
|
|
|
|
self.emit ("highlight-changed")
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
def source_get (self):
|
|
|
|
|
return self.copy ()
|
|
|
|
|
|
|
|
|
|
def source_set (self, selection):
|
|
|
|
|
self.cursor = selection.cursor
|
|
|
|
|
self.cursor_dir = selection.cursor_dir
|
|
|
|
|
self.selection = selection.selection.copy ()
|
|
|
|
|
|
|
|
|
|
self.emit ("changed")
|
|
|
|
|
self.emit ("change-complete")
|
|
|
|
|
|
|
|
|
|
def add_history_source (self):
|
|
|
|
|
history.add_source (self.source_get, self.source_set)
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
selection = Selection ()
|
2018-09-29 04:17:28 +08:00
|
|
|
|
selection.add_history_source ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
class FindSamplesPopover (Gtk.Popover):
|
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.Popover.__init__ (self, *args, **kwargs)
|
|
|
|
|
|
|
|
|
|
vbox = Gtk.Box (orientation = Gtk.Orientation.VERTICAL,
|
|
|
|
|
border_width = 20,
|
|
|
|
|
spacing = 8)
|
|
|
|
|
self.add (vbox)
|
|
|
|
|
vbox.show ()
|
|
|
|
|
|
|
|
|
|
entry = Gtk.Entry (width_chars = 40,
|
|
|
|
|
placeholder_text = "Python expression")
|
|
|
|
|
self.entry = entry
|
|
|
|
|
vbox.pack_start (entry, False, False, 0)
|
|
|
|
|
entry.show ()
|
|
|
|
|
|
|
|
|
|
entry.connect ("activate", self.find_samples)
|
|
|
|
|
|
|
|
|
|
entry.get_buffer ().connect (
|
|
|
|
|
"notify::text",
|
|
|
|
|
lambda *args: self.entry.get_style_context ().remove_class ("error")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
frame = Gtk.Frame (label = "Selection",
|
|
|
|
|
shadow_type = Gtk.ShadowType.NONE)
|
|
|
|
|
vbox.pack_start (frame, False, False, 8)
|
|
|
|
|
frame.get_label_widget ().get_style_context ().add_class ("dim-label")
|
|
|
|
|
frame.show ()
|
|
|
|
|
|
|
|
|
|
vbox2 = Gtk.Box (orientation = Gtk.Orientation.VERTICAL,
|
|
|
|
|
border_width = 8,
|
|
|
|
|
spacing = 8)
|
|
|
|
|
frame.add (vbox2)
|
|
|
|
|
vbox2.show ()
|
|
|
|
|
|
|
|
|
|
self.radios = []
|
|
|
|
|
|
2018-09-21 14:23:37 +08:00
|
|
|
|
radio = Gtk.RadioButton.new_with_mnemonic (None, "_Replace")
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
self.radios.append ((radio, SelectionOp.REPLACE))
|
|
|
|
|
vbox2.pack_start (radio, False, False, 0)
|
|
|
|
|
radio.show ()
|
|
|
|
|
|
2018-09-21 14:23:37 +08:00
|
|
|
|
radio = Gtk.RadioButton.new_with_mnemonic_from_widget (radio, "_Add")
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
self.radios.append ((radio, SelectionOp.ADD))
|
|
|
|
|
vbox2.pack_start (radio, False, False, 0)
|
|
|
|
|
radio.show ()
|
|
|
|
|
|
2018-09-21 14:23:37 +08:00
|
|
|
|
radio = Gtk.RadioButton.new_with_mnemonic_from_widget (radio, "_Subtract")
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
self.radios.append ((radio, SelectionOp.SUBTRACT))
|
|
|
|
|
vbox2.pack_start (radio, False, False, 0)
|
|
|
|
|
radio.show ()
|
|
|
|
|
|
2018-09-21 14:23:37 +08:00
|
|
|
|
radio = Gtk.RadioButton.new_with_mnemonic_from_widget (radio, "_Intersect")
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
self.radios.append ((radio, SelectionOp.INTERSECT))
|
|
|
|
|
vbox2.pack_start (radio, False, False, 0)
|
|
|
|
|
radio.show ()
|
|
|
|
|
|
2018-09-21 14:23:37 +08:00
|
|
|
|
button = Gtk.Button.new_with_mnemonic ("_Find")
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
vbox.pack_start (button, False, False, 0)
|
2018-09-21 14:23:37 +08:00
|
|
|
|
button.set_halign (Gtk.Align.CENTER)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button.connect ("clicked", self.find_samples)
|
|
|
|
|
|
|
|
|
|
def do_hide (self):
|
|
|
|
|
self.entry.set_text ("")
|
|
|
|
|
self.entry.get_style_context ().remove_class ("error")
|
|
|
|
|
|
|
|
|
|
Gtk.Popover.do_hide (self)
|
|
|
|
|
|
|
|
|
|
def find_samples (self, *args):
|
|
|
|
|
def var_name (var):
|
|
|
|
|
return var.replace ("-", "_")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
f = eval ("lambda thread, function, %s: %s" % (
|
|
|
|
|
", ".join (map (var_name, var_defs)),
|
|
|
|
|
self.entry.get_text ()))
|
|
|
|
|
except:
|
|
|
|
|
self.entry.get_style_context ().add_class ("error")
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
sel = set ()
|
|
|
|
|
|
|
|
|
|
for i in range (len (samples)):
|
|
|
|
|
try:
|
|
|
|
|
def thread (id, state = None):
|
|
|
|
|
for thread in samples[i].backtrace or []:
|
|
|
|
|
if (type (id) == int and id == thread.id) or \
|
2018-09-21 14:23:37 +08:00
|
|
|
|
(type (id) == str and thread.name and \
|
|
|
|
|
re.fullmatch (id, thread.name)):
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
if state is None or \
|
|
|
|
|
re.fullmatch (state, str (thread.state)):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def function (name, exclusive = False):
|
|
|
|
|
for thread in samples[i].backtrace or []:
|
|
|
|
|
if exclusive:
|
|
|
|
|
if re.fullmatch (name, thread.frames[0].info.name):
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
for frame in thread.frames:
|
|
|
|
|
if re.fullmatch (name, frame.info.name):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
if f (thread, function, **{
|
|
|
|
|
var_name (var): value.value
|
|
|
|
|
for var, value in samples[i].vars.items ()
|
|
|
|
|
}):
|
|
|
|
|
sel.add (i)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
op = [op for radio, op in self.radios if radio.get_active ()][0]
|
|
|
|
|
|
|
|
|
|
selection.select (sel, op)
|
|
|
|
|
|
|
|
|
|
selection.change_complete ()
|
|
|
|
|
|
|
|
|
|
self.hide ()
|
|
|
|
|
|
|
|
|
|
class CellRendererColorToggle (Gtk.CellRendererToggle):
|
|
|
|
|
padding = 3
|
|
|
|
|
|
|
|
|
|
color = GObject.Property (type = Gdk.RGBA, default = Gdk.RGBA (0, 0, 0))
|
|
|
|
|
|
|
|
|
|
def do_render (self, cr, widget, background_area, cell_area, flags):
|
|
|
|
|
state = widget.get_state ()
|
|
|
|
|
style = widget.get_style_context ()
|
|
|
|
|
bg_color = style.get_background_color (state)
|
|
|
|
|
active = self.get_property ("active")
|
|
|
|
|
size = max (min (cell_area.width, cell_area.height) -
|
|
|
|
|
2 * self.padding,
|
|
|
|
|
0)
|
|
|
|
|
|
|
|
|
|
(r, g, b, a) = self.color
|
|
|
|
|
|
|
|
|
|
if is_bright_color (bg_color):
|
|
|
|
|
bg = (r, g, b)
|
|
|
|
|
fg = (0.75 * r, 0.75 * g, 0.75 * b)
|
|
|
|
|
else:
|
|
|
|
|
bg = (0.75 * r, 0.75 * g, 0.75 * b)
|
|
|
|
|
fg = (r, g, b)
|
|
|
|
|
|
|
|
|
|
x = cell_area.x + (cell_area.width - size) // 2
|
|
|
|
|
y = cell_area.y + (cell_area.height - size) // 2
|
|
|
|
|
|
|
|
|
|
cr.rectangle (x, y, size, size)
|
|
|
|
|
|
|
|
|
|
if active:
|
|
|
|
|
cr.set_source_rgba (*bg)
|
|
|
|
|
else:
|
|
|
|
|
Gdk.cairo_set_source_rgba (cr, bg_color)
|
|
|
|
|
|
|
|
|
|
cr.fill_preserve ()
|
|
|
|
|
|
|
|
|
|
cr.set_source_rgb (*fg)
|
|
|
|
|
cr.set_line_width (2)
|
|
|
|
|
cr.stroke ()
|
|
|
|
|
|
|
|
|
|
class VariableSet (Gtk.TreeView):
|
|
|
|
|
class Store (Gtk.ListStore):
|
|
|
|
|
NAME = 0
|
|
|
|
|
ACTIVE = 1
|
|
|
|
|
COLOR = 2
|
|
|
|
|
|
|
|
|
|
def __init__ (self):
|
|
|
|
|
Gtk.ListStore.__init__ (self, str, bool, Gdk.RGBA)
|
|
|
|
|
|
|
|
|
|
for var in var_defs:
|
|
|
|
|
i = self.append ((var, False, Gdk.RGBA (*var_defs[var].color)))
|
|
|
|
|
|
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.TreeView.__init__ (self, *args, headers_visible = False, **kwargs)
|
|
|
|
|
|
|
|
|
|
store = self.Store ()
|
|
|
|
|
self.store = store
|
|
|
|
|
self.set_model (store)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn ()
|
|
|
|
|
self.append_column (col)
|
|
|
|
|
|
|
|
|
|
cell = CellRendererColorToggle ()
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "active", store.ACTIVE)
|
|
|
|
|
col.add_attribute (cell, "color", store.COLOR)
|
|
|
|
|
|
|
|
|
|
cell.connect ("toggled", self.var_toggled)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText ()
|
|
|
|
|
col.pack_start (cell, True)
|
|
|
|
|
col.add_attribute (cell, "text", store.NAME)
|
|
|
|
|
|
|
|
|
|
def var_toggled (self, cell, path):
|
|
|
|
|
self.store[path][self.store.ACTIVE] = not cell.get_property ("active")
|
|
|
|
|
|
|
|
|
|
class SampleGraph (Gtk.DrawingArea):
|
|
|
|
|
def __init__ (self, model = None, *args, **kwargs):
|
|
|
|
|
Gtk.DrawingArea.__init__ (self, *args, can_focus = True, **kwargs)
|
|
|
|
|
|
|
|
|
|
self.style_widget = Gtk.Entry ()
|
|
|
|
|
|
|
|
|
|
self.model = model
|
|
|
|
|
|
|
|
|
|
if model:
|
|
|
|
|
model.connect ("row-changed", lambda *args: self.update ())
|
|
|
|
|
|
|
|
|
|
self.update ()
|
|
|
|
|
|
|
|
|
|
self.selection = None
|
|
|
|
|
self.sel = None
|
|
|
|
|
|
|
|
|
|
selection.connect ("changed", self.selection_changed)
|
|
|
|
|
selection.connect ("highlight-changed",
|
|
|
|
|
lambda selection: self.queue_draw ())
|
|
|
|
|
|
|
|
|
|
self.add_events (Gdk.EventMask.BUTTON_PRESS_MASK |
|
|
|
|
|
Gdk.EventMask.BUTTON_RELEASE_MASK |
|
|
|
|
|
Gdk.EventMask.KEY_PRESS_MASK |
|
|
|
|
|
Gdk.EventMask.KEY_RELEASE_MASK)
|
|
|
|
|
|
|
|
|
|
self.selection_changed (selection)
|
|
|
|
|
|
|
|
|
|
def sample_to_x (self, i):
|
|
|
|
|
if not samples:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
width = self.get_allocated_width ()
|
|
|
|
|
n_samples = max (len (samples), 2)
|
|
|
|
|
|
|
|
|
|
return 1 + (width - 3) * i / (n_samples - 1)
|
|
|
|
|
|
|
|
|
|
def sample_to_range (self, i):
|
|
|
|
|
if not samples:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
width = self.get_allocated_width ()
|
|
|
|
|
n_samples = max (len (samples), 2)
|
|
|
|
|
|
|
|
|
|
return (1 + math.floor ((width - 3) * (i - 0.5) / (n_samples - 1)),
|
|
|
|
|
1 + math.ceil ((width - 3) * (i + 0.5) / (n_samples - 1)))
|
|
|
|
|
|
|
|
|
|
def x_to_sample (self, x):
|
|
|
|
|
if not samples:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
width = max (self.get_allocated_width (), 4)
|
|
|
|
|
n_samples = len (samples)
|
|
|
|
|
|
|
|
|
|
return round ((n_samples - 1) * (x - 1) / (width - 3))
|
|
|
|
|
|
|
|
|
|
def update (self):
|
|
|
|
|
if not samples or not self.model:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.max_value = 1
|
|
|
|
|
|
|
|
|
|
for row in self.model:
|
|
|
|
|
var_name = row[self.model.NAME]
|
|
|
|
|
var_active = row[self.model.ACTIVE]
|
|
|
|
|
|
|
|
|
|
if var_active:
|
2018-09-30 03:37:55 +08:00
|
|
|
|
values = (sample.vars[var_name].value for sample in samples)
|
|
|
|
|
values = filter (lambda x: x is not None, values)
|
|
|
|
|
values = filter (math.isfinite, values)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
self.max_value = max (self.max_value, max (values))
|
|
|
|
|
except:
|
|
|
|
|
pass
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
self.queue_draw ()
|
|
|
|
|
|
|
|
|
|
def selection_changed (self, selection):
|
|
|
|
|
if selection.selection:
|
|
|
|
|
self.sel = list (selection.selection)
|
|
|
|
|
self.sel.sort ()
|
|
|
|
|
else:
|
|
|
|
|
self.sel = None
|
|
|
|
|
|
|
|
|
|
self.queue_draw ()
|
|
|
|
|
|
|
|
|
|
def do_get_preferred_width (self):
|
|
|
|
|
return (300, 300)
|
|
|
|
|
|
|
|
|
|
def do_get_preferred_height (self):
|
|
|
|
|
if self.model:
|
|
|
|
|
return (32, 256)
|
|
|
|
|
else:
|
|
|
|
|
return (16, 16)
|
|
|
|
|
|
|
|
|
|
def update_selection (self):
|
|
|
|
|
sel = self.selection.copy ()
|
|
|
|
|
|
|
|
|
|
i0 = self.selection_i0
|
|
|
|
|
i1 = self.selection_i1
|
|
|
|
|
|
|
|
|
|
if self.selection_range:
|
|
|
|
|
swap = i0 > i1
|
|
|
|
|
|
|
|
|
|
if swap:
|
|
|
|
|
temp = i0
|
|
|
|
|
i0 = i1
|
|
|
|
|
i1 = temp
|
|
|
|
|
|
|
|
|
|
n_samples = len (samples)
|
|
|
|
|
|
|
|
|
|
while i0 > 0 and not samples[i0 - 1].markers: i0 -= 1
|
|
|
|
|
while i1 < n_samples - 1 and not samples[i1 + 1].markers: i1 += 1
|
|
|
|
|
|
|
|
|
|
if swap:
|
|
|
|
|
temp = i0
|
|
|
|
|
i0 = i1
|
|
|
|
|
i1 = temp
|
|
|
|
|
|
|
|
|
|
sel.select_range (i0, i1, self.selection_op)
|
|
|
|
|
|
|
|
|
|
selection.select (sel.selection)
|
|
|
|
|
|
|
|
|
|
selection.cursor = i1
|
|
|
|
|
selection.cursor_dir = i1 - i0
|
|
|
|
|
|
|
|
|
|
def do_button_press_event (self, event):
|
|
|
|
|
self.grab_focus ()
|
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
if event.button == 1:
|
|
|
|
|
i = self.x_to_sample (event.x)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
if i is None:
|
|
|
|
|
return False
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
self.selection = selection.copy ()
|
|
|
|
|
self.selection_i0 = i
|
|
|
|
|
self.selection_i1 = i
|
|
|
|
|
self.selection_op = SelectionOp.REPLACE
|
|
|
|
|
self.selection_range = event.type != Gdk.EventType.BUTTON_PRESS
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
state = event.state & Gdk.ModifierType.MODIFIER_MASK
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
if state == Gdk.ModifierType.SHIFT_MASK:
|
|
|
|
|
self.selection_op = SelectionOp.ADD
|
|
|
|
|
elif state == Gdk.ModifierType.CONTROL_MASK:
|
|
|
|
|
self.selection_op = SelectionOp.SUBTRACT
|
|
|
|
|
elif state == (Gdk.ModifierType.SHIFT_MASK |
|
|
|
|
|
Gdk.ModifierType.CONTROL_MASK):
|
|
|
|
|
self.selection_op = SelectionOp.INTERSECT
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
self.update_selection ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
self.grab_add ()
|
|
|
|
|
elif event.button == 3:
|
|
|
|
|
selection.select (set ())
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
self.grab_add ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
def do_button_release_event (self, event):
|
2018-09-27 13:09:46 +08:00
|
|
|
|
if event.button == 1 or event.button == 3:
|
|
|
|
|
self.selection = None
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
selection.change_complete ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
self.grab_remove ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
return True
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
return False
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
def do_motion_notify_event (self, event):
|
|
|
|
|
i = self.x_to_sample (event.x)
|
|
|
|
|
|
|
|
|
|
selection.set_highlight (i)
|
|
|
|
|
|
|
|
|
|
if self.selection and i is not None:
|
|
|
|
|
self.selection_i1 = i
|
|
|
|
|
|
|
|
|
|
self.update_selection ()
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
2018-10-04 04:12:28 +08:00
|
|
|
|
def do_leave_notify_event (self, event):
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
selection.set_highlight (None)
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def do_key_press_event (self, event):
|
|
|
|
|
if event.keyval == Gdk.KEY_Left or \
|
|
|
|
|
event.keyval == Gdk.KEY_Right or \
|
|
|
|
|
event.keyval == Gdk.KEY_Home or \
|
|
|
|
|
event.keyval == Gdk.KEY_KP_Home or \
|
|
|
|
|
event.keyval == Gdk.KEY_End or \
|
|
|
|
|
event.keyval == Gdk.KEY_KP_End:
|
|
|
|
|
n_samples = len (samples)
|
|
|
|
|
|
|
|
|
|
state = event.state & Gdk.ModifierType.MODIFIER_MASK
|
|
|
|
|
|
|
|
|
|
op = SelectionOp.REPLACE
|
|
|
|
|
|
|
|
|
|
if state == Gdk.ModifierType.SHIFT_MASK:
|
|
|
|
|
op = SelectionOp.XOR
|
|
|
|
|
|
|
|
|
|
cursor = selection.cursor
|
|
|
|
|
cursor_dir = selection.cursor_dir
|
|
|
|
|
|
|
|
|
|
if event.keyval == Gdk.KEY_Left or \
|
|
|
|
|
event.keyval == Gdk.KEY_Home or \
|
|
|
|
|
event.keyval == Gdk.KEY_KP_Home:
|
|
|
|
|
if selection.cursor is not None:
|
|
|
|
|
if cursor_dir <= 0 or op == SelectionOp.REPLACE:
|
|
|
|
|
cursor -= 1
|
|
|
|
|
else:
|
|
|
|
|
cursor = n_samples - 1
|
|
|
|
|
|
|
|
|
|
cursor_dir = -1
|
|
|
|
|
elif event.keyval == Gdk.KEY_Right or \
|
|
|
|
|
event.keyval == Gdk.KEY_End or \
|
|
|
|
|
event.keyval == Gdk.KEY_KP_End:
|
|
|
|
|
if cursor is not None:
|
|
|
|
|
if cursor_dir >= 0 or op == SelectionOp.REPLACE:
|
|
|
|
|
cursor += 1
|
|
|
|
|
else:
|
|
|
|
|
cursor = 0
|
|
|
|
|
|
|
|
|
|
cursor_dir = +1
|
|
|
|
|
|
|
|
|
|
if cursor < 0 or cursor >= n_samples:
|
|
|
|
|
cursor = min (max (cursor, 0), n_samples - 1)
|
|
|
|
|
|
|
|
|
|
selection.cursor = cursor
|
|
|
|
|
selection.cursor_dir = cursor_dir
|
|
|
|
|
|
|
|
|
|
if op != SelectionOp.REPLACE:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
i0 = cursor
|
|
|
|
|
|
|
|
|
|
if event.keyval == Gdk.KEY_Home or \
|
|
|
|
|
event.keyval == Gdk.KEY_KP_Home:
|
|
|
|
|
cursor = 0
|
|
|
|
|
elif event.keyval == Gdk.KEY_End or \
|
|
|
|
|
event.keyval == Gdk.KEY_KP_End:
|
|
|
|
|
cursor = n_samples - 1
|
|
|
|
|
|
|
|
|
|
if op == SelectionOp.REPLACE:
|
|
|
|
|
i0 = cursor
|
|
|
|
|
|
|
|
|
|
selection.select_range (i0, cursor, op)
|
|
|
|
|
|
2018-09-27 13:09:46 +08:00
|
|
|
|
if len (selection.selection) > 1:
|
|
|
|
|
selection.cursor = cursor
|
|
|
|
|
selection.cursor_dir = cursor_dir
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
elif event.keyval == Gdk.KEY_Escape:
|
|
|
|
|
selection.select (set ())
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def do_key_release_event (self, event):
|
|
|
|
|
selection.change_complete ()
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def do_draw (self, cr):
|
|
|
|
|
state = self.get_state ()
|
|
|
|
|
style = (self.style_widget if self.model else
|
|
|
|
|
self).get_style_context ()
|
|
|
|
|
(width, height) = (self.get_allocated_width (),
|
|
|
|
|
self.get_allocated_height ())
|
|
|
|
|
|
|
|
|
|
fg_color = tuple (style.get_color (state))
|
|
|
|
|
grid_color = (*fg_color[:3], 0.25 * fg_color[3])
|
|
|
|
|
highlight_color = grid_color
|
|
|
|
|
selection_color = (*fg_color[:3], 0.15 * fg_color[3])
|
|
|
|
|
|
|
|
|
|
Gtk.render_background (style, cr, 0, 0, width, height)
|
|
|
|
|
|
|
|
|
|
if self.model:
|
2018-09-30 03:37:55 +08:00
|
|
|
|
max_value = self.max_value
|
|
|
|
|
vscale = (height - 4) / max_value
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
cr.save ()
|
|
|
|
|
|
|
|
|
|
cr.translate (0, height - 2)
|
|
|
|
|
cr.scale (1, -1)
|
|
|
|
|
|
|
|
|
|
first_sample = True
|
2018-09-30 03:37:55 +08:00
|
|
|
|
has_infinite = False
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
for row in self.model:
|
|
|
|
|
var_name = row[self.model.NAME]
|
|
|
|
|
var_active = row[self.model.ACTIVE]
|
|
|
|
|
|
|
|
|
|
if var_active:
|
|
|
|
|
is_boolean = var_defs[var_name].type == "boolean"
|
|
|
|
|
is_continuous = not is_boolean
|
|
|
|
|
|
|
|
|
|
for i in range (len (samples)):
|
|
|
|
|
value = samples[i].vars[var_name].value
|
|
|
|
|
|
|
|
|
|
if value is not None:
|
2018-09-30 03:37:55 +08:00
|
|
|
|
first_sample = False
|
|
|
|
|
|
|
|
|
|
if math.isinf (value):
|
|
|
|
|
first_sample = True
|
|
|
|
|
has_infinite = True
|
|
|
|
|
|
|
|
|
|
value = max_value
|
|
|
|
|
elif is_boolean:
|
|
|
|
|
value *= max_value
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
y = value * vscale
|
|
|
|
|
|
|
|
|
|
if is_continuous:
|
|
|
|
|
x = self.sample_to_x (i)
|
|
|
|
|
|
|
|
|
|
if first_sample:
|
|
|
|
|
cr.move_to (x, y)
|
|
|
|
|
else:
|
|
|
|
|
cr.line_to (x, y)
|
|
|
|
|
else:
|
|
|
|
|
(x0, x1) = self.sample_to_range (i)
|
|
|
|
|
|
|
|
|
|
if first_sample:
|
|
|
|
|
cr.move_to (x0, y)
|
|
|
|
|
else:
|
|
|
|
|
cr.line_to (x0, y)
|
|
|
|
|
|
|
|
|
|
cr.line_to (x1, y)
|
|
|
|
|
else:
|
|
|
|
|
first_sample = True
|
|
|
|
|
|
|
|
|
|
(r, g, b) = var_defs[var_name].color
|
|
|
|
|
|
|
|
|
|
cr.set_source_rgb (r, g, b)
|
|
|
|
|
cr.set_line_width (2)
|
|
|
|
|
cr.stroke ()
|
|
|
|
|
|
2018-09-30 03:37:55 +08:00
|
|
|
|
if has_infinite:
|
|
|
|
|
cr.save ()
|
|
|
|
|
|
|
|
|
|
for i in range (len (samples)):
|
|
|
|
|
value = samples[i].vars[var_name].value
|
|
|
|
|
|
|
|
|
|
if value is not None and math.isinf (value):
|
|
|
|
|
first_sample = False
|
|
|
|
|
|
|
|
|
|
y = max_value * vscale
|
|
|
|
|
|
|
|
|
|
if is_continuous:
|
|
|
|
|
x = self.sample_to_x (i)
|
|
|
|
|
|
|
|
|
|
if first_sample:
|
|
|
|
|
cr.move_to (x, y)
|
|
|
|
|
else:
|
|
|
|
|
cr.line_to (x, y)
|
|
|
|
|
else:
|
|
|
|
|
(x0, x1) = self.sample_to_range (i)
|
|
|
|
|
|
|
|
|
|
if first_sample:
|
|
|
|
|
cr.move_to (x0, y)
|
|
|
|
|
else:
|
|
|
|
|
cr.line_to (x0, y)
|
|
|
|
|
|
|
|
|
|
cr.line_to (x1, y)
|
|
|
|
|
else:
|
|
|
|
|
first_sample = True
|
|
|
|
|
|
|
|
|
|
cr.set_dash ([6, 6], 0)
|
|
|
|
|
cr.stroke ()
|
|
|
|
|
|
|
|
|
|
cr.restore ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
cr.restore ()
|
|
|
|
|
|
|
|
|
|
cr.set_line_width (1)
|
|
|
|
|
|
|
|
|
|
cr.set_source_rgba (*grid_color)
|
|
|
|
|
|
|
|
|
|
n_hgrid_lines = 4
|
|
|
|
|
n_vgrid_lines = 1
|
|
|
|
|
|
|
|
|
|
for i in range (n_hgrid_lines + 1):
|
|
|
|
|
cr.move_to (0, round (i * (height - 1) / n_hgrid_lines) + 0.5)
|
|
|
|
|
cr.rel_line_to (width, 0)
|
|
|
|
|
|
|
|
|
|
cr.stroke ()
|
|
|
|
|
|
|
|
|
|
for i in range (n_vgrid_lines + 1):
|
|
|
|
|
cr.move_to (round (i * (width - 1) / n_vgrid_lines) + 0.5, 0)
|
|
|
|
|
cr.rel_line_to (0, height)
|
|
|
|
|
|
|
|
|
|
cr.stroke ()
|
|
|
|
|
else:
|
|
|
|
|
for i in range (len (samples)):
|
|
|
|
|
if samples[i].markers:
|
|
|
|
|
(x0, x1) = self.sample_to_range (i)
|
|
|
|
|
|
|
|
|
|
cr.rectangle (x0, 0, x1 - x0, height)
|
|
|
|
|
|
|
|
|
|
cr.set_source_rgba (*fg_color)
|
|
|
|
|
cr.fill ()
|
|
|
|
|
|
|
|
|
|
if selection.highlight is not None:
|
|
|
|
|
(x0, x1) = self.sample_to_range (selection.highlight)
|
|
|
|
|
|
|
|
|
|
cr.rectangle (x0, 0, x1 - x0, height)
|
|
|
|
|
|
|
|
|
|
(r, g, b, a) = style.get_color (state)
|
|
|
|
|
|
|
|
|
|
cr.set_source_rgba (*highlight_color)
|
|
|
|
|
cr.fill ()
|
|
|
|
|
|
|
|
|
|
if self.sel:
|
|
|
|
|
def draw_selection ():
|
|
|
|
|
x0 = self.sample_to_range (i0)[0]
|
|
|
|
|
x1 = self.sample_to_range (i1)[1]
|
|
|
|
|
|
|
|
|
|
cr.rectangle (x0, 0, x1 - x0, height)
|
|
|
|
|
|
|
|
|
|
(r, g, b, a) = style.get_color (state)
|
|
|
|
|
|
|
|
|
|
cr.set_source_rgba (*selection_color)
|
|
|
|
|
cr.fill ()
|
|
|
|
|
|
|
|
|
|
i0 = None
|
|
|
|
|
|
|
|
|
|
for i in self.sel:
|
|
|
|
|
if i0 is None:
|
|
|
|
|
i0 = i
|
|
|
|
|
i1 = i
|
|
|
|
|
elif i == i1 + 1:
|
|
|
|
|
i1 = i
|
|
|
|
|
else:
|
|
|
|
|
draw_selection ()
|
|
|
|
|
|
|
|
|
|
i0 = i
|
|
|
|
|
i1 = i
|
|
|
|
|
|
|
|
|
|
if i0 is not None:
|
|
|
|
|
draw_selection ()
|
|
|
|
|
|
|
|
|
|
class SampleGraphList (Gtk.Box):
|
|
|
|
|
Item = namedtuple (
|
|
|
|
|
"SampleGraphListGraph", ("widget",
|
|
|
|
|
"model",
|
|
|
|
|
"remove_button",
|
|
|
|
|
"move_up_button",
|
|
|
|
|
"move_down_button")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.Box.__init__ (self,
|
|
|
|
|
*args,
|
|
|
|
|
orientation = Gtk.Orientation.VERTICAL,
|
|
|
|
|
**kwargs)
|
|
|
|
|
|
|
|
|
|
self.items = []
|
|
|
|
|
|
|
|
|
|
self.vset_size_group = Gtk.SizeGroup (
|
|
|
|
|
mode = Gtk.SizeGroupMode.HORIZONTAL
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
hbox = Gtk.Box (orientation = Gtk.Orientation.HORIZONTAL)
|
|
|
|
|
self.pack_start (hbox, False, False, 0)
|
|
|
|
|
hbox.show ()
|
|
|
|
|
|
|
|
|
|
empty = Gtk.DrawingArea ()
|
|
|
|
|
hbox.pack_start (empty, False, True, 0)
|
|
|
|
|
self.vset_size_group.add_widget (empty)
|
|
|
|
|
empty.show ()
|
|
|
|
|
|
|
|
|
|
graph = SampleGraph (has_tooltip = True)
|
|
|
|
|
hbox.pack_start (graph, True, True, 0)
|
|
|
|
|
graph.show ()
|
|
|
|
|
|
|
|
|
|
graph.connect ("query-tooltip", self.graph_query_tooltip)
|
|
|
|
|
|
|
|
|
|
separator = Gtk.Separator (orientation = Gtk.Orientation.HORIZONTAL)
|
|
|
|
|
self.pack_start (separator, False, False, 0)
|
|
|
|
|
separator.show ()
|
|
|
|
|
|
|
|
|
|
vbox = Gtk.Box (orientation = Gtk.Orientation.VERTICAL)
|
|
|
|
|
self.items_vbox = vbox
|
|
|
|
|
self.pack_start (vbox, False, False, 0)
|
|
|
|
|
vbox.show ()
|
|
|
|
|
|
|
|
|
|
self.add_item (0)
|
|
|
|
|
|
|
|
|
|
def update_items (self):
|
|
|
|
|
for widget in self.items_vbox.get_children ():
|
|
|
|
|
self.items_vbox.remove (widget)
|
|
|
|
|
|
|
|
|
|
i = 0
|
|
|
|
|
|
|
|
|
|
for item in self.items:
|
|
|
|
|
if i > 0:
|
|
|
|
|
separator = Gtk.Separator (
|
|
|
|
|
orientation = Gtk.Orientation.HORIZONTAL
|
|
|
|
|
)
|
|
|
|
|
self.items_vbox.pack_start (separator, False, False, 0)
|
|
|
|
|
separator.show ()
|
|
|
|
|
|
|
|
|
|
self.items_vbox.pack_start (item.widget, False, False, 0)
|
|
|
|
|
|
|
|
|
|
item.remove_button.set_sensitive (len (self.items) > 1)
|
|
|
|
|
item.move_up_button.set_sensitive (i > 0)
|
|
|
|
|
item.move_down_button.set_sensitive (i < len (self.items) - 1)
|
|
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
|
|
def add_item (self, i):
|
|
|
|
|
hbox = Gtk.Box (orientation = Gtk.Orientation.HORIZONTAL)
|
|
|
|
|
hbox.show ()
|
|
|
|
|
|
|
|
|
|
vbox = Gtk.Box (orientation = Gtk.Orientation.VERTICAL)
|
|
|
|
|
hbox.pack_start (vbox, False, True, 0)
|
|
|
|
|
self.vset_size_group.add_widget (vbox)
|
|
|
|
|
vbox.show ()
|
|
|
|
|
|
|
|
|
|
scroll = Gtk.ScrolledWindow (
|
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.NEVER,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.AUTOMATIC
|
|
|
|
|
)
|
|
|
|
|
vbox.pack_start (scroll, True, True, 0)
|
|
|
|
|
scroll.show ()
|
|
|
|
|
|
|
|
|
|
vset = VariableSet ()
|
|
|
|
|
scroll.add (vset)
|
|
|
|
|
vset.show ()
|
|
|
|
|
|
|
|
|
|
buttons = Gtk.ButtonBox (orientation = Gtk.Orientation.HORIZONTAL)
|
|
|
|
|
vbox.pack_start (buttons, False, False, 0)
|
|
|
|
|
buttons.set_layout (Gtk.ButtonBoxStyle.EXPAND)
|
|
|
|
|
buttons.show ()
|
|
|
|
|
|
|
|
|
|
button = Gtk.Button.new_from_icon_name ("list-add-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
add_button = button
|
|
|
|
|
buttons.add (button)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button = Gtk.Button.new_from_icon_name ("list-remove-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
remove_button = button
|
|
|
|
|
buttons.add (button)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button = Gtk.Button.new_from_icon_name ("go-up-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
move_up_button = button
|
|
|
|
|
buttons.add (button)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button = Gtk.Button.new_from_icon_name ("go-down-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
move_down_button = button
|
|
|
|
|
buttons.add (button)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
graph = SampleGraph (vset.get_model (), has_tooltip = True)
|
|
|
|
|
hbox.pack_start (graph, True, True, 0)
|
|
|
|
|
graph.show ()
|
|
|
|
|
|
|
|
|
|
graph.connect ("query-tooltip", self.graph_query_tooltip)
|
|
|
|
|
|
|
|
|
|
item = self.Item (
|
|
|
|
|
widget = hbox,
|
|
|
|
|
model = vset.get_model (),
|
|
|
|
|
remove_button = remove_button,
|
|
|
|
|
move_up_button = move_up_button,
|
|
|
|
|
move_down_button = move_down_button
|
|
|
|
|
)
|
|
|
|
|
self.items.insert (i, item)
|
|
|
|
|
|
|
|
|
|
add_button.connect ("clicked",
|
|
|
|
|
lambda *args: self.add_item (
|
|
|
|
|
self.items.index (item) + 1
|
|
|
|
|
))
|
|
|
|
|
remove_button.connect ("clicked",
|
|
|
|
|
lambda *args: self.remove_item (
|
|
|
|
|
self.items.index (item)
|
|
|
|
|
))
|
|
|
|
|
move_up_button.connect ("clicked",
|
|
|
|
|
lambda *args: self.move_item (
|
|
|
|
|
self.items.index (item),
|
|
|
|
|
-1
|
|
|
|
|
))
|
|
|
|
|
move_down_button.connect ("clicked",
|
|
|
|
|
lambda *args: self.move_item (
|
|
|
|
|
self.items.index (item),
|
|
|
|
|
+1
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
self.update_items ()
|
|
|
|
|
|
|
|
|
|
def remove_item (self, i):
|
|
|
|
|
del self.items[i]
|
|
|
|
|
|
|
|
|
|
self.update_items ()
|
|
|
|
|
|
|
|
|
|
def move_item (self, i, offset):
|
|
|
|
|
item = self.items[i]
|
|
|
|
|
del self.items[i]
|
|
|
|
|
self.items.insert (i + offset, item)
|
|
|
|
|
|
|
|
|
|
self.update_items ()
|
|
|
|
|
|
|
|
|
|
def graph_query_tooltip (self, graph, x, y, keyboard_mode, tooltip):
|
|
|
|
|
if keyboard_mode:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
i = graph.x_to_sample (x)
|
|
|
|
|
|
|
|
|
|
if i is None or i < 0 or i >= len (samples):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
grid = Gtk.Grid (column_spacing = 4)
|
|
|
|
|
tooltip.set_custom (grid)
|
|
|
|
|
grid.show ()
|
|
|
|
|
|
|
|
|
|
row = 0
|
|
|
|
|
|
|
|
|
|
label = Gtk.Label ()
|
|
|
|
|
grid.attach (label, 0, row, 2, 1)
|
|
|
|
|
label.set_markup ("<b>Sample %d</b>" % i)
|
|
|
|
|
label.show ()
|
|
|
|
|
|
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
|
|
label = Gtk.Label ()
|
|
|
|
|
grid.attach (label, 0, row, 2, 1)
|
|
|
|
|
label.set_markup ("<sub>%s</sub>" %
|
|
|
|
|
format_duration (samples[i].t / 1000000))
|
|
|
|
|
label.get_style_context ().add_class ("dim-label")
|
|
|
|
|
label.show ()
|
|
|
|
|
|
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
|
|
for item in self.items:
|
|
|
|
|
model = item.model
|
|
|
|
|
|
|
|
|
|
vars = tuple (var[model.NAME] for var in model if var[model.ACTIVE])
|
|
|
|
|
|
|
|
|
|
if not vars:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
separator = Gtk.Separator (orientation = Gtk.Orientation.HORIZONTAL)
|
|
|
|
|
grid.attach (separator, 0, row, 2, 1)
|
|
|
|
|
separator.show ()
|
|
|
|
|
|
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
|
|
for var in vars:
|
|
|
|
|
color = format_color (var_defs[var].color)
|
|
|
|
|
|
|
|
|
|
label = Gtk.Label (halign = Gtk.Align.START)
|
|
|
|
|
grid.attach (label, 0, row, 1, 1)
|
|
|
|
|
label.set_markup (
|
|
|
|
|
"<span color=\"%s\"><b>%s</b></span>" % (color, var)
|
|
|
|
|
)
|
|
|
|
|
label.show ()
|
|
|
|
|
|
|
|
|
|
value = samples[i].vars[var].value
|
|
|
|
|
text = var_types[var_defs[var].type].format (value) \
|
|
|
|
|
if value is not None else "N/A"
|
|
|
|
|
|
|
|
|
|
label = Gtk.Label (text, halign = Gtk.Align.END)
|
|
|
|
|
grid.attach (label, 1, row, 1, 1)
|
|
|
|
|
label.show ()
|
|
|
|
|
|
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
|
|
markers = samples[i].markers
|
|
|
|
|
|
|
|
|
|
if markers:
|
|
|
|
|
separator = Gtk.Separator (orientation = Gtk.Orientation.HORIZONTAL)
|
|
|
|
|
grid.attach (separator, 0, row, 2, 1)
|
|
|
|
|
separator.show ()
|
|
|
|
|
|
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
|
|
for marker in markers:
|
|
|
|
|
label = Gtk.Label (halign = Gtk.Align.START)
|
|
|
|
|
grid.attach (label, 0, row, 1, 1)
|
|
|
|
|
label.set_markup ("<b>Marker %d</b>" % (marker.id))
|
|
|
|
|
label.show ()
|
|
|
|
|
|
|
|
|
|
if marker.description:
|
|
|
|
|
label = Gtk.Label (marker.description,
|
|
|
|
|
halign = Gtk.Align.END)
|
|
|
|
|
grid.attach (label, 1, row, 1, 1)
|
|
|
|
|
label.show ()
|
|
|
|
|
|
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
class InformationViewer (Gtk.ScrolledWindow):
|
|
|
|
|
class Store (Gtk.ListStore):
|
|
|
|
|
NAME = 0
|
|
|
|
|
VALUE = 1
|
|
|
|
|
|
|
|
|
|
def __init__ (self):
|
|
|
|
|
Gtk.ListStore.__init__ (self, str, str)
|
|
|
|
|
|
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.ScrolledWindow.__init__ (
|
|
|
|
|
self,
|
|
|
|
|
*args,
|
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
|
|
|
|
|
**kwargs
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
vbox = Gtk.Box (orientation = Gtk.Orientation.VERTICAL,
|
|
|
|
|
border_width = 32,
|
|
|
|
|
margin_left = 64,
|
|
|
|
|
margin_right = 64,
|
|
|
|
|
spacing = 32)
|
|
|
|
|
self.add (vbox)
|
|
|
|
|
vbox.show ()
|
|
|
|
|
|
|
|
|
|
def add_element (element):
|
|
|
|
|
name = {
|
|
|
|
|
"params": "Log Parameters",
|
|
|
|
|
"gimp-version": "GIMP Version",
|
|
|
|
|
"env": "Environment",
|
|
|
|
|
"gegl-config": "GEGL Config"
|
|
|
|
|
}.get (element.tag, element.tag)
|
|
|
|
|
text = element.text.strip ()
|
|
|
|
|
n_items = len (element)
|
|
|
|
|
|
|
|
|
|
if not text and n_items == 0:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
vbox2 = Gtk.Box (orientation = Gtk.Orientation.VERTICAL,
|
|
|
|
|
spacing = 16)
|
|
|
|
|
vbox.pack_start (vbox2, False, False, 0)
|
|
|
|
|
vbox2.show ()
|
|
|
|
|
|
|
|
|
|
label = Gtk.Label (xalign = 0)
|
|
|
|
|
vbox2.pack_start (label, False, False, 0)
|
|
|
|
|
label.set_markup ("<b>%s</b>" % name)
|
|
|
|
|
label.show ()
|
|
|
|
|
|
|
|
|
|
frame = Gtk.Frame (shadow_type = Gtk.ShadowType.IN)
|
|
|
|
|
vbox2.pack_start (frame, False, False, 0)
|
|
|
|
|
frame.show ()
|
|
|
|
|
|
|
|
|
|
if text:
|
|
|
|
|
scrolled = Gtk.ScrolledWindow (
|
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
|
|
|
|
|
height_request = 400
|
|
|
|
|
)
|
|
|
|
|
frame.add (scrolled)
|
|
|
|
|
scrolled.show ()
|
|
|
|
|
|
|
|
|
|
text = Gtk.TextView (editable = False,
|
|
|
|
|
monospace = True,
|
|
|
|
|
wrap_mode = Gtk.WrapMode.WORD,
|
|
|
|
|
left_margin = 16,
|
|
|
|
|
right_margin = 16,
|
|
|
|
|
top_margin = 16,
|
|
|
|
|
bottom_margin = 16)
|
|
|
|
|
scrolled.add (text)
|
|
|
|
|
text.get_buffer ().set_text (element.text.strip (), -1)
|
|
|
|
|
text.show ()
|
|
|
|
|
else:
|
|
|
|
|
scrolled = Gtk.ScrolledWindow (
|
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.NEVER
|
|
|
|
|
)
|
|
|
|
|
frame.add (scrolled)
|
|
|
|
|
scrolled.show ()
|
|
|
|
|
|
|
|
|
|
store = self.Store ()
|
|
|
|
|
|
|
|
|
|
for item in element:
|
|
|
|
|
store.append ((item.tag, item.text.strip ()))
|
|
|
|
|
|
|
|
|
|
tree = Gtk.TreeView (model = store)
|
|
|
|
|
scrolled.add (tree)
|
|
|
|
|
tree.show ()
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Name")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText ()
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", store.NAME)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Value")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_alignment (0.5)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText (xalign = 1)
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", store.VALUE)
|
|
|
|
|
|
|
|
|
|
params = log.find ("params")
|
|
|
|
|
|
|
|
|
|
if params:
|
|
|
|
|
add_element (params)
|
|
|
|
|
|
|
|
|
|
info = log.find ("info")
|
|
|
|
|
|
|
|
|
|
if info:
|
|
|
|
|
for element in info:
|
|
|
|
|
add_element (element)
|
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
class VariablesViewer (Gtk.ScrolledWindow):
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
class Store (Gtk.ListStore):
|
|
|
|
|
NAME = 0
|
|
|
|
|
COLOR = 1
|
|
|
|
|
VALUE = 2
|
|
|
|
|
RAW = 3
|
|
|
|
|
MIN = 4
|
|
|
|
|
MAX = 5
|
|
|
|
|
MEDIAN = 6
|
|
|
|
|
MEAN = 7
|
|
|
|
|
STDEV = 8
|
|
|
|
|
LAST_COLUMN = 9
|
|
|
|
|
|
|
|
|
|
def __init__ (self):
|
|
|
|
|
n_stats = self.LAST_COLUMN - self.COLOR
|
|
|
|
|
|
|
|
|
|
Gtk.ListStore.__init__ (self,
|
|
|
|
|
*((str, Gdk.RGBA) + n_stats * (str,)))
|
|
|
|
|
enum.Enum.__init__ (self)
|
|
|
|
|
|
|
|
|
|
for var in var_defs:
|
|
|
|
|
i = self.append (((var,
|
|
|
|
|
Gdk.RGBA (*var_defs[var].color)) +
|
|
|
|
|
n_stats * ("",)))
|
|
|
|
|
|
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.Box.__init__ (self,
|
|
|
|
|
*args,
|
2018-09-23 11:23:28 +08:00
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
**kwargs)
|
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
self.needs_update = True
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
store = self.Store ()
|
|
|
|
|
self.store = store
|
|
|
|
|
|
|
|
|
|
tree = Gtk.TreeView (model = store)
|
2018-09-23 11:23:28 +08:00
|
|
|
|
self.add (tree)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
tree.show ()
|
|
|
|
|
|
|
|
|
|
self.single_sample_cols = []
|
|
|
|
|
self.multi_sample_cols = []
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Variable")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
|
|
|
|
cell = CellRendererColorToggle (active = True)
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "color", store.COLOR)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText ()
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", store.NAME)
|
|
|
|
|
|
|
|
|
|
def add_value_column (title, column, single_sample):
|
|
|
|
|
col = Gtk.TreeViewColumn (title = title)
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
col.set_alignment (0.5)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText (xalign = 1)
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", column)
|
|
|
|
|
|
|
|
|
|
if single_sample:
|
|
|
|
|
self.single_sample_cols.append (col)
|
|
|
|
|
else:
|
|
|
|
|
self.multi_sample_cols.append (col)
|
|
|
|
|
|
|
|
|
|
add_value_column ("Value", store.VALUE, True)
|
|
|
|
|
add_value_column ("Raw", store.RAW, True)
|
|
|
|
|
add_value_column ("Min", store.MIN, False)
|
|
|
|
|
add_value_column ("Max", store.MAX, False)
|
|
|
|
|
add_value_column ("Median", store.MEDIAN, False)
|
|
|
|
|
add_value_column ("Mean", store.MEAN, False)
|
|
|
|
|
add_value_column ("Std. Dev.", store.STDEV, False)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn ()
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
|
|
|
|
|
selection.connect ("change-complete", self.selection_change_complete)
|
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
def update (self):
|
|
|
|
|
if not self.needs_update:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.needs_update = False
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
sel = selection.get_effective_selection ()
|
|
|
|
|
n_sel = len (sel)
|
|
|
|
|
|
|
|
|
|
if n_sel == 1:
|
|
|
|
|
i, = sel
|
|
|
|
|
|
|
|
|
|
for row in self.store:
|
|
|
|
|
var_name = row[self.store.NAME]
|
|
|
|
|
|
|
|
|
|
var = samples[i].vars[var_name]
|
|
|
|
|
var_type = var_types[var_defs[var_name].type]
|
|
|
|
|
|
|
|
|
|
row[self.store.VALUE] = var_type.format (var.value)
|
|
|
|
|
row[self.store.RAW] = var.raw if var.raw is not None \
|
|
|
|
|
else "N/A"
|
|
|
|
|
else:
|
|
|
|
|
for row in self.store:
|
|
|
|
|
var_name = row[self.store.NAME]
|
|
|
|
|
|
|
|
|
|
var_type = var_types[var_defs[var_name].type]
|
|
|
|
|
|
|
|
|
|
vals = (samples[i].vars[var_name].value for i in sel)
|
|
|
|
|
vals = tuple (val for val in vals if val is not None)
|
|
|
|
|
|
|
|
|
|
if vals:
|
|
|
|
|
min_val = min (vals)
|
|
|
|
|
max_val = max (vals)
|
|
|
|
|
median = statistics.median (vals)
|
|
|
|
|
mean = statistics.mean (vals)
|
|
|
|
|
stdev = statistics.pstdev (vals, mean)
|
|
|
|
|
|
|
|
|
|
row[self.store.MIN] = var_type.format (min_val)
|
|
|
|
|
row[self.store.MAX] = var_type.format (max_val)
|
|
|
|
|
row[self.store.MEDIAN] = var_type.format (median)
|
|
|
|
|
row[self.store.MEAN] = var_type.format_numeric (mean)
|
|
|
|
|
row[self.store.STDEV] = var_type.format_numeric (stdev)
|
|
|
|
|
else:
|
|
|
|
|
row[self.store.MIN] = \
|
|
|
|
|
row[self.store.MAX] = \
|
|
|
|
|
row[self.store.MEDIAN] = \
|
|
|
|
|
row[self.store.MEAN] = \
|
|
|
|
|
row[self.store.STDEV] = var_type.format (None)
|
|
|
|
|
|
|
|
|
|
for col in self.single_sample_cols: col.set_visible (n_sel == 1)
|
|
|
|
|
for col in self.multi_sample_cols: col.set_visible (n_sel > 1)
|
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
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 ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
class BacktraceViewer (Gtk.Box):
|
|
|
|
|
class ThreadStore (Gtk.ListStore):
|
|
|
|
|
INDEX = 0
|
|
|
|
|
ID = 1
|
|
|
|
|
NAME = 2
|
|
|
|
|
STATE = 3
|
|
|
|
|
|
|
|
|
|
def __init__ (self):
|
|
|
|
|
Gtk.ListStore.__init__ (self, int, int, str, str)
|
|
|
|
|
|
|
|
|
|
class FrameStore (Gtk.ListStore):
|
|
|
|
|
ID = 0
|
|
|
|
|
ADDRESS = 1
|
|
|
|
|
OBJECT = 2
|
|
|
|
|
FUNCTION = 3
|
|
|
|
|
OFFSET = 4
|
|
|
|
|
SOURCE = 5
|
|
|
|
|
LINE = 6
|
|
|
|
|
|
|
|
|
|
def __init__ (self):
|
|
|
|
|
Gtk.ListStore.__init__ (self, int, str, str, str, str, str, str)
|
|
|
|
|
|
2018-09-30 16:51:19 +08:00
|
|
|
|
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:
|
2018-09-30 20:54:34 +08:00
|
|
|
|
run_editor (self.file, self.line)
|
2018-09-30 16:51:19 +08:00
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.Box.__init__ (self,
|
|
|
|
|
*args,
|
|
|
|
|
orientation = Gtk.Orientation.HORIZONTAL,
|
|
|
|
|
**kwargs)
|
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
self.needs_update = True
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
vbox = Gtk.Box (orientation = Gtk.Orientation.VERTICAL)
|
|
|
|
|
self.pack_start (vbox, False, False, 0)
|
|
|
|
|
vbox.show ()
|
|
|
|
|
|
|
|
|
|
header = Gtk.HeaderBar (title = "Threads", has_subtitle = False)
|
|
|
|
|
vbox.pack_start (header, False, False, 0)
|
|
|
|
|
header.show ()
|
|
|
|
|
|
|
|
|
|
scrolled = Gtk.ScrolledWindow (
|
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.NEVER,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.AUTOMATIC
|
|
|
|
|
)
|
|
|
|
|
vbox.pack_start (scrolled, True, True, 0)
|
|
|
|
|
scrolled.show ()
|
|
|
|
|
|
|
|
|
|
store = self.ThreadStore ()
|
|
|
|
|
self.thread_store = store
|
|
|
|
|
|
|
|
|
|
tree = Gtk.TreeView (model = store)
|
|
|
|
|
self.thread_tree = tree
|
|
|
|
|
scrolled.add (tree)
|
|
|
|
|
tree.set_search_column (store.NAME)
|
|
|
|
|
tree.show ()
|
|
|
|
|
|
|
|
|
|
tree.connect ("row-activated", self.threads_row_activated)
|
|
|
|
|
|
|
|
|
|
tree.get_selection ().connect ("changed",
|
|
|
|
|
self.threads_selection_changed)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "ID")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText (xalign = 1)
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", self.ThreadStore.ID)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Name")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText ()
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", self.ThreadStore.NAME)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "State")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText ()
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", self.ThreadStore.STATE)
|
|
|
|
|
|
|
|
|
|
separator = Gtk.Separator (orientation = Gtk.Orientation.VERTICAL)
|
|
|
|
|
self.pack_start (separator, False, False, 0)
|
|
|
|
|
separator.show ()
|
|
|
|
|
|
|
|
|
|
vbox = Gtk.Box (orientation = Gtk.Orientation.VERTICAL)
|
|
|
|
|
self.pack_start (vbox, True, True, 0)
|
|
|
|
|
vbox.show ()
|
|
|
|
|
|
|
|
|
|
header = Gtk.HeaderBar (title = "Stack", has_subtitle = False)
|
|
|
|
|
vbox.pack_start (header, False, False, 0)
|
|
|
|
|
header.show ()
|
|
|
|
|
|
|
|
|
|
scrolled = Gtk.ScrolledWindow (
|
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.AUTOMATIC
|
|
|
|
|
)
|
|
|
|
|
vbox.pack_start (scrolled, True, True, 0)
|
|
|
|
|
scrolled.show ()
|
|
|
|
|
|
|
|
|
|
store = self.FrameStore ()
|
|
|
|
|
self.frame_store = store
|
|
|
|
|
|
2018-09-21 20:23:59 +08:00
|
|
|
|
tree = Gtk.TreeView (model = store, has_tooltip = True)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
scrolled.add (tree)
|
|
|
|
|
tree.set_search_column (store.FUNCTION)
|
|
|
|
|
tree.show ()
|
|
|
|
|
|
|
|
|
|
tree.connect ("row-activated", self.frames_row_activated)
|
2018-09-21 20:23:59 +08:00
|
|
|
|
tree.connect ("query-tooltip", self.frames_query_tooltip)
|
|
|
|
|
|
|
|
|
|
def format_filename_col (tree_col, cell, model, iter, col):
|
|
|
|
|
object = model[iter][col]
|
|
|
|
|
|
|
|
|
|
cell.set_property ("text", get_basename (object) if object else "")
|
|
|
|
|
|
|
|
|
|
self.tooltip_columns = {}
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "#")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
2018-09-23 13:37:27 +08:00
|
|
|
|
cell = Gtk.CellRendererText (xalign = 1)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", self.FrameStore.ID)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Address")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText (xalign = 1)
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", self.FrameStore.ADDRESS)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Object")
|
2018-09-21 20:23:59 +08:00
|
|
|
|
self.tooltip_columns[col] = store.OBJECT
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText ()
|
|
|
|
|
col.pack_start (cell, False)
|
2018-09-21 20:23:59 +08:00
|
|
|
|
col.set_cell_data_func (cell, format_filename_col, store.OBJECT)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Function")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText ()
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", self.FrameStore.FUNCTION)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Offset")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText (xalign = 1)
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", self.FrameStore.OFFSET)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Source")
|
2018-09-21 20:23:59 +08:00
|
|
|
|
self.tooltip_columns[col] = store.SOURCE
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText ()
|
|
|
|
|
col.pack_start (cell, False)
|
2018-09-21 20:23:59 +08:00
|
|
|
|
col.set_cell_data_func (cell, format_filename_col, store.SOURCE)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Line")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText (xalign = 1)
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", self.FrameStore.LINE)
|
|
|
|
|
|
2018-09-30 16:51:19 +08:00
|
|
|
|
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
|
|
|
|
|
|
2018-09-21 20:23:59 +08:00
|
|
|
|
col = Gtk.TreeViewColumn ()
|
2018-09-30 16:51:19 +08:00
|
|
|
|
self.tooltip_columns[col] = format_view_source_tooltip
|
2018-09-21 20:23:59 +08:00
|
|
|
|
tree.append_column (col)
|
|
|
|
|
|
2018-09-30 16:51:19 +08:00
|
|
|
|
cell = self.CellRendererViewSource (xalign = 0)
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.set_cell_data_func (cell, format_view_source_col, (store.SOURCE,
|
|
|
|
|
store.LINE))
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
selection.connect ("change-complete", self.selection_change_complete)
|
|
|
|
|
|
|
|
|
|
@GObject.Property (type = bool, default = False)
|
|
|
|
|
def available (self):
|
|
|
|
|
sel = selection.get_effective_selection ()
|
|
|
|
|
|
|
|
|
|
if len (sel) == 1:
|
|
|
|
|
i, = sel
|
|
|
|
|
|
|
|
|
|
return bool (samples[i].backtrace)
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
def update (self):
|
|
|
|
|
if not self.needs_update or not self.available:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.needs_update = False
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
tid = None
|
|
|
|
|
|
|
|
|
|
sel_rows = self.thread_tree.get_selection ().get_selected_rows ()[1]
|
|
|
|
|
|
|
|
|
|
if sel_rows:
|
|
|
|
|
tid = self.thread_store[sel_rows[0]][self.ThreadStore.ID]
|
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
i, = selection.get_effective_selection ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
self.thread_store.clear ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
for t in range (len (samples[i].backtrace)):
|
|
|
|
|
thread = samples[i].backtrace[t]
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
iter = self.thread_store.append (
|
|
|
|
|
(t, thread.id, thread.name, str (thread.state))
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if thread.id == tid:
|
|
|
|
|
self.thread_tree.get_selection ().select_iter (iter)
|
|
|
|
|
|
|
|
|
|
def do_map (self):
|
|
|
|
|
self.update ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
Gtk.Box.do_map (self)
|
|
|
|
|
|
|
|
|
|
def selection_change_complete (self, selection):
|
|
|
|
|
self.needs_update = True
|
|
|
|
|
|
|
|
|
|
if self.get_mapped ():
|
|
|
|
|
self.update ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
self.notify ("available")
|
|
|
|
|
|
|
|
|
|
def threads_row_activated (self, tree, path, col):
|
|
|
|
|
iter = self.thread_store.get_iter (path)
|
|
|
|
|
|
|
|
|
|
tid = self.thread_store[iter][self.ThreadStore.ID]
|
|
|
|
|
|
|
|
|
|
sel = set ()
|
|
|
|
|
|
|
|
|
|
for i in range (len (samples)):
|
|
|
|
|
threads = filter (lambda thread:
|
|
|
|
|
thread.id == tid and
|
|
|
|
|
thread.state == ThreadState.RUNNING,
|
|
|
|
|
samples[i].backtrace or [])
|
|
|
|
|
|
|
|
|
|
if list (threads):
|
|
|
|
|
sel.add (i)
|
|
|
|
|
|
|
|
|
|
selection.select (sel)
|
|
|
|
|
|
|
|
|
|
selection.change_complete ()
|
|
|
|
|
|
|
|
|
|
def threads_selection_changed (self, tree_sel):
|
|
|
|
|
self.frame_store.clear ()
|
|
|
|
|
|
|
|
|
|
(store, rows) = tree_sel.get_selected_rows ()
|
|
|
|
|
|
|
|
|
|
if not rows:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
i, = selection.get_effective_selection ()
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
frames = samples[i].backtrace[store[rows[0]][store.INDEX]].frames
|
|
|
|
|
|
|
|
|
|
for frame in frames:
|
|
|
|
|
info = frame.info
|
|
|
|
|
|
|
|
|
|
self.frame_store.append ((
|
|
|
|
|
frame.id, hex (frame.address), info.object, info.symbol,
|
|
|
|
|
hex (info.offset) if info.offset is not None else None,
|
|
|
|
|
info.source, str (info.line) if info.line else None
|
|
|
|
|
))
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
def frames_row_activated (self, tree, path, col):
|
|
|
|
|
iter = self.frame_store.get_iter (path)
|
|
|
|
|
|
|
|
|
|
address = int (self.frame_store[iter][self.FrameStore.ADDRESS], 0)
|
|
|
|
|
info = address_map.get (address, None)
|
|
|
|
|
|
|
|
|
|
if not info:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
id = info.id
|
|
|
|
|
|
|
|
|
|
if not id:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
sel = set ()
|
|
|
|
|
|
|
|
|
|
def has_frame (sample, id):
|
|
|
|
|
for thread in sample.backtrace or []:
|
|
|
|
|
for frame in thread.frames:
|
|
|
|
|
if frame.info.id == id:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
for i in range (len (samples)):
|
|
|
|
|
if has_frame (samples[i], id):
|
|
|
|
|
sel.add (i)
|
|
|
|
|
|
|
|
|
|
selection.select (sel)
|
|
|
|
|
|
|
|
|
|
selection.change_complete ()
|
|
|
|
|
|
2018-09-21 20:23:59 +08:00
|
|
|
|
def frames_query_tooltip (self, tree, x, y, keyboard_mode, tooltip):
|
|
|
|
|
hit, x, y, model, path, iter = tree.get_tooltip_context (x, y,
|
|
|
|
|
keyboard_mode)
|
|
|
|
|
|
|
|
|
|
if hit:
|
2018-09-30 16:51:19 +08:00
|
|
|
|
column = None
|
2018-09-21 20:23:59 +08:00
|
|
|
|
|
|
|
|
|
if keyboard_mode:
|
|
|
|
|
cursor_path, cursor_col = tree.get_cursor ()
|
|
|
|
|
|
|
|
|
|
if path.compare (cursor_path) == 0:
|
|
|
|
|
column = self.tooltip_columns[cursor_col]
|
|
|
|
|
else:
|
|
|
|
|
for col in self.tooltip_columns:
|
|
|
|
|
area = tree.get_cell_area (path, col)
|
|
|
|
|
|
|
|
|
|
if x >= area.x and x < area.x + area.width and \
|
|
|
|
|
y >= area.y and y < area.y + area.height:
|
|
|
|
|
column = self.tooltip_columns[col]
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
2018-09-30 16:51:19 +08:00
|
|
|
|
if column is not None:
|
|
|
|
|
value = None
|
|
|
|
|
|
|
|
|
|
if type (column) == int:
|
|
|
|
|
value = model[iter][column]
|
|
|
|
|
else:
|
|
|
|
|
value = column (model[iter])
|
2018-09-21 20:23:59 +08:00
|
|
|
|
|
|
|
|
|
if value:
|
|
|
|
|
tooltip.set_text (str (value))
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
2018-10-19 22:02:04 +08:00
|
|
|
|
class CellRendererPercentage (Gtk.CellRendererText):
|
|
|
|
|
padding = 0
|
|
|
|
|
|
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.CellRendererText.__init__ (self, *args, xalign = 1, **kwargs)
|
|
|
|
|
|
|
|
|
|
self.value = 0
|
|
|
|
|
|
|
|
|
|
@GObject.property (type = float)
|
|
|
|
|
def value (self):
|
|
|
|
|
return self.value_property
|
|
|
|
|
|
|
|
|
|
@value.setter
|
|
|
|
|
def value (self, value):
|
|
|
|
|
self.value_property = value
|
|
|
|
|
|
|
|
|
|
self.set_property ("text", format_percentage (value, 2))
|
|
|
|
|
|
|
|
|
|
def do_render (self, cr, widget, background_area, cell_area, flags):
|
|
|
|
|
full_width = cell_area.width - 2 * self.padding
|
|
|
|
|
full_height = cell_area.height - 2 * self.padding
|
|
|
|
|
|
|
|
|
|
if full_width <= 0 or full_height <= 0:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
state = widget.get_state ()
|
|
|
|
|
style = widget.get_style_context ()
|
|
|
|
|
fg_color = style.get_color (state)
|
|
|
|
|
|
|
|
|
|
rounded_rectangle (cr,
|
|
|
|
|
cell_area.x + self.padding,
|
|
|
|
|
cell_area.y + self.padding,
|
|
|
|
|
full_width,
|
|
|
|
|
full_height,
|
|
|
|
|
1)
|
|
|
|
|
|
|
|
|
|
cr.clip ()
|
|
|
|
|
|
|
|
|
|
cr.set_source_rgba (*blend_colors ((0, 0, 0, 0), fg_color, 0.2))
|
|
|
|
|
cr.paint ()
|
|
|
|
|
|
|
|
|
|
Gtk.CellRendererText.do_render (self,
|
|
|
|
|
cr, widget,
|
|
|
|
|
background_area, cell_area,
|
2018-10-20 22:56:09 +08:00
|
|
|
|
flags)
|
2018-10-19 22:02:04 +08:00
|
|
|
|
|
|
|
|
|
value = min (max (self.value, 0), 1)
|
|
|
|
|
width = round (full_width * value)
|
|
|
|
|
height = full_height
|
|
|
|
|
|
|
|
|
|
if width > 0 and height > 0:
|
2018-10-20 22:56:09 +08:00
|
|
|
|
state = Gtk.StateFlags (state |
|
|
|
|
|
Gtk.StateFlags.SELECTED)
|
|
|
|
|
flags = Gtk.CellRendererState (flags |
|
|
|
|
|
Gtk.CellRendererState.SELECTED)
|
2018-10-19 22:02:04 +08:00
|
|
|
|
|
|
|
|
|
style.save ()
|
|
|
|
|
style.set_state (state)
|
|
|
|
|
|
|
|
|
|
fg_color = style.get_color (state)
|
|
|
|
|
bg_color = style.get_background_color (state)
|
|
|
|
|
|
|
|
|
|
x = round ((full_width - width) * self.get_property ("xalign"))
|
|
|
|
|
|
|
|
|
|
cr.rectangle (cell_area.x + self.padding + x,
|
|
|
|
|
cell_area.y + self.padding,
|
|
|
|
|
width,
|
|
|
|
|
height)
|
|
|
|
|
|
|
|
|
|
cr.clip ()
|
|
|
|
|
|
|
|
|
|
cr.set_source_rgba (*blend_colors (bg_color, fg_color, -0.3))
|
|
|
|
|
cr.paint ()
|
|
|
|
|
|
|
|
|
|
Gtk.CellRendererText.do_render (self,
|
|
|
|
|
cr, widget,
|
|
|
|
|
background_area, cell_area,
|
2018-10-20 22:56:09 +08:00
|
|
|
|
flags)
|
2018-10-19 22:02:04 +08:00
|
|
|
|
|
|
|
|
|
style.restore ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
class ProfileViewer (Gtk.ScrolledWindow):
|
2018-09-23 12:44:50 +08:00
|
|
|
|
class ThreadFilter (Gtk.TreeView):
|
|
|
|
|
class Store (Gtk.ListStore):
|
|
|
|
|
VISIBLE = 0
|
|
|
|
|
ID = 1
|
|
|
|
|
NAME = 2
|
|
|
|
|
STATE = {list (ThreadState)[i]: 3 + i
|
|
|
|
|
for i in range (len (ThreadState))}
|
|
|
|
|
|
|
|
|
|
def __init__ (self):
|
|
|
|
|
Gtk.ListStore.__init__ (self,
|
|
|
|
|
bool, int, str,
|
|
|
|
|
*(len (self.STATE) * (bool,)))
|
|
|
|
|
|
|
|
|
|
threads = list ({thread.id
|
|
|
|
|
for sample in samples
|
|
|
|
|
for thread in sample.backtrace or ()})
|
|
|
|
|
threads.sort ()
|
|
|
|
|
|
|
|
|
|
states = [state == ThreadState.RUNNING for state in self.STATE]
|
|
|
|
|
|
|
|
|
|
for id in threads:
|
|
|
|
|
self.append ((False, id, None, *states))
|
|
|
|
|
|
|
|
|
|
def get_filter (self):
|
|
|
|
|
return {row[self.ID]: {state
|
|
|
|
|
for state, column in self.STATE.items ()
|
|
|
|
|
if row[column]}
|
|
|
|
|
for row in self}
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
def set_filter (self, filter):
|
|
|
|
|
for row in self:
|
|
|
|
|
states = filter[row[self.ID]]
|
|
|
|
|
|
|
|
|
|
for state, column in self.STATE.items ():
|
|
|
|
|
row[column] = state in states
|
|
|
|
|
|
2018-09-23 12:44:50 +08:00
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.TreeView.__init__ (self, *args, **kwargs)
|
|
|
|
|
|
|
|
|
|
self.needs_update = True
|
|
|
|
|
|
|
|
|
|
store = self.Store ()
|
|
|
|
|
self.store = store
|
|
|
|
|
|
|
|
|
|
filter = Gtk.TreeModelFilter (child_model = store)
|
|
|
|
|
filter.set_visible_column (store.VISIBLE)
|
|
|
|
|
self.set_model (filter)
|
2018-09-23 13:17:22 +08:00
|
|
|
|
self.set_search_column (store.NAME)
|
2018-09-23 12:44:50 +08:00
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "ID")
|
|
|
|
|
self.append_column (col)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText (xalign = 1)
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", store.ID)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Name")
|
|
|
|
|
self.append_column (col)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText ()
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", store.NAME)
|
|
|
|
|
|
|
|
|
|
for state in store.STATE:
|
|
|
|
|
col = Gtk.TreeViewColumn (title = str (state))
|
|
|
|
|
col.column = store.STATE[state]
|
|
|
|
|
self.append_column (col)
|
|
|
|
|
col.set_alignment (0.5)
|
|
|
|
|
col.set_clickable (True)
|
|
|
|
|
|
|
|
|
|
def col_clicked (col):
|
|
|
|
|
active = not all (row[col.column] for row in filter)
|
|
|
|
|
|
|
|
|
|
for row in filter:
|
|
|
|
|
row[col.column] = active
|
|
|
|
|
|
|
|
|
|
col.connect ("clicked", col_clicked)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererToggle ()
|
|
|
|
|
cell.column = store.STATE[state]
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "active", store.STATE[state])
|
|
|
|
|
|
|
|
|
|
def cell_toggled (cell, path):
|
2018-10-11 20:20:08 +08:00
|
|
|
|
filter[path][cell.column] = not cell.get_property ("active")
|
2018-09-23 12:44:50 +08:00
|
|
|
|
|
|
|
|
|
cell.connect ("toggled", cell_toggled)
|
|
|
|
|
|
|
|
|
|
selection.connect ("change-complete",
|
|
|
|
|
self.selection_change_complete)
|
|
|
|
|
|
|
|
|
|
def update (self):
|
|
|
|
|
if not self.needs_update:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
self.needs_update = False
|
|
|
|
|
|
|
|
|
|
sel = selection.get_effective_selection ()
|
|
|
|
|
|
|
|
|
|
threads = {thread.id: thread.name
|
|
|
|
|
for i in sel
|
|
|
|
|
for thread in samples[i].backtrace or ()}
|
|
|
|
|
|
|
|
|
|
for row in self.store:
|
|
|
|
|
id = row[self.store.ID]
|
|
|
|
|
|
|
|
|
|
if id in threads:
|
|
|
|
|
row[self.store.VISIBLE] = True
|
|
|
|
|
row[self.store.NAME] = threads[id]
|
|
|
|
|
else:
|
|
|
|
|
row[self.store.VISIBLE] = False
|
|
|
|
|
row[self.store.NAME] = None
|
|
|
|
|
|
|
|
|
|
def do_map (self):
|
|
|
|
|
self.update ()
|
|
|
|
|
|
|
|
|
|
Gtk.TreeView.do_map (self)
|
|
|
|
|
|
|
|
|
|
def selection_change_complete (self, selection):
|
|
|
|
|
self.needs_update = True
|
|
|
|
|
|
|
|
|
|
if self.get_mapped ():
|
|
|
|
|
self.update ()
|
|
|
|
|
|
|
|
|
|
class ThreadPopover (Gtk.Popover):
|
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.Popover.__init__ (self, *args, border_width = 4, **kwargs)
|
|
|
|
|
|
|
|
|
|
frame = Gtk.Frame (shadow_type = Gtk.ShadowType.IN)
|
|
|
|
|
self.add (frame)
|
|
|
|
|
frame.show ()
|
|
|
|
|
|
|
|
|
|
scrolled = Gtk.ScrolledWindow (
|
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.NEVER,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
|
|
|
|
|
propagate_natural_height = True,
|
|
|
|
|
max_content_height = 400
|
|
|
|
|
)
|
|
|
|
|
frame.add (scrolled)
|
|
|
|
|
scrolled.show ()
|
|
|
|
|
|
|
|
|
|
thread_filter = ProfileViewer.ThreadFilter ()
|
|
|
|
|
self.thread_filter = thread_filter
|
|
|
|
|
scrolled.add (thread_filter)
|
|
|
|
|
thread_filter.show ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
class Profile (Gtk.Box):
|
|
|
|
|
ProfileFrame = namedtuple ("ProfileFrame", ("sample", "stack", "i"))
|
|
|
|
|
|
|
|
|
|
class Direction (enum.Enum):
|
|
|
|
|
CALLEES = enum.auto ()
|
|
|
|
|
CALLERS = enum.auto ()
|
|
|
|
|
|
|
|
|
|
class Store (Gtk.ListStore):
|
|
|
|
|
ID = 0
|
|
|
|
|
FUNCTION = 1
|
|
|
|
|
EXCLUSIVE = 2
|
|
|
|
|
INCLUSIVE = 3
|
|
|
|
|
|
|
|
|
|
def __init__ (self):
|
|
|
|
|
Gtk.ListStore.__init__ (self,
|
|
|
|
|
GObject.TYPE_UINT64, str, float, float)
|
|
|
|
|
|
|
|
|
|
__gsignals__ = {
|
2018-09-29 12:14:38 +08:00
|
|
|
|
"needs-update": (GObject.SIGNAL_RUN_FIRST,
|
|
|
|
|
None, (bool,)),
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
"subprofile-added": (GObject.SIGNAL_RUN_FIRST,
|
|
|
|
|
None, (Gtk.Widget,)),
|
|
|
|
|
"subprofile-removed": (GObject.SIGNAL_RUN_FIRST,
|
2018-09-30 20:54:34 +08:00
|
|
|
|
None, (Gtk.Widget,)),
|
|
|
|
|
"path-changed": (GObject.SIGNAL_RUN_FIRST,
|
|
|
|
|
None, ())
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def __init__ (self,
|
|
|
|
|
root = None,
|
|
|
|
|
id = None,
|
|
|
|
|
title = None,
|
|
|
|
|
frames = None,
|
|
|
|
|
direction = Direction.CALLEES,
|
|
|
|
|
sort = (Store.INCLUSIVE, Gtk.SortType.DESCENDING),
|
|
|
|
|
*args,
|
|
|
|
|
**kwargs):
|
|
|
|
|
Gtk.Box.__init__ (self,
|
|
|
|
|
*args,
|
|
|
|
|
orientation = Gtk.Orientation.HORIZONTAL,
|
|
|
|
|
**kwargs)
|
|
|
|
|
|
|
|
|
|
self.root = root or self
|
|
|
|
|
self.id = id
|
|
|
|
|
self.frames = frames
|
|
|
|
|
self.direction = direction
|
|
|
|
|
|
|
|
|
|
self.subprofile = None
|
|
|
|
|
|
|
|
|
|
vbox = Gtk.Box (orientation = Gtk.Orientation.VERTICAL)
|
|
|
|
|
self.pack_start (vbox, False, False, 0)
|
|
|
|
|
vbox.show ()
|
|
|
|
|
|
|
|
|
|
header = Gtk.HeaderBar (title = title or "All Functions")
|
|
|
|
|
self.header = header
|
|
|
|
|
vbox.pack_start (header, False, False, 0)
|
|
|
|
|
header.show ()
|
|
|
|
|
|
|
|
|
|
if not id:
|
2018-09-23 12:44:50 +08:00
|
|
|
|
popover = ProfileViewer.ThreadPopover ()
|
|
|
|
|
|
|
|
|
|
thread_filter_store = popover.thread_filter.store
|
|
|
|
|
|
|
|
|
|
self.thread_filter_store = thread_filter_store
|
|
|
|
|
self.thread_filter = thread_filter_store.get_filter ()
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.add_source (self.thread_filter_source_get,
|
|
|
|
|
self.thread_filter_source_set)
|
|
|
|
|
|
2018-09-23 12:44:50 +08:00
|
|
|
|
button = Gtk.MenuButton (popover = popover)
|
|
|
|
|
header.pack_end (button)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button.connect ("toggled", self.thread_filter_button_toggled)
|
|
|
|
|
|
|
|
|
|
hbox = Gtk.Box (orientation = Gtk.Orientation.HORIZONTAL,
|
|
|
|
|
spacing = 4)
|
|
|
|
|
button.add (hbox)
|
|
|
|
|
hbox.show ()
|
|
|
|
|
|
|
|
|
|
label = Gtk.Label ("Threads")
|
|
|
|
|
hbox.pack_start (label, False, False, 0)
|
|
|
|
|
label.show ()
|
|
|
|
|
|
|
|
|
|
image = Gtk.Image.new_from_icon_name ("pan-down-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
hbox.pack_start (image, False, False, 0)
|
|
|
|
|
image.show ()
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.add_source (self.direction_source_get,
|
|
|
|
|
self.direction_source_set)
|
|
|
|
|
|
2018-09-21 19:36:34 +08:00
|
|
|
|
button = Gtk.Button (tooltip_text = "Call-graph direction")
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
header.pack_end (button)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button.connect ("clicked", self.direction_button_clicked)
|
|
|
|
|
|
|
|
|
|
image = Gtk.Image ()
|
|
|
|
|
self.direction_image = image
|
|
|
|
|
button.add (image)
|
|
|
|
|
image.show ()
|
|
|
|
|
else:
|
|
|
|
|
button = Gtk.Button.new_from_icon_name (
|
|
|
|
|
"edit-select-all-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON
|
|
|
|
|
)
|
|
|
|
|
header.pack_end (button)
|
|
|
|
|
button.set_tooltip_text (
|
|
|
|
|
str (Selection (frame.sample for frame in frames))
|
|
|
|
|
)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button.connect ("clicked", self.select_samples_clicked)
|
|
|
|
|
|
|
|
|
|
scrolled = Gtk.ScrolledWindow (
|
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.NEVER,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.AUTOMATIC
|
|
|
|
|
)
|
|
|
|
|
vbox.pack_start (scrolled, True, True, 0)
|
|
|
|
|
scrolled.show ()
|
|
|
|
|
|
|
|
|
|
store = self.Store ()
|
|
|
|
|
self.store = store
|
|
|
|
|
store.set_sort_column_id (*sort)
|
|
|
|
|
|
|
|
|
|
tree = Gtk.TreeView (model = store)
|
|
|
|
|
self.tree = tree
|
|
|
|
|
scrolled.add (tree)
|
|
|
|
|
tree.set_search_column (store.FUNCTION)
|
|
|
|
|
tree.show ()
|
|
|
|
|
|
|
|
|
|
tree.get_selection ().connect ("changed",
|
|
|
|
|
self.tree_selection_changed)
|
|
|
|
|
|
|
|
|
|
tree.connect ("row-activated", self.tree_row_activated)
|
|
|
|
|
tree.connect ("key-press-event", self.tree_key_press_event)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Function")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_resizable (True)
|
2018-10-21 15:33:51 +08:00
|
|
|
|
col.set_sort_column_id (store.FUNCTION)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText (ellipsize = Pango.EllipsizeMode.END)
|
|
|
|
|
col.pack_start (cell, True)
|
|
|
|
|
col.add_attribute (cell, "text", store.FUNCTION)
|
|
|
|
|
cell.set_property ("width-chars", 40)
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Self")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_alignment (0.5)
|
|
|
|
|
col.set_sort_column_id (store.EXCLUSIVE)
|
|
|
|
|
|
2018-10-19 22:02:04 +08:00
|
|
|
|
cell = CellRendererPercentage ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
col.pack_start (cell, False)
|
2018-10-19 22:02:04 +08:00
|
|
|
|
col.add_attribute (cell, "value", store.EXCLUSIVE)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "All")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_alignment (0.5)
|
|
|
|
|
col.set_sort_column_id (store.INCLUSIVE)
|
|
|
|
|
|
2018-10-19 22:02:04 +08:00
|
|
|
|
cell = CellRendererPercentage ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
col.pack_start (cell, False)
|
2018-10-19 22:02:04 +08:00
|
|
|
|
col.add_attribute (cell, "value", store.INCLUSIVE)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
if id:
|
|
|
|
|
self.update ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
def update (self):
|
|
|
|
|
self.remove_subprofile ()
|
|
|
|
|
|
|
|
|
|
if not self.id:
|
|
|
|
|
self.update_frames ()
|
|
|
|
|
|
|
|
|
|
self.update_store ()
|
|
|
|
|
|
|
|
|
|
self.update_ui ()
|
|
|
|
|
|
|
|
|
|
def update_frames (self):
|
|
|
|
|
self.frames = []
|
|
|
|
|
|
|
|
|
|
for i in selection.get_effective_selection ():
|
|
|
|
|
for thread in samples[i].backtrace or []:
|
2018-09-23 12:44:50 +08:00
|
|
|
|
if thread.state in self.thread_filter[thread.id]:
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
thread_frames = thread.frames
|
|
|
|
|
|
|
|
|
|
if self.direction == self.Direction.CALLERS:
|
|
|
|
|
thread_frames = reversed (thread_frames)
|
|
|
|
|
|
|
|
|
|
stack = []
|
|
|
|
|
prev_id = 0
|
|
|
|
|
|
|
|
|
|
for frame in thread_frames:
|
|
|
|
|
id = frame.info.id
|
|
|
|
|
|
|
|
|
|
if id == prev_id:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
self.frames.append (self.ProfileFrame (
|
|
|
|
|
sample = i,
|
|
|
|
|
stack = stack,
|
|
|
|
|
i = len (stack)
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
stack.append (frame)
|
|
|
|
|
|
|
|
|
|
prev_id = id
|
|
|
|
|
|
|
|
|
|
def update_store (self):
|
|
|
|
|
stacks = {}
|
|
|
|
|
symbols = {}
|
|
|
|
|
|
|
|
|
|
sort = self.store.get_sort_column_id ()
|
|
|
|
|
|
|
|
|
|
self.store = self.Store ()
|
|
|
|
|
|
|
|
|
|
for frame in self.frames:
|
|
|
|
|
info = frame.stack[frame.i].info
|
|
|
|
|
symbol_id = info.id
|
|
|
|
|
stack_id = builtins.id (frame.stack)
|
|
|
|
|
|
|
|
|
|
symbol = symbols.get (symbol_id, None)
|
|
|
|
|
|
|
|
|
|
if not symbol:
|
|
|
|
|
symbol = [info, 0, 0]
|
|
|
|
|
symbols[symbol_id] = symbol
|
|
|
|
|
|
|
|
|
|
stack = stacks.get (stack_id, None)
|
|
|
|
|
|
|
|
|
|
if not stack:
|
|
|
|
|
stack = set ()
|
|
|
|
|
stacks[stack_id] = stack
|
|
|
|
|
|
|
|
|
|
if frame.i == 0:
|
|
|
|
|
symbol[1] += 1
|
|
|
|
|
|
|
|
|
|
if symbol_id not in stack:
|
|
|
|
|
stack.add (symbol_id)
|
|
|
|
|
|
|
|
|
|
symbol[2] += 1
|
|
|
|
|
|
|
|
|
|
n_stacks = len (stacks)
|
|
|
|
|
|
|
|
|
|
for symbol in symbols.values ():
|
|
|
|
|
id = symbol[0].id
|
|
|
|
|
name = symbol[0].name if id != self.id else "[Self]"
|
|
|
|
|
|
|
|
|
|
self.store.append ((id,
|
|
|
|
|
name,
|
|
|
|
|
symbol[1] / n_stacks,
|
|
|
|
|
symbol[2] / n_stacks))
|
|
|
|
|
|
|
|
|
|
self.store.set_sort_column_id (*sort)
|
|
|
|
|
|
|
|
|
|
self.tree.set_model (self.store)
|
2018-09-17 00:33:51 +08:00
|
|
|
|
self.tree.set_search_column (self.store.FUNCTION)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
def update_ui (self):
|
|
|
|
|
if not self.id:
|
|
|
|
|
if self.direction == self.Direction.CALLEES:
|
2018-09-21 19:36:34 +08:00
|
|
|
|
icon_name = "format-indent-more-symbolic"
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
else:
|
2018-09-21 19:36:34 +08:00
|
|
|
|
icon_name = "format-indent-less-symbolic"
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
self.direction_image.set_from_icon_name (icon_name,
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
else:
|
|
|
|
|
if self.direction == self.Direction.CALLEES:
|
|
|
|
|
subtitle = "Callees"
|
|
|
|
|
else:
|
|
|
|
|
subtitle = "Callers"
|
|
|
|
|
|
|
|
|
|
self.header.set_subtitle (subtitle)
|
|
|
|
|
|
|
|
|
|
def select (self, id):
|
|
|
|
|
if id is not None:
|
|
|
|
|
for row in self.store:
|
|
|
|
|
if row[self.store.ID] == id:
|
|
|
|
|
iter = row.iter
|
|
|
|
|
path = self.store.get_path (iter)
|
|
|
|
|
|
|
|
|
|
self.tree.get_selection ().select_iter (iter)
|
|
|
|
|
|
|
|
|
|
self.tree.scroll_to_cell (path, None, True, 0.5, 0)
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
self.tree.get_selection ().unselect_all ()
|
|
|
|
|
|
|
|
|
|
def add_subprofile (self, subprofile):
|
|
|
|
|
self.remove_subprofile ()
|
|
|
|
|
|
|
|
|
|
box = Gtk.Box (orientation = Gtk.Orientation.HORIZONTAL)
|
2018-09-29 04:17:28 +08:00
|
|
|
|
self.subprofile_box = box
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
self.pack_start (box, True, True, 0)
|
|
|
|
|
box.show ()
|
|
|
|
|
|
|
|
|
|
separator = Gtk.Separator (orientation = Gtk.Orientation.VERTICAL)
|
|
|
|
|
box.pack_start (separator, False, False, 0)
|
|
|
|
|
separator.show ()
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
self.subprofile = subprofile
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
box.pack_start (subprofile, True, True, 0)
|
|
|
|
|
subprofile.show ()
|
|
|
|
|
|
|
|
|
|
subprofile.connect ("subprofile-added",
|
|
|
|
|
lambda profile, subprofile:
|
|
|
|
|
self.emit ("subprofile-added",
|
|
|
|
|
subprofile))
|
|
|
|
|
subprofile.connect ("subprofile-removed",
|
|
|
|
|
lambda profile, subprofile:
|
|
|
|
|
self.emit ("subprofile-removed",
|
|
|
|
|
subprofile))
|
2018-09-30 20:54:34 +08:00
|
|
|
|
subprofile.connect ("path-changed",
|
|
|
|
|
lambda profile: self.emit ("path-changed"))
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
self.emit ("subprofile-added", subprofile)
|
|
|
|
|
|
|
|
|
|
def remove_subprofile (self):
|
|
|
|
|
if self.subprofile:
|
|
|
|
|
subprofile = self.subprofile
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
self.remove (self.subprofile_box)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
self.subprofile = None
|
|
|
|
|
self.subprofile_box = None
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
self.emit ("subprofile-removed", subprofile)
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
def get_path (self):
|
|
|
|
|
tree_sel = self.tree.get_selection ()
|
|
|
|
|
|
|
|
|
|
sel_rows = tree_sel.get_selected_rows ()[1]
|
|
|
|
|
|
|
|
|
|
if not sel_rows:
|
|
|
|
|
return ()
|
|
|
|
|
|
|
|
|
|
id = self.store[sel_rows[0]][self.store.ID]
|
|
|
|
|
|
|
|
|
|
if self.subprofile:
|
|
|
|
|
return (id,) + self.subprofile.get_path ()
|
|
|
|
|
else:
|
|
|
|
|
return (id,)
|
|
|
|
|
|
|
|
|
|
def set_path (self, path):
|
|
|
|
|
self.select (path[0] if path else None)
|
|
|
|
|
|
|
|
|
|
if self.subprofile:
|
|
|
|
|
self.subprofile.set_path (path[1:])
|
|
|
|
|
|
|
|
|
|
def thread_filter_source_get (self):
|
|
|
|
|
return self.thread_filter_store.get_filter ()
|
|
|
|
|
|
|
|
|
|
def thread_filter_source_set (self, thread_filter):
|
|
|
|
|
self.thread_filter = thread_filter
|
|
|
|
|
|
|
|
|
|
self.thread_filter_store.set_filter (thread_filter)
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.emit ("needs-update", False)
|
2018-09-29 04:17:28 +08:00
|
|
|
|
|
2018-09-23 12:44:50 +08:00
|
|
|
|
def thread_filter_button_toggled (self, button):
|
|
|
|
|
if not button.get_active ():
|
|
|
|
|
thread_filter = self.thread_filter_store.get_filter ()
|
|
|
|
|
|
|
|
|
|
if thread_filter != self.thread_filter:
|
|
|
|
|
self.thread_filter = thread_filter
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.start_group ()
|
|
|
|
|
|
|
|
|
|
history.record ()
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.emit ("needs-update", True)
|
2018-09-23 12:44:50 +08:00
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.end_group ()
|
|
|
|
|
|
|
|
|
|
def direction_source_get (self):
|
|
|
|
|
return self.direction
|
|
|
|
|
|
|
|
|
|
def direction_source_set (self, direction):
|
|
|
|
|
self.direction = direction
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.emit ("needs-update", False)
|
2018-09-29 04:17:28 +08:00
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
def direction_button_clicked (self, button):
|
|
|
|
|
if self.direction == self.Direction.CALLEES:
|
|
|
|
|
self.direction = self.Direction.CALLERS
|
|
|
|
|
else:
|
|
|
|
|
self.direction = self.Direction.CALLEES
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.start_group ()
|
|
|
|
|
|
|
|
|
|
history.record ()
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.emit ("needs-update", True)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.end_group ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
def select_samples_clicked (self, button):
|
2018-09-30 20:48:40 +08:00
|
|
|
|
selection.select ({frame.sample for frame in self.frames})
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
selection.change_complete ()
|
|
|
|
|
|
|
|
|
|
def tree_selection_changed (self, tree_sel):
|
|
|
|
|
self.remove_subprofile ()
|
|
|
|
|
|
|
|
|
|
sel_rows = tree_sel.get_selected_rows ()[1]
|
|
|
|
|
|
|
|
|
|
if not sel_rows:
|
2018-09-30 20:54:34 +08:00
|
|
|
|
self.emit ("path-changed")
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
id = self.store[sel_rows[0]][self.store.ID]
|
|
|
|
|
title = self.store[sel_rows[0]][self.store.FUNCTION]
|
|
|
|
|
|
|
|
|
|
frames = []
|
|
|
|
|
|
|
|
|
|
for frame in self.frames:
|
|
|
|
|
if frame.stack[frame.i].info.id == id:
|
|
|
|
|
frames.append (frame)
|
|
|
|
|
|
2018-09-30 20:54:34 +08:00
|
|
|
|
if frame.i > 0 and id != self.id:
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
frames.append (self.ProfileFrame (sample = frame.sample,
|
|
|
|
|
stack = frame.stack,
|
|
|
|
|
i = frame.i - 1))
|
|
|
|
|
|
2018-09-30 20:54:34 +08:00
|
|
|
|
if id != self.id:
|
|
|
|
|
self.add_subprofile (ProfileViewer.Profile (
|
|
|
|
|
self.root,
|
|
|
|
|
id,
|
|
|
|
|
title,
|
|
|
|
|
frames,
|
|
|
|
|
self.direction,
|
|
|
|
|
self.store.get_sort_column_id ()
|
|
|
|
|
))
|
|
|
|
|
else:
|
|
|
|
|
filenames = {frame.stack[frame.i].info.source
|
|
|
|
|
for frame in frames}
|
|
|
|
|
filenames = list (filter (bool, filenames))
|
|
|
|
|
|
|
|
|
|
if len (filenames) == 1:
|
|
|
|
|
file = find_file (filenames[0])
|
|
|
|
|
|
|
|
|
|
if file:
|
|
|
|
|
self.add_subprofile (ProfileViewer.SourceProfile (
|
|
|
|
|
file,
|
|
|
|
|
frames[0].stack[frames[0].i].info.name,
|
|
|
|
|
frames
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
self.emit ("path-changed")
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
def tree_row_activated (self, tree, path, col):
|
|
|
|
|
if self.root != self:
|
|
|
|
|
self.root.select (self.store[path][self.store.ID])
|
|
|
|
|
|
|
|
|
|
def tree_key_press_event (self, tree, event):
|
|
|
|
|
if event.keyval == Gdk.KEY_Escape:
|
|
|
|
|
self.select (None)
|
|
|
|
|
|
|
|
|
|
if self.root is not self:
|
|
|
|
|
self.get_parent ().get_ancestor (
|
|
|
|
|
ProfileViewer.Profile
|
|
|
|
|
).tree.grab_focus ()
|
|
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
2018-09-30 20:54:34 +08:00
|
|
|
|
class SourceProfile (Gtk.Box):
|
|
|
|
|
class Store (Gtk.ListStore):
|
2018-10-19 22:02:04 +08:00
|
|
|
|
LINE = 0
|
|
|
|
|
HAS_FRAMES = 1
|
|
|
|
|
EXCLUSIVE = 2
|
|
|
|
|
INCLUSIVE = 3
|
|
|
|
|
TEXT = 4
|
2018-09-30 20:54:34 +08:00
|
|
|
|
|
|
|
|
|
def __init__ (self):
|
2018-10-19 22:02:04 +08:00
|
|
|
|
Gtk.ListStore.__init__ (self, int, bool, float, float, str)
|
2018-09-30 20:54:34 +08:00
|
|
|
|
|
|
|
|
|
__gsignals__ = {
|
|
|
|
|
"subprofile-added": (GObject.SIGNAL_RUN_FIRST,
|
|
|
|
|
None, (Gtk.Widget,)),
|
|
|
|
|
"subprofile-removed": (GObject.SIGNAL_RUN_FIRST,
|
|
|
|
|
None, (Gtk.Widget,)),
|
|
|
|
|
"path-changed": (GObject.SIGNAL_RUN_FIRST,
|
|
|
|
|
None, ())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def __init__ (self,
|
|
|
|
|
file,
|
|
|
|
|
function,
|
|
|
|
|
frames,
|
|
|
|
|
*args,
|
|
|
|
|
**kwargs):
|
|
|
|
|
Gtk.Box.__init__ (self,
|
|
|
|
|
*args,
|
|
|
|
|
orientation = Gtk.Orientation.VERTICAL,
|
|
|
|
|
**kwargs)
|
|
|
|
|
|
|
|
|
|
self.file = file
|
|
|
|
|
self.frames = frames
|
|
|
|
|
|
|
|
|
|
header = Gtk.HeaderBar (title = file.get_basename (),
|
|
|
|
|
subtitle = function)
|
|
|
|
|
self.header = header
|
|
|
|
|
self.pack_start (header, False, False, 0)
|
|
|
|
|
header.show ()
|
|
|
|
|
|
|
|
|
|
box = Gtk.Box (orientation = Gtk.Orientation.HORIZONTAL)
|
|
|
|
|
header.pack_start (box)
|
|
|
|
|
box.get_style_context ().add_class ("linked")
|
|
|
|
|
box.get_style_context ().add_class ("raised")
|
|
|
|
|
box.show ()
|
|
|
|
|
|
|
|
|
|
button = Gtk.Button.new_from_icon_name ("go-up-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
self.prev_button = button
|
|
|
|
|
box.pack_start (button, False, True, 0)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button.connect ("clicked", lambda *args: self.move (-1))
|
|
|
|
|
|
|
|
|
|
button = Gtk.Button.new_from_icon_name ("go-down-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
self.next_button = button
|
|
|
|
|
box.pack_end (button, False, True, 0)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button.connect ("clicked", lambda *args: self.move (+1))
|
|
|
|
|
|
|
|
|
|
button = Gtk.Button.new_from_icon_name ("edit-select-all-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
self.select_samples_button = button
|
|
|
|
|
header.pack_end (button)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button.connect ("clicked", self.select_samples_clicked)
|
|
|
|
|
|
|
|
|
|
button = Gtk.Button.new_from_icon_name ("text-x-generic-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
header.pack_end (button)
|
|
|
|
|
button.set_tooltip_text (file.get_path ())
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
button.connect ("clicked", self.view_source_clicked)
|
|
|
|
|
|
|
|
|
|
scrolled = Gtk.ScrolledWindow (
|
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.NEVER,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.AUTOMATIC
|
|
|
|
|
)
|
|
|
|
|
self.pack_start (scrolled, True, True, 0)
|
|
|
|
|
scrolled.show ()
|
|
|
|
|
|
|
|
|
|
store = self.Store ()
|
|
|
|
|
self.store = store
|
|
|
|
|
|
|
|
|
|
tree = Gtk.TreeView (model = store)
|
|
|
|
|
self.tree = tree
|
|
|
|
|
scrolled.add (tree)
|
|
|
|
|
tree.set_search_column (store.LINE)
|
|
|
|
|
tree.show ()
|
|
|
|
|
|
|
|
|
|
tree.get_selection ().connect ("changed",
|
|
|
|
|
self.tree_selection_changed)
|
|
|
|
|
|
|
|
|
|
scale = 0.85
|
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "Self")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_alignment (0.5)
|
|
|
|
|
col.set_sort_column_id (store.EXCLUSIVE)
|
|
|
|
|
|
2018-10-19 22:02:04 +08:00
|
|
|
|
cell = CellRendererPercentage (scale = scale)
|
2018-09-30 20:54:34 +08:00
|
|
|
|
col.pack_start (cell, False)
|
2018-10-19 22:02:04 +08:00
|
|
|
|
col.add_attribute (cell, "visible", store.HAS_FRAMES)
|
|
|
|
|
col.add_attribute (cell, "value", store.EXCLUSIVE)
|
2018-09-30 20:54:34 +08:00
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn (title = "All")
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
col.set_alignment (0.5)
|
|
|
|
|
col.set_sort_column_id (store.INCLUSIVE)
|
|
|
|
|
|
2018-10-19 22:02:04 +08:00
|
|
|
|
cell = CellRendererPercentage (scale = scale)
|
2018-09-30 20:54:34 +08:00
|
|
|
|
col.pack_start (cell, False)
|
2018-10-19 22:02:04 +08:00
|
|
|
|
col.add_attribute (cell, "visible", store.HAS_FRAMES)
|
|
|
|
|
col.add_attribute (cell, "value", store.INCLUSIVE)
|
2018-09-30 20:54:34 +08:00
|
|
|
|
|
|
|
|
|
col = Gtk.TreeViewColumn ()
|
|
|
|
|
tree.append_column (col)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText (xalign = 1,
|
|
|
|
|
xpad = 8,
|
|
|
|
|
family = "Monospace",
|
|
|
|
|
weight = Pango.Weight.BOLD,
|
|
|
|
|
scale =scale)
|
|
|
|
|
col.pack_start (cell, False)
|
|
|
|
|
col.add_attribute (cell, "text", store.LINE)
|
|
|
|
|
|
|
|
|
|
cell = Gtk.CellRendererText (family = "Monospace",
|
|
|
|
|
scale = scale)
|
|
|
|
|
col.pack_start (cell, True)
|
|
|
|
|
col.add_attribute (cell, "text", store.TEXT)
|
|
|
|
|
|
|
|
|
|
self.update ()
|
|
|
|
|
|
|
|
|
|
def get_samples (self):
|
|
|
|
|
sel_rows = self.tree.get_selection ().get_selected_rows ()[1]
|
|
|
|
|
|
|
|
|
|
if sel_rows:
|
|
|
|
|
line = self.store[sel_rows[0]][self.store.LINE]
|
|
|
|
|
|
|
|
|
|
sel = {frame.sample for frame in self.frames
|
|
|
|
|
if frame.stack[frame.i].info.line == line}
|
|
|
|
|
|
|
|
|
|
return sel
|
|
|
|
|
else:
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
def update (self):
|
|
|
|
|
self.update_store ()
|
|
|
|
|
self.update_ui ()
|
|
|
|
|
|
|
|
|
|
def update_store (self):
|
|
|
|
|
stacks = {}
|
|
|
|
|
lines = {}
|
|
|
|
|
|
|
|
|
|
for frame in self.frames:
|
|
|
|
|
info = frame.stack[frame.i].info
|
|
|
|
|
line_id = info.line
|
|
|
|
|
stack_id = builtins.id (frame.stack)
|
|
|
|
|
|
|
|
|
|
line = lines.get (line_id, None)
|
|
|
|
|
|
|
|
|
|
if not line:
|
|
|
|
|
line = [0, 0]
|
|
|
|
|
lines[line_id] = line
|
|
|
|
|
|
|
|
|
|
stack = stacks.get (stack_id, None)
|
|
|
|
|
|
|
|
|
|
if not stack:
|
|
|
|
|
stack = set ()
|
|
|
|
|
stacks[stack_id] = stack
|
|
|
|
|
|
|
|
|
|
if frame.i == 0:
|
|
|
|
|
line[0] += 1
|
|
|
|
|
|
|
|
|
|
if line_id not in stack:
|
|
|
|
|
stack.add (line_id)
|
|
|
|
|
|
|
|
|
|
line[1] += 1
|
|
|
|
|
|
|
|
|
|
self.lines = list (lines.keys ())
|
|
|
|
|
self.lines.sort ()
|
|
|
|
|
|
|
|
|
|
n_stacks = len (stacks)
|
|
|
|
|
|
|
|
|
|
self.store.clear ()
|
|
|
|
|
|
|
|
|
|
i = 1
|
|
|
|
|
|
|
|
|
|
for text in open (self.file.get_path (), "r"):
|
|
|
|
|
text = text.rstrip ("\n")
|
|
|
|
|
|
2018-10-19 22:02:04 +08:00
|
|
|
|
line = lines.get (i, None)
|
2018-09-30 20:54:34 +08:00
|
|
|
|
|
2018-10-19 22:02:04 +08:00
|
|
|
|
if line:
|
|
|
|
|
self.store.append ((i,
|
|
|
|
|
True,
|
|
|
|
|
line[0] / n_stacks,
|
|
|
|
|
line[1] / n_stacks,
|
|
|
|
|
text))
|
|
|
|
|
else:
|
|
|
|
|
self.store.append ((i,
|
|
|
|
|
False,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
text))
|
2018-09-30 20:54:34 +08:00
|
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
|
|
self.select (max (lines.items (), key = lambda line: line[1][1])[0])
|
|
|
|
|
|
|
|
|
|
def update_ui (self):
|
|
|
|
|
sel_rows = self.tree.get_selection ().get_selected_rows ()[1]
|
|
|
|
|
|
|
|
|
|
if sel_rows:
|
|
|
|
|
line = self.store[sel_rows[0]][self.store.LINE]
|
|
|
|
|
|
|
|
|
|
i = bisect.bisect_left (self.lines, line)
|
|
|
|
|
|
|
|
|
|
self.prev_button.set_sensitive (i > 0)
|
|
|
|
|
|
|
|
|
|
if i < len (self.lines) and self.lines[i] == line:
|
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
|
|
self.next_button.set_sensitive (i < len (self.lines))
|
|
|
|
|
else:
|
|
|
|
|
self.prev_button.set_sensitive (False)
|
|
|
|
|
self.next_button.set_sensitive (False)
|
|
|
|
|
|
|
|
|
|
samples = self.get_samples ()
|
|
|
|
|
|
|
|
|
|
if samples:
|
|
|
|
|
self.select_samples_button.set_sensitive (True)
|
|
|
|
|
self.select_samples_button.set_tooltip_text (
|
|
|
|
|
str (Selection (samples))
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
self.select_samples_button.set_sensitive (False)
|
|
|
|
|
self.select_samples_button.set_tooltip_text (None)
|
|
|
|
|
|
|
|
|
|
def select (self, line):
|
|
|
|
|
if line is not None:
|
|
|
|
|
for row in self.store:
|
|
|
|
|
if row[self.store.LINE] == line:
|
|
|
|
|
iter = row.iter
|
|
|
|
|
path = self.store.get_path (iter)
|
|
|
|
|
|
|
|
|
|
self.tree.get_selection ().select_iter (iter)
|
|
|
|
|
|
|
|
|
|
self.tree.scroll_to_cell (path, None, True, 0.5, 0)
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
self.tree.get_selection ().unselect_all ()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def move (self, dir):
|
|
|
|
|
if dir == 0:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
sel_rows = self.tree.get_selection ().get_selected_rows ()[1]
|
|
|
|
|
|
|
|
|
|
if sel_rows:
|
|
|
|
|
line = self.store[sel_rows[0]][self.store.LINE]
|
|
|
|
|
|
|
|
|
|
i = bisect.bisect_left (self.lines, line)
|
|
|
|
|
|
|
|
|
|
if dir < 0:
|
|
|
|
|
i -= 1
|
|
|
|
|
elif i < len (self.lines) and self.lines[i] == line:
|
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
|
|
if i >= 0 and i < len (self.lines):
|
|
|
|
|
self.select (self.lines[i])
|
|
|
|
|
else:
|
|
|
|
|
self.select (None)
|
|
|
|
|
|
|
|
|
|
def select_samples_clicked (self, button):
|
|
|
|
|
selection.select (self.get_samples ())
|
|
|
|
|
|
|
|
|
|
selection.change_complete ()
|
|
|
|
|
|
|
|
|
|
def view_source_clicked (self, button):
|
|
|
|
|
line = 0
|
|
|
|
|
|
|
|
|
|
sel_rows = self.tree.get_selection ().get_selected_rows ()[1]
|
|
|
|
|
|
|
|
|
|
if sel_rows:
|
|
|
|
|
line = self.store[sel_rows[0]][self.store.LINE]
|
|
|
|
|
|
|
|
|
|
run_editor (self.file, line)
|
|
|
|
|
|
|
|
|
|
def get_path (self):
|
|
|
|
|
tree_sel = self.tree.get_selection ()
|
|
|
|
|
|
|
|
|
|
sel_rows = tree_sel.get_selected_rows ()[1]
|
|
|
|
|
|
|
|
|
|
if not sel_rows:
|
|
|
|
|
return ()
|
|
|
|
|
|
|
|
|
|
line = self.store[sel_rows[0]][self.store.LINE]
|
|
|
|
|
|
|
|
|
|
return (line,)
|
|
|
|
|
|
|
|
|
|
def set_path (self, path):
|
|
|
|
|
self.select (path[0] if path else None)
|
|
|
|
|
|
|
|
|
|
def tree_selection_changed (self, tree_sel):
|
|
|
|
|
self.update_ui ()
|
|
|
|
|
|
|
|
|
|
self.emit ("path-changed")
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.ScrolledWindow.__init__ (
|
|
|
|
|
self,
|
|
|
|
|
*args,
|
|
|
|
|
hscrollbar_policy = Gtk.PolicyType.AUTOMATIC,
|
|
|
|
|
vscrollbar_policy = Gtk.PolicyType.NEVER,
|
|
|
|
|
**kwargs
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
self.adjustment_changed_handler = None
|
2018-09-23 11:23:28 +08:00
|
|
|
|
self.needs_update = True
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.path = ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
profile = self.Profile ()
|
|
|
|
|
self.root_profile = profile
|
|
|
|
|
self.add (profile)
|
|
|
|
|
profile.show ()
|
|
|
|
|
|
2018-09-23 11:23:28 +08:00
|
|
|
|
selection.connect ("change-complete", self.selection_change_complete)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
profile.connect ("needs-update", self.profile_needs_update)
|
|
|
|
|
profile.connect ("subprofile-added", self.profile_subprofile_added)
|
|
|
|
|
profile.connect ("subprofile-removed", self.profile_subprofile_removed)
|
2018-09-30 20:54:34 +08:00
|
|
|
|
profile.connect ("path-changed", self.profile_path_changed)
|
2018-09-29 04:17:28 +08:00
|
|
|
|
|
|
|
|
|
history.add_source (self.source_get, self.source_set)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
@GObject.Property (type = bool, default = False)
|
|
|
|
|
def available (self):
|
|
|
|
|
sel = selection.get_effective_selection ()
|
|
|
|
|
|
|
|
|
|
if len (sel) > 1:
|
|
|
|
|
return any (samples[i].backtrace for i in sel)
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def update (self):
|
2018-09-29 12:14:38 +08:00
|
|
|
|
if not self.available:
|
2018-09-23 11:23:28 +08:00
|
|
|
|
return
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
history.block ()
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
if self.needs_update:
|
|
|
|
|
self.root_profile.update ()
|
|
|
|
|
|
|
|
|
|
self.needs_update = False
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.root_profile.set_path (self.path)
|
|
|
|
|
|
|
|
|
|
history.unblock ()
|
2018-09-23 11:23:28 +08:00
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
def queue_update (self, now = False):
|
|
|
|
|
self.needs_update = True
|
|
|
|
|
|
|
|
|
|
if now or self.get_mapped ():
|
|
|
|
|
self.update ()
|
2018-09-23 11:23:28 +08:00
|
|
|
|
|
|
|
|
|
def do_map (self):
|
|
|
|
|
self.update ()
|
|
|
|
|
|
|
|
|
|
Gtk.ScrolledWindow.do_map (self)
|
|
|
|
|
|
|
|
|
|
def selection_change_complete (self, selection):
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.queue_update ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
self.notify ("available")
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
def profile_needs_update (self, profile, now):
|
|
|
|
|
self.queue_update (now)
|
|
|
|
|
|
|
|
|
|
def profile_subprofile_added (self, profile, subprofile):
|
|
|
|
|
if not history.is_blocked ():
|
|
|
|
|
self.path = profile.get_path ()
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.record ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
if not self.adjustment_changed_handler:
|
|
|
|
|
adjustment = self.get_hadjustment ()
|
|
|
|
|
|
|
|
|
|
def adjustment_changed (adjustment):
|
|
|
|
|
GObject.signal_handler_disconnect (
|
|
|
|
|
adjustment,
|
|
|
|
|
self.adjustment_changed_handler
|
|
|
|
|
)
|
|
|
|
|
self.adjustment_changed_handler = None
|
|
|
|
|
|
|
|
|
|
adjustment.set_value (adjustment.get_upper ())
|
|
|
|
|
|
|
|
|
|
self.adjustment_changed_handler = adjustment.connect (
|
|
|
|
|
"changed",
|
|
|
|
|
adjustment_changed
|
|
|
|
|
)
|
|
|
|
|
|
2018-09-29 12:14:38 +08:00
|
|
|
|
def profile_subprofile_removed (self, profile, subprofile):
|
|
|
|
|
if not history.is_blocked ():
|
|
|
|
|
self.path = profile.get_path ()
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.record ()
|
|
|
|
|
|
2018-09-30 20:54:34 +08:00
|
|
|
|
|
|
|
|
|
def profile_path_changed (self, profile):
|
|
|
|
|
if not history.is_blocked ():
|
|
|
|
|
self.path = profile.get_path ()
|
|
|
|
|
|
|
|
|
|
history.update ()
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
def source_get (self):
|
2018-09-29 12:14:38 +08:00
|
|
|
|
return self.path
|
2018-09-29 04:17:28 +08:00
|
|
|
|
|
|
|
|
|
def source_set (self, path):
|
2018-09-29 12:14:38 +08:00
|
|
|
|
self.path = path
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
if self.get_mapped ():
|
|
|
|
|
self.root_profile.set_path (path)
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
class LogViewer (Gtk.Window):
|
|
|
|
|
def __init__ (self, *args, **kwargs):
|
|
|
|
|
Gtk.Window.__init__ (
|
|
|
|
|
self,
|
|
|
|
|
*args,
|
|
|
|
|
default_width = 1024,
|
|
|
|
|
default_height = 768,
|
|
|
|
|
window_position = Gtk.WindowPosition.CENTER,
|
|
|
|
|
**kwargs)
|
|
|
|
|
|
|
|
|
|
header = Gtk.HeaderBar (
|
|
|
|
|
title = "GIMP Performance Log Viewer",
|
|
|
|
|
show_close_button = True
|
|
|
|
|
)
|
|
|
|
|
self.header = header
|
|
|
|
|
self.set_titlebar (header)
|
|
|
|
|
header.show ()
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
box = Gtk.Box (orientation = Gtk.Orientation.HORIZONTAL)
|
|
|
|
|
header.pack_start (box)
|
|
|
|
|
box.get_style_context ().add_class ("linked")
|
|
|
|
|
box.get_style_context ().add_class ("raised")
|
|
|
|
|
box.show ()
|
|
|
|
|
|
|
|
|
|
button = Gtk.Button.new_from_icon_name ("go-previous-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
box.pack_start (button, False, True, 0)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
history.bind_property ("can-undo",
|
|
|
|
|
button, "sensitive",
|
|
|
|
|
GObject.BindingFlags.SYNC_CREATE)
|
|
|
|
|
|
|
|
|
|
button.connect ("clicked", lambda *args: history.undo ())
|
|
|
|
|
|
|
|
|
|
button = Gtk.Button.new_from_icon_name ("go-next-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
box.pack_end (button, False, True, 0)
|
|
|
|
|
button.show ()
|
|
|
|
|
|
|
|
|
|
history.bind_property ("can-redo",
|
|
|
|
|
button, "sensitive",
|
|
|
|
|
GObject.BindingFlags.SYNC_CREATE)
|
|
|
|
|
|
|
|
|
|
button.connect ("clicked", lambda *args: history.redo ())
|
|
|
|
|
|
2018-09-21 14:23:37 +08:00
|
|
|
|
button = Gtk.MenuButton ()
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
header.pack_end (button)
|
|
|
|
|
button.set_tooltip_text ("Find samples")
|
|
|
|
|
button.show ()
|
|
|
|
|
|
2018-09-21 14:23:37 +08:00
|
|
|
|
image = Gtk.Image.new_from_icon_name ("edit-find-symbolic",
|
|
|
|
|
Gtk.IconSize.BUTTON)
|
|
|
|
|
button.add (image)
|
|
|
|
|
image.show ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
popover = FindSamplesPopover (relative_to = button)
|
|
|
|
|
self.find_popover = popover
|
2018-09-21 14:23:37 +08:00
|
|
|
|
button.set_popover (popover)
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
|
|
|
|
|
paned = Gtk.Paned (orientation = Gtk.Orientation.VERTICAL)
|
|
|
|
|
self.paned = paned
|
|
|
|
|
self.add (paned)
|
|
|
|
|
paned.set_position (144)
|
|
|
|
|
paned.show ()
|
|
|
|
|
|
|
|
|
|
graphs = SampleGraphList ()
|
|
|
|
|
paned.add1 (graphs)
|
|
|
|
|
paned.child_set (graphs, shrink = False)
|
|
|
|
|
graphs.show ()
|
|
|
|
|
|
|
|
|
|
hbox = Gtk.Box (orientation = Gtk.Orientation.HORIZONTAL)
|
|
|
|
|
paned.add2 (hbox)
|
|
|
|
|
hbox.show ()
|
|
|
|
|
|
|
|
|
|
sidebar = Gtk.StackSidebar ()
|
|
|
|
|
hbox.pack_start (sidebar, False, False, 0)
|
|
|
|
|
sidebar.show ()
|
|
|
|
|
|
|
|
|
|
stack = Gtk.Stack (transition_type = Gtk.StackTransitionType.CROSSFADE)
|
|
|
|
|
self.stack = stack
|
|
|
|
|
hbox.pack_start (stack, True, True, 0)
|
|
|
|
|
stack.show ()
|
|
|
|
|
|
|
|
|
|
sidebar.set_stack (stack)
|
|
|
|
|
|
|
|
|
|
info_viewer = InformationViewer ()
|
|
|
|
|
stack.add_titled (info_viewer, "information", "Information")
|
|
|
|
|
info_viewer.show ()
|
|
|
|
|
|
|
|
|
|
vars_viewer = VariablesViewer ()
|
|
|
|
|
stack.add_titled (vars_viewer, "variables", "Variables")
|
|
|
|
|
vars_viewer.show ()
|
|
|
|
|
|
|
|
|
|
box = Gtk.Box (orientation = Gtk.Orientation.VERTICAL)
|
|
|
|
|
self.cflow_box = box
|
|
|
|
|
stack.add_named (box, "cflow")
|
|
|
|
|
|
|
|
|
|
backtrace_viewer = BacktraceViewer ()
|
|
|
|
|
self.backtrace_viewer = backtrace_viewer
|
|
|
|
|
box.pack_start (backtrace_viewer, True, True, 0)
|
|
|
|
|
|
|
|
|
|
backtrace_viewer.bind_property ("available",
|
|
|
|
|
backtrace_viewer, "visible",
|
|
|
|
|
GObject.BindingFlags.SYNC_CREATE)
|
|
|
|
|
|
|
|
|
|
backtrace_viewer.connect ("notify::available",
|
|
|
|
|
self.cflow_notify_available)
|
|
|
|
|
|
|
|
|
|
profile_viewer = ProfileViewer ()
|
|
|
|
|
self.profile_viewer = profile_viewer
|
|
|
|
|
box.pack_start (profile_viewer, True, True, 0)
|
|
|
|
|
|
|
|
|
|
profile_viewer.bind_property ("available",
|
|
|
|
|
profile_viewer, "visible",
|
|
|
|
|
GObject.BindingFlags.SYNC_CREATE)
|
|
|
|
|
|
|
|
|
|
profile_viewer.connect ("notify::available",
|
|
|
|
|
self.cflow_notify_available)
|
|
|
|
|
|
|
|
|
|
self.cflow_notify_available (self)
|
|
|
|
|
|
|
|
|
|
selection.connect ("change-complete", self.selection_change_complete)
|
|
|
|
|
|
|
|
|
|
self.selection_change_complete (selection)
|
|
|
|
|
|
|
|
|
|
def selection_change_complete (self, selection):
|
|
|
|
|
self.header.set_subtitle (str (selection))
|
|
|
|
|
|
|
|
|
|
def cflow_notify_available (self, *args):
|
|
|
|
|
if self.backtrace_viewer.available:
|
|
|
|
|
self.stack.child_set (self.cflow_box, title = "Backtrace")
|
|
|
|
|
self.cflow_box.show ()
|
|
|
|
|
elif self.profile_viewer.available:
|
|
|
|
|
self.stack.child_set (self.cflow_box, title = "Profile")
|
|
|
|
|
self.cflow_box.show ()
|
|
|
|
|
else:
|
|
|
|
|
self.cflow_box.hide ()
|
|
|
|
|
|
|
|
|
|
Gtk.Settings.get_default ().set_property ("gtk-application-prefer-dark-theme",
|
|
|
|
|
True)
|
|
|
|
|
|
|
|
|
|
window = LogViewer ()
|
|
|
|
|
window.show ()
|
|
|
|
|
|
|
|
|
|
window.connect ("destroy", Gtk.main_quit)
|
|
|
|
|
|
2018-09-29 04:17:28 +08:00
|
|
|
|
history.record ()
|
|
|
|
|
|
tools: add performance-log-viewer.py and driver
performance-log-viewer.py is a viewer for GIMP performance logs.
The viewer is made up of two parts: a sample-selection area at the
top, and an information area at the bottom.
The sample-selection area visualizes the sampled variables and
markers using a simultaneous set of plots, and displays the
currently selected samples. Samples can be selected directly
through the sample-selection area, or by other means, such as
searching for all samples satisfying a certain condition, or
containing a certain function.
The information area shows global information stored in the log, as
well as information specific to the currently selected samples,
including variable listing and statistics, full backtrace, and
profile/call-graph information.
Note that performance-log-viewer.py takes its input from STDIN,
like the rest of the performance-log tools, and is therefore
suitable for use as part of a pipeline. For standalone use, the
performance-log-viewer driver is also included, which takes the log
file as a command-line argument, and processes it through an
appropriate pipeline before feeding it to the viewer.
2018-09-16 23:34:37 +08:00
|
|
|
|
Gtk.main ()
|