1 /* breakpoint.c: a debugger breakpoint
2    Copyright (c) 2002-2011 Philip Kendall
3    Copyright (c) 2013-2015 Sergio Baldoví
4    Copyright (c) 2015 Tom Seddon
5    Copyright (c) 2016 BogDan Vatra
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License along
18    with this program; if not, write to the Free Software Foundation, Inc.,
19    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 
21    Author contact information:
22 
23    E-mail: philip-fuse@shadowmagic.org.uk
24 
25 */
26 
27 #include <config.h>
28 
29 #include <ctype.h>
30 #include <string.h>
31 
32 #include <libspectrum.h>
33 
34 #include "debugger_internals.h"
35 #include "event.h"
36 #include "fuse.h"
37 #include "memory_pages.h"
38 #include "ui/ui.h"
39 #include "utils.h"
40 
41 /* The current breakpoints */
42 GSList *debugger_breakpoints;
43 
44 /* The next breakpoint ID to use */
45 static size_t next_breakpoint_id;
46 
47 /* Textual representations of the breakpoint types and lifetimes */
48 const char *debugger_breakpoint_type_text[] = {
49   "Execute", "Read", "Write", "Port Read", "Port Write", "Time", "Event",
50 };
51 
52 const char debugger_breakpoint_type_abbr[][4] = {
53   "Exe", "Rd", "Wr", "PtR", "PtW", "Tm", "Ev",
54 };
55 
56 const char *debugger_breakpoint_life_text[] = {
57   "Permanent", "One Shot",
58 };
59 
60 const char debugger_breakpoint_life_abbr[][5] = {
61   "Perm", "Once",
62 };
63 
64 static int breakpoint_add( debugger_breakpoint_type type,
65 			   debugger_breakpoint_value value, size_t ignore,
66 			   debugger_breakpoint_life life,
67 			   debugger_expression *condition );
68 static int breakpoint_check( debugger_breakpoint *bp,
69 			     debugger_breakpoint_type type,
70 			     libspectrum_dword value );
71 static debugger_breakpoint* get_breakpoint_by_id( size_t id );
72 static gint find_breakpoint_by_id( gconstpointer data,
73 				   gconstpointer user_data );
74 static void remove_time( gpointer data, gpointer user_data );
75 static gint find_breakpoint_by_id( gconstpointer data,
76 				   gconstpointer user_data );
77 static gint find_breakpoint_by_address( gconstpointer data,
78 					gconstpointer user_data );
79 static void free_breakpoint( gpointer data, gpointer user_data );
80 static void add_time_event( gpointer data, gpointer user_data );
81 
82 /* Add a breakpoint */
83 int
debugger_breakpoint_add_address(debugger_breakpoint_type type,int source,int page,libspectrum_word offset,size_t ignore,debugger_breakpoint_life life,debugger_expression * condition)84 debugger_breakpoint_add_address( debugger_breakpoint_type type, int source,
85                                  int page, libspectrum_word offset,
86                                  size_t ignore, debugger_breakpoint_life life,
87 				 debugger_expression *condition )
88 {
89   debugger_breakpoint_value value;
90 
91   switch( type ) {
92   case DEBUGGER_BREAKPOINT_TYPE_EXECUTE:
93   case DEBUGGER_BREAKPOINT_TYPE_READ:
94   case DEBUGGER_BREAKPOINT_TYPE_WRITE:
95     break;
96 
97   default:
98     ui_error( UI_ERROR_ERROR, "debugger_breakpoint_add_address given type %d",
99 	      type );
100     fuse_abort();
101   }
102 
103   value.address.source = source;
104   value.address.page = page;
105   value.address.offset = offset;
106 
107   return breakpoint_add( type, value, ignore, life, condition );
108 }
109 
110 int
debugger_breakpoint_add_port(debugger_breakpoint_type type,libspectrum_word port,libspectrum_word mask,size_t ignore,debugger_breakpoint_life life,debugger_expression * condition)111 debugger_breakpoint_add_port( debugger_breakpoint_type type,
112 			      libspectrum_word port, libspectrum_word mask,
113 			      size_t ignore, debugger_breakpoint_life life,
114 			      debugger_expression *condition )
115 {
116   debugger_breakpoint_value value;
117 
118   switch( type ) {
119   case DEBUGGER_BREAKPOINT_TYPE_PORT_READ:
120   case DEBUGGER_BREAKPOINT_TYPE_PORT_WRITE:
121     break;
122 
123   default:
124     ui_error( UI_ERROR_ERROR, "debugger_breakpoint_add_port given type %d",
125 	      type );
126     fuse_abort();
127   }
128 
129   value.port.port = port;
130   value.port.mask = mask;
131 
132   return breakpoint_add( type, value, ignore, life, condition );
133 }
134 
135 int
debugger_breakpoint_add_time(debugger_breakpoint_type type,libspectrum_dword breakpoint_tstates,size_t ignore,debugger_breakpoint_life life,debugger_expression * condition)136 debugger_breakpoint_add_time( debugger_breakpoint_type type,
137 			      libspectrum_dword breakpoint_tstates,
138                               size_t ignore, debugger_breakpoint_life life,
139 			      debugger_expression *condition )
140 {
141   debugger_breakpoint_value value;
142 
143   switch( type ) {
144   case DEBUGGER_BREAKPOINT_TYPE_TIME:
145     break;
146 
147   default:
148     ui_error( UI_ERROR_ERROR, "debugger_breakpoint_add_time given type %d",
149 	      type );
150     fuse_abort();
151   }
152 
153   value.time.triggered = 0;
154   value.time.tstates = breakpoint_tstates;
155   value.time.initial_tstates = breakpoint_tstates;
156 
157   return breakpoint_add( type, value, ignore, life, condition );
158 }
159 
160 int
debugger_breakpoint_add_event(debugger_breakpoint_type type,const char * type_string,const char * detail,size_t ignore,debugger_breakpoint_life life,debugger_expression * condition)161 debugger_breakpoint_add_event( debugger_breakpoint_type type,
162 			       const char *type_string, const char *detail,
163 			       size_t ignore, debugger_breakpoint_life life,
164 			       debugger_expression *condition )
165 {
166   debugger_breakpoint_value value;
167 
168   switch( type ) {
169   case DEBUGGER_BREAKPOINT_TYPE_EVENT:
170     break;
171 
172   default:
173     ui_error( UI_ERROR_ERROR, "%s given type %d", __func__, type );
174     fuse_abort();
175   }
176 
177   if( !debugger_event_is_registered( type_string, detail ) ) {
178     ui_error( UI_ERROR_WARNING, "Event type %s:%s not known", type_string,
179               detail );
180     return 1;
181   }
182 
183   value.event.detail = NULL;
184   value.event.type = utils_safe_strdup( type_string );
185   value.event.detail = utils_safe_strdup( detail );
186 
187   return breakpoint_add( type, value, ignore, life, condition );
188 }
189 
190 static int
breakpoint_add(debugger_breakpoint_type type,debugger_breakpoint_value value,size_t ignore,debugger_breakpoint_life life,debugger_expression * condition)191 breakpoint_add( debugger_breakpoint_type type, debugger_breakpoint_value value,
192 		size_t ignore, debugger_breakpoint_life life,
193 		debugger_expression *condition )
194 {
195   debugger_breakpoint *bp;
196 
197   bp = libspectrum_new( debugger_breakpoint, 1 );
198 
199   bp->id = next_breakpoint_id++; bp->type = type;
200   bp->value = value;
201   bp->ignore = ignore; bp->life = life;
202   if( condition ) {
203     bp->condition = debugger_expression_copy( condition );
204     if( !bp->condition ) {
205       libspectrum_free( bp );
206       return 1;
207     }
208   } else {
209     bp->condition = NULL;
210   }
211 
212   bp->commands = NULL;
213 
214   debugger_breakpoints = g_slist_append( debugger_breakpoints, bp );
215 
216   if( debugger_mode == DEBUGGER_MODE_INACTIVE )
217     debugger_mode = DEBUGGER_MODE_ACTIVE;
218 
219   /* If this was a timed breakpoint, set an event to stop emulation
220      at that point */
221   if( type == DEBUGGER_BREAKPOINT_TYPE_TIME )
222     event_add( value.time.tstates, debugger_breakpoint_event );
223 
224   ui_breakpoints_updated();
225 
226   return 0;
227 }
228 
229 /* Check whether the debugger should become active at this point */
230 int
debugger_check(debugger_breakpoint_type type,libspectrum_dword value)231 debugger_check( debugger_breakpoint_type type, libspectrum_dword value )
232 {
233   GSList *ptr; debugger_breakpoint *bp;
234   GSList *ptr_next;
235 
236   int signal_breakpoints_updated = 0;
237 
238   switch( debugger_mode ) {
239 
240   case DEBUGGER_MODE_INACTIVE: return 0;
241 
242   case DEBUGGER_MODE_ACTIVE:
243     for( ptr = debugger_breakpoints; ptr; ptr = ptr_next ) {
244 
245       bp = ptr->data;
246       ptr_next = ptr->next;
247 
248       if( breakpoint_check( bp, type, value ) ) {
249         debugger_mode = DEBUGGER_MODE_HALTED;
250         debugger_command_evaluate( bp->commands );
251 
252         if( bp->life == DEBUGGER_BREAKPOINT_LIFE_ONESHOT ) {
253           debugger_breakpoints = g_slist_remove( debugger_breakpoints, bp );
254           libspectrum_free( bp );
255           signal_breakpoints_updated = 1;
256         }
257       }
258 
259     }
260     break;
261 
262   case DEBUGGER_MODE_HALTED: return 1;
263 
264   }
265 
266   if( signal_breakpoints_updated )
267       ui_breakpoints_updated();
268 
269   /* Debugger mode could have been reset by a breakpoint command */
270   return ( debugger_mode == DEBUGGER_MODE_HALTED );
271 }
272 
273 void
debugger_breakpoint_reduce_tstates(libspectrum_dword tstates)274 debugger_breakpoint_reduce_tstates( libspectrum_dword tstates )
275 {
276   GSList *ptr;
277   debugger_breakpoint *bp;
278 
279   if( debugger_mode != DEBUGGER_MODE_ACTIVE ) return;
280 
281   for( ptr = debugger_breakpoints; ptr; ptr = ptr->next ) {
282     bp = ptr->data;
283 
284     if( bp->type == DEBUGGER_BREAKPOINT_TYPE_TIME && !bp->value.time.triggered )
285       bp->value.time.tstates -= tstates;
286   }
287 }
288 
289 static memory_page*
get_page(debugger_breakpoint_type type,libspectrum_word address)290 get_page( debugger_breakpoint_type type, libspectrum_word address )
291 {
292   memory_page *bank;
293 
294   switch( type ) {
295   case DEBUGGER_BREAKPOINT_TYPE_EXECUTE:
296   case DEBUGGER_BREAKPOINT_TYPE_READ:
297     bank = memory_map_read;
298     break;
299 
300   case DEBUGGER_BREAKPOINT_TYPE_WRITE:
301     bank = memory_map_write;
302     break;
303 
304   default:
305     ui_error( UI_ERROR_ERROR,
306 	      "%s:get_page: unexpected breakpoint type %d", __FILE__, type );
307     fuse_abort();
308   }
309 
310   return &bank[ address >> MEMORY_PAGE_SIZE_LOGARITHM ];
311 }
312 
313 int
debugger_breakpoint_trigger(debugger_breakpoint * bp)314 debugger_breakpoint_trigger( debugger_breakpoint *bp )
315 {
316   if( bp->ignore ) { bp->ignore--; return 0; }
317 
318   if( bp->type == DEBUGGER_BREAKPOINT_TYPE_TIME )
319     bp->value.time.triggered = 1;
320 
321   if( bp->condition && !debugger_expression_evaluate( bp->condition ) )
322     return 0;
323 
324   return 1;
325 }
326 
327 /* Check whether 'bp' should trigger if we're looking for a breakpoint
328    of 'type' with parameter 'value'. Returns non-zero if we should trigger */
329 static int
breakpoint_check(debugger_breakpoint * bp,debugger_breakpoint_type type,libspectrum_dword value)330 breakpoint_check( debugger_breakpoint *bp, debugger_breakpoint_type type,
331 		  libspectrum_dword value )
332 {
333   if( bp->type != type ) return 0;
334 
335   switch( type ) {
336 
337   case DEBUGGER_BREAKPOINT_TYPE_EXECUTE:
338   case DEBUGGER_BREAKPOINT_TYPE_READ:
339   case DEBUGGER_BREAKPOINT_TYPE_WRITE:
340 
341     /* If source == memory_source_any, value must match exactly; otherwise,
342        the source, page and offset must match */
343     if( bp->value.address.source == memory_source_any ) {
344       if( bp->value.address.offset != value ) return 0;
345     } else {
346       memory_page *page = get_page( type, value );
347       if( bp->value.address.source != page->source ||
348           bp->value.address.page != page->page_num ||
349           bp->value.address.offset != ( value & 0x3fff ) ) return 0;
350     }
351     break;
352 
353     /* Port values must match after masking */
354   case DEBUGGER_BREAKPOINT_TYPE_PORT_READ:
355   case DEBUGGER_BREAKPOINT_TYPE_PORT_WRITE:
356     if( ( value & bp->value.port.mask ) != bp->value.port.port ) return 0;
357     break;
358 
359     /* Timed breakpoints trigger if we're past the relevant time */
360   case DEBUGGER_BREAKPOINT_TYPE_TIME:
361     if( bp->value.time.triggered || bp->value.time.tstates > tstates ) return 0;
362     break;
363 
364   default:
365     ui_error( UI_ERROR_ERROR, "Unknown breakpoint type %d", bp->type );
366     fuse_abort();
367 
368   }
369 
370   return debugger_breakpoint_trigger( bp );
371 }
372 
373 struct remove_t {
374 
375   libspectrum_dword tstates;
376   int done;
377 
378 };
379 
380 /* Remove breakpoint with the given ID */
381 int
debugger_breakpoint_remove(size_t id)382 debugger_breakpoint_remove( size_t id )
383 {
384   debugger_breakpoint *bp;
385 
386   bp = get_breakpoint_by_id( id ); if( !bp ) return 1;
387 
388   debugger_breakpoints = g_slist_remove( debugger_breakpoints, bp );
389   if( debugger_mode == DEBUGGER_MODE_ACTIVE && !debugger_breakpoints )
390     debugger_mode = DEBUGGER_MODE_INACTIVE;
391 
392   /* If this was a timed breakpoint, remove the event as well */
393   if( bp->type == DEBUGGER_BREAKPOINT_TYPE_TIME ) {
394 
395     struct remove_t remove;
396 
397     remove.tstates = bp->value.time.tstates;
398     remove.done = 0;
399 
400     event_foreach( remove_time, &remove );
401   }
402 
403   libspectrum_free( bp );
404 
405   ui_breakpoints_updated();
406 
407   return 0;
408 }
409 
410 static debugger_breakpoint*
get_breakpoint_by_id(size_t id)411 get_breakpoint_by_id( size_t id )
412 {
413   GSList *ptr;
414 
415   ptr = g_slist_find_custom( debugger_breakpoints, &id,
416 			     find_breakpoint_by_id );
417   if( !ptr ) {
418     ui_error( UI_ERROR_ERROR, "Breakpoint %ld does not exist",
419 	      (unsigned long)id );
420     return NULL;
421   }
422 
423   return ptr->data;
424 }
425 
426 static gint
find_breakpoint_by_id(gconstpointer data,gconstpointer user_data)427 find_breakpoint_by_id( gconstpointer data, gconstpointer user_data )
428 {
429   const debugger_breakpoint *bp = data;
430   size_t id = *(const size_t*)user_data;
431 
432   return bp->id - id;
433 }
434 
435 static void
remove_time(gpointer data,gpointer user_data)436 remove_time( gpointer data, gpointer user_data )
437 {
438   event_t *event;
439   struct remove_t *remove;
440 
441   event = data; remove = user_data;
442 
443   if( remove->done ) return;
444 
445   if( event->type == debugger_breakpoint_event &&
446       event->tstates == remove->tstates ) {
447     event->type = event_type_null;
448     remove->done = 1;
449   }
450 }
451 
452 /* Remove all breakpoints at the given address */
453 int
debugger_breakpoint_clear(libspectrum_word address)454 debugger_breakpoint_clear( libspectrum_word address )
455 {
456   GSList *ptr;
457   gpointer ptr_data;
458 
459   int found = 0;
460 
461   while( 1 ) {
462 
463     ptr = g_slist_find_custom( debugger_breakpoints, &address,
464 			       find_breakpoint_by_address );
465     if( !ptr ) break;
466 
467     found++;
468 
469     ptr_data = ptr->data;
470     debugger_breakpoints = g_slist_remove( debugger_breakpoints, ptr_data );
471     if( debugger_mode == DEBUGGER_MODE_ACTIVE && !debugger_breakpoints )
472       debugger_mode = DEBUGGER_MODE_INACTIVE;
473 
474     free_breakpoint( ptr_data, NULL );
475   }
476 
477   if( !found ) {
478     if( debugger_output_base == 10 ) {
479       ui_error( UI_ERROR_ERROR, "No breakpoint at %d", address );
480     } else {
481       ui_error( UI_ERROR_ERROR, "No breakpoint at 0x%04x", address );
482     }
483   } else {
484       ui_breakpoints_updated();
485   }
486 
487   return 0;
488 }
489 
490 static gint
find_breakpoint_by_address(gconstpointer data,gconstpointer user_data)491 find_breakpoint_by_address( gconstpointer data, gconstpointer user_data )
492 {
493   const debugger_breakpoint *bp = data;
494   libspectrum_word address = *(const libspectrum_word*)user_data;
495 
496   if( bp->type != DEBUGGER_BREAKPOINT_TYPE_EXECUTE &&
497       bp->type != DEBUGGER_BREAKPOINT_TYPE_READ    &&
498       bp->type != DEBUGGER_BREAKPOINT_TYPE_WRITE      )
499     return 1;
500 
501   /* Ignore all page-specific breakpoints */
502   if( bp->value.address.source != memory_source_any ) return 1;
503 
504   return bp->value.address.offset - address;
505 }
506 
507 /* Remove all breakpoints */
508 int
debugger_breakpoint_remove_all(void)509 debugger_breakpoint_remove_all( void )
510 {
511   g_slist_foreach( debugger_breakpoints, free_breakpoint, NULL );
512   g_slist_free( debugger_breakpoints ); debugger_breakpoints = NULL;
513 
514   if( debugger_mode == DEBUGGER_MODE_ACTIVE )
515     debugger_mode = DEBUGGER_MODE_INACTIVE;
516 
517   /* Restart the breakpoint numbering */
518   next_breakpoint_id = 1;
519 
520   ui_breakpoints_updated();
521 
522   return 0;
523 }
524 
525 static void
free_breakpoint(gpointer data,gpointer user_data GCC_UNUSED)526 free_breakpoint( gpointer data, gpointer user_data GCC_UNUSED )
527 {
528   debugger_breakpoint *bp = data;
529 
530   switch( bp->type ) {
531   case DEBUGGER_BREAKPOINT_TYPE_EVENT:
532     libspectrum_free( bp->value.event.type );
533     libspectrum_free( bp->value.event.detail );
534     break;
535 
536   case DEBUGGER_BREAKPOINT_TYPE_EXECUTE:
537   case DEBUGGER_BREAKPOINT_TYPE_READ:
538   case DEBUGGER_BREAKPOINT_TYPE_WRITE:
539   case DEBUGGER_BREAKPOINT_TYPE_PORT_READ:
540   case DEBUGGER_BREAKPOINT_TYPE_PORT_WRITE:
541   case DEBUGGER_BREAKPOINT_TYPE_TIME:
542     /* No action needed */
543     break;
544   }
545 
546   if( bp->condition ) debugger_expression_delete( bp->condition );
547   if( bp->commands ) libspectrum_free( bp->commands );
548 
549   libspectrum_free( bp );
550 }
551 
552 /* Ignore breakpoint 'id' the next 'ignore' times it hits */
553 int
debugger_breakpoint_ignore(size_t id,size_t ignore)554 debugger_breakpoint_ignore( size_t id, size_t ignore )
555 {
556   debugger_breakpoint *bp;
557 
558   bp = get_breakpoint_by_id( id ); if( !bp ) return 1;
559 
560   bp->ignore = ignore;
561 
562   return 0;
563 }
564 
565 /* Set the breakpoint's conditional expression */
566 int
debugger_breakpoint_set_condition(size_t id,debugger_expression * condition)567 debugger_breakpoint_set_condition( size_t id, debugger_expression *condition )
568 {
569   debugger_breakpoint *bp;
570 
571   bp = get_breakpoint_by_id( id ); if( !bp ) return 1;
572 
573   if( bp->condition ) debugger_expression_delete( bp->condition );
574 
575   if( condition ) {
576     bp->condition = debugger_expression_copy( condition );
577     if( !bp->condition ) return 1;
578   } else {
579     bp->condition = NULL;
580   }
581 
582   return 0;
583 }
584 
585 int
debugger_breakpoint_set_commands(size_t id,const char * commands)586 debugger_breakpoint_set_commands( size_t id, const char *commands )
587 {
588   debugger_breakpoint *bp = get_breakpoint_by_id( id );
589   if( !bp ) return 1;
590 
591   libspectrum_free( bp->commands );
592   bp->commands = utils_safe_strdup( commands );
593 
594   return 0;
595 }
596 
597 /* Add events corresponding to all the time breakpoints to happen during
598    this frame */
599 int
debugger_add_time_events(void)600 debugger_add_time_events( void )
601 {
602   g_slist_foreach( debugger_breakpoints, add_time_event, NULL );
603   return 0;
604 }
605 
606 static void
add_time_event(gpointer data,gpointer user_data GCC_UNUSED)607 add_time_event( gpointer data, gpointer user_data GCC_UNUSED )
608 {
609   debugger_breakpoint *bp = data;
610 
611   if( bp->type == DEBUGGER_BREAKPOINT_TYPE_TIME && bp->value.time.triggered ) {
612     bp->value.time.triggered = 0;
613     bp->value.time.tstates = bp->value.time.initial_tstates;
614     event_add( bp->value.time.tstates, debugger_breakpoint_event );
615   }
616 }
617 
618 void
debugger_breakpoint_time_fn(libspectrum_dword tstates,int type GCC_UNUSED,void * user_data GCC_UNUSED)619 debugger_breakpoint_time_fn( libspectrum_dword tstates, int type GCC_UNUSED,
620                              void *user_data GCC_UNUSED )
621 {
622   debugger_check( DEBUGGER_BREAKPOINT_TYPE_TIME, 0 );
623 }
624