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 * 		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
22struct stack_frame {
23	struct stack_frame	*previous;
24	addr_t				return_address;
25};
26
27#define NUM_PREVIOUS_LOCATIONS 32
28
29extern struct iframe_stack gBootFrameStack;
30
31
32static bool
33already_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
56static inline stack_frame *
57get_current_stack_frame()
58{
59	stack_frame *frame;
60	asm volatile("move.l %%fp,%0" : "=r"(frame));
61	return frame;
62}
63
64
65static status_t
66get_next_frame(addr_t framePointer, addr_t *next, addr_t *ip)
67{
68	stack_frame frame;
69	if (debug_memcpy(B_CURRENT_TEAM, &frame, (void*)framePointer, sizeof(frame))
70			!= B_OK) {
71		return B_BAD_ADDRESS;
72	}
73
74	*ip = frame.return_address;
75	*next = (addr_t)frame.previous;
76
77	return B_OK;
78}
79
80
81static void
82print_stack_frame(Thread *thread, addr_t ip, addr_t framePointer,
83	addr_t nextFramePointer)
84{
85	addr_t diff = nextFramePointer - framePointer;
86
87	// kernel space/user space switch
88	if (diff & 0x80000000)
89		diff = 0;
90
91	// lookup symbol
92	const char *symbol, *image;
93	addr_t baseAddress;
94	bool exactMatch;
95	status_t status = elf_debug_lookup_symbol_address(ip, &baseAddress, &symbol,
96		&image, &exactMatch);
97	if (status != B_OK && !IS_KERNEL_ADDRESS(ip) && thread) {
98		// try to locate the image in the images loaded into user space
99		status = image_debug_lookup_user_symbol_address(thread->team, ip,
100			&baseAddress, &symbol, &image, &exactMatch);
101	}
102	if (status == B_OK) {
103		if (symbol != NULL) {
104			kprintf("%08lx (+%4ld) %08lx   <%s>:%s + 0x%04lx%s\n", framePointer,
105				diff, ip, image, symbol, ip - baseAddress,
106				(exactMatch ? "" : " (nearest)"));
107		} else {
108			kprintf("%08lx (+%4ld) %08lx   <%s@%p>:unknown + 0x%04lx\n",
109				framePointer, diff, ip, image, (void *)baseAddress,
110				ip - baseAddress);
111		}
112	} else
113		kprintf("%08lx (+%4ld) %08lx\n", framePointer, diff, ip);
114}
115
116
117static int
118stack_trace(int argc, char **argv)
119{
120	uint32 previousLocations[NUM_PREVIOUS_LOCATIONS];
121	struct iframe_stack *frameStack;
122	Thread *thread;
123	addr_t framePointer;
124	int32 i, num = 0, last = 0;
125
126	if (argc < 2) {
127		thread = thread_get_current_thread();
128		framePointer = (addr_t)get_current_stack_frame();
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]) {
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("   d0 0x%08lx    d1 0x%08lx    d2 0x%08lx    d3 0x%08lx\n",
198				frame->d[0], frame->d[1], frame->d[2], frame->d[3]);
199			kprintf("   d4 0x%08lx    d5 0x%08lx    d6 0x%08lx    d7 0x%08lx\n",
200				frame->d[4], frame->d[5], frame->d[6], frame->d[7]);
201			kprintf("   a0 0x%08lx    a1 0x%08lx    a2 0x%08lx    a3 0x%08lx\n",
202				frame->a[0], frame->a[1], frame->a[2], frame->a[3]);
203			kprintf("   a4 0x%08lx    a5 0x%08lx    a6 0x%08lx    a7 0x%08lx (sp)\n",
204#warning M68K: a7 in iframe ??
205				frame->a[4], frame->a[5], frame->a[6], -1L/*frame->a[7]*/);
206
207			/*kprintf("   pc 0x%08lx   ccr 0x%02x\n",
208			  frame->pc, frame->ccr);*/
209			kprintf("   pc 0x%08lx        sr 0x%04x\n",
210				frame->cpu.pc, frame->cpu.sr);
211#warning M68K: missing regs
212
213			print_stack_frame(thread, frame->cpu.pc, framePointer, frame->a[6]);
214 			framePointer = frame->a[6];
215		} else {
216			addr_t ip, nextFramePointer;
217
218			if (get_next_frame(framePointer, &nextFramePointer, &ip) != B_OK) {
219				kprintf("%08lx -- read fault\n", framePointer);
220				break;
221			}
222
223			if (ip == 0 || framePointer == 0)
224				break;
225
226			print_stack_frame(thread, ip, framePointer, nextFramePointer);
227			framePointer = nextFramePointer;
228		}
229
230		if (already_visited(previousLocations, &last, &num, framePointer)) {
231			kprintf("circular stack frame: %p!\n", (void *)framePointer);
232			break;
233		}
234		if (framePointer == 0)
235			break;
236	}
237
238/*	if (oldPageDirectory != 0) {
239		// switch back to the previous page directory to no cause any troubles
240		write_cr3(oldPageDirectory);
241	}
242*/
243
244	return 0;
245}
246
247
248
249// #pragma mark -
250
251
252void
253arch_debug_save_registers(struct arch_debug_registers* registers)
254{
255}
256
257
258void
259arch_debug_stack_trace(void)
260{
261}
262
263
264bool
265arch_debug_contains_call(Thread *thread, const char *symbol,
266	addr_t start, addr_t end)
267{
268	return false;
269}
270
271
272void *
273arch_debug_get_caller(void)
274{
275	// TODO: implement me
276	//return __builtin_frame_address(1);
277	struct stack_frame *frame;
278	//frame = __builtin_frame_address(0);
279	frame = get_current_stack_frame();
280	return (void *)frame->previous->return_address;
281}
282
283
284int32
285arch_debug_get_stack_trace(addr_t* returnAddresses, int32 maxCount,
286	int32 skipIframes, int32 skipFrames, uint32 flags)
287{
288	struct iframe_stack *frameStack;
289	addr_t framePointer;
290	int32 count = 0;
291	int32 i, num = 0, last = 0;
292
293	// Keep skipping normal stack frames until we've skipped the iframes we're
294	// supposed to skip.
295	if (skipIframes > 0)
296		skipFrames = INT_MAX;
297
298	Thread* thread = thread_get_current_thread();
299	framePointer = (addr_t)get_current_stack_frame();
300	bool onKernelStack = true;
301
302	// We don't have a thread pointer early in the boot process
303	if (thread != NULL)
304		frameStack = &thread->arch_info.iframes;
305	else
306		frameStack = &gBootFrameStack;
307
308	while (framePointer != 0 && count < maxCount) {
309		onKernelStack = onKernelStack && IS_KERNEL_ADDRESS(framePointer);
310			// TODO: Correctly determine whether this is a kernel address!
311		if (!onKernelStack && (flags & STACK_TRACE_USER) == 0)
312			break;
313
314		// see if the frame pointer matches the iframe
315		struct iframe *frame = NULL;
316		for (i = 0; i < frameStack->index; i++) {
317			if (framePointer == (addr_t)frameStack->frames[i]) {
318				// it's an iframe
319				frame = frameStack->frames[i];
320				break;
321			}
322		}
323
324		addr_t ip;
325		addr_t nextFrame;
326
327		if (frame) {
328			ip = frame->cpu.pc;
329 			nextFrame = frame->a[6];
330
331			if (skipIframes > 0) {
332				if (--skipIframes == 0)
333					skipFrames = 0;
334			}
335		} else {
336			if (get_next_frame(framePointer, &nextFrame, &ip) != B_OK)
337				break;
338		}
339
340		if (skipFrames <= 0
341			&& ((flags & STACK_TRACE_KERNEL) != 0 || onKernelStack)) {
342			returnAddresses[count++] = ip;
343		} else
344			skipFrames--;
345
346		framePointer = nextFrame;
347	}
348
349	return count;
350}
351
352
353void*
354arch_debug_get_interrupt_pc(bool* _isSyscall)
355{
356	// TODO: Implement!
357	return NULL;
358}
359
360
361void
362arch_debug_unset_current_thread(void)
363{
364	// TODO: Implement!
365}
366
367
368void
369arch_debug_call_with_fault_handler(cpu_ent* cpu, jmp_buf jumpBuffer,
370	void (*function)(void*), void* parameter)
371{
372	// TODO: Implement! Most likely in assembly.
373	longjmp(jumpBuffer, 1);
374}
375
376
377bool
378arch_is_debug_variable_defined(const char* variableName)
379{
380	// TODO: Implement!
381	return false;
382}
383
384
385status_t
386arch_set_debug_variable(const char* variableName, uint64 value)
387{
388	// TODO: Implement!
389	return B_ENTRY_NOT_FOUND;
390}
391
392
393status_t
394arch_get_debug_variable(const char* variableName, uint64* value)
395{
396	// TODO: Implement!
397	return B_ENTRY_NOT_FOUND;
398}
399
400
401ssize_t
402arch_debug_gdb_get_registers(char* buffer, size_t bufferSize)
403{
404	// TODO: Implement!
405	return B_NOT_SUPPORTED;
406}
407
408
409status_t
410arch_debug_init(kernel_args *args)
411{
412	add_debugger_command("where", &stack_trace, "Same as \"sc\"");
413	add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)");
414	add_debugger_command("sc", &stack_trace, "Stack crawl for current thread");
415
416	return B_NO_ERROR;
417}
418
419