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