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