xref: /haiku/src/system/kernel/arch/x86/arch_debug.cpp (revision 90ca02568835b140b0e59de496a7f1f1d3513f67)
1 /*
2  * Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  *
5  * Copyright 2001, Travis Geiselbrecht. All rights reserved.
6  * Distributed under the terms of the NewOS License.
7  */
8 
9 
10 #include <arch/debug.h>
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 
15 #include <debug.h>
16 #include <elf.h>
17 #include <kernel.h>
18 #include <kimage.h>
19 #include <thread.h>
20 #include <vm.h>
21 #include <vm_types.h>
22 
23 #include <arch_cpu.h>
24 
25 
26 struct stack_frame {
27 	struct stack_frame	*previous;
28 	addr_t				return_address;
29 };
30 
31 #define NUM_PREVIOUS_LOCATIONS 32
32 
33 
34 static bool
35 already_visited(uint32 *visited, int32 *_last, int32 *_num, uint32 ebp)
36 {
37 	int32 last = *_last;
38 	int32 num = *_num;
39 	int32 i;
40 
41 	for (i = 0; i < num; i++) {
42 		if (visited[(NUM_PREVIOUS_LOCATIONS + last - i) % NUM_PREVIOUS_LOCATIONS] == ebp)
43 			return true;
44 	}
45 
46 	*_last = last = (last + 1) % NUM_PREVIOUS_LOCATIONS;
47 	visited[last] = ebp;
48 
49 	if (num < NUM_PREVIOUS_LOCATIONS)
50 		*_num = num + 1;
51 
52 	return false;
53 }
54 
55 
56 static status_t
57 get_next_frame(addr_t ebp, addr_t *_next, addr_t *_eip)
58 {
59 	// set fault handler, so that we can safely access user stacks
60 	addr_t oldFaultHandler = thread_get_current_thread()->fault_handler;
61 	thread_get_current_thread()->fault_handler = (addr_t)&&error;
62 	// Fake goto to trick the compiler not to optimize the code at the label
63 	// away.
64 	if (ebp == 0)
65 		goto error;
66 
67 	*_eip = ((struct stack_frame *)ebp)->return_address;
68 	*_next = (addr_t)((struct stack_frame *)ebp)->previous;
69 
70 	thread_get_current_thread()->fault_handler = oldFaultHandler;
71 	return B_OK;
72 
73 error:
74 	thread_get_current_thread()->fault_handler = oldFaultHandler;
75 	return B_BAD_ADDRESS;
76 }
77 
78 
79 static status_t
80 lookup_symbol(struct thread* thread, addr_t address, addr_t *_baseAddress,
81 	const char **_symbolName, const char **_imageName, bool *_exactMatch)
82 {
83 	status_t status = B_ENTRY_NOT_FOUND;
84 
85 	if (IS_KERNEL_ADDRESS(address)) {
86 		// a kernel symbol
87 		status = elf_debug_lookup_symbol_address(address, _baseAddress,
88 			_symbolName, _imageName, _exactMatch);
89 	} else if (thread != NULL && thread->team != NULL) {
90 		// try a lookup using the userland runtime loader structures
91 		status = elf_debug_lookup_user_symbol_address(thread->team, address,
92 			_baseAddress, _symbolName, _imageName, _exactMatch);
93 
94 		if (status != B_OK) {
95 			// try to locate the image in the images loaded into user space
96 			status = image_debug_lookup_user_symbol_address(thread->team,
97 				address, _baseAddress, _symbolName, _imageName, _exactMatch);
98 		}
99 	}
100 
101 	return status;
102 }
103 
104 
105 static void
106 print_stack_frame(struct thread *thread, addr_t eip, addr_t ebp, addr_t nextEbp)
107 {
108 	const char *symbol, *image;
109 	addr_t baseAddress;
110 	bool exactMatch;
111 	status_t status;
112 	addr_t diff;
113 
114 	diff = nextEbp - ebp;
115 
116 	// kernel space/user space switch
117 	if (diff & 0x80000000)
118 		diff = 0;
119 
120 	status = lookup_symbol(thread, eip, &baseAddress, &symbol, &image,
121 		&exactMatch);
122 
123 	kprintf("%08lx (+%4ld) %08lx", ebp, diff, eip);
124 
125 	if (status == B_OK) {
126 		if (symbol != NULL) {
127 			kprintf("   <%s>:%s + 0x%04lx%s\n", image, symbol,
128 				eip - baseAddress, exactMatch ? "" : " (nearest)");
129 		} else {
130 			kprintf("   <%s@%p>:unknown + 0x%04lx\n", image,
131 				(void *)baseAddress, eip - baseAddress);
132 		}
133 	} else {
134 		vm_area *area = NULL;
135 		if (thread->team->address_space != NULL)
136 			area = vm_area_lookup(thread->team->address_space, eip);
137 		if (area != NULL) {
138 			kprintf("   %ld:%s@%p + %#lx\n", area->id, area->name, (void *)area->base,
139 				eip - area->base);
140 		} else
141 			kprintf("\n");
142 	}
143 }
144 
145 
146 static void
147 print_iframe(struct iframe *frame)
148 {
149 	kprintf("iframe at %p (end = %p)\n", frame, frame + 1);
150 
151 	kprintf(" eax 0x%-9lx    ebx 0x%-9lx     ecx 0x%-9lx  edx 0x%lx\n",
152 		frame->eax, frame->ebx, frame->ecx, frame->edx);
153 	kprintf(" esi 0x%-9lx    edi 0x%-9lx     ebp 0x%-9lx  esp 0x%lx\n",
154 		frame->esi, frame->edi, frame->ebp, frame->esp);
155 	kprintf(" eip 0x%-9lx eflags 0x%-9lx", frame->eip, frame->flags);
156 	if ((frame->error_code & 0x4) != 0) {
157 		// from user space
158 		kprintf("user esp 0x%lx", frame->user_esp);
159 	}
160 	kprintf("\n");
161 	kprintf(" vector: 0x%lx, error code: 0x%lx\n", frame->vector,
162 		frame->error_code);
163 }
164 
165 
166 static void
167 setup_for_thread(char *arg, struct thread **_thread, uint32 *_ebp,
168 	uint32 *_oldPageDirectory)
169 {
170 	struct thread *thread = NULL;
171 
172 	if (arg != NULL) {
173 		thread_id id = strtoul(arg, NULL, 0);
174 		thread = thread_get_thread_struct_locked(id);
175 		if (thread == NULL) {
176 			kprintf("could not find thread %ld\n", id);
177 			return;
178 		}
179 
180 		if (id != thread_get_current_thread_id()) {
181 			// switch to the page directory of the new thread to be
182 			// able to follow the stack trace into userland
183 			addr_t newPageDirectory = (addr_t)x86_next_page_directory(
184 				thread_get_current_thread(), thread);
185 
186 			if (newPageDirectory != 0) {
187 				read_cr3(*_oldPageDirectory);
188 				write_cr3(newPageDirectory);
189 			}
190 
191 			// read %ebp from the thread's stack stored by a pushad
192 			*_ebp = thread->arch_info.current_stack.esp[2];
193 		} else
194 			thread = NULL;
195 	}
196 
197 	if (thread == NULL) {
198 		// if we don't have a thread yet, we want the current one
199 		// (ebp has been set by the caller for this case already)
200 		thread = thread_get_current_thread();
201 	}
202 
203 	*_thread = thread;
204 }
205 
206 
207 static bool
208 is_kernel_stack_address(struct thread* thread, addr_t address)
209 {
210 	// We don't have a thread pointer in the early boot process, but then we are
211 	// on the kernel stack for sure.
212 	if (thread == NULL)
213 		return IS_KERNEL_ADDRESS(address);
214 
215 	return address >= thread->kernel_stack_base
216 		&& address < thread->kernel_stack_top;
217 }
218 
219 
220 static bool
221 is_iframe(struct thread* thread, addr_t frame)
222 {
223 	if (!is_kernel_stack_address(thread, frame))
224 		return false;
225 
226 	addr_t previousFrame = *(addr_t*)frame;
227 	return ((previousFrame & ~IFRAME_TYPE_MASK) == 0 && previousFrame != 0);
228 }
229 
230 
231 static struct iframe *
232 find_previous_iframe(struct thread *thread, addr_t frame)
233 {
234 	// iterate backwards through the stack frames, until we hit an iframe
235 	while (is_kernel_stack_address(thread, frame)) {
236 		if (is_iframe(thread, frame))
237 			return (struct iframe*)frame;
238 
239 		frame = *(addr_t*)frame;
240 	}
241 
242 	return NULL;
243 }
244 
245 
246 static struct iframe*
247 get_previous_iframe(struct thread* thread, struct iframe* frame)
248 {
249 	if (frame == NULL)
250 		return NULL;
251 
252 	return find_previous_iframe(thread, frame->ebp);
253 }
254 
255 
256 static int
257 stack_trace(int argc, char **argv)
258 {
259 	static const char* usage = "usage: %s [ <thread id> ]\n"
260 		"Prints a stack trace for the current, respectively the specified\n"
261 		"thread.\n"
262 		"  <thread id>  -  The ID of the thread for which to print the stack\n"
263 		"                  trace.\n";
264 	if (argc > 2 || argc == 2 && strcmp(argv[1], "--help") == 0) {
265 		kprintf(usage, argv[0]);
266 		return 0;
267 	}
268 
269 	uint32 previousLocations[NUM_PREVIOUS_LOCATIONS];
270 	struct thread *thread = NULL;
271 	addr_t oldPageDirectory = 0;
272 	uint32 ebp = x86_read_ebp();
273 	int32 i, num = 0, last = 0;
274 
275 	setup_for_thread(argc == 2 ? argv[1] : NULL, &thread, &ebp,
276 		&oldPageDirectory);
277 
278 	if (thread != NULL) {
279 		kprintf("stack trace for thread %ld \"%s\"\n", thread->id,
280 			thread->name);
281 
282 		kprintf("    kernel stack: %p to %p\n",
283 			(void *)thread->kernel_stack_base,
284 			(void *)(thread->kernel_stack_top));
285 		if (thread->user_stack_base != 0) {
286 			kprintf("      user stack: %p to %p\n",
287 				(void *)thread->user_stack_base,
288 				(void *)(thread->user_stack_base + thread->user_stack_size));
289 		}
290 	}
291 
292 	kprintf("frame            caller     <image>:function + offset\n");
293 
294 	bool onKernelStack = true;
295 
296 	for (;;) {
297 		onKernelStack = onKernelStack
298 			&& is_kernel_stack_address(thread, ebp);
299 
300 		if (onKernelStack && is_iframe(thread, ebp)) {
301 			struct iframe *frame = (struct iframe *)ebp;
302 
303 			print_iframe(frame);
304 			print_stack_frame(thread, frame->eip, ebp, frame->ebp);
305 
306  			ebp = frame->ebp;
307 		} else {
308 			addr_t eip, nextEbp;
309 
310 			if (get_next_frame(ebp, &nextEbp, &eip) != B_OK) {
311 				kprintf("%08lx -- read fault\n", ebp);
312 				break;
313 			}
314 
315 			if (eip == 0 || ebp == 0)
316 				break;
317 
318 			print_stack_frame(thread, eip, ebp, nextEbp);
319 			ebp = nextEbp;
320 		}
321 
322 		if (already_visited(previousLocations, &last, &num, ebp)) {
323 			kprintf("circular stack frame: %p!\n", (void *)ebp);
324 			break;
325 		}
326 		if (ebp == 0)
327 			break;
328 	}
329 
330 	if (oldPageDirectory != 0) {
331 		// switch back to the previous page directory to no cause any troubles
332 		write_cr3(oldPageDirectory);
333 	}
334 
335 	return 0;
336 }
337 
338 
339 static void
340 print_call(struct thread *thread, addr_t eip, addr_t ebp, addr_t nextEbp,
341 	int32 argCount)
342 {
343 	const char *symbol, *image;
344 	addr_t baseAddress;
345 	bool exactMatch;
346 	status_t status;
347 
348 	status = lookup_symbol(thread, eip, &baseAddress, &symbol, &image,
349 		&exactMatch);
350 
351 	kprintf("%08lx %08lx", ebp, eip);
352 
353 	if (status == B_OK) {
354 		if (symbol != NULL) {
355 			kprintf("   <%s>:%s%s", image, symbol,
356 				exactMatch ? "" : " (nearest)");
357 		} else {
358 			kprintf("   <%s@%p>:unknown + 0x%04lx", image,
359 				(void *)baseAddress, eip - baseAddress);
360 		}
361 	} else {
362 		vm_area *area = NULL;
363 		if (thread->team->address_space != NULL)
364 			area = vm_area_lookup(thread->team->address_space, eip);
365 		if (area != NULL) {
366 			kprintf("   %ld:%s@%p + %#lx", area->id, area->name,
367 				(void *)area->base, eip - area->base);
368 		}
369 	}
370 
371 	int32 *arg = (int32 *)(nextEbp + 8);
372 	kprintf("(");
373 
374 	for (int32 i = 0; i < argCount; i++) {
375 		if (i > 0)
376 			kprintf(", ");
377 		kprintf("%#lx", *arg);
378 		if (*arg > -0x10000 && *arg < 0x10000)
379 			kprintf(" (%ld)", *arg);
380 
381 		char name[8];
382 		snprintf(name, sizeof(name), "_arg%ld", i + 1);
383 		set_debug_variable(name, *(uint32 *)arg);
384 
385 		arg++;
386 	}
387 
388 	kprintf(")\n");
389 
390 	set_debug_variable("_frame", nextEbp);
391 }
392 
393 
394 static int
395 show_call(int argc, char **argv)
396 {
397 	static const char* usage
398 		= "usage: %s [ <thread id> ] <call index> [ -<arg count> ]\n"
399 		"Prints a function call with parameters of the current, respectively\n"
400 		"the specified thread.\n"
401 		"  <thread id>   -  The ID of the thread for which to print the call.\n"
402 		"  <call index>  -  The index of the call in the stack trace.\n"
403 		"  <arg count>   -  The number of call arguments to print.\n";
404 	if (argc == 2 && strcmp(argv[1], "--help") == 0) {
405 		kprintf(usage, argv[0]);
406 		return 0;
407 	}
408 
409 	struct thread *thread = NULL;
410 	addr_t oldPageDirectory = 0;
411 	addr_t ebp = x86_read_ebp();
412 	int32 argCount = 0;
413 
414 	if (argc >= 2 && argv[argc - 1][0] == '-') {
415 		argCount = strtoul(argv[argc - 1] + 1, NULL, 0);
416 		if (argCount < 0 || argCount > 16) {
417 			kprintf("Invalid argument count \"%ld\".\n", argCount);
418 			return 0;
419 		}
420 		argc--;
421 	}
422 
423 	if (argc < 2 || argc > 3) {
424 		kprintf(usage, argv[0]);
425 		return 0;
426 	}
427 
428 	setup_for_thread(argc == 3 ? argv[1] : NULL, &thread, &ebp,
429 		&oldPageDirectory);
430 
431 	int32 callIndex = strtoul(argv[argc == 3 ? 2 : 1], NULL, 0);
432 
433 	if (thread != NULL)
434 		kprintf("thread %ld, %s\n", thread->id, thread->name);
435 
436 	bool onKernelStack = true;
437 
438 	for (int32 index = 0; index <= callIndex; index++) {
439 		onKernelStack = onKernelStack
440 			&& is_kernel_stack_address(thread, ebp);
441 
442 		if (onKernelStack && is_iframe(thread, ebp)) {
443 			struct iframe *frame = (struct iframe *)ebp;
444 
445 			if (index == callIndex)
446 				print_call(thread, frame->eip, ebp, frame->ebp, argCount);
447 
448  			ebp = frame->ebp;
449 		} else {
450 			addr_t eip, nextEbp;
451 
452 			if (get_next_frame(ebp, &nextEbp, &eip) != B_OK) {
453 				kprintf("%08lx -- read fault\n", ebp);
454 				break;
455 			}
456 
457 			if (eip == 0 || ebp == 0)
458 				break;
459 
460 			if (index == callIndex)
461 				print_call(thread, eip, ebp, nextEbp, argCount);
462 
463 			ebp = nextEbp;
464 		}
465 
466 		if (ebp == 0)
467 			break;
468 	}
469 
470 	if (oldPageDirectory != 0) {
471 		// switch back to the previous page directory to not cause any troubles
472 		write_cr3(oldPageDirectory);
473 	}
474 
475 	return 0;
476 }
477 
478 
479 static int
480 dump_iframes(int argc, char **argv)
481 {
482 	static const char* usage = "usage: %s [ <thread id> ]\n"
483 		"Prints the iframe stack for the current, respectively the specified\n"
484 		"thread.\n"
485 		"  <thread id>  -  The ID of the thread for which to print the iframe\n"
486 		"                  stack.\n";
487 	if (argc == 2 && strcmp(argv[1], "--help") == 0) {
488 		kprintf(usage, argv[0]);
489 		return 0;
490 	}
491 
492 	struct thread *thread = NULL;
493 	int32 i;
494 
495 	if (argc < 2) {
496 		thread = thread_get_current_thread();
497 	} else if (argc == 2) {
498 		thread_id id = strtoul(argv[1], NULL, 0);
499 		thread = thread_get_thread_struct_locked(id);
500 		if (thread == NULL) {
501 			kprintf("could not find thread %ld\n", id);
502 			return 0;
503 		}
504 	} else if (argc > 2) {
505 		kprintf(usage, argv[0]);
506 		return 0;
507 	}
508 
509 	if (thread != NULL)
510 		kprintf("iframes for thread %ld \"%s\"\n", thread->id, thread->name);
511 
512 	struct iframe* frame = find_previous_iframe(thread, x86_read_ebp());
513 	while (frame != NULL) {
514 		print_iframe(frame);
515 		frame = get_previous_iframe(thread, frame);
516 	}
517 
518 	return 0;
519 }
520 
521 
522 static bool
523 is_calling(struct thread *thread, addr_t eip, const char *pattern,
524 	addr_t start, addr_t end)
525 {
526 	if (pattern == NULL)
527 		return eip >= start && eip < end;
528 
529 	const char *symbol;
530 	if (lookup_symbol(thread, eip, NULL, &symbol, NULL, NULL) != B_OK)
531 		return false;
532 
533 	return strstr(symbol, pattern);
534 }
535 
536 
537 //	#pragma mark -
538 
539 
540 bool
541 arch_debug_contains_call(struct thread *thread, const char *symbol,
542 	addr_t start, addr_t end)
543 {
544 	addr_t ebp;
545 	if (thread == thread_get_current_thread())
546 		ebp = x86_read_ebp();
547 	else
548 		ebp = thread->arch_info.current_stack.esp[2];
549 
550 	for (;;) {
551 		if (!is_kernel_stack_address(thread, ebp))
552 			break;
553 
554 		if (is_iframe(thread, ebp)) {
555 			struct iframe *frame = (struct iframe *)ebp;
556 
557 			if (is_calling(thread, frame->eip, symbol, start, end))
558 				return true;
559 
560  			ebp = frame->ebp;
561 		} else {
562 			addr_t eip, nextEbp;
563 
564 			if (get_next_frame(ebp, &nextEbp, &eip) != B_OK
565 				|| eip == 0 || ebp == 0)
566 				break;
567 
568 			if (is_calling(thread, eip, symbol, start, end))
569 				return true;
570 
571 			ebp = nextEbp;
572 		}
573 
574 		if (ebp == 0)
575 			break;
576 	}
577 
578 	return false;
579 }
580 
581 
582 void *
583 arch_debug_get_caller(void)
584 {
585 	struct stack_frame *frame = (struct stack_frame *)x86_read_ebp();
586 	return (void *)frame->previous->return_address;
587 }
588 
589 
590 int32
591 arch_debug_get_stack_trace(addr_t* returnAddresses, int32 maxCount,
592 	int32 skipFrames, bool userOnly)
593 {
594 	// always skip our own frame
595 	skipFrames++;
596 
597 	struct thread* thread = thread_get_current_thread();
598 	int32 count = 0;
599 	addr_t ebp = x86_read_ebp();
600 	bool onKernelStack = true;
601 
602 	while (ebp != 0 && count < maxCount) {
603 		onKernelStack = onKernelStack
604 			&& is_kernel_stack_address(thread, ebp);
605 
606 		addr_t eip;
607 		addr_t nextEbp;
608 
609 		if (onKernelStack && is_iframe(thread, ebp)) {
610 			struct iframe *frame = (struct iframe*)ebp;
611 			eip = frame->eip;
612  			nextEbp = frame->ebp;
613 		} else {
614 			if (get_next_frame(ebp, &nextEbp, &eip) != B_OK)
615 				break;
616 		}
617 
618 		if (skipFrames <= 0 && (!userOnly || IS_USER_ADDRESS(ebp)))
619 			returnAddresses[count++] = eip;
620 		else
621 			skipFrames--;
622 
623 		ebp = nextEbp;
624 	}
625 
626 	return count;
627 }
628 
629 
630 status_t
631 arch_debug_init(kernel_args *args)
632 {
633 	// at this stage, the debugger command system is alive
634 
635 	add_debugger_command("where", &stack_trace, "Same as \"sc\"");
636 	add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)");
637 	add_debugger_command("sc", &stack_trace,
638 		"Stack crawl for current thread (or any other)");
639 	add_debugger_command("iframe", &dump_iframes,
640 		"Dump iframes for the specified thread");
641 	add_debugger_command("call", &show_call, "Show call with arguments");
642 
643 	return B_NO_ERROR;
644 }
645 
646