From owner-svn-src-all@freebsd.org Sun Apr 10 01:25:49 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id CBD7AB08231; Sun, 10 Apr 2016 01:25:49 +0000 (UTC) (envelope-from markj@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 8E3971C51; Sun, 10 Apr 2016 01:25:49 +0000 (UTC) (envelope-from markj@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u3A1PmT0090638; Sun, 10 Apr 2016 01:25:48 GMT (envelope-from markj@FreeBSD.org) Received: (from markj@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u3A1PmpK090636; Sun, 10 Apr 2016 01:25:48 GMT (envelope-from markj@FreeBSD.org) Message-Id: <201604100125.u3A1PmpK090636@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: markj set sender to markj@FreeBSD.org using -f From: Mark Johnston Date: Sun, 10 Apr 2016 01:25:48 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r297773 - in head: cddl/contrib/opensolaris/cmd/dtrace sys/cddl/contrib/opensolaris/uts/common/dtrace X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 10 Apr 2016 01:25:49 -0000 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 #endif +#ifdef __FreeBSD__ +#include +#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 #include #include +#include #include #include #include @@ -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