svn commit: r297773 - in head: cddl/contrib/opensolaris/cmd/dtrace sys/cddl/contrib/opensolaris/uts/common/dtrace

Mark Johnston markj at FreeBSD.org
Sun Apr 10 01:25:49 UTC 2016


Author: markj
Date: Sun Apr 10 01:25:48 2016
New Revision: 297773
URL: https://svnweb.freebsd.org/changeset/base/297773

Log:
  Implement support for boot-time DTrace.
  
  This allows one to enable DTrace probes relatively early during boot,
  during SI_SUB_DTRACE_ANON, before dtrace(1) can invoked. The desired
  enabling is created using dtrace -A, which writes a /boot/dtrace.dof
  file and uses nextboot(8) to ensure that DTrace kernel modules are loaded
  and that the DOF file describing the enabling is loaded by loader(8)
  during the subsequent boot. The trace output can then be fetched with
  dtrace -a.
  
  With this commit, boot-time DTrace is only functional on i386 and amd64: on
  other architectures, the high-resolution timer frequency is initialized
  during SI_SUB_CLOCKS and is thus not available when the anonymous
  tracing state is initialized. On x86, the TSC is used and is thus available
  earlier.
  
  MFC after:	1 month
  Relnotes:	yes

Modified:
  head/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c
  head/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c

Modified: head/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c
==============================================================================
--- head/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c	Sun Apr 10 01:25:12 2016	(r297772)
+++ head/cddl/contrib/opensolaris/cmd/dtrace/dtrace.c	Sun Apr 10 01:25:48 2016	(r297773)
@@ -50,6 +50,9 @@
 #ifdef illumos
 #include <libproc.h>
 #endif
+#ifdef __FreeBSD__
+#include <spawn.h>
+#endif
 
 typedef struct dtrace_cmd {
 	void (*dc_func)(struct dtrace_cmd *);	/* function to compile arg */
@@ -397,7 +400,41 @@ dof_prune(const char *fname)
 	free(buf);
 }
 
-#ifdef illumos
+#ifdef __FreeBSD__
+/*
+ * Use nextboot(8) to tell the loader to load DTrace kernel modules during
+ * the next boot of the system. The nextboot(8) configuration is removed during
+ * boot, so it will not persist indefinitely.
+ */
+static void
+bootdof_add(void)
+{
+	char * const nbargv[] = {
+		"nextboot", "-a",
+		"-e", "dtraceall_load=\"YES\"",
+		"-e", "dtrace_dof_load=\"YES\"",
+		"-e", "dtrace_dof_name=\"/boot/dtrace.dof\"",
+		"-e", "dtrace_dof_type=\"dtrace_dof\"",
+		NULL,
+	};
+	pid_t child;
+	int err, status;
+
+	err = posix_spawnp(&child, "nextboot", NULL, NULL, nbargv,
+	    NULL);
+	if (err != 0) {
+		error("failed to execute nextboot: %s", strerror(err));
+		exit(E_ERROR);
+	}
+
+	if (waitpid(child, &status, 0) != child)
+		fatal("waiting for nextboot");
+	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+		error("nextboot returned with status %d", status);
+		exit(E_ERROR);
+	}
+}
+#else
 static void
 etcsystem_prune(void)
 {
@@ -508,7 +545,7 @@ etcsystem_add(void)
 
 	error("added forceload directives to %s\n", g_ofile);
 }
-#endif /* illumos */
+#endif /* !__FreeBSD__ */
 
 static void
 print_probe_info(const dtrace_probeinfo_t *p)
