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 #include <sys/param.h> 29fe51b5a7SColin Percival #include <sys/kernel.h> 30fe51b5a7SColin Percival #include <sys/linker.h> 3146dd801aSColin Percival #include <sys/malloc.h> 3246dd801aSColin Percival #include <sys/proc.h> 33e31e7199SColin Percival #include <sys/sbuf.h> 34e31e7199SColin Percival #include <sys/sysctl.h> 35e31e7199SColin Percival #include <sys/systm.h> 36e31e7199SColin Percival #include <sys/tslog.h> 37e31e7199SColin Percival 38e31e7199SColin Percival #include <machine/atomic.h> 39e31e7199SColin Percival #include <machine/cpu.h> 40e31e7199SColin Percival 41e31e7199SColin Percival #ifndef TSLOGSIZE 42e31e7199SColin Percival #define TSLOGSIZE 262144 43e31e7199SColin Percival #endif 44e31e7199SColin Percival 45e31e7199SColin Percival static volatile long nrecs = 0; 46e31e7199SColin Percival static struct timestamp { 47e31e7199SColin Percival void * td; 48e31e7199SColin Percival int type; 49e31e7199SColin Percival const char * f; 50e31e7199SColin Percival const char * s; 51e31e7199SColin Percival uint64_t tsc; 52e31e7199SColin Percival } timestamps[TSLOGSIZE]; 53e31e7199SColin Percival 54e31e7199SColin Percival void 55e31e7199SColin Percival tslog(void * td, int type, const char * f, const char * s) 56e31e7199SColin Percival { 57e31e7199SColin Percival uint64_t tsc = get_cyclecount(); 58e31e7199SColin Percival long pos; 59e31e7199SColin Percival 6002d90458SColin Percival /* A NULL thread is thread0 before curthread is set. */ 6102d90458SColin Percival if (td == NULL) 6202d90458SColin Percival td = &thread0; 6302d90458SColin Percival 64e31e7199SColin Percival /* Grab a slot. */ 65e31e7199SColin Percival pos = atomic_fetchadd_long(&nrecs, 1); 66e31e7199SColin Percival 67e31e7199SColin Percival /* Store record. */ 68e31e7199SColin Percival if (pos < nitems(timestamps)) { 69e31e7199SColin Percival timestamps[pos].td = td; 70e31e7199SColin Percival timestamps[pos].type = type; 71e31e7199SColin Percival timestamps[pos].f = f; 72e31e7199SColin Percival timestamps[pos].s = s; 73e31e7199SColin Percival timestamps[pos].tsc = tsc; 74e31e7199SColin Percival } 75e31e7199SColin Percival } 76e31e7199SColin Percival 77e31e7199SColin Percival static int 78e31e7199SColin Percival sysctl_debug_tslog(SYSCTL_HANDLER_ARGS) 79e31e7199SColin Percival { 80e31e7199SColin Percival int error; 81e31e7199SColin Percival struct sbuf *sb; 82e31e7199SColin Percival size_t i, limit; 83fe51b5a7SColin Percival caddr_t loader_tslog; 84fe51b5a7SColin Percival void * loader_tslog_buf; 85fe51b5a7SColin Percival size_t loader_tslog_len; 86e31e7199SColin Percival 87e31e7199SColin Percival /* 88e31e7199SColin Percival * This code can race against the code in tslog() which stores 89e31e7199SColin Percival * records: Theoretically we could end up reading a record after 90e31e7199SColin Percival * its slots have been reserved but before it has been written. 91e31e7199SColin Percival * Since this code takes orders of magnitude longer to run than 92e31e7199SColin Percival * tslog() takes to write a record, it is highly unlikely that 93e31e7199SColin Percival * anyone will ever experience this race. 94e31e7199SColin Percival */ 95e31e7199SColin Percival sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req); 96fe51b5a7SColin Percival 97fe51b5a7SColin Percival /* Get data from the boot loader, if it provided any. */ 98fe51b5a7SColin Percival loader_tslog = preload_search_by_type("TSLOG data"); 99fe51b5a7SColin Percival if (loader_tslog != NULL) { 100fe51b5a7SColin Percival loader_tslog_buf = preload_fetch_addr(loader_tslog); 101fe51b5a7SColin Percival loader_tslog_len = preload_fetch_size(loader_tslog); 102fe51b5a7SColin Percival sbuf_bcat(sb, loader_tslog_buf, loader_tslog_len); 103fe51b5a7SColin Percival } 104fe51b5a7SColin Percival 105fe51b5a7SColin Percival /* Add data logged within the kernel. */ 106e31e7199SColin Percival limit = MIN(nrecs, nitems(timestamps)); 107e31e7199SColin Percival for (i = 0; i < limit; i++) { 108e31e7199SColin Percival sbuf_printf(sb, "%p", timestamps[i].td); 109e31e7199SColin Percival sbuf_printf(sb, " %llu", 110e31e7199SColin Percival (unsigned long long)timestamps[i].tsc); 111e31e7199SColin Percival switch (timestamps[i].type) { 112e31e7199SColin Percival case TS_ENTER: 113*0a713948SAlexander Motin sbuf_cat(sb, " ENTER"); 114e31e7199SColin Percival break; 115e31e7199SColin Percival case TS_EXIT: 116*0a713948SAlexander Motin sbuf_cat(sb, " EXIT"); 117e31e7199SColin Percival break; 118e31e7199SColin Percival case TS_THREAD: 119*0a713948SAlexander Motin sbuf_cat(sb, " THREAD"); 120e31e7199SColin Percival break; 121e31e7199SColin Percival case TS_EVENT: 122*0a713948SAlexander Motin sbuf_cat(sb, " EVENT"); 123e31e7199SColin Percival break; 124e31e7199SColin Percival } 125e31e7199SColin Percival sbuf_printf(sb, " %s", timestamps[i].f ? timestamps[i].f : "(null)"); 126e31e7199SColin Percival if (timestamps[i].s) 127e31e7199SColin Percival sbuf_printf(sb, " %s\n", timestamps[i].s); 128e31e7199SColin Percival else 129*0a713948SAlexander Motin sbuf_putc(sb, '\n'); 130e31e7199SColin Percival } 131e31e7199SColin Percival error = sbuf_finish(sb); 132e31e7199SColin Percival sbuf_delete(sb); 133e31e7199SColin Percival return (error); 134e31e7199SColin Percival } 135e31e7199SColin Percival 1362406867fSColin Percival SYSCTL_PROC(_debug, OID_AUTO, tslog, 1372406867fSColin Percival CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_MPSAFE|CTLFLAG_SKIP, 138e31e7199SColin Percival 0, 0, sysctl_debug_tslog, "", "Dump recorded event timestamps"); 13946dd801aSColin Percival 14046dd801aSColin Percival MALLOC_DEFINE(M_TSLOGUSER, "tsloguser", "Strings used by userland tslog"); 14146dd801aSColin Percival static struct procdata { 14246dd801aSColin Percival pid_t ppid; 14346dd801aSColin Percival uint64_t tsc_forked; 14446dd801aSColin Percival uint64_t tsc_exited; 14546dd801aSColin Percival char * execname; 14646dd801aSColin Percival char * namei; 14746dd801aSColin Percival int reused; 14846dd801aSColin Percival } procs[PID_MAX + 1]; 14946dd801aSColin Percival 15046dd801aSColin Percival void 15146dd801aSColin Percival tslog_user(pid_t pid, pid_t ppid, const char * execname, const char * namei) 15246dd801aSColin Percival { 15346dd801aSColin Percival uint64_t tsc = get_cyclecount(); 15446dd801aSColin Percival 15546dd801aSColin Percival /* If we wrapped, do nothing. */ 15646dd801aSColin Percival if (procs[pid].reused) 15746dd801aSColin Percival return; 15846dd801aSColin Percival 15946dd801aSColin Percival /* If we have a ppid, we're recording a fork. */ 16046dd801aSColin Percival if (ppid != (pid_t)(-1)) { 16146dd801aSColin Percival /* If we have a ppid already, we wrapped. */ 16246dd801aSColin Percival if (procs[pid].ppid) { 16346dd801aSColin Percival procs[pid].reused = 1; 16446dd801aSColin Percival return; 16546dd801aSColin Percival } 16646dd801aSColin Percival 16746dd801aSColin Percival /* Fill in some fields. */ 16846dd801aSColin Percival procs[pid].ppid = ppid; 16946dd801aSColin Percival procs[pid].tsc_forked = tsc; 17046dd801aSColin Percival return; 17146dd801aSColin Percival } 17246dd801aSColin Percival 17346dd801aSColin Percival /* If we have an execname, record it. */ 17446dd801aSColin Percival if (execname != NULL) { 17552e125c2SColin Percival if (procs[pid].execname != NULL) 17652e125c2SColin Percival free(procs[pid].execname, M_TSLOGUSER); 17746dd801aSColin Percival procs[pid].execname = strdup(execname, M_TSLOGUSER); 17846dd801aSColin Percival return; 17946dd801aSColin Percival } 18046dd801aSColin Percival 18146dd801aSColin Percival /* Record the first namei for the process. */ 18246dd801aSColin Percival if (namei != NULL) { 18346dd801aSColin Percival if (procs[pid].namei == NULL) 18446dd801aSColin Percival procs[pid].namei = strdup(namei, M_TSLOGUSER); 18546dd801aSColin Percival return; 18646dd801aSColin Percival } 18746dd801aSColin Percival 18846dd801aSColin Percival /* Otherwise we're recording an exit. */ 18946dd801aSColin Percival procs[pid].tsc_exited = tsc; 19046dd801aSColin Percival } 19146dd801aSColin Percival 19246dd801aSColin Percival static int 19346dd801aSColin Percival sysctl_debug_tslog_user(SYSCTL_HANDLER_ARGS) 19446dd801aSColin Percival { 19546dd801aSColin Percival int error; 19646dd801aSColin Percival struct sbuf *sb; 19746dd801aSColin Percival pid_t pid; 19846dd801aSColin Percival 19946dd801aSColin Percival sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req); 20046dd801aSColin Percival 20146dd801aSColin Percival /* Export the data we logged. */ 20246dd801aSColin Percival for (pid = 0; pid <= PID_MAX; pid++) { 20346dd801aSColin Percival sbuf_printf(sb, "%zu", (size_t)pid); 20446dd801aSColin Percival sbuf_printf(sb, " %zu", (size_t)procs[pid].ppid); 20546dd801aSColin Percival sbuf_printf(sb, " %llu", 20646dd801aSColin Percival (unsigned long long)procs[pid].tsc_forked); 20746dd801aSColin Percival sbuf_printf(sb, " %llu", 20846dd801aSColin Percival (unsigned long long)procs[pid].tsc_exited); 20946dd801aSColin Percival sbuf_printf(sb, " \"%s\"", procs[pid].execname ? 21046dd801aSColin Percival procs[pid].execname : ""); 21146dd801aSColin Percival sbuf_printf(sb, " \"%s\"", procs[pid].namei ? 21246dd801aSColin Percival procs[pid].namei : ""); 213*0a713948SAlexander Motin sbuf_putc(sb, '\n'); 21446dd801aSColin Percival } 21546dd801aSColin Percival error = sbuf_finish(sb); 21646dd801aSColin Percival sbuf_delete(sb); 21746dd801aSColin Percival return (error); 21846dd801aSColin Percival } 21946dd801aSColin Percival 22046dd801aSColin Percival SYSCTL_PROC(_debug, OID_AUTO, tslog_user, 2212406867fSColin Percival CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_MPSAFE|CTLFLAG_SKIP, 2222406867fSColin Percival 0, 0, sysctl_debug_tslog_user, 22346dd801aSColin Percival "", "Dump recorded userland event timestamps"); 224