1 /* $OpenBSD: db_trace.c,v 1.17 2024/11/07 16:02:29 miod Exp $ */ 2 /* $NetBSD: db_trace.c,v 1.8 2003/01/17 22:28:48 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 2000, 2001 Ben Harris 6 * Copyright (c) 1996 Scott K. Stevens 7 * 8 * Mach Operating System 9 * Copyright (c) 1991,1990 Carnegie Mellon University 10 * All Rights Reserved. 11 * 12 * Permission to use, copy, modify and distribute this software and its 13 * documentation is hereby granted, provided that both the copyright 14 * notice and this permission notice appear in all copies of the 15 * software, derivative works or modified versions, and any portions 16 * thereof, and that both notices appear in supporting documentation. 17 * 18 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 19 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 20 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 21 * 22 * Carnegie Mellon requests users of this software to return to 23 * 24 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 25 * School of Computer Science 26 * Carnegie Mellon University 27 * Pittsburgh PA 15213-3890 28 * 29 * any improvements or extensions that they make and grant Carnegie the 30 * rights to redistribute these changes. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 36 #include <sys/proc.h> 37 #include <sys/stacktrace.h> 38 #include <sys/user.h> 39 #include <arm64/armreg.h> 40 #include <machine/db_machdep.h> 41 42 #include <ddb/db_access.h> 43 #include <ddb/db_interface.h> 44 #include <ddb/db_variables.h> 45 #include <ddb/db_sym.h> 46 #include <ddb/db_output.h> 47 48 db_regs_t ddb_regs; 49 50 #define INKERNEL(va) (((vaddr_t)(va)) & (1ULL << 63)) 51 52 void 53 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count, 54 char *modif, int (*pr)(const char *, ...)) 55 { 56 vaddr_t frame, lastframe, lr, lastlr, sp; 57 char c, *cp = modif; 58 db_expr_t offset; 59 Elf_Sym * sym; 60 const char *name; 61 int kernel_only = 1; 62 int trace_thread = 0; 63 64 while ((c = *cp++) != 0) { 65 if (c == 'u') 66 kernel_only = 0; 67 if (c == 't') 68 trace_thread = 1; 69 } 70 71 if (!have_addr) { 72 sp = ddb_regs.tf_sp; 73 lr = ddb_regs.tf_lr; 74 lastlr = ddb_regs.tf_elr; 75 frame = ddb_regs.tf_x[29]; 76 } else { 77 if (trace_thread) { 78 struct proc *p = tfind((pid_t)addr); 79 if (p == NULL) { 80 (*pr)("not found\n"); 81 return; 82 } 83 frame = p->p_addr->u_pcb.pcb_tf->tf_x[29]; 84 sp = p->p_addr->u_pcb.pcb_tf->tf_sp; 85 lr = p->p_addr->u_pcb.pcb_tf->tf_lr; 86 lastlr = p->p_addr->u_pcb.pcb_tf->tf_elr; 87 } else { 88 sp = addr; 89 db_read_bytes(sp, sizeof(vaddr_t), 90 (char *)&frame); 91 db_read_bytes(sp + 8, sizeof(vaddr_t), 92 (char *)&lr); 93 lastlr = 0; 94 } 95 } 96 97 while (count-- && frame != 0) { 98 lastframe = frame; 99 100 if (INKERNEL(frame)) { 101 sym = db_search_symbol(lastlr, DB_STGY_ANY, &offset); 102 db_symbol_values(sym, &name, NULL); 103 } else { 104 sym = NULL; 105 name = NULL; 106 } 107 108 if (name == NULL || strcmp(name, "end") == 0) { 109 (*pr)("%llx at 0x%lx", lastlr, lr - 4); 110 } else { 111 (*pr)("%s() at ", name); 112 db_printsym(lr - 4, DB_STGY_PROC, pr); 113 } 114 (*pr)("\n"); 115 116 if (name != NULL) { 117 if ((strcmp (name, "handle_el0_irq") == 0) || 118 (strcmp (name, "handle_el1_irq") == 0)) { 119 (*pr)("--- interrupt ---\n"); 120 } else if ( 121 (strcmp (name, "handle_el0_sync") == 0) || 122 (strcmp (name, "handle_el1_sync") == 0)) { 123 (*pr)("--- trap ---\n"); 124 } 125 } 126 127 lastframe = frame; 128 db_read_bytes(frame, sizeof(vaddr_t), (char *)&frame); 129 130 if (frame == 0) { 131 /* end of chain */ 132 break; 133 } 134 135 if (INKERNEL(frame)) { 136 /* staying in kernel */ 137 if (frame <= lastframe) { 138 (*pr)("Bad frame pointer: 0x%lx\n", frame); 139 break; 140 } 141 } else if (INKERNEL(lastframe)) { 142 /* switch from user to kernel */ 143 if (kernel_only) { 144 (*pr)("end of kernel\n"); 145 break; /* kernel stack only */ 146 } 147 } else { 148 /* in user */ 149 if (frame <= lastframe) { 150 (*pr)("Bad user frame pointer: 0x%lx\n", 151 frame); 152 break; 153 } 154 } 155 156 lastlr = lr; 157 db_read_bytes(frame + 8, sizeof(vaddr_t), (char *)&lr); 158 159 --count; 160 } 161 } 162 163 void 164 stacktrace_save_at(struct stacktrace *st, unsigned int skip) 165 { 166 struct callframe *frame, *lastframe, *limit; 167 struct proc *p = curproc; 168 169 st->st_count = 0; 170 171 if (p == NULL) 172 return; 173 174 frame = __builtin_frame_address(0); 175 KASSERT(INKERNEL(frame)); 176 limit = (struct callframe *)STACKALIGN(p->p_addr + USPACE - 177 sizeof(struct trapframe) - 0x10); 178 179 while (st->st_count < STACKTRACE_MAX) { 180 if (skip == 0) 181 st->st_pc[st->st_count++] = frame->f_lr; 182 else 183 skip--; 184 185 lastframe = frame; 186 frame = frame->f_frame; 187 188 if (frame <= lastframe) 189 break; 190 if (frame >= limit) 191 break; 192 if (!INKERNEL(frame->f_lr)) 193 break; 194 } 195 } 196 197 void 198 stacktrace_save_utrace(struct stacktrace *st) 199 { 200 st->st_count = 0; 201 } 202