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
272int32
273arch_debug_get_stack_trace(addr_t* returnAddresses, int32 maxCount,
274	int32 skipIframes, int32 skipFrames, uint32 flags)
275{
276	struct iframe_stack *frameStack;
277	addr_t framePointer;
278	int32 count = 0;
279	int32 i, num = 0, last = 0;
280
281	// Keep skipping normal stack frames until we've skipped the iframes we're
282	// supposed to skip.
283	if (skipIframes > 0)
284		skipFrames = INT_MAX;
285
286	Thread* thread = thread_get_current_thread();
287	framePointer = (addr_t)get_current_stack_frame();
288	bool onKernelStack = true;
289
290	// We don't have a thread pointer early in the boot process
291	if (thread != NULL)
292		frameStack = &thread->arch_info.iframes;
293	else
294		frameStack = &gBootFrameStack;
295
296	while (framePointer != 0 && count < maxCount) {
297		onKernelStack = onKernelStack && IS_KERNEL_ADDRESS(framePointer);
298			// TODO: Correctly determine whether this is a kernel address!
299		if (!onKernelStack && (flags & STACK_TRACE_USER) == 0)
300			break;
301
302		// see if the frame pointer matches the iframe
303		struct iframe *frame = NULL;
304		for (i = 0; i < frameStack->index; i++) {
305			if (framePointer == (addr_t)frameStack->frames[i]) {
306				// it's an iframe
307				frame = frameStack->frames[i];
308				break;
309			}
310		}
311
312		addr_t ip;
313		addr_t nextFrame;
314
315		if (frame) {
316			ip = frame->cpu.pc;
317 			nextFrame = frame->a[6];
318
319			if (skipIframes > 0) {
320				if (--skipIframes == 0)
321					skipFrames = 0;
322			}
323		} else {
324			if (get_next_frame(framePointer, &nextFrame, &ip) != B_OK)
325				break;
326		}
327
328		if (skipFrames <= 0
329			&& ((flags & STACK_TRACE_KERNEL) != 0 || onKernelStack)) {
330			returnAddresses[count++] = ip;
331		} else
332			skipFrames--;
333
334		framePointer = nextFrame;
335	}
336
337	return count;
338}
339
340
341void*
342arch_debug_get_interrupt_pc(bool* _isSyscall)
343{
344	// TODO: Implement!
345	return NULL;
346}
347
348
349void
350arch_debug_unset_current_thread(void)
351{
352	// TODO: Implement!
353}
354
355
356void
357arch_debug_call_with_fault_handler(cpu_ent* cpu, jmp_buf jumpBuffer,
358	void (*function)(void*), void* parameter)
359{
360	// TODO: Implement! Most likely in assembly.
361	longjmp(jumpBuffer, 1);
362}
363
364
365bool
366arch_is_debug_variable_defined(const char* variableName)
367{
368	// TODO: Implement!
369	return false;
370}
371
372
373status_t
374arch_set_debug_variable(const char* variableName, uint64 value)
375{
376	// TODO: Implement!
377	return B_ENTRY_NOT_FOUND;
378}
379
380
381status_t
382arch_get_debug_variable(const char* variableName, uint64* value)
383{
384	// TODO: Implement!
385	return B_ENTRY_NOT_FOUND;
386}
387
388
389ssize_t
390arch_debug_gdb_get_registers(char* buffer, size_t bufferSize)
391{
392	// TODO: Implement!
393	return B_NOT_SUPPORTED;
394}
395
396
397status_t
398arch_debug_init(kernel_args *args)
399{
400	add_debugger_command("where", &stack_trace, "Same as \"sc\"");
401	add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)");
402	add_debugger_command("sc", &stack_trace, "Stack crawl for current thread");
403
404	return B_NO_ERROR;
405}
406
407