@@ -643,24 +680,24 @@ anon_prog(const dtrace_cmd_t *dcp, dof_h
 	p = (uchar_t *)dof;
 	q = p + dof->dofh_loadsz;
 
-#ifdef illumos
-	oprintf("dof-data-%d=0x%x", n, *p++);
-
-	while (p < q)
-		oprintf(",0x%x", *p++);
-
-	oprintf(";\n");
-#else
+#ifdef __FreeBSD__
 	/*
-	 * On FreeBSD, the DOF data is handled as a kernel environment (kenv)
-	 * string. We use two hex characters per DOF byte.
+	 * On FreeBSD, the DOF file is read directly during boot - just write
+	 * two hex characters per byte.
 	 */
-	oprintf("dof-data-%d=%02x", n, *p++);
+	oprintf("dof-data-%d=", n);
 
 	while (p < q)
 		oprintf("%02x", *p++);
 
 	oprintf("\n");
+#else
+	oprintf("dof-data-%d=0x%x", n, *p++);
+
+	while (p < q)
+		oprintf(",0x%x", *p++);
+
+	oprintf(";\n");
 #endif
 
 	dtrace_dof_destroy(g_dtp, dof);
@@ -1725,8 +1762,7 @@ main(int argc, char *argv[])
 #else
 			/*
 			 * On FreeBSD, anonymous DOF data is written to
-			 * the DTrace DOF file that the boot loader will
-			 * read if booting with the DTrace option.
+			 * the DTrace DOF file.
 			 */
 			g_ofile = "/boot/dtrace.dof";
 #endif
@@ -1765,7 +1801,10 @@ main(int argc, char *argv[])
 		 * that itself contains a #pragma D option quiet.
 		 */
 		error("saved anonymous enabling in %s\n", g_ofile);
-#ifdef illumos
+
+#ifdef __FreeBSD__
+		bootdof_add();
+#else
 		etcsystem_add();
 		error("run update_drv(1M) or reboot to enable changes\n");
 #endif

Modified: head/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c	Sun Apr 10 01:25:12 2016	(r297772)
+++ head/sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c	Sun Apr 10 01:25:48 2016	(r297773)
@@ -117,6 +117,7 @@
 #include <sys/ctype.h>
 #include <sys/eventhandler.h>
 #include <sys/limits.h>
+#include <sys/linker.h>
 #include <sys/kdb.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
@@ -11916,6 +11917,21 @@ dtrace_buffer_activate(dtrace_state_t *s
 	dtrace_interrupt_enable(cookie);
 }
 
+#ifdef __FreeBSD__
+/*
+ * Activate the specified per-CPU buffer.  This is used instead of
+ * dtrace_buffer_activate() when APs have not yet started, i.e. when
+ * activating anonymous state.
+ */
+static void
+dtrace_buffer_activate_cpu(dtrace_state_t *state, int cpu)
+{
+
+	if (state->dts_buffer[cpu].dtb_tomax != NULL)
+		state->dts_buffer[cpu].dtb_flags &= ~DTRACEBUF_INACTIVE;
+}
+#endif
+
 static int
 dtrace_buffer_alloc(dtrace_buffer_t *bufs, size_t size, int flags,
     processorid_t cpu, int *factor)
@@ -12532,9 +12548,15 @@ dtrace_enabling_dump(dtrace_enabling_t *
 	for (i = 0; i < enab->dten_ndesc; i++) {
 		dtrace_probedesc_t *desc = &enab->dten_desc[i]->dted_probe;
 
+#ifdef __FreeBSD__
+		printf("dtrace: enabling probe %d (%s:%s:%s:%s)\n", i,
+		    desc->dtpd_provider, desc->dtpd_mod,
+		    desc->dtpd_func, desc->dtpd_name);
+#else
 		cmn_err(CE_NOTE, "enabling probe %d (%s:%s:%s:%s)", i,
 		    desc->dtpd_provider, desc->dtpd_mod,
 		    desc->dtpd_func, desc->dtpd_name);
+#endif
 	}
 }
 
@@ -13185,19 +13207,91 @@ dtrace_dof_char(char c)
 		return (c - 'a' + 10);
 	}
 	/* Should not reach here. */
