cfad47cfa3/tads3/vmdbg.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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 */
46
    int is_in_use() const { return in_use_; }
47
    void set_in_use(int f) { in_use_ = f; }
48
49
    /* get/set the disabled flag */
50
    int is_disabled() const { return disabled_; }
51
    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 */
62
    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 */
89
    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
     */
97
    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
     */
111
    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 */
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
     */
230
    const char *objid_to_sym(vm_obj_id_t objid) const
231
        { return find_rev_sym(obj_rev_table_, (ulong)objid); }
232
    
233
    const char *propid_to_sym(vm_prop_id_t propid) const
234
        { return find_rev_sym(prop_rev_table_, (ulong)propid); }
235
236
    const char *funcaddr_to_sym(pool_ofs_t func_addr) const
237
        { return find_rev_sym(func_rev_table_, (ulong)func_addr); }
238
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 */
266
    ulong get_method_header_cnt() const { return method_hdr_cnt_; }
267
268
    /* set the given method header list entry */
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 */
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
     */
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 DEBUG TRACE mode.  This activates single-step mode from within a
369
     *   VM intrinsic. 
370
     */
371
    void set_debug_trace()
372
    {
373
        /* 
374
         *   set single-step mode so that we stop as soon as we're back in
375
         *   byte code 
376
         */
377
        set_step_in();
378
379
        /* 
380
         *   clear any current statement location, so that we stop
381
         *   immediately as soon as we get back to byte code, even if our
382
         *   last debugger entry was in the same line of code 
383
         */
384
        cur_stm_start_ = cur_stm_end_ = 0;
385
    }
386
387
    /* 
388
     *   Set single-step mode for a 'break' key (Ctrl-C, Ctrl+Break, etc.,
389
     *   according to local conventions).  This asynchronously interrupts the
390
     *   interpreter and breaks into the debugger while the program is
391
     *   running, which is useful to break out of infinite loops or very
392
     *   lengthy processing.  
393
     */
394
    void set_break_stop()
395
    {
396
        /* set single-step-in mode */
397
        set_step_in();
398
399
        /* 
400
         *   Forget our last execution location, so that we'll stop again
401
         *   even if we haven't moved from our last break location.  Since
402
         *   we're explicitly breaking execution, we want to stop whether
403
         *   we've gone anywhere or not.  
404
         */
405
        cur_stm_start_ = cur_stm_end_ = 0;
406
    }
407
408
    /*
409
     *   Set single-step mode to STEP OVER.  In this mode, we will stop as
410
     *   soon as we reach a new statement at the same stack level as the
411
     *   current statement or at an enclosing stack level. 
412
     */
413
    void set_step_over(VMG0_);
414
415
    /*
416
     *   Set single-step mode to STEP OUT.  In this mode, we will stop as
417
     *   soon as we reach a new statement at a stack level enclosing the
418
     *   current statement's stack level. 
419
     */
420
    void set_step_out(VMG0_);
421
422
    /*
423
     *   Set single-step mode to GO.  In this mode, we won't stop until we
424
     *   reach a breakpoint.
425
     */
426
    void set_go()
427
    {
428
        /* clear single-step mode */
429
        single_step_ = FALSE;
430
        step_in_ = FALSE;
431
        step_out_ = FALSE;
432
    }
433
434
    /*
435
     *   Save the step mode in preparation for a recursive execution (of a
436
     *   debugger expression), and set the mode for the recursive
437
     *   execution.  Turns off stepping modes. 
438
     */
439
    void prepare_for_eval(vmdbg_step_save_t *info)
440
    {
441
        /* save the original execution mode */
442
        info->old_step = single_step_;
443
        info->old_step_in = step_in_;
444
        info->old_step_out = step_out_;
445
446
        /* set execution mode to RUN */
447
        single_step_ = FALSE;
448
        step_in_ = FALSE;
449
        step_out_ = FALSE;
450
    }
451
452
    /* restore original execution modes after recursive execution */
453
    void restore_from_eval(vmdbg_step_save_t *info)
454
    {
455
        single_step_ = info->old_step;
456
        step_in_ = info->old_step_in;
457
        step_out_ = info->old_step_out;
458
    }
459
460
461
    /* -------------------------------------------------------------------- */
462
    /*
463
     *   Set the execution pointer to a new location.  The new code
464
     *   address is given as an absolute code pool address.  The new
465
     *   location must be within the current method.  Updates
466
     *   *exec_ofs_ptr with the method offset of the new location.  
467
     */
468
    int set_exec_ofs(unsigned int *exec_ofs_ptr, unsigned long code_addr)
