xref: /dragonfly/sys/platform/pc64/x86_64/db_trace.c (revision cfd1aba3)
1 /*
2  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  * 3. Neither the name of The DragonFly Project nor the names of its
15  *    contributors may be used to endorse or promote products derived
16  *    from this software without specific, prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * --
32  *
33  * Mach Operating System
34  * Copyright (c) 1991,1990 Carnegie Mellon University
35  * All Rights Reserved.
36  *
37  * Permission to use, copy, modify and distribute this software and its
38  * documentation is hereby granted, provided that both the copyright
39  * notice and this permission notice appear in all copies of the
40  * software, derivative works or modified versions, and any portions
41  * thereof, and that both notices appear in supporting documentation.
42  *
43  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
44  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
45  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
46  *
47  * Carnegie Mellon requests users of this software to return to
48  *
49  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
50  *  School of Computer Science
51  *  Carnegie Mellon University
52  *  Pittsburgh PA 15213-3890
53  *
54  * any improvements or extensions that they make and grant Carnegie the
55  * rights to redistribute these changes.
56  *
57  * $FreeBSD: src/sys/i386/i386/db_trace.c,v 1.35.2.3 2002/02/21 22:31:25 silby Exp $
58  */
59 
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/linker_set.h>
63 #include <sys/lock.h>
64 #include <sys/proc.h>
65 #include <sys/reg.h>
66 
67 #include <machine/cpu.h>
68 #include <machine/md_var.h>
69 
70 #include <vm/vm.h>
71 #include <vm/vm_param.h>
72 #include <vm/pmap.h>
73 #include <vm/vm_map.h>
74 #include <ddb/ddb.h>
75 #include <dlfcn.h>	/* DLL */
76 
77 #include <sys/user.h>
78 
79 #include <ddb/db_access.h>
80 #include <ddb/db_sym.h>
81 #include <ddb/db_variables.h>
82 
83 db_varfcn_t db_dr0;
84 db_varfcn_t db_dr1;
85 db_varfcn_t db_dr2;
86 db_varfcn_t db_dr3;
87 db_varfcn_t db_dr4;
88 db_varfcn_t db_dr5;
89 db_varfcn_t db_dr6;
90 db_varfcn_t db_dr7;
91 
92 /*
93  * Machine register set.
94  */
95 struct db_variable db_regs[] = {
96 	{ "cs",		&ddb_regs.tf_cs,     NULL },
97 /*	{ "ds",		&ddb_regs.tf_ds,     NULL },
98 	{ "es",		&ddb_regs.tf_es,     NULL },
99 	{ "fs",		&ddb_regs.tf_fs,     NULL },
100 	{ "gs",		&ddb_regs.tf_gs,     NULL }, */
101 	{ "ss",		&ddb_regs.tf_ss,     NULL },
102 	{ "rax",	&ddb_regs.tf_rax,    NULL },
103 	{ "rcx",	&ddb_regs.tf_rcx,    NULL },
104 	{ "rdx",	&ddb_regs.tf_rdx,    NULL },
105 	{ "rbx",	&ddb_regs.tf_rbx,    NULL },
106 	{ "rsp",	&ddb_regs.tf_rsp,    NULL },
107 	{ "rbp",	&ddb_regs.tf_rbp,    NULL },
108 	{ "rsi",	&ddb_regs.tf_rsi,    NULL },
109 	{ "rdi",	&ddb_regs.tf_rdi,    NULL },
110 	{ "rip",	&ddb_regs.tf_rip,    NULL },
111 	{ "rfl",	&ddb_regs.tf_rflags, NULL },
112 	{ "r8",		&ddb_regs.tf_r8,     NULL },
113 	{ "r9",		&ddb_regs.tf_r9,     NULL },
114 	{ "r10",	&ddb_regs.tf_r10,    NULL },
115 	{ "r11",	&ddb_regs.tf_r11,    NULL },
116 	{ "r12",	&ddb_regs.tf_r12,    NULL },
117 	{ "r13",	&ddb_regs.tf_r13,    NULL },
118 	{ "r14",	&ddb_regs.tf_r14,    NULL },
119 	{ "r15",	&ddb_regs.tf_r15,    NULL },
120 	{ "dr0",	NULL,		     db_dr0 },
121 	{ "dr1",	NULL,		     db_dr1 },
122 	{ "dr2",	NULL,		     db_dr2 },
123 	{ "dr3",	NULL,		     db_dr3 },
124 	{ "dr4",	NULL,		     db_dr4 },
125 	{ "dr5",	NULL,		     db_dr5 },
126 	{ "dr6",	NULL,		     db_dr6 },
127 	{ "dr7",	NULL,		     db_dr7 },
128 };
129 struct db_variable *db_eregs = db_regs + NELEM(db_regs);
130 
131 /*
132  * Stack trace.
133  */
134 #define	INKERNEL(va)	(((vm_offset_t)(va)) >= USRSTACK)
135 
136 struct x86_64_frame {
137 	struct x86_64_frame	*f_frame;
138 	long			f_retaddr;
139 	long			f_arg0;
140 };
141 
142 #define NORMAL		0
143 #define	TRAP		1
144 #define	INTERRUPT	2
145 #define	SYSCALL		3
146 
147 static void	db_nextframe(struct x86_64_frame **, db_addr_t *);
148 static int	db_numargs(struct x86_64_frame *);
149 static void	db_print_stack_entry(const char *, int, char **, long *, db_addr_t);
150 static void	dl_symbol_values(long callpc, const char **name);
151 
152 
153 static char	*watchtype_str(int type);
154 static int	kx86_64_set_watch(int watchnum, unsigned int watchaddr,
155                                int size, int access, struct dbreg * d);
156 static int	kx86_64_clr_watch(int watchnum, struct dbreg * d);
157 int		db_md_set_watchpoint(db_expr_t addr, db_expr_t size);
158 int		db_md_clr_watchpoint(db_expr_t addr, db_expr_t size);
159 void		db_md_list_watchpoints(void);
160 
161 
162 /*
163  * Figure out how many arguments were passed into the frame at "fp".
164  */
165 static int
166 db_numargs(struct x86_64_frame *fp)
167 {
168 #if 1
169 	return (0);	/* regparm, needs dwarf2 info */
170 #else
171 	int	args;
172 #if 0
173 	int	*argp;
174 	int	inst;
175 
176 	argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE);
177 	/*
178 	 * XXX etext is wrong for LKMs.  We should attempt to interpret
179 	 * the instruction at the return address in all cases.  This
180 	 * may require better fault handling.
181 	 */
182 	if (argp < (int *)btext || argp >= (int *)etext) {
183 		args = 5;
184 	} else {
185 		inst = db_get_value((int)argp, 4, FALSE);
186 		if ((inst & 0xff) == 0x59)	/* popl %ecx */
187 			args = 1;
188 		else if ((inst & 0xffff) == 0xc483)	/* addl $Ibs, %esp */
189 			args = ((inst >> 16) & 0xff) / 4;
190 		else
191 			args = 5;
192 	}
193 #endif
194 	args = 5;
195 	return(args);
196 #endif
197 }
198 
199 static void
200 db_print_stack_entry(const char *name, int narg, char **argnp, long *argp,
201 		     db_addr_t callpc)
202 {
203 	db_printf("%s(", name);
204 	while (narg) {
205 		if (argnp)
206 			db_printf("%s=", *argnp++);
207 		db_printf("%ld", (long)db_get_value((long)argp, 8, FALSE));
208 		argp++;
209 		if (--narg != 0)
210 			db_printf(",");
211 	}
212 	db_printf(") at ");
213 	db_printsym(callpc, DB_STGY_PROC);
214 	db_printf(" %p ",  (void*) callpc);
215 	db_printf("\n");
216 }
217 
218 /*
219  * Figure out the next frame up in the call stack.
220  */
221 static void
222 db_nextframe(struct x86_64_frame **fp, db_addr_t *ip)
223 {
224 	struct trapframe *tf;
225 	int frame_type;
226 	long rip, rsp, rbp;
227 	db_expr_t offset;
228 	const char *sym, *name;
229 
230 	if ((unsigned long)*fp < PAGE_SIZE) {
231 		*fp = NULL;
232 		return;
233 	}
234 	rip = db_get_value((long) &(*fp)->f_retaddr, 8, FALSE);
235 	rbp = db_get_value((long) &(*fp)->f_frame, 8, FALSE);
236 
237 	/*
238 	 * Figure out frame type.
239 	 */
240 
241 	frame_type = NORMAL;
242 
243 	sym = db_search_symbol(rip, DB_STGY_ANY, &offset);
244 	db_symbol_values(sym, &name, NULL);
245 	dl_symbol_values(rip, &name);
246 	if (name != NULL) {
247 		if (!strcmp(name, "calltrap")) {
248 			frame_type = TRAP;
249 		} else if (!strncmp(name, "Xresume", 7)) {
250 			frame_type = INTERRUPT;
251 		} else if (!strcmp(name, "_Xsyscall")) {
252 			frame_type = SYSCALL;
253 		}
254 	}
255 
256 	/*
257 	 * Normal frames need no special processing.
258 	 */
259 	if (frame_type == NORMAL) {
260 		*ip = (db_addr_t) rip;
261 		*fp = (struct x86_64_frame *) rbp;
262 		return;
263 	}
264 
265 	db_print_stack_entry(name, 0, 0, 0, rip);
266 
267 	/*
268 	 * Point to base of trapframe which is just above the
269 	 * current frame.
270 	 */
271 	tf = (struct trapframe *)((long)*fp + 16);
272 
273 #if 0
274 	rsp = (ISPL(tf->tf_cs) == SEL_UPL) ?  tf->tf_rsp : (long)&tf->tf_rsp;
275 #endif
276 	rsp = (long)&tf->tf_rsp;
277 
278 	switch (frame_type) {
279 	case TRAP:
280 		{
281 			rip = tf->tf_rip;
282 			rbp = tf->tf_rbp;
283 			db_printf(
284 	    "--- trap %016lx, rip = %016lx, rsp = %016lx, rbp = %016lx ---\n",
285 			    tf->tf_trapno, rip, rsp, rbp);
286 		}
287 		break;
288 	case SYSCALL:
289 		{
290 			rip = tf->tf_rip;
291 			rbp = tf->tf_rbp;
292 			db_printf(
293 	"--- syscall %016lx, rip = %016lx, rsp = %016lx, rbp = %016lx ---\n",
294 			    tf->tf_rax, rip, rsp, rbp);
295 		}
296 		break;
297 	case INTERRUPT:
298 		tf = (struct trapframe *)((long)*fp + 16);
299 		{
300 			rip = tf->tf_rip;
301 			rbp = tf->tf_rbp;
302 			db_printf(
303 	    "--- interrupt, rip = %016lx, rsp = %016lx, rbp = %016lx ---\n",
304 			    rip, rsp, rbp);
305 		}
306 		break;
307 	default:
308 		break;
309 	}
310 
311 	*ip = (db_addr_t) rip;
312 	*fp = (struct x86_64_frame *) rbp;
313 }
314 
315 void
316 db_stack_trace_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
317 		   char *modif)
318 {
319 	struct x86_64_frame *frame;
320 	long *argp;
321 	db_addr_t callpc;
322 	boolean_t first;
323 	int i;
324 
325 	if (count == -1)
326 		count = 1024;
327 
328 	if (!have_addr) {
329 		frame = (struct x86_64_frame *)BP_REGS(&ddb_regs);
330 		if (frame == NULL)
331 			frame = (struct x86_64_frame *)(SP_REGS(&ddb_regs) - 8);
332 		callpc = PC_REGS(&ddb_regs);
333 	} else {
334 		/*
335 		 * Look for something that might be a frame pointer, just as
336 		 * a convenience.
337 		 */
338 		frame = (struct x86_64_frame *)addr;
339 		for (i = 0; i < 4096; i += 8) {
340 			struct x86_64_frame *check;
341 
342 			check = (struct x86_64_frame *)db_get_value((long)((char *)&frame->f_frame + i), 8, FALSE);
343 			if ((char *)check - (char *)frame >= 0 &&
344 			    (char *)check - (char *)frame < 4096
345 			) {
346 				break;
347 			}
348 			db_printf("%p does not look like a stack frame, skipping\n", (char *)&frame->f_frame + i);
349 		}
350 		if (i == 4096) {
351 			db_printf("Unable to find anything that looks like a stack frame\n");
352 			return;
353 		}
354 		frame = (void *)((char *)frame + i);
355 		db_printf("Trace beginning at frame %p\n", frame);
356 		callpc = (db_addr_t)db_get_value((long)&frame->f_retaddr, 8, FALSE);
357 	}
358 
359 	first = TRUE;
360 	while (count--) {
361 		struct x86_64_frame *actframe;
362 		int		narg;
363 		const char *	name;
364 		db_expr_t	offset;
365 		c_db_sym_t	sym;
366 #define MAXNARG	16
367 		char	*argnames[MAXNARG], **argnp = NULL;
368 
369 		sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
370 		db_symbol_values(sym, &name, NULL);
371 		dl_symbol_values(callpc, &name);
372 
373 		/*
374 		 * Attempt to determine a (possibly fake) frame that gives
375 		 * the caller's pc.  It may differ from `frame' if the
376 		 * current function never sets up a standard frame or hasn't
377 		 * set one up yet or has just discarded one.  The last two
378 		 * cases can be guessed fairly reliably for code generated
379 		 * by gcc.  The first case is too much trouble to handle in
380 		 * general because the amount of junk on the stack depends
381 		 * on the pc (the special handling of "calltrap", etc. in
382 		 * db_nextframe() works because the `next' pc is special).
383 		 */
384 		actframe = frame;
385 		if (first) {
386 			if (!have_addr) {
387 				int instr;
388 
389 				instr = db_get_value(callpc, 4, FALSE);
390 				if ((instr & 0xffffffff) == 0xe5894855) {
391 					/* pushq %rbp; movq %rsp, %rbp */
392 					actframe = (struct x86_64_frame *)
393 					    (SP_REGS(&ddb_regs) - 8);
394 				} else if ((instr & 0xffffff) == 0xe58948) {
395 					/* movq %rsp, %rbp */
396 					actframe = (struct x86_64_frame *)
397 					    SP_REGS(&ddb_regs);
398 					if (ddb_regs.tf_rbp == 0) {
399 						/* Fake caller's frame better. */
400 						frame = actframe;
401 					}
402 				} else if ((instr & 0xff) == 0xc3) {
403 					/* ret */
404 					actframe = (struct x86_64_frame *)
405 					    (SP_REGS(&ddb_regs) - 8);
406 				} else if (offset == 0) {
407 					/* Probably a symbol in assembler code. */
408 					actframe = (struct x86_64_frame *)
409 					    (SP_REGS(&ddb_regs) - 8);
410 				}
411 			} else if (name != NULL &&
412 				   strcmp(name, "fork_trampoline") == 0) {
413 				/*
414 				 * Don't try to walk back on a stack for a
415 				 * process that hasn't actually been run yet.
416 				 */
417 				db_print_stack_entry(name, 0, 0, 0, callpc);
418 				break;
419 			}
420 			first = FALSE;
421 		}
422 
423 		argp = &actframe->f_arg0;
424 		narg = MAXNARG;
425 		if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) {
426 			argnp = argnames;
427 		} else {
428 			narg = db_numargs(frame);
429 		}
430 
431 		db_print_stack_entry(name, narg, argnp, argp, callpc);
432 
433 		/*
434 		 * Stop at the system call boundary (else we risk
435 		 * double-faulting on junk).
436 		 */
437 		if (name && strcmp(name, "Xfast_syscall") == 0)
438 			break;
439 
440 		if (actframe != frame) {
441 			/* `frame' belongs to caller. */
442 			callpc = (db_addr_t)
443 			    db_get_value((long)&actframe->f_retaddr, 8, FALSE);
444 			continue;
445 		}
446 
447 		db_nextframe(&frame, &callpc);
448 		if (frame == NULL)
449 			break;
450 	}
451 }
452 
453 void
454 print_backtrace(int count)
455 {
456 	register_t  rbp;
457 
458 	__asm __volatile("movq %%rbp, %0" : "=r" (rbp));
459 	db_stack_trace_cmd(rbp, 1, count, NULL);
460 }
461 
462 #define DB_DRX_FUNC(reg)						\
463 int									\
464 db_ ## reg (struct db_variable *vp, db_expr_t *valuep, int op)		\
465 {									\
466 	if (op == DB_VAR_GET)						\
467 		*valuep = r ## reg ();					\
468 	else								\
469 		load_ ## reg (*valuep); 				\
470 									\
471 	return(0);							\
472 }
473 
474 DB_DRX_FUNC(dr0)
475 DB_DRX_FUNC(dr1)
476 DB_DRX_FUNC(dr2)
477 DB_DRX_FUNC(dr3)
478 DB_DRX_FUNC(dr4)
479 DB_DRX_FUNC(dr5)
480 DB_DRX_FUNC(dr6)
481 DB_DRX_FUNC(dr7)
482 
483 static int
484 kx86_64_set_watch(int watchnum, unsigned int watchaddr, int size, int access,
485 	       struct dbreg *d)
486 {
487 	int i;
488 	unsigned int mask;
489 
490 	if (watchnum == -1) {
491 		for (i = 0, mask = 0x3; i < 4; i++, mask <<= 2)
492 			if ((d->dr[7] & mask) == 0)
493 				break;
494 		if (i < 4)
495 			watchnum = i;
496 		else
497 			return(-1);
498 	}
499 
500 	switch (access) {
501 	case DBREG_DR7_EXEC:
502 		size = 1; /* size must be 1 for an execution breakpoint */
503 		/* fall through */
504 	case DBREG_DR7_WRONLY:
505 	case DBREG_DR7_RDWR:
506 		break;
507 	default:
508 		return(-1);
509 	}
510 
511 	/*
512 	 * we can watch a 1, 2, 4, or 8 byte sized location
513 	 */
514 	switch (size) {
515 	case 1:
516 		mask = 0x00;
517 		break;
518 	case 2:
519 		mask = 0x01 << 2;
520 		break;
521 	case 4:
522 		mask = 0x03 << 2;
523 		break;
524 	case 8:
525 		mask = 0x02 << 2;
526 		break;
527 	default:
528 		return(-1);
529 	}
530 
531 	mask |= access;
532 
533 	/* clear the bits we are about to affect */
534 	d->dr[7] &= ~((0x3 << (watchnum * 2)) | (0x0f << (watchnum * 4 + 16)));
535 
536 	/* set drN register to the address, N=watchnum */
537 	DBREG_DRX(d, watchnum) = watchaddr;
538 
539 	/* enable the watchpoint */
540 	d->dr[7] |= (0x2 << (watchnum * 2)) | (mask << (watchnum * 4 + 16));
541 
542 	return(watchnum);
543 }
544 
545 
546 int
547 kx86_64_clr_watch(int watchnum, struct dbreg *d)
548 {
549 	if (watchnum < 0 || watchnum >= 4)
550 		return(-1);
551 
552 	d->dr[7] &= ~((0x3 << (watchnum * 2)) | (0x0f << (watchnum * 4 + 16)));
553 	DBREG_DRX(d, watchnum) = 0;
554 
555 	return(0);
556 }
557 
558 
559 int
560 db_md_set_watchpoint(db_expr_t addr, db_expr_t size)
561 {
562 	int avail, wsize;
563 	int i;
564 	struct dbreg d;
565 
566 	fill_dbregs(NULL, &d);
567 
568 	avail = 0;
569 	for (i = 0; i < 4; i++) {
570 		if ((d.dr[7] & (3 << (i * 2))) == 0)
571 			avail++;
572 	}
573 
574 	if (avail * 8 < size)
575 		return(-1);
576 
577 	for (i=0; i < 4 && (size != 0); i++) {
578 		if ((d.dr[7] & (3 << (i * 2))) == 0) {
579 			if (size >= 8 || (avail == 1 && size > 4))
580 				wsize = 8;
581 			else if (size > 2)
582 				wsize = 4;
583 			else
584 				wsize = size;
585 			if (wsize == 3)
586 				wsize++;
587 			kx86_64_set_watch(i, addr, wsize, DBREG_DR7_WRONLY, &d);
588 			addr += wsize;
589 			size -= wsize;
590 		}
591 	}
592 
593 	set_dbregs(NULL, &d);
594 
595 	return(0);
596 }
597 
598 int
599 db_md_clr_watchpoint(db_expr_t addr, db_expr_t size)
600 {
601 	struct dbreg d;
602 	int i;
603 
604 	fill_dbregs(NULL, &d);
605 
606 	for(i = 0; i < 4; i++) {
607 		if (d.dr[7] & (3 << (i * 2))) {
608 			if ((DBREG_DRX((&d), i) >= addr) &&
609 			    (DBREG_DRX((&d), i) < addr + size))
610 				kx86_64_clr_watch(i, &d);
611 		}
612 	}
613 
614 	set_dbregs(NULL, &d);
615 
616 	return(0);
617 }
618 
619 static char *
620 watchtype_str(int type)
621 {
622 	switch (type) {
623 	case DBREG_DR7_EXEC:
624 		return "execute";
625 	case DBREG_DR7_RDWR:
626 		return "read/write";
627 	case DBREG_DR7_WRONLY:
628 		return "write";
629 	default:
630 		return "invalid";
631 	}
632 }
633 
634 void
635 db_md_list_watchpoints(void)
636 {
637 	int i;
638 	struct dbreg d;
639 
640 	fill_dbregs(NULL, &d);
641 
642 	db_printf("\nhardware watchpoints:\n");
643 	db_printf("  watch    status        type  len     address\n"
644 		  "  -----  --------  ----------  ---  ----------\n");
645 	for (i = 0; i < 4; i++) {
646 		if (d.dr[7] & (0x03 << (i * 2))) {
647 			unsigned type, len;
648 			type = (d.dr[7] >> (16 + (i * 4))) & 3;
649 			len =  (d.dr[7] >> (16 + (i * 4) + 2)) & 3;
650 			db_printf("  %-5d  %-8s  %10s  %3d  0x%08lx\n",
651 				  i, "enabled", watchtype_str(type),
652 				  len + 1, DBREG_DRX((&d), i));
653 		} else {
654 			db_printf("  %-5d  disabled\n", i);
655 		}
656 	}
657 
658 	db_printf("\ndebug register values:\n");
659 	for (i = 0; i < 8; i++)
660 		db_printf("  dr%d 0x%08lx\n", i, DBREG_DRX((&d),i));
661 	db_printf("\n");
662 }
663 
664 /*
665  * See if dladdr() can get the symbol name via the standard dynamic loader.
666  */
667 static
668 void
669 dl_symbol_values(long callpc, const char **name)
670 {
671 #if 0
672 	Dl_info info;
673 	if (*name == NULL) {
674 		if (dladdr((const void *)callpc, &info) != 0) {
675 			if (info.dli_saddr <= (const void *)callpc)
676 				*name = info.dli_sname;
677 		}
678 	}
679 #endif
680 }
681 
682