[DTrace-devel] [PATCH v4 17/23] drti: re-register DOF after fork
Nick Alcock
nick.alcock at oracle.com
Wed Feb 21 20:48:11 UTC 2024
DTrace v2 doesn't automatically copy tracing sessions across fork boundaries
the way v1 used to (in the kernel). Instead. re-register DOF in the forked
child just like it was registered for the original process at ELF
constructor invocation time.
We do this using pthread_atfork, but older glibc puts pthread_atfork in
-lpthread which clients are obviously not required to link with; so we spot
such old glibcs and directly call the corresponding not-exactly-internal
glibc function if found. This function is always in libc; a call to it is
found in libc_nonshared and it is directly used by systemd, so it's
ABI-stable and safe to call.)
In the process, clean up some messes: moving the device file I/O into a
separate function means we no longer need to close it on error in a dozen
places; don't mess around with temporary variables just to open
/proc/self/maps, which is a literal string (back in the day it used to be
/proc/%i/maps where %i was getpid(), but not for years).
(Technically, calling ioctl() from a pthread_atfork() handler is dodgy as it
is not async-signal-safe. In practice, in this implementation, it's fine.
The use of dprintf() in error messages is more likely to be problematic.)
Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
---
Makeconfig | 1 +
include/sys/compiler.h | 4 ++-
libdtrace/drti.c | 77 +++++++++++++++++++++++++++++-------------
3 files changed, 58 insertions(+), 24 deletions(-)
diff --git a/Makeconfig b/Makeconfig
index a4f77f116e5d3..bcea9df97cf5c 100644
--- a/Makeconfig
+++ b/Makeconfig
@@ -184,6 +184,7 @@ help-overrides-header::
$(eval $(call check-symbol-rule,ELF_GETSHDRSTRNDX,elf_getshdrstrndx,elf))
$(eval $(call check-symbol-rule,LIBCTF,ctf_open,ctf,t))
$(eval $(call check-symbol-rule,STRRSTR,strrstr,c))
+$(eval $(call check-symbol-rule,PTHREAD_ATFORK,pthread_atfork,c))
$(eval $(call check-symbol-rule,LIBSYSTEMD,sd_notify,systemd,t))
ifndef WANTS_LIBFUSE2
$(eval $(call check-symbol-rule,FUSE_LOG,fuse_set_log_func,fuse3))
diff --git a/include/sys/compiler.h b/include/sys/compiler.h
index 82066e5e48d5f..8931756981037 100644
--- a/include/sys/compiler.h
+++ b/include/sys/compiler.h
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
@@ -37,6 +37,7 @@
#define _dt_printflike_(string_index,first_to_check) __attribute__((__format__(__printf__,(string_index),(first_to_check))))
#define _dt_unused_ __attribute__((__unused__))
#define _dt_noreturn_ __attribute__((__noreturn__))
+#define _dt_weak_ __attribute__((__weak__))
#define _dt_unlikely_(x) __builtin_expect((x),0)
#define _dt_barrier_(x) __asm__ __volatile__("": :"r"(x):"memory")
@@ -45,6 +46,7 @@
#define _dt_constructor_(x) _Pragma("init(" #x ")")
#define _dt_destructor_(x) _Pragma("fini(" #x ")")
#define _dt_noreturn_
+#define _dt_weak_
#define _dt_unlikely_(x) (x)
#define _dt_barrier_(x)
diff --git a/libdtrace/drti.c b/libdtrace/drti.c
index e1252b4fe02b7..38c845d028688 100644
--- a/libdtrace/drti.c
+++ b/libdtrace/drti.c
@@ -1,6 +1,6 @@
/*
* Oracle Linux DTrace.
- * Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2024, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
@@ -12,6 +12,7 @@
#include <sys/dtrace.h>
#include <sys/compiler.h>
#include <sys/ioctl.h>
+#include <pthread.h>
#include <gelf.h>
@@ -37,6 +38,32 @@ static const char *modname; /* Name of this load object */
static int gen; /* DOF helper generation */
extern dof_hdr_t __SUNW_dof; /* DOF defined in the .SUNW_dof section */
static boolean_t dof_init_debug = B_FALSE; /* From DTRACE_DOF_INIT_DEBUG */
+static dof_helper_t dh;
+
+static void dtrace_dof_register(void);
+
+static int
+private_pthread_atfork(void (*prepare)(void), void (*parent)(void),
+ void (*child)(void))
+{
+ /* Sufficiently old glibc doesn't define pthread_atfork in libc, so we have to
+ use an internal interface instead in order to not force all probe users to
+ pull in -lpthread. This internal interface is used by the pthread_atfork
+ implementation in libc_nonshared.a in all glibcs new enough not to be
+ affected by this problem, so there are no stable-ABI concerns here: the ABI
+ is stable regardless. */
+
+#ifdef HAVE_PTHREAD_ATFORK
+ return pthread_atfork(prepare, parent, child);
+#else
+ extern int __register_atfork(void (*prepare) (void),
+ void (*parent) (void),
+ void (*child) (void), void *dso_handle);
+ extern void *__dso_handle _dt_weak_;
+
+ return __register_atfork(prepare, parent, child, __dso_handle);
+#endif
+}
_dt_constructor_(dtrace_dof_init)
static void
@@ -48,12 +75,9 @@ dtrace_dof_init(void)
#else
Elf32_Ehdr *elf;
#endif
- dof_helper_t dh;
struct link_map *lmp = NULL;
Lmid_t lmid = -1;
- int fd;
const char *p;
- char mfn[PATH_MAX]; /* "/proc/<pid>/maps" */
char str[4096]; /* read buffer */
char *enm = NULL; /* pointer to target executable name */
FILE *fp;
@@ -68,30 +92,22 @@ dtrace_dof_init(void)
if ((p = getenv("DTRACE_DOF_INIT_DEVNAME")) != NULL)
devname = p;
- if ((fd = open(devname, O_RDWR)) < 0) {
- if (dof_init_debug)
- dprintf(2, "DRTI: Failed to open helper device %s\n",
- devname);
- return;
- }
-
#if 0
if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &lmp) == -1) {
dprintf(2, "DRTI: Couldn't discover module name or address.\n");
- goto out;
+ return;
}
if (dlinfo(RTLD_SELF, RTLD_DI_LMID, &lmid) == -1) {
dprintf(2, "DRTI: Couldn't discover link map ID.\n");
- goto out;
+ return;
}
#else
lmid = 0; /* We need a way to determine this. */
- snprintf(mfn, sizeof(mfn), "/proc/self/maps");
- if ((fp = fopen(mfn, "re")) == NULL) {
+ if ((fp = fopen("/proc/self/maps", "re")) == NULL) {
dprintf(2, "DRTI: Failed to open maps file.\n");
- goto out;
+ return;
}
while (fgets(str, sizeof(str), fp) != NULL) {
uintptr_t start, end;
@@ -119,7 +135,7 @@ dtrace_dof_init(void)
if (_dt_unlikely_(enm == NULL)) {
fclose(fp);
dprintf(2, "DRTI: Couldn't discover module name or address.\n");
- goto out;
+ return;
}
/* Now start at the beginning & look for 1st segment of the target */
@@ -150,7 +166,7 @@ dtrace_dof_init(void)
#endif
if (_dt_unlikely_(lmp == NULL)) {
dprintf(2, "DRTI: Couldn't discover module name or address.\n");
- goto out;
+ return;
}
if ((modname = strrchr(lmp->l_name, '/')) == NULL)
@@ -164,7 +180,7 @@ dtrace_dof_init(void)
dof->dofh_ident[DOF_ID_MAG3] != DOF_MAG_MAG3) {
dprintf(2, "DRTI: .SUNW_dof section corrupt in %s.\n",
lmp->l_name);
- goto out;
+ return;
}
elf = (void *)lmp->l_addr;
@@ -179,14 +195,29 @@ dtrace_dof_init(void)
(void) snprintf(dh.dofhp_mod, sizeof (dh.dofhp_mod),
"LM%lu`%s", lmid, modname);
}
+ dtrace_dof_register();
+ private_pthread_atfork(NULL, NULL, dtrace_dof_register);
+}
+
+static void
+dtrace_dof_register(void)
+{
+ int fd;
+
+ if ((fd = open(devname, O_RDWR, O_CLOEXEC)) < 0) {
+ if (dof_init_debug)
+ dprintf(2, "DRTI: Failed to open helper device %s\n",
+ devname);
+ return;
+ }
if ((gen = ioctl(fd, DTRACEHIOC_ADDDOF, &dh)) == -1)
- dprintf(2, "DRTI: Ioctl failed for DOF at %p\n", (void *)dof);
+ dprintf(2, "DRTI: Ioctl failed for DOF at %llx\n",
+ (long long unsigned) dh.dofhp_addr);
else if (dof_init_debug)
- dprintf(2, "DRTI: Ioctl OK for DOF at %p (gen %d)\n",
- (void *)dof, gen);
+ dprintf(2, "DRTI: Ioctl OK for DOF at %llx (gen %d)\n",
+ (long long unsigned) dh.dofhp_addr, gen);
- out:
close(fd);
}
--
2.43.0.272.gce700b77fd
More information about the DTrace-devel
mailing list