469
    {
470
        /* set the method offset pointer */
471
        *exec_ofs_ptr = (unsigned int)(code_addr - entry_ofs_);
472
473
        /* success */
474
        return 0;
475
    }
476
477
    /*
478
     *   Determine if a code location is within the current active method.
479
     *   Returns true if so, false if not. 
480
     */
481
    int is_in_current_method(VMG_ unsigned long code_addr);
482
483
    /* -------------------------------------------------------------------- */
484
    /*
485
     *   Evaluate an expression.  Returns zero on success, non-zero on
486
     *   failure.  'level' is the stack level at which to evaluate the
487
     *   expression - 0 is the currently active method, 1 is its caller,
488
     *   and so on.  
489
     */
490
    int eval_expr(VMG_ char *buf, size_t buflen, const char *expr,
491
                  int level, int *is_lval, int *is_openable,
492
                  void (*aggcb)(void *, const char *, int, const char *),
493
                  void *aggctx, int speculative);
494
495
    /*
496
     *   Compile an expression.  Fills in *code_obj with a handle to the
497
     *   code pool object containing the compiled byte-code.  If dst_buf
498
     *   is non-null, we will format a message describing any error that
499
     *   occurs into the buffer.
500
     *   
501
     *   We'll compile the expression using the given local symbol table
502
     *   and the given active stack level.  Note that the local symbol
503
     *   table specified need not be the local symbol table for the given
504
     *   active stack level, since we're only compiling, not evaluating,
505
     *   the expression; for example, when compiling a condition
506
     *   expression for a breakpoint, we'll normally compile the
507
     *   expression in the context of the breakpoint's source location,
508
     *   which might not even be in the active stack when the condition is
509
     *   compiled.  
510
     */
511
    int compile_expr(VMG_ const char *expr,
512
                     int level, class CVmDbgSymtab *local_symtab,
513
                     int self_valid, int speculative, int *is_lval,
514
                     class CVmPoolDynObj **code_obj,
515
                     char *dst_buf, size_t dst_buf_len);
516
517
    /* -------------------------------------------------------------------- */
518
    /*
519
     *   Get/set the UI context.  This information is for use by the UI
520
     *   code.  We don't use the UI context for anything; we just maintain
521
     *   it so that the UI can keep track of what's going on between
522
     *   calls. 
523
     */
524
    void *get_ui_ctx() const { return ui_ctx_; }
525
    void set_ui_ctx(void *ctx) { ui_ctx_ = ctx; }
526
527
protected:
528
    /* -------------------------------------------------------------------- */
529
    /*
530
     *   CVmRun API - the byte-code execution loop uses these routines 
531
     */
532
    
533
    /* 
534
     *   Step into the debugger - the byte-code execution loop calls this
535
     *   just before executing each instruction when the interpreter is in
536
     *   single-step mode, or when a breakpoint is encountered.  We'll
537
     *   activate the debugger user interface if necessary.  This returns
538
     *   in order to resume execution.  bp is true if we encountered a
539
     *   breakpoint, false if we're single-stepping.  
540
     */
541
    void step(VMG_ const uchar **pc_ptr, pool_ofs_t, int bp,
542
              int error_code);
543
544
    /* 
545
     *   synchronize the internal execution point - acts like a step()
546
     *   without actually stopping 
547
     */
548
    void sync_exec_pos(VMG_ const uchar *pc_ptr,
549
                       pool_ofs_t method_start_ofs);
550
551
    /* 
552
     *   Get single-step mode - returns true if we're stopping at each
553
     *   instruction, false if we're running until we hit a breakpoint.
554
     *   The byte-code execution loop should call step() on each
555
     *   instruction if this returns true.
556
     *   
557
     *   We must also single-step through code if there are any active
558
     *   global breakpoints, even when the user is not interactively
559
     *   single-stepping.  
560
     */
561
    int is_single_step() const
562
        { return single_step_ || global_bp_cnt_ != 0; }
563
564
565
    /* -------------------------------------------------------------------- */
566
    /*
567
     *   Internal operations 
568
     */
569
570
    /*
571
     *   Get information on the execution location at the given stack
572
     *   level 
573
     */
574
    int get_stack_level_info(VMG_ int level, class CVmFuncPtr *func_ptr,
575
                             class CVmDbgLinePtr *line_ptr,
576
                             ulong *stm_start, ulong *stm_end) const;
577
578
    /* 
579
     *   look up a symbol in one of our reverse mapping tables
580
     */
581
    const char *find_rev_sym(const class CVmHashTable *hashtab,
582
                             ulong val) const;
583
584
    /*
585
     *   Format a value into a buffer 
586
     */
587
    void format_val(VMG_ char *buf, size_t buflen,
588
                    const struct vm_val_t *val);
