Analyzing Plug-ins Tutorial

About

This is a tutorial, with working examples.

The companion document “Debuggin Plugins” is a reference. It describes Gimp’s support to analyze plugins.

The audience is plugin authors.

We use the verb “analyze” to mean “use a tool: gdb or valgrind.” The tools wrap the program under test.

Background

This section describes the system in which you analyze plugins.

Plugins and Gimp are separate processes

Plugins are separate processes that remotely call Gimp in another process. This document is mostly about how to analyze the separate plugin process.

Analyzing Gimp along with a plugin

You might want to analyze Gimp at the same time as you analyze a plugin.

To debug Gimp, when you start it from a terminal:

gdb gimp

To valgrind Gimp, when you start it from a terminal:

valgrind gimp

When you analyze both Gimp and a plugin, the tool prompts and outputs, for separate processes are interleaved in the terminal. That can be hard for you to distinguish. You might have interleaved output from:

  • the Gimp app
  • the ScriptFu extension
  • a running plugin under test
  • any plugin called by the plugin under test

Kinds of plugins

The techniques describe here work for all the kinds of plugins:

  • file load/save plugins (export and import)
  • filter plugins in the C language
  • interpreted plugins (Python, Lua, ScriptFu/Scheme, etc.)
  • plugins served by the ScriptFu extension (old-style Scheme plugins)
  • the tool plugins: PDB Browser etc. and the ScriptFu tools ScriptFu Console, Eval, etc.

Analyzing the phases of a plugin

A plugin is called several times in its life, in phases. When you first install a plugin, when Gimp starts, it calls the plugin in query phase. The plugin process starts, resonds to the query, then exits.

After that, when you start Gimp again, and the file of the plugin has not changed, Gimp does not query the plugin again, but instead has cached the particulars of the plugin in an .rc file.

Later, when a user invokes a plugin, Gimp starts the plugin process again, in run phase.

You usually don’t analyze the query phase. In the examples below, we only analyze the run phase, since we don’t use special args to GIMP_PLUGIN_DEBUG_WRAP, and it defaults to only the run phase. For more information, see “Debugging Plugins.”

Alternatives

You can use gdb in two ways:

  1. you can start gdb before the tested program
  2. OR you can start gdb later and “attach” to the already running tested program.

This tutorial is about the first way. One advantage of the first way is its simplicity. Another advantage of the first way is that you can set breakpoints before the plugin starts.

If all you want is for Gimp to launch gdb and get a backtrace “after the fact”, when the plugin throws WARNING or CRITICAL, see the reference doc “Debugging Plugins.”

About valgrind and gdb

This tutorial uses the tools gdb and valgrind. They are Linux progams. They might be available on other platforms.

Valgrind does not require you to recompile Gimp. Valgrind finds memory access issues in called libraries without needing special versions of the libraries. Valgrind is slow, often taking minutes. Valgrind is slow by about a factor of ten, be patient. Valgrind also detects memory leaks, allocated memory never freed. It prints a report when a program under test exits.

Both valgrind and gdb are more informative when you build programs under test “with debug symbols.” That is, using the meson option, “–buildtype=debug”, which is the default. Otherwise, the tools show traces of program execution without names of functions. You might also want to install the packages of libraries that have debug symbols. You might also want to build babl and gegl configured for debug.

Analyzing all plugins

FUTURE: this is not working now. You must name plugins individually.

Valgrinding all plugins

You can set up to valgrind all plugins, and then only invoke the plugin you want to valgrind. Otherwise, you must know the name of the plugin file you want to valgrind.

In the terminal where you will start Gimp:

export GIMP_PLUGIN_DEBUG_WRAP=all
export GIMP_PLUGIN_DEBUG_WRAPPER=valgrind

Then start Gimp from the terminal:

>gimp

Gimp starts as usual. But as Gimp invokes any plugin, you will see message from valgrind in the terminal. Again, several processes will be executing (including always ScriptFu extension) and their valgrind messages are interleaved.

