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