Debugging Plug-ins
Eeek! The filter or plug-in you’re working on has a bug in it! And the fix isn’t completely obvious, so you want to use a debugger to see what is going on.
Debugging a filter is straight-foward like debugging GIMP (since the filter is a shared library loaded by GEGL which is loaded by GIMP, GIMP process will crash so the debugger can catch the threads information, including the one on which the filter crashed). So, see: Debugging Tips.
But hmm, how does one start a plug-in under a debugger if GIMP is the one who is starting the plug-in… If you analyze both GIMP and a plugin, you might have interleaved output from:
- the GIMP app process
- the old ScriptFu extension
- a running plugin under test
- any plugin called by the plugin under test
To address this issue, libgimp has some hooks controlled by the
GIMP_PLUGIN_DEBUG or GIMP_PLUGIN_DEBUG_WRAP* environment variables at runtime.
Format of GIMP_PLUGIN_DEBUG
GIMP_PLUGIN_DEBUG lets you arrange that a plug-in suspends when it starts, and
then you can start a debugger and attach the debugger to the pid of the plug-in.
GIMP_PLUGIN_DEBUG=<domain>,<options>It have a similar format to GIMP_DEBUG.
The “domain” will be the plug-in name. all is a domain to debug every plug-in.
A plug-in name is usually the name of the executable file, including any suffix, not the procedure-name, e.g. “file-psd” not “file-psd-export”. Other valid examples: “foggify.py” or on some platforms “foo.exe”.
“options” is zero or more of the following options, separated by :’s
query: suspend the plug-in when its query func is called (only on plug-in first install).init: suspend the plug-in when its init func is called (on subsequent starts at splash screen).run: suspend the plug-in when its run func is called (on plug-in execution by the user, default).quit: suspend the plug-in when its quit func is called.pid: just print the pid of the plug-in on run_proc.fatal-warnings: similar to gimp –g-fatal-warnings on the command line, but for the plugin processfw: shorthand for above (fatal-warnings).fatal-criticals: make CRITICAL level messages fatal (but not WARNING)
In the absence of an options string, only ERRORs are fatal and generate a backtrace according to stack-trace-mode.
To use a debugger on a plug-in
-
Ensure GIMP was built with debugging information. A detailed backtrace partly depends on building GIMP (
-Dbuildtype=debug*) and dependencies with debug info enabled. -
In a first terminal, start GIMP with the environment variable
GIMP_PLUGIN_DEBUGset:export GIMP_PLUGIN_DEBUG=blur,run #on Linux/macOS $env:GIMP_PLUGIN_DEBUG='blur,run' #on Windows path_to_gimpExceptions in an interpreted language may print on their own and not generate log events to be caught by
GIMP_PLUGIN_DEBUG.To get frames at least from log events of the interpreter calling out to LibGimp and GLib, evaluate to
all,fatal-criticals, orall,fatal-warningsif appropriate. -
In another, second terminal, start a debugger (gdb, cdb, lldb, or other) and load the plug-in program into the debugger. Loading only loads the debug symbols.
gdb path_to_blur_plugin -
Invoke the plug-in procedure in GIMP. GIMP will start the plug-in process, then suspend it and print the pid of the plug-in process to the terminal where you started GIMP. On Windows: you should see something in the first terminal like:
(blur:8992): LibGimp-DEBUG: 16:44:50.894: Debugging (restart externally): 8992. The number at the end is the<pid>of the plug-in. -
In the debugger on the second terminal, attach to the pid of the plug-in process. Expect the debugger to say where the plug-in is suspended.
attach <pid> #on GDB/LLDB (e.g. Linux, macOS) .Attach 0n<pid> #on CDB (Windows) -
In the debugger, set breakpoints (or examine memory, or step, etc.)
-
Windows: in a third terminal, resume the plug-in process with
gimp-debug-resume.exeavailable from your build directory.path_to_build/tools/gimp-debug-resume.exe <pid>Linux and Unix-like: the gdb
continueor lldbccommand might resume the attached process. Possibly you will have to send a SIGCONT signal:kill -SIGCONT <pid> -
In the debugger on the second terminal, enter
continueif GDB orcif LLDB. Expect the plug-in to resume under control of the debugger and pause at breakpoints.c #on GDB/LLDB (e.g. Linux, macOS) g #on cDB (Windows) -
When it crashes, the debugger should catch a signal. Your response might be the following to obtain a backtrace:
bt #on GDB/LLDB (e.g. Linux, macOS) kp #on CDB (Windows)
Format of GIMP_PLUGIN_DEBUG_WRAP*
Hmm, but what about memory debuggers such as valgrind? For those you need to set the following two vars:
GIMP_PLUGIN_DEBUG_WRAP=<domain>,<options>
GIMP_PLUGIN_DEBUG_WRAPPER=<debugger>GIMP_PLUGIN_DEBUG_WRAP is similar to GIMP_PLUGIN_DEBUG.
But all domain does not always work. And query, init, and run are the
only valid options.
“debugger” on GIMP_PLUGIN_DEBUG_WRAPPER refers to the debugger executable
name on PATH.
You can put command line options here too, they will be parsed like they do
in the shell.
To use a debugger on a plug-in (alternative)
-
Similarly to when using GIMP_PLUGIN_DEBUG, ensure GIMP (
-Dbuildtype=debug*) and dependencies were built with debug info enabled.Even some debuggers, like valgrind, working differently (and slowly) to get more information, they still will be benefited from debug info in binaries. -
In a terminal, start GIMP with:
export GIMP_PLUGIN_DEBUG_WRAP=script-fu #on Linux/macOS export GIMP_PLUGIN_DEBUG_WRAPPER="gdb --args" #on Linux/macOS $env:GIMP_PLUGIN_DEBUG_WRAP='script-fu' #on Windows $env:GIMP_PLUGIN_DEBUG_WRAPPER="gdb --args" #on Windows path_to_gimpGIMP_PLUGIN_DEBUG_WRAP is more limited than GIMP_PLUGIN_DEBUG for interpreted plugins, since, as said, it do not even have fatal-* <options>.
You can, then, set
G_DEBUGenv var evaluating it tofatal-criticalsso that CRITICALS are fatal, orfatal-warningsfor fatal WARNINGS and CRITICALS.You can change
gdb --argstocdb,lldb --or another debugger (e.g.valgrind)We run the debugger in a way that everything after thatwill be passed as args to the program being debugged, since there may be many args that GIMP passes to a plug-in (e.g. for ScriptFu extension). -
Launch the plugin from the GIMP app. The debugger will now be invoked and load the symbols.
-
The debugger is waiting on you. Set breakpoints, etc, then run:
r #on GDB/LLDB (e.g. Linux, macOS) g #on CDB (Windows) -
When it crashes, the debugger should catch a signal, write the details to the terminal, and prompt you. Your response might be the following to obtain a backtrace:
bt #on GDB/LLDB (e.g. Linux, macOS) kp #on CDB (Windows)
GIMP_PLUGIN_DEBUG and stack-trace-mode
The GIMP app on the command line can take this flag:
--stack-trace-mode [never, query, always]That flag will make GIMP redirect the stack-trace printing from the usual GUI gimp-debug-tool to the CLI (the terminal where GIMP was called) instead.
When the GIMP app forks a plugin process, it passes that arg to the plugin, and the arg controls how a backtrace is printed:
- The default is
query, which means libgimp will ask you: “[E]xit [S]tacktrace [P]roceed” (similar to the GLib default handler for ERROR log events.) alwaysmeans libgimp prints a backtrace (and then the plugin terminates.)nevermeans libgimp does not print a backtrace, only a message. But for GIMP_PLUGIN_DEBUG=all,fatal-warning, the plugin terminates on the first WARNING.