Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d86d20f

Browse files
committed
Add backend support for injection points
Injection points are a new facility that makes possible for developers to run custom code in pre-defined code paths. Its goal is to provide ways to design and run advanced tests, for cases like: - Race conditions, where processes need to do actions in a controlled ordered manner. - Forcing a state, like an ERROR, FATAL or even PANIC for OOM, to force recovery, etc. - Arbitrary sleeps. This implements some basics, and there are plans to extend it more in the future depending on what's required. Hence, this commit adds a set of routines in the backend that allows developers to attach, detach and run injection points: - A code path calling an injection point can be declared with the macro INJECTION_POINT(name). - InjectionPointAttach() and InjectionPointDetach() to respectively attach and detach a callback to/from an injection point. An injection point name is registered in a shmem hash table with a library name and a function name, which will be used to load the callback attached to an injection point when its code path is run. Injection point names are just strings, so as an injection point can be declared and run by out-of-core extensions and modules, with callbacks defined in external libraries. This facility is hidden behind a dedicated switch for ./configure and meson, disabled by default. Note that backends use a local cache to store callbacks already loaded, cleaning up their cache if a callback has found to be removed on a best-effort basis. This could be refined further but any tests but what we have here was fine with the tests I've written while implementing these backend APIs. Author: Michael Paquier, with doc suggestions from Ashutosh Bapat. Reviewed-by: Ashutosh Bapat, Nathan Bossart, Álvaro Herrera, Dilip Kumar, Amul Sul, Nazir Bilal Yavuz Discussion: https://postgr.es/m/ZTiV8tn_MIb_H2rE@paquier.xyz
1 parent c03d91d commit d86d20f

17 files changed

+512
-0
lines changed

configure

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ CPPFLAGS
759759
LDFLAGS
760760
CFLAGS
761761
CC
762+
enable_injection_points
762763
enable_tap_tests
763764
enable_dtrace
764765
DTRACEFLAGS
@@ -839,6 +840,7 @@ enable_profiling
839840
enable_coverage
840841
enable_dtrace
841842
enable_tap_tests
843+
enable_injection_points
842844
with_blocksize
843845
with_segsize
844846
with_segsize_blocks
@@ -1532,6 +1534,8 @@ Optional Features:
15321534
--enable-coverage build with coverage testing instrumentation
15331535
--enable-dtrace build with DTrace support
15341536
--enable-tap-tests enable TAP tests (requires Perl and IPC::Run)
1537+
--enable-injection-points
1538+
enable injection points (for testing)
15351539
--enable-depend turn on automatic dependency tracking
15361540
--enable-cassert enable assertion checks (for debugging)
15371541
--disable-largefile omit support for large files
@@ -3682,6 +3686,36 @@ fi
36823686

36833687

36843688

3689+
#
3690+
# Injection points
3691+
#
3692+
3693+
3694+
# Check whether --enable-injection-points was given.
3695+
if test "${enable_injection_points+set}" = set; then :
3696+
enableval=$enable_injection_points;
3697+
case $enableval in
3698+
yes)
3699+
3700+
$as_echo "#define USE_INJECTION_POINTS 1" >>confdefs.h
3701+
3702+
;;
3703+
no)
3704+
:
3705+
;;
3706+
*)
3707+
as_fn_error $? "no argument expected for --enable-injection-points option" "$LINENO" 5
3708+
;;
3709+
esac
3710+
3711+
else
3712+
enable_injection_points=no
3713+
3714+
fi
3715+
3716+
3717+
3718+
36853719
#
36863720
# Block size
36873721
#

configure.ac

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,13 @@ PGAC_ARG_BOOL(enable, tap-tests, no,
250250
[enable TAP tests (requires Perl and IPC::Run)])
251251
AC_SUBST(enable_tap_tests)
252252

253+
#
254+
# Injection points
255+
#
256+
PGAC_ARG_BOOL(enable, injection-points, no, [enable injection points (for testing)],
257+
[AC_DEFINE([USE_INJECTION_POINTS], 1, [Define to 1 to build with injection points. (--enable-injection-points)])])
258+
AC_SUBST(enable_injection_points)
259+
253260
#
254261
# Block size
255262
#