589
590
    /* find a breakpoint given a code address */
591
    CVmDebugBp *find_bp(ulong code_addr);
592
593
    /* allocate a new breakpoint record */
594
    CVmDebugBp *alloc_bp();
595
596
    /* 
597
     *   suspend/resume breakpoints - this doesn't affect the permanent
598
     *   status of any breakpoint, but simply allows for removal of BP
599
     *   instructions from the code while the debugger has control 
600
     */
601
    void suspend_all_bps(VMG0_);
602
    void restore_all_bps(VMG0_);
603
604
private:
605
    /* flag: debugger has control */
606
    int in_debugger_ : 1;
607
    
608
    /* 
609
     *   single-step mode - if this is true, we're stepping through code;
610
     *   otherwise, we're running until we hit a breakpoint 
611
     */
612
    int single_step_ : 1;
613
614
    /* 
615
     *   step-in mode - if this is true, and single_step_ is true, we'll
616
     *   break as soon as we reach a new statement anywhere; otherwise,
617
     *   we'll stop only if we reach a new statement at the same stack
618
     *   level as the current statement (in other words, we're stepping
619
     *   over subroutine calls made by the current statement) 
620
     */
621
    int step_in_ : 1 ;
622
623
    /* 
624
     *   Step-out mode - if this is true, and single_step_ is true, we'll
625
     *   break as soon as we reach a new statement outside the current
626
     *   call level.  (This mode flag is actually only important for
627
     *   native code interaction, because we actually know to stop on a
628
     *   step-out using step-over with an enclosing stack level.)  
629
     */
630
    int step_out_ : 1;
631
632
    /* 
633
     *   step-over-breakpoint mode - if this is true, we're stepping over
634
     *   a breakpoint 
635
     */
636
    int step_over_bp_ : 1;
637
638
    /* 
639
     *   Original step flags - these store the step flags that will be in
640
     *   effect after we finish a step_over_bp operation 
641
     */
642
    int orig_single_step_ : 1;
643
    int orig_step_in_ : 1;
644
    int orig_step_out_ : 1;
645
646
    /* 
647
     *   flag: we've been initialized during program load; when this flag is
648
     *   set, we'll have to make corresponding uninitializations when the
649
     *   program terminates 
650
     */
651
    int program_inited_ : 1;
652
653
    /* breakpoint being stepped over */
654
    CVmDebugBp *step_over_bp_bp_;
655
656
    /*
657
     *   Step-resume stack frame level.  When step_in_ is false, we won't
658
     *   resume stepping code until the frame pointer is at this level or
659
     *   an enclosing level.  
660
     */
661
    size_t step_frame_depth_;
662
663
    /* 
664
     *   Method header for the current function.  We set this each time we
665
     *   enter the debugger via step(). 
666
     */
667
    CVmFuncPtr func_ptr_;
668
669
    /*
670
     *   Debug records pointer for the current function.  We set this each
671
     *   time we enter the debugger via step().  Note that we must keep
672
     *   track of whether the debug pointer is valid, because some
673
     *   functions might not have debug tables at all.  
674
     */
675
    CVmDbgTablePtr dbg_ptr_;
676
    int dbg_ptr_valid_ : 1;
677
678
    /* function header pointer for current function */
679
    pool_ofs_t entry_ofs_;
680
681
    /* current program counter */
682
    pool_ofs_t pc_;
683
684
    /* debugger line records for current statement */
685
    CVmDbgLinePtr cur_stm_line_;
686
687
    /* 
688
     *   Current statement boundaries.  We set this each time we enter the
689
     *   debugger via step().  We usually step through code until we reach
690
     *   the beginning of a new statement; we use these boundaries to
691
     *   determine when we leave the current statement.  As long as the
692
     *   current byte-code offset is within these boundaries, (inclusive),
693
     *   we know we're within the same statement.  
694
     */
695
    ulong cur_stm_start_;
696
    ulong cur_stm_end_;
697
698
    /*
699
     *   User interface context.  We maintain this location for use by the
700
     *   UI code to store its context information. 
701
     */
702
    void *ui_ctx_;
703
704
    /*
705
     *   Reverse-mapping hash tables for various symbol types.  These hash
706
     *   tables allow us to find the symbol for a given object ID,
707
     *   property ID, or function address. 
708
     */
709
    class CVmHashTable *obj_rev_table_;
710
    class CVmHashTable *prop_rev_table_;
711
    class CVmHashTable *func_rev_table_;
712
    class CVmHashTable *enum_rev_table_;
713
714
    /* breakpoints */
715
    CVmDebugBp bp_[VMDBG_BP_MAX];
