1 /* 2 * Copyright 2003-2009, Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Axel Dörfler <axeld@pinc-software.de> 7 * Ingo Weinhold <bonefish@cs.tu-berlin.de> 8 * François Revol <revol@free.fr> 9 */ 10 11 12 #include <arch/debug.h> 13 14 #include <arch_cpu.h> 15 #include <debug.h> 16 #include <elf.h> 17 #include <kernel.h> 18 #include <kimage.h> 19 #include <thread.h> 20 21 22 struct stack_frame { 23 struct stack_frame *previous; 24 addr_t return_address; 25 }; 26 27 #define NUM_PREVIOUS_LOCATIONS 32 28 29 extern struct iframe_stack gBootFrameStack; 30 31 32 static bool 33 already_visited(uint32 *visited, int32 *_last, int32 *_num, uint32 framePointer) 34 { 35 int32 last = *_last; 36 int32 num = *_num; 37 int32 i; 38 39 for (i = 0; i < num; i++) { 40 if (visited[(NUM_PREVIOUS_LOCATIONS + last - i) 41 % NUM_PREVIOUS_LOCATIONS] == framePointer) { 42 return true; 43 } 44 } 45 46 *_last = last = (last + 1) % NUM_PREVIOUS_LOCATIONS; 47 visited[last] = framePointer; 48 49 if (num < NUM_PREVIOUS_LOCATIONS) 50 *_num = num + 1; 51 52 return false; 53 } 54 55 56 static inline stack_frame * 57 get_current_stack_frame() 58 { 59 stack_frame *frame; 60 asm volatile("move.l %%fp,%0" : "=r"(frame)); 61 return frame; 62 } 63 64 65 static status_t 66 get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip) 67 { 68 stack_frame frame; 69 if (debug_memcpy(&frame, (void*)framePointer, sizeof(frame)) != B_OK) 70 return B_BAD_ADDRESS; 71 72 *ip = frame.return_address; 73 *next = (addr_t)frame.previous; 74 75 return B_OK; 76 } 77 78 79 static void 80 print_stack_frame(struct thread *thread, addr_t ip, addr_t framePointer, 81 addr_t nextFramePointer) 82 { 83 addr_t diff = nextFramePointer - framePointer; 84 85 // kernel space/user space switch 86 if (diff & 0x80000000) 87 diff = 0; 88 89 // lookup symbol 90 const char *symbol, *image; 91 addr_t baseAddress; 92 bool exactMatch; 93 status_t status = elf_debug_lookup_symbol_address(ip, &baseAddress, &symbol, 94 &image, &exactMatch); 95 if (status != B_OK && !IS_KERNEL_ADDRESS(ip) && thread) { 96 // try to locate the image in the images loaded into user space 97 status = image_debug_lookup_user_symbol_address(thread->team, ip, 98 &baseAddress, &symbol, &image, &exactMatch); 99 } 100 if (status == B_OK) { 101 if (symbol != NULL) { 102 kprintf("%08lx (+%4ld) %08lx <%s>:%s + 0x%04lx%s\n", framePointer, 103 diff, ip, image, symbol, ip - baseAddress, 104 (exactMatch ? "" : " (nearest)")); 105 } else { 106 kprintf("%08lx (+%4ld) %08lx <%s@%p>:unknown + 0x%04lx\n", 107 framePointer, diff, ip, image, (void *)baseAddress, 108 ip - baseAddress); 109 } 110 } else 111 kprintf("%08lx (+%4ld) %08lx\n", framePointer, diff, ip); 112 } 113 114 115 static int 116 stack_trace(int argc, char **argv) 117 { 118 uint32 previousLocations[NUM_PREVIOUS_LOCATIONS]; 119 struct iframe_stack *frameStack; 120 struct thread *thread; 121 addr_t framePointer; 122 int32 i, num = 0, last = 0; 123 124 if (argc < 2) { 125 thread = thread_get_current_thread(); 126 framePointer = (addr_t)get_current_stack_frame(); 127 } else { 128 // TODO: Add support for stack traces of other threads. 129 /* thread_id id = strtoul(argv[1], NULL, 0); 130 thread = thread_get_thread_struct_locked(id); 131 if (thread == NULL) { 132 kprintf("could not find thread %ld\n", id); 133 return 0; 134 } 135 136 // read %ebp from the thread's stack stored by a pushad 137 ebp = thread->arch_info.current_stack.esp[2]; 138 139 if (id != thread_get_current_thread_id()) { 140 // switch to the page directory of the new thread to be 141 // able to follow the stack trace into userland 142 addr_t newPageDirectory = (addr_t)x86_next_page_directory( 143 thread_get_current_thread(), thread); 144 145 if (newPageDirectory != 0) { 146 read_cr3(oldPageDirectory); 147 write_cr3(newPageDirectory); 148 } 149 } 150 */ 151 kprintf("Stack traces of other threads not supported yet!\n"); 152 return 0; 153 } 154 155 // We don't have a thread pointer early in the boot process 156 if (thread != NULL) 157 frameStack = &thread->arch_info.iframes; 158 else 159 frameStack = &gBootFrameStack; 160 161 for (i = 0; i < frameStack->index; i++) { 162 kprintf("iframe %p (end = %p)\n", 163 frameStack->frames[i], frameStack->frames[i] + 1); 164 } 165 166 if (thread != NULL) { 167 kprintf("stack trace for thread 0x%lx \"%s\"\n", thread->id, 168 thread->name); 169 170 kprintf(" kernel stack: %p to %p\n", 171 (void *)thread->kernel_stack_base, 172 (void *)(thread->kernel_stack_top)); 173 if (thread->user_stack_base != 0) { 174 kprintf(" user stack: %p to %p\n", 175 (void *)thread->user_stack_base, 176 (void *)(thread->user_stack_base + thread->user_stack_size)); 177 } 178 } 179 180 kprintf("frame caller <image>:function + offset\n"); 181 182 for (;;) { 183 // see if the frame pointer matches the iframe 184 struct iframe *frame = NULL; 185 for (i = 0; i < frameStack->index; i++) { 186 if (framePointer == (addr_t)frameStack->frames[i]) { 187 // it's an iframe 188 frame = frameStack->frames[i]; 189 break; 190 } 191 } 192 193 if (frame) { 194 kprintf("iframe at %p\n", frame); 195 kprintf(" d0 0x%08lx d1 0x%08lx d2 0x%08lx d3 0x%08lx\n", 196 frame->d[0], frame->d[1], frame->d[2], frame->d[3]); 197 kprintf(" d4 0x%08lx d5 0x%08lx d6 0x%08lx d7 0x%08lx\n", 198 frame->d[4], frame->d[5], frame->d[6], frame->d[7]); 199 kprintf(" a0 0x%08lx a1 0x%08lx a2 0x%08lx a3 0x%08lx\n", 200 frame->a[0], frame->a[1], frame->a[2], frame->a[3]); 201 kprintf(" a4 0x%08lx a5 0x%08lx a6 0x%08lx a7 0x%08lx (sp)\n", 202 #warning M68K: a7 in iframe ?? 203 frame->a[4], frame->a[5], frame->a[6], -1L/*frame->a[7]*/); 204 205 /*kprintf(" pc 0x%08lx ccr 0x%02x\n", 206 frame->pc, frame->ccr);*/ 207 kprintf(" pc 0x%08lx sr 0x%04x\n", 208 frame->cpu.pc, frame->cpu.sr); 209 #warning M68K: missing regs 210 211 print_stack_frame(thread, frame->cpu.pc, framePointer, frame->a[6]); 212 framePointer = frame->a[6]; 213 } else { 214 addr_t ip, nextFramePointer; 215 216 if (get_next_frame(framePointer, &nextFramePointer, &ip) != B_OK) { 217 kprintf("%08lx -- read fault\n", framePointer); 218 break; 219 } 220 221 if (ip == 0 || framePointer == 0) 222 break; 223 224 print_stack_frame(thread, ip, framePointer, nextFramePointer); 225 framePointer = nextFramePointer; 226 } 227 228 if (already_visited(previousLocations, &last, &num, framePointer)) { 229 kprintf("circular stack frame: %p!\n", (void *)framePointer); 230 break; 231 } 232 if (framePointer == 0) 233 break; 234 } 235 236 /* if (oldPageDirectory != 0) { 237 // switch back to the previous page directory to no cause any troubles 238 write_cr3(oldPageDirectory); 239 } 240 */ 241 242 return 0; 243 } 244 245 246 247 // #pragma mark - 248 249 250 void 251 arch_debug_save_registers(int *regs) 252 { 253 } 254 255 256 void 257 arch_debug_stack_trace(void) 258 { 259 } 260 261 262 bool 263 arch_debug_contains_call(struct thread *thread, const char *symbol, 264 addr_t start, addr_t end) 265 { 266 return false; 267 } 268 269 270 void * 271 arch_debug_get_caller(void) 272 { 273 // TODO: implement me 274 //return __builtin_frame_address(1); 275 struct stack_frame *frame; 276 //frame = __builtin_frame_address(0); 277 frame = get_current_stack_frame(); 278 return (void *)frame->previous->return_address; 279 } 280 281 282 int32 283 arch_debug_get_stack_trace(addr_t* returnAddresses, int32 maxCount, 284 int32 skipIframes, int32 skipFrames, uint32 flags) 285 { 286 struct iframe_stack *frameStack; 287 addr_t framePointer; 288 int32 count = 0; 289 int32 i, num = 0, last = 0; 290 291 // Keep skipping normal stack frames until we've skipped the iframes we're 292 // supposed to skip. 293 if (skipIframes > 0) 294 skipFrames = INT_MAX; 295 296 struct thread* thread = thread_get_current_thread(); 297 framePointer = (addr_t)get_current_stack_frame(); 298 bool onKernelStack = true; 299 300 // We don't have a thread pointer early in the boot process 301 if (thread != NULL) 302 frameStack = &thread->arch_info.iframes; 303 else 304 frameStack = &gBootFrameStack; 305 306 while (framePointer != 0 && count < maxCount) { 307 onKernelStack = onKernelStack && IS_KERNEL_ADDRESS(framePointer); 308 // TODO: Correctly determine whether this is a kernel address! 309 if (!onKernelStack && (flags & STACK_TRACE_USER) == 0) 310 break; 311 312 // see if the frame pointer matches the iframe 313 struct iframe *frame = NULL; 314 for (i = 0; i < frameStack->index; i++) { 315 if (framePointer == (addr_t)frameStack->frames[i]) { 316 // it's an iframe 317 frame = frameStack->frames[i]; 318 break; 319 } 320 } 321 322 addr_t ip; 323 addr_t nextFrame; 324 325 if (frame) { 326 ip = frame->cpu.pc; 327 nextFrame = frame->a[6]; 328 329 if (skipIframes > 0) { 330 if (--skipIframes == 0) 331 skipFrames = 0; 332 } 333 } else { 334 if (get_next_frame(framePointer, &nextFrame, &ip) != B_OK) 335 break; 336 } 337 338 if (skipFrames <= 0 339 && ((flags & STACK_TRACE_KERNEL) != 0 || onKernelStack)) { 340 returnAddresses[count++] = ip; 341 } else 342 skipFrames--; 343 344 framePointer = nextFrame; 345 } 346 347 return count; 348 } 349 350 351 void* 352 arch_debug_get_interrupt_pc(bool* _isSyscall) 353 { 354 // TODO: Implement! 355 return NULL; 356 } 357 358 359 void 360 arch_debug_unset_current_thread(void) 361 { 362 // TODO: Implement! 363 } 364 365 366 void 367 arch_debug_call_with_fault_handler(cpu_ent* cpu, jmp_buf jumpBuffer, 368 void (*function)(void*), void* parameter) 369 { 370 // TODO: Implement! Most likely in assembly. 371 longjmp(jumpBuffer, 1); 372 } 373 374 375 bool 376 arch_is_debug_variable_defined(const char* variableName) 377 { 378 // TODO: Implement! 379 return false; 380 } 381 382 383 status_t 384 arch_set_debug_variable(const char* variableName, uint64 value) 385 { 386 // TODO: Implement! 387 return B_ENTRY_NOT_FOUND; 388 } 389 390 391 status_t 392 arch_get_debug_variable(const char* variableName, uint64* value) 393 { 394 // TODO: Implement! 395 return B_ENTRY_NOT_FOUND; 396 } 397 398 399 status_t 400 arch_debug_init(kernel_args *args) 401 { 402 add_debugger_command("where", &stack_trace, "Same as \"sc\""); 403 add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)"); 404 add_debugger_command("sc", &stack_trace, "Stack crawl for current thread"); 405 406 return B_NO_ERROR; 407 } 408 409