1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2008-2022 NetApp, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/boottrace.h>
30 #include <sys/kernel.h>
31 #include <sys/malloc.h>
32 #include <sys/pcpu.h>
33 #include <sys/proc.h>
34 #include <sys/resourcevar.h>
35 #include <sys/sbuf.h>
36 #include <sys/syscallsubr.h>
37 #include <sys/sysctl.h>
38 #include <sys/time.h>
39
40 #include <machine/stdarg.h>
41
42 #define dprintf(fmt, ...) \
43 do { \
44 if (dotrace_debugging) \
45 printf(fmt, ##__VA_ARGS__); \
46 } while (0);
47
48 static MALLOC_DEFINE(M_BOOTTRACE, "boottrace", "memory for boot tracing");
49
50 #define BT_TABLE_DEFSIZE 3000
51 #define BT_TABLE_RUNSIZE 2000
52 #define BT_TABLE_SHTSIZE 1000
53 #define BT_TABLE_MINSIZE 500
54
55 /*
56 * Boot-time & shutdown-time event.
57 */
58 struct bt_event {
59 uint64_t tsc; /* Timestamp */
60 uint64_t tick; /* Kernel tick */
61 uint64_t cputime; /* Microseconds of process CPU time */
62 uint32_t cpuid; /* CPU on which the event ran */
63 uint32_t inblock; /* # of blocks in */
64 uint32_t oublock; /* # of blocks out */
65 pid_t pid; /* Current PID */
66 char name[BT_EVENT_NAMELEN]; /* Event name */
67 char tdname[BT_EVENT_TDNAMELEN]; /* Thread name */
68 };
69
70 struct bt_table {
71 uint32_t size; /* Trace table size */
72 uint32_t curr; /* Trace entry to use */
73 uint32_t wrap; /* Wrap-around, instead of dropping */
74 uint32_t drops_early; /* Trace entries dropped before init */
75 uint32_t drops_full; /* Trace entries dropped after full */
76 struct bt_event *table;
77 };
78
79 /* Boot-time tracing */
80 static struct bt_table bt;
81
82 /* Run-time tracing */
83 static struct bt_table rt;
84
85 /* Shutdown-time tracing */
86 static struct bt_table st;
87
88 /* Set when system boot is complete, and we've switched to runtime tracing. */
89 static bool bootdone;
90
91 /* Set when system shutdown has started. */
92 static bool shutdown;
93
94 /* Turn on dotrace() debug logging to console. */
95 static bool dotrace_debugging;
96 TUNABLE_BOOL("debug.boottrace.dotrace_debugging", &dotrace_debugging);
97
98 /* Trace kernel events */
99 static bool dotrace_kernel = true;
100 TUNABLE_BOOL("kern.boottrace.dotrace_kernel", &dotrace_kernel);
101
102 /* Trace userspace events */
103 static bool dotrace_user = true;
104 TUNABLE_BOOL("kern.boottrace.dotrace_user", &dotrace_user);
105
106 static int sysctl_log(SYSCTL_HANDLER_ARGS);
107 static int sysctl_boottrace(SYSCTL_HANDLER_ARGS);
108 static int sysctl_runtrace(SYSCTL_HANDLER_ARGS);
109 static int sysctl_shuttrace(SYSCTL_HANDLER_ARGS);
110 static int sysctl_boottrace_reset(SYSCTL_HANDLER_ARGS);
111
112 SYSCTL_NODE(_kern, OID_AUTO, boottrace, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
113 "boottrace statistics");
114
115 SYSCTL_PROC(_kern_boottrace, OID_AUTO, log,
116 CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE | CTLFLAG_SKIP,
117 0, 0, sysctl_log, "A",
118 "Print a log of the boottrace trace data");
119 SYSCTL_PROC(_kern_boottrace, OID_AUTO, boottrace,
120 CTLTYPE_STRING | CTLFLAG_WR | CTLFLAG_MPSAFE,
121 0, 0, sysctl_boottrace, "A",
122 "Capture a boot-time trace event");
123 SYSCTL_PROC(_kern_boottrace, OID_AUTO, runtrace,
124 CTLTYPE_STRING | CTLFLAG_WR | CTLFLAG_MPSAFE,
125 0, 0, sysctl_runtrace, "A",
126 "Capture a run-time trace event");
127 SYSCTL_PROC(_kern_boottrace, OID_AUTO, shuttrace,
128 CTLTYPE_STRING | CTLFLAG_WR | CTLFLAG_MPSAFE,
129 0, 0, sysctl_shuttrace, "A",
130 "Capture a shutdown-time trace event");
131 SYSCTL_PROC(_kern_boottrace, OID_AUTO, reset,
132 CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_MPSAFE,
133 0, 0, sysctl_boottrace_reset, "A",
134 "Reset run-time tracing table");
135
136 /*
137 * Global enable.
138 */
139 bool __read_mostly boottrace_enabled = false;
140 SYSCTL_BOOL(_kern_boottrace, OID_AUTO, enabled, CTLFLAG_RDTUN,
141 &boottrace_enabled, 0,
142 "Boot-time and shutdown-time tracing enabled");
143
144 /*
145 * Enable dumping of the shutdown trace entries to console.
146 */
147 bool shutdown_trace = false;
148 SYSCTL_BOOL(_kern_boottrace, OID_AUTO, shutdown_trace, CTLFLAG_RWTUN,
149 &shutdown_trace, 0,
150 "Enable kernel shutdown tracing to the console");
151
152 /*
153 * Set the delta threshold (ms) below which events are ignored, used in
154 * determining what to dump to the console.
155 */
156 static int shutdown_trace_threshold;
157 SYSCTL_INT(_kern_boottrace, OID_AUTO, shutdown_trace_threshold, CTLFLAG_RWTUN,
158 &shutdown_trace_threshold, 0,
159 "Tracing threshold (ms) below which tracing is ignored");
160
161 SYSCTL_UINT(_kern_boottrace, OID_AUTO, table_size,
162 CTLFLAG_RDTUN | CTLFLAG_NOFETCH, &bt.size, 0,
163 "Boot-time tracing table size");
164
165 /*
166 * Dump a trace to buffer or if buffer is NULL to console.
167 *
168 * Non-zero delta_threshold selectively prints entries based on delta
169 * with current and previous entry. Otherwise, delta_threshold of 0
170 * prints every trace entry and delta.
171 *
172 * Output something like this:
173 *
174 * CPU msecs delta process event
175 * 11 1228262715 0 init shutdown pre sync begin
176 * 3 1228265622 2907 init shutdown pre sync complete
177 * 3 1228265623 0 init shutdown turned swap off
178 * 18 1228266466 843 init shutdown unmounted all filesystems
179 *
180 * How to interpret:
181 *
182 * delta column represents the time in milliseconds between this event and the previous.
183 * Usually that means you can take the previous event, current event, match them
184 * up in the code, and whatever lies between is the culprit taking time.
185 *
186 * For example, above: Pre-Sync is taking 2907ms, and something between swap and unmount
187 * filesystems is taking 843 milliseconds.
188 *
189 * An event with a delta of 0 are 'landmark' events that simply exist in the output
190 * for the developer to know where the time measurement begins. The 0 is an arbitrary
191 * number that can effectively be ignored.
192 */
193 #define BTC_DELTA_PRINT(bte, msecs, delta) do { \
194 if (sbp) { \
195 sbuf_printf(sbp, fmt, (bte)->cpuid, msecs, delta, \
196 (bte)->tdname, (bte)->name, (bte)->pid, \
197 (bte)->cputime / 1000000, \
198 ((bte)->cputime % 1000000) / 10000, \
199 (bte)->inblock, (bte)->oublock); \
200 } else { \
201 printf(fmt, (bte)->cpuid, msecs, delta, \
202 (bte)->tdname, (bte)->name, (bte)->pid, \
203 (bte)->cputime / 1000000, \
204 ((bte)->cputime % 1000000) / 10000, \
205 (bte)->inblock, (bte)->oublock); \
206 } \
207 } while (0)
208
209 /*
210 * Print the trace entries to the message buffer, or to an sbuf, if provided.
211 *
212 * Entries with a difference less than dthres will not be printed.
213 */
214 static void
boottrace_display(struct sbuf * sbp,struct bt_table * btp,uint64_t dthres)215 boottrace_display(struct sbuf *sbp, struct bt_table *btp, uint64_t dthres)
216 {
217 struct bt_event *evtp;
218 struct bt_event *last_evtp;
219 uint64_t msecs;
220 uint64_t first_msecs;
221 uint64_t last_msecs;
222 uint64_t dmsecs;
223 uint64_t last_dmsecs;
224 uint64_t total_dmsecs;
225 uint32_t i;
226 uint32_t curr;
227 const char *fmt = "%3u %10llu %10llu %-24s %-40s %5d %4d.%02d %5u %5u\n";
228 const char *hdr_fmt = "\n\n%3s %10s %10s %-24s %-40s %5s %6s %5s %5s\n";
229 bool printed;
230 bool last_printed;
231
232 /* Print the header */
233 if (sbp != NULL)
234 sbuf_printf(sbp, hdr_fmt,
235 "CPU", "msecs", "delta", "process",
236 "event", "PID", "CPUtime", "IBlks", "OBlks");
237 else
238 printf(hdr_fmt,
239 "CPU", "msecs", "delta", "process",
240 "event", "PID", "CPUtime", "IBlks", "OBlks");
241
242 first_msecs = 0;
243 last_evtp = NULL;
244 last_msecs = 0;
245 last_dmsecs = 0;
246 last_printed = false;
247 i = curr = btp->curr;
248
249 do {
250 evtp = &btp->table[i];
251 if (evtp->tsc == 0)
252 goto next;
253
254 msecs = cputick2usec(evtp->tick) / 1000;
255 dmsecs = (last_msecs != 0 && msecs > last_msecs) ?
256 msecs - last_msecs : 0;
257 printed = false;
258
259 /*
260 * If a threshold is defined, start filtering events by the
261 * delta of msecs.
262 */
263 if (dthres != 0 && (dmsecs > dthres)) {
264 /*
265 * Print the previous entry as a landmark, even if it
266 * falls below the threshold.
267 */
268 if (last_evtp != NULL && !last_printed)
269 BTC_DELTA_PRINT(last_evtp, last_msecs,
270 last_dmsecs);
271 BTC_DELTA_PRINT(evtp, msecs, dmsecs);
272 printed = true;
273 } else if (dthres == 0) {
274 BTC_DELTA_PRINT(evtp, msecs, dmsecs);
275 printed = true;
276 }
277 if (first_msecs == 0 || msecs < first_msecs)
278 first_msecs = msecs;
279 last_evtp = evtp;
280 last_msecs = msecs;
281 last_dmsecs = dmsecs;
282 last_printed = printed;
283 maybe_yield();
284 next:
285 i = (i + 1) % btp->size;
286 } while (i != curr);
287
288 total_dmsecs = last_msecs > first_msecs ?
289 (last_msecs - first_msecs) : 0;
290 if (sbp != NULL)
291 sbuf_printf(sbp, "Total measured time: %ju msecs\n",
292 (uintmax_t)total_dmsecs);
293 else
294 printf("Total measured time: %ju msecs\n",
295 (uintmax_t)total_dmsecs);
296 }
297
298 /*
299 * Dump trace table entries to the console, given a delta threshold.
300 */
301 void
boottrace_dump_console(void)302 boottrace_dump_console(void)
303 {
304 if (!boottrace_enabled)
305 return;
306
307 if (shutdown || rebooting || KERNEL_PANICKED()) {
308 boottrace_display(NULL, &st, shutdown_trace_threshold);
309 } else {
310 boottrace_display(NULL, &bt, 0);
311 boottrace_display(NULL, &rt, 0);
312 }
313 }
314
315 /*
316 * Records a new tracing event to the specified table.
317 *
318 * We don't use a lock because we want this to be callable from interrupt
319 * context.
320 */
321 static int
dotrace(struct bt_table * btp,const char * eventname,const char * tdname)322 dotrace(struct bt_table *btp, const char *eventname, const char *tdname)
323 {
324 uint32_t idx, nxt;
325 struct rusage usage;
326
327 MPASS(boottrace_enabled);
328 if (tdname == NULL)
329 tdname = (curproc->p_flag & P_SYSTEM) ?
330 curthread->td_name : curproc->p_comm;
331
332 dprintf("dotrace[");
333 dprintf("cpu=%u, pid=%d, tsc=%ju, tick=%d, td='%s', event='%s'",
334 PCPU_GET(cpuid), curthread->td_proc->p_pid,
335 (uintmax_t)get_cyclecount(), ticks, tdname, eventname);
336 if (btp->table == NULL) {
337 btp->drops_early++;
338 dprintf(", return=ENOSPC_1]\n");
339 return (ENOSPC);
340 }
341
342 /* Claim a slot in the table. */
343 do {
344 idx = btp->curr;
345 nxt = (idx + 1) % btp->size;
346 if (nxt == 0 && btp->wrap == 0) {
347 btp->drops_full++;
348 dprintf(", return=ENOSPC_2]\n");
349 return (ENOSPC);
350 }
351 } while (!atomic_cmpset_int(&btp->curr, idx, nxt));
352
353 btp->table[idx].cpuid = PCPU_GET(cpuid);
354 btp->table[idx].tsc = get_cyclecount(),
355 btp->table[idx].tick = cpu_ticks();
356 btp->table[idx].pid = curthread->td_proc->p_pid;
357
358 /*
359 * Get the resource usage for the process. Don't attempt this for the
360 * kernel proc0 or for critical section activities.
361 */
362 if ((curthread->td_proc == &proc0) || (curthread->td_critnest != 0)) {
363 btp->table[idx].cputime = 0;
364 btp->table[idx].inblock = 0;
365 btp->table[idx].oublock = 0;
366 } else {
367 kern_getrusage(curthread, RUSAGE_CHILDREN, &usage);
368 btp->table[idx].cputime =
369 (uint64_t)(usage.ru_utime.tv_sec * 1000000 +
370 usage.ru_utime.tv_usec + usage.ru_stime.tv_sec * 1000000 +
371 usage.ru_stime.tv_usec);
372 btp->table[idx].inblock = (uint32_t)usage.ru_inblock;
373 btp->table[idx].oublock = (uint32_t)usage.ru_oublock;
374 }
375 strlcpy(btp->table[idx].name, eventname, BT_EVENT_NAMELEN);
376 strlcpy(btp->table[idx].tdname, tdname, BT_EVENT_TDNAMELEN);
377
378 dprintf(", return=0]\n");
379 return (0);
380 }
381
382 /*
383 * Log various boot-time events, with the trace message encoded using
384 * printf-like arguments.
385 */
386 int
boottrace(const char * tdname,const char * fmt,...)387 boottrace(const char *tdname, const char *fmt, ...)
388 {
389 char eventname[BT_EVENT_NAMELEN];
390 struct bt_table *btp;
391 va_list ap;
392
393 if (!dotrace_kernel)
394 return (ENXIO);
395
396 va_start(ap, fmt);
397 vsnprintf(eventname, sizeof(eventname), fmt, ap);
398 va_end(ap);
399
400 btp = &bt;
401 if (bootdone)
402 btp = &rt;
403 if (shutdown || rebooting || KERNEL_PANICKED())
404 btp = &st;
405
406 return (dotrace(btp, eventname, tdname));
407 }
408
409 /*
410 * Log a run-time event & switch over to run-time tracing mode.
411 */
412 static int
runtrace(const char * eventname,const char * tdname)413 runtrace(const char *eventname, const char *tdname)
414 {
415
416 bootdone = true;
417 return (dotrace(&rt, eventname, tdname));
418 }
419
420 /*
421 * Parse a boottrace message from userspace.
422 *
423 * The input from may contain a ':' to denote tdname. If not, tdname is
424 * inferred from the process' name.
425 *
426 * e.g. reboot(8):SIGINT to init(8)
427 */
428 static void
boottrace_parse_message(char * message,char ** eventname,char ** tdname)429 boottrace_parse_message(char *message, char **eventname, char **tdname)
430 {
431 char *delim;
432
433 delim = strchr(message, ':');
434 if (delim != NULL) {
435 *delim = '\0';
436 *tdname = message;
437 *eventname = ++delim;
438 } else {
439 *tdname = curproc->p_comm;
440 *eventname = message;
441 }
442 }
443
444 static int
_boottrace_sysctl(struct bt_table * btp,struct sysctl_oid * oidp,struct sysctl_req * req)445 _boottrace_sysctl(struct bt_table *btp, struct sysctl_oid *oidp,
446 struct sysctl_req *req)
447 {
448 char message[BT_MSGLEN];
449 char *eventname, *tdname;
450 int error;
451
452 if (!dotrace_user)
453 return (ENXIO);
454
455 message[0] = '\0';
456 error = sysctl_handle_string(oidp, message, sizeof(message), req);
457 if (error)
458 return (error);
459
460 boottrace_parse_message(message, &eventname, &tdname);
461 error = dotrace(btp, eventname, tdname);
462 if (error == ENOSPC) {
463 /* Ignore table full error. */
464 error = 0;
465 }
466 return (error);
467 }
468
469 static int
sysctl_log(SYSCTL_HANDLER_ARGS)470 sysctl_log(SYSCTL_HANDLER_ARGS)
471 {
472 struct sbuf *sbuf;
473 int error;
474
475 if (!boottrace_enabled || req->newptr != NULL)
476 return (0);
477
478 sbuf = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
479 boottrace_display(sbuf, &bt, 0);
480 boottrace_display(sbuf, &rt, 0);
481 sbuf_finish(sbuf);
482 error = SYSCTL_OUT(req, sbuf_data(sbuf), sbuf_len(sbuf));
483 sbuf_delete(sbuf);
484
485 return (error);
486 }
487
488 static int
sysctl_boottrace(SYSCTL_HANDLER_ARGS)489 sysctl_boottrace(SYSCTL_HANDLER_ARGS)
490 {
491
492 if (!boottrace_enabled)
493 return (0);
494
495 /* No output */
496 if (req->newptr == NULL)
497 return (0);
498
499 /* Trace to rt if we have reached multi-user. */
500 return (_boottrace_sysctl(bootdone ? &rt : &bt, oidp, req));
501 }
502
503 /*
504 * Log a run-time event and switch over to run-time tracing mode.
505 */
506 static int
sysctl_runtrace(SYSCTL_HANDLER_ARGS)507 sysctl_runtrace(SYSCTL_HANDLER_ARGS)
508 {
509
510 if (!boottrace_enabled)
511 return (0);
512
513 /* No output */
514 if (req->newptr == NULL)
515 return (0);
516
517 bootdone = true;
518 return (_boottrace_sysctl(&rt, oidp, req));
519 }
520
521 /*
522 * Log a shutdown-time event and switch over to shutdown tracing mode.
523 */
524 static int
sysctl_shuttrace(SYSCTL_HANDLER_ARGS)525 sysctl_shuttrace(SYSCTL_HANDLER_ARGS)
526 {
527
528 if (!boottrace_enabled)
529 return (0);
530
531 /* No output */
532 if (req->newptr == NULL)
533 return (0);
534
535 shutdown = true;
536 return (_boottrace_sysctl(&st, oidp, req));
537 }
538
539 /*
540 * Reset the runtime tracing buffer.
541 */
542 void
boottrace_reset(const char * actor)543 boottrace_reset(const char *actor)
544 {
545 char tmpbuf[64];
546
547 snprintf(tmpbuf, sizeof(tmpbuf), "reset: %s", actor);
548 runtrace(tmpbuf, NULL);
549 }
550
551 /*
552 * Note that a resize implies a reset, i.e., the index is reset to 0.
553 * We never shrink the array; we can only increase its size.
554 */
555 int
boottrace_resize(u_int newsize)556 boottrace_resize(u_int newsize)
557 {
558
559 if (newsize <= rt.size) {
560 return (EINVAL);
561 }
562 rt.table = realloc(rt.table, newsize * sizeof(struct bt_event),
563 M_BOOTTRACE, M_WAITOK | M_ZERO);
564 rt.size = newsize;
565 boottrace_reset("boottrace_resize");
566 return (0);
567 }
568
569 static int
sysctl_boottrace_reset(SYSCTL_HANDLER_ARGS)570 sysctl_boottrace_reset(SYSCTL_HANDLER_ARGS)
571 {
572
573 if (!boottrace_enabled)
574 return (0);
575
576 if (req->newptr != NULL)
577 boottrace_reset("sysctl_boottrace_reset");
578
579 return (0);
580 }
581
582 static void
boottrace_init(void)583 boottrace_init(void)
584 {
585
586 if (!boottrace_enabled)
587 return;
588
589 /* Boottime trace table */
590 bt.size = BT_TABLE_DEFSIZE;
591 TUNABLE_INT_FETCH("kern.boottrace.table_size", &bt.size);
592 bt.size = max(bt.size, BT_TABLE_MINSIZE);
593 bt.table = malloc(bt.size * sizeof(struct bt_event), M_BOOTTRACE,
594 M_WAITOK | M_ZERO);
595
596 /* Stick in an initial entry. */
597 bt.table[0].cpuid = PCPU_GET(cpuid);
598 strlcpy(bt.table[0].tdname, "boottime", BT_EVENT_TDNAMELEN);
599 strlcpy(bt.table[0].name, "initial event", BT_EVENT_NAMELEN);
600 bt.curr = 1;
601
602 /* Run-time trace table (may wrap-around). */
603 rt.wrap = 1;
604 rt.size = BT_TABLE_RUNSIZE;
605 rt.table = malloc(rt.size * sizeof(struct bt_event), M_BOOTTRACE,
606 M_WAITOK | M_ZERO);
607
608 /* Shutdown trace table */
609 st.size = BT_TABLE_SHTSIZE;
610 st.table = malloc(st.size * sizeof(struct bt_event), M_BOOTTRACE,
611 M_WAITOK | M_ZERO);
612 }
613 SYSINIT(boottrace, SI_SUB_CPU, SI_ORDER_ANY, boottrace_init, NULL);
614