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