Improved Glulxercise test program
[projects/chimara/chimara.git] / interpreters / nitfol / debug.c
1 /*  Nitfol - z-machine interpreter using Glk for output.
2     Copyright (C) 1999  Evin Robertson
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
17
18     The author can be reached at nitfol@deja.com
19 */
20 #include "nitfol.h"
21
22 #ifdef HEADER
23
24 #ifndef DEBUGGING
25
26 #define debug_object(o, t)
27 #define debug_attrib(a, o)
28
29 #endif
30
31 typedef enum { CONT_GO, CONT_STEP, CONT_NEXT, CONT_FINISH, CONT_UNTIL, CONT_STEPI, CONT_NEXTI } Cont_type;
32
33
34 #endif
35
36 BOOL enter_debugger = FALSE;
37
38 #ifdef DEBUGGING
39
40 BOOL exit_debugger = FALSE;
41 infix_file *cur_file;
42 int cur_line;
43 int cur_break;
44 int cur_stack_depth;
45 int infix_selected_frame;
46
47 static Cont_type debug_cont = CONT_GO;
48 static int step_count = 0;
49
50 typedef enum { bp_none, bp_break, bp_write_watch, bp_read_watch, bp_access_watch } bptype;
51
52 typedef enum { del, del_at_next_stop, disable, donttouch } bpdisp;
53
54 typedef struct breakpoint breakpoint;
55
56 struct breakpoint {
57   breakpoint *next;
58
59   int number;
60   BOOL enabled;
61
62   bptype type;
63   bpdisp disposition;
64
65   offset PC;
66   int break_frame;  /* If not -1, only break at this depth */
67
68   infix_file *file;
69   int line;
70
71   unsigned hit_count;
72   unsigned ignore_count; /* Don't break until 0 */
73
74   char *condition;
75
76   char *watch_expression;
77   int watch_frame;  /* Frame at which to evaluate watch; -1 if no locals */
78   z_typed watch_value;
79 };
80
81
82 static breakpoint *breaklist;
83 static int breaknumber = 1;
84
85
86 void set_step(Cont_type t, int count)
87 {
88   debug_cont = t;
89   step_count = count;
90   exit_debugger = TRUE;
91
92   do_check_watches = TRUE;
93   if(debug_cont == CONT_GO && breaklist == NULL)
94     do_check_watches = FALSE;
95 }
96
97
98 int infix_set_break(offset location)
99 {
100   breakpoint newbreak;
101
102   infix_location cur_location;
103   
104   if(location == 0) {
105     infix_print_string("No code at that location.\n");
106     return 0;
107   }
108   
109   newbreak.number = breaknumber++;
110   newbreak.enabled = TRUE;
111   newbreak.type = bp_break;
112   newbreak.disposition = donttouch;
113   newbreak.PC = location;
114   newbreak.break_frame = -1;
115   newbreak.hit_count = 0;
116   newbreak.ignore_count = 0;
117   newbreak.condition = NULL;
118
119   LEadd(breaklist, newbreak);
120
121   infix_print_string("Breakpoint ");
122   infix_print_number(newbreak.number);
123   infix_print_string(" at ");
124   infix_print_number(location);
125
126   if(infix_decode_PC(&cur_location, location)) {
127     infix_print_string(": file ");
128     infix_print_string(cur_location.file->filename);
129     infix_print_string(", line ");
130     infix_print_number(cur_location.line_num);
131   }
132   infix_print_string(".\n");
133
134   do_check_watches = TRUE;
135   return newbreak.number;
136 }
137
138 static breakpoint *find_breakpoint(int breaknum)
139 {
140   breakpoint *p;
141   LEsearch(breaklist, p, p->number == breaknum);
142
143   if(p)
144     return p;
145
146   infix_print_string("No breakpoint number ");
147   infix_print_number(breaknum);
148   infix_print_string(".\n");
149   return NULL;
150 }
151
152
153 void infix_delete_breakpoint(int breaknum)
154 {
155   breakpoint *p, *t;
156   LEsearchremove(breaklist, p, t, p->number == breaknum, n_free(p->condition));
157 }
158
159 void infix_set_cond(int breaknum, const char *condition)
160 {
161   breakpoint *p = find_breakpoint(breaknum);
162
163   if(p) {
164     if(p->condition) {
165       n_free(p->condition);
166     }
167     p->condition = n_strdup(condition);
168   }
169 }
170
171 void infix_set_ignore(int breaknum, int count)
172 {
173   breakpoint *p = find_breakpoint(breaknum);
174
175   if(p)
176     p->ignore_count = count;
177 }
178
179 void infix_set_break_enabled(int breaknum, BOOL enabled)
180 {
181   breakpoint *p = find_breakpoint(breaknum);
182
183   if(p)
184     p->enabled = enabled;
185 }
186
187 static void infix_show_break(breakpoint *b)
188 {
189   infix_print_number(b->number);
190   infix_print_char(' ');
191   infix_print_string("breakpoint");
192   infix_print_char(' ');
193   infix_print_string("keep");
194   infix_print_char(' ');
195   if(b->enabled)
196     infix_print_char('y');
197   else
198     infix_print_char('n');
199   infix_print_offset(b->PC);
200   infix_print_char(' ');
201   infix_gprint_loc(0, b->PC);
202   infix_print_char('\n');
203 }
204
205 void infix_show_all_breakpoints(void)
206 {
207   breakpoint *p;
208   if(!breaklist) {
209     infix_print_string("No breakpoints or watchpoints.\n");
210   } else {
211     for(p = breaklist; p; p=p->next)
212       infix_show_break(p);
213   }
214 }
215
216 void infix_show_breakpoint(int breaknum)
217 {
218   breakpoint *p = find_breakpoint(breaknum);
219   if(p) {
220     infix_show_break(p);
221   } else {
222     infix_print_string("No such breakpoint or watchpoint.\n");
223   }
224 }
225
226
227 typedef struct auto_display auto_display;
228
229 struct auto_display {
230   auto_display *next;
231
232   int number;
233   BOOL enabled;
234
235   char *exp;
236 };
237
238 static auto_display *displist;
239 static int dispnumber = 1;
240
241 int infix_auto_display(const char *expression)
242 {
243   auto_display newdisp;
244   newdisp.number = dispnumber++;
245   newdisp.enabled = TRUE;
246   newdisp.exp = n_strdup(expression);
247
248   LEadd(displist, newdisp);
249
250   return newdisp.number;
251 }
252
253 void perform_displays(void)
254 {
255   auto_display *p;
256   for(p = displist; p; p=p->next) {
257     if(p->enabled) {
258       infix_print_number(p->number);
259       infix_print_string(": ");
260       infix_print_string(p->exp);
261       infix_print_string(" = ");
262       infix_display(evaluate_expression(p->exp, infix_selected_frame));
263     }
264   }
265 }
266
267
268 static auto_display *find_auto_display(int displaynum)
269 {
270   auto_display *p;
271   LEsearch(displist, p, p->number == displaynum);
272
273   if(p)
274     return p;
275
276   infix_print_string("No auto-display number ");
277   infix_print_number(displaynum);
278   infix_print_string(".\n");
279   return NULL;  
280 }
281
282
283 void infix_auto_undisplay(int displaynum)
284 {
285   auto_display *p, *t;
286   LEsearchremove(displist, p, t, p->number == displaynum, n_free(p->exp));
287 }
288
289
290 void infix_set_display_enabled(int displaynum, BOOL enabled)
291 {
292   auto_display *p = find_auto_display(displaynum);
293
294   if(p)
295     p->enabled = enabled;
296 }
297
298
299 const char *debug_decode_number(unsigned number)
300 {
301   const char *name;
302   z_typed val;
303   val.v = number;
304
305   val.t = Z_OBJECT;
306   name = infix_get_name(val);
307   
308   if(!name) {
309     val.t = Z_ROUTINE;
310     name = infix_get_name(val);
311   }
312   if(!name) {
313     val.t = Z_STRING;
314     name = infix_get_name(val);
315   }
316   return name;
317 }
318
319
320 unsigned opcode_counters[OFFSET_END];
321
322
323 void check_watches(void)
324 {
325   /* This function is called after every instruction, and infix_decode_PC is
326      relatively expensive, so only get it when we need it */
327   infix_location cur_location;
328   BOOL found_location = FALSE;
329
330   BOOL is_breakpoint = FALSE;
331   int depth;
332   BOOL go_debug;
333   breakpoint *p;
334
335
336   switch(debug_cont) {
337   case CONT_STEP:
338     if(!infix_decode_PC(&cur_location, oldPC))
339       break;
340     found_location = TRUE;
341     if(cur_file != cur_location.file || cur_line != cur_location.line_num)
342       go_debug = TRUE;
343     break;
344
345   case CONT_STEPI:
346     go_debug = TRUE;
347     break;
348
349   case CONT_NEXT:
350     depth = stack_get_depth();
351     if(depth < cur_stack_depth) {
352       go_debug = TRUE;
353     } else if(cur_stack_depth == depth) {
354       if(!infix_decode_PC(&cur_location, oldPC))
355         break;
356       found_location = TRUE;
357
358       if(cur_file != cur_location.file || cur_line != cur_location.line_num)
359         go_debug = TRUE;
360     }
361     break;
362
363   case CONT_NEXTI:
364     depth = stack_get_depth();
365     if(depth <= cur_stack_depth)
366       go_debug = TRUE;
367     break;
368
369   case CONT_FINISH:
370     depth = stack_get_depth();
371     if(depth < cur_stack_depth)
372       go_debug = TRUE;
373     break;
374
375   case CONT_UNTIL:
376     depth = stack_get_depth();
377     if(depth < cur_stack_depth) {
378       go_debug = TRUE;
379     } else if(cur_stack_depth == depth) {
380       if(!infix_decode_PC(&cur_location, oldPC))
381         break;
382       found_location = TRUE;
383
384       if(cur_file != cur_location.file || cur_line > cur_location.line_num)
385         go_debug = TRUE;
386     }
387     break;
388
389   case CONT_GO:
390     go_debug = FALSE;
391   }
392
393   if(go_debug && step_count && --step_count)
394     go_debug = FALSE;
395
396   for(p = breaklist; p; p=p->next) {
397     if(p->enabled) {
398       BOOL break_hit = FALSE;
399       switch(p->type) {
400       case bp_none:
401       case bp_read_watch:
402         break;
403         
404       case bp_break:
405         break_hit = p->PC == oldPC;
406         break;
407
408       case bp_write_watch:
409       case bp_access_watch:
410         
411         ;
412       }
413
414       if(break_hit) {
415         if(p->ignore_count) {
416           p->ignore_count--;
417         } else {
418           z_typed foo;
419           if(p->condition)
420             foo = evaluate_expression(p->condition, infix_selected_frame);
421           if(!p->condition || foo.v) {
422             is_breakpoint = TRUE;
423             go_debug = TRUE;
424             
425             p->hit_count++;
426             
427             infix_print_string("Breakpoint ");
428             infix_print_number(p->number);
429             infix_print_string(", ");
430             
431             switch(p->disposition) {
432             case del:
433               infix_delete_breakpoint(p->number);
434               break;
435             
436             case disable:
437               p->enabled = FALSE;
438               break;
439               
440             case del_at_next_stop:
441             case donttouch:
442               ;
443             }
444           }
445         }
446       }
447     }
448   }
449
450
451   if(go_debug || enter_debugger) {
452     depth = stack_get_depth();
453
454     enter_debugger = FALSE;
455
456     if(!found_location)
457       found_location = infix_decode_PC(&cur_location, oldPC);
458
459     if(found_location) {
460       cur_file = cur_location.file;
461       cur_line = cur_location.line_num;
462     } else {
463       cur_file = NULL;
464       cur_line = 0;
465     }
466
467     if(is_breakpoint || cur_stack_depth != depth) {
468       infix_gprint_loc(depth, 0);
469     } else {
470       infix_file_print_line(cur_file, cur_line);
471     }
472
473     infix_selected_frame = cur_stack_depth = depth;
474
475     for(p = breaklist; p; p=p->next) {
476       if(p->disposition == del_at_next_stop)
477         infix_delete_breakpoint(p->number);
478     }
479
480     debug_prompt();
481   }
482
483   return;
484 }
485
486
487 void debug_prompt(void)
488 {
489   char buffer[513];
490   exit_debugger = FALSE;
491   perform_displays();
492   while(!exit_debugger) {
493     if(db_prompt)
494       infix_print_string(db_prompt);
495     else
496       infix_print_string("(nitfol) ");
497     infix_get_string(buffer, 512);
498     process_debug_command(buffer);
499   }
500 }
501
502 void infix_select_frame(int num)
503 {
504   if(frame_is_valid(num))
505     infix_selected_frame = num;
506 }
507
508 void infix_show_frame(int frame)
509 {
510   infix_print_char('#');
511   infix_print_number(frame);
512   infix_print_string("  ");
513   infix_gprint_loc(frame, 0);
514 }
515
516 void infix_backtrace(int start, int length)
517 {
518   int n;
519   for(n = start + length - 1; n >= start; n--) {
520     infix_show_frame(n);
521   }
522 }
523
524
525
526 const char *watchnames[] = {
527   "reading object",
528   "move to object",
529   "moving object"
530 };
531
532
533
534
535 void debug_object(zword objectnum, watchinfo type)
536 {
537   /*n_show_debug(E_OBJECT, watchnames[type], objectnum);*/
538 }
539
540 void debug_attrib(zword attribnum, zword objectnum)
541 {
542   /*n_show_debug(E_OBJECT, "using attribute", attribnum);*/
543 }
544
545
546
547 #endif
548