FreeBSD kernel kern code
kern_tslog.c
Go to the documentation of this file.
1/*-
2 * Copyright (c) 2017 Colin Percival
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/linker.h>
33#include <sys/malloc.h>
34#include <sys/proc.h>
35#include <sys/sbuf.h>
36#include <sys/sysctl.h>
37#include <sys/systm.h>
38#include <sys/tslog.h>
39
40#include <machine/atomic.h>
41#include <machine/cpu.h>
42
43#ifndef TSLOGSIZE
44#define TSLOGSIZE 262144
45#endif
46
47static volatile long nrecs = 0;
48static struct timestamp {
49 void * td;
50 int type;
51 const char * f;
52 const char * s;
53 uint64_t tsc;
55
56void
57tslog(void * td, int type, const char * f, const char * s)
58{
59 uint64_t tsc = get_cyclecount();
60 long pos;
61
62 /* Grab a slot. */
63 pos = atomic_fetchadd_long(&nrecs, 1);
64
65 /* Store record. */
66 if (pos < nitems(timestamps)) {
67 timestamps[pos].td = td;
68 timestamps[pos].type = type;
69 timestamps[pos].f = f;
70 timestamps[pos].s = s;
71 timestamps[pos].tsc = tsc;
72 }
73}
74
75static int
76sysctl_debug_tslog(SYSCTL_HANDLER_ARGS)
77{
78 int error;
79 struct sbuf *sb;
80 size_t i, limit;
81 caddr_t loader_tslog;
82 void * loader_tslog_buf;
83 size_t loader_tslog_len;
84
85 /*
86 * This code can race against the code in tslog() which stores
87 * records: Theoretically we could end up reading a record after
88 * its slots have been reserved but before it has been written.
89 * Since this code takes orders of magnitude longer to run than
90 * tslog() takes to write a record, it is highly unlikely that
91 * anyone will ever experience this race.
92 */
93 sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req);
94
95 /* Get data from the boot loader, if it provided any. */
96 loader_tslog = preload_search_by_type("TSLOG data");
97 if (loader_tslog != NULL) {
98 loader_tslog_buf = preload_fetch_addr(loader_tslog);
99 loader_tslog_len = preload_fetch_size(loader_tslog);
100 sbuf_bcat(sb, loader_tslog_buf, loader_tslog_len);
101 }
102
103 /* Add data logged within the kernel. */
104 limit = MIN(nrecs, nitems(timestamps));
105 for (i = 0; i < limit; i++) {
106 sbuf_printf(sb, "%p", timestamps[i].td);
107 sbuf_printf(sb, " %llu",
108 (unsigned long long)timestamps[i].tsc);
109 switch (timestamps[i].type) {
110 case TS_ENTER:
111 sbuf_printf(sb, " ENTER");
112 break;
113 case TS_EXIT:
114 sbuf_printf(sb, " EXIT");
115 break;
116 case TS_THREAD:
117 sbuf_printf(sb, " THREAD");
118 break;
119 case TS_EVENT:
120 sbuf_printf(sb, " EVENT");
121 break;
122 }
123 sbuf_printf(sb, " %s", timestamps[i].f ? timestamps[i].f : "(null)");
124 if (timestamps[i].s)
125 sbuf_printf(sb, " %s\n", timestamps[i].s);
126 else
127 sbuf_printf(sb, "\n");
128 }
129 error = sbuf_finish(sb);
130 sbuf_delete(sb);
131 return (error);
132}
133
134SYSCTL_PROC(_debug, OID_AUTO, tslog, CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_MPSAFE,
135 0, 0, sysctl_debug_tslog, "", "Dump recorded event timestamps");
136
137MALLOC_DEFINE(M_TSLOGUSER, "tsloguser", "Strings used by userland tslog");
138static struct procdata {
139 pid_t ppid;
140 uint64_t tsc_forked;
141 uint64_t tsc_exited;
142 char * execname;
143 char * namei;
145} procs[PID_MAX + 1];
146
147void
148tslog_user(pid_t pid, pid_t ppid, const char * execname, const char * namei)
149{
150 uint64_t tsc = get_cyclecount();
151
152 /* If we wrapped, do nothing. */
153 if (procs[pid].reused)
154 return;
155
156 /* If we have a ppid, we're recording a fork. */
157 if (ppid != (pid_t)(-1)) {
158 /* If we have a ppid already, we wrapped. */
159 if (procs[pid].ppid) {
160 procs[pid].reused = 1;
161 return;
162 }
163
164 /* Fill in some fields. */
165 procs[pid].ppid = ppid;
166 procs[pid].tsc_forked = tsc;
167 return;
168 }
169
170 /* If we have an execname, record it. */
171 if (execname != NULL) {
172 if (procs[pid].execname != NULL)
173 free(procs[pid].execname, M_TSLOGUSER);
174 procs[pid].execname = strdup(execname, M_TSLOGUSER);
175 return;
176 }
177
178 /* Record the first namei for the process. */
179 if (namei != NULL) {
180 if (procs[pid].namei == NULL)
181 procs[pid].namei = strdup(namei, M_TSLOGUSER);
182 return;
183 }
184
185 /* Otherwise we're recording an exit. */
186 procs[pid].tsc_exited = tsc;
187}
188
189static int
190sysctl_debug_tslog_user(SYSCTL_HANDLER_ARGS)
191{
192 int error;
193 struct sbuf *sb;
194 pid_t pid;
195
196 sb = sbuf_new_for_sysctl(NULL, NULL, 1024, req);
197
198 /* Export the data we logged. */
199 for (pid = 0; pid <= PID_MAX; pid++) {
200 sbuf_printf(sb, "%zu", (size_t)pid);
201 sbuf_printf(sb, " %zu", (size_t)procs[pid].ppid);
202 sbuf_printf(sb, " %llu",
203 (unsigned long long)procs[pid].tsc_forked);
204 sbuf_printf(sb, " %llu",
205 (unsigned long long)procs[pid].tsc_exited);
206 sbuf_printf(sb, " \"%s\"", procs[pid].execname ?
207 procs[pid].execname : "");
208 sbuf_printf(sb, " \"%s\"", procs[pid].namei ?
209 procs[pid].namei : "");
210 sbuf_printf(sb, "\n");
211 }
212 error = sbuf_finish(sb);
213 sbuf_delete(sb);
214 return (error);
215}
216
217SYSCTL_PROC(_debug, OID_AUTO, tslog_user,
218 CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_MPSAFE, 0, 0, sysctl_debug_tslog_user,
219 "", "Dump recorded userland event timestamps");
device_property_type_t type
Definition: bus_if.m:941
void free(void *addr, struct malloc_type *mtp)
Definition: kern_malloc.c:907
struct sbuf * sbuf_new_for_sysctl(struct sbuf *s, char *buf, int length, struct sysctl_req *req)
Definition: kern_sysctl.c:2503
static struct procdata procs[PID_MAX+1]
static int sysctl_debug_tslog(SYSCTL_HANDLER_ARGS)
Definition: kern_tslog.c:76
__FBSDID("$FreeBSD$")
static int sysctl_debug_tslog_user(SYSCTL_HANDLER_ARGS)
Definition: kern_tslog.c:190
void tslog(void *td, int type, const char *f, const char *s)
Definition: kern_tslog.c:57
static volatile long nrecs
Definition: kern_tslog.c:47
void tslog_user(pid_t pid, pid_t ppid, const char *execname, const char *namei)
Definition: kern_tslog.c:148
#define TSLOGSIZE
Definition: kern_tslog.c:44
MALLOC_DEFINE(M_TSLOGUSER, "tsloguser", "Strings used by userland tslog")
static struct timestamp timestamps[TSLOGSIZE]
SYSCTL_PROC(_debug, OID_AUTO, tslog, CTLTYPE_STRING|CTLFLAG_RD|CTLFLAG_MPSAFE, 0, 0, sysctl_debug_tslog, "", "Dump recorded event timestamps")
int reused
Definition: kern_tslog.c:144
uint64_t tsc_forked
Definition: kern_tslog.c:140
pid_t ppid
Definition: kern_tslog.c:139
char * execname
Definition: kern_tslog.c:142
uint64_t tsc_exited
Definition: kern_tslog.c:141
char * namei
Definition: kern_tslog.c:143
int type
Definition: kern_tslog.c:50
const char * s
Definition: kern_tslog.c:52
const char * f
Definition: kern_tslog.c:51
uint64_t tsc
Definition: kern_tslog.c:53
void * td
Definition: kern_tslog.c:49
size_t preload_fetch_size(caddr_t mod)
Definition: subr_module.c:269
caddr_t preload_search_by_type(const char *type)
Definition: subr_module.c:87
void * preload_fetch_addr(caddr_t mod)
Definition: subr_module.c:258
int sbuf_finish(struct sbuf *s)
Definition: subr_sbuf.c:833
void sbuf_delete(struct sbuf *s)
Definition: subr_sbuf.c:898
int sbuf_printf(struct sbuf *s, const char *fmt,...)
Definition: subr_sbuf.c:739
int sbuf_bcat(struct sbuf *s, const void *buf, size_t len)
Definition: subr_sbuf.c:509
int namei(struct nameidata *ndp)
Definition: vfs_lookup.c:535