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