doc/src/sgml/installation.sgml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,21 @@ build-postgresql:
16561656
</listitem>
16571657
</varlistentry>
16581658

1659+
<varlistentry id="configure-option-enable-injection-points">
1660+
<term><option>--enable-injection-points</option></term>
1661+
<listitem>
1662+
<para>
1663+
Compiles <productname>PostgreSQL</productname> with support for
1664+
injection points in the server. Injection points allow to run
1665+
user-defined code from within the server in pre-defined code paths.
1666+
This helps in testing and in the investigation of concurrency scenarios
1667+
in a controlled fashion. This option is disabled by default. See
1668+
<xref linkend="xfunc-addin-injection-points"/> for more details. This
1669+
option is intended to be used only by developers for testing.
1670+
</para>
1671+
</listitem>
1672+
</varlistentry>
1673+
16591674
<varlistentry id="configure-option-with-segsize-blocks">
16601675
<term><option>--with-segsize-blocks=SEGSIZE_BLOCKS</option></term>
16611676
<listitem>
@@ -3160,6 +3175,21 @@ ninja install
31603175
</listitem>
31613176
</varlistentry>
31623177

3178+
<varlistentry id="configure-injection-points-meson">
3179+
<term><option>-Dinjection_points={ true | false }</option></term>
3180+
<listitem>
3181+
<para>
3182+
Compiles <productname>PostgreSQL</productname> with support for
3183+
injection points in the server. Injection points allow to run
3184+
user-defined code from within the server in pre-defined code paths.
3185+
This helps in testing and in the investigation of concurrency scenarios
3186+
in a controlled fashion. This option is disabled by default. See
3187+
<xref linkend="xfunc-addin-injection-points"/> for more details. This
3188+
option is intended to be used only by developers for testing.
3189+
</para>
3190+
</listitem>
3191+
</varlistentry>
3192+
31633193
<varlistentry id="configure-segsize-blocks-meson">
31643194
<term><option>-Dsegsize_blocks=SEGSIZE_BLOCKS</option></term>
31653195
<listitem>

doc/src/sgml/xfunc.sgml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3599,6 +3599,75 @@ uint32 WaitEventExtensionNew(const char *wait_event_name)
35993599
</para>
36003600
</sect2>
36013601

3602+
<sect2 id="xfunc-addin-injection-points">
3603+
<title>Injection Points</title>
3604+
3605+
<para>
3606+
An injection point with a given <literal>name</literal> is declared using
3607+
macro:
3608+
<programlisting>
3609+
INJECTION_POINT(name);
3610+
</programlisting>
3611+
3612+
There are a few injection points already declared at strategic points
3613+
within the server code. After adding a new injection point the code needs
3614+
to be compiled in order for that injection point to be available in the
3615+
binary. Add-ins written in C-language can declare injection points in
3616+
their own code using the same macro.
3617+
</para>
3618+
3619+
<para>
3620+
Add-ins can attach callbacks to an already-declared injection point by
3621+
calling:
3622+
<programlisting>
3623+
extern void InjectionPointAttach(const char *name,
3624+
const char *library,
3625+
const char *function);
3626+
</programlisting>
3627+
3628+
<literal>name</literal> is the name of the injection point, which when
3629+
reached during execution will execute the <literal>function</literal>
3630+
loaded from <literal>library</literal>.
3631+
</para>
3632+
3633+
<para>
3634+
Here is an example of callback for
3635+
<literal>InjectionPointCallback</literal>:
3636+
<programlisting>
3637+
static void
3638+
custom_injection_callback(const char *name)
3639+
{
3640+
elog(NOTICE, "%s: executed custom callback", name);
3641+
}
3642+
</programlisting>
3643+
This callback prints a message to server error log with severity
3644+
<literal>NOTICE</literal>, but callbacks may implement more complex
3645+
logic.
3646+
</para>
3647+
3648+
<para>
3649+
Optionally, it is possible to detach an injection point by calling:
3650+
<programlisting>
3651+
extern void InjectionPointDetach(const char *name);
3652+
</programlisting>
3653+
</para>
3654+
3655+
<para>
3656+
A callback attached to an injection point is available across all the
3657+
backends including the backends started after
3658+
<literal>InjectionPointAttach</literal> is called. It remains attached
3659+
while the server is running or until the injection point is detached
3660+
using <literal>InjectionPointDetach</literal>.
3661+
</para>
3662+
3663+
<para>
3664+
Enabling injections points requires
3665+
<option>--enable-injection-points</option> with
3666+
<command>configure</command> or <option>-Dinjection_points=true</option>
3667+
with <application>Meson</application>.
3668+
</para>
3669+
</sect2>
3670+
36023671
<sect2 id="extend-cpp">
36033672
<title>Using C++ for Extensibility</title>
36043673

meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ meson_bin = find_program(meson_binpath, native: true)
431431
###############################################################
432432

433433
cdata.set('USE_ASSERT_CHECKING', get_option('cassert') ? 1 : false)
434+
cdata.set('USE_INJECTION_POINTS', get_option('injection_points') ? 1 : false)
434435

435436
blocksize = get_option('blocksize').to_int() * 1024
436437

meson_options.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ option('cassert', type: 'boolean', value: false,
4343
option('tap_tests', type: 'feature', value: 'auto',
4444
description: 'Enable TAP tests')
4545

46+
option('injection_points', type: 'boolean', value: false,
47+
description: 'Enable injection points')
48+
4649
option('PG_TEST_EXTRA', type: 'string', value: '',
4750
description: 'Enable selected extra tests')
4851

src/Makefile.global.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ enable_nls = @enable_nls@
203203
enable_debug = @enable_debug@
204204
enable_dtrace = @enable_dtrace@
205205
enable_coverage = @enable_coverage@
206+
enable_injection_points = @enable_injection_points@
206207
enable_tap_tests = @enable_tap_tests@
207208

208209
python_includespec = @python_includespec@

src/backend/storage/ipc/ipci.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include "storage/sinvaladt.h"
5252
#include "storage/spin.h"
5353
#include "utils/guc.h"
54+
#include "utils/injection_point.h"
5455
#include "utils/snapmgr.h"
5556
#include "utils/wait_event.h"
5657

@@ -151,6 +152,7 @@ CalculateShmemSize(int *num_semaphores)
151152
size = add_size(size, AsyncShmemSize());
152153
size = add_size(size, StatsShmemSize());
153154
size = add_size(size, WaitEventExtensionShmemSize());
155+
size = add_size(size, InjectionPointShmemSize());
154156
#ifdef EXEC_BACKEND
155157
size = add_size(size, ShmemBackendArraySize());
156158
#endif
@@ -354,6 +356,7 @@ CreateOrAttachShmemStructs(void)
354356
AsyncShmemInit();
355357
StatsShmemInit();
356358
WaitEventExtensionShmemInit();
359+
InjectionPointShmemInit();
357360
}
358361

359362
/*

src/backend/storage/lmgr/lwlocknames.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ NotifyQueueTailLock 47
5656
WaitEventExtensionLock 48
5757
WALSummarizerLock 49
5858
DSMRegistryLock 50
59+
InjectionPointLock 51

src/backend/utils/activity/wait_event_names.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ NotifyQueueTail "Waiting to update limit on <command>NOTIFY</command> message st
330330
WaitEventExtension "Waiting to read or update custom wait events information for extensions."
331331
WALSummarizer "Waiting to read or update WAL summarization state."
332332
DSMRegistry "Waiting to read or update the dynamic shared memory registry."
333+
InjectionPoint "Waiting to read or update information related to injection points."
333334

334335
#
335336
# END OF PREDEFINED LWLOCKS (DO NOT CHANGE THIS LINE)

src/backend/utils/misc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ OBJS = \
2121
guc_funcs.o \
2222
guc_tables.o \
2323
help_config.o \
24+
injection_point.o \
2425
pg_config.o \
2526
pg_controldata.o \
2627
pg_rusage.o \

0 commit comments

Comments
 (0)