Debugging all plugins

You can do a similar thing using the debugger gdb, but then whenever Gimp calls any plugin, you must respond to the gdb prompt as discussed below. That would include ScriptFu extension. We don’t give an example because we assume you would only want to debug a plugin by name.

Analyzing Python plugins

Python plugins for Gimp are interpreted by the Python 3 interpreter. They have a hash-bang in their first line. Gimp spawns a new process each time it invokes a Python plugin.

Analyzing a Python plugin is similar to analyzing an interpreted plugin in another language (new-style Scheme, Lua, Javascript.)

Analyzing checks both the plugin and the Python interpreter: all the code in the process.

Debugging a Python plugin

In the terminal where you will start Gimp:

export GIMP_PLUGIN_DEBUG_WRAP=foggify.py
export GIMP_PLUGIN_DEBUG_WRAPPER="gdb --args"

Here, “foggify.py” is the name of file containing the source code of the plugin. It will be installed in its own same-named directory in the /plug-ins directory of Gimp’s or the user’s .config directory.

Then start Gimp from the terminal. Gimp starts as usual.

Now launch the plugin from the Gimp app. Choose Filters>Decor>Fog

Now you will see, in the terminal:

Reading symbols from /usr/bin/env...
(No debugging symbols found in /usr/bin/env)
(gdb)

Gdb is waiting on you. Enter “r” for run. Or set breakpoints, etc. and then run.

At this point you will see in the terminal:

Starting program: /usr/bin/env python3
  /usr/local/lib/x86_64-linux-gnu/gimp/3.0/plug-ins/foggify/foggify.py
  -gimp 274 18 16 -run 1

and in the GUI you will see the dialog for the plugin. Choose the OK button.

If the plugin signals an exception, gdb will catch it, show you the details, give a prompt, and wait.

This example has no issues, and gdb finally prints:

[Inferior 1 (process 405) exited normally]
(gdb)

Enter “q” to quit gdb.

Valgrinding a Python plugin

In the terminal where you will start Gimp:

export GIMP_PLUGIN_DEBUG_WRAP=foggify.py
export GIMP_PLUGIN_DEBUG_WRAPPER=valgrind

See above for what “foggify.py” names.

Then start Gimp from the terminal. Gimp starts as usual.

Now launch the plugin from the Gimp app. Choose Filters>Decor>Fog

Now you will see, in the terminal, a few lines that valgrind wrote:

==464== Memcheck, a memory error detector

It won’t write much until you OK the dialog of the plugin, and valgrind finds an issue.

The dialog of the plugin will be slow to start, since again, Valgrind slows execution down.

Now you will see the dialog of the plugin. Choose the OK button. The plugin will run and exit, and valgrind will report any issues to the terminal.

For that plugin, valgrind does not find any issues.

Analyzing ScriptFu extension i.e. old-style Scheme scripts

Old-style Scheme plugins installed to /scripts are served by the the long-running ScriptFu extension process. When you choose a menu item provided by an old-style Scheme plugin, Gimp sends a message to the ScriptFu extension process to execute the plugin.

When you analyze old-style Scheme scripts you test this stack of code:

the outer ScriptFu extension process
the inner TinyScheme interpreter
the plugin script
libgimp for script's calls to the PDB

Calls to the PDB are usually remote calls to the Gimp process. You can also analyze the Gimp process at the same time, but configuring an analyzer on Gimp is a separate step, discussed earlier

Valgrinding the ScriptFu extension

In the terminal where you will start Gimp:

export GIMP_PLUGIN_DEBUG_WRAP=script-fu
export GIMP_PLUGIN_DEBUG_WRAPPER=valgrind

Here, “script-fu” is the name of the executable, since ScriptFu extension is a plugin written in C.

You use the same name “script-fu” to analyze the other ScriptFu tools: ScriptFu Console and ScriptFu Eval.

