xref: /freebsd/sys/kern/kern_tslog.c (revision 02d90458669d6f8063ef32e35069e9246f487b37)
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