1 /* Generic simulator watchpoint support. 2 Copyright (C) 1997, 2007, 2008, 2009, 2010, 2011 3 Free Software Foundation, Inc. 4 Contributed by Cygnus Support. 5 6 This file is part of GDB, the GNU debugger. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 20 21 #include "sim-main.h" 22 #include "sim-options.h" 23 24 #include "sim-assert.h" 25 26 #include <ctype.h> 27 28 #ifdef HAVE_STRING_H 29 #include <string.h> 30 #else 31 #ifdef HAVE_STRINGS_H 32 #include <strings.h> 33 #endif 34 #endif 35 36 #ifdef HAVE_STDLIB_H 37 #include <stdlib.h> 38 #endif 39 40 enum { 41 OPTION_WATCH_DELETE = OPTION_START, 42 43 OPTION_WATCH_INFO, 44 OPTION_WATCH_CLOCK, 45 OPTION_WATCH_CYCLES, 46 OPTION_WATCH_PC, 47 48 OPTION_WATCH_OP, 49 }; 50 51 52 /* Break an option number into its op/int-nr */ 53 static watchpoint_type 54 option_to_type (SIM_DESC sd, 55 int option) 56 { 57 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 58 watchpoint_type type = ((option - OPTION_WATCH_OP) 59 / (watch->nr_interrupts + 1)); 60 SIM_ASSERT (type >= 0 && type < nr_watchpoint_types); 61 return type; 62 } 63 64 static int 65 option_to_interrupt_nr (SIM_DESC sd, 66 int option) 67 { 68 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 69 int interrupt_nr = ((option - OPTION_WATCH_OP) 70 % (watch->nr_interrupts + 1)); 71 return interrupt_nr; 72 } 73 74 static int 75 type_to_option (SIM_DESC sd, 76 watchpoint_type type, 77 int interrupt_nr) 78 { 79 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 80 return ((type * (watch->nr_interrupts + 1)) 81 + interrupt_nr 82 + OPTION_WATCH_OP); 83 } 84 85 86 /* Delete one or more watchpoints. Fail if no watchpoints were found */ 87 88 static SIM_RC 89 do_watchpoint_delete (SIM_DESC sd, 90 int ident, 91 watchpoint_type type) 92 { 93 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 94 sim_watch_point **entry = &watch->points; 95 SIM_RC status = SIM_RC_FAIL; 96 while ((*entry) != NULL) 97 { 98 if ((*entry)->ident == ident 99 || (*entry)->type == type) 100 { 101 sim_watch_point *dead = (*entry); 102 (*entry) = (*entry)->next; 103 sim_events_deschedule (sd, dead->event); 104 free (dead); 105 status = SIM_RC_OK; 106 } 107 else 108 entry = &(*entry)->next; 109 } 110 return status; 111 } 112 113 static const char * 114 watchpoint_type_to_str (SIM_DESC sd, 115 watchpoint_type type) 116 { 117 switch (type) 118 { 119 case pc_watchpoint: 120 return "pc"; 121 case clock_watchpoint: 122 return "clock"; 123 case cycles_watchpoint: 124 return "cycles"; 125 case invalid_watchpoint: 126 case nr_watchpoint_types: 127 return "(invalid-type)"; 128 } 129 return NULL; 130 } 131 132 static const char * 133 interrupt_nr_to_str (SIM_DESC sd, 134 int interrupt_nr) 135 { 136 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 137 if (interrupt_nr < 0) 138 return "(invalid-interrupt)"; 139 else if (interrupt_nr >= watch->nr_interrupts) 140 return "breakpoint"; 141 else 142 return watch->interrupt_names[interrupt_nr]; 143 } 144 145 146 static void 147 do_watchpoint_info (SIM_DESC sd) 148 { 149 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 150 sim_watch_point *point; 151 sim_io_printf (sd, "Watchpoints:\n"); 152 for (point = watch->points; point != NULL; point = point->next) 153 { 154 sim_io_printf (sd, "%3d: watch %s %s ", 155 point->ident, 156 watchpoint_type_to_str (sd, point->type), 157 interrupt_nr_to_str (sd, point->interrupt_nr)); 158 if (point->is_periodic) 159 sim_io_printf (sd, "+"); 160 if (!point->is_within) 161 sim_io_printf (sd, "!"); 162 sim_io_printf (sd, "0x%lx", point->arg0); 163 if (point->arg1 != point->arg0) 164 sim_io_printf (sd, ",0x%lx", point->arg1); 165 sim_io_printf (sd, "\n"); 166 } 167 } 168 169 170 171 static sim_event_handler handle_watchpoint; 172 173 static SIM_RC 174 schedule_watchpoint (SIM_DESC sd, 175 sim_watch_point *point) 176 { 177 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 178 switch (point->type) 179 { 180 case pc_watchpoint: 181 point->event = sim_events_watch_sim (sd, 182 watch->pc, 183 watch->sizeof_pc, 184 0/* host-endian */, 185 point->is_within, 186 point->arg0, point->arg1, 187 /* PC in arg0..arg1 */ 188 handle_watchpoint, 189 point); 190 return SIM_RC_OK; 191 case clock_watchpoint: 192 point->event = sim_events_watch_clock (sd, 193 point->arg0, /* ms time */ 194 handle_watchpoint, 195 point); 196 return SIM_RC_OK; 197 case cycles_watchpoint: 198 point->event = sim_events_schedule (sd, 199 point->arg0, /* time */ 200 handle_watchpoint, 201 point); 202 return SIM_RC_OK; 203 default: 204 sim_engine_abort (sd, NULL, NULL_CIA, 205 "handle_watchpoint - internal error - bad switch"); 206 return SIM_RC_FAIL; 207 } 208 return SIM_RC_OK; 209 } 210 211 212 static void 213 handle_watchpoint (SIM_DESC sd, void *data) 214 { 215 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 216 sim_watch_point *point = (sim_watch_point *) data; 217 int interrupt_nr = point->interrupt_nr; 218 219 if (point->is_periodic) 220 /* reschedule this event before processing it */ 221 schedule_watchpoint (sd, point); 222 else 223 do_watchpoint_delete (sd, point->ident, invalid_watchpoint); 224 225 if (point->interrupt_nr == watch->nr_interrupts) 226 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_stopped, SIM_SIGINT); 227 else 228 watch->interrupt_handler (sd, &watch->interrupt_names[interrupt_nr]); 229 } 230 231 232 static SIM_RC 233 do_watchpoint_create (SIM_DESC sd, 234 watchpoint_type type, 235 int opt, 236 char *arg) 237 { 238 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 239 sim_watch_point **point; 240 241 /* create the watchpoint */ 242 point = &watch->points; 243 while ((*point) != NULL) 244 point = &(*point)->next; 245 (*point) = ZALLOC (sim_watch_point); 246 247 /* fill in the details */ 248 (*point)->ident = ++(watch->last_point_nr); 249 (*point)->type = option_to_type (sd, opt); 250 (*point)->interrupt_nr = option_to_interrupt_nr (sd, opt); 251 /* prefixes to arg - +== periodic, !==not or outside */ 252 (*point)->is_within = 1; 253 while (1) 254 { 255 if (arg[0] == '+') 256 (*point)->is_periodic = 1; 257 else if (arg[0] == '!') 258 (*point)->is_within = 0; 259 else 260 break; 261 arg++; 262 } 263 264 (*point)->arg0 = strtoul (arg, &arg, 0); 265 if (arg[0] == ',') 266 (*point)->arg0 = strtoul (arg, NULL, 0); 267 else 268 (*point)->arg1 = (*point)->arg0; 269 270 /* schedule it */ 271 schedule_watchpoint (sd, (*point)); 272 273 return SIM_RC_OK; 274 } 275 276 277 static SIM_RC 278 watchpoint_option_handler (SIM_DESC sd, sim_cpu *cpu, int opt, 279 char *arg, int is_command) 280 { 281 if (opt >= OPTION_WATCH_OP) 282 return do_watchpoint_create (sd, clock_watchpoint, opt, arg); 283 else 284 switch (opt) 285 { 286 287 case OPTION_WATCH_DELETE: 288 if (isdigit ((int) arg[0])) 289 { 290 int ident = strtol (arg, NULL, 0); 291 if (do_watchpoint_delete (sd, ident, invalid_watchpoint) 292 != SIM_RC_OK) 293 { 294 sim_io_eprintf (sd, "Watchpoint %d not found\n", ident); 295 return SIM_RC_FAIL; 296 } 297 return SIM_RC_OK; 298 } 299 else if (strcasecmp (arg, "all") == 0) 300 { 301 watchpoint_type type; 302 for (type = invalid_watchpoint + 1; 303 type < nr_watchpoint_types; 304 type++) 305 { 306 do_watchpoint_delete (sd, 0, type); 307 } 308 return SIM_RC_OK; 309 } 310 else if (strcasecmp (arg, "pc") == 0) 311 { 312 if (do_watchpoint_delete (sd, 0, pc_watchpoint) 313 != SIM_RC_OK) 314 { 315 sim_io_eprintf (sd, "No PC watchpoints found\n"); 316 return SIM_RC_FAIL; 317 } 318 return SIM_RC_OK; 319 } 320 else if (strcasecmp (arg, "clock") == 0) 321 { 322 if (do_watchpoint_delete (sd, 0, clock_watchpoint) != SIM_RC_OK) 323 { 324 sim_io_eprintf (sd, "No CLOCK watchpoints found\n"); 325 return SIM_RC_FAIL; 326 } 327 return SIM_RC_OK; 328 } 329 else if (strcasecmp (arg, "cycles") == 0) 330 { 331 if (do_watchpoint_delete (sd, 0, cycles_watchpoint) != SIM_RC_OK) 332 { 333 sim_io_eprintf (sd, "No CYCLES watchpoints found\n"); 334 return SIM_RC_FAIL; 335 } 336 return SIM_RC_OK; 337 } 338 sim_io_eprintf (sd, "Unknown watchpoint type `%s'\n", arg); 339 return SIM_RC_FAIL; 340 341 case OPTION_WATCH_INFO: 342 { 343 do_watchpoint_info (sd); 344 return SIM_RC_OK; 345 } 346 347 default: 348 sim_io_eprintf (sd, "Unknown watch option %d\n", opt); 349 return SIM_RC_FAIL; 350 351 } 352 353 } 354 355 356 static SIM_RC 357 sim_watchpoint_init (SIM_DESC sd) 358 { 359 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 360 sim_watch_point *point; 361 /* NOTE: Do not need to de-schedule any previous watchpoints as 362 sim-events has already done this */ 363 /* schedule any watchpoints enabled by command line options */ 364 for (point = watch->points; point != NULL; point = point->next) 365 { 366 schedule_watchpoint (sd, point); 367 } 368 return SIM_RC_OK; 369 } 370 371 372 static const OPTION watchpoint_options[] = 373 { 374 { {"watch-delete", required_argument, NULL, OPTION_WATCH_DELETE }, 375 '\0', "IDENT|all|pc|cycles|clock", "Delete a watchpoint", 376 watchpoint_option_handler, NULL }, 377 378 { {"watch-info", no_argument, NULL, OPTION_WATCH_INFO }, 379 '\0', NULL, "List scheduled watchpoints", 380 watchpoint_option_handler, NULL }, 381 382 { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL, NULL } 383 }; 384 385 static const char *default_interrupt_names[] = { "int", 0, }; 386 387 388 389 SIM_RC 390 sim_watchpoint_install (SIM_DESC sd) 391 { 392 sim_watchpoints *watch = STATE_WATCHPOINTS (sd); 393 SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); 394 /* the basic command set */ 395 sim_module_add_init_fn (sd, sim_watchpoint_init); 396 sim_add_option_table (sd, NULL, watchpoint_options); 397 /* fill in some details */ 398 if (watch->interrupt_names == NULL) 399 watch->interrupt_names = default_interrupt_names; 400 watch->nr_interrupts = 0; 401 while (watch->interrupt_names[watch->nr_interrupts] != NULL) 402 watch->nr_interrupts++; 403 /* generate more advansed commands */ 404 { 405 OPTION *int_options = NZALLOC (OPTION, 1 + (watch->nr_interrupts + 1) * nr_watchpoint_types); 406 int interrupt_nr; 407 for (interrupt_nr = 0; interrupt_nr <= watch->nr_interrupts; interrupt_nr++) 408 { 409 watchpoint_type type; 410 for (type = 0; type < nr_watchpoint_types; type++) 411 { 412 char *name; 413 int nr = interrupt_nr * nr_watchpoint_types + type; 414 OPTION *option = &int_options[nr]; 415 if (asprintf (&name, "watch-%s-%s", 416 watchpoint_type_to_str (sd, type), 417 interrupt_nr_to_str (sd, interrupt_nr)) < 0) 418 return SIM_RC_FAIL; 419 option->opt.name = name; 420 option->opt.has_arg = required_argument; 421 option->opt.val = type_to_option (sd, type, interrupt_nr); 422 option->doc = ""; 423 option->doc_name = ""; 424 option->handler = watchpoint_option_handler; 425 } 426 } 427 /* adjust first few entries so that they contain real 428 documentation, the first entry includes a list of actions. */ 429 { 430 const char *prefix = 431 "Watch the simulator, take ACTION in COUNT cycles (`+' for every COUNT cycles), ACTION is"; 432 char *doc; 433 int len = strlen (prefix) + 1; 434 for (interrupt_nr = 0; interrupt_nr <= watch->nr_interrupts; interrupt_nr++) 435 len += strlen (interrupt_nr_to_str (sd, interrupt_nr)) + 1; 436 doc = NZALLOC (char, len); 437 strcpy (doc, prefix); 438 for (interrupt_nr = 0; interrupt_nr <= watch->nr_interrupts; interrupt_nr++) 439 { 440 strcat (doc, " "); 441 strcat (doc, interrupt_nr_to_str (sd, interrupt_nr)); 442 } 443 int_options[0].doc_name = "watch-cycles-ACTION"; 444 int_options[0].arg = "[+]COUNT"; 445 int_options[0].doc = doc; 446 } 447 int_options[1].doc_name = "watch-pc-ACTION"; 448 int_options[1].arg = "[!]ADDRESS"; 449 int_options[1].doc = 450 "Watch the PC, take ACTION when matches ADDRESS (in range ADDRESS,ADDRESS), `!' negates test"; 451 int_options[2].doc_name = "watch-clock-ACTION"; 452 int_options[2].arg = "[+]MILLISECONDS"; 453 int_options[2].doc = 454 "Watch the clock, take ACTION after MILLISECONDS (`+' for every MILLISECONDS)"; 455 456 sim_add_option_table (sd, NULL, int_options); 457 } 458 return SIM_RC_OK; 459 } 460