Then start Gimp from the terminal. When gimp starts, it forks or spawns the ScriptFu extension.

Now you will see, in the terminal, a few lines from valgrind:

==464== Memcheck, a memory error detector

Valgrind won’t write much until you invoke a plugin, and valgrind finds an issue.

Gimp will be slow to start, waiting on the slowed ScriptFu extension. Valgrind slows the execution of the ScriptFu extension and any old-style plugins served by the ScriptFu extension.

To make the ScriptFu extension do something, launch an old-style plugin from the Gimp app. Choose Filters>Artistic>Clothify.

For that plugin, valgrind does find an issue, but it is probably spurious. Valgrind does give false alarms.

ScriptFu extension stays running until you quit Gimp. So valgrind does not find any memory leaks in ScriptFu extension or the scripts it evaluates until you quit Gimp.

Debugging the ScriptFu extension

In the terminal where you will start Gimp:

export GIMP_PLUGIN_DEBUG_WRAP=script-fu
export GIMP_PLUGIN_DEBUG_WRAPPER="gdb --args"

Here, “–args” is important. It is a flag to gdb that means everything after that will be passed as args to the program being debugged. The command that Gimp spawns is something like:

gdb --args /usr/local/lib/gimp/3.0/plug-ins/script-fu/script-fu
  -gimp 274 15 14 -run 1

so there are many args that Gimp passes to ScriptFu extension.

Start Gimp. At this point, Gimp spawns ScriptFu extension and waits for it. You won’t see the Gimp GUI yet. In the terminal you will see something like:

...
Reading symbols from /usr/.../script-fu...
(gdb)

Gdb is waiting on you. Enter “r” for run. Or set breakpoints, etc. and then run.

Now you will see the Gimp GUI.

To make the ScriptFu extension do something, launch an old-style plugin from the Gimp app. Suppose it is one that you have installed to Gimp and that it crashes hard, taking down ScriptFu extension. (Which is an unlikely scenario. It is more likely for compiled C plugins.)

When it crashes, gdb should catch a signal, write the details to the terminal, and prompt you. Your response might be “bt” to obtain a backtrace.

Gimp will continue to run, it is an independent process.

Analyzing new-style Scheme scripts

New-style scripts are interpreted by an independent interpreter, not by ScriptFu extension. They have a hash-bang in their first line. Gimp spawns a new process for each invocation of a new-style plugin.

Valgrinding new-style Scheme scripts

In the terminal where you will start Gimp:

export GIMP_PLUGIN_DEBUG_WRAP=test-display.scm
export GIMP_PLUGIN_DEBUG_WRAPPER=valgrind

Here, “test-display.scm” is the name of file containing the source code of the new-style plugin. It will be installed in its own same-named directory in the /plug-ins directory of Gimp’s or the user’s .config directory. Note that this plugin is only installed in a “debug” build of Gimp.

Then start Gimp from the terminal. Gimp starts as usual.

Now launch the plugin from the Gimp app. For example, choose Filters>Development>Demos>Test display and error functions.

Now you will see, in the terminal, a few lines that valgrind wrote:

==464== Memcheck, a memory error detector

Valgrind won’t write much until you OK the dialog of the plugin, and valgrind finds an issue.

The dialog of the plugin will be slow to start, since Valgrind slows execution down.

Now you will see the dialog of the plugin. Choose the OK button. The plugin will run and exit, and valgrind will report any issues to the terminal.

For this plugin, valgrind does not find any issues.

Debugging new-style Scheme scripts

Debugging new-style Scheme scripts is similar, just substitute “gdb –args” as in other examples.

Analyzing a C plugin

Analyzing a plugin written in the C language is similar to analyzing any other plugin except that the name is the filename of the executable binary file (which might include suffix .exe on some platforms):

export GIMP_PLUGIN_DEBUG_WRAP=warp

To invoke that plugin, choose: Filters>Map>Warp…