xref: /haiku/src/system/kernel/arch/m68k/arch_debug.cpp (revision c9ad965c81b08802fed0827fd1dd16f45297928a)
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 /*!	Captures a stack trace (the return addresses) of the current thread.
283 	\param returnAddresses The array the return address shall be written to.
284 	\param maxCount The maximum number of return addresses to be captured.
285 	\param skipIframes The number of interrupt frames that shall be skipped. If
286 		greater than 0, \a skipFrames is ignored.
287 	\param skipFrames The number of stack frames that shall be skipped.
288 	\param userOnly If \c true, only userland return addresses are captured.
289 	\return The number of return addresses written to the given array.
290 */
291 int32
292 arch_debug_get_stack_trace(addr_t* returnAddresses, int32 maxCount,
293 	int32 skipIframes, int32 skipFrames, bool userOnly)
294 {
295 	struct iframe_stack *frameStack;
296 	addr_t framePointer;
297 	int32 count = 0;
298 	int32 i, num = 0, last = 0;
299 
300 	// Keep skipping normal stack frames until we've skipped the iframes we're
301 	// supposed to skip.
302 	if (skipIframes > 0) {
303 		skipFrames = INT_MAX;
304 	} else {
305 		// always skip our own frame
306 		skipFrames++;
307 	}
308 
309 	struct thread* thread = thread_get_current_thread();
310 	framePointer = (addr_t)get_current_stack_frame();
311 
312 	// We don't have a thread pointer early in the boot process
313 	if (thread != NULL)
314 		frameStack = &thread->arch_info.iframes;
315 	else
316 		frameStack = &gBootFrameStack;
317 
318 	while (framePointer != 0 && count < maxCount) {
319 		// see if the frame pointer matches the iframe
320 		struct iframe *frame = NULL;
321 		for (i = 0; i < frameStack->index; i++) {
322 			if (framePointer == (addr_t)frameStack->frames[i]) {
323 				// it's an iframe
324 				frame = frameStack->frames[i];
325 				break;
326 			}
327 		}
328 
329 		addr_t ip;
330 		addr_t nextFrame;
331 
332 		if (frame) {
333 			ip = frame->cpu.pc;
334  			nextFrame = frame->a[6];
335 
336 			if (skipIframes > 0) {
337 				if (--skipIframes == 0)
338 					skipFrames = 0;
339 			}
340 		} else {
341 			if (get_next_frame(framePointer, &nextFrame, &ip) != B_OK)
342 				break;
343 		}
344 
345 		if (skipFrames <= 0 && (!userOnly || IS_USER_ADDRESS(framePointer)))
346 			returnAddresses[count++] = ip;
347 		else
348 			skipFrames--;
349 
350 		framePointer = nextFrame;
351 	}
352 
353 	return count;
354 }
355 
356 
357 void*
358 arch_debug_get_interrupt_pc(bool* _isSyscall)
359 {
360 	// TODO: Implement!
361 	return NULL;
362 }
363 
364 
365 void
366 arch_debug_unset_current_thread(void)
367 {
368 	// TODO: Implement!
369 }
370 
371 
372 void
373 arch_debug_call_with_fault_handler(cpu_ent* cpu, jmp_buf jumpBuffer,
374 	void (*function)(void*), void* parameter)
375 {
376 	// TODO: Implement! Most likely in assembly.
377 	longjmp(jumpBuffer, 1);
378 }
379 
380 
381 bool
382 arch_is_debug_variable_defined(const char* variableName)
383 {
384 	// TODO: Implement!
385 	return false;
386 }
387 
388 
389 status_t
390 arch_set_debug_variable(const char* variableName, uint64 value)
391 {
392 	// TODO: Implement!
393 	return B_ENTRY_NOT_FOUND;
394 }
395 
396 
397 status_t
398 arch_get_debug_variable(const char* variableName, uint64* value)
399 {
400 	// TODO: Implement!
401 	return B_ENTRY_NOT_FOUND;
402 }
403 
404 
405 status_t
406 arch_debug_init(kernel_args *args)
407 {
408 	add_debugger_command("where", &stack_trace, "Same as \"sc\"");
409 	add_debugger_command("bt", &stack_trace, "Same as \"sc\" (as in gdb)");
410 	add_debugger_command("sc", &stack_trace, "Stack crawl for current thread");
411 
412 	return B_NO_ERROR;
413 }
414 
415