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