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: releng/11.0/sys/ddb/db_break.c 283248 2015-05-21 15:16:18Z pfg $");
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
62273006Spfgdb_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
81273006Spfgdb_breakpoint_free(db_breakpoint_t bkpt)
824Srgrimes{
834Srgrimes	bkpt->link = db_free_breakpoints;
844Srgrimes	db_free_breakpoints = bkpt;
854Srgrimes}
864Srgrimes
8712515Sphkstatic void
88273006Spfgdb_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
114273006Spfgdb_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
137273006Spfgdb_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
153273006Spfgdb_find_breakpoint_here(db_addr_t addr)
1544Srgrimes{
155272958Spfg	return db_find_breakpoint(db_map_addr(addr), addr);
1564Srgrimes}
1574Srgrimes
158283248Spfgstatic bool	db_breakpoints_inserted = true;
1594Srgrimes
16083506Sdfr#ifndef BKPT_WRITE
161272958Spfg#define	BKPT_WRITE(addr, storage)				\
16283506Sdfrdo {								\
163283088Spfg	*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
169272958Spfg#define	BKPT_CLEAR(addr, storage) \
17083506Sdfr	db_put_value(addr, BKPT_SIZE, *storage)
17183506Sdfr#endif
17283506Sdfr
1734Srgrimesvoid
174273006Spfgdb_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			}
186283088Spfg		db_breakpoints_inserted = true;
1874Srgrimes	}
1884Srgrimes}
1894Srgrimes
1904Srgrimesvoid
191273006Spfgdb_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			}
203283088Spfg		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
214273006Spfgdb_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
235273006Spfgdb_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
246273006Spfgdb_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
270283248Spfgdb_delete_cmd(db_expr_t addr, bool 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
278283248Spfgdb_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
2794Srgrimes{
2804Srgrimes	if (count == -1)
2814Srgrimes	    count = 1;
2824Srgrimes
2834Srgrimes	db_set_breakpoint(db_map_addr(addr), (db_addr_t)addr, count);
2844Srgrimes}
2854Srgrimes
2864Srgrimes/* list breakpoints */
2874Srgrimesvoid
288283248Spfgdb_listbreak_cmd(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4)
2894Srgrimes{
2904Srgrimes	db_list_breakpoints();
2914Srgrimes}
2924Srgrimes
2934Srgrimes/*
2944Srgrimes *	We want ddb to be usable before most of the kernel has been
2954Srgrimes *	initialized.  In particular, current_thread() or kernel_map
2964Srgrimes *	(or both) may be null.
2974Srgrimes */
2984Srgrimes
299283248Spfgbool
300273006Spfgdb_map_equal(vm_map_t map1, vm_map_t map2)
3014Srgrimes{
3024Srgrimes	return ((map1 == map2) ||
3034Srgrimes		((map1 == NULL) && (map2 == kernel_map)) ||
3044Srgrimes		((map1 == kernel_map) && (map2 == NULL)));
3054Srgrimes}
3064Srgrimes
307283248Spfgbool
308273006Spfgdb_map_current(vm_map_t map)
3094Srgrimes{
3104Srgrimes#if 0
3114Srgrimes	thread_t	thread;
3124Srgrimes
3134Srgrimes	return ((map == NULL) ||
3144Srgrimes		(map == kernel_map) ||
3154Srgrimes		(((thread = current_thread()) != NULL) &&
3164Srgrimes		 (map == thread->task->map)));
3174Srgrimes#else
318283248Spfg	return (true);
3194Srgrimes#endif
3204Srgrimes}
3214Srgrimes
3224Srgrimesvm_map_t
323273006Spfgdb_map_addr(vm_offset_t addr)
3244Srgrimes{
3254Srgrimes#if 0
3264Srgrimes	thread_t	thread;
3274Srgrimes
3284Srgrimes	/*
3294Srgrimes	 *	We want to return kernel_map for all
3304Srgrimes	 *	non-user addresses, even when debugging
3314Srgrimes	 *	kernel tasks with their own maps.
3324Srgrimes	 */
3334Srgrimes
3344Srgrimes	if ((VM_MIN_ADDRESS <= addr) &&
3354Srgrimes	    (addr < VM_MAX_ADDRESS) &&
3364Srgrimes	    ((thread = current_thread()) != NULL))
3374Srgrimes	    return thread->task->map;
3384Srgrimes	else
3394Srgrimes#endif
3404Srgrimes	    return kernel_map;
3414Srgrimes}
342