1e31e7199SColin Percival /*- 2e31e7199SColin Percival * Copyright (c) 2017 Colin Percival 3e31e7199SColin Percival * All rights reserved. 4e31e7199SColin Percival * 5e31e7199SColin Percival * Redistribution and use in source and binary forms, with or without 6e31e7199SColin Percival * modification, are permitted provided that the following conditions 7e31e7199SColin Percival * are met: 8e31e7199SColin Percival * 1. Redistributions of source code must retain the above copyright 9e31e7199SColin Percival * notice, this list of conditions and the following disclaimer. 10e31e7199SColin Percival * 2. Redistributions in binary form must reproduce the above copyright 11e31e7199SColin Percival * notice, this list of conditions and the following disclaimer in the 12e31e7199SColin Percival * documentation and/or other materials provided with the distribution. 13e31e7199SColin Percival * 14e31e7199SColin Percival * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15e31e7199SColin Percival * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16e31e7199SColin Percival * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17e31e7199SColin Percival * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18e31e7199SColin Percival * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19e31e7199SColin Percival * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20e31e7199SColin Percival * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21e31e7199SColin Percival * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22e31e7199SColin Percival * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23e31e7199SColin Percival * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24e31e7199SColin Percival * SUCH DAMAGE. 25e31e7199SColin Percival */ 26e31e7199SColin Percival 27e31e7199SColin Percival #include <sys/cdefs.h> 28e31e7199SColin Percival __FBSDID("$FreeBSD$"); 29e31e7199SColin Percival 30e31e7199SColin Percival #include <sys/param.h> 31fe51b5a7SColin Percival #include <sys/kernel.h> 32fe51b5a7SColin Percival #include <sys/linker.h> 3346dd801aSColin Percival #include <sys/malloc.h> 3446dd801aSColin Percival #include <sys/proc.h> 35e31e7199SColin Percival #include <sys/sbuf.h> 36e31e7199SColin Percival #include <sys/sysctl.h> 37e31e7199SColin Percival #include <sys/systm.h> 38e31e7199SColin Percival #include <sys/tslog.h> 39e31e7199SColin Percival 40e31e7199SColin Percival #include <machine/atomic.h> 41e31e7199SColin Percival #include <machine/cpu.h> 42e31e7199SColin Percival 43e31e7199SColin Percival #ifndef TSLOGSIZE 44e31e7199SColin Percival #define TSLOGSIZE 262144 45e31e7199SColin Percival #endif 46e31e7199SColin Percival 47e31e7199SColin Percival static volatile long nrecs = 0; 48e31e7199SColin Percival static struct timestamp { 49e31e7199SColin Percival void * td; 50e31e7199SColin Percival int type; 51e31e7199SColin Percival const char * f; 52e31e7199SColin Percival const char * s; 53e31e7199SColin Percival uint64_t tsc; 54e31e7199SColin Percival } timestamps[TSLOGSIZE]; 55e31e7199SColin Percival 56e31e7199SColin Percival void 57e31e7199SColin Percival tslog(void * td, int type, const char * f, const char * s) 58e31e7199SColin Percival { 59e31e7199SColin Percival uint64_t tsc = get_cyclecount(); 60e31e7199SColin Percival long pos; 61e31e7199SColin Percival 62*02d90458SColin Percival /* A NULL thread is thread0 before curthread is set. */ 63*02d90458SColin Percival if (td == NULL) 64*02d90458SColin Percival td = &thread0; 65*02d90458SColin Percival 66e31e7199SColin Percival /* Grab a slot. */ 67e31e7199SColin Percival pos = atomic_fetchadd_long(&nrecs, 1); 68e31e7199SColin Percival 69e31e7199SColin Percival /* Store record. */ 70e31e7199SColin Percival if (pos < nitems(timestamps)) { 71e31e7199SColin Percival timestamps[pos].td = td; 72e31e7199SColin Percival timestamps[pos].type = type; 73e31e7199SColin Percival timestamps[pos].f = f; 74e31e7199SColin Percival timestamps[pos].s = s; 75e31e7199SColin Percival timestamps[pos].tsc = tsc; 76e31e7199SColin Percival } 77e31e7199SColin Percival } 78e31e7199SColin Percival 79e31e7199SColin Percival static int 80e31e7199SColin Percival sysctl_debug_tslog(SYSCTL_HANDLER_ARGS) 81e31e7199SColin Percival { 82e31e7199SColin Percival int error; 83e31e7199SColin Percival struct sbuf *sb; 84e31e7199SColin Percival size_t i, limit; 85fe51b5a7SColin Percival caddr_t loader_tslog; 86fe51b5a7SColin Percival void * loader_tslog_buf; 87fe51b5a7SColin Percival size_t loader_tslog_len; 88e31e7199SColin Percival 89e31e7199SColin Percival /* 90e31e7199SColin Percival * This code can race against the code in tslog() which stores 91e31e7199SColin Percival * records: Theoretically we could end up reading a record after 92e31e7199SColin Percival * its slots have been reserved but before it has been written. 93e31e7199SColin Percival * Since this code takes orders of magnitude longer to run than 94e31e7199SColin Percival * tslog() takes to write a record, it is highly unlikely that 95e31e7199SColin Percival * anyone will ever experience this race. 96e31e7199SColin Percival */ 97e31e7199SColin Percival sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req); 98fe51b5a7SColin Percival 99fe51b5a7SColin Percival /* Get data from the boot loader, if it provided any. */ 100fe51b5a7SColin Percival loader_tslog = preload_search_by_type("TSLOG data"); 101fe51b5a7SColin Percival if (loader_tslog != NULL) { 102fe51b5a7SColin Percival loader_tslog_buf = preload_fetch_addr(loader_tslog); 103fe51b5a7SColin Percival loader_tslog_len = preload_fetch_size(loader_tslog); 104fe51b5a7SColin Percival sbuf_bcat(sb, loader_tslog_buf, loader_tslog_len); 105fe51b5a7SColin Percival } 106fe51b5a7SColin Percival 107fe51b5a7SColin Percival /* Add data logged within the kernel. */ 108e31e7199SColin Percival limit = MIN(nrecs, nitems(timestamps)); 109e31e7199SColin Percival for (i = 0; i < limit; i++) { 110e31e7199SColin Percival sbuf_printf(sb, "%p", timestamps[i].td); 111e31e7199SColin Percival sbuf_printf(sb, " %llu", 112e31e7199SColin Percival (unsigned long long)timestamps[i].tsc); 113e31e7199SColin Percival switch (timestamps[i].type) { 114e31e7199SColin Percival case TS_ENTER: 115e31e7199SColin Percival sbuf_printf(sb, " ENTER"); 116e31e7199SColin Percival break; 117e31e7199SColin Percival case TS_EXIT: 118e31e7199SColin Percival sbuf_printf(sb, " EXIT"); 119e31e7199SColin Percival break; 120e31e7199SColin Percival case TS_THREAD: 121e31e7199SColin Percival sbuf_printf(sb, " THREAD"); 122e31e7199SColin Percival break; 123e31e7199SColin Percival case TS_EVENT: 124e31e7199SColin Percival sbuf_printf(sb, " EVENT"); 125e31e7199SColin Percival break; 126e31e7199SColin Percival } 127e31e7199SColin Percival sbuf_printf(sb, " %s", timestamps[i].f ? timestamps[i].f : "(null)"); 128e31e7199SColin Percival if (timestamps[i].s) 129e31e7199SColin Percival sbuf_printf(sb, " %s\n", timestamps[i].s); 130e31e7199SColin Percival else 131e31e7199SColin Percival sbuf_printf(sb, "\n"); 132e31e7199SColin Percival } 133e31e7199SColin Percival error = sbuf_finish(sb); 134e31e7199SColin Percival sbuf_delete(sb); 135e31e7199SColin Percival return (error); 136e31e7199SColin Percival } 137e31e7199SColin Percival 1382406867fSColin Percival SYSCTL_PROC(_debug, OID_AUTO, tslog, 1392406867fSColin Percival CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_MPSAFE|CTLFLAG_SKIP, 140e31e7199SColin Percival 0, 0, sysctl_debug_tslog, "", "Dump recorded event timestamps"); 14146dd801aSColin Percival 14246dd801aSColin Percival MALLOC_DEFINE(M_TSLOGUSER, "tsloguser", "Strings used by userland tslog"); 14346dd801aSColin Percival static struct procdata { 14446dd801aSColin Percival pid_t ppid; 14546dd801aSColin Percival uint64_t tsc_forked; 14646dd801aSColin Percival uint64_t tsc_exited; 14746dd801aSColin Percival char * execname; 14846dd801aSColin Percival char * namei; 14946dd801aSColin Percival int reused; 15046dd801aSColin Percival } procs[PID_MAX + 1]; 15146dd801aSColin Percival 15246dd801aSColin Percival void 15346dd801aSColin Percival tslog_user(pid_t pid, pid_t ppid, const char * execname, const char * namei) 15446dd801aSColin Percival { 15546dd801aSColin Percival uint64_t tsc = get_cyclecount(); 15646dd801aSColin Percival 15746dd801aSColin Percival /* If we wrapped, do nothing. */ 15846dd801aSColin Percival if (procs[pid].reused) 15946dd801aSColin Percival return; 16046dd801aSColin Percival 16146dd801aSColin Percival /* If we have a ppid, we're recording a fork. */ 16246dd801aSColin Percival if (ppid != (pid_t)(-1)) { 16346dd801aSColin Percival /* If we have a ppid already, we wrapped. */ 16446dd801aSColin Percival if (procs[pid].ppid) { 16546dd801aSColin Percival procs[pid].reused = 1; 16646dd801aSColin Percival return; 16746dd801aSColin Percival } 16846dd801aSColin Percival 16946dd801aSColin Percival /* Fill in some fields. */ 17046dd801aSColin Percival procs[pid].ppid = ppid; 17146dd801aSColin Percival procs[pid].tsc_forked = tsc; 17246dd801aSColin Percival return; 17346dd801aSColin Percival } 17446dd801aSColin Percival 17546dd801aSColin Percival /* If we have an execname, record it. */ 17646dd801aSColin Percival if (execname != NULL) { 17752e125c2SColin Percival if (procs[pid].execname != NULL) 17852e125c2SColin Percival free(procs[pid].execname, M_TSLOGUSER); 17946dd801aSColin Percival procs[pid].execname = strdup(execname, M_TSLOGUSER); 18046dd801aSColin Percival return; 18146dd801aSColin Percival } 18246dd801aSColin Percival 18346dd801aSColin Percival /* Record the first namei for the process. */ 18446dd801aSColin Percival if (namei != NULL) { 18546dd801aSColin Percival if (procs[pid].namei == NULL) 18646dd801aSColin Percival procs[pid].namei = strdup(namei, M_TSLOGUSER); 18746dd801aSColin Percival return; 18846dd801aSColin Percival } 18946dd801aSColin Percival 19046dd801aSColin Percival /* Otherwise we're recording an exit. */ 19146dd801aSColin Percival procs[pid].tsc_exited = tsc; 19246dd801aSColin Percival } 19346dd801aSColin Percival 19446dd801aSColin Percival static int 19546dd801aSColin Percival sysctl_debug_tslog_user(SYSCTL_HANDLER_ARGS) 19646dd801aSColin Percival { 19746dd801aSColin Percival int error; 19846dd801aSColin Percival struct sbuf *sb; 19946dd801aSColin Percival pid_t pid; 20046dd801aSColin Percival 20146dd801aSColin Percival sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req); 20246dd801aSColin Percival 20346dd801aSColin Percival /* Export the data we logged. */ 20446dd801aSColin Percival for (pid = 0; pid <= PID_MAX; pid++) { 20546dd801aSColin Percival sbuf_printf(sb, "%zu", (size_t)pid); 20646dd801aSColin Percival sbuf_printf(sb, " %zu", (size_t)procs[pid].ppid); 20746dd801aSColin Percival sbuf_printf(sb, " %llu", 20846dd801aSColin Percival (unsigned long long)procs[pid].tsc_forked); 20946dd801aSColin Percival sbuf_printf(sb, " %llu", 21046dd801aSColin Percival (unsigned long long)procs[pid].tsc_exited); 21146dd801aSColin Percival sbuf_printf(sb, " \"%s\"", procs[pid].execname ? 21246dd801aSColin Percival procs[pid].execname : ""); 21346dd801aSColin Percival sbuf_printf(sb, " \"%s\"", procs[pid].namei ? 21446dd801aSColin Percival procs[pid].namei : ""); 21546dd801aSColin Percival sbuf_printf(sb, "\n"); 21646dd801aSColin Percival } 21746dd801aSColin Percival error = sbuf_finish(sb); 21846dd801aSColin Percival sbuf_delete(sb); 21946dd801aSColin Percival return (error); 22046dd801aSColin Percival } 22146dd801aSColin Percival 22246dd801aSColin Percival SYSCTL_PROC(_debug, OID_AUTO, tslog_user, 2232406867fSColin Percival CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_MPSAFE|CTLFLAG_SKIP, 2242406867fSColin Percival 0, 0, sysctl_debug_tslog_user, 22546dd801aSColin Percival "", "Dump recorded userland event timestamps"); 226