Loading the PL/pgSQL debugger (and other plugins) - Mailing list pgsql-hackers

From korry
Subject Loading the PL/pgSQL debugger (and other plugins)
Date
Msg-id BAY101-DAV2A04597B2B11BEED0FF97D6600@phx.gbl
Whole thread Raw
Responses Re: Loading the PL/pgSQL debugger (and other plugins)  (Tom Lane <tgl@sss.pgh.pa.us>)
List 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




pgsql-hackers by date:

Previous
From: Darcy Buskermolen
Date:
Subject: Re: Progress bar updates
Next
From: Andrew Dunstan
Date:
Subject: Re: pg_regress breaks on msys