xref: /freebsd/sys/kern/subr_stack.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
18d511e2aSJeff Roberson /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
38a36da99SPedro F. Giffuni  *
48d511e2aSJeff Roberson  * Copyright (c) 2005 Antoine Brodin
58d511e2aSJeff Roberson  * All rights reserved.
68d511e2aSJeff Roberson  *
78d511e2aSJeff Roberson  * Redistribution and use in source and binary forms, with or without
88d511e2aSJeff Roberson  * modification, are permitted provided that the following conditions
98d511e2aSJeff Roberson  * are met:
108d511e2aSJeff Roberson  * 1. Redistributions of source code must retain the above copyright
118d511e2aSJeff Roberson  *    notice, this list of conditions and the following disclaimer.
128d511e2aSJeff Roberson  * 2. Redistributions in binary form must reproduce the above copyright
138d511e2aSJeff Roberson  *    notice, this list of conditions and the following disclaimer in the
148d511e2aSJeff Roberson  *    documentation and/or other materials provided with the distribution.
158d511e2aSJeff Roberson  *
168d511e2aSJeff Roberson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
178d511e2aSJeff Roberson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
188d511e2aSJeff Roberson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
198d511e2aSJeff Roberson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
208d511e2aSJeff Roberson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
218d511e2aSJeff Roberson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
228d511e2aSJeff Roberson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
238d511e2aSJeff Roberson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
248d511e2aSJeff Roberson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
258d511e2aSJeff Roberson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
268d511e2aSJeff Roberson  * SUCH DAMAGE.
278d511e2aSJeff Roberson  */
288d511e2aSJeff Roberson 
299ccca7d1SRobert Watson #include "opt_ddb.h"
309ccca7d1SRobert Watson 
318d511e2aSJeff Roberson #include <sys/param.h>
328d511e2aSJeff Roberson #include <sys/kernel.h>
338d511e2aSJeff Roberson #ifdef KTR
348d511e2aSJeff Roberson #include <sys/ktr.h>
358d511e2aSJeff Roberson #endif
368d511e2aSJeff Roberson #include <sys/linker.h>
378d511e2aSJeff Roberson #include <sys/malloc.h>
388d511e2aSJeff Roberson #include <sys/sbuf.h>
398d511e2aSJeff Roberson #include <sys/stack.h>
408d511e2aSJeff Roberson #include <sys/systm.h>
41de5b1952SAlexander Leidinger #include <sys/sysctl.h>
42de5b1952SAlexander Leidinger 
43de5b1952SAlexander Leidinger FEATURE(stack, "Support for capturing kernel stack");
448d511e2aSJeff Roberson 
45dd902d01SGleb Smirnoff MALLOC_DEFINE(M_STACK, "stack", "Stack Traces");
468d511e2aSJeff Roberson 
4736fecbf3SRobert Watson static int stack_symbol(vm_offset_t pc, char *namebuf, u_int buflen,
48d158fa4aSConrad Meyer 	    long *offset, int flags);
4936fecbf3SRobert Watson static int stack_symbol_ddb(vm_offset_t pc, const char **name, long *offset);
508d511e2aSJeff Roberson 
518d511e2aSJeff Roberson struct stack *
stack_create(int flags)52f38c0c46SMark Johnston stack_create(int flags)
538d511e2aSJeff Roberson {
548d511e2aSJeff Roberson 	struct stack *st;
558d511e2aSJeff Roberson 
56f38c0c46SMark Johnston 	st = malloc(sizeof(*st), M_STACK, flags | M_ZERO);
578d511e2aSJeff Roberson 	return (st);
588d511e2aSJeff Roberson }
598d511e2aSJeff Roberson 
608d511e2aSJeff Roberson void
stack_destroy(struct stack * st)618d511e2aSJeff Roberson stack_destroy(struct stack *st)
628d511e2aSJeff Roberson {
638d511e2aSJeff Roberson 
648d511e2aSJeff Roberson 	free(st, M_STACK);
658d511e2aSJeff Roberson }
668d511e2aSJeff Roberson 
678d511e2aSJeff Roberson int
stack_put(struct stack * st,vm_offset_t pc)688d511e2aSJeff Roberson stack_put(struct stack *st, vm_offset_t pc)
698d511e2aSJeff Roberson {
708d511e2aSJeff Roberson 
718d511e2aSJeff Roberson 	if (st->depth < STACK_MAX) {
728d511e2aSJeff Roberson 		st->pcs[st->depth++] = pc;
738d511e2aSJeff Roberson 		return (0);
748d511e2aSJeff Roberson 	} else
758d511e2aSJeff Roberson 		return (-1);
768d511e2aSJeff Roberson }
778d511e2aSJeff Roberson 
788d511e2aSJeff Roberson void
stack_copy(const struct stack * src,struct stack * dst)79a2035830SPawel Jakub Dawidek stack_copy(const struct stack *src, struct stack *dst)
808d511e2aSJeff Roberson {
818d511e2aSJeff Roberson 
828d511e2aSJeff Roberson 	*dst = *src;
838d511e2aSJeff Roberson }
848d511e2aSJeff Roberson 
858d511e2aSJeff Roberson void
stack_zero(struct stack * st)868d511e2aSJeff Roberson stack_zero(struct stack *st)
878d511e2aSJeff Roberson {
888d511e2aSJeff Roberson 
898d511e2aSJeff Roberson 	bzero(st, sizeof *st);
908d511e2aSJeff Roberson }
918d511e2aSJeff Roberson 
928d511e2aSJeff Roberson void
stack_print(const struct stack * st)93a2035830SPawel Jakub Dawidek stack_print(const struct stack *st)
948d511e2aSJeff Roberson {
959ccca7d1SRobert Watson 	char namebuf[64];
968d511e2aSJeff Roberson 	long offset;
978d511e2aSJeff Roberson 	int i;
988d511e2aSJeff Roberson 
9980a8e5daSKris Kennaway 	KASSERT(st->depth <= STACK_MAX, ("bogus stack"));
1008d511e2aSJeff Roberson 	for (i = 0; i < st->depth; i++) {
10136fecbf3SRobert Watson 		(void)stack_symbol(st->pcs[i], namebuf, sizeof(namebuf),
102d158fa4aSConrad Meyer 		    &offset, M_WAITOK);
1038d511e2aSJeff Roberson 		printf("#%d %p at %s+%#lx\n", i, (void *)st->pcs[i],
1049ccca7d1SRobert Watson 		    namebuf, offset);
1058d511e2aSJeff Roberson 	}
1068d511e2aSJeff Roberson }
1078d511e2aSJeff Roberson 
10836fecbf3SRobert Watson void
stack_print_short(const struct stack * st)109a2035830SPawel Jakub Dawidek stack_print_short(const struct stack *st)
11036fecbf3SRobert Watson {
11136fecbf3SRobert Watson 	char namebuf[64];
11236fecbf3SRobert Watson 	long offset;
11336fecbf3SRobert Watson 	int i;
11436fecbf3SRobert Watson 
11536fecbf3SRobert Watson 	KASSERT(st->depth <= STACK_MAX, ("bogus stack"));
11636fecbf3SRobert Watson 	for (i = 0; i < st->depth; i++) {
11736fecbf3SRobert Watson 		if (i > 0)
11836fecbf3SRobert Watson 			printf(" ");
11936fecbf3SRobert Watson 		if (stack_symbol(st->pcs[i], namebuf, sizeof(namebuf),
120d158fa4aSConrad Meyer 		    &offset, M_WAITOK) == 0)
12136fecbf3SRobert Watson 			printf("%s+%#lx", namebuf, offset);
12236fecbf3SRobert Watson 		else
12336fecbf3SRobert Watson 			printf("%p", (void *)st->pcs[i]);
12436fecbf3SRobert Watson 	}
12536fecbf3SRobert Watson 	printf("\n");
12636fecbf3SRobert Watson }
12736fecbf3SRobert Watson 
1288d511e2aSJeff Roberson void
stack_print_ddb(const struct stack * st)129a2035830SPawel Jakub Dawidek stack_print_ddb(const struct stack *st)
1308d511e2aSJeff Roberson {
131ea797aaeSKonstantin Belousov 	const char *name;
1328d511e2aSJeff Roberson 	long offset;
1338d511e2aSJeff Roberson 	int i;
1348d511e2aSJeff Roberson 
13580a8e5daSKris Kennaway 	KASSERT(st->depth <= STACK_MAX, ("bogus stack"));
1368d511e2aSJeff Roberson 	for (i = 0; i < st->depth; i++) {
137ea797aaeSKonstantin Belousov 		stack_symbol_ddb(st->pcs[i], &name, &offset);
1389ccca7d1SRobert Watson 		printf("#%d %p at %s+%#lx\n", i, (void *)st->pcs[i],
139ea797aaeSKonstantin Belousov 		    name, offset);
1409ccca7d1SRobert Watson 	}
1419ccca7d1SRobert Watson }
14236fecbf3SRobert Watson 
1431b9254f8SMark Johnston #if defined(DDB) || defined(WITNESS)
14436fecbf3SRobert Watson void
stack_print_short_ddb(const struct stack * st)145a2035830SPawel Jakub Dawidek stack_print_short_ddb(const struct stack *st)
14636fecbf3SRobert Watson {
14736fecbf3SRobert Watson 	const char *name;
14836fecbf3SRobert Watson 	long offset;
14936fecbf3SRobert Watson 	int i;
15036fecbf3SRobert Watson 
15136fecbf3SRobert Watson 	KASSERT(st->depth <= STACK_MAX, ("bogus stack"));
15236fecbf3SRobert Watson 	for (i = 0; i < st->depth; i++) {
15336fecbf3SRobert Watson 		if (i > 0)
15436fecbf3SRobert Watson 			printf(" ");
15536fecbf3SRobert Watson 		if (stack_symbol_ddb(st->pcs[i], &name, &offset) == 0)
15636fecbf3SRobert Watson 			printf("%s+%#lx", name, offset);
15736fecbf3SRobert Watson 		else
15836fecbf3SRobert Watson 			printf("%p", (void *)st->pcs[i]);
15936fecbf3SRobert Watson 	}
16036fecbf3SRobert Watson 	printf("\n");
16136fecbf3SRobert Watson }
1623c90d1eaSRobert Watson #endif
1639ccca7d1SRobert Watson 
1649ccca7d1SRobert Watson /*
165d158fa4aSConrad Meyer  * Format stack into sbuf from live kernel.
166d158fa4aSConrad Meyer  *
167d158fa4aSConrad Meyer  * flags - M_WAITOK or M_NOWAIT (EWOULDBLOCK).
1689ccca7d1SRobert Watson  */
169d158fa4aSConrad Meyer int
stack_sbuf_print_flags(struct sbuf * sb,const struct stack * st,int flags,enum stack_sbuf_fmt format)170cd1c083dSPawel Biernacki stack_sbuf_print_flags(struct sbuf *sb, const struct stack *st, int flags,
171cd1c083dSPawel Biernacki     enum stack_sbuf_fmt format)
1729ccca7d1SRobert Watson {
1739ccca7d1SRobert Watson 	char namebuf[64];
1749ccca7d1SRobert Watson 	long offset;
175d158fa4aSConrad Meyer 	int i, error;
1769ccca7d1SRobert Watson 
1779ccca7d1SRobert Watson 	KASSERT(st->depth <= STACK_MAX, ("bogus stack"));
1789ccca7d1SRobert Watson 	for (i = 0; i < st->depth; i++) {
179d158fa4aSConrad Meyer 		error = stack_symbol(st->pcs[i], namebuf, sizeof(namebuf),
180d158fa4aSConrad Meyer 		    &offset, flags);
181d158fa4aSConrad Meyer 		if (error == EWOULDBLOCK)
182d158fa4aSConrad Meyer 			return (error);
183cd1c083dSPawel Biernacki 		switch (format) {
184cd1c083dSPawel Biernacki 		case STACK_SBUF_FMT_LONG:
185cd1c083dSPawel Biernacki 			sbuf_printf(sb, "#%d %p at %s+%#lx\n", i,
186cd1c083dSPawel Biernacki 			    (void *)st->pcs[i], namebuf, offset);
187cd1c083dSPawel Biernacki 			break;
188cd1c083dSPawel Biernacki 		case STACK_SBUF_FMT_COMPACT:
189cd1c083dSPawel Biernacki 			sbuf_printf(sb, "%s+%#lx ", namebuf, offset);
190cd1c083dSPawel Biernacki 			break;
191cd1c083dSPawel Biernacki 		default:
192cd1c083dSPawel Biernacki 			__assert_unreachable();
1939ccca7d1SRobert Watson 		}
194cd1c083dSPawel Biernacki 	}
195cd1c083dSPawel Biernacki 	sbuf_nl_terminate(sb);
196d158fa4aSConrad Meyer 	return (0);
197d158fa4aSConrad Meyer }
198d158fa4aSConrad Meyer 
199d158fa4aSConrad Meyer void
stack_sbuf_print(struct sbuf * sb,const struct stack * st)200d158fa4aSConrad Meyer stack_sbuf_print(struct sbuf *sb, const struct stack *st)
201d158fa4aSConrad Meyer {
202d158fa4aSConrad Meyer 
203cd1c083dSPawel Biernacki 	(void)stack_sbuf_print_flags(sb, st, M_WAITOK, STACK_SBUF_FMT_LONG);
2049ccca7d1SRobert Watson }
2059ccca7d1SRobert Watson 
2061b9254f8SMark Johnston #if defined(DDB) || defined(WITNESS)
2079ccca7d1SRobert Watson void
stack_sbuf_print_ddb(struct sbuf * sb,const struct stack * st)208a2035830SPawel Jakub Dawidek stack_sbuf_print_ddb(struct sbuf *sb, const struct stack *st)
2099ccca7d1SRobert Watson {
210ea797aaeSKonstantin Belousov 	const char *name;
2119ccca7d1SRobert Watson 	long offset;
2129ccca7d1SRobert Watson 	int i;
2139ccca7d1SRobert Watson 
2149ccca7d1SRobert Watson 	KASSERT(st->depth <= STACK_MAX, ("bogus stack"));
2159ccca7d1SRobert Watson 	for (i = 0; i < st->depth; i++) {
21636fecbf3SRobert Watson 		(void)stack_symbol_ddb(st->pcs[i], &name, &offset);
2179ccca7d1SRobert Watson 		sbuf_printf(sb, "#%d %p at %s+%#lx\n", i, (void *)st->pcs[i],
218ea797aaeSKonstantin Belousov 		    name, offset);
2198d511e2aSJeff Roberson 	}
2208d511e2aSJeff Roberson }
221b0606bd1SMaxim Sobolev #endif
2228d511e2aSJeff Roberson 
2238d511e2aSJeff Roberson #ifdef KTR
2248d511e2aSJeff Roberson void
stack_ktr(u_int mask,const char * file,int line,const struct stack * st,u_int depth)225a2035830SPawel Jakub Dawidek stack_ktr(u_int mask, const char *file, int line, const struct stack *st,
22654533f66SConrad Meyer     u_int depth)
2278d511e2aSJeff Roberson {
228b0606bd1SMaxim Sobolev #ifdef DDB
229ea797aaeSKonstantin Belousov 	const char *name;
2308d511e2aSJeff Roberson 	long offset;
2318d511e2aSJeff Roberson 	int i;
232b0606bd1SMaxim Sobolev #endif
2338d511e2aSJeff Roberson 
23480a8e5daSKris Kennaway 	KASSERT(st->depth <= STACK_MAX, ("bogus stack"));
235b0606bd1SMaxim Sobolev #ifdef DDB
236e37a4994SPawel Jakub Dawidek 	if (depth == 0 || st->depth < depth)
237e37a4994SPawel Jakub Dawidek 		depth = st->depth;
238e37a4994SPawel Jakub Dawidek 	for (i = 0; i < depth; i++) {
23936fecbf3SRobert Watson 		(void)stack_symbol_ddb(st->pcs[i], &name, &offset);
2408d511e2aSJeff Roberson 		ktr_tracepoint(mask, file, line, "#%d %p at %s+%#lx",
241ea797aaeSKonstantin Belousov 		    i, st->pcs[i], (u_long)name, offset, 0, 0);
2428d511e2aSJeff Roberson 	}
2438d511e2aSJeff Roberson #endif
244b0606bd1SMaxim Sobolev }
2459ccca7d1SRobert Watson #endif
2468d511e2aSJeff Roberson 
2479ccca7d1SRobert Watson /*
2489ccca7d1SRobert Watson  * Two variants of stack symbol lookup -- one that uses the DDB interfaces
2499ccca7d1SRobert Watson  * and bypasses linker locking, and the other that doesn't.
2509ccca7d1SRobert Watson  */
25136fecbf3SRobert Watson static int
stack_symbol(vm_offset_t pc,char * namebuf,u_int buflen,long * offset,int flags)252d158fa4aSConrad Meyer stack_symbol(vm_offset_t pc, char *namebuf, u_int buflen, long *offset,
253d158fa4aSConrad Meyer     int flags)
2548d511e2aSJeff Roberson {
255d158fa4aSConrad Meyer 	int error;
2568d511e2aSJeff Roberson 
257d158fa4aSConrad Meyer 	error = linker_search_symbol_name_flags((caddr_t)pc, namebuf, buflen,
258d158fa4aSConrad Meyer 	    offset, flags);
259d158fa4aSConrad Meyer 	if (error == 0 || error == EWOULDBLOCK)
260d158fa4aSConrad Meyer 		return (error);
261d158fa4aSConrad Meyer 
2628d511e2aSJeff Roberson 	*offset = 0;
26356905239SRobert Watson 	strlcpy(namebuf, "??", buflen);
26436fecbf3SRobert Watson 	return (ENOENT);
2659ccca7d1SRobert Watson }
2669ccca7d1SRobert Watson 
26736fecbf3SRobert Watson static int
stack_symbol_ddb(vm_offset_t pc,const char ** name,long * offset)268ea797aaeSKonstantin Belousov stack_symbol_ddb(vm_offset_t pc, const char **name, long *offset)
2699ccca7d1SRobert Watson {
270ea797aaeSKonstantin Belousov 	linker_symval_t symval;
271ea797aaeSKonstantin Belousov 	c_linker_sym_t sym;
2729ccca7d1SRobert Watson 
273ea797aaeSKonstantin Belousov 	if (linker_ddb_search_symbol((caddr_t)pc, &sym, offset) != 0)
274ea797aaeSKonstantin Belousov 		goto out;
275ea797aaeSKonstantin Belousov 	if (linker_ddb_symbol_values(sym, &symval) != 0)
276ea797aaeSKonstantin Belousov 		goto out;
277ea797aaeSKonstantin Belousov 	if (symval.name != NULL) {
278ea797aaeSKonstantin Belousov 		*name = symval.name;
27936fecbf3SRobert Watson 		return (0);
280ea797aaeSKonstantin Belousov 	}
281ea797aaeSKonstantin Belousov  out:
2829ccca7d1SRobert Watson 	*offset = 0;
283ea797aaeSKonstantin Belousov 	*name = "??";
28436fecbf3SRobert Watson 	return (ENOENT);
2859ccca7d1SRobert Watson }
286