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