Loading the PL/pgSQL debugger (and other plugins)

From: korry <korryd(at)enterprisedb(dot)com>
To: pgsql-hackers(at)postgresql(dot)org
Subject: Loading the PL/pgSQL debugger (and other plugins)
Date: 2006-07-19 15:59:35
Message-ID: BAY101-DAV2A04597B2B11BEED0FF97D6600@phx.gbl
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

I'm working on a patch that implements the PL/pgSQL instrumentation
stuff (i.e. the PL/pgSQL debugger) that I discussed at the Anniversary
Summit and I need some opinions (this seems like a good place to look
for opinions :-)

A quick review: the PL/pgSQL debugger is designed as an optional
"plugin" that loads into the PL/pgSQL interpreter on-demand. You can
use the plugin idea to implement other kinds of instrumentation (I
demo'ed a tracer and a profiler at the conference, along with a
debugger). A plugin architecture greatly reduces the (source code)
footprint that would normally be required to implement a full-featured
debugger.

A plugin is basically a structure that contains a few function
pointers. If those function pointers are NULL, the PL/pgSQL interpreter
works exactly the way it does today. If any of those function pointers
are non-NULL, the PL/pgSQL interpreter calls the target function (which
points to a chunk of code inside of the plugin) and the plugin does
whatever it needs to do.

Right now, the plugin structure looks like this:

typedef struct
{
void (*init)( estate, func, error_callback, assign_expr, expr );
void (*func_beg)( PLpgSQL_execstate * estate, PLpgSQL_function * func );
void (*func_end)( PLpgSQL_execstate * estate, PLpgSQL_function * func );
void (*stmt_beg)( PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt );
void (*stmt_end)( PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt );
} PLpgSQL_plugin;

I've truncated the argument list (in this e-mail) for the (*init)()
function since it's rather long (error_callback and assign_expr are both
function pointers).

When the PL/pgSQL intrepreter loads the plugin, it calls the
plugin->init() function.
When the PL/pgSQL intrepreter starts running a new function, it calls
the plugin->func_beg() function.
When the PL/pgSQL intrepreter completes a function, it calls the
plugin->func_end() function.
When the PL/pgSQL interpreter is about to execute a line of PL/pgSQL
code, it calls plugin->stmt_beg()
When the PL/pgSQL interpreter has finished executing a line of PL/pgSQL
code, it calls plugin->stmt_end()

So here is where I need a few opinions:

1) I think the most straightforward way to load an instrumentation
plugin is to define a new custom GUC variable (using the
custom_variable_classes mechanism). When the PL/pgSQL call-handler
loads, it can check that config. variable (something like plpgsql.plugin
= '$libdir/plugin_profiler' or plpgsql.plugin =
'$libdir/plugin_debugger') and load the plugin if non-NULL. That seems
a little obtuse to me since custom variables don't appear in the
prototype postgresql.conf file. Would it be better to add a real GUC
variable instead of a custom variable?

2) Given that plpgsql.plugin points to the name of a shared-object file
(or DLL or whatever you prefer to call it), we need to find *something*
inside of the file. The most obvious choice would be to look for a
variable (a structure or structure pointer) with a fixed name. That
would mean, for example, that a plugin would define an externally
visible PLpgSQL_plugin structure named "plugin_hooks" and the PL/pgSQL
interpreter would look for that symbol inside of the plugin.
Alternatively, we could look for a function inside of the plugin
(something like 'plugin_loader') and then call that function with a
pointer to a PLpgSQL_plugin structure. I prefer the function-pointer
approach since we already have a reliable mechanism in place for finding
a function inside of a shared-object (the same mechanism works for
finding a variable instead of a function pointer, but I doubt that that
has been tested in all platforms).

3) Any comments on the PLpgSQL_plugin structure? Should it include (as
it's first member) a structure version number so we can add to/change
the structure as needed?

4) Do we need to support multiple active plugins? Would you ever need
to load the debugger at the same time you've loaded the profiler (no)?
Would you ever need to load the tracer at the same time you need the
debugger (probably not)? If we need to support multiple plugins, should
be just introduce a meta-plugin that knows how to handle a list of other
plugins? (Messy, but certainly gets the job done without worrying about
it right now).

5) I'll also be adding a void pointer to the PLpgSQL_execstate structure
(think of a PLpgSQL_execstate as a stack frame). The new pointer is
reserved for use by the plugin. It may be handy to add a void pointer
to each PLpgSQL_stmt as well - is that acceptable? (That would mean an
extra 4-bytes per-line of compiled PL/pgSQL code, even if you don't have
a plugin loaded).

Any other comments? Obviously, you'll have a chance to critique the
patch when I get it sent in.

Thanks for your help.

-- Korry

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Andrew Dunstan 2006-07-19 16:08:35 Re: pg_regress breaks on msys
Previous Message Darcy Buskermolen 2006-07-19 15:54:33 Re: Progress bar updates