mirror of https://github.com/GNOME/gimp.git
306 lines
11 KiB
Plaintext
306 lines
11 KiB
Plaintext
Debugging Plug-ins
|
|
==================
|
|
|
|
Eeek! The 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.
|
|
But hmm, how does one start a plug-in under a debugger if GIMP is the one
|
|
who is starting the plug-in...
|
|
|
|
To address this issue, libgimp has some hooks controlled by the
|
|
GIMP_PLUGIN_DEBUG environment variable at runtime.
|
|
|
|
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 also lets you arrange for log messages of levels WARNING,
|
|
CRITICAL, and ERROR (from the plug-in, GIMP libraries, and GLib libraries)
|
|
to be fatal and generate a backtrace at that point (also called
|
|
a stack trace, similar to that generated in a debugger using the 'bt' command.)
|
|
|
|
When GIMP_PLUGIN_DEBUG is not defined, depending on how other GLib environment
|
|
variables are defined, a plug-in may quietly execute past
|
|
warning and critical logged events, and only terminate on a ERROR logged event
|
|
(without a backtrace), or on a soft or hard fault such as a memory exception
|
|
(possibly producing a core dump that you can examine using a debugger.)
|
|
|
|
|
|
Format of GIMP_PLUGIN_DEBUG:
|
|
----------------------------
|
|
|
|
GIMP_PLUGIN_DEBUG=name<,options>
|
|
|
|
"name" specifies the name of the plug-in that you wish to debug,
|
|
or "all" to debug every plug-in. See below "Plug-in names".
|
|
|
|
"options" is zero or more of the following options, separated by :'s
|
|
|
|
run: suspend the plug-in when its run func is called.
|
|
query: suspend the plug-in when its query func is called.
|
|
init: suspend the plug-in when its init func is called.
|
|
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 process
|
|
fw: shorthand for above.
|
|
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 C-language plug-in:
|
|
------------------------------
|
|
|
|
0. Ensure GIMP was built with debugging information (gcc -g)
|
|
|
|
1. In a terminal, start GIMP with the environment variable GIMP_PLUGIN_DEBUG set
|
|
( e.g. ">GIMP_PLUGIN_DEBUG=blur,on gimp" )
|
|
|
|
2. In another terminal, start a debugger (gdb, lldb, or other) and load the plug-in
|
|
program into the debugger. Loading only loads the debug symbols.
|
|
( e.g. ">gdb blur" )
|
|
|
|
3. 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.
|
|
|
|
4. In the debugger, attach to the pid of the plug-in process.
|
|
(e.g. "gdb> attach <pid>")
|
|
Expect the debugger to say where the plug-in is suspended.
|
|
|
|
|
|
5. In the debugger, set breakpoints (or examine memory, or step, etc.)
|
|
|
|
6. Windows: resume the plug-in process with "gimp-debug-resume.exe <pid>"
|
|
(On Linux, the gdb continue command resumes the attached process.)
|
|
|
|
7. In the debugger, enter "continue". Expect the plug-in to resume under
|
|
control of the debugger and pause at breakpoints.
|
|
(e.g. "gdb> continue")
|
|
|
|
|
|
|
|
Examples using a debugger:
|
|
--------------------------
|
|
|
|
GIMP_PLUGIN_DEBUG=blur
|
|
|
|
When the blur plug-in's run func is called (the run phase),
|
|
GIMP suspends it and prints to the console:
|
|
|
|
(blur:9000): LibGimp-DEBUG: Waiting for debugger...
|
|
|
|
9000 is the pid of the new plug-in process. You can start your debugger,
|
|
attach to the plug-in, set breakpoints/watches/etc. and continue from there.
|
|
Using gdb command "continue" will resume the plugin.
|
|
|
|
Expect the plugin to execute until it hits a breakpoint or until a WARNING
|
|
or worse event is logged or until a hard fault such as a memory violation.
|
|
Then the debugger will be back in control.
|
|
|
|
GIMP_PLUGIN_DEBUG=blur,on
|
|
GIMP_PLUGIN_DEBUG=blur,run:fatal-warnings
|
|
|
|
Same effect as above.
|
|
|
|
GIMP_PLUGIN_DEBUG=blur,pid
|
|
|
|
Prints:
|
|
|
|
(blur:9000): LibGimp-DEBUG: Here I am!
|
|
|
|
This simply prints the pid but doesn't suspend the plug-in. It is a
|
|
convenience for a plug-in having a GUI. The GUI starts up and waits
|
|
for user input. When you attach, you will find the plug-in waiting
|
|
in the event loop.
|
|
|
|
GIMP_PLUGIN_DEBUG=blur,query
|
|
GIMP_PLUGIN_DEBUG=blur,init
|
|
GIMP_PLUGIN_DEBUG=blur,quit
|
|
|
|
Same effect as for "run", but instead libgimp suspends the plug-in before
|
|
one of a plugin's phases: query, init, run, quit
|
|
E.G. when it is queried or init'ed on GIMP startup.
|
|
|
|
|
|
To get a backtrace for a plug-in in any language:
|
|
-------------------------------------------------
|
|
|
|
0. Ensure GIMP (and all libraries you want details for)
|
|
were built with debugging information (gcc -g)
|
|
|
|
1. In a terminal, start GIMP with the environment variable GIMP_PLUGIN_DEBUG set
|
|
( e.g. ">GIMP_PLUGIN_DEBUG=all,fatal-criticals gimp" )
|
|
Expect GIMP to start normally.
|
|
|
|
2. In GIMP, do something that would invoke a plugin.
|
|
|
|
Whenever the specified plug-in processes generate log events of
|
|
the specified levels or worse, libgimp will print or offer to print
|
|
(depends on stack-trace-mode) a backtrace, and then terminate the plug-in.
|
|
The GIMP app will usually continue to run.
|
|
|
|
For interpreted language plug-ins, the backtrace will include many frames
|
|
from the interpreter and modules such as PyGObject. Exceptions in the
|
|
interpreted language may print on their own and not generate log events
|
|
to be caught by GIMP_PLUGIN_DEBUG. But log events from the interpreter
|
|
calling out (to LibGimp and GLib) can generate backtraces.
|
|
|
|
|
|
GIMP_PLUGIN_DEBUG and stack-trace-mode
|
|
--------------------------------------
|
|
|
|
GIMP_PLUGIN_DEBUG=all,fatal-warning only makes the machinery *consider*
|
|
generating a backtrace, for more log events. The 'stack-trace-mode' pertains.
|
|
|
|
The GIMP app on the command line takes a flag:
|
|
|
|
--stack-trace-mode [never, query, always]
|
|
|
|
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.)
|
|
|
|
"always" means libgimp prints a backtrace (and then the plugin terminates.)
|
|
|
|
"never" means libgimp does not print a backtrace, only a message. But for
|
|
GIMP_PLUGIN_DEBUG=all,fatal-warning, the plugin terminates on the first WARNING.
|
|
|
|
|
|
Examples getting a backtrace on logged events:
|
|
--------------------------------------------
|
|
|
|
GIMP_PLUGIN_DEBUG=blur,fatal-warnings
|
|
|
|
The blur plug-in will run until the first logged WARNING or worse,
|
|
from the plug-in, GIMP libraries, or GLib libraries.
|
|
Then a backtrace can print to the console and the blur plug-in terminate.
|
|
Usually you will also see a message from the main GIMP app
|
|
saying the plugin aborted without returning a value.
|
|
|
|
GIMP_PLUGIN_DEBUG=all,fatal-criticals
|
|
|
|
Every plug-in (whether you invoked it from the GIMP GUI or it was called
|
|
by another plug-in) will run until the first logged CRITICAL or worse,
|
|
from the plug-in, GIMP libraries, or GLib libraries.
|
|
Then, as above, a backtrace can print etc.
|
|
|
|
GIMP_PLUGIN_DEBUG=all
|
|
|
|
As above, for all plugins, but only for a logged ERROR.
|
|
|
|
|
|
Quality of a backtrace
|
|
----------------------
|
|
|
|
A detailed backtrace depends on:
|
|
|
|
1) building GIMP and dependencies with debug info enabled
|
|
2) having a debugger installed
|
|
|
|
When a debugger is not installed, a backtrace may lack details such as
|
|
function names and line numbers.
|
|
|
|
|
|
More about logging levels
|
|
-------------------------
|
|
|
|
See https://developer.gnome.org/glib/stable/glib-Message-Logging.html
|
|
|
|
Level Engendered by
|
|
-----------------------
|
|
WARNING g_warning().
|
|
CRITICAL g_return_value_if_fail() or g_return_if_fail().
|
|
ERROR g_assert() or g_error().
|
|
|
|
Use of logging levels is by convention, and libraries that GIMP uses may not
|
|
follow conventions.
|
|
|
|
Generally speaking...
|
|
|
|
WARNINGs are common but don't signify much. They might mean that your plug-in
|
|
code does not understand, or is sloppy with, the GIMP API.
|
|
|
|
CRITICALs are rare but more significant. They usually mean that GIMP will
|
|
attempt to continue past an errant condition. The GLib function
|
|
g_return_value_if_fail() is often used in GIMP code as a precondition,
|
|
required to succeed before a GIMP function executes its body,
|
|
the function returning early when the precondition fails.
|
|
|
|
ERRORs are usually dire. They always terminate a plug-in.
|
|
|
|
|
|
|
|
Plug-in names
|
|
-------------
|
|
|
|
A plug-in may register many PDB procedures. Use the plug-in name, not a
|
|
procedure-name, e.g. "file-psd" not "file-psd-save". When a plug-in is "run",
|
|
one of its PDB procedures is run, and all of its PDB procedures
|
|
are covered by a GIMP_PLUGIN_DEBUG definition.
|
|
|
|
A name is usually the name of the executable file, including any suffix.
|
|
Examples: "file-psd" or "foggify.py" or on some platforms "foo.exe."
|
|
|
|
Usually an interpreted plug-in has a hashbang on the first line
|
|
e.g. "#!/usr/bin/env python3" in the first line of foo.py.
|
|
Then the file is executable, GIMP forks that file, and the Linux loader
|
|
invokes the interpreter.
|
|
|
|
However, GIMP still allows a plug-in source
|
|
to lack a hashbang (and it is not technically "an executable")
|
|
but then GIMP forks the interpreter, passing the script filename.
|
|
In this case, you must still use the name of the script file e.g. "foo.py"
|
|
in GIMP_PLUGIN_DEBUG.
|
|
|
|
(Since forever, GIMP does not understand Python packages.
|
|
GIMP only installs Python plugins from directories named like foo/foo.py.
|
|
A directory that is a Python package (having an __init__.py file)
|
|
will be read by GIMP at startup, but GIMP will only install one plugin
|
|
from that directory, and only if the .py file is named like the directory.)
|
|
|
|
|
|
Finding plug-in source by name
|
|
------------------------------
|
|
|
|
A GIMP supported C language plug-in's source should be in a similar-named
|
|
directory in the GIMP repository. For example, "file-psd" is a directory
|
|
(but there is no "file-psd.c".)
|
|
|
|
An interpreted plug-in is *installed* in a directory of a similar name
|
|
e.g. "plug-ins/foggify/foggify.py".
|
|
But in the GIMP repository, foggify.py does not live at foggify/foggify.py
|
|
but at plug-ins/python/foggify.py .
|
|
|
|
|
|
|
|
Examples using other debug tools:
|
|
---------------------------------
|
|
|
|
Hmm, but what about memory debuggers such as valgrind or purify? For those
|
|
you can set the following:
|
|
|
|
GIMP_PLUGIN_DEBUG_WRAP=name<,options>
|
|
|
|
This is similar to GIMP_PLUGIN_DEBUG. Only "query", "init", and "run"
|
|
are valid, and "on" defaults to simply "run"
|
|
|
|
GIMP_PLUGIN_DEBUG_WRAPPER=debugger
|
|
|
|
debugger refers to the debugger program, such as valgrind. You can
|
|
put command line options here too, they will be parsed like they do
|
|
in the shell.
|
|
|
|
|
|
Windows:
|
|
--------
|
|
|
|
When compiled with Windows, a plug-in process is halted by Windows functions.
|
|
It must be resumed externally by invoking gimp-debug-resume.exe <pid>
|
|
The plug-ins pid can be found out by invoking gimp-debug-resume.exe
|
|
without parameters. It shows the pid of all running processes.
|