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
62273265Spfgdb_breakpoint_alloc(void)
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
81273265Spfgdb_breakpoint_free(db_breakpoint_t bkpt)
824Srgrimes{
834Srgrimes	bkpt->link = db_free_breakpoints;
844Srgrimes	db_free_breakpoints = bkpt;
854Srgrimes}
864Srgrimes
8712515Sphkstatic void
88273265Spfgdb_set_breakpoint(vm_map_t map, db_addr_t addr, int count)
894Srgrimes{
904Srgrimes	register db_breakpoint_t	bkpt;
914Srgrimes
924Srgrimes	if (db_find_breakpoint(map, addr)) {
934Srgrimes	    db_printf("Already set.\n");
944Srgrimes	    return;
954Srgrimes	}
964Srgrimes
974Srgrimes	bkpt = db_breakpoint_alloc();
984Srgrimes	if (bkpt == 0) {
994Srgrimes	    db_printf("Too many breakpoints.\n");
1004Srgrimes	    return;
1014Srgrimes	}
1024Srgrimes
1034Srgrimes	bkpt->map = map;
1044Srgrimes	bkpt->address = addr;
1054Srgrimes	bkpt->flags = 0;
1064Srgrimes	bkpt->init_count = count;
1074Srgrimes	bkpt->count = count;
1084Srgrimes
1094Srgrimes	bkpt->link = db_breakpoint_list;
1104Srgrimes	db_breakpoint_list = bkpt;
1114Srgrimes}
1124Srgrimes
11312515Sphkstatic void
114273265Spfgdb_delete_breakpoint(vm_map_t map, db_addr_t addr)
1154Srgrimes{
1164Srgrimes	register db_breakpoint_t	bkpt;
1174Srgrimes	register db_breakpoint_t	*prev;
1184Srgrimes
1194Srgrimes	for (prev = &db_breakpoint_list;
1204Srgrimes	     (bkpt = *prev) != 0;
1214Srgrimes	     prev = &bkpt->link) {
1224Srgrimes	    if (db_map_equal(bkpt->map, map) &&
1234Srgrimes		(bkpt->address == addr)) {
1244Srgrimes		*prev = bkpt->link;
1254Srgrimes		break;
1264Srgrimes	    }
1274Srgrimes	}
1284Srgrimes	if (bkpt == 0) {
1294Srgrimes	    db_printf("Not set.\n");
1304Srgrimes	    return;
1314Srgrimes	}
1324Srgrimes
1334Srgrimes	db_breakpoint_free(bkpt);
1344Srgrimes}
1354Srgrimes
13612515Sphkstatic db_breakpoint_t
137273265Spfgdb_find_breakpoint(vm_map_t map, db_addr_t addr)
1384Srgrimes{
1394Srgrimes	register db_breakpoint_t	bkpt;
1404Srgrimes
1414Srgrimes	for (bkpt = db_breakpoint_list;
1424Srgrimes	     bkpt != 0;
1434Srgrimes	     bkpt = bkpt->link)
1444Srgrimes	{
1454Srgrimes	    if (db_map_equal(bkpt->map, map) &&
1464Srgrimes		(bkpt->address == addr))
1474Srgrimes		return (bkpt);
1484Srgrimes	}
1494Srgrimes	return (0);
1504Srgrimes}
1514Srgrimes
1524Srgrimesdb_breakpoint_t
153273265Spfgdb_find_breakpoint_here(db_addr_t addr)
1544Srgrimes{
155273265Spfg	return db_find_breakpoint(db_map_addr(addr), addr);
1564Srgrimes}
1574Srgrimes
15812515Sphkstatic boolean_t	db_breakpoints_inserted = TRUE;
1594Srgrimes
16083506Sdfr#ifndef BKPT_WRITE
161273265Spfg#define	BKPT_WRITE(addr, storage)				\
16283506Sdfrdo {								\
16383506Sdfr	*storage = db_get_value(addr, BKPT_SIZE, FALSE);	\
16483506Sdfr	db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage));	\
16583506Sdfr} while (0)
16683506Sdfr#endif
16783506Sdfr
16883506Sdfr#ifndef BKPT_CLEAR
169273265Spfg#define	BKPT_CLEAR(addr, storage) \
17083506Sdfr	db_put_value(addr, BKPT_SIZE, *storage)
17183506Sdfr#endif
17283506Sdfr
1734Srgrimesvoid
174273265Spfgdb_set_breakpoints(void)
1754Srgrimes{
1764Srgrimes	register db_breakpoint_t	bkpt;
1774Srgrimes
1784Srgrimes	if (!db_breakpoints_inserted) {
1794Srgrimes
18083506Sdfr		for (bkpt = db_breakpoint_list;
18183506Sdfr		     bkpt != 0;
18283506Sdfr		     bkpt = bkpt->link)
18383506Sdfr			if (db_map_current(bkpt->map)) {
18483506Sdfr				BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
18583506Sdfr			}
18683506Sdfr		db_breakpoints_inserted = TRUE;
1874Srgrimes	}
1884Srgrimes}
1894Srgrimes
1904Srgrimesvoid
191273265Spfgdb_clear_breakpoints(void)
1924Srgrimes{
1934Srgrimes	register db_breakpoint_t	bkpt;
1944Srgrimes
1954Srgrimes	if (db_breakpoints_inserted) {
1964Srgrimes
19783506Sdfr		for (bkpt = db_breakpoint_list;
19883506Sdfr		     bkpt != 0;
19983506Sdfr		     bkpt = bkpt->link)
20083506Sdfr			if (db_map_current(bkpt->map)) {
20183506Sdfr				BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
20283506Sdfr			}
20383506Sdfr		db_breakpoints_inserted = FALSE;
2044Srgrimes	}
2054Srgrimes}
2064Srgrimes
20736735Sdfr#ifdef SOFTWARE_SSTEP
2084Srgrimes/*
2094Srgrimes * Set a temporary breakpoint.
2104Srgrimes * The instruction is changed immediately,
2114Srgrimes * so the breakpoint does not have to be on the breakpoint list.
2124Srgrimes */
21336735Sdfrdb_breakpoint_t
214273265Spfgdb_set_temp_breakpoint(db_addr_t addr)
2154Srgrimes{
2164Srgrimes	register db_breakpoint_t	bkpt;
2174Srgrimes
2184Srgrimes	bkpt = db_breakpoint_alloc();
2194Srgrimes	if (bkpt == 0) {
2204Srgrimes	    db_printf("Too many breakpoints.\n");
2214Srgrimes	    return 0;
2224Srgrimes	}
2234Srgrimes
2244Srgrimes	bkpt->map = NULL;
2254Srgrimes	bkpt->address = addr;
2264Srgrimes	bkpt->flags = BKPT_TEMP;
2274Srgrimes	bkpt->init_count = 1;
2284Srgrimes	bkpt->count = 1;
2294Srgrimes
23083506Sdfr	BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst);
2314Srgrimes	return bkpt;
2324Srgrimes}
2334Srgrimes
23436735Sdfrvoid
235273265Spfgdb_delete_temp_breakpoint(db_breakpoint_t bkpt)
2364Srgrimes{
23783506Sdfr	BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst);
2384Srgrimes	db_breakpoint_free(bkpt);
2394Srgrimes}
24036745Sbde#endif /* SOFTWARE_SSTEP */
24136735Sdfr
2424Srgrimes/*
2434Srgrimes * List breakpoints.
2444Srgrimes */
24512515Sphkstatic void
246273265Spfgdb_list_breakpoints(void)
2474Srgrimes{
2484Srgrimes	register db_breakpoint_t	bkpt;
2494Srgrimes
2504Srgrimes	if (db_breakpoint_list == 0) {
2514Srgrimes	    db_printf("No breakpoints set\n");
2524Srgrimes	    return;
2534Srgrimes	}
2544Srgrimes
2554Srgrimes	db_printf(" Map      Count    Address\n");
2564Srgrimes	for (bkpt = db_breakpoint_list;
2574Srgrimes	     bkpt != 0;
25837497Sbde	     bkpt = bkpt->link) {
25937497Sbde	    db_printf("%s%8p %5d    ",
2604Srgrimes		      db_map_current(bkpt->map) ? "*" : " ",
26137497Sbde		      (void *)bkpt->map, bkpt->init_count);
2624Srgrimes	    db_printsym(bkpt->address, DB_STGY_PROC);
2634Srgrimes	    db_printf("\n");
2644Srgrimes	}
2654Srgrimes}
2664Srgrimes
2674Srgrimes/* Delete breakpoint */
2684Srgrimes/*ARGSUSED*/
2694Srgrimesvoid
270273265Spfgdb_delete_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, char *modif)
2714Srgrimes{
2724Srgrimes	db_delete_breakpoint(db_map_addr(addr), (db_addr_t)addr);
2734Srgrimes}
2744Srgrimes
2754Srgrimes/* Set breakpoint with skip count */
2764Srgrimes/*ARGSUSED*/
2774Srgrimesvoid
278273265Spfgdb_breakpoint_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
279273265Spfg    char *modif)
2804Srgrimes{
2814Srgrimes	if (count == -1)
2824Srgrimes	    count = 1;
2834Srgrimes
2844Srgrimes	db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
2854Srgrimes}
2864Srgrimes
2874Srgrimes/* list breakpoints */
2884Srgrimesvoid
289273265Spfgdb_listbreak_cmd(db_expr_t dummy1, boolean_t dummy2, db_expr_t dummy3,
290273265Spfg    char *dummy4)
2914Srgrimes{
2924Srgrimes	db_list_breakpoints();
2934Srgrimes}
2944Srgrimes
2954Srgrimes/*
2964Srgrimes *	We want ddb to be usable before most of the kernel has been
2974Srgrimes *	initialized.  In particular, current_thread() or kernel_map
2984Srgrimes *	(or both) may be null.
2994Srgrimes */
3004Srgrimes
3014Srgrimesboolean_t
302273265Spfgdb_map_equal(vm_map_t map1, vm_map_t map2)
3034Srgrimes{
3044Srgrimes	return ((map1 == map2) ||
3054Srgrimes		((map1 == NULL) && (map2 == kernel_map)) ||
3064Srgrimes		((map1 == kernel_map) && (map2 == NULL)));
3074Srgrimes}
3084Srgrimes
3094Srgrimesboolean_t
310273265Spfgdb_map_current(vm_map_t map)
3114Srgrimes{
3124Srgrimes#if 0
3134Srgrimes	thread_t	thread;
3144Srgrimes
3154Srgrimes	return ((map == NULL) ||
3164Srgrimes		(map == kernel_map) ||
3174Srgrimes		(((thread = current_thread()) != NULL) &&
3184Srgrimes		 (map == thread->task->map)));
3194Srgrimes#else
3204Srgrimes	return (1);
3214Srgrimes#endif
3224Srgrimes}
3234Srgrimes
3244Srgrimesvm_map_t
325273265Spfgdb_map_addr(vm_offset_t addr)
3264Srgrimes{
3274Srgrimes#if 0
3284Srgrimes	thread_t	thread;
3294Srgrimes
3304Srgrimes	/*
3314Srgrimes	 *	We want to return kernel_map for all
3324Srgrimes	 *	non-user addresses, even when debugging
3334Srgrimes	 *	kernel tasks with their own maps.
3344Srgrimes	 */
3354Srgrimes
3364Srgrimes	if ((VM_MIN_ADDRESS <= addr) &&
3374Srgrimes	    (addr < VM_MAX_ADDRESS) &&
3384Srgrimes	    ((thread = current_thread()) != NULL))
3394Srgrimes	    return thread->task->map;
3404Srgrimes	else
3414Srgrimes#endif
3424Srgrimes	    return kernel_map;
3434Srgrimes}
344