-	return (0);
+	return (UCHAR_MAX);
 }
 #endif /* __FreeBSD__ */
 
 static dof_hdr_t *
 dtrace_dof_property(const char *name)
 {
+#ifdef __FreeBSD__
+	uint8_t *dofbuf;
+	u_char *data, *eol;
+	caddr_t doffile;
+	size_t bytes, len, i;
+	dof_hdr_t *dof;
+	u_char c1, c2;
+
+	dof = NULL;
+
+	doffile = preload_search_by_type("dtrace_dof");
+	if (doffile == NULL)
+		return (NULL);
+
+	data = preload_fetch_addr(doffile);
+	len = preload_fetch_size(doffile);
+	for (;;) {
+		/* Look for the end of the line. All lines end in a newline. */
+		eol = memchr(data, '\n', len);
+		if (eol == NULL)
+			return (NULL);
+
+		if (strncmp(name, data, strlen(name)) == 0)
+			break;
+
+		eol++; /* skip past the newline */
+		len -= eol - data;
+		data = eol;
+	}
+
+	/* We've found the data corresponding to the specified key. */
+
+	data += strlen(name) + 1; /* skip past the '=' */
+	len = eol - data;
+	bytes = len / 2;
+
+	if (bytes < sizeof(dof_hdr_t)) {
+		dtrace_dof_error(NULL, "truncated header");
+		goto doferr;
+	}
+
+	/*
+	 * Each byte is represented by the two ASCII characters in its hex
+	 * representation.
+	 */
+	dofbuf = malloc(bytes, M_SOLARIS, M_WAITOK);
+	for (i = 0; i < bytes; i++) {
+		c1 = dtrace_dof_char(data[i * 2]);
+		c2 = dtrace_dof_char(data[i * 2 + 1]);
+		if (c1 == UCHAR_MAX || c2 == UCHAR_MAX) {
+			dtrace_dof_error(NULL, "invalid hex char in DOF");
+			goto doferr;
+		}
+		dofbuf[i] = c1 * 16 + c2;
+	}
+
+	dof = (dof_hdr_t *)dofbuf;
+	if (bytes < dof->dofh_loadsz) {
+		dtrace_dof_error(NULL, "truncated DOF");
+		goto doferr;
+	}
+
+	if (dof->dofh_loadsz >= dtrace_dof_maxsize) {
+		dtrace_dof_error(NULL, "oversized DOF");
+		goto doferr;
+	}
+
+	return (dof);
+
+doferr:
+	free(dof, M_SOLARIS);
+	return (NULL);
+#else /* __FreeBSD__ */
 	uchar_t *buf;
 	uint64_t loadsz;
 	unsigned int len, i;
 	dof_hdr_t *dof;
 
-#ifdef illumos
 	/*
 	 * Unfortunately, array of values in .conf files are always (and
 	 * only) interpreted to be integer arrays.  We must read our DOF
@@ -13231,49 +13325,9 @@ dtrace_dof_property(const char *name)
 	dof = kmem_alloc(loadsz, KM_SLEEP);
 	bcopy(buf, dof, loadsz);
 	ddi_prop_free(buf);
-#else
-	char *p;
-	char *p_env;
-
-	if ((p_env = kern_getenv(name)) == NULL)
-		return (NULL);
-
-	len = strlen(p_env) / 2;
-
-	buf = kmem_alloc(len, KM_SLEEP);
-
-	dof = (dof_hdr_t *) buf;
-
-	p = p_env;
-
-	for (i = 0; i < len; i++) {
-		buf[i] = (dtrace_dof_char(p[0]) << 4) |
-		     dtrace_dof_char(p[1]);
-		p += 2;
-	}
-
-	freeenv(p_env);
-
-	if (len < sizeof (dof_hdr_t)) {
-		kmem_free(buf, 0);
-		dtrace_dof_error(NULL, "truncated header");
-		return (NULL);
-	}
-
-	if (len < (loadsz = dof->dofh_loadsz)) {
-		kmem_free(buf, 0);
-		dtrace_dof_error(NULL, "truncated DOF");
-		return (NULL);
-	}
-
-	if (loadsz >= dtrace_dof_maxsize) {
-		kmem_free(buf, 0);
-		dtrace_dof_error(NULL, "oversized DOF");
-		return (NULL);
-	}
-#endif
 
 	return (dof);
+#endif /* !__FreeBSD__ */
 }
 
 static void
@@ -14332,7 +14386,7 @@ static dtrace_state_t *
 #ifdef illumos
 dtrace_state_create(dev_t *devp, cred_t *cr)
 #else
-dtrace_state_create(struct cdev *dev)
+dtrace_state_create(struct cdev *dev, struct ucred *cred __unused)
 #endif
 {
 #ifdef illumos
@@ -14945,6 +14999,18 @@ dtrace_state_go(dtrace_state_t *state, p
 	if (state->dts_activity == DTRACE_ACTIVITY_WARMUP)
 		state->dts_activity = DTRACE_ACTIVITY_ACTIVE;
 
+#ifdef __FreeBSD__
+	/*
+	 * We enable anonymous tracing before APs are started, so we must
+	 * activate buffers using the current CPU.
+	 */
+	if (state == dtrace_anon.dta_state)
+		for (int i = 0; i < NCPU; i++)
+			dtrace_buffer_activate_cpu(state, i);
+	else
+		dtrace_xcall(DTRACE_CPUALL,
+		    (dtrace_xcall_t)dtrace_buffer_activate, state);
+#else
 	/*
 	 * Regardless of whether or not now we're in ACTIVE or DRAINING, we
 	 * want each CPU to transition its principal buffer out of the
@@ -14955,6 +15021,7 @@ dtrace_state_go(dtrace_state_t *state, p
 	 */
 	dtrace_xcall(DTRACE_CPUALL,
 	    (dtrace_xcall_t)dtrace_buffer_activate, state);
+#endif
 	goto out;
 
 err:
@@ -15316,11 +15383,7 @@ dtrace_anon_property(void)
 		 * If we haven't allocated an anonymous state, we'll do so now.
 		 */
 		if ((state = dtrace_anon.dta_state) == NULL) {
-#ifdef illumos
 			state = dtrace_state_create(NULL, NULL);
-#else
-			state = dtrace_state_create(NULL);
-#endif
 			dtrace_anon.dta_state = state;
 
 			if (state == NULL) {
@@ -17001,7 +17064,7 @@ dtrace_open(struct cdev *dev, int oflags
 
 	state = dtrace_state_create(devp, cred_p);
 #else
-	state = dtrace_state_create(dev);
+	state = dtrace_state_create(dev, NULL);
 	devfs_set_cdevpriv(state, dtrace_dtr);
 #endif
 


More information about the svn-src-head mailing list