xref: /freebsd/sys/ddb/db_break.c (revision 796df753f404f4772ae0fd7da0e1d2b948268448)
1dd3cb568SWarner Losh /*-
2*796df753SPedro F. Giffuni  * SPDX-License-Identifier: MIT-CMU
3*796df753SPedro F. Giffuni  *
45b81b6b3SRodney W. Grimes  * Mach Operating System
55b81b6b3SRodney W. Grimes  * Copyright (c) 1991,1990 Carnegie Mellon University
65b81b6b3SRodney W. Grimes  * All Rights Reserved.
75b81b6b3SRodney W. Grimes  *
85b81b6b3SRodney W. Grimes  * Permission to use, copy, modify and distribute this software and its
95b81b6b3SRodney W. Grimes  * documentation is hereby granted, provided that both the copyright
105b81b6b3SRodney W. Grimes  * notice and this permission notice appear in all copies of the
115b81b6b3SRodney W. Grimes  * software, derivative works or modified versions, and any portions
125b81b6b3SRodney W. Grimes  * thereof, and that both notices appear in supporting documentation.
135b81b6b3SRodney W. Grimes  *
145b81b6b3SRodney W. Grimes  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
155b81b6b3SRodney W. Grimes  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
165b81b6b3SRodney W. Grimes  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
175b81b6b3SRodney W. Grimes  *
185b81b6b3SRodney W. Grimes  * Carnegie Mellon requests users of this software to return to
195b81b6b3SRodney W. Grimes  *
205b81b6b3SRodney W. Grimes  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
215b81b6b3SRodney W. Grimes  *  School of Computer Science
225b81b6b3SRodney W. Grimes  *  Carnegie Mellon University
235b81b6b3SRodney W. Grimes  *  Pittsburgh PA 15213-3890
245b81b6b3SRodney W. Grimes  *
255b81b6b3SRodney W. Grimes  * any improvements or extensions that they make and grant Carnegie the
265b81b6b3SRodney W. Grimes  * rights to redistribute these changes.
270edf66ecSRodney W. Grimes  *
285b81b6b3SRodney W. Grimes  */
295b81b6b3SRodney W. Grimes /*
305b81b6b3SRodney W. Grimes  *	Author: David B. Golub, Carnegie Mellon University
315b81b6b3SRodney W. Grimes  *	Date:	7/90
325b81b6b3SRodney W. Grimes  */
335b81b6b3SRodney W. Grimes /*
345b81b6b3SRodney W. Grimes  * Breakpoints.
355b81b6b3SRodney W. Grimes  */
36753960f7SDavid E. O'Brien 
37753960f7SDavid E. O'Brien #include <sys/cdefs.h>
38753960f7SDavid E. O'Brien __FBSDID("$FreeBSD$");
39753960f7SDavid E. O'Brien 
40f540b106SGarrett Wollman #include <sys/param.h>
415b81b6b3SRodney W. Grimes 
425ccbc3ccSBruce Evans #include <vm/vm.h>
435ccbc3ccSBruce Evans #include <vm/vm_kern.h>
445ccbc3ccSBruce Evans 
455ccbc3ccSBruce Evans #include <ddb/ddb.h>
465b81b6b3SRodney W. Grimes #include <ddb/db_break.h>
475b81b6b3SRodney W. Grimes #include <ddb/db_access.h>
485b81b6b3SRodney W. Grimes #include <ddb/db_sym.h>
495b81b6b3SRodney W. Grimes 
505b81b6b3SRodney W. Grimes #define	NBREAKPOINTS	100
5125eb640dSPoul-Henning Kamp static struct db_breakpoint	db_break_table[NBREAKPOINTS];
52f73a856dSPoul-Henning Kamp static db_breakpoint_t		db_next_free_breakpoint = &db_break_table[0];
53f73a856dSPoul-Henning Kamp static db_breakpoint_t		db_free_breakpoints = 0;
54f73a856dSPoul-Henning Kamp static db_breakpoint_t		db_breakpoint_list = 0;
555b81b6b3SRodney W. Grimes 
5614e10f99SAlfred Perlstein static db_breakpoint_t	db_breakpoint_alloc(void);
5714e10f99SAlfred Perlstein static void	db_breakpoint_free(db_breakpoint_t bkpt);
5814e10f99SAlfred Perlstein static void	db_delete_breakpoint(vm_map_t map, db_addr_t addr);
5914e10f99SAlfred Perlstein static db_breakpoint_t	db_find_breakpoint(vm_map_t map, db_addr_t addr);
6014e10f99SAlfred Perlstein static void	db_list_breakpoints(void);
6114e10f99SAlfred Perlstein static void	db_set_breakpoint(vm_map_t map, db_addr_t addr, int count);
62f73a856dSPoul-Henning Kamp 
63f73a856dSPoul-Henning Kamp static db_breakpoint_t
64a41dd031SPedro F. Giffuni db_breakpoint_alloc(void)
655b81b6b3SRodney W. Grimes {
665b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
675b81b6b3SRodney W. Grimes 
685b81b6b3SRodney W. Grimes 	if ((bkpt = db_free_breakpoints) != 0) {
695b81b6b3SRodney W. Grimes 	    db_free_breakpoints = bkpt->link;
705b81b6b3SRodney W. Grimes 	    return (bkpt);
715b81b6b3SRodney W. Grimes 	}
725b81b6b3SRodney W. Grimes 	if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) {
735b81b6b3SRodney W. Grimes 	    db_printf("All breakpoints used.\n");
745b81b6b3SRodney W. Grimes 	    return (0);
755b81b6b3SRodney W. Grimes 	}
765b81b6b3SRodney W. Grimes 	bkpt = db_next_free_breakpoint;
775b81b6b3SRodney W. Grimes 	db_next_free_breakpoint++;
785b81b6b3SRodney W. Grimes 
795b81b6b3SRodney W. Grimes 	return (bkpt);
805b81b6b3SRodney W. Grimes }
815b81b6b3SRodney W. Grimes 
82f73a856dSPoul-Henning Kamp static void
83a41dd031SPedro F. Giffuni db_breakpoint_free(db_breakpoint_t bkpt)
845b81b6b3SRodney W. Grimes {
855b81b6b3SRodney W. Grimes 	bkpt->link = db_free_breakpoints;
865b81b6b3SRodney W. Grimes 	db_free_breakpoints = bkpt;
875b81b6b3SRodney W. Grimes }
885b81b6b3SRodney W. Grimes 
89f73a856dSPoul-Henning Kamp static void
90a41dd031SPedro F. Giffuni db_set_breakpoint(vm_map_t map, db_addr_t addr, int count)
915b81b6b3SRodney W. Grimes {
925b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
935b81b6b3SRodney W. Grimes 
945b81b6b3SRodney W. Grimes 	if (db_find_breakpoint(map, addr)) {
955b81b6b3SRodney W. Grimes 	    db_printf("Already set.\n");
965b81b6b3SRodney W. Grimes 	    return;
975b81b6b3SRodney W. Grimes 	}
985b81b6b3SRodney W. Grimes 
995b81b6b3SRodney W. Grimes 	bkpt = db_breakpoint_alloc();
1005b81b6b3SRodney W. Grimes 	if (bkpt == 0) {
1015b81b6b3SRodney W. Grimes 	    db_printf("Too many breakpoints.\n");
1025b81b6b3SRodney W. Grimes 	    return;
1035b81b6b3SRodney W. Grimes 	}
1045b81b6b3SRodney W. Grimes 
1055b81b6b3SRodney W. Grimes 	bkpt->map = map;
1065b81b6b3SRodney W. Grimes 	bkpt->address = addr;
1075b81b6b3SRodney W. Grimes 	bkpt->flags = 0;
1085b81b6b3SRodney W. Grimes 	bkpt->init_count = count;
1095b81b6b3SRodney W. Grimes 	bkpt->count = count;
1105b81b6b3SRodney W. Grimes 
1115b81b6b3SRodney W. Grimes 	bkpt->link = db_breakpoint_list;
1125b81b6b3SRodney W. Grimes 	db_breakpoint_list = bkpt;
1135b81b6b3SRodney W. Grimes }
1145b81b6b3SRodney W. Grimes 
115f73a856dSPoul-Henning Kamp static void
116a41dd031SPedro F. Giffuni db_delete_breakpoint(vm_map_t map, db_addr_t addr)
1175b81b6b3SRodney W. Grimes {
1185b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
1195b81b6b3SRodney W. Grimes 	register db_breakpoint_t	*prev;
1205b81b6b3SRodney W. Grimes 
1215b81b6b3SRodney W. Grimes 	for (prev = &db_breakpoint_list;
1225b81b6b3SRodney W. Grimes 	     (bkpt = *prev) != 0;
1235b81b6b3SRodney W. Grimes 	     prev = &bkpt->link) {
1245b81b6b3SRodney W. Grimes 	    if (db_map_equal(bkpt->map, map) &&
1255b81b6b3SRodney W. Grimes 		(bkpt->address == addr)) {
1265b81b6b3SRodney W. Grimes 		*prev = bkpt->link;
1275b81b6b3SRodney W. Grimes 		break;
1285b81b6b3SRodney W. Grimes 	    }
1295b81b6b3SRodney W. Grimes 	}
1305b81b6b3SRodney W. Grimes 	if (bkpt == 0) {
1315b81b6b3SRodney W. Grimes 	    db_printf("Not set.\n");
1325b81b6b3SRodney W. Grimes 	    return;
1335b81b6b3SRodney W. Grimes 	}
1345b81b6b3SRodney W. Grimes 
1355b81b6b3SRodney W. Grimes 	db_breakpoint_free(bkpt);
1365b81b6b3SRodney W. Grimes }
1375b81b6b3SRodney W. Grimes 
138f73a856dSPoul-Henning Kamp static db_breakpoint_t
139a41dd031SPedro F. Giffuni db_find_breakpoint(vm_map_t map, db_addr_t addr)
1405b81b6b3SRodney W. Grimes {
1415b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
1425b81b6b3SRodney W. Grimes 
1435b81b6b3SRodney W. Grimes 	for (bkpt = db_breakpoint_list;
1445b81b6b3SRodney W. Grimes 	     bkpt != 0;
1455b81b6b3SRodney W. Grimes 	     bkpt = bkpt->link)
1465b81b6b3SRodney W. Grimes 	{
1475b81b6b3SRodney W. Grimes 	    if (db_map_equal(bkpt->map, map) &&
1485b81b6b3SRodney W. Grimes 		(bkpt->address == addr))
1495b81b6b3SRodney W. Grimes 		return (bkpt);
1505b81b6b3SRodney W. Grimes 	}
1515b81b6b3SRodney W. Grimes 	return (0);
1525b81b6b3SRodney W. Grimes }
1535b81b6b3SRodney W. Grimes 
1545b81b6b3SRodney W. Grimes db_breakpoint_t
155a41dd031SPedro F. Giffuni db_find_breakpoint_here(db_addr_t addr)
1565b81b6b3SRodney W. Grimes {
1575b81b6b3SRodney W. Grimes 	return db_find_breakpoint(db_map_addr(addr), addr);
1585b81b6b3SRodney W. Grimes }
1595b81b6b3SRodney W. Grimes 
160cd508278SPedro F. Giffuni static bool	db_breakpoints_inserted = true;
1615b81b6b3SRodney W. Grimes 
1623a0b4f25SDoug Rabson #ifndef BKPT_WRITE
1633a0b4f25SDoug Rabson #define	BKPT_WRITE(addr, storage)				\
1643a0b4f25SDoug Rabson do {								\
1652b490bc7SPedro F. Giffuni 	*storage = db_get_value(addr, BKPT_SIZE, false);	\
1663a0b4f25SDoug Rabson 	db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage));	\
1673a0b4f25SDoug Rabson } while (0)
1683a0b4f25SDoug Rabson #endif
1693a0b4f25SDoug Rabson 
1703a0b4f25SDoug Rabson #ifndef BKPT_CLEAR
1713a0b4f25SDoug Rabson #define	BKPT_CLEAR(addr, storage) \
1723a0b4f25SDoug Rabson 	db_put_value(addr, BKPT_SIZE, *storage)
1733a0b4f25SDoug Rabson #endif
1743a0b4f25SDoug Rabson 
1755b81b6b3SRodney W. Grimes void
176a41dd031SPedro F. Giffuni db_set_breakpoints(void)
1775b81b6b3SRodney W. Grimes {
1785b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
1795b81b6b3SRodney W. Grimes 
1805b81b6b3SRodney W. Grimes 	if (!db_breakpoints_inserted) {
1815b81b6b3SRodney W. Grimes 
1825b81b6b3SRodney W. Grimes 		for (bkpt = db_breakpoint_list;
1835b81b6b3SRodney W. Grimes 		     bkpt != 0;
1845b81b6b3SRodney W. Grimes 		     bkpt = bkpt->link)
1855b81b6b3SRodney W. Grimes 			if (db_map_current(bkpt->map)) {
1863a0b4f25SDoug Rabson 				BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
1875b81b6b3SRodney W. Grimes 			}
1882b490bc7SPedro F. Giffuni 		db_breakpoints_inserted = true;
1895b81b6b3SRodney W. Grimes 	}
1905b81b6b3SRodney W. Grimes }
1915b81b6b3SRodney W. Grimes 
1925b81b6b3SRodney W. Grimes void
193a41dd031SPedro F. Giffuni db_clear_breakpoints(void)
1945b81b6b3SRodney W. Grimes {
1955b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
1965b81b6b3SRodney W. Grimes 
1975b81b6b3SRodney W. Grimes 	if (db_breakpoints_inserted) {
1985b81b6b3SRodney W. Grimes 
1995b81b6b3SRodney W. Grimes 		for (bkpt = db_breakpoint_list;
2005b81b6b3SRodney W. Grimes 		     bkpt != 0;
2015b81b6b3SRodney W. Grimes 		     bkpt = bkpt->link)
2025b81b6b3SRodney W. Grimes 			if (db_map_current(bkpt->map)) {
2033a0b4f25SDoug Rabson 				BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
2045b81b6b3SRodney W. Grimes 			}
2052b490bc7SPedro F. Giffuni 		db_breakpoints_inserted = false;
2065b81b6b3SRodney W. Grimes 	}
2075b81b6b3SRodney W. Grimes }
2085b81b6b3SRodney W. Grimes 
209ecbb00a2SDoug Rabson #ifdef SOFTWARE_SSTEP
2105b81b6b3SRodney W. Grimes /*
2115b81b6b3SRodney W. Grimes  * Set a temporary breakpoint.
2125b81b6b3SRodney W. Grimes  * The instruction is changed immediately,
2135b81b6b3SRodney W. Grimes  * so the breakpoint does not have to be on the breakpoint list.
2145b81b6b3SRodney W. Grimes  */
215ecbb00a2SDoug Rabson db_breakpoint_t
216a41dd031SPedro F. Giffuni db_set_temp_breakpoint(db_addr_t addr)
2175b81b6b3SRodney W. Grimes {
2185b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
2195b81b6b3SRodney W. Grimes 
2205b81b6b3SRodney W. Grimes 	bkpt = db_breakpoint_alloc();
2215b81b6b3SRodney W. Grimes 	if (bkpt == 0) {
2225b81b6b3SRodney W. Grimes 	    db_printf("Too many breakpoints.\n");
2235b81b6b3SRodney W. Grimes 	    return 0;
2245b81b6b3SRodney W. Grimes 	}
2255b81b6b3SRodney W. Grimes 
2265b81b6b3SRodney W. Grimes 	bkpt->map = NULL;
2275b81b6b3SRodney W. Grimes 	bkpt->address = addr;
2285b81b6b3SRodney W. Grimes 	bkpt->flags = BKPT_TEMP;
2295b81b6b3SRodney W. Grimes 	bkpt->init_count = 1;
2305b81b6b3SRodney W. Grimes 	bkpt->count = 1;
2315b81b6b3SRodney W. Grimes 
2323a0b4f25SDoug Rabson 	BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
2335b81b6b3SRodney W. Grimes 	return bkpt;
2345b81b6b3SRodney W. Grimes }
2355b81b6b3SRodney W. Grimes 
236ecbb00a2SDoug Rabson void
237a41dd031SPedro F. Giffuni db_delete_temp_breakpoint(db_breakpoint_t bkpt)
2385b81b6b3SRodney W. Grimes {
2393a0b4f25SDoug Rabson 	BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
2405b81b6b3SRodney W. Grimes 	db_breakpoint_free(bkpt);
2415b81b6b3SRodney W. Grimes }
2424204c166SBruce Evans #endif /* SOFTWARE_SSTEP */
2435b81b6b3SRodney W. Grimes 
2445b81b6b3SRodney W. Grimes /*
2455b81b6b3SRodney W. Grimes  * List breakpoints.
2465b81b6b3SRodney W. Grimes  */
247f73a856dSPoul-Henning Kamp static void
248a41dd031SPedro F. Giffuni db_list_breakpoints(void)
2495b81b6b3SRodney W. Grimes {
2505b81b6b3SRodney W. Grimes 	register db_breakpoint_t	bkpt;
2515b81b6b3SRodney W. Grimes 
2525b81b6b3SRodney W. Grimes 	if (db_breakpoint_list == 0) {
2535b81b6b3SRodney W. Grimes 	    db_printf("No breakpoints set\n");
2545b81b6b3SRodney W. Grimes 	    return;
2555b81b6b3SRodney W. Grimes 	}
2565b81b6b3SRodney W. Grimes 
2575b81b6b3SRodney W. Grimes 	db_printf(" Map      Count    Address\n");
2585b81b6b3SRodney W. Grimes 	for (bkpt = db_breakpoint_list;
2595b81b6b3SRodney W. Grimes 	     bkpt != 0;
26090163a9bSBruce Evans 	     bkpt = bkpt->link) {
26190163a9bSBruce Evans 	    db_printf("%s%8p %5d    ",
2625b81b6b3SRodney W. Grimes 		      db_map_current(bkpt->map) ? "*" : " ",
26390163a9bSBruce Evans 		      (void *)bkpt->map, bkpt->init_count);
2645b81b6b3SRodney W. Grimes 	    db_printsym(bkpt->address, DB_STGY_PROC);
2655b81b6b3SRodney W. Grimes 	    db_printf("\n");
2665b81b6b3SRodney W. Grimes 	}
2675b81b6b3SRodney W. Grimes }
2685b81b6b3SRodney W. Grimes 
2695b81b6b3SRodney W. Grimes /* Delete breakpoint */
2705b81b6b3SRodney W. Grimes /*ARGSUSED*/
2715b81b6b3SRodney W. Grimes void
272cd508278SPedro F. Giffuni db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
2735b81b6b3SRodney W. Grimes {
2745b81b6b3SRodney W. Grimes 	db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr);
2755b81b6b3SRodney W. Grimes }
2765b81b6b3SRodney W. Grimes 
2775b81b6b3SRodney W. Grimes /* Set breakpoint with skip count */
2785b81b6b3SRodney W. Grimes /*ARGSUSED*/
2795b81b6b3SRodney W. Grimes void
280cd508278SPedro F. Giffuni db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
2815b81b6b3SRodney W. Grimes {
2825b81b6b3SRodney W. Grimes 	if (count == -1)
2835b81b6b3SRodney W. Grimes 	    count = 1;
2845b81b6b3SRodney W. Grimes 
2855b81b6b3SRodney W. Grimes 	db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
2865b81b6b3SRodney W. Grimes }
2875b81b6b3SRodney W. Grimes 
2885b81b6b3SRodney W. Grimes /* list breakpoints */
2895b81b6b3SRodney W. Grimes void
290cd508278SPedro F. Giffuni db_listbreak_cmd(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
2915b81b6b3SRodney W. Grimes {
2925b81b6b3SRodney W. Grimes 	db_list_breakpoints();
2935b81b6b3SRodney W. Grimes }
2945b81b6b3SRodney W. Grimes 
2955b81b6b3SRodney W. Grimes /*
2965b81b6b3SRodney W. Grimes  *	We want ddb to be usable before most of the kernel has been
2975b81b6b3SRodney W. Grimes  *	initialized.  In particular, current_thread() or kernel_map
2985b81b6b3SRodney W. Grimes  *	(or both) may be null.
2995b81b6b3SRodney W. Grimes  */
3005b81b6b3SRodney W. Grimes 
301cd508278SPedro F. Giffuni bool
302a41dd031SPedro F. Giffuni db_map_equal(vm_map_t map1, vm_map_t map2)
3035b81b6b3SRodney W. Grimes {
3045b81b6b3SRodney W. Grimes 	return ((map1 == map2) ||
3055b81b6b3SRodney W. Grimes 		((map1 == NULL) && (map2 == kernel_map)) ||
3065b81b6b3SRodney W. Grimes 		((map1 == kernel_map) && (map2 == NULL)));
3075b81b6b3SRodney W. Grimes }
3085b81b6b3SRodney W. Grimes 
309cd508278SPedro F. Giffuni bool
310a41dd031SPedro F. Giffuni db_map_current(vm_map_t map)
3115b81b6b3SRodney W. Grimes {
3125b81b6b3SRodney W. Grimes #if 0
3135b81b6b3SRodney W. Grimes 	thread_t	thread;
3145b81b6b3SRodney W. Grimes 
3155b81b6b3SRodney W. Grimes 	return ((map == NULL) ||
3165b81b6b3SRodney W. Grimes 		(map == kernel_map) ||
3175b81b6b3SRodney W. Grimes 		(((thread = current_thread()) != NULL) &&
3185b81b6b3SRodney W. Grimes 		 (map == thread->task->map)));
3195b81b6b3SRodney W. Grimes #else
320cd508278SPedro F. Giffuni 	return (true);
3215b81b6b3SRodney W. Grimes #endif
3225b81b6b3SRodney W. Grimes }
3235b81b6b3SRodney W. Grimes 
3245b81b6b3SRodney W. Grimes vm_map_t
325a41dd031SPedro F. Giffuni db_map_addr(vm_offset_t addr)
3265b81b6b3SRodney W. Grimes {
3275b81b6b3SRodney W. Grimes #if 0
3285b81b6b3SRodney W. Grimes 	thread_t	thread;
3295b81b6b3SRodney W. Grimes 
3305b81b6b3SRodney W. Grimes 	/*
3315b81b6b3SRodney W. Grimes 	 *	We want to return kernel_map for all
3325b81b6b3SRodney W. Grimes 	 *	non-user addresses, even when debugging
3335b81b6b3SRodney W. Grimes 	 *	kernel tasks with their own maps.
3345b81b6b3SRodney W. Grimes 	 */
3355b81b6b3SRodney W. Grimes 
3365b81b6b3SRodney W. Grimes 	if ((VM_MIN_ADDRESS <= addr) &&
3375b81b6b3SRodney W. Grimes 	    (addr < VM_MAX_ADDRESS) &&
3385b81b6b3SRodney W. Grimes 	    ((thread = current_thread()) != NULL))
3395b81b6b3SRodney W. Grimes 	    return thread->task->map;
3405b81b6b3SRodney W. Grimes 	else
3415b81b6b3SRodney W. Grimes #endif
3425b81b6b3SRodney W. Grimes 	    return kernel_map;
3435b81b6b3SRodney W. Grimes }
344