PostgreSQL Source Code: src/backend/utils/misc/injection_point.c Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
19
21
22#ifdef USE_INJECTION_POINTS
23
24#include <sys/stat.h>
25
33
34
35#define INJ_NAME_MAXLEN 64
36#define INJ_LIB_MAXLEN 128
37#define INJ_FUNC_MAXLEN 128
38#define INJ_PRIVATE_MAXLEN 1024
39
40
41typedef struct InjectionPointEntry
42{
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
62
64 char library[INJ_LIB_MAXLEN];
65 char function[INJ_FUNC_MAXLEN];
66
67
68
69
70
71 char private_data[INJ_PRIVATE_MAXLEN];
72} InjectionPointEntry;
73
74#define MAX_INJECTION_POINTS 128
75
76
77
78
79
80
81
82
83typedef struct InjectionPointsCtl
84{
86 InjectionPointEntry entries[MAX_INJECTION_POINTS];
87} InjectionPointsCtl;
88
90
91
92
93
94
95typedef struct InjectionPointCacheEntry
96{
98 char private_data[INJ_PRIVATE_MAXLEN];
100
101
102
103
104
105
106 int slot_idx;
108} InjectionPointCacheEntry;
109
110static HTAB *InjectionPointCache = NULL;
111
112
113
114
115
116
117static InjectionPointCacheEntry *
118injection_point_cache_add(const char *name,
119 int slot_idx,
122 const void *private_data)
123{
124 InjectionPointCacheEntry *entry;
125 bool found;
126
127
128 if (InjectionPointCache == NULL)
129 {
131
133 hash_ctl.entrysize = sizeof(InjectionPointCacheEntry);
135
136 InjectionPointCache = hash_create("InjectionPoint cache hash",
137 MAX_INJECTION_POINTS,
138 &hash_ctl,
140 }
141
142 entry = (InjectionPointCacheEntry *)
144
146 strlcpy(entry->name, name, sizeof(entry->name));
147 entry->slot_idx = slot_idx;
148 entry->generation = generation;
150 memcpy(entry->private_data, private_data, INJ_PRIVATE_MAXLEN);
151
152 return entry;
153}
154
155
156
157
158
159
160
161
162static void
163injection_point_cache_remove(const char *name)
164{
166
169}
170
171
172
173
174
175
176static InjectionPointCacheEntry *
177injection_point_cache_load(InjectionPointEntry *entry, int slot_idx, uint64 generation)
178{
180 void *injection_callback_local;
181
183 entry->library, DLSUFFIX);
184
186 elog(ERROR, "could not find library \"%s\" for injection point \"%s\"",
187 path, entry->name);
188
189 injection_callback_local = (void *)
191
192 if (injection_callback_local == NULL)
193 elog(ERROR, "could not find function \"%s\" in library \"%s\" for injection point \"%s\"",
194 entry->function, path, entry->name);
195
196
197 return injection_point_cache_add(entry->name,
198 slot_idx,
199 generation,
200 injection_callback_local,
201 entry->private_data);
202}
203
204
205
206
207
208
209static InjectionPointCacheEntry *
210injection_point_cache_get(const char *name)
211{
212 bool found;
213 InjectionPointCacheEntry *entry;
214
215
216 if (InjectionPointCache == NULL)
217 return NULL;
218
219 entry = (InjectionPointCacheEntry *)
221
222 if (found)
223 return entry;
224
225 return NULL;
226}
227#endif
228
229
230
231
234{
235#ifdef USE_INJECTION_POINTS
237
238 sz = add_size(sz, sizeof(InjectionPointsCtl));
239 return sz;
240#else
241 return 0;
242#endif
243}
244
245
246
247
248void
250{
251#ifdef USE_INJECTION_POINTS
252 bool found;
253
254 ActiveInjectionPoints = ShmemInitStruct("InjectionPoint hash",
255 sizeof(InjectionPointsCtl),
256 &found);
258 {
261 for (int i = 0; i < MAX_INJECTION_POINTS; i++)
263 }
264 else
266#endif
267}
268
269
270
271
272void
274 const char *library,
276 const void *private_data,
277 int private_data_size)
278{
279#ifdef USE_INJECTION_POINTS
280 InjectionPointEntry *entry;
283 int free_idx;
284
286 elog(ERROR, "injection point name %s too long (maximum of %u)",
288 if (strlen(library) >= INJ_LIB_MAXLEN)
289 elog(ERROR, "injection point library %s too long (maximum of %u)",
290 library, INJ_LIB_MAXLEN);
291 if (strlen(function) >= INJ_FUNC_MAXLEN)
292 elog(ERROR, "injection point function %s too long (maximum of %u)",
294 if (private_data_size >= INJ_PRIVATE_MAXLEN)
295 elog(ERROR, "injection point data too long (maximum of %u)",
296 INJ_PRIVATE_MAXLEN);
297
298
299
300
301
304 free_idx = -1;
305
306 for (int idx = 0; idx < max_inuse; idx++)
307 {
308 entry = &ActiveInjectionPoints->entries[idx];
310 if (generation % 2 == 0)
311 {
312
313
314
315
316 if (free_idx == -1)
317 free_idx = idx;
318 }
319 else if (strcmp(entry->name, name) == 0)
320 elog(ERROR, "injection point \"%s\" already defined", name);
321 }
322 if (free_idx == -1)
323 {
324 if (max_inuse == MAX_INJECTION_POINTS)
325 elog(ERROR, "too many injection points");
326 free_idx = max_inuse;
327 }
328 entry = &ActiveInjectionPoints->entries[free_idx];
330 Assert(generation % 2 == 0);
331
332
333 strlcpy(entry->name, name, sizeof(entry->name));
335 strlcpy(entry->library, library, sizeof(entry->library));
336 entry->library[INJ_LIB_MAXLEN - 1] = '\0';
337 strlcpy(entry->function, function, sizeof(entry->function));
338 entry->function[INJ_FUNC_MAXLEN - 1] = '\0';
339 if (private_data != NULL)
340 memcpy(entry->private_data, private_data, private_data_size);
341
344
345 if (free_idx + 1 > max_inuse)
347
349
350#else
351 elog(ERROR, "injection points are not supported by this build");
352#endif
353}
354
355
356
357
358
359
360bool
362{
363#ifdef USE_INJECTION_POINTS
364 bool found = false;
366 int max_inuse;
367
369
370
371 max_inuse = (int) pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
372 for (idx = max_inuse - 1; idx >= 0; --idx)
373 {
374 InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
376
378 if (generation % 2 == 0)
379 continue;
380
381 if (strcmp(entry->name, name) == 0)
382 {
384 found = true;
386 break;
387 }
388 }
389
390
391 if (found && idx == max_inuse - 1)
392 {
394 {
395 InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
397
399 if (generation % 2 != 0)
400 break;
401 }
403 }
405
406 return found;
407#else
408 elog(ERROR, "Injection points are not supported by this build");
409 return true;
410#endif
411}
412
413#ifdef USE_INJECTION_POINTS
414
415
416
417
418
419
420static InjectionPointCacheEntry *
421InjectionPointCacheRefresh(const char *name)
422{
424 int namelen;
425 InjectionPointEntry local_copy;
426 InjectionPointCacheEntry *cached;
427
428
429
430
431
432
433
435 if (max_inuse == 0)
436 {
437 if (InjectionPointCache)
438 {
440 InjectionPointCache = NULL;
441 }
442 return NULL;
443 }
444
445
446
447
448
449 cached = injection_point_cache_get(name);
450 if (cached)
451 {
452 int idx = cached->slot_idx;
453 InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
454
456 {
457
458 return cached;
459 }
460 injection_point_cache_remove(name);
461 cached = NULL;
462 }
463
464
465
466
467
468
469
470
471
472
473 namelen = strlen(name);
474 for (int idx = 0; idx < max_inuse; idx++)
475 {
476 InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
478
479
480
481
482
483
485 if (generation % 2 == 0)
486 continue;
488
489
490 if (memcmp(entry->name, name, namelen + 1) != 0)
491 continue;
492
493
494
495
496
497
498
499 memcpy(&local_copy, entry, sizeof(InjectionPointEntry));
500
503 {
504
505
506
507
508
509
510
511
512
513 continue;
514 }
515
516
517 return injection_point_cache_load(&local_copy, idx, generation);
518 }
519 return NULL;
520}
521#endif
522
523
524
525
526
527
528
529
530void
532{
533#ifdef USE_INJECTION_POINTS
534 InjectionPointCacheRefresh(name);
535#else
536 elog(ERROR, "Injection points are not supported by this build");
537#endif
538}
539
540
541
542
543void
545{
546#ifdef USE_INJECTION_POINTS
547 InjectionPointCacheEntry *cache_entry;
548
549 cache_entry = InjectionPointCacheRefresh(name);
550 if (cache_entry)
551 cache_entry->callback(name, cache_entry->private_data, arg);
552#else
553 elog(ERROR, "Injection points are not supported by this build");
554#endif
555}
556
557
558
559
560void
562{
563#ifdef USE_INJECTION_POINTS
564 InjectionPointCacheEntry *cache_entry;
565
566 cache_entry = injection_point_cache_get(name);
567 if (cache_entry)
568 cache_entry->callback(name, cache_entry->private_data, arg);
569#else
570 elog(ERROR, "Injection points are not supported by this build");
571#endif
572}
573
574
575
576
577bool
579{
580#ifdef USE_INJECTION_POINTS
581 return InjectionPointCacheRefresh(name) != NULL;
582#else
583 elog(ERROR, "Injection points are not supported by this build");
584 return false;
585#endif
586}
Datum idx(PG_FUNCTION_ARGS)
static void pg_atomic_write_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
#define pg_read_barrier()
static void pg_atomic_init_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
#define pg_write_barrier()
static void pg_atomic_write_u32(volatile pg_atomic_uint32 *ptr, uint32 val)
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
static void pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
static uint64 pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr)
#define PG_USED_FOR_ASSERTS_ONLY
void * load_external_function(const char *filename, const char *funcname, bool signalNotFound, void **filehandle)
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
void hash_destroy(HTAB *hashp)
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
bool pg_file_exists(const char *name)
char pkglib_path[MAXPGPATH]
Assert(PointerIsAligned(start, uint64))
void InjectionPointShmemInit(void)
Size InjectionPointShmemSize(void)
void InjectionPointLoad(const char *name)
bool InjectionPointDetach(const char *name)
void InjectionPointRun(const char *name, void *arg)
bool IsInjectionPointAttached(const char *name)
void InjectionPointAttach(const char *name, const char *library, const char *function, const void *private_data, int private_data_size)
void InjectionPointCached(const char *name, void *arg)
void(* InjectionPointCallback)(const char *name, const void *private_data, void *arg)
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
void LWLockRelease(LWLock *lock)
MemoryContext TopMemoryContext
on_exit_nicely_callback function
size_t strlcpy(char *dst, const char *src, size_t siz)
Size add_size(Size s1, Size s2)
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)