1 /* $Header$ */
2 
3 /*
4  *   Copyright (c) 1999, 2002 Michael J. Roberts.  All Rights Reserved.
5  *
6  *   Please see the accompanying license file, LICENSE.TXT, for information
7  *   on using and copying this software.
8  */
9 /*
10 Name
11   vmdbg.h - T3 VM debugging API
12 Function
13   Provides an interface to debugging operations in the VM
14 Notes
15 
16 Modified
17   11/23/99 MJRoberts  - Creation
18 */
19 
20 #ifndef VMDBG_H
21 #define VMDBG_H
22 
23 #include "vmglob.h"
24 #include "vmfunc.h"
25 #include "vmhash.h"
26 #include "vmobj.h"
27 #include "tcprstyp.h"
28 
29 
30 /* ------------------------------------------------------------------------ */
31 /*
32  *   Internal breakpoint tracking record
33  */
34 struct CVmDebugBp
35 {
36     /* create */
37     CVmDebugBp();
38 
39     /* delete */
40     ~CVmDebugBp();
41 
42     /* notify of VM termination */
43     void do_terminate(VMG0_);
44 
45     /* get/set the in-use flag */
is_in_useCVmDebugBp46     int is_in_use() const { return in_use_; }
set_in_useCVmDebugBp47     void set_in_use(int f) { in_use_ = f; }
48 
49     /* get/set the disabled flag */
is_disabledCVmDebugBp50     int is_disabled() const { return disabled_; }
set_disabledCVmDebugBp51     void set_disabled(int f) { disabled_ = f; }
52 
53     /*
54      *   set the breakpoint's information - returns zero on success,
55      *   non-zero if an error occurs (such as compiling the condition
56      *   expression)
57      */
58     int set_info(VMG_ ulong code_addr, const char *cond, int change,
59                  int disabled, char *errbuf, size_t errbuflen);
60 
61     /* get my code address */
get_code_addrCVmDebugBp62     ulong get_code_addr() const { return code_addr_; }
63 
64     /*
65      *   Set the condition text - returns zero on success, non-zero if an
66      *   error occurs compiling the expression.  If 'change' is true, then we
67      *   break when the condition changes; otherwise, we break when the
68      *   condition becomes true.
69      */
70     int set_condition(VMG_ const char *cond, int change,
71                       char *errbuf, size_t errbuflen);
72 
73     /*
74      *   delete the breakpoint - remove the breakpoint from the code and
75      *   mark it as unused
76      */
77     void do_delete(VMG0_);
78 
79     /*
80      *   Set or remove the BP instruction at my code address.  If 'always'
81      *   is set, we'll update the instruction regardless of whether the
82      *   debugger has control or not; normally, we'll only update the
83      *   instruction when the debugger doesn't have control, since we
84      *   don't leave breakpoint instructions in the code at other times.
85      */
86     void set_bp_instr(VMG_ int set, int always);
87 
88     /* check to see if I have a condition */
has_conditionCVmDebugBp89     int has_condition() const { return has_cond_ && compiled_cond_ != 0; }
90 
91     /*
92      *   Is our condition a stop-on-change condition?  This returns true if
93      *   so, false if this we instead have a stop-when-true condition.  Note
94      *   that this information is not meaningful unless has_condition()
95      *   returns true.
96      */
stop_on_changeCVmDebugBp97     int stop_on_change() const { return stop_on_change_; }
98 
99     /*
100      *   evaluate the condition - return true if the condition evaluates
101      *   to true (non-zero integer, true, or any object, string, or list
102      *   value), false if not
103      */
104     int eval_cond(VMG0_);
105 
106     /*
107      *   determine if this is a global breakpoint - a global breakpoint is
108      *   one that is not associated with a code location (indicated by a
109      *   code address of zero)
110      */
is_globalCVmDebugBp111     int is_global() const { return code_addr_ == 0; }
112 
113 private:
114     /* code address of breakpoint */
115     ulong code_addr_;
116 
117     /* condition expression */
118     char *cond_;
119 
120     /* length of buffer allocated for cond, if any */
121     size_t cond_buf_len_;
122 
123     /* code object with compiled expression */
124     class CVmPoolDynObj *compiled_cond_;
125 
126     /*
127      *   The previous value of the condition.  If this is a stop-on-change
128      *   condition, we'll stop as soon as the current value of the expression
129      *   differs from this saved value.  If we have a condition, but we
130      *   haven't yet computed the "old" value, this will have the 'empty'
131      *   type.
132      */
133     vm_globalvar_t *prv_val;
134 
135     /*
136      *   original instruction byte at this breakpoint (we replace the
137      *   instruction byte with the BP instruction, but we must remember
138      *   the original for when we clear or disable the breakpoint)
139      */
140     char orig_instr_;
141 
142     /* flag: breakpoint is in use */
143     uint in_use_ : 1;
144 
145     /* flag: breakpoint has a conditional expression */
146     uint has_cond_ : 1;
147 
148     /* flag: our condition is stop-on-change, not stop-when-true */
149     uint stop_on_change_ : 1;
150 
151     /* flag: breakpoint is disabled */
152     uint disabled_ : 1;
153 
154 };
155 
156 /* maximum number of breakpoints */
157 const size_t VMDBG_BP_MAX = 100;
158 
159 /* ------------------------------------------------------------------------ */
160 /*
161  *   Structure for saving debugger step modes.  This is used to save and
162  *   restore the step modes before doing a recursive evaluation.
163  */
164 struct vmdbg_step_save_t
165 {
166     int old_step;
167     int old_step_in;
168     int old_step_out;
169 };
170 
171 
172 /* ------------------------------------------------------------------------ */
173 /*
174  *   Debugger API object
175  */
176 class CVmDebug
177 {
178     friend class CVmRun;
179     friend struct CVmDebugBp;
180 
181 public:
182     CVmDebug(VMG0_);
183     ~CVmDebug();
184 
185     /*
186      *   Initialize.  The VM will call this after setting up all of the VM
187      *   globals, but before loading the image file.
188      */
189     void init(VMG_ const char *image_filename);
190 
191     /*
192      *   Initialization phase 2 - this is called just after we finish
193      *   loading the image file.  The debugger can perform any final
194      *   set-up that can't be handled until after the image is loaded.
195      */
196     void init_after_load(VMG0_);
197 
198     /*
199      *   Terminate.  The VM will call this when shutting down, before
200      *   deleting any of the globals.
201      */
202     void terminate(VMG0_);
203 
204     /*
205      *   Determine if the loaded program was compiled for debugging.
206      *   We'll check to see if the image loader found a GSYM (global
207      *   symbol table) block in the file.
208      */
209     int image_has_debug_info(VMG0_) const;
210 
211     /* determine if the debugger has control */
is_in_debugger()212     int is_in_debugger() const { return in_debugger_ != 0; }
213 
214 
215     /* -------------------------------------------------------------------- */
216     /*
217      *   Add an entry to a reverse-lookup hash table.  The image file
218      *   loader must call this function for each object, function, and
219      *   property symbol it loads from the debug records in an image file.
220      */
221     void add_rev_sym(const char *sym, size_t sym_len,
222                      tc_symtype_t sym_type, ulong sym_val);
223 
224     /*
225      *   Look up a symbol given the type and identifier.  Returns a
226      *   pointer to a null-terminated string giving the name of the
227      *   symbol, or null if the given identifier doesn't have an
228      *   associated symbol.
229      */
objid_to_sym(vm_obj_id_t objid)230     const char *objid_to_sym(vm_obj_id_t objid) const
231         { return find_rev_sym(obj_rev_table_, (ulong)objid); }
232 
propid_to_sym(vm_prop_id_t propid)233     const char *propid_to_sym(vm_prop_id_t propid) const
234         { return find_rev_sym(prop_rev_table_, (ulong)propid); }
235 
funcaddr_to_sym(pool_ofs_t func_addr)236     const char *funcaddr_to_sym(pool_ofs_t func_addr) const
237         { return find_rev_sym(func_rev_table_, (ulong)func_addr); }
238 
enum_to_sym(ulong enum_id)239     const char *enum_to_sym(ulong enum_id) const
240         { return find_rev_sym(enum_rev_table_, enum_id); }
241 
242     /*
243      *   Given a symbol name, get the final modifying object.  If the symbol
244      *   is a synthesized modified base object, we'll find the actual symbol
245      *   that was used in the source code with 'modify.'
246      */
247     const char *get_modifying_sym(const char *base_sym) const;
248 
249 
250     /* -------------------------------------------------------------------- */
251     /*
252      *   Method header list
253      */
254 
255     /* allocate the method header list */
256     void alloc_method_header_list(ulong cnt);
257 
258     /*
259      *   Given a code pool address, find the method header containing the
260      *   address.  This searches the method header list for the nearest
261      *   method header whose address is less than the given address.
262      */
263     pool_ofs_t find_method_header(pool_ofs_t ofs);
264 
265     /* get the number of method headers */
get_method_header_cnt()266     ulong get_method_header_cnt() const { return method_hdr_cnt_; }
267 
268     /* set the given method header list entry */
set_method_header(ulong idx,ulong addr)269     void set_method_header(ulong idx, ulong addr)
270     {
271         /* store the given entry */
272         method_hdr_[idx] = addr;
273     }
274 
275     /* get the given method header entry */
get_method_header(ulong idx)276     ulong get_method_header(ulong idx) const { return method_hdr_[idx]; }
277 
278 
279     /* -------------------------------------------------------------------- */
280     /*
281      *   Get information on the source location at a given stack level.
282      *   If successful, fills in the filename pointer and line number and
283      *   returns zero.  If no source information is available at the stack
284      *   level, returns non-zero to indicate failure.
285      *
286      *   Level 0 is the current stack level, 1 is the enclosing frame, and
287      *   so on.
288      */
289     int get_source_info(VMG_ const char **fname, unsigned long *linenum,
290                         int level) const;
291 
292 
293     /*
294      *   Enumerate local variables at the given stack level - 0 is the
295      *   current stack level, 1 is the enclosing frame, and so on.
296      */
297     void enum_locals(VMG_ void (*cbfunc)(void *, const char *, size_t),
298                      void *cbctx, int level);
299 
300     /*
301      *   Build a stack trace listing, invoking the callback for each level
302      *   of the stack trace.  If 'source_info' is true, we'll include the
303      *   source file name and line number for each point in the trace.
304      */
305     void build_stack_listing(VMG_
306                              void (*cbfunc)(void *ctx,
307                                             const char *str, int strl),
308                              void *cbctx, int source_info);
309 
310 
311     /* -------------------------------------------------------------------- */
312     /*
313      *   Breakpoints
314      */
315 
316     /*
317      *   Toggle a breakpoint at the given code location.  Returns zero on
318      *   success, non-zero on failure.  If successful, fills in *bpnum
319      *   with the breakpoint ID, and fills in *did_set with true if we set
320      *   a breakpoint, false if we deleted an existing breakpoint at the
321      *   location.  cond is null if the breakpoint is unconditional, or a
322      *   pointer to a character string giving the source code for an
323      *   expression that must evaluate to true when the breakpoint is
324      *   encountered for the breakpoint to suspend execution.
325      */
326     int toggle_breakpoint(VMG_ ulong code_addr,
327                           const char *cond, int change,
328                           int *bpnum, int *did_set,
329                           char *errbuf, size_t errbuflen);
330 
331     /* toggle the disabled status of a breakpoint */
332     void toggle_breakpoint_disable(VMG_ int bpnum);
333 
334     /* set a breakpoint's disabled status */
335     void set_breakpoint_disable(VMG_ int bpnum, int disable);
336 
337     /* determine if a breakpoint is disabled - returns true if so */
338     int is_breakpoint_disabled(VMG_ int bpnum);
339 
340     /* set a breakpoint's condition expression - returns 0 on success */
341     int set_breakpoint_condition(VMG_ int bpnum, const char *cond, int change,
342                                  char *errbuf, size_t errbuflen);
343 
344     /* delete a breakpoint given the breakpoint ID */
345     void delete_breakpoint(VMG_ int bpnum);
346 
347     /* -------------------------------------------------------------------- */
348     /*
349      *   Set execution mode.  These calls do not immediately resume
350      *   execution, but merely set the mode that will be in effect when
351      *   execution resumes.  These calls can only be made while the
352      *   program is stopped.
353      */
354 
355     /*
356      *   Set single-step mode to STEP IN.  In this mode, we will stop as
357      *   soon as we reach a new statement.
358      */
set_step_in()359     void set_step_in()
360     {
361         /* set single-step and step-in modes */
362         single_step_ = TRUE;
363         step_in_ = TRUE;
364         step_out_ = FALSE;
365     }
366 
367     /*
368      *   Set single-step mode for a 'break' key (Ctrl-C, Ctrl+Break, etc.,
369      *   according to local conventions).  This asynchronously interrupts the
370      *   interpreter and breaks into the debugger while the program is
371      *   running, which is useful to break out of infinite loops or very
372      *   lengthy processing.
373      */
set_break_stop()374     void set_break_stop()
375     {
376         /* set single-step-in mode */
377         set_step_in();
378 
379         /*
380          *   Forget our last execution location, so that we'll stop again
381          *   even if we haven't moved from our last break location.  Since
382          *   we're explicitly breaking execution, we want to stop whether
383          *   we've gone anywhere or not.
384          */
385         cur_stm_start_ = cur_stm_end_ = 0;
386     }
387 
388     /*
389      *   Set single-step mode to STEP OVER.  In this mode, we will stop as
390      *   soon as we reach a new statement at the same stack level as the
391      *   current statement or at an enclosing stack level.
392      */
393     void set_step_over(VMG0_);
394 
395     /*
396      *   Set single-step mode to STEP OUT.  In this mode, we will stop as
397      *   soon as we reach a new statement at a stack level enclosing the
398      *   current statement's stack level.
399      */
400     void set_step_out(VMG0_);
401 
402     /*
403      *   Set single-step mode to GO.  In this mode, we won't stop until we
404      *   reach a breakpoint.
405      */
set_go()406     void set_go()
407     {
408         /* clear single-step mode */
409         single_step_ = FALSE;
410         step_in_ = FALSE;
411         step_out_ = FALSE;
412     }
413 
414     /*
415      *   Save the step mode in preparation for a recursive execution (of a
416      *   debugger expression), and set the mode for the recursive
417      *   execution.  Turns off stepping modes.
418      */
prepare_for_eval(vmdbg_step_save_t * info)419     void prepare_for_eval(vmdbg_step_save_t *info)
420     {
421         /* save the original execution mode */
422         info->old_step = single_step_;
423         info->old_step_in = step_in_;
424         info->old_step_out = step_out_;
425 
426         /* set execution mode to RUN */
427         single_step_ = FALSE;
428         step_in_ = FALSE;
429         step_out_ = FALSE;
430     }
431 
432     /* restore original execution modes after recursive execution */
restore_from_eval(vmdbg_step_save_t * info)433     void restore_from_eval(vmdbg_step_save_t *info)
434     {
435         single_step_ = info->old_step;
436         step_in_ = info->old_step_in;
437         step_out_ = info->old_step_out;
438     }
439 
440 
441     /* -------------------------------------------------------------------- */
442     /*
443      *   Set the execution pointer to a new location.  The new code
444      *   address is given as an absolute code pool address.  The new
445      *   location must be within the current method.  Updates
446      *   *exec_ofs_ptr with the method offset of the new location.
447      */
set_exec_ofs(unsigned int * exec_ofs_ptr,unsigned long code_addr)448     int set_exec_ofs(unsigned int *exec_ofs_ptr, unsigned long code_addr)
449     {
450         /* set the method offset pointer */
451         *exec_ofs_ptr = (unsigned int)(code_addr - entry_ofs_);
452 
453         /* success */
454         return 0;
455     }
456 
457     /*
458      *   Determine if a code location is within the current active method.
459      *   Returns true if so, false if not.
460      */
461     int is_in_current_method(VMG_ unsigned long code_addr);
462 
463     /* -------------------------------------------------------------------- */
464     /*
465      *   Evaluate an expression.  Returns zero on success, non-zero on
466      *   failure.  'level' is the stack level at which to evaluate the
467      *   expression - 0 is the currently active method, 1 is its caller,
468      *   and so on.
469      */
470     int eval_expr(VMG_ char *buf, size_t buflen, const char *expr,
471                   int level, int *is_lval, int *is_openable,
472                   void (*aggcb)(void *, const char *, int, const char *),
473                   void *aggctx, int speculative);
474 
475     /*
476      *   Compile an expression.  Fills in *code_obj with a handle to the
477      *   code pool object containing the compiled byte-code.  If dst_buf
478      *   is non-null, we will format a message describing any error that
479      *   occurs into the buffer.
480      *
481      *   We'll compile the expression using the given local symbol table
482      *   and the given active stack level.  Note that the local symbol
483      *   table specified need not be the local symbol table for the given
484      *   active stack level, since we're only compiling, not evaluating,
485      *   the expression; for example, when compiling a condition
486      *   expression for a breakpoint, we'll normally compile the
487      *   expression in the context of the breakpoint's source location,
488      *   which might not even be in the active stack when the condition is
489      *   compiled.
490      */
491     int compile_expr(VMG_ const char *expr,
492                      int level, class CVmDbgSymtab *local_symtab,
493                      int self_valid, int speculative, int *is_lval,
494                      class CVmPoolDynObj **code_obj,
495                      char *dst_buf, size_t dst_buf_len);
496 
497     /* -------------------------------------------------------------------- */
498     /*
499      *   Get/set the UI context.  This information is for use by the UI
500      *   code.  We don't use the UI context for anything; we just maintain
501      *   it so that the UI can keep track of what's going on between
502      *   calls.
503      */
get_ui_ctx()504     void *get_ui_ctx() const { return ui_ctx_; }
set_ui_ctx(void * ctx)505     void set_ui_ctx(void *ctx) { ui_ctx_ = ctx; }
506 
507 protected:
508     /* -------------------------------------------------------------------- */
509     /*
510      *   CVmRun API - the byte-code execution loop uses these routines
511      */
512 
513     /*
514      *   Step into the debugger - the byte-code execution loop calls this
515      *   just before executing each instruction when the interpreter is in
516      *   single-step mode, or when a breakpoint is encountered.  We'll
517      *   activate the debugger user interface if necessary.  This returns
518      *   in order to resume execution.  bp is true if we encountered a
519      *   breakpoint, false if we're single-stepping.
520      */
521     void step(VMG_ const uchar **pc_ptr, pool_ofs_t, int bp,
522               int error_code);
523 
524     /*
525      *   synchronize the internal execution point - acts like a step()
526      *   without actually stopping
527      */
528     void sync_exec_pos(VMG_ const uchar *pc_ptr,
529                        pool_ofs_t method_start_ofs);
530 
531     /*
532      *   Get single-step mode - returns true if we're stopping at each
533      *   instruction, false if we're running until we hit a breakpoint.
534      *   The byte-code execution loop should call step() on each
535      *   instruction if this returns true.
536      *
537      *   We must also single-step through code if there are any active
538      *   global breakpoints, even when the user is not interactively
539      *   single-stepping.
540      */
is_single_step()541     int is_single_step() const
542         { return single_step_ || global_bp_cnt_ != 0; }
543 
544 
545     /* -------------------------------------------------------------------- */
546     /*
547      *   Internal operations
548      */
549 
550     /*
551      *   Get information on the execution location at the given stack
552      *   level
553      */
554     int get_stack_level_info(VMG_ int level, class CVmFuncPtr *func_ptr,
555                              class CVmDbgLinePtr *line_ptr,
556                              ulong *stm_start, ulong *stm_end) const;
557 
558     /*
559      *   look up a symbol in one of our reverse mapping tables
560      */
561     const char *find_rev_sym(const class CVmHashTable *hashtab,
562                              ulong val) const;
563 
564     /*
565      *   Format a value into a buffer
566      */
567     void format_val(VMG_ char *buf, size_t buflen,
568                     const struct vm_val_t *val);
569 
570     /* find a breakpoint given a code address */
571     CVmDebugBp *find_bp(ulong code_addr);
572 
573     /* allocate a new breakpoint record */
574     CVmDebugBp *alloc_bp();
575 
576     /*
577      *   suspend/resume breakpoints - this doesn't affect the permanent
578      *   status of any breakpoint, but simply allows for removal of BP
579      *   instructions from the code while the debugger has control
580      */
581     void suspend_all_bps(VMG0_);
582     void restore_all_bps(VMG0_);
583 
584 private:
585     /* flag: debugger has control */
586     int in_debugger_ : 1;
587 
588     /*
589      *   single-step mode - if this is true, we're stepping through code;
590      *   otherwise, we're running until we hit a breakpoint
591      */
592     int single_step_ : 1;
593 
594     /*
595      *   step-in mode - if this is true, and single_step_ is true, we'll
596      *   break as soon as we reach a new statement anywhere; otherwise,
597      *   we'll stop only if we reach a new statement at the same stack
598      *   level as the current statement (in other words, we're stepping
599      *   over subroutine calls made by the current statement)
600      */
601     int step_in_ : 1 ;
602 
603     /*
604      *   Step-out mode - if this is true, and single_step_ is true, we'll
605      *   break as soon as we reach a new statement outside the current
606      *   call level.  (This mode flag is actually only important for
607      *   native code interaction, because we actually know to stop on a
608      *   step-out using step-over with an enclosing stack level.)
609      */
610     int step_out_ : 1;
611 
612     /*
613      *   step-over-breakpoint mode - if this is true, we're stepping over
614      *   a breakpoint
615      */
616     int step_over_bp_ : 1;
617 
618     /*
619      *   Original step flags - these store the step flags that will be in
620      *   effect after we finish a step_over_bp operation
621      */
622     int orig_single_step_ : 1;
623     int orig_step_in_ : 1;
624     int orig_step_out_ : 1;
625 
626     /*
627      *   flag: we've been initialized during program load; when this flag is
628      *   set, we'll have to make corresponding uninitializations when the
629      *   program terminates
630      */
631     int program_inited_ : 1;
632 
633     /* breakpoint being stepped over */
634     CVmDebugBp *step_over_bp_bp_;
635 
636     /*
637      *   Step-resume stack frame level.  When step_in_ is false, we won't
638      *   resume stepping code until the frame pointer is at this level or
639      *   an enclosing level.
640      */
641     size_t step_frame_depth_;
642 
643     /*
644      *   Method header for the current function.  We set this each time we
645      *   enter the debugger via step().
646      */
647     CVmFuncPtr func_ptr_;
648 
649     /*
650      *   Debug records pointer for the current function.  We set this each
651      *   time we enter the debugger via step().  Note that we must keep
652      *   track of whether the debug pointer is valid, because some
653      *   functions might not have debug tables at all.
654      */
655     CVmDbgTablePtr dbg_ptr_;
656     int dbg_ptr_valid_ : 1;
657 
658     /* function header pointer for current function */
659     pool_ofs_t entry_ofs_;
660 
661     /* current program counter */
662     pool_ofs_t pc_;
663 
664     /* debugger line records for current statement */
665     CVmDbgLinePtr cur_stm_line_;
666 
667     /*
668      *   Current statement boundaries.  We set this each time we enter the
669      *   debugger via step().  We usually step through code until we reach
670      *   the beginning of a new statement; we use these boundaries to
671      *   determine when we leave the current statement.  As long as the
672      *   current byte-code offset is within these boundaries, (inclusive),
673      *   we know we're within the same statement.
674      */
675     ulong cur_stm_start_;
676     ulong cur_stm_end_;
677 
678     /*
679      *   User interface context.  We maintain this location for use by the
680      *   UI code to store its context information.
681      */
682     void *ui_ctx_;
683 
684     /*
685      *   Reverse-mapping hash tables for various symbol types.  These hash
686      *   tables allow us to find the symbol for a given object ID,
687      *   property ID, or function address.
688      */
689     class CVmHashTable *obj_rev_table_;
690     class CVmHashTable *prop_rev_table_;
691     class CVmHashTable *func_rev_table_;
692     class CVmHashTable *enum_rev_table_;
693 
694     /* breakpoints */
695     CVmDebugBp bp_[VMDBG_BP_MAX];
696 
697     /*
698      *   Number of global breakpoints in effect (when this is non-zero, we
699      *   must trace through code even in 'go' mode, so we can evaluate the
700      *   global breakpoints repeatedly and thereby catch when the first
701      *   one hits).  This only counts enabled global breakpoints - if a
702      *   global breakpoint is disabled it must be removed from this count,
703      *   since we won't need to consider it when checking for hits.
704      */
705     int global_bp_cnt_;
706 
707     /* host interface (for the compiler's use) */
708     class CTcHostIfcDebug *hostifc_;
709 
710     /*
711      *   method header list - this is a list of the method headers in the
712      *   program, sorted by address
713      */
714     ulong *method_hdr_;
715     ulong method_hdr_cnt_;
716 };
717 
718 /* ------------------------------------------------------------------------ */
719 /*
720  *   Debugger programmatic interface to the user interface.  This is to be
721  *   implemented by each UI subsystem.
722  */
723 class CVmDebugUI
724 {
725 public:
726     /*
727      *   Initialize.  We'll call this once before entering any other
728      *   functions, so that the UI code can set up its private
729      *   information.  The UI can create a private context and store a
730      *   pointer via G_debugger->set_ui_ctx().
731      */
732     static void init(VMG_ const char *image_filename);
733 
734     /*
735      *   Initialization, phase 2 - this is called just after we've
736      *   finished loading the image file.
737      */
738     static void init_after_load(VMG0_);
739 
740     /*
741      *   Terminate.  We'll call this before terminating, to allow the UI
742      *   code to release any resources it allocated.
743      */
744     static void terminate(VMG0_);
745 
746     /*
747      *   Invoke the UI main command loop entrypoint.  The engine calls
748      *   this whenever we hit a breakpoint or reach a single-step point.
749      *   This routine should not return until it is ready to let the
750      *   program continue execution.
751      */
752     static void cmd_loop(VMG_ int bp_number,
753                          int error_code, unsigned int *exec_ofs);
754 };
755 
756 
757 /* ------------------------------------------------------------------------ */
758 /*
759  *   Hash table entry for reverse-mapping hash tables.  These tables allow
760  *   the debugger to look up a symbol name given the symbol's value: an
761  *   object ID, a property ID, or a function address.  We map each of
762  *   these types of values to a ulong value, which we use as the hash key.
763  */
764 class CVmHashEntryDbgRev: public CVmHashEntry
765 {
766 public:
767     /*
768      *   Construct the entry.  sym_val is the symbol's value (an object
769      *   ID, a property ID, or a function address) coerced to a ulong.
770      */
771     CVmHashEntryDbgRev(ulong sym_val, const char *sym, size_t len);
772     ~CVmHashEntryDbgRev();
773 
774     /* check for a match */
775     virtual int matches(const char *str, size_t len) const;
776 
777     /* get the symbol name for this entry */
get_sym()778     const char *get_sym() const { return sym_; }
get_sym_len()779     size_t get_sym_len() const { return sym_len_; }
780 
781 protected:
782     char *sym_;
783     size_t sym_len_;
784 };
785 
786 /*
787  *   Hash function for reverse mapping tables.  This hash function doesn't
788  *   make any assumptions about the range of character values, since we're
789  *   using sequences of raw binary bytes for hash keys.
790  */
791 class CVmHashFuncDbgRev: public CVmHashFunc
792 {
793 public:
794     unsigned int compute_hash(const char *str, size_t len) const;
795 };
796 
797 
798 /* ------------------------------------------------------------------------ */
799 /*
800  *   Condition compilation macros.  Some files can be compiled for
801  *   stand-alone use or use within a debugger application; when compiled
802  *   stand-alone, certain debugger-related operations are removed, which
803  *   can improve performance over the debugger-enabled version.
804  */
805 #ifdef VM_DEBUGGER
806 /*
807  *   DEBUGGER-ENABLED VERSION
808  */
809 
810 /* include debugger-only code */
811 #define VM_IF_DEBUGGER(x)  x
812 
813 /* do NOT include non-debugger-only code */
814 #define VM_IF_NOT_DEBUGGER(x)
815 
816 #else /* VM_DEBUGGER */
817 /*
818  *   STAND-ALONE (NON-DEBUGGER) VERSION
819  */
820 
821 /* do NOT include debugger-only code in a stand-alone version */
822 #define VM_IF_DEBUGGER(x)
823 
824 /* include non-debugger-only code */
825 #define VM_IF_NOT_DEBUGGER(x)  x
826 
827 #endif /* VM_DEBUGGER */
828 
829 #endif /* VMDBG_H */
830 
831