716
717
    /* 
718
     *   Number of global breakpoints in effect (when this is non-zero, we
719
     *   must trace through code even in 'go' mode, so we can evaluate the
720
     *   global breakpoints repeatedly and thereby catch when the first
721
     *   one hits).  This only counts enabled global breakpoints - if a
722
     *   global breakpoint is disabled it must be removed from this count,
723
     *   since we won't need to consider it when checking for hits.  
724
     */
725
    int global_bp_cnt_;
726
727
    /* host interface (for the compiler's use) */
728
    class CTcHostIfcDebug *hostifc_;
729
730
    /* 
731
     *   method header list - this is a list of the method headers in the
732
     *   program, sorted by address 
733
     */
734
    ulong *method_hdr_;
735
    ulong method_hdr_cnt_;
736
};
737
738
/* ------------------------------------------------------------------------ */
739
/*
740
 *   Debugger programmatic interface to the user interface.  This is to be
741
 *   implemented by each UI subsystem.  
742
 */
743
class CVmDebugUI
744
{
745
public:
746
    /* 
747
     *   Initialize.  We'll call this once before entering any other
748
     *   functions, so that the UI code can set up its private
749
     *   information.  The UI can create a private context and store a
750
     *   pointer via G_debugger->set_ui_ctx().  
751
     */
752
    static void init(VMG_ const char *image_filename);
753
754
    /*
755
     *   Initialization, phase 2 - this is called just after we've
756
     *   finished loading the image file.
757
     */
758
    static void init_after_load(VMG0_);
759
760
    /*
761
     *   Terminate.  We'll call this before terminating, to allow the UI
762
     *   code to release any resources it allocated. 
763
     */
764
    static void terminate(VMG0_);
765
    
766
    /* 
767
     *   Invoke the UI main command loop entrypoint.  The engine calls
768
     *   this whenever we hit a breakpoint or reach a single-step point.
769
     *   This routine should not return until it is ready to let the
770
     *   program continue execution. 
771
     */
772
    static void cmd_loop(VMG_ int bp_number,
773
                         int error_code, unsigned int *exec_ofs);
774
};
775
776
777
/* ------------------------------------------------------------------------ */
778
/*
779
 *   Hash table entry for reverse-mapping hash tables.  These tables allow
780
 *   the debugger to look up a symbol name given the symbol's value: an
781
 *   object ID, a property ID, or a function address.  We map each of
782
 *   these types of values to a ulong value, which we use as the hash key.
783
 */
784
class CVmHashEntryDbgRev: public CVmHashEntry
785
{
786
public:
787
    /*
788
     *   Construct the entry.  sym_val is the symbol's value (an object
789
     *   ID, a property ID, or a function address) coerced to a ulong.
790
     */
791
    CVmHashEntryDbgRev(ulong sym_val, const char *sym, size_t len);
792
    ~CVmHashEntryDbgRev();
793
794
    /* check for a match */
795
    virtual int matches(const char *str, size_t len) const;
796
797
    /* get the symbol name for this entry */
798
    const char *get_sym() const { return sym_; }
799
    size_t get_sym_len() const { return sym_len_; }
800
801
protected:
802
    char *sym_;
803
    size_t sym_len_;
804
};
805
806
/*
807
 *   Hash function for reverse mapping tables.  This hash function doesn't
808
 *   make any assumptions about the range of character values, since we're
809
 *   using sequences of raw binary bytes for hash keys.  
810
 */
811
class CVmHashFuncDbgRev: public CVmHashFunc
812
{
813
public:
814
    unsigned int compute_hash(const char *str, size_t len) const;
815
};
816
817
818
/* ------------------------------------------------------------------------ */
819
/*
820
 *   Condition compilation macros.  Some files can be compiled for
821
 *   stand-alone use or use within a debugger application; when compiled
822
 *   stand-alone, certain debugger-related operations are removed, which
823
 *   can improve performance over the debugger-enabled version. 
824
 */
825
#ifdef VM_DEBUGGER
826
/*
827
 *   DEBUGGER-ENABLED VERSION 
828
 */
829
830
/* include debugger-only code */
831
#define VM_IF_DEBUGGER(x)  x
832
833
/* do NOT include non-debugger-only code */
834
#define VM_IF_NOT_DEBUGGER(x)
835
836
#else /* VM_DEBUGGER */
837
/*
838
 *   STAND-ALONE (NON-DEBUGGER) VERSION
839
 */
840
841
/* do NOT include debugger-only code in a stand-alone version */
842
#define VM_IF_DEBUGGER(x)
843
844
/* include non-debugger-only code */
845
#define VM_IF_NOT_DEBUGGER(x)  x
846
847
#endif /* VM_DEBUGGER */
848
849
#endif /* VMDBG_H */
850