xref: /freebsd/sys/ddb/db_break.c (revision cd508278c11d046bf7ce79ca39f6aabeb8c77f36)
1dd3cb568SWarner Losh /*-
25b81b6b3SRodney W. Grimes  * Mach Operating System
35b81b6b3SRodney W. Grimes  * Copyright (c) 1991,1990 Carnegie Mellon University
45b81b6b3SRodney W. Grimes  * All Rights Reserved.
55b81b6b3SRodney W. Grimes  *
65b81b6b3SRodney W. Grimes  * Permission to use, copy, modify and distribute this software and its
75b81b6b3SRodney W. Grimes  * documentation is hereby granted, provided that both the copyright
85b81b6b3SRodney W. Grimes  * notice and this permission notice appear in all copies of the
95b81b6b3SRodney W. Grimes  * software, derivative works or modified versions, and any portions
105b81b6b3SRodney W. Grimes  * thereof, and that both notices appear in supporting documentation.
115b81b6b3SRodney W. Grimes  *
125b81b6b3SRodney W. Grimes  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
135b81b6b3SRodney W. Grimes  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
145b81b6b3SRodney W. Grimes  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
155b81b6b3SRodney W. Grimes  *
165b81b6b3SRodney W. Grimes  * Carnegie Mellon requests users of this software to return to
175b81b6b3SRodney W. Grimes  *
185b81b6b3SRodney W. Grimes  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
195b81b6b3SRodney W. Grimes  *  School of Computer Science
205b81b6b3SRodney W. Grimes  *  Carnegie Mellon University
215b81b6b3SRodney W. Grimes  *  Pittsburgh PA 15213-3890
225b81b6b3SRodney W. Grimes  *
235b81b6b3SRodney W. Grimes  * any improvements or extensions that they make and grant Carnegie the
245b81b6b3SRodney W. Grimes  * rights to redistribute these changes.
250edf66ecSRodney W. Grimes  *
265b81b6b3SRodney W. Grimes  */
275b81b6b3SRodney W. Grimes /*
285b81b6b3SRodney W. Grimes  *	Author: David B. Golub, Carnegie Mellon University
295b81b6b3SRodney W. Grimes  *	Date:	7/90
305b81b6b3SRodney W. Grimes  */
315b81b6b3SRodney W. Grimes /*
325b81b6b3SRodney W. Grimes  * Breakpoints.
335b81b6b3SRodney W. Grimes  */
34753960f7SDavid E. O'Brien 
35753960f7SDavid E. O'Brien #include <sys/cdefs.h>
36753960f7SDavid E. O'Brien __FBSDID("$FreeBSD$");
37753960f7SDavid E. O'Brien 
38f540b106SGarrett Wollman #include <sys/param.h>
395b81b6b3SRodney W. Grimes 
405ccbc3ccSBruce Evans #include <vm/vm.h>
415ccbc3ccSBruce Evans #include <vm/vm_kern.h>
425ccbc3ccSBruce Evans 
435ccbc3ccSBruce Evans #include <ddb/ddb.h>
445b81b6b3SRodney W. Grimes #include <ddb/db_break.h>
455b81b6b3SRodney W. Grimes #include <ddb/db_access.h>
465b81b6b3SRodney W. Grimes #include <ddb/db_sym.h>
475b81b6b3SRodney W. Grimes 
485b81b6b3SRodney W. Grimes #define	NBREAKPOINTS	100
4925eb640dSPoul-Henning Kamp static struct db_breakpoint	db_break_table[NBREAKPOINTS];
50f73a856dSPoul-Henning Kamp static db_breakpoint_t		db_next_free_breakpoint = &db_break_table[0];
51f73a856dSPoul-Henning Kamp static db_breakpoint_t		db_free_breakpoints = 0;
52f73a856dSPoul-Henning Kamp static db_breakpoint_t		db_breakpoint_list = 0;
535b81b6b3SRodney W. Grimes 
5414e10f99SAlfred Perlstein static db_breakpoint_t	db_breakpoint_alloc(void);
5514e10f99SAlfred Perlstein static void	db_breakpoint_free(db_breakpoint_t bkpt);
5614e10f99SAlfred Perlstein static void	db_delete_breakpoint(vm_map_t map, db_addr_t addr);
5714e10f99SAlfred Perlstein static db_breakpoint_t	db_find_breakpoint(vm_map_t map, db_addr_t addr);
5814e10f99SAlfred Perlstein static void	db_list_breakpoints(void);
5914e10f99SAlfred Perlstein static void	db_set_breakpoint(vm_map_t map, db_addr_t addr, int count);
60f73a856dSPoul-Henning Kamp 
61f73a856dSPoul-Henning Kamp static db_breakpoint_t
62a41dd031SPedro F. Giffuni db_breakpoint_alloc(void)
635b81b6b3SRodney W. Grimes {
645b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
655b81b6b3SRodney W. Grimes 
665b81b6b3SRodney W. Grimes 	if ((bkpt = db_free_breakpoints) != 0) {
675b81b6b3SRodney W. Grimes 	    db_free_breakpoints = bkpt->link;
685b81b6b3SRodney W. Grimes 	    return (bkpt);
695b81b6b3SRodney W. Grimes 	}
705b81b6b3SRodney W. Grimes 	if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) {
715b81b6b3SRodney W. Grimes 	    db_printf("All breakpoints used.\n");
725b81b6b3SRodney W. Grimes 	    return (0);
735b81b6b3SRodney W. Grimes 	}
745b81b6b3SRodney W. Grimes 	bkpt = db_next_free_breakpoint;
755b81b6b3SRodney W. Grimes 	db_next_free_breakpoint++;
765b81b6b3SRodney W. Grimes 
775b81b6b3SRodney W. Grimes 	return (bkpt);
785b81b6b3SRodney W. Grimes }
795b81b6b3SRodney W. Grimes 
80f73a856dSPoul-Henning Kamp static void
81a41dd031SPedro F. Giffuni db_breakpoint_free(db_breakpoint_t bkpt)
825b81b6b3SRodney W. Grimes {
835b81b6b3SRodney W. Grimes 	bkpt->link = db_free_breakpoints;
845b81b6b3SRodney W. Grimes 	db_free_breakpoints = bkpt;
855b81b6b3SRodney W. Grimes }
865b81b6b3SRodney W. Grimes 
87f73a856dSPoul-Henning Kamp static void
88a41dd031SPedro F. Giffuni db_set_breakpoint(vm_map_t map, db_addr_t addr, int count)
895b81b6b3SRodney W. Grimes {
905b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
915b81b6b3SRodney W. Grimes 
925b81b6b3SRodney W. Grimes 	if (db_find_breakpoint(map, addr)) {
935b81b6b3SRodney W. Grimes 	    db_printf("Already set.\n");
945b81b6b3SRodney W. Grimes 	    return;
955b81b6b3SRodney W. Grimes 	}
965b81b6b3SRodney W. Grimes 
975b81b6b3SRodney W. Grimes 	bkpt = db_breakpoint_alloc();
985b81b6b3SRodney W. Grimes 	if (bkpt == 0) {
995b81b6b3SRodney W. Grimes 	    db_printf("Too many breakpoints.\n");
1005b81b6b3SRodney W. Grimes 	    return;
1015b81b6b3SRodney W. Grimes 	}
1025b81b6b3SRodney W. Grimes 
1035b81b6b3SRodney W. Grimes 	bkpt->map = map;
1045b81b6b3SRodney W. Grimes 	bkpt->address = addr;
1055b81b6b3SRodney W. Grimes 	bkpt->flags = 0;
1065b81b6b3SRodney W. Grimes 	bkpt->init_count = count;
1075b81b6b3SRodney W. Grimes 	bkpt->count = count;
1085b81b6b3SRodney W. Grimes 
1095b81b6b3SRodney W. Grimes 	bkpt->link = db_breakpoint_list;
1105b81b6b3SRodney W. Grimes 	db_breakpoint_list = bkpt;
1115b81b6b3SRodney W. Grimes }
1125b81b6b3SRodney W. Grimes 
113f73a856dSPoul-Henning Kamp static void
114a41dd031SPedro F. Giffuni db_delete_breakpoint(vm_map_t map, db_addr_t addr)
1155b81b6b3SRodney W. Grimes {
1165b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
1175b81b6b3SRodney W. Grimes 	register db_breakpoint_t	*prev;
1185b81b6b3SRodney W. Grimes 
1195b81b6b3SRodney W. Grimes 	for (prev = &db_breakpoint_list;
1205b81b6b3SRodney W. Grimes 	     (bkpt = *prev) != 0;
1215b81b6b3SRodney W. Grimes 	     prev = &bkpt->link) {
1225b81b6b3SRodney W. Grimes 	    if (db_map_equal(bkpt->map, map) &&
1235b81b6b3SRodney W. Grimes 		(bkpt->address == addr)) {
1245b81b6b3SRodney W. Grimes 		*prev = bkpt->link;
1255b81b6b3SRodney W. Grimes 		break;
1265b81b6b3SRodney W. Grimes 	    }
1275b81b6b3SRodney W. Grimes 	}
1285b81b6b3SRodney W. Grimes 	if (bkpt == 0) {
1295b81b6b3SRodney W. Grimes 	    db_printf("Not set.\n");
1305b81b6b3SRodney W. Grimes 	    return;
1315b81b6b3SRodney W. Grimes 	}
1325b81b6b3SRodney W. Grimes 
1335b81b6b3SRodney W. Grimes 	db_breakpoint_free(bkpt);
1345b81b6b3SRodney W. Grimes }
1355b81b6b3SRodney W. Grimes 
136f73a856dSPoul-Henning Kamp static db_breakpoint_t
137a41dd031SPedro F. Giffuni db_find_breakpoint(vm_map_t map, db_addr_t addr)
1385b81b6b3SRodney W. Grimes {
1395b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
1405b81b6b3SRodney W. Grimes 
1415b81b6b3SRodney W. Grimes 	for (bkpt = db_breakpoint_list;
1425b81b6b3SRodney W. Grimes 	     bkpt != 0;
1435b81b6b3SRodney W. Grimes 	     bkpt = bkpt->link)
1445b81b6b3SRodney W. Grimes 	{
1455b81b6b3SRodney W. Grimes 	    if (db_map_equal(bkpt->map, map) &&
1465b81b6b3SRodney W. Grimes 		(bkpt->address == addr))
1475b81b6b3SRodney W. Grimes 		return (bkpt);
1485b81b6b3SRodney W. Grimes 	}
1495b81b6b3SRodney W. Grimes 	return (0);
1505b81b6b3SRodney W. Grimes }
1515b81b6b3SRodney W. Grimes 
1525b81b6b3SRodney W. Grimes db_breakpoint_t
153a41dd031SPedro F. Giffuni db_find_breakpoint_here(db_addr_t addr)
1545b81b6b3SRodney W. Grimes {
1555b81b6b3SRodney W. Grimes 	return db_find_breakpoint(db_map_addr(addr), addr);
1565b81b6b3SRodney W. Grimes }
1575b81b6b3SRodney W. Grimes 
158*cd508278SPedro F. Giffuni static bool	db_breakpoints_inserted = true;
1595b81b6b3SRodney W. Grimes 
1603a0b4f25SDoug Rabson #ifndef BKPT_WRITE
1613a0b4f25SDoug Rabson #define	BKPT_WRITE(addr, storage)				\
1623a0b4f25SDoug Rabson do {								\
1632b490bc7SPedro F. Giffuni 	*storage = db_get_value(addr, BKPT_SIZE, false);	\
1643a0b4f25SDoug Rabson 	db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage));	\
1653a0b4f25SDoug Rabson } while (0)
1663a0b4f25SDoug Rabson #endif
1673a0b4f25SDoug Rabson 
1683a0b4f25SDoug Rabson #ifndef BKPT_CLEAR
1693a0b4f25SDoug Rabson #define	BKPT_CLEAR(addr, storage) \
1703a0b4f25SDoug Rabson 	db_put_value(addr, BKPT_SIZE, *storage)
1713a0b4f25SDoug Rabson #endif
1723a0b4f25SDoug Rabson 
1735b81b6b3SRodney W. Grimes void
174a41dd031SPedro F. Giffuni db_set_breakpoints(void)
1755b81b6b3SRodney W. Grimes {
1765b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
1775b81b6b3SRodney W. Grimes 
1785b81b6b3SRodney W. Grimes 	if (!db_breakpoints_inserted) {
1795b81b6b3SRodney W. Grimes 
1805b81b6b3SRodney W. Grimes 		for (bkpt = db_breakpoint_list;
1815b81b6b3SRodney W. Grimes 		     bkpt != 0;
1825b81b6b3SRodney W. Grimes 		     bkpt = bkpt->link)
1835b81b6b3SRodney W. Grimes 			if (db_map_current(bkpt->map)) {
1843a0b4f25SDoug Rabson 				BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
1855b81b6b3SRodney W. Grimes 			}
1862b490bc7SPedro F. Giffuni 		db_breakpoints_inserted = true;
1875b81b6b3SRodney W. Grimes 	}
1885b81b6b3SRodney W. Grimes }
1895b81b6b3SRodney W. Grimes 
1905b81b6b3SRodney W. Grimes void
191a41dd031SPedro F. Giffuni db_clear_breakpoints(void)
1925b81b6b3SRodney W. Grimes {
1935b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
1945b81b6b3SRodney W. Grimes 
1955b81b6b3SRodney W. Grimes 	if (db_breakpoints_inserted) {
1965b81b6b3SRodney W. Grimes 
1975b81b6b3SRodney W. Grimes 		for (bkpt = db_breakpoint_list;
1985b81b6b3SRodney W. Grimes 		     bkpt != 0;
1995b81b6b3SRodney W. Grimes 		     bkpt = bkpt->link)
2005b81b6b3SRodney W. Grimes 			if (db_map_current(bkpt->map)) {
2013a0b4f25SDoug Rabson 				BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
2025b81b6b3SRodney W. Grimes 			}
2032b490bc7SPedro F. Giffuni 		db_breakpoints_inserted = false;
2045b81b6b3SRodney W. Grimes 	}
2055b81b6b3SRodney W. Grimes }
2065b81b6b3SRodney W. Grimes 
207ecbb00a2SDoug Rabson #ifdef SOFTWARE_SSTEP
2085b81b6b3SRodney W. Grimes /*
2095b81b6b3SRodney W. Grimes  * Set a temporary breakpoint.
2105b81b6b3SRodney W. Grimes  * The instruction is changed immediately,
2115b81b6b3SRodney W. Grimes  * so the breakpoint does not have to be on the breakpoint list.
2125b81b6b3SRodney W. Grimes  */
213ecbb00a2SDoug Rabson db_breakpoint_t
214a41dd031SPedro F. Giffuni db_set_temp_breakpoint(db_addr_t addr)
2155b81b6b3SRodney W. Grimes {
2165b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
2175b81b6b3SRodney W. Grimes 
2185b81b6b3SRodney W. Grimes 	bkpt = db_breakpoint_alloc();
2195b81b6b3SRodney W. Grimes 	if (bkpt == 0) {
2205b81b6b3SRodney W. Grimes 	    db_printf("Too many breakpoints.\n");
2215b81b6b3SRodney W. Grimes 	    return 0;
2225b81b6b3SRodney W. Grimes 	}
2235b81b6b3SRodney W. Grimes 
2245b81b6b3SRodney W. Grimes 	bkpt->map = NULL;
2255b81b6b3SRodney W. Grimes 	bkpt->address = addr;
2265b81b6b3SRodney W. Grimes 	bkpt->flags = BKPT_TEMP;
2275b81b6b3SRodney W. Grimes 	bkpt->init_count = 1;
2285b81b6b3SRodney W. Grimes 	bkpt->count = 1;
2295b81b6b3SRodney W. Grimes 
2303a0b4f25SDoug Rabson 	BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
2315b81b6b3SRodney W. Grimes 	return bkpt;
2325b81b6b3SRodney W. Grimes }
2335b81b6b3SRodney W. Grimes 
234ecbb00a2SDoug Rabson void
235a41dd031SPedro F. Giffuni db_delete_temp_breakpoint(db_breakpoint_t bkpt)
2365b81b6b3SRodney W. Grimes {
2373a0b4f25SDoug Rabson 	BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
2385b81b6b3SRodney W. Grimes 	db_breakpoint_free(bkpt);
2395b81b6b3SRodney W. Grimes }
2404204c166SBruce Evans #endif /* SOFTWARE_SSTEP */
2415b81b6b3SRodney W. Grimes 
2425b81b6b3SRodney W. Grimes /*
2435b81b6b3SRodney W. Grimes  * List breakpoints.
2445b81b6b3SRodney W. Grimes  */
245f73a856dSPoul-Henning Kamp static void
246a41dd031SPedro F. Giffuni db_list_breakpoints(void)
2475b81b6b3SRodney W. Grimes {
2485b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
2495b81b6b3SRodney W. Grimes 
2505b81b6b3SRodney W. Grimes 	if (db_breakpoint_list == 0) {
2515b81b6b3SRodney W. Grimes 	    db_printf("No breakpoints set\n");
2525b81b6b3SRodney W. Grimes 	    return;
2535b81b6b3SRodney W. Grimes 	}
2545b81b6b3SRodney W. Grimes 
2555b81b6b3SRodney W. Grimes 	db_printf(" Map      Count    Address\n");
2565b81b6b3SRodney W. Grimes 	for (bkpt = db_breakpoint_list;
2575b81b6b3SRodney W. Grimes 	     bkpt != 0;
25890163a9bSBruce Evans 	     bkpt = bkpt->link) {
25990163a9bSBruce Evans 	    db_printf("%s%8p %5d    ",
2605b81b6b3SRodney W. Grimes 		      db_map_current(bkpt->map) ? "*" : " ",
26190163a9bSBruce Evans 		      (void *)bkpt->map, bkpt->init_count);
2625b81b6b3SRodney W. Grimes 	    db_printsym(bkpt->address, DB_STGY_PROC);
2635b81b6b3SRodney W. Grimes 	    db_printf("\n");
2645b81b6b3SRodney W. Grimes 	}
2655b81b6b3SRodney W. Grimes }
2665b81b6b3SRodney W. Grimes 
2675b81b6b3SRodney W. Grimes /* Delete breakpoint */
2685b81b6b3SRodney W. Grimes /*ARGSUSED*/
2695b81b6b3SRodney W. Grimes void
270*cd508278SPedro F. Giffuni db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
2715b81b6b3SRodney W. Grimes {
2725b81b6b3SRodney W. Grimes 	db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr);
2735b81b6b3SRodney W. Grimes }
2745b81b6b3SRodney W. Grimes 
2755b81b6b3SRodney W. Grimes /* Set breakpoint with skip count */
2765b81b6b3SRodney W. Grimes /*ARGSUSED*/
2775b81b6b3SRodney W. Grimes void
278*cd508278SPedro F. Giffuni db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
2795b81b6b3SRodney W. Grimes {
2805b81b6b3SRodney W. Grimes 	if (count == -1)
2815b81b6b3SRodney W. Grimes 	    count = 1;
2825b81b6b3SRodney W. Grimes 
2835b81b6b3SRodney W. Grimes 	db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
2845b81b6b3SRodney W. Grimes }
2855b81b6b3SRodney W. Grimes 
2865b81b6b3SRodney W. Grimes /* list breakpoints */
2875b81b6b3SRodney W. Grimes void
288*cd508278SPedro F. Giffuni db_listbreak_cmd(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
2895b81b6b3SRodney W. Grimes {
2905b81b6b3SRodney W. Grimes 	db_list_breakpoints();
2915b81b6b3SRodney W. Grimes }
2925b81b6b3SRodney W. Grimes 
2935b81b6b3SRodney W. Grimes /*
2945b81b6b3SRodney W. Grimes  *	We want ddb to be usable before most of the kernel has been
2955b81b6b3SRodney W. Grimes  *	initialized.  In particular, current_thread() or kernel_map
2965b81b6b3SRodney W. Grimes  *	(or both) may be null.
2975b81b6b3SRodney W. Grimes  */
2985b81b6b3SRodney W. Grimes 
299*cd508278SPedro F. Giffuni bool
300a41dd031SPedro F. Giffuni db_map_equal(vm_map_t map1, vm_map_t map2)
3015b81b6b3SRodney W. Grimes {
3025b81b6b3SRodney W. Grimes 	return ((map1 == map2) ||
3035b81b6b3SRodney W. Grimes 		((map1 == NULL) && (map2 == kernel_map)) ||
3045b81b6b3SRodney W. Grimes 		((map1 == kernel_map) && (map2 == NULL)));
3055b81b6b3SRodney W. Grimes }
3065b81b6b3SRodney W. Grimes 
307*cd508278SPedro F. Giffuni bool
308a41dd031SPedro F. Giffuni db_map_current(vm_map_t map)
3095b81b6b3SRodney W. Grimes {
3105b81b6b3SRodney W. Grimes #if 0
3115b81b6b3SRodney W. Grimes 	thread_t	thread;
3125b81b6b3SRodney W. Grimes 
3135b81b6b3SRodney W. Grimes 	return ((map == NULL) ||
3145b81b6b3SRodney W. Grimes 		(map == kernel_map) ||
3155b81b6b3SRodney W. Grimes 		(((thread = current_thread()) != NULL) &&
3165b81b6b3SRodney W. Grimes 		 (map == thread->task->map)));
3175b81b6b3SRodney W. Grimes #else
318*cd508278SPedro F. Giffuni 	return (true);
3195b81b6b3SRodney W. Grimes #endif
3205b81b6b3SRodney W. Grimes }
3215b81b6b3SRodney W. Grimes 
3225b81b6b3SRodney W. Grimes vm_map_t
323a41dd031SPedro F. Giffuni db_map_addr(vm_offset_t addr)
3245b81b6b3SRodney W. Grimes {
3255b81b6b3SRodney W. Grimes #if 0
3265b81b6b3SRodney W. Grimes 	thread_t	thread;
3275b81b6b3SRodney W. Grimes 
3285b81b6b3SRodney W. Grimes 	/*
3295b81b6b3SRodney W. Grimes 	 *	We want to return kernel_map for all
3305b81b6b3SRodney W. Grimes 	 *	non-user addresses, even when debugging
3315b81b6b3SRodney W. Grimes 	 *	kernel tasks with their own maps.
3325b81b6b3SRodney W. Grimes 	 */
3335b81b6b3SRodney W. Grimes 
3345b81b6b3SRodney W. Grimes 	if ((VM_MIN_ADDRESS <= addr) &&
3355b81b6b3SRodney W. Grimes 	    (addr < VM_MAX_ADDRESS) &&
3365b81b6b3SRodney W. Grimes 	    ((thread = current_thread()) != NULL))
3375b81b6b3SRodney W. Grimes 	    return thread->task->map;
3385b81b6b3SRodney W. Grimes 	else
3395b81b6b3SRodney W. Grimes #endif
3405b81b6b3SRodney W. Grimes 	    return kernel_map;
3415b81b6b3SRodney W. Grimes }
342