summaryrefslogtreecommitdiffstats
path: root/devel-docs/performance-logs/performance-logs.md
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:13:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 03:13:10 +0000
commit3c57dd931145d43f2b0aef96c4d178135956bf91 (patch)
tree3de698981e9f0cc2c4f9569b19a5f3595e741f6b /devel-docs/performance-logs/performance-logs.md
parentInitial commit. (diff)
downloadgimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz
gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devel-docs/performance-logs/performance-logs.md')
-rw-r--r--devel-docs/performance-logs/performance-logs.md651
1 files changed, 651 insertions, 0 deletions
diff --git a/devel-docs/performance-logs/performance-logs.md b/devel-docs/performance-logs/performance-logs.md
new file mode 100644
index 0000000..86e0746
--- /dev/null
+++ b/devel-docs/performance-logs/performance-logs.md
@@ -0,0 +1,651 @@
+# Performance Logs
+
+GIMP 2.10.8 and above has the ability to record *performance logs* containing
+detailed information about the run-time behavior of the program, helping
+locating and analyzing performance bottlenecks.
+
+This document describes how to record and view performance logs, and how to
+report performance-related issues.
+
+## Table of Contents
+
+ - [1. Recording Performance Logs](#1-recording-performance-logs)
+ - [1.1. Event Markers](#11-event-markers)
+ - [1.2. Log Parameters](#12-log-parameters)
+ - [2. Reporting Performance-Related Issues](#2-reporting-performance-related-issues)
+ - [3. What Information is Contained in the Log?](#3-what-information-is-contained-in-the-log)
+ - [4. Viewing Performance Logs](#4-viewing-performance-logs)
+ - [4.1. Sample-Selection Area](#41-sample-selection-area)
+ - [4.1.1. Selecting Samples](#411-selecting-samples)
+ - [4.2. Information Area](#42-information-area)
+ - [4.2.1. Information Page](#421-information-page)
+ - [4.2.2. Markers Page](#422-markers-page)
+ - [4.2.3. Variables Page](#423-variables-page)
+ - [4.2.4. Backtrace Page](#424-backtrace-page)
+ - [4.2.4.1. Threads Pane](#4241-threads-pane)
+ - [4.2.4.2. Stack Pane](#4242-stack-pane)
+ - [4.2.5. Profile Page](#425-profile-page)
+ - [4.2.5.1. Root Column](#4251-root-column)
+ - [4.2.5.1.1. Thread Filter](#42511-thread-filter)
+ - [4.2.5.1.2. Call-Graph Direction](#42512-call-graph-direction)
+ - [4.2.5.2. Function Columns](#4252-function-columns)
+ - [4.2.5.3. Source Columns](#4253-source-columns)
+ - [4.3. Selection Modifiers](#43-selection-modifiers)
+ - [4.3.1. Searching Samples](#431-searching-samples)
+ - [4.4. History Navigation](#44-history-navigation)
+ - [4.5. Environment Variables](#45-environment-variables)
+
+## 1. Recording Performance Logs
+
+Performance logs track the program state while the program is normally running.
+In the usual case, you would record a performance log while performing a task
+whose performance you wish to optimize, or against which you wish to report a
+performance-related bug.
+The recorded log can then be used to investigate the run-time behavior of the
+program during the execution of the task.
+
+![Start recording a performance log](dashboard-start-recording.png)
+
+To record a performance log, open the *Dashboard* dockable (*Windows ▸
+Dockable Dialogs ▸ Dashboard*), click the *Record* button at the
+bottom of the dockable, and select a filename for the log.
+The *Record* button will subsequently change its color, indicating that the log
+is being recorded.
+
+At this point, you should perform the task of interest as you normally would,
+while state information is being recorded.
+The log contains periodic samples of the program state during execution, and it
+is therefore important to keep performing the task long enough (possibly
+repeating it multiple times) so that a sufficiently large number of samples is
+collected.
+30 seconds or more is a good amount of time (longer periods improve the quality
+of the log, while increasing its size).
+
+![Stop recording a performance log](dashboard-stop-recording.png)
+
+When you're done performing the task, stop recording the log by pressing the
+*Record* button again.
+At this point, GIMP will perform any necessary final steps, which may take a
+while (usually, no more than a few seconds).
+At the end of this process, the log is ready.
+
+*Note:*
+In situations where you can't cleanly stop recording the log, such as
+when GIMP crashes or freezes in the middle of the process, make sure to record
+a *progressive* log, as described in [section *1.2*](#12-log-parameters).
+
+### 1.1. Event Markers
+
+When the recorded task is made up of multiple steps, it is useful to have an
+indication of where the different steps begin and end within the log.
+This is achieved using *event markers*.
+An event marker is an annotated time-point in the log, signifying the
+occurrence of an event of interest, such as the beginning or end of a logical
+step.
+It is up to you to add event markers to the log, as you see necessary.
+
+![Add an event marker to a performance log](dashboard-add-marker.png)
+
+To add an event marker, click the *Add Marker* button at the bottom of the
+Dashboard dockable, next to the *Record* button, and enter a short description
+for the corresponding event.
+(Note that the *Add Marker* button is only available while a log is being
+recorded.)
+You may also click the button while holding the *Shift* key, to add an *empty*
+event marker, not containing a description; this is useful when event markers
+are needed to be added quickly.
+
+Event markers are numbered sequentially, and may be referred to by their
+number (this is especially useful for empty event markers.)
+The number of the next event marker (the one that will be added when the button
+is clicked) is displayed on the *Add Marker* button.
+
+### 1.2. Log Parameters
+
+When creating a performance log, several parameters can be set at the bottom of
+the file-selection dialog.
+There should normally be no reason to change the parameters, unless noted
+otherwise.
+
+![Performance-log parameters](performance-log-parameters.png)
+
+ - *Sample Frequency:*
+ The number of program-state samples per second.
+ Higher values improve the resolution of the log, while making the recording
+ itself more expensive.
+
+ - *Backtrace:*
+ Whether or not to include backtraces in the log.
+ Backtraces capture the code-paths taken while recording the log, and should
+ normally be included.
+
+ - *Messages:*
+ Whether or not to include diagnostic messages in the log.
+ Diagnostic messages indicate errors and unexpected conditions, and should
+ normally be included.
+
+ - *Progressive:*
+ Whether or not to record a progressive log.
+
+ GIMP normally defers certain information to the end of the log, in order to
+ minimize the cost of recording.
+ If the log can't be cleanly finished—for example, if GIMP crashes or
+ freezes while recording the log—this information will be missing, rendering
+ the log of limited use.
+ In situations like these, a progressive log should be recorded; progressive
+ logs include all relevant information at each sample, allowing them to be
+ terminated at any time.
+
+ Progressive logs may incur a higher overhead while recording.
+
+## 2. Reporting Performance-Related Issues
+
+![Reporting a performance-related issue](new-performance-issue.png)
+
+When reporting a performance-related issue, use the
+[*performance* template][new-performance-issue] in GIMP's GitLab issue
+tracker.
+Provide a detailed description of the task you were performing, and attach a
+performance log.
+If the performance log is too big, attach a compressed version.
+If you've added any empty event markers to the log, provide a description for
+these markers in the report.
+
+Note that the log alone, while containing plenty of low-level information,
+doesn't in itself explain *what* you were doing, nor does it contain all the
+relevant details, so please *do* take time to describe the issue in addition to
+the log.
+Attaching a screencast of the process, possibly *while* recording the log, can
+also be very helpful.
+
+## 3. What Information is Contained in the Log?
+
+The log consists mainly of a series of periodic *samples* of the program state.
+(The default sampling rate is 10 samples per second; see
+[section *1.2*](#12-log-parameters) for a way to modify this value.)
+Each sample contains the values of all the *instrumentation variables*
+displayed in the Dashboard.
+Additionally, on supported platforms, each sample contains a full *program
+backtrace*.
+(Backtraces are currently supported on Linux and Windows; their level of detail
+might depend on the available libraries and build-time options.)
+When debugging information is available, corresponding source-location
+information is included.
+
+Additionally, the log contains certain global information, not related to any
+sample.
+This includes GIMP version information, performance-log parameters (such as the
+sampling rate), GEGL configuration parameters (such as the tile size), and the
+values of all environment variables starting with `GIMP_`, `GEGL_`, or `BABL_`.
+
+## 4. Viewing Performance Logs
+
+![Performance-log viewer](performance-log-viewer.png)
+
+The GIMP source tree includes a graphical viewer for performance logs, under
+the `tools/` directory.
+The viewer is not included as part of GIMP installations, but is distributed as
+part of source tarballs.
+
+To view a performance log, run:
+
+ $GIMP_SRC_DIR/tools/performance-log-viewer $LOG_FILE
+
+where `$GIMP_SRC_DIR` is the root of the GIMP source tree, and `$LOG_FILE` is
+the log filename.
+
+Note that there is no need to build or install GIMP in order to use the log
+viewer.
+Indeed, only the files prefixed with `performance-log` in the `tools/`
+directory are actually needed.
+The log viewer does require, however, a POSIX-compatible shell, Python 3,
+PyGObject, and GTK+ 3 (including gir bindings).
+
+The rest of this section describes how to use the log viewer.
+
+### 4.1. Sample-Selection Area
+
+The upper part of the viewer window is the *sample-selection area*.
+It consists of one or more graphs, visualizing a selected subset of the
+instrumentation variables at each sample.
+A variable list to the left of each graph is used to select the set of
+variables to be visualized.
+The list is searchable by variable name, and its tooltip shows the variable
+descriptions.
+A row buttons below the list allows adding, removing, and reordering the
+graphs.
+
+Note that all variables visualized by the same graph share the same scale.
+It therefore makes sense to visualize variables that measure related
+quantities, such as `cache-occupied` and `swap-occupied`, using a single graph,
+while visualizing variables that measure unrelated quantities, such as
+`cache-occupied` and `cpu-usage`, using separate graphs.
+Boolean variables, such as `cpu-active`, are an exception, and always use the
+full height of the graph, regardless of the other variables they are visualized
+along.
+
+A dashed line in a graph indicates that the corresponding variable's value is
+infinite.
+A missing line in part of the graph indicates that a measurement of the
+corresponding variable is not available at the corresponding samples.
+
+Event markers are displayed as ticks above the graphs, at the nearest sample.
+
+Hovering over the sample-selection area displays a tooltip, showing the current
+sample index, the time at which it was taken relative to the start of the log,
+the values of all visualized variables at the current sample, and the
+descriptions for all event markers corresponding to the current sample, if any.
+
+#### 4.1.1. Selecting Samples
+
+In addition to visualizing the instrumentation variables, the sample-selection
+area is used to select a subset of the samples, referred to as the current
+*selection*, which affects the data displayed in the information area,
+described below.
+The samples included in the current selection are highlighted on the graphs,
+and a textual description of the current selection appears in the window's
+titlebar.
+
+A single sample can be selected by clicking any of the graphs at the
+corresponding area.
+Multiple samples can be selected by clicking and dragging.
+Entire spans of samples enclosed between a pair of event markers can be
+selected by double-clicking, or double-clicking and dragging.
+*Shift*, *Ctrl*, and *Shift+Ctrl* can be used as modifiers in order to
+add-to, subtract-from, and intersect-with the current selection, respectively.
+
+Samples can also be selected using the keyboard, with the arrow, *Home*/*End*,
+and *Shift* keys, while any of the graphs has focus.
+
+Right-clicking on any of the graphs, or pressing *Escape* while they have
+focus, clears the selection.
+The behavior when the selection is empty is equivalent to selecting all
+samples.
+
+*Ctrl*-right-clicking on any of the graphs inverts the selection.
+
+### 4.2. Information Area
+
+The lower part of the viewer window is the *information area*.
+It consists of multiple *pages* showing different data, generally relating to
+the current selection of samples.
+
+#### 4.2.1. Information Page
+
+The *information page* shows global information contained in the log, not
+associated with any sample, including:
+
+ - *Log Parameters*:
+ Various parameters relating to the performance log itself.
+ See [section *1.2*](#12-log-parameters) for more information.
+
+ - *GIMP Version*:
+ Verbose GIMP version information, as reported by `gimp -v`.
+
+ - *Environment*:
+ A list of environment variables for the GIMP process recording the log,
+ starting with `GIMP_`, `GEGL_`, or `BABL_`.
+ These environment variables directly affect the behavior of GIMP.
+
+ - *GEGL Config*:
+ A list of all the properties of the `GeglConfig` object of the GIMP process
+ recording the log.
+
+The key/value lists are searchable by key name.
+
+#### 4.2.2. Markers Page
+
+The *markers page* lists the event markers contained in the log, displaying
+their number, relative time, and description.
+It is only present in logs containing event markers.
+
+If the current selection contains samples corresponding to any markers, the
+markers are selected in the markers-page list. Conversely, if any markers are
+selected in the markers-page list, the corresponding samples are selected.
+
+#### 4.2.3. Variables Page
+
+The *variables page* shows instrumentation-variable statistics for the current
+selection.
+
+When a single sample is selected, the current values of all variables are
+listed, both in a formatted form, and in raw form as appears in the log file.
+The formatted form is usually more convenient, while the raw form is more
+precise.
+
+When multiple samples are selected, a number of statistics are listed for each
+variables, taken over the selected set of samples.
+This includes the minimum, maximum, and median values, the mean, and the
+standard deviation.
+
+The variable list is searchable by variable name, and its tooltip shows the
+variable descriptions.
+
+#### 4.2.4. Backtrace Page
+
+The *backtrace page* shows the program backtrace at the current sample.
+It is only available when a single sample is selected, in logs containing
+backtraces.
+
+##### 4.2.4.1. Threads Pane
+
+The *threads pane*, on the left side of the page, lists all active threads at
+the time of the sample, displaying the following information:
+
+ - *ID*:
+ The operating-system numeric thread-ID, uniquely identifying the thread.
+
+ - *Name*:
+ The descriptive thread name.
+ Note that not all threads may be named, or the name may not be available on
+ some platforms.
+
+ - *State*:
+ The thread's state.
+ May be one of:
+
+ - *`R`*:
+ The thread is in a *running* state, executing code on the CPU.
+
+ - *`S`*:
+ The thread is in a *sleeping* state, not executing any code.
+ This may include various types of non-running states on different
+ platforms.
+
+ Note that on some platforms, thread state information may not be fully
+ accurate.
+
+The thread list is searchable by thread name.
+
+Double-clicking on a thread selects all samples at which the thread is in the
+running state.
+
+##### 4.2.4.2. Stack Pane
+
+The *stack pane*, on the right side of the page, shows the selected thread's
+call stack at the time of the sample, displaying the following information:
+
+ - *Frame Number (#)*:
+ The stack-frame's nesting level, where 0 is the most-nested frame.
+
+ - *Address*:
+ The start address of the function.
+
+ - *Object*:
+ The object file containing the function.
+ The full path is available in the corresponding tooltip.
+
+ - *Function*:
+ The function name.
+
+ - *Offset*:
+ The offset of the current instruction, relative to the start of the
+ function.
+
+ - *Source*:
+ The source file corresponding to the current instruction.
+ The full path is available in the corresponding tooltip.
+
+ - *Line*:
+ The line number corresponding to the current instruction.
+
+Note that for some frames, only partial information may be available.
+
+When the source file for a given frame is found locally, a *file* icon is shown
+at the end of the corresponding row.
+The icon's tooltip shows the local path to the source file, and clicking on the
+icon opens the file in a text editor, at the corresponding line (when
+possible).
+See [section *4.5*](#45-environment-variables) for information regarding how to
+control local source-file lookup and the selected text editor.
+
+The frame list is searchable by function name.
+
+Double-clicking on a frame selects all samples at which the corresponding
+function is present in the backtrace.
+
+#### 4.2.5. Profile Page
+
+The *profile page* shows a fully context-sensitive *call graph*, annotated with
+frequency information, for the current selection.
+It replaces the backtrace page when multiple samples are selected, in logs
+containing backtraces.
+
+The call graph is comprised of all the call stacks at the selected samples.
+Note that each sample generally includes multiple call stacks—one per each
+thread.
+Some call stacks may be filtered out, as described below.
+
+The call graph is traversed using a series of cascading columns (aka *Miller
+columns*).
+Each non-root column lists the direct *descendants* (*callers* or *callees*) of
+a given function; selecting a descendant opens a new column to the right of the
+current column, showing the descendants of the selected function, and so on.
+
+##### 4.2.5.1. Root Column
+
+The *root column* of the call graph shows a list of all functions included in
+the graph.
+The following frequency statistics are shown next to each function:
+
+ - *Self*:
+ The number of call stacks in which the function appears at the most-nested
+ frame (when the graph direction is *caller → callee*), or at the
+ least-nested frame (when the graph direction is *callee → caller*), as a
+ percentage of the total number of call stacks.
+ When the graph direction is *caller → callee*, this quantity indicates the
+ relative amount of time spent executing code belonging exclusively to this
+ function.
+
+ - *All*:
+ The number of call stacks in which the function appears at any frame, as a
+ percentage of the total number of call stacks.
+ This quantity indicates the relative amount of time spent executing code
+ belonging to this function, or any of its (direct or indirect) descendant,
+ and is always greater-than or equal-to *Self*.
+
+By default, the list is sorted by *All*, but it can be sorted by *Self* or by
+function name as well.
+The list is searchable by function name.
+Pressing *Escape* while the list has focus deselects the current item.
+
+The root-column header buttons allow controlling the structure of the call
+graph:
+
+###### 4.2.5.1.1. Thread Filter
+
+The *Threads* button opens the *thread filter*, allowing control over which
+threads, and which states of each thread, are included in the graph.
+
+The thread filter lists all threads included in the current selection.
+Each thread is identified by ID and name, as described in
+[section *4.2.4.1*](#4241-threads-pane).
+Next to each thread is a row of toggles, corresponding to the different thread
+states; only call stacks during which the thread was in one of the active
+states are included in the graph.
+Clicking on a thread-state column title toggles the entire column.
+
+The thread list can be searched by thread name.
+
+###### 4.2.5.1.2. Call-Graph Direction
+
+By default, the graph direction is *caller → callee*—the direct descendants of
+each function are its callees.
+The *Call-Graph Direction* button allows toggling the graph between the *caller
+→ callee* direction, and the reverse *callee → caller* direction, in which the
+direct descendants of each function are its callers.
+
+##### 4.2.5.2. Function Columns
+
+When a function from the root column is selected, a new *function column* opens
+to the right of the root column, listing the direct descendants of the
+function.
+The descendants list has a similar structure to the function list of the root
+column.
+When a descendant function is selected, a subsequent function column opens to
+the right of current column, showing its direct descendants, and so on.
+In this manner, the call graph can be traversed according to its current
+direction.
+
+Note that the call graph is fully *context-sensitive*.
+This means that the listed descendants of the function depend on the current
+column's path in the graph, that is, on the call chain leading to the column.
+
+For example, suppose functions `foo1` and `foo2` both call `bar`, which, in
+turn, calls `baz1` and `baz2`; however, `bar` only calls `baz1` when called
+through `foo1`, and `baz2` when called through `foo2`.
+When selecting `foo1` in the root column, in the *caller → callee* direction,
+and subsequently selecting `bar` in `foo1`'s column, `bar`'s column will only
+list `baz1` as a descendant, since `baz2` is never called by `bar` when it is
+called through `foo1`.
+Likewise, when selecting `foo2` in the root column, and `bar` in `foo2`'s
+column, only `baz2` will be listed in `bar`'s column.
+
+Context-sensitivity extends to the frequency statistics as well:
+The only call stacks considered for each descendant are those in which it
+appears as part of a call chain matching the current column.
+In other words, the statistics are local to the current call chain, and are
+generally different from the global statistics for the same functions.
+
+In addition to the function's callees or callers, the descendants list includes
+a special *[Self]* item, corresponding to the current function.
+It is most useful for its *Self* percentage, which, in the *caller → callee*
+direction, indicates the amount of time spent executing code belonging
+exclusively to the current function, and not to any of its descendants,
+relative to the total amount of time spent executing the function as a whole.
+In other words, in non-recursive cases, the sum of *[Self]*'s *Self*
+percentage, and the rest of the descendants' *All* percentage, adds up to
+100%—the full execution time of the function.
+
+Double-clicking on any of the descendants selects the corresponding function in
+the root column, effectively discarding the current call-chain context.
+It is especially useful before swapping the graph's direction.
+
+The *Select Samples* button in the column header selects all the samples
+corresponding to the current column, that is, all the samples whose call stacks
+contribute to column.
+The button's tooltip shows a textual description of the samples.
+
+##### 4.2.5.3. Source Columns
+
+When the *[Self]* item of a function column is selected, if the log contains
+source-location information for the function, and the corresponding source file
+is found locally, a new *source column* opens to the right of the function
+column, showing the source code for the function.
+Source lines which appear as part of call stacks display similar frequency
+statistics to the root- and function-columns, and are referred to as *annotated
+lines*.
+Similarly to descendants in function columns, annotated lines are also
+context-sensitive.
+
+When a source column is opened, the annotated line with the highest *All*
+percentage is selected.
+The *Previous Annotated Line* and *Next Annotated Line* buttons on the left
+side of the column header can be used to quickly navigate between annotated
+lines.
+The source code is searchable by line number.
+
+The *Select Samples* button in the column header selects all the samples
+corresponding to the selected annotated source line.
+The button's tooltip shows a textual description of the samples.
+
+The file button in the column header opens the source file in a text editor, at
+the selected line, if possible (see
+[section *4.5*](#45-environment-variables)).
+The button's tooltip shows the full path to the file.
+
+### 4.3. Selection Modifiers
+
+The buttons on the right side of the window's titlebar allow modifying the
+selection in various ways:
+The *Clear Selection* and *Invert Selection* buttons clear and invert the
+selection, respectively.
+The *Find Samples* button allows searching for samples, as explained below.
+
+#### 4.3.1. Searching Samples
+
+The *Find Samples* button opens a popover allowing searching for all samples
+matching a user-defined condition.
+The condition should be a Python expression, evaluating to a boolean value.
+The selected samples are those for which the expression evaluates to `True`.
+
+A number of sample-dependent variables and functions are provided:
+
+ - *Instrumentation Variables*:
+ All instrumentation variables are available as variables of type `float`.
+ Hyphens in variable names are replaced with underscores.
+
+ For example, the expression `cpu_usage > 0.5` matches all samples at which
+ the `cpu-usage` variable is greater-than or equal-to 0.5, that is, at which
+ CPU usage is above 50%.
+
+ - *`thread (id, state = None)`*:
+ The `thread()` function matches all samples whose backtrace contains a
+ given thread.
+
+ The `id` argument may be either a thread-ID of type `int`, or a thread name
+ of type `str`.
+ The argument may be a regular expression, which should fully match the
+ thread name.
+
+ The optional `state` argument, if not `None`, may specify a thread state
+ (see [section *4.2.4.1*](#4241-threads-pane)).
+ Only samples at which the thread is in the given state are matched.
+ The argument may be a regular expression, which should fully match the
+ thread state.
+
+ For example, the expression `thread ('paint', 'R')` matches all samples at
+ which the `paint` thread is in the `R` (*running*) state.
+
+ - *`function (name, id = None, state = None)`*:
+ The `function()` function matches all samples whose backtrace contains a
+ given function.
+
+ The `name` argument should specify a function name.
+ The argument may be a regular expression, which should fully match the
+ function name.
+
+ The optional `id` and `state` arguments have similar meaning to the
+ corresponding arguments of the `thread()` function, and may be used to
+ limit the search to the call stacks of matching threads.
+
+ For example, the expression `function ('gimp_image_.*')` matches all
+ samples whose backtrace includes a function beginning with `gimp_image_`,
+ that is, one of the `GimpImage` functions.
+
+By default, the matching samples replace the current selection, but they can be
+set to *add-to*, *subtract-from*, or *intersect-with* the current selection
+instead.
+
+### 4.4. History Navigation
+
+The *Back* and *Forward* buttons on the left side of the window's titlebar
+allow moving backward and forward in the viewer's state history.
+The viewer state consists of the current selection, and the current call-graph
+direction, thread filter, and path.
+
+### 4.5. Environment Variables
+
+The following environment variables are used by the viewer:
+
+ - *`PERFORMANCE_LOG_VIEWER_PATH`*:
+ Colon-separated list of paths, under which to look for local source files.
+ If the variable is not defined, the current directory is used instead.
+
+ Source files appearing in the log are searched as follows, where the first
+ matching file is selected:
+
+ - If the path is absolute, look for the file at the exact location.
+
+ - For each local path in `PERFORMANCE_LOG_VIEWER_PATH`, try concatenating
+ the local path and the log path.
+ If no such file exists, remove the first component of log path, and try
+ again.
+
+ - *`PERFORMANCE_LOG_VIEWER_EDITOR`*:
+ The command to use to launch the text editor.
+ The special strings `{file}` and `{line}` are replaced with the filename
+ and line number, respectively.
+ If the variable is not defined, a default text editor is used.
+
+[new-performance-issue]: https://gitlab.gnome.org/GNOME/gimp/issues/new?issuable_template=performance