xref: /freebsd/sys/amd64/amd64/db_trace.c (revision 7bd6fde3)
1 /*-
2  * Mach Operating System
3  * Copyright (c) 1991,1990 Carnegie Mellon University
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify and distribute this software and its
7  * documentation is hereby granted, provided that both the copyright
8  * notice and this permission notice appear in all copies of the
9  * software, derivative works or modified versions, and any portions
10  * thereof, and that both notices appear in supporting documentation.
11  *
12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15  *
16  * Carnegie Mellon requests users of this software to return to
17  *
18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19  *  School of Computer Science
20  *  Carnegie Mellon University
21  *  Pittsburgh PA 15213-3890
22  *
23  * any improvements or extensions that they make and grant Carnegie the
24  * rights to redistribute these changes.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kdb.h>
33 #include <sys/proc.h>
34 #include <sys/stack.h>
35 #include <sys/sysent.h>
36 
37 #include <machine/cpu.h>
38 #include <machine/md_var.h>
39 #include <machine/pcb.h>
40 #include <machine/reg.h>
41 
42 #include <vm/vm.h>
43 #include <vm/vm_param.h>
44 #include <vm/pmap.h>
45 
46 #include <ddb/ddb.h>
47 #include <ddb/db_access.h>
48 #include <ddb/db_sym.h>
49 #include <ddb/db_variables.h>
50 
51 static db_varfcn_t db_dr0;
52 static db_varfcn_t db_dr1;
53 static db_varfcn_t db_dr2;
54 static db_varfcn_t db_dr3;
55 static db_varfcn_t db_dr4;
56 static db_varfcn_t db_dr5;
57 static db_varfcn_t db_dr6;
58 static db_varfcn_t db_dr7;
59 static db_varfcn_t db_frame;
60 static db_varfcn_t db_rsp;
61 static db_varfcn_t db_ss;
62 
63 /*
64  * Machine register set.
65  */
66 #define	DB_OFFSET(x)	(db_expr_t *)offsetof(struct trapframe, x)
67 struct db_variable db_regs[] = {
68 	{ "cs",		DB_OFFSET(tf_cs),	db_frame },
69 #if 0
70 	{ "ds",		DB_OFFSET(tf_ds),	db_frame },
71 	{ "es",		DB_OFFSET(tf_es),	db_frame },
72 	{ "fs",		DB_OFFSET(tf_fs),	db_frame },
73 	{ "gs",		DB_OFFSET(tf_gs),	db_frame },
74 #endif
75 	{ "ss",		NULL,			db_ss },
76 	{ "rax",	DB_OFFSET(tf_rax),	db_frame },
77 	{ "rcx",        DB_OFFSET(tf_rcx),	db_frame },
78 	{ "rdx",	DB_OFFSET(tf_rdx),	db_frame },
79 	{ "rbx",	DB_OFFSET(tf_rbx),	db_frame },
80 	{ "rsp",	NULL,			db_rsp },
81 	{ "rbp",	DB_OFFSET(tf_rbp),	db_frame },
82 	{ "rsi",	DB_OFFSET(tf_rsi),	db_frame },
83 	{ "rdi",	DB_OFFSET(tf_rdi),	db_frame },
84 	{ "r8",		DB_OFFSET(tf_r8),	db_frame },
85 	{ "r9",		DB_OFFSET(tf_r9),	db_frame },
86 	{ "r10",	DB_OFFSET(tf_r10),	db_frame },
87 	{ "r11",	DB_OFFSET(tf_r11),	db_frame },
88 	{ "r12",	DB_OFFSET(tf_r12),	db_frame },
89 	{ "r13",	DB_OFFSET(tf_r13),	db_frame },
90 	{ "r14",	DB_OFFSET(tf_r14),	db_frame },
91 	{ "r15",	DB_OFFSET(tf_r15),	db_frame },
92 	{ "rip",	DB_OFFSET(tf_rip),	db_frame },
93 	{ "rflags",	DB_OFFSET(tf_rflags),	db_frame },
94 #define	DB_N_SHOW_REGS	20	/* Don't show registers after here. */
95 	{ "dr0",	NULL,			db_dr0 },
96 	{ "dr1",	NULL,			db_dr1 },
97 	{ "dr2",	NULL,			db_dr2 },
98 	{ "dr3",	NULL,			db_dr3 },
99 	{ "dr4",	NULL,			db_dr4 },
100 	{ "dr5",	NULL,			db_dr5 },
101 	{ "dr6",	NULL,			db_dr6 },
102 	{ "dr7",	NULL,			db_dr7 },
103 };
104 struct db_variable *db_eregs = db_regs + DB_N_SHOW_REGS;
105 
106 #define DB_DRX_FUNC(reg)		\
107 static int				\
108 db_ ## reg (vp, valuep, op)		\
109 	struct db_variable *vp;		\
110 	db_expr_t * valuep;		\
111 	int op;				\
112 {					\
113 	if (op == DB_VAR_GET)		\
114 		*valuep = r ## reg ();	\
115 	else				\
116 		load_ ## reg (*valuep); \
117 	return (1);			\
118 }
119 
120 DB_DRX_FUNC(dr0)
121 DB_DRX_FUNC(dr1)
122 DB_DRX_FUNC(dr2)
123 DB_DRX_FUNC(dr3)
124 DB_DRX_FUNC(dr4)
125 DB_DRX_FUNC(dr5)
126 DB_DRX_FUNC(dr6)
127 DB_DRX_FUNC(dr7)
128 
129 static __inline long
130 get_rsp(struct trapframe *tf)
131 {
132 	return ((ISPL(tf->tf_cs)) ? tf->tf_rsp :
133 	    (db_expr_t)tf + offsetof(struct trapframe, tf_rsp));
134 }
135 
136 static int
137 db_frame(struct db_variable *vp, db_expr_t *valuep, int op)
138 {
139 	long *reg;
140 
141 	if (kdb_frame == NULL)
142 		return (0);
143 
144 	reg = (long *)((uintptr_t)kdb_frame + (db_expr_t)vp->valuep);
145 	if (op == DB_VAR_GET)
146 		*valuep = *reg;
147 	else
148 		*reg = *valuep;
149 	return (1);
150 }
151 
152 static int
153 db_rsp(struct db_variable *vp, db_expr_t *valuep, int op)
154 {
155 
156 	if (kdb_frame == NULL)
157 		return (0);
158 
159 	if (op == DB_VAR_GET)
160 		*valuep = get_rsp(kdb_frame);
161 	else if (ISPL(kdb_frame->tf_cs))
162 		kdb_frame->tf_rsp = *valuep;
163 	return (1);
164 }
165 
166 static int
167 db_ss(struct db_variable *vp, db_expr_t *valuep, int op)
168 {
169 
170 	if (kdb_frame == NULL)
171 		return (0);
172 
173 	if (op == DB_VAR_GET)
174 		*valuep = (ISPL(kdb_frame->tf_cs)) ? kdb_frame->tf_ss : rss();
175 	else if (ISPL(kdb_frame->tf_cs))
176 		kdb_frame->tf_ss = *valuep;
177 	return (1);
178 }
179 
180 /*
181  * Stack trace.
182  */
183 #define	INKERNEL(va) (((va) >= DMAP_MIN_ADDRESS && (va) < DMAP_MAX_ADDRESS) \
184 	    || ((va) >= KERNBASE && (va) < VM_MAX_KERNEL_ADDRESS))
185 
186 struct amd64_frame {
187 	struct amd64_frame	*f_frame;
188 	long			f_retaddr;
189 	long			f_arg0;
190 };
191 
192 #define NORMAL		0
193 #define	TRAP		1
194 #define	INTERRUPT	2
195 #define	SYSCALL		3
196 #define	TRAP_INTERRUPT	5
197 
198 static void db_nextframe(struct amd64_frame **, db_addr_t *, struct thread *);
199 static int db_numargs(struct amd64_frame *);
200 static void db_print_stack_entry(const char *, int, char **, long *, db_addr_t);
201 static void decode_syscall(int, struct thread *);
202 
203 static const char * watchtype_str(int type);
204 int  amd64_set_watch(int watchnum, unsigned long watchaddr, int size,
205 		    int access, struct dbreg *d);
206 int  amd64_clr_watch(int watchnum, struct dbreg *d);
207 
208 /*
209  * Figure out how many arguments were passed into the frame at "fp".
210  */
211 static int
212 db_numargs(fp)
213 	struct amd64_frame *fp;
214 {
215 #if 1
216 	return (0);	/* regparm, needs dwarf2 info */
217 #else
218 	long	*argp;
219 	int	inst;
220 	int	args;
221 
222 	argp = (long *)db_get_value((long)&fp->f_retaddr, 8, FALSE);
223 	/*
224 	 * XXX etext is wrong for LKMs.  We should attempt to interpret
225 	 * the instruction at the return address in all cases.  This
226 	 * may require better fault handling.
227 	 */
228 	if (argp < (long *)btext || argp >= (long *)etext) {
229 		args = 5;
230 	} else {
231 		inst = db_get_value((long)argp, 4, FALSE);
232 		if ((inst & 0xff) == 0x59)	/* popl %ecx */
233 			args = 1;
234 		else if ((inst & 0xffff) == 0xc483)	/* addl $Ibs, %esp */
235 			args = ((inst >> 16) & 0xff) / 4;
236 		else
237 			args = 5;
238 	}
239 	return (args);
240 #endif
241 }
242 
243 static void
244 db_print_stack_entry(name, narg, argnp, argp, callpc)
245 	const char *name;
246 	int narg;
247 	char **argnp;
248 	long *argp;
249 	db_addr_t callpc;
250 {
251 	db_printf("%s(", name);
252 #if 0
253 	while (narg) {
254 		if (argnp)
255 			db_printf("%s=", *argnp++);
256 		db_printf("%lr", (long)db_get_value((long)argp, 8, FALSE));
257 		argp++;
258 		if (--narg != 0)
259 			db_printf(",");
260 	}
261 #endif
262 	db_printf(") at ");
263 	db_printsym(callpc, DB_STGY_PROC);
264 	db_printf("\n");
265 }
266 
267 static void
268 decode_syscall(int number, struct thread *td)
269 {
270 	struct proc *p;
271 	c_db_sym_t sym;
272 	db_expr_t diff;
273 	sy_call_t *f;
274 	const char *symname;
275 
276 	db_printf(" (%d", number);
277 	p = (td != NULL) ? td->td_proc : NULL;
278 	if (p != NULL && 0 <= number && number < p->p_sysent->sv_size) {
279 		f = p->p_sysent->sv_table[number].sy_call;
280 		sym = db_search_symbol((db_addr_t)f, DB_STGY_ANY, &diff);
281 		if (sym != DB_SYM_NULL && diff == 0) {
282 			db_symbol_values(sym, &symname, NULL);
283 			db_printf(", %s, %s", p->p_sysent->sv_name, symname);
284 		}
285 	}
286 	db_printf(")");
287 }
288 
289 /*
290  * Figure out the next frame up in the call stack.
291  */
292 static void
293 db_nextframe(struct amd64_frame **fp, db_addr_t *ip, struct thread *td)
294 {
295 	struct trapframe *tf;
296 	int frame_type;
297 	long rip, rsp, rbp;
298 	db_expr_t offset;
299 	c_db_sym_t sym;
300 	const char *name;
301 
302 	rip = db_get_value((long) &(*fp)->f_retaddr, 8, FALSE);
303 	rbp = db_get_value((long) &(*fp)->f_frame, 8, FALSE);
304 
305 	/*
306 	 * Figure out frame type.  We look at the address just before
307 	 * the saved instruction pointer as the saved EIP is after the
308 	 * call function, and if the function being called is marked as
309 	 * dead (such as panic() at the end of dblfault_handler()), then
310 	 * the instruction at the saved EIP will be part of a different
311 	 * function (syscall() in this example) rather than the one that
312 	 * actually made the call.
313 	 */
314 	frame_type = NORMAL;
315 	sym = db_search_symbol(rip - 1, DB_STGY_ANY, &offset);
316 	db_symbol_values(sym, &name, NULL);
317 	if (name != NULL) {
318 		if (strcmp(name, "calltrap") == 0 ||
319 		    strcmp(name, "fork_trampoline") == 0 ||
320 		    strcmp(name, "nmi_calltrap") == 0)
321 			frame_type = TRAP;
322 		else if (strncmp(name, "Xatpic_intr", 11) == 0 ||
323 		    strncmp(name, "Xapic_isr", 9) == 0 ||
324 		    strcmp(name, "Xtimerint") == 0 ||
325 		    strcmp(name, "Xipi_intr_bitmap_handler") == 0 ||
326 		    strcmp(name, "Xcpustop") == 0 ||
327 		    strcmp(name, "Xrendezvous") == 0)
328 			frame_type = INTERRUPT;
329 		else if (strcmp(name, "Xfast_syscall") == 0)
330 			frame_type = SYSCALL;
331 		/* XXX: These are interrupts with trap frames. */
332 		else if (strcmp(name, "Xtimerint") == 0 ||
333 		    strcmp(name, "Xcpustop") == 0 ||
334 		    strcmp(name, "Xrendezvous") == 0 ||
335 		    strcmp(name, "Xipi_intr_bitmap_handler") == 0)
336 			frame_type = TRAP_INTERRUPT;
337 	}
338 
339 	/*
340 	 * Normal frames need no special processing.
341 	 */
342 	if (frame_type == NORMAL) {
343 		*ip = (db_addr_t) rip;
344 		*fp = (struct amd64_frame *) rbp;
345 		return;
346 	}
347 
348 	db_print_stack_entry(name, 0, 0, 0, rip);
349 
350 	/*
351 	 * Point to base of trapframe which is just above the
352 	 * current frame.
353 	 */
354 	tf = (struct trapframe *)((long)*fp + 16);
355 
356 	if (INKERNEL((long) tf)) {
357 		rsp = get_rsp(tf);
358 		rip = tf->tf_rip;
359 		rbp = tf->tf_rbp;
360 		switch (frame_type) {
361 		case TRAP:
362 			db_printf("--- trap %#lr", tf->tf_trapno);
363 			break;
364 		case SYSCALL:
365 			db_printf("--- syscall");
366 			decode_syscall(tf->tf_rax, td);
367 			break;
368 		case TRAP_INTERRUPT:
369 		case INTERRUPT:
370 			db_printf("--- interrupt");
371 			break;
372 		default:
373 			panic("The moon has moved again.");
374 		}
375 		db_printf(", rip = %#lr, rsp = %#lr, rbp = %#lr ---\n", rip,
376 		    rsp, rbp);
377 	}
378 
379 	*ip = (db_addr_t) rip;
380 	*fp = (struct amd64_frame *) rbp;
381 }
382 
383 static int
384 db_backtrace(struct thread *td, struct trapframe *tf,
385     struct amd64_frame *frame, db_addr_t pc, int count)
386 {
387 	struct amd64_frame *actframe;
388 #define MAXNARG	16
389 	char *argnames[MAXNARG], **argnp = NULL;
390 	const char *name;
391 	long *argp;
392 	db_expr_t offset;
393 	c_db_sym_t sym;
394 	int narg;
395 	boolean_t first;
396 
397 	if (count == -1)
398 		count = 1024;
399 
400 	first = TRUE;
401 	while (count-- && !db_pager_quit) {
402 		sym = db_search_symbol(pc, DB_STGY_ANY, &offset);
403 		db_symbol_values(sym, &name, NULL);
404 
405 		/*
406 		 * Attempt to determine a (possibly fake) frame that gives
407 		 * the caller's pc.  It may differ from `frame' if the
408 		 * current function never sets up a standard frame or hasn't
409 		 * set one up yet or has just discarded one.  The last two
410 		 * cases can be guessed fairly reliably for code generated
411 		 * by gcc.  The first case is too much trouble to handle in
412 		 * general because the amount of junk on the stack depends
413 		 * on the pc (the special handling of "calltrap", etc. in
414 		 * db_nextframe() works because the `next' pc is special).
415 		 */
416 		actframe = frame;
417 		if (first) {
418 			if (tf != NULL) {
419 				int instr;
420 
421 				instr = db_get_value(pc, 4, FALSE);
422 				if ((instr & 0xffffffff) == 0xe5894855) {
423 					/* pushq %rbp; movq %rsp, %rbp */
424 					actframe = (void *)(get_rsp(tf) - 8);
425 				} else if ((instr & 0xffffff) == 0xe58948) {
426 					/* movq %rsp, %rbp */
427 					actframe = (void *)get_rsp(tf);
428 					if (tf->tf_rbp == 0) {
429 						/* Fake frame better. */
430 						frame = actframe;
431 					}
432 				} else if ((instr & 0xff) == 0xc3) {
433 					/* ret */
434 					actframe = (void *)(get_rsp(tf) - 8);
435 				} else if (offset == 0) {
436 					/* Probably an assembler symbol. */
437 					actframe = (void *)(get_rsp(tf) - 8);
438 				}
439 			} else if (strcmp(name, "fork_trampoline") == 0) {
440 				/*
441 				 * Don't try to walk back on a stack for a
442 				 * process that hasn't actually been run yet.
443 				 */
444 				db_print_stack_entry(name, 0, 0, 0, pc);
445 				break;
446 			}
447 			first = FALSE;
448 		}
449 
450 		argp = &actframe->f_arg0;
451 		narg = MAXNARG;
452 		if (sym != NULL && db_sym_numargs(sym, &narg, argnames)) {
453 			argnp = argnames;
454 		} else {
455 			narg = db_numargs(frame);
456 		}
457 
458 		db_print_stack_entry(name, narg, argnp, argp, pc);
459 
460 		if (actframe != frame) {
461 			/* `frame' belongs to caller. */
462 			pc = (db_addr_t)
463 			    db_get_value((long)&actframe->f_retaddr, 8, FALSE);
464 			continue;
465 		}
466 
467 		db_nextframe(&frame, &pc, td);
468 
469 		if (INKERNEL((long)pc) && !INKERNEL((long)frame)) {
470 			sym = db_search_symbol(pc, DB_STGY_ANY, &offset);
471 			db_symbol_values(sym, &name, NULL);
472 			db_print_stack_entry(name, 0, 0, 0, pc);
473 			break;
474 		}
475 		if (!INKERNEL((long) frame)) {
476 			break;
477 		}
478 	}
479 
480 	return (0);
481 }
482 
483 void
484 db_trace_self(void)
485 {
486 	struct amd64_frame *frame;
487 	db_addr_t callpc;
488 	register_t rbp;
489 
490 	__asm __volatile("movq %%rbp,%0" : "=r" (rbp));
491 	frame = (struct amd64_frame *)rbp;
492 	callpc = (db_addr_t)db_get_value((long)&frame->f_retaddr, 8, FALSE);
493 	frame = frame->f_frame;
494 	db_backtrace(curthread, NULL, frame, callpc, -1);
495 }
496 
497 int
498 db_trace_thread(struct thread *thr, int count)
499 {
500 	struct pcb *ctx;
501 
502 	ctx = kdb_thr_ctx(thr);
503 	return (db_backtrace(thr, NULL, (struct amd64_frame *)ctx->pcb_rbp,
504 		    ctx->pcb_rip, count));
505 }
506 
507 void
508 stack_save(struct stack *st)
509 {
510 	struct amd64_frame *frame;
511 	vm_offset_t callpc;
512 	register_t rbp;
513 
514 	stack_zero(st);
515 	__asm __volatile("movq %%rbp,%0" : "=r" (rbp));
516 	frame = (struct amd64_frame *)rbp;
517 	while (1) {
518 		if (!INKERNEL((long)frame))
519 			break;
520 		callpc = frame->f_retaddr;
521 		if (!INKERNEL(callpc))
522 			break;
523 		if (stack_put(st, callpc) == -1)
524 			break;
525 		if (frame->f_frame <= frame ||
526 		    (vm_offset_t)frame->f_frame >=
527 		    (vm_offset_t)rbp + KSTACK_PAGES * PAGE_SIZE)
528 			break;
529 		frame = frame->f_frame;
530 	}
531 }
532 
533 int
534 amd64_set_watch(watchnum, watchaddr, size, access, d)
535 	int watchnum;
536 	unsigned long watchaddr;
537 	int size;
538 	int access;
539 	struct dbreg *d;
540 {
541 	int i, len;
542 
543 	if (watchnum == -1) {
544 		for (i = 0; i < 4; i++)
545 			if (!DBREG_DR7_ENABLED(d->dr[7], i))
546 				break;
547 		if (i < 4)
548 			watchnum = i;
549 		else
550 			return (-1);
551 	}
552 
553 	switch (access) {
554 	case DBREG_DR7_EXEC:
555 		size = 1; /* size must be 1 for an execution breakpoint */
556 		/* fall through */
557 	case DBREG_DR7_WRONLY:
558 	case DBREG_DR7_RDWR:
559 		break;
560 	default:
561 		return (-1);
562 	}
563 
564 	/*
565 	 * we can watch a 1, 2, 4, or 8 byte sized location
566 	 */
567 	switch (size) {
568 	case 1:
569 		len = DBREG_DR7_LEN_1;
570 		break;
571 	case 2:
572 		len = DBREG_DR7_LEN_2;
573 		break;
574 	case 4:
575 		len = DBREG_DR7_LEN_4;
576 		break;
577 	case 8:
578 		len = DBREG_DR7_LEN_8;
579 		break;
580 	default:
581 		return (-1);
582 	}
583 
584 	/* clear the bits we are about to affect */
585 	d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
586 
587 	/* set drN register to the address, N=watchnum */
588 	DBREG_DRX(d, watchnum) = watchaddr;
589 
590 	/* enable the watchpoint */
591 	d->dr[7] |= DBREG_DR7_SET(watchnum, len, access,
592 	    DBREG_DR7_GLOBAL_ENABLE);
593 
594 	return (watchnum);
595 }
596 
597 
598 int
599 amd64_clr_watch(watchnum, d)
600 	int watchnum;
601 	struct dbreg *d;
602 {
603 
604 	if (watchnum < 0 || watchnum >= 4)
605 		return (-1);
606 
607 	d->dr[7] &= ~DBREG_DR7_MASK(watchnum);
608 	DBREG_DRX(d, watchnum) = 0;
609 
610 	return (0);
611 }
612 
613 
614 int
615 db_md_set_watchpoint(addr, size)
616 	db_expr_t addr;
617 	db_expr_t size;
618 {
619 	struct dbreg d;
620 	int avail, i, wsize;
621 
622 	fill_dbregs(NULL, &d);
623 
624 	avail = 0;
625 	for(i = 0; i < 4; i++) {
626 		if (!DBREG_DR7_ENABLED(d.dr[7], i))
627 			avail++;
628 	}
629 
630 	if (avail * 8 < size)
631 		return (-1);
632 
633 	for (i = 0; i < 4 && (size > 0); i++) {
634 		if (!DBREG_DR7_ENABLED(d.dr[7], i)) {
635 			if (size >= 8 || (avail == 1 && size > 4))
636 				wsize = 8;
637 			else if (size > 2)
638 				wsize = 4;
639 			else
640 				wsize = size;
641 			amd64_set_watch(i, addr, wsize,
642 				       DBREG_DR7_WRONLY, &d);
643 			addr += wsize;
644 			size -= wsize;
645 			avail--;
646 		}
647 	}
648 
649 	set_dbregs(NULL, &d);
650 
651 	return(0);
652 }
653 
654 
655 int
656 db_md_clr_watchpoint(addr, size)
657 	db_expr_t addr;
658 	db_expr_t size;
659 {
660 	struct dbreg d;
661 	int i;
662 
663 	fill_dbregs(NULL, &d);
664 
665 	for(i = 0; i < 4; i++) {
666 		if (DBREG_DR7_ENABLED(d.dr[7], i)) {
667 			if ((DBREG_DRX((&d), i) >= addr) &&
668 			    (DBREG_DRX((&d), i) < addr+size))
669 				amd64_clr_watch(i, &d);
670 
671 		}
672 	}
673 
674 	set_dbregs(NULL, &d);
675 
676 	return(0);
677 }
678 
679 
680 static const char *
681 watchtype_str(type)
682 	int type;
683 {
684 	switch (type) {
685 		case DBREG_DR7_EXEC   : return "execute";    break;
686 		case DBREG_DR7_RDWR   : return "read/write"; break;
687 		case DBREG_DR7_WRONLY : return "write";	     break;
688 		default		      : return "invalid";    break;
689 	}
690 }
691 
692 
693 void
694 db_md_list_watchpoints()
695 {
696 	struct dbreg d;
697 	int i, len, type;
698 
699 	fill_dbregs(NULL, &d);
700 
701 	db_printf("\nhardware watchpoints:\n");
702 	db_printf("  watch    status        type  len             address\n");
703 	db_printf("  -----  --------  ----------  ---  ------------------\n");
704 	for (i = 0; i < 4; i++) {
705 		if (DBREG_DR7_ENABLED(d.dr[7], i)) {
706 			type = DBREG_DR7_ACCESS(d.dr[7], i);
707 			len = DBREG_DR7_LEN(d.dr[7], i);
708 			if (len == DBREG_DR7_LEN_8)
709 				len = 8;
710 			else
711 				len++;
712 			db_printf("  %-5d  %-8s  %10s  %3d  ",
713 			    i, "enabled", watchtype_str(type), len);
714 			db_printsym((db_addr_t)DBREG_DRX((&d), i), DB_STGY_ANY);
715 			db_printf("\n");
716 		} else {
717 			db_printf("  %-5d  disabled\n", i);
718 		}
719 	}
720 
721 	db_printf("\ndebug register values:\n");
722 	for (i = 0; i < 8; i++) {
723 		db_printf("  dr%d 0x%016lx\n", i, DBREG_DRX((&d), i));
724 	}
725 	db_printf("\n");
726 }
727