1/*
2 * Copyright 2003-2011, 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 */
9
10
11#include <arch/debug.h>
12
13#include <arch_cpu.h>
14#include <debug.h>
15#include <elf.h>
16#include <kernel.h>
17#include <kimage.h>
18#include <thread.h>
19
20
21struct stack_frame {
22	struct stack_frame	*previous;
23	addr_t				return_address;
24};
25
26#define NUM_PREVIOUS_LOCATIONS 32
27
28extern struct iframe_stack gBootFrameStack;
29
30
31static bool
32already_visited(uint32 *visited, int32 *_last, int32 *_num, uint32 framePointer)
33{
34	int32 last = *_last;
35	int32 num = *_num;
36	int32 i;
37
38	for (i = 0; i < num; i++) {
39		if (visited[(NUM_PREVIOUS_LOCATIONS + last - i)
40				% NUM_PREVIOUS_LOCATIONS] == framePointer) {
41			return true;
42		}
43	}
44
45	*_last = last = (last + 1) % NUM_PREVIOUS_LOCATIONS;
46	visited[last] = framePointer;
47
48	if (num < NUM_PREVIOUS_LOCATIONS)
49		*_num = num + 1;
50
51	return false;
52}
53
54
55static inline stack_frame *
56get_current_stack_frame()
57{
58	stack_frame *frame;
59	asm volatile("mr %0, %%r1" : "=r"(frame));
60	return frame;
61}
62
63
64static status_t
65get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
66{
67	stack_frame frame;
68	if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)framePointer, sizeof(frame))
69			!= B_OK) {
70		return B_BAD_ADDRESS;
71	}
72
73	*ip = frame.return_address;
74	*next = (addr_t)frame.previous;
75
76	return B_OK;
77}
78
79
80static void
81print_stack_frame(Thread *thread, addr_t ip, addr_t framePointer,
82	addr_t nextFramePointer)
83{
84	addr_t diff = nextFramePointer - framePointer;
85
86	// kernel space/user space switch
87	if (diff & 0x80000000)
88		diff = 0;
89
90	// lookup symbol
91	const char *symbol, *image;
92	addr_t baseAddress;
93	bool exactMatch;
94	status_t status = elf_debug_lookup_symbol_address(ip, &baseAddress, &symbol,
95		&image, &exactMatch);
96	if (status != B_OK && !IS_KERNEL_ADDRESS(ip) && thread) {
97		// try to locate the image in the images loaded into user space
98		status = image_debug_lookup_user_symbol_address(thread->team, ip,
99			&baseAddress, &symbol, &image, &exactMatch);
100	}
101	if (status == B_OK) {
102		if (symbol != NULL) {
103			kprintf("%08lx (+%4ld) %08lx   <%s>:%s + 0x%04lx%s\n", framePointer,
104				diff, ip, image, symbol, ip - baseAddress,
105				(exactMatch ? "" : " (nearest)"));
106		} else {
107			kprintf("%08lx (+%4ld) %08lx   <%s@%p>:unknown + 0x%04lx\n",
108				framePointer, diff, ip, image, (void *)baseAddress,
109				ip - baseAddress);
110		}
111	} else
112		kprintf("%08lx (+%4ld) %08lx\n", framePointer, diff, ip);
113}
114
115
116static int
117stack_trace(int argc, char **argv)
118{
119	uint32 previousLocations[NUM_PREVIOUS_LOCATIONS];
120	struct iframe_stack *frameStack;
121	Thread *thread;
122	addr_t framePointer;
123	int32 i, num = 0, last = 0;
124
125	if (argc < 2) {
126		thread = thread_get_current_thread();
127		int32 cpu = smp_get_current_cpu();
128		framePointer = debug_get_debug_registers(cpu)->r1;
129	} else {
130// TODO: Add support for stack traces of other threads.
131/*		thread_id id = strtoul(argv[1], NULL, 0);
132		thread = Thread::GetDebug(id);
133		if (thread == NULL) {
134			kprintf("could not find thread %ld\n", id);
135			return 0;
136		}
137
138		// read %ebp from the thread's stack stored by a pushad
139		ebp = thread->arch_info.current_stack.esp[2];
140
141		if (id != thread_get_current_thread_id()) {
142			// switch to the page directory of the new thread to be
143			// able to follow the stack trace into userland
144			addr_t newPageDirectory = (addr_t)x86_next_page_directory(
145				thread_get_current_thread(), thread);
146
147			if (newPageDirectory != 0) {
148				read_cr3(oldPageDirectory);
149				write_cr3(newPageDirectory);
150			}
151		}
152*/
153kprintf("Stack traces of other threads not supported yet!\n");
154return 0;
155	}
156
157	// We don't have a thread pointer early in the boot process
158	if (thread != NULL)
159		frameStack = &thread->arch_info.iframes;
160	else
161		frameStack = &gBootFrameStack;
162
163	for (i = 0; i < frameStack->index; i++) {
164		kprintf("iframe %p (end = %p)\n",
165			frameStack->frames[i], frameStack->frames[i] + 1);
166	}
167
168	if (thread != NULL) {
169		kprintf("stack trace for thread 0x%lx \"%s\"\n", thread->id,
170			thread->name);
171
172		kprintf("    kernel stack: %p to %p\n",
173			(void *)thread->kernel_stack_base,
174			(void *)(thread->kernel_stack_top));
175		if (thread->user_stack_base != 0) {
176			kprintf("      user stack: %p to %p\n",
177				(void *)thread->user_stack_base,
178				(void *)(thread->user_stack_base + thread->user_stack_size));
179		}
180	}
181
182	kprintf("frame            caller     <image>:function + offset\n");
183
184	for (;;) {
185		// see if the frame pointer matches the iframe
186		struct iframe *frame = NULL;
187		for (i = 0; i < frameStack->index; i++) {
188			if (framePointer == (((addr_t)frameStack->frames[i] - 8) & ~0xf)) {
189				// it's an iframe
190				frame = frameStack->frames[i];
191				break;
192			}
193		}
194
195		if (frame) {
196			kprintf("iframe at %p\n", frame);
197			kprintf("   r0 0x%08lx    r1 0x%08lx    r2 0x%08lx    r3 0x%08lx\n",
198				frame->r0, frame->r1, frame->r2, frame->r3);
199			kprintf("   r4 0x%08lx    r5 0x%08lx    r6 0x%08lx    r7 0x%08lx\n",
200				frame->r4, frame->r5, frame->r6, frame->r7);
201			kprintf("   r8 0x%08lx    r9 0x%08lx   r10 0x%08lx   r11 0x%08lx\n",
202				frame->r8, frame->r9, frame->r10, frame->r11);
203			kprintf("  r12 0x%08lx   r13 0x%08lx   r14 0x%08lx   r15 0x%08lx\n",
204				frame->r12, frame->r13, frame->r14, frame->r15);
205			kprintf("  r16 0x%08lx   r17 0x%08lx   r18 0x%08lx   r19 0x%08lx\n",
206				frame->r16, frame->r17, frame->r18, frame->r19);
207			kprintf("  r20 0x%08lx   r21 0x%08lx   r22 0x%08lx   r23 0x%08lx\n",
208				frame->r20, frame->r21, frame->r22, frame->r23);
209			kprintf("  r24 0x%08lx   r25 0x%08lx   r26 0x%08lx   r27 0x%08lx\n",
210				frame->r24, frame->r25, frame->r26, frame->r27);
211			kprintf("  r28 0x%08lx   r29 0x%08lx   r30 0x%08lx   r31 0x%08lx\n",
212				frame->r28, frame->r29, frame->r30, frame->r31);
213			kprintf("   lr 0x%08lx    cr 0x%08lx   xer 0x%08lx   ctr 0x%08lx\n",
214				frame->lr, frame->cr, frame->xer, frame->ctr);
215			kprintf("fpscr 0x%08lx\n", frame->fpscr);
216			kprintf(" srr0 0x%08lx  srr1 0x%08lx   dar 0x%08lx dsisr 0x%08lx\n",
217				frame->srr0, frame->srr1, frame->dar, frame->dsisr);
218			kprintf(" vector: 0x%lx\n", frame->vector);
219
220			print_stack_frame(thread, frame->srr0, framePointer, frame->r1);
221 			framePointer = frame->r1;
222		} else {
223			addr_t ip, nextFramePointer;
224
225			if (get_next_frame(framePointer, &nextFramePointer, &ip) != B_OK) {
226				kprintf("%08lx -- read fault\n", framePointer);
227				break;
228			}
229
230			if (ip == 0 || framePointer == 0)
231				break;
232
233			print_stack_frame(thread, ip, framePointer, nextFramePointer);
234			framePointer = nextFramePointer;
235		}
236
237		if (already_visited(previousLocations, &last, &num, framePointer)) {
238			kprintf("circular stack frame: %p!\n", (void *)framePointer);
239			break;
240		}
241		if (framePointer == 0)
242			break;
243	}
244
245/*	if (oldPageDirectory != 0) {
246		// switch back to the previous page directory to no cause any troubles
247		write_cr3(oldPageDirectory);
248	}
249*/
250
251	return 0;
252}
253
254
255
256// #pragma mark -
257
258
259void
260arch_debug_save_registers(struct arch_debug_registers* registers)
261{
262	// get the caller's frame pointer
263	stack_frame* frame = (stack_frame*)get_current_stack_frame();
264	registers->r1 = (addr_t)frame->previous;
265}
266
267
268void
269arch_debug_stack_trace(void)
270{
271	stack_trace(0, NULL);
272}
273
274
275bool
276arch_debug_contains_call(Thread *thread, const char *symbol,
277	addr_t start, addr_t end)
278{
279	return false;
280}
281
282
283void *
284arch_debug_get_caller(void)
285{
286	struct stack_frame *frame = get_current_stack_frame()->previous;
287	return (void *)frame->previous->return_address;
288}
289
290
291int32
292arch_debug_get_stack_trace(addr_t* returnAddresses, int32 maxCount,
293	int32 skipIframes, int32 skipFrames, uint32 flags)
294{
295	// TODO: Implement!
296	return 0;
297}
298
299
300void*
301arch_debug_get_interrupt_pc(bool* _isSyscall)
302{
303	// TODO: Implement!
304	return NULL;
305}
306
307
308void
309arch_debug_unset_current_thread(void)
310{
311	// TODO: Implement!
312}
313
314
315bool
316arch_is_debug_variable_defined(const char* variableName)
317{
318	// TODO: Implement!
319	return false;
320}
321
322
323status_t
324arch_set_debug_variable(const char* variableName, uint64 value)
325{
326	// TODO: Implement!
327	return B_ENTRY_NOT_FOUND;
328}
329
330
331status_t
332arch_get_debug_variable(const char* variableName, uint64* value)
333{
334	// TODO: Implement!
335	return B_ENTRY_NOT_FOUND;
336}
337
338
339ssize_t
340arch_debug_gdb_get_registers(char* buffer, size_t bufferSize)
341{
342	// TODO: Implement!
343	return B_NOT_SUPPORTED;
344}
345
346
347status_t
348arch_debug_init(kernel_args *args)
349{
350	add_debugger_command("where", &stack_trace, "Same as \"sc\"");
351	add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)");
352	add_debugger_command("sc", &stack_trace, "Stack crawl for current thread");
353
354	return B_NO_ERROR;
355}
356
357