1139747Simp/*-
24Srgrimes * Mach Operating System
34Srgrimes * Copyright (c) 1991,1990 Carnegie Mellon University
44Srgrimes * All Rights Reserved.
58876Srgrimes *
64Srgrimes * Permission to use, copy, modify and distribute this software and its
74Srgrimes * documentation is hereby granted, provided that both the copyright
84Srgrimes * notice and this permission notice appear in all copies of the
94Srgrimes * software, derivative works or modified versions, and any portions
104Srgrimes * thereof, and that both notices appear in supporting documentation.
118876Srgrimes *
128876Srgrimes * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
134Srgrimes * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
144Srgrimes * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
158876Srgrimes *
164Srgrimes * Carnegie Mellon requests users of this software to return to
178876Srgrimes *
184Srgrimes *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
194Srgrimes *  School of Computer Science
204Srgrimes *  Carnegie Mellon University
214Srgrimes *  Pittsburgh PA 15213-3890
228876Srgrimes *
234Srgrimes * any improvements or extensions that they make and grant Carnegie the
244Srgrimes * rights to redistribute these changes.
254Srgrimes *
264Srgrimes */
274Srgrimes/*
284Srgrimes *	Author: David B. Golub, Carnegie Mellon University
294Srgrimes *	Date:	7/90
304Srgrimes */
314Srgrimes/*
324Srgrimes * Breakpoints.
334Srgrimes */
34116176Sobrien
35116176Sobrien#include <sys/cdefs.h>
36116176Sobrien__FBSDID("$FreeBSD$");
37116176Sobrien
382056Swollman#include <sys/param.h>
3912734Sbde
4012662Sdg#include <vm/vm.h>
4112734Sbde#include <vm/vm_kern.h>
4212734Sbde
432056Swollman#include <ddb/ddb.h>
444Srgrimes#include <ddb/db_break.h>
454Srgrimes#include <ddb/db_access.h>
464Srgrimes#include <ddb/db_sym.h>
474Srgrimes
484Srgrimes#define	NBREAKPOINTS	100
4912720Sphkstatic struct db_breakpoint	db_break_table[NBREAKPOINTS];
5012515Sphkstatic db_breakpoint_t		db_next_free_breakpoint = &db_break_table[0];
5112515Sphkstatic db_breakpoint_t		db_free_breakpoints = 0;
5212515Sphkstatic db_breakpoint_t		db_breakpoint_list = 0;
534Srgrimes
5492756Salfredstatic db_breakpoint_t	db_breakpoint_alloc(void);
5592756Salfredstatic void	db_breakpoint_free(db_breakpoint_t bkpt);
5692756Salfredstatic void	db_delete_breakpoint(vm_map_t map, db_addr_t addr);
5792756Salfredstatic db_breakpoint_t	db_find_breakpoint(vm_map_t map, db_addr_t addr);
5892756Salfredstatic void	db_list_breakpoints(void);
5992756Salfredstatic void	db_set_breakpoint(vm_map_t map, db_addr_t addr, int count);
6012515Sphk
6112515Sphkstatic db_breakpoint_t
624Srgrimesdb_breakpoint_alloc()
634Srgrimes{
644Srgrimes	register db_breakpoint_t	bkpt;
654Srgrimes
664Srgrimes	if ((bkpt = db_free_breakpoints) != 0) {
674Srgrimes	    db_free_breakpoints = bkpt->link;
684Srgrimes	    return (bkpt);
694Srgrimes	}
704Srgrimes	if (db_next_free_breakpoint == &db_break_table[NBREAKPOINTS]) {
714Srgrimes	    db_printf("All breakpoints used.\n");
724Srgrimes	    return (0);
734Srgrimes	}
744Srgrimes	bkpt = db_next_free_breakpoint;
754Srgrimes	db_next_free_breakpoint++;
764Srgrimes
774Srgrimes	return (bkpt);
784Srgrimes}
794Srgrimes
8012515Sphkstatic void
814Srgrimesdb_breakpoint_free(bkpt)
824Srgrimes	register db_breakpoint_t	bkpt;
834Srgrimes{
844Srgrimes	bkpt->link = db_free_breakpoints;
854Srgrimes	db_free_breakpoints = bkpt;
864Srgrimes}
874Srgrimes
8812515Sphkstatic void
894Srgrimesdb_set_breakpoint(map, addr, count)
904Srgrimes	vm_map_t	map;
914Srgrimes	db_addr_t	addr;
924Srgrimes	int		count;
934Srgrimes{
944Srgrimes	register db_breakpoint_t	bkpt;
954Srgrimes
964Srgrimes	if (db_find_breakpoint(map, addr)) {
974Srgrimes	    db_printf("Already set.\n");
984Srgrimes	    return;
994Srgrimes	}
1004Srgrimes
1014Srgrimes	bkpt = db_breakpoint_alloc();
1024Srgrimes	if (bkpt == 0) {
1034Srgrimes	    db_printf("Too many breakpoints.\n");
1044Srgrimes	    return;
1054Srgrimes	}
1064Srgrimes
1074Srgrimes	bkpt->map = map;
1084Srgrimes	bkpt->address = addr;
1094Srgrimes	bkpt->flags = 0;
1104Srgrimes	bkpt->init_count = count;
1114Srgrimes	bkpt->count = count;
1124Srgrimes
1134Srgrimes	bkpt->link = db_breakpoint_list;
1144Srgrimes	db_breakpoint_list = bkpt;
1154Srgrimes}
1164Srgrimes
11712515Sphkstatic void
1184Srgrimesdb_delete_breakpoint(map, addr)
1194Srgrimes	vm_map_t	map;
1204Srgrimes	db_addr_t	addr;
1214Srgrimes{
1224Srgrimes	register db_breakpoint_t	bkpt;
1234Srgrimes	register db_breakpoint_t	*prev;
1244Srgrimes
1254Srgrimes	for (prev = &db_breakpoint_list;
1264Srgrimes	     (bkpt = *prev) != 0;
1274Srgrimes	     prev = &bkpt->link) {
1284Srgrimes	    if (db_map_equal(bkpt->map, map) &&
1294Srgrimes		(bkpt->address == addr)) {
1304Srgrimes		*prev = bkpt->link;
1314Srgrimes		break;
1324Srgrimes	    }
1334Srgrimes	}
1344Srgrimes	if (bkpt == 0) {
1354Srgrimes	    db_printf("Not set.\n");
1364Srgrimes	    return;
1374Srgrimes	}
1384Srgrimes
1394Srgrimes	db_breakpoint_free(bkpt);
1404Srgrimes}
1414Srgrimes
14212515Sphkstatic db_breakpoint_t
1434Srgrimesdb_find_breakpoint(map, addr)
1444Srgrimes	vm_map_t	map;
1454Srgrimes	db_addr_t	addr;
1464Srgrimes{
1474Srgrimes	register db_breakpoint_t	bkpt;
1484Srgrimes
1494Srgrimes	for (bkpt = db_breakpoint_list;
1504Srgrimes	     bkpt != 0;
1514Srgrimes	     bkpt = bkpt->link)
1524Srgrimes	{
1534Srgrimes	    if (db_map_equal(bkpt->map, map) &&
1544Srgrimes		(bkpt->address == addr))
1554Srgrimes		return (bkpt);
1564Srgrimes	}
1574Srgrimes	return (0);
1584Srgrimes}
1594Srgrimes
1604Srgrimesdb_breakpoint_t
1614Srgrimesdb_find_breakpoint_here(addr)
1624Srgrimes	db_addr_t	addr;
1634Srgrimes{
1644Srgrimes    return db_find_breakpoint(db_map_addr(addr), addr);
1654Srgrimes}
1664Srgrimes
16712515Sphkstatic boolean_t	db_breakpoints_inserted = TRUE;
1684Srgrimes
16983506Sdfr#ifndef BKPT_WRITE
17083506Sdfr#define BKPT_WRITE(addr, storage)				\
17183506Sdfrdo {								\
17283506Sdfr	*storage = db_get_value(addr, BKPT_SIZE, FALSE);	\
17383506Sdfr	db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage));	\
17483506Sdfr} while (0)
17583506Sdfr#endif
17683506Sdfr
17783506Sdfr#ifndef BKPT_CLEAR
17883506Sdfr#define BKPT_CLEAR(addr, storage) \
17983506Sdfr	db_put_value(addr, BKPT_SIZE, *storage)
18083506Sdfr#endif
18183506Sdfr
1824Srgrimesvoid
1834Srgrimesdb_set_breakpoints()
1844Srgrimes{
1854Srgrimes	register db_breakpoint_t	bkpt;
1864Srgrimes
1874Srgrimes	if (!db_breakpoints_inserted) {
1884Srgrimes
18983506Sdfr		for (bkpt = db_breakpoint_list;
19083506Sdfr		     bkpt != 0;
19183506Sdfr		     bkpt = bkpt->link)
19283506Sdfr			if (db_map_current(bkpt->map)) {
19383506Sdfr				BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
19483506Sdfr			}
19583506Sdfr		db_breakpoints_inserted = TRUE;
1964Srgrimes	}
1974Srgrimes}
1984Srgrimes
1994Srgrimesvoid
2004Srgrimesdb_clear_breakpoints()
2014Srgrimes{
2024Srgrimes	register db_breakpoint_t	bkpt;
2034Srgrimes
2044Srgrimes	if (db_breakpoints_inserted) {
2054Srgrimes
20683506Sdfr		for (bkpt = db_breakpoint_list;
20783506Sdfr		     bkpt != 0;
20883506Sdfr		     bkpt = bkpt->link)
20983506Sdfr			if (db_map_current(bkpt->map)) {
21083506Sdfr				BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
21183506Sdfr			}
21283506Sdfr		db_breakpoints_inserted = FALSE;
2134Srgrimes	}
2144Srgrimes}
2154Srgrimes
21636735Sdfr#ifdef SOFTWARE_SSTEP
2174Srgrimes/*
2184Srgrimes * Set a temporary breakpoint.
2194Srgrimes * The instruction is changed immediately,
2204Srgrimes * so the breakpoint does not have to be on the breakpoint list.
2214Srgrimes */
22236735Sdfrdb_breakpoint_t
2234Srgrimesdb_set_temp_breakpoint(addr)
2244Srgrimes	db_addr_t	addr;
2254Srgrimes{
2264Srgrimes	register db_breakpoint_t	bkpt;
2274Srgrimes
2284Srgrimes	bkpt = db_breakpoint_alloc();
2294Srgrimes	if (bkpt == 0) {
2304Srgrimes	    db_printf("Too many breakpoints.\n");
2314Srgrimes	    return 0;
2324Srgrimes	}
2334Srgrimes
2344Srgrimes	bkpt->map = NULL;
2354Srgrimes	bkpt->address = addr;
2364Srgrimes	bkpt->flags = BKPT_TEMP;
2374Srgrimes	bkpt->init_count = 1;
2384Srgrimes	bkpt->count = 1;
2394Srgrimes
24083506Sdfr	BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
2414Srgrimes	return bkpt;
2424Srgrimes}
2434Srgrimes
24436735Sdfrvoid
2454Srgrimesdb_delete_temp_breakpoint(bkpt)
2464Srgrimes	db_breakpoint_t	bkpt;
2474Srgrimes{
24883506Sdfr	BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
2494Srgrimes	db_breakpoint_free(bkpt);
2504Srgrimes}
25136745Sbde#endif /* SOFTWARE_SSTEP */
25236735Sdfr
2534Srgrimes/*
2544Srgrimes * List breakpoints.
2554Srgrimes */
25612515Sphkstatic void
2574Srgrimesdb_list_breakpoints()
2584Srgrimes{
2594Srgrimes	register db_breakpoint_t	bkpt;
2604Srgrimes
2614Srgrimes	if (db_breakpoint_list == 0) {
2624Srgrimes	    db_printf("No breakpoints set\n");
2634Srgrimes	    return;
2644Srgrimes	}
2654Srgrimes
2664Srgrimes	db_printf(" Map      Count    Address\n");
2674Srgrimes	for (bkpt = db_breakpoint_list;
2684Srgrimes	     bkpt != 0;
26937497Sbde	     bkpt = bkpt->link) {
27037497Sbde	    db_printf("%s%8p %5d    ",
2714Srgrimes		      db_map_current(bkpt->map) ? "*" : " ",
27237497Sbde		      (void *)bkpt->map, bkpt->init_count);
2734Srgrimes	    db_printsym(bkpt->address, DB_STGY_PROC);
2744Srgrimes	    db_printf("\n");
2754Srgrimes	}
2764Srgrimes}
2774Srgrimes
2784Srgrimes/* Delete breakpoint */
2794Srgrimes/*ARGSUSED*/
2804Srgrimesvoid
2814Srgrimesdb_delete_cmd(addr, have_addr, count, modif)
2824Srgrimes	db_expr_t	addr;
28312473Sbde	boolean_t	have_addr;
2844Srgrimes	db_expr_t	count;
2854Srgrimes	char *		modif;
2864Srgrimes{
2874Srgrimes	db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr);
2884Srgrimes}
2894Srgrimes
2904Srgrimes/* Set breakpoint with skip count */
2914Srgrimes/*ARGSUSED*/
2924Srgrimesvoid
2934Srgrimesdb_breakpoint_cmd(addr, have_addr, count, modif)
2944Srgrimes	db_expr_t	addr;
29512473Sbde	boolean_t	have_addr;
2964Srgrimes	db_expr_t	count;
2974Srgrimes	char *		modif;
2984Srgrimes{
2994Srgrimes	if (count == -1)
3004Srgrimes	    count = 1;
3014Srgrimes
3024Srgrimes	db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
3034Srgrimes}
3044Srgrimes
3054Srgrimes/* list breakpoints */
3064Srgrimesvoid
30712473Sbdedb_listbreak_cmd(dummy1, dummy2, dummy3, dummy4)
30812473Sbde	db_expr_t	dummy1;
30912473Sbde	boolean_t	dummy2;
31012473Sbde	db_expr_t	dummy3;
31112473Sbde	char *		dummy4;
3124Srgrimes{
3134Srgrimes	db_list_breakpoints();
3144Srgrimes}
3154Srgrimes
3164Srgrimes/*
3174Srgrimes *	We want ddb to be usable before most of the kernel has been
3184Srgrimes *	initialized.  In particular, current_thread() or kernel_map
3194Srgrimes *	(or both) may be null.
3204Srgrimes */
3214Srgrimes
3224Srgrimesboolean_t
3234Srgrimesdb_map_equal(map1, map2)
3244Srgrimes	vm_map_t	map1, map2;
3254Srgrimes{
3264Srgrimes	return ((map1 == map2) ||
3274Srgrimes		((map1 == NULL) && (map2 == kernel_map)) ||
3284Srgrimes		((map1 == kernel_map) && (map2 == NULL)));
3294Srgrimes}
3304Srgrimes
3314Srgrimesboolean_t
3324Srgrimesdb_map_current(map)
3334Srgrimes	vm_map_t	map;
3344Srgrimes{
3354Srgrimes#if 0
3364Srgrimes	thread_t	thread;
3374Srgrimes
3384Srgrimes	return ((map == NULL) ||
3394Srgrimes		(map == kernel_map) ||
3404Srgrimes		(((thread = current_thread()) != NULL) &&
3414Srgrimes		 (map == thread->task->map)));
3424Srgrimes#else
3434Srgrimes	return (1);
3444Srgrimes#endif
3454Srgrimes}
3464Srgrimes
3474Srgrimesvm_map_t
3484Srgrimesdb_map_addr(addr)
3494Srgrimes	vm_offset_t addr;
3504Srgrimes{
3514Srgrimes#if 0
3524Srgrimes	thread_t	thread;
3534Srgrimes
3544Srgrimes	/*
3554Srgrimes	 *	We want to return kernel_map for all
3564Srgrimes	 *	non-user addresses, even when debugging
3574Srgrimes	 *	kernel tasks with their own maps.
3584Srgrimes	 */
3594Srgrimes
3604Srgrimes	if ((VM_MIN_ADDRESS <= addr) &&
3614Srgrimes	    (addr < VM_MAX_ADDRESS) &&
3624Srgrimes	    ((thread = current_thread()) != NULL))
3634Srgrimes	    return thread->task->map;
3644Srgrimes	else
3654Srgrimes#endif
3664Srgrimes	    return kernel_map;
3674Srgrimes}
368