1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header$";
4 #endif
5 
6 /*
7  *   Copyright (c) 1999, 2002 Michael J. Roberts.  All Rights Reserved.
8  *
9  *   Please see the accompanying license file, LICENSE.TXT, for information
10  *   on using and copying this software.
11  */
12 /*
13 Name
14   vmdbg.cpp - T3 VM debugger API
15 Function
16 
17 Notes
18 
19 Modified
20   11/23/99 MJRoberts  - Creation
21 */
22 
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include "t3std.h"
27 #include "os.h"
28 #include "charmap.h"
29 #include "vmglob.h"
30 #include "vmtype.h"
31 #include "vmdbg.h"
32 #include "vmrun.h"
33 #include "vmsrcf.h"
34 #include "vmhash.h"
35 #include "vmpool.h"
36 #include "vmop.h"
37 #include "vmlst.h"
38 #include "tctarg.h"
39 #include "tcglob.h"
40 #include "tctok.h"
41 #include "tcprs.h"
42 #include "tcmain.h"
43 #include "resload.h"
44 #include "tchostsi.h"
45 #include "tchost.h"
46 #include "tcgen.h"
47 #include "vmimage.h"
48 #include "vmhost.h"
49 #include "vmvec.h"
50 #include "vmbignum.h"
51 #include "vmanonfn.h"
52 
53 
54 /* ------------------------------------------------------------------------ */
55 /*
56  *   Special override host interface for compiling expressions in the
57  *   debugger.  This host interface allows us to capture error messages
58  *   generated during compilation for display in the debugger user
59  *   interface.
60  */
61 class CTcHostIfcDebug: public CTcHostIfc
62 {
63 public:
CTcHostIfcDebug()64     CTcHostIfcDebug()
65     {
66         /* initially clear the message buffer */
67         reset_messages();
68     }
69 
70     /* clear the message buffer */
reset_messages()71     void reset_messages()
72     {
73         errmsg_[0] = '\0';
74         errmsg_free_ = errmsg_;
75         have_errmsg_ = FALSE;
76     }
77 
78     /* get the text of the first error message since we cleared the buffer */
get_error_msg() const79     const char *get_error_msg() const { return errmsg_; }
80 
81     /*
82      *   CTcHostIfc interface
83      */
84 
85     /* display an informational message */
v_print_msg(const char *,va_list)86     virtual void v_print_msg(const char *, va_list)
87     {
88         /* ignore informational messages */
89     }
90 
91     /* display a process step message */
v_print_step(const char *,va_list)92     virtual void v_print_step(const char *, va_list)
93     {
94         /* ignore process step messages */
95     }
96 
97     /* display an error message */
v_print_err(const char * msg,va_list args)98     virtual void v_print_err(const char *msg, va_list args)
99     {
100         /* if we haven't already captured an error message, capture it */
101         if (!have_errmsg_)
102         {
103             char buf[1024];
104 
105             /* format the message */
106             t3vsprintf(buf, sizeof(buf), msg, args);
107 
108             /* append as much of it as possible to our buffer */
109             strncpy(errmsg_free_, buf,
110                     sizeof(errmsg_) - (errmsg_free_ - errmsg_) - 1);
111 
112             /* ensure the message is null-terminated */
113             errmsg_[sizeof(errmsg_) - 1] = '\0';
114 
115             /* advance the free pointer past what we just added */
116             errmsg_free_ += strlen(errmsg_free_);
117 
118             /*
119              *   if there's a newline in the appended message, we're done
120              *   gathering this message line
121              */
122             if (strchr(buf, '\n') != 0)
123             {
124                 char *p;
125 
126                 /* note that we have the complete first error line now */
127                 have_errmsg_ = TRUE;
128 
129                 /* terminate the message at the newline */
130                 p = strchr(errmsg_, '\n');
131                 if (p != 0)
132                     *p = '\0';
133             }
134         }
135     }
136 
137 private:
138     /* our error message buffer */
139     char errmsg_[256];
140 
141     /* pointer to next available byte of message buffer */
142     char *errmsg_free_;
143 
144     /* flag: we've captured an error message */
145     uint have_errmsg_ : 1;
146 };
147 
148 
149 /* ------------------------------------------------------------------------ */
150 /*
151  *   Compiler parser symbol table interface.  This provides an
152  *   implementation of the compiler parser's debugger symbol table
153  *   interface, which allows the compiler to look up symbols in the image
154  *   file's debug records.
155  */
156 class CVmDbgSymtab: public CTcPrsDbgSymtab
157 {
158 public:
CVmDbgSymtab()159     CVmDbgSymtab()
160     {
161         globals_ = 0;
162         frame_id_ = 0;
163     }
164 
165     /* initialize in a given scope */
CVmDbgSymtab(VMG_ const CVmDbgTablePtr * debug_table,uint frame_id,uint stack_level)166     CVmDbgSymtab(VMG_ const CVmDbgTablePtr *debug_table, uint frame_id,
167                  uint stack_level)
168     {
169         /* initialize with the given parameters */
170         init(vmg_ debug_table, frame_id, stack_level);
171     }
172 
173     /* find a symbol - implementation of CTcPrsDbgSymtab interface */
174     virtual int find_symbol(const textchar_t *sym, size_t len,
175                             tcprsdbg_sym_info *info);
176 
init(VMG_ const CVmDbgTablePtr * debug_table,uint frame_id,uint stack_level)177     void init(VMG_ const CVmDbgTablePtr *debug_table, uint frame_id,
178               uint stack_level)
179     {
180         /* save the globals if necessary */
181         globals_ = VMGLOB_ADDR;
182 
183         /* save my debug table pointer */
184         debug_ptr_.copy_from(debug_table);
185 
186         /* save my frame ID */
187         frame_id_ = frame_id;
188 
189         /* save my stack level */
190         stack_level_ = stack_level;
191     }
192 
193 private:
194     /* VM globals */
195     vm_globals *globals_;
196 
197     /* my debug records table */
198     CVmDbgTablePtr debug_ptr_;
199 
200     /* active frame ID for this scope */
201     uint frame_id_;
202 
203     /* stack frame level */
204     uint stack_level_;
205 };
206 
207 /*
208  *   Find a symbol
209  */
find_symbol(const textchar_t * sym,size_t len,tcprsdbg_sym_info * info)210 int CVmDbgSymtab::find_symbol(const textchar_t *sym, size_t len,
211                               tcprsdbg_sym_info *info)
212 {
213     CVmDbgFramePtr frame;
214     VMGLOB_PTR(globals_);
215 
216     /* if I have no local frame, there's nothing to do */
217     if (frame_id_ == 0)
218         return FALSE;
219 
220     /*
221      *   search the enclosing frames, starting with my innermost frame and
222      *   working outwards
223      */
224     debug_ptr_.set_frame_ptr(vmg_ &frame, frame_id_);
225     for (;;)
226     {
227         CVmDbgFrameSymPtr sym_ptr;
228         uint i;
229 
230         /* iterate over the symbols in this frame */
231         for (i = frame.get_sym_count(), frame.set_first_sym_ptr(&sym_ptr) ;
232              i != 0 ;
233              --i, sym_ptr.inc(vmg0_))
234         {
235             /* if this one matches, we found it */
236             if (sym_ptr.get_sym_len(vmg0_) == len
237                 && memcmp(sym_ptr.get_sym(vmg0_), sym, len) == 0)
238             {
239                 /* fill in the type information for the caller */
240                 if (sym_ptr.is_ctx_local())
241                 {
242                     /* it's a context local */
243                     info->sym_type = TC_SYM_LOCAL;
244                     info->ctx_arr_idx = sym_ptr.get_ctx_arr_idx();
245 
246                     /* fill in the local variable ID and stack level */
247                     info->var_id = sym_ptr.get_var_num();
248                     info->frame_idx = stack_level_;
249                 }
250                 else if (sym_ptr.is_local())
251                 {
252                     /* it's a local */
253                     info->sym_type = TC_SYM_LOCAL;
254                     info->ctx_arr_idx = 0;
255 
256                     /* fill in the local variable ID and stack level */
257                     info->var_id = sym_ptr.get_var_num();
258                     info->frame_idx = stack_level_;
259                 }
260                 else if (sym_ptr.is_param())
261                 {
262                     /* it's a parameter variable */
263                     info->sym_type = TC_SYM_PARAM;
264                     info->ctx_arr_idx = 0;
265 
266                     /* fill in the parameter ID and stack level */
267                     info->var_id = sym_ptr.get_var_num();
268                     info->frame_idx = stack_level_;
269                 }
270                 else
271                 {
272                     /* unknown symbol type */
273                     info->sym_type = TC_SYM_UNKNOWN;
274                 }
275 
276                 /* tell the caller we found it */
277                 return TRUE;
278             }
279         }
280 
281         /* if there's no enclosing frame, we're done */
282         if (frame.get_enclosing_frame() == 0)
283             break;
284 
285         /* move on to the enclosing frame */
286         debug_ptr_.set_frame_ptr(vmg_ &frame, frame.get_enclosing_frame());
287     }
288 
289     /* failure */
290     return FALSE;
291 }
292 
293 
294 /* ------------------------------------------------------------------------ */
295 /*
296  *   Creation
297  */
CVmDebug(VMG0_)298 CVmDebug::CVmDebug(VMG0_)
299 {
300     /* we have not yet been initialized during program load */
301     program_inited_ = FALSE;
302 
303     /* we have no UI context yet */
304     ui_ctx_ = 0;
305 
306     /* not currently in the debugger */
307     in_debugger_ = FALSE;
308 
309     /* there's no valid debug pointer yet */
310     dbg_ptr_valid_ = FALSE;
311 
312     /* no method headers loaded yet */
313     method_hdr_ = 0;
314     method_hdr_cnt_ = 0;
315 
316     /*
317      *   start in step-in mode, so that we break as soon as we start
318      *   executing the program
319      */
320     set_step_in();
321 
322     /* note in step-over-breakpoint mode yet */
323     step_over_bp_ = FALSE;
324     step_frame_depth_ = 0;
325 
326     /* no global breakpoints yet */
327     global_bp_cnt_ = 0;
328 
329     /*
330      *   we have no valid source line bounds yet, so set both ends to zero
331      *   - zero is never a valid PC address (even if a function is at
332      *   address zero, its method header will precede any executable code
333      *   in the function, hence the program counter could never be at
334      *   zero)
335      */
336     cur_stm_start_ = cur_stm_end_ = 0;
337 
338     /* create our reverse-lookup hash tables */
339     obj_rev_table_ = new CVmHashTable(256, new CVmHashFuncDbgRev, TRUE);
340     prop_rev_table_ = new CVmHashTable(256, new CVmHashFuncDbgRev, TRUE);
341     func_rev_table_ = new CVmHashTable(256, new CVmHashFuncDbgRev, TRUE);
342     enum_rev_table_ = new CVmHashTable(256, new CVmHashFuncDbgRev, TRUE);
343 
344     /* create a host interface for the compiler */
345     hostifc_ = new CTcHostIfcDebug();
346 
347     /* no halt requested yet */
348     G_interpreter->set_halt_vm(FALSE);
349 }
350 
351 /*
352  *   Deletion
353  */
~CVmDebug()354 CVmDebug::~CVmDebug()
355 {
356     /* delete the host interface object */
357     delete hostifc_;
358 
359     /* delete our reverse-lookup hash tables */
360     delete obj_rev_table_;
361     delete prop_rev_table_;
362     delete func_rev_table_;
363     delete enum_rev_table_;
364 
365     /* if we allocated a method header array, delete it */
366     if (method_hdr_ != 0)
367         t3free(method_hdr_);
368 }
369 
370 /* ------------------------------------------------------------------------ */
371 /*
372  *   VM initialization
373  */
init(VMG_ const char * image_fname)374 void CVmDebug::init(VMG_ const char *image_fname)
375 {
376     /* note that the program has been initialized */
377     program_inited_ = TRUE;
378 
379     /* tell the UI to initialize */
380     CVmDebugUI::init(vmg_ image_fname);
381 
382     /* initialize the compiler */
383     CTcMain::init(hostifc_, G_host_ifc->get_cmap_res_loader(), 0);
384 }
385 
386 /*
387  *   Initialization phase 2 - after loading the image file
388  */
init_after_load(VMG0_)389 void CVmDebug::init_after_load(VMG0_)
390 {
391     /* tell the UI to initialize */
392     CVmDebugUI::init_after_load(vmg0_);
393 }
394 
395 /*
396  *   VM termination
397  */
terminate(VMG0_)398 void CVmDebug::terminate(VMG0_)
399 {
400     CVmDebugBp *bp;
401     size_t i;
402 
403     /* if we were never initialized, there's nothing to terminate */
404     if (!program_inited_)
405         return;
406 
407     /* tell the UI to terminate */
408     CVmDebugUI::terminate(vmg0_);
409 
410     /* tell the breakpoint objects we're about to terminate */
411     for (i = 0, bp = bp_ ; i < VMDBG_BP_MAX ; ++i, ++bp)
412         bp->do_terminate(vmg0_);
413 
414     /* terminate the compiler */
415     CTcMain::terminate();
416 }
417 
418 
419 /* ------------------------------------------------------------------------ */
420 /*
421  *   Determine if the image file has debugger information
422  */
image_has_debug_info(VMG0_) const423 int CVmDebug::image_has_debug_info(VMG0_) const
424 {
425     /* ask the image loader if there's a GSYM block */
426     return G_image_loader->has_gsym();
427 }
428 
429 
430 /* ------------------------------------------------------------------------ */
431 /*
432  *   Add an entry to the reverse-lookup hash tables
433  */
add_rev_sym(const char * sym,size_t sym_len,tc_symtype_t sym_type,ulong sym_val)434 void CVmDebug::add_rev_sym(const char *sym, size_t sym_len,
435                            tc_symtype_t sym_type, ulong sym_val)
436 {
437     CVmHashTable *table;
438     CVmHashEntryDbgRev *entry;
439 
440     /* figure out which table to use, based on the symbol type */
441     switch(sym_type)
442     {
443     case TC_SYM_FUNC:
444         /* function */
445         table = func_rev_table_;
446         break;
447 
448     case TC_SYM_OBJ:
449         /* object */
450         table = obj_rev_table_;
451         break;
452 
453     case TC_SYM_PROP:
454         /* property */
455         table = prop_rev_table_;
456         break;
457 
458     case TC_SYM_ENUM:
459         /* enumerator */
460         table = enum_rev_table_;
461         break;
462 
463     default:
464         /*
465          *   ignore other symbol types - we don't maintain reverse-lookup
466          *   tables for anything else
467          */
468         return;
469     }
470 
471     /* create the symbol */
472     entry = new CVmHashEntryDbgRev(sym_val, sym, sym_len);
473 
474     /* add it to the table */
475     table->add(entry);
476 }
477 
478 /*
479  *   Look up a symbol in one of our reverse-mapping tables
480  */
find_rev_sym(const CVmHashTable * tab,ulong val) const481 const char *CVmDebug::find_rev_sym(const CVmHashTable *tab,
482                                    ulong val) const
483 {
484     CVmHashEntryDbgRev *entry;
485 
486     /* look up the symbol */
487     entry = (CVmHashEntryDbgRev *)tab->find((char *)&val, sizeof(val));
488 
489     /* if we found it, return the name pointer, otherwise null */
490     return (entry != 0 ? entry->get_sym() : 0);
491 }
492 
493 /*
494  *   Find the actual symbol for a 'modify' chain.
495  */
get_modifying_sym(const char * sym_name) const496 const char *CVmDebug::get_modifying_sym(const char *sym_name) const
497 {
498     /* if there's no original symbol, there's obviously no modifier */
499     if (sym_name == 0)
500         return 0;
501 
502     /* keep going until we find the base object */
503     for (;;)
504     {
505         CTcSymObj *sym;
506 
507         /* look up the symbol in the global symbol table */
508         sym = (CTcSymObj *)G_prs->get_global_symtab()
509               ->find(sym_name, strlen(sym_name));
510 
511         /*
512          *   if we found it, and it has a modifier object, proceed up to the
513          *   modifier object
514          */
515         if (sym != 0
516             && sym->get_type() == TC_SYM_OBJ
517             && sym->get_modifying_obj_id() != VM_INVALID_OBJ)
518         {
519             const char *new_sym_name;
520 
521             /*
522              *   Forget the current object, and use the modification object
523              *   instead.  Look up the symbol name for the modification
524              *   object, and replace the previous symbol - which was just a
525              *   fake symbol that the compiler synthesized anyway - with the
526              *   modifying object.  At the top of the chain of modifying
527              *   objects is the actual symbol name used in the source code,
528              *   which is what we're after.
529              */
530             new_sym_name = objid_to_sym(sym->get_modifying_obj_id());
531 
532             /*
533              *   if we can't find the modifying object name, stop at the
534              *   previous symbol
535              */
536             if (new_sym_name == 0)
537                 return sym_name;
538 
539             /* we got a symbol - use it */
540             sym_name = new_sym_name;
541         }
542         else
543         {
544             /*
545              *   There's either no symbol or no modifier, so we can stop
546              *   looking.  Simply return what we have.
547              */
548             return sym_name;
549         }
550     }
551 }
552 
553 
554 /* ------------------------------------------------------------------------ */
555 /*
556  *   Get information on the source location at a given stack level
557  */
get_source_info(VMG_ const char ** fname,unsigned long * linenum,int level) const558 int CVmDebug::get_source_info(VMG_ const char **fname,
559                               unsigned long *linenum, int level) const
560 {
561     CVmFuncPtr func_ptr;
562     CVmDbgLinePtr line_ptr;
563     CVmSrcfEntry *srcf_entry;
564     ulong stm_start;
565     ulong stm_end;
566 
567     /* if there's no source file table, we can't get any information */
568     if (G_srcf_table == 0)
569         return 1;
570 
571     /* get information on the execution location at the requested level */
572     if (get_stack_level_info(vmg_ level, &func_ptr, &line_ptr,
573                              &stm_start, &stm_end))
574         return 1;
575 
576     /* get the source file entry for this line's source file index */
577     srcf_entry = G_srcf_table->get_entry(line_ptr.get_source_id());
578 
579     /* if we didn't find an entry, we can't return any information */
580     if (srcf_entry == 0)
581         return 1;
582 
583     /* fill in the caller's filename pointer */
584     *fname = srcf_entry->get_name();
585 
586     /* fill in the caller's line number record */
587     *linenum = line_ptr.get_source_line();
588 
589     /* success */
590     return 0;
591 }
592 
593 /* ------------------------------------------------------------------------ */
594 /*
595  *   Enumerate local variables at the given stack level
596  */
enum_locals(VMG_ void (* cbfunc)(void *,const char *,size_t),void * cbctx,int level)597 void CVmDebug::enum_locals(VMG_ void (*cbfunc)(void *, const char *, size_t),
598                            void *cbctx, int level)
599 {
600     CVmFuncPtr func_ptr;
601     CVmDbgLinePtr line_ptr;
602     CVmDbgTablePtr dbg_ptr;
603     CVmDbgFramePtr frame_ptr;
604     CVmDbgFrameSymPtr sym;
605     ulong stm_start;
606     ulong stm_end;
607     uint i;
608     uint frame_id;
609 
610     /* get information on the execution location at the requested level */
611     if (get_stack_level_info(vmg_ level, &func_ptr, &line_ptr,
612                              &stm_start, &stm_end))
613         return;
614 
615     /* set up a debug table pointer for the function */
616     if (!func_ptr.set_dbg_ptr(&dbg_ptr))
617         return;
618 
619     /* if there's a 'self' in this context, add it to the list */
620     if (G_interpreter->get_self_at_level(vmg_ level) != VM_INVALID_OBJ)
621         (*cbfunc)(cbctx, "self", 4);
622 
623     /* iterate over the enclosing frames, starting with the innermost */
624     for (frame_id = line_ptr.get_frame_id() ; frame_id != 0 ;
625          frame_id = frame_ptr.get_enclosing_frame())
626     {
627         /* set up a frame table pointer for the current local frame */
628         dbg_ptr.set_frame_ptr(vmg_ &frame_ptr, frame_id);
629 
630         /* set up a pointer to the first symbol */
631         frame_ptr.set_first_sym_ptr(&sym);
632 
633         /* iterate through the frame and call the callback for each symbol */
634         for (i = frame_ptr.get_sym_count() ; i != 0 ; --i, sym.inc(vmg0_))
635             (*cbfunc)(cbctx, sym.get_sym(vmg0_), sym.get_sym_len(vmg0_));
636     }
637 }
638 
639 /* ------------------------------------------------------------------------ */
640 /*
641  *   Build a stack traceback listing
642  */
build_stack_listing(VMG_ void (* cbfunc)(void *,const char *,int),void * cbctx,int source_info)643 void CVmDebug::build_stack_listing(VMG_
644                                    void (*cbfunc)(void *, const char *, int),
645                                    void *cbctx, int source_info)
646 {
647     ulong method_ofs;
648     vm_val_t *fp;
649     vm_obj_id_t self_obj;
650     pool_ofs_t entry_addr;
651     int level;
652 
653     /* start at the current function */
654     fp = G_interpreter->get_frame_ptr();
655     self_obj = G_interpreter->get_self(vmg0_);
656     entry_addr = entry_ofs_;
657     method_ofs = pc_ - entry_ofs_;
658 
659     /* iterate through the frames */
660     for (level = 0 ; fp != 0 ;
661          fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp), ++level)
662     {
663         vm_obj_id_t def_obj;
664         int argc;
665         char buf[256];
666         int i;
667         char *p;
668         char *open_paren_ptr = 0;
669         char *close_paren_ptr = 0;
670         size_t rem;
671 
672         /* get the current 'self' object */
673         self_obj = G_interpreter->get_self_from_frame(vmg_ fp);
674 
675         /* get the current number of arguments */
676         argc = G_interpreter->get_argc_from_frame(vmg_ fp);
677 
678         /* get the current function's defining object */
679         def_obj = G_interpreter->get_defining_obj_from_frame(vmg_ fp);
680 
681         /* determine whether we have an object.method or a function call */
682         if (method_ofs == 0)
683         {
684             /*
685              *   a zero method offset indicates a native caller making a
686              *   recursive entry into the byte-code interpreter
687              */
688             sprintf(buf, "<System>");
689 
690             /*
691              *   we don't know anything about the arguments to a native
692              *   routine
693              */
694             argc = 0;
695         }
696         else if (def_obj == VM_INVALID_OBJ)
697         {
698             const char *func_sym;
699 
700             /* it's a function call */
701             if ((func_sym = funcaddr_to_sym(entry_addr)) != 0)
702                 sprintf(buf, "%.255s", func_sym);
703             else
704                 sprintf(buf, "%08lx", entry_addr);
705         }
706         else
707         {
708             const char *def_obj_sym;
709             const char *self_obj_sym;
710             const char *prop_sym;
711             vm_prop_id_t prop_id;
712             int is_anon_fn = FALSE;
713 
714             /* it's an object.method call */
715             def_obj_sym = objid_to_sym(def_obj);
716             self_obj_sym = objid_to_sym(self_obj);
717 
718             /* translate the object to the original 'modify' object name */
719             def_obj_sym = get_modifying_sym(def_obj_sym);
720 
721             /*
722              *   Add the object name.  If we have a name for 'self', use that
723              *   name; otherwise, if we have a name for the defining object,
724              *   use that, and show the self object number after it;
725              *   otherwise, if this is an anonymous function object, note
726              *   that and show it specially; otherwise just show the object
727              *   number.
728              */
729             if (def_obj != VM_INVALID_OBJ
730                 && CVmObjAnonFn::is_anonfn_obj(vmg_ def_obj))
731             {
732                 /*
733                  *   it's an anonymous function - note this, and use a
734                  *   special format for the display
735                  */
736                 is_anon_fn = TRUE;
737                 sprintf(buf, "{anonfn:%lx}", def_obj);
738             }
739             else if (self_obj_sym != 0)
740             {
741                 /* 'self' has a name, so show that */
742                 strcpy(buf, self_obj_sym);
743             }
744             else if (def_obj_sym != 0)
745             {
746                 /*
747                  *   'self' has no name, but the defining object does, so
748                  *   show the defining object name and add the 'self' object
749                  *   number
750                  */
751                 sprintf(buf, "%.240s [%lx]", def_obj_sym, self_obj);
752             }
753             else
754             {
755                 /* there's no name to be had, so use the object number */
756                 sprintf(buf, "[%lx]", self_obj);
757             }
758 
759             /* prepare to add to the buffer */
760             p = buf + strlen(buf);
761 
762             /* get the property ID */
763             prop_id = G_interpreter->get_target_prop_from_frame(vmg_ fp);
764             if (is_anon_fn)
765             {
766                 /* it's an anonymous function - there's no property part */
767                 *p = '\0';
768             }
769             else if (prop_id != VM_INVALID_PROP
770                      && (prop_sym = propid_to_sym(prop_id)) != 0)
771             {
772                 /* we got the property symbol - add it */
773                 sprintf(p, ".%.255s", prop_sym);
774             }
775             else
776             {
777                 /* unknown property name */
778                 sprintf(p, ".prop#%x", (uint)prop_id);
779             }
780         }
781 
782         /* get the remainder of the buffer */
783         p = buf + strlen(buf);
784         rem = sizeof(buf) - (p - buf);
785 
786         /* add the open paren */
787         if (entry_addr != 0)
788         {
789             open_paren_ptr = p;
790             *p++ = '(';
791             --rem;
792         }
793 
794         /* add the arguments */
795         for (i = 0 ; i < argc ; ++i)
796         {
797             size_t cur_len;
798 
799             /*
800              *   if we're running out of space, just add an indication
801              *   that more arguments follow, and stop here
802              */
803             if (rem <= 10)
804             {
805                 strcpy(p, " ...");
806                 p += 4;
807                 rem -= 4;
808                 break;
809             }
810 
811             /*
812              *   add this argument, making sure we leave room over for the
813              *   closing paren, newline, null terminator, and if necessary
814              *   an ellipsis indicating we ran out of room
815              */
816             format_val(vmg_ p, rem - 10,
817                        G_interpreter->get_param_from_frame(vmg_ fp, i));
818 
819             /* move past the latest addition */
820             cur_len = strlen(p);
821             rem -= cur_len;
822             p += cur_len;
823 
824             /* add a comma if this isn't the last argument */
825             if (i + 1 < argc)
826             {
827                 *p++ = ',';
828                 *p++ = ' ';
829                 rem -= 2;
830             }
831         }
832 
833         /* add a close paren */
834         if (entry_addr != 0)
835         {
836             close_paren_ptr = p;
837             *p++ = ')';
838         }
839 
840         /* if we have room, add the method offset */
841         if (entry_addr != 0 && rem > 12)
842         {
843             sprintf(p, "+ %lx", method_ofs);
844             p += strlen(p);
845         }
846 
847         /* add a newline and a null terminator */
848         *p++ = '\n';
849         *p = '\0';
850 
851         /*
852          *   if the method offset is non-zero, and they want source line
853          *   information, add it
854          */
855         if (method_ofs != 0 && source_info)
856         {
857             const char *fname;
858             unsigned long linenum;
859 
860             /* get the source information */
861             if (!get_source_info(vmg_ &fname, &linenum, level))
862             {
863                 char numbuf[32];
864                 size_t fname_len;
865                 size_t num_len;
866 
867                 /* convert the line number to a string */
868                 sprintf(numbuf, " line %ld]\n", linenum);
869 
870                 /* get the lengths of what we're adding */
871                 fname_len = strlen(fname);
872                 num_len = strlen(numbuf);
873 
874                 /*
875                  *   if we don't have room, try getting the root of the
876                  *   filename, in case it has a long path prefix
877                  */
878                 if (fname_len + num_len + 2 >= rem)
879                 {
880                     /* skip to the root of the filename */
881                     fname = os_get_root_name((char *)fname);
882 
883                     /* get the new length */
884                     fname_len = strlen(fname);
885                 }
886 
887                 /*
888                  *   If it's still too long, we must have a really long
889                  *   argument list - try removing some of the argument list,
890                  *   adding "..." in place of the removed bits, to make room
891                  *   for the line number.  Don't bother if we can't free up
892                  *   enough space even doing this.
893                  */
894                 if (fname_len + num_len + 2 >= rem
895                     && close_paren_ptr != 0
896                     && (fname_len + num_len + 2
897                         < rem + (close_paren_ptr - open_paren_ptr - 4)))
898                 {
899                     char *dst;
900                     size_t adjust;
901 
902                     /*
903                      *   get the adjustment size - back up by as much as we
904                      *   need to make the filename/number fit, plus space
905                      *   for the terminating null and the ellipsis
906                      */
907                     adjust = (fname_len + num_len + 2 - rem + 1 + 3);
908 
909                     /* back up by the adjustment */
910                     dst = close_paren_ptr - adjust;
911 
912                     /* insert the ellipsis */
913                     memcpy(dst, "...", 3);
914 
915                     /* move the part after the close paren */
916                     memmove(dst + 3, close_paren_ptr,
917                             strlen(close_paren_ptr));
918 
919                     /*
920                      *   adjust by the amount we moved, not counting the
921                      *   ellipsis we added
922                      */
923                     rem += adjust - 3;
924                     p -= adjust - 3;
925                 }
926 
927                 /* add it if there's room */
928                 if (fname_len + num_len + 2 < rem)
929                 {
930                     /* change the newline to a space */
931                     *(p-1) = ' ';
932 
933                     /* add the filename and line number in brackets */
934                     *p = '[';
935                     memcpy(p + 1, fname, fname_len);
936                     memcpy(p + 1 + fname_len, numbuf, num_len + 1);
937                 }
938             }
939         }
940 
941         /* invoke the callback for this level */
942         (*cbfunc)(cbctx, buf, strlen(buf));
943 
944         /* move on to the enclosing frame */
945         entry_addr = G_interpreter
946                      ->get_enclosing_entry_ptr_from_frame(vmg_ fp);
947         method_ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp);
948     }
949 }
950 
951 /* ------------------------------------------------------------------------ */
952 /*
953  *   Toggle a breakpoint
954  */
toggle_breakpoint(VMG_ ulong code_addr,const char * cond,int change,int * bpnum,int * did_set,char * errbuf,size_t errbuflen)955 int CVmDebug::toggle_breakpoint(VMG_ ulong code_addr,
956                                 const char *cond, int change,
957                                 int *bpnum, int *did_set,
958                                 char *errbuf, size_t errbuflen)
959 {
960     char *code_ptr;
961     CVmDebugBp *bp;
962 
963     /* if there's a code address, get a pointer to it */
964     if (code_addr != 0)
965     {
966         /* get a writable pointer to the code location */
967         code_ptr = G_code_pool->get_writable_ptr(code_addr);
968 
969         /* if that failed, we can't set a breakpoint */
970         if (code_ptr == 0)
971         {
972             /* set a message if appropriate */
973             if (errbuf != 0 && errbuflen != 0)
974             {
975                 strncpy(errbuf, "unable to write code address", errbuflen - 1);
976                 errbuf[errbuflen - 1] = '\0';
977             }
978 
979             /* return failure */
980             return 1;
981         }
982     }
983     else
984     {
985         /* global breakpoint - no code address is required */
986         code_ptr = 0;
987     }
988 
989     /* search for a breakpoint at this same address */
990     bp = find_bp(code_addr);
991 
992     /*
993      *   if we found it, we're deleting the old one; otherwise, we're
994      *   creating a new one
995      */
996     if (bp == 0)
997     {
998         int err;
999 
1000         /* setting a new breakpoint - allocate a new record */
1001         bp = alloc_bp();
1002         if (bp == 0)
1003         {
1004             /* generate a message */
1005             if (errbuf != 0 && errbuflen != 0)
1006             {
1007                 strncpy(errbuf, "out of internal breakpoint records",
1008                         errbuflen - 1);
1009                 errbuf[errbuflen - 1] = '\0';
1010             }
1011 
1012             /* return failure */
1013             return 2;
1014         }
1015 
1016         /* initialize the breakpoint */
1017         if ((err = bp->set_info(vmg_ code_addr, cond, change, FALSE,
1018                                 errbuf, errbuflen)) != 0)
1019         {
1020             /* un-allocate the breakpoint */
1021             bp->set_in_use(FALSE);
1022 
1023             /* return the error */
1024             return err;
1025         }
1026 
1027         /* indicate to the caller that we're creating a breakpoint */
1028         *did_set = TRUE;
1029 
1030         /*
1031          *   fill in the breakpoint ID for the caller - this is just the
1032          *   1-based index into our breakpoint array of the breakpoint
1033          *   record
1034          */
1035         *bpnum = (int)(bp - bp_) + 1;
1036 
1037         /* if it's global, count it */
1038         if (bp->is_global())
1039             ++global_bp_cnt_;
1040     }
1041     else
1042     {
1043         /* if it's global, count the deletion */
1044         if (bp->is_global() && !bp->is_disabled())
1045             --global_bp_cnt_;
1046 
1047         /* free the breakpoint record */
1048         bp->do_delete(vmg0_);
1049 
1050         /* indicate to the caller that we're deleting the breakpoint */
1051         *did_set = FALSE;
1052 
1053         /* let the caller know which breakpoint we deleted */
1054         *bpnum = (int)(bp - bp_) + 1;
1055     }
1056 
1057     /* success */
1058     return 0;
1059 }
1060 
1061 /*
1062  *   Toggle a breakpoint's enabled/disabled status
1063  */
toggle_breakpoint_disable(VMG_ int bpnum)1064 void CVmDebug::toggle_breakpoint_disable(VMG_ int bpnum)
1065 {
1066     CVmDebugBp *bp;
1067 
1068     /* if the breakpoint ID is invalid, ignore the request */
1069     if (bpnum < 1 || bpnum > VMDBG_BP_MAX)
1070         return;
1071 
1072     /* get the breakpoint record (the breakpoint ID is a 1-based index) */
1073     bp = bp_ + (bpnum - 1);
1074 
1075     /* toggle the disabled flag */
1076     set_breakpoint_disable(vmg_ bpnum, !bp->is_disabled());
1077 }
1078 
1079 /*
1080  *   Set a breakpoint's disabled status
1081  */
set_breakpoint_disable(VMG_ int bpnum,int disable)1082 void CVmDebug::set_breakpoint_disable(VMG_ int bpnum, int disable)
1083 {
1084     CVmDebugBp *bp;
1085 
1086     /* if the breakpoint ID is invalid, ignore the request */
1087     if (bpnum < 1 || bpnum > VMDBG_BP_MAX)
1088         return;
1089 
1090     /* get the breakpoint record (the breakpoint ID is a 1-based index) */
1091     bp = bp_ + (bpnum - 1);
1092 
1093     /* set the new state */
1094     bp->set_disabled(disable);
1095 
1096     /* add or remove the breakpoint from the code if necessary */
1097     bp->set_bp_instr(vmg_ !bp->is_disabled(), FALSE);
1098 
1099     /* if it's global, count the change */
1100     if (bp->is_global())
1101     {
1102         if (bp->is_disabled())
1103             --global_bp_cnt_;
1104         else
1105             ++global_bp_cnt_;
1106     }
1107 }
1108 
1109 /*
1110  *   Determine if a breakpoint is disabled
1111  */
is_breakpoint_disabled(VMG_ int bpnum)1112 int CVmDebug::is_breakpoint_disabled(VMG_ int bpnum)
1113 {
1114     CVmDebugBp *bp;
1115 
1116     /* if the breakpoint ID is invalid, ignore the request */
1117     if (bpnum < 1 || bpnum > VMDBG_BP_MAX)
1118         return FALSE;
1119 
1120     /* get the breakpoint record (the breakpoint ID is a 1-based index) */
1121     bp = bp_ + (bpnum - 1);
1122 
1123     /* return the status */
1124     return (bp->is_disabled() != 0);
1125 }
1126 
1127 /*
1128  *   Set a breakpoint's condition expression
1129  */
set_breakpoint_condition(VMG_ int bpnum,const char * cond,int change,char * errbuf,size_t errbuflen)1130 int CVmDebug::set_breakpoint_condition(VMG_ int bpnum,
1131                                        const char *cond, int change,
1132                                        char *errbuf, size_t errbuflen)
1133 {
1134     CVmDebugBp *bp;
1135     int err;
1136 
1137     /* if the breakpoint ID is invalid, ignore the request */
1138     if (bpnum < 1 || bpnum > VMDBG_BP_MAX)
1139     {
1140         /* set the error message if needed */
1141         if (errbuf != 0 && errbuflen != 0)
1142         {
1143             strncpy(errbuf, "invalid breakpoint", errbuflen - 1);
1144             errbuf[errbuflen - 1] = '\0';
1145         }
1146 
1147         /* return failure */
1148         return 1;
1149     }
1150 
1151     /* get the breakpoint record (the breakpoint ID is a 1-based index) */
1152     bp = bp_ + (bpnum - 1);
1153 
1154     /* set the condition */
1155     if ((err = bp->set_condition(vmg_ cond, change, errbuf, errbuflen)) != 0)
1156         return err;
1157 
1158     /* success */
1159     return 0;
1160 }
1161 
1162 /*
1163  *   Delete a breakpoint
1164  */
delete_breakpoint(VMG_ int bpnum)1165 void CVmDebug::delete_breakpoint(VMG_ int bpnum)
1166 {
1167     CVmDebugBp *bp;
1168 
1169     /* if the breakpoint ID is invalid, ignore the request */
1170     if (bpnum < 1 || bpnum > VMDBG_BP_MAX)
1171         return;
1172 
1173     /* get the breakpoint record (the breakpoint ID is a 1-based index) */
1174     bp = bp_ + (bpnum - 1);
1175 
1176     /*
1177      *   If the breakpoint is global, update the global bp count.  (Don't
1178      *   bother if the breakpoint is disabled, as we don't include disabled
1179      *   breakpoints in the count in the first place.)
1180      */
1181     if (bp->is_global() && !bp->is_disabled())
1182         --global_bp_cnt_;
1183 
1184     /* clear the breakpoint */
1185     bp->do_delete(vmg0_);
1186 }
1187 
1188 /*
1189  *   Allocate a new breakpoint record
1190  */
alloc_bp()1191 CVmDebugBp *CVmDebug::alloc_bp()
1192 {
1193     size_t i;
1194     CVmDebugBp *bp;
1195 
1196     /* scan our list for an entry not currently in use */
1197     for (i = 0, bp = bp_ ; i < VMDBG_BP_MAX ; ++i, ++bp)
1198     {
1199         /* if this one isn't in use, allocate it */
1200         if (!bp->is_in_use())
1201         {
1202             /* mark it in use */
1203             bp->set_in_use(TRUE);
1204 
1205             /* return the new record */
1206             return bp;
1207         }
1208     }
1209 
1210     /* there are no free records - return failure */
1211     return 0;
1212 }
1213 
1214 /*
1215  *   Find a breakpoint at a given address
1216  */
find_bp(ulong code_addr)1217 CVmDebugBp *CVmDebug::find_bp(ulong code_addr)
1218 {
1219     size_t i;
1220     CVmDebugBp *bp;
1221 
1222     /*
1223      *   if the code address is zero, we know there can't be a breakpoint
1224      *   there
1225      */
1226     if (code_addr == 0)
1227         return 0;
1228 
1229     /* scan our list */
1230     for (i = 0, bp = bp_ ; i < VMDBG_BP_MAX ; ++i, ++bp)
1231     {
1232         /* if this one is in use, and it matches the address, it's the one */
1233         if (bp->is_in_use() && bp->get_code_addr() == code_addr)
1234             return bp;
1235     }
1236 
1237     /* we didn't find it */
1238     return 0;
1239 }
1240 
1241 
1242 /* ------------------------------------------------------------------------ */
1243 /*
1244  *   Format a value into the given buffer
1245  */
format_val(VMG_ char * dst,size_t dstlen,const vm_val_t * val)1246 void CVmDebug::format_val(VMG_ char *dst, size_t dstlen, const vm_val_t *val)
1247 {
1248     const char *p;
1249     char buf[128];
1250 
1251     /* if there's no buffer, we can't do anything */
1252     if (dstlen == 0)
1253         return;
1254 
1255     /* format the value based on the type */
1256     switch(val->typ)
1257     {
1258     case VM_NIL:
1259         /* constant 'nil' value */
1260         p = "nil";
1261         break;
1262 
1263     case VM_TRUE:
1264         /* constant 'true' value */
1265         p = "true";
1266         break;
1267 
1268     case VM_OBJ:
1269         /* object reference - get the object's name */
1270         p = objid_to_sym(val->val.obj);
1271 
1272         /* if there's no symbol name, try alternative display formats */
1273         if (p == 0)
1274         {
1275             /* try it as a string */
1276             if ((p = vm_objp(vmg_ val->val.obj)->get_as_string(vmg0_)) != 0)
1277                 goto format_string;
1278 
1279             /* try it as a list */
1280             if ((p = vm_objp(vmg_ val->val.obj)->get_as_list()) != 0)
1281                 goto format_list;
1282 
1283             /* try it as a BigNumber */
1284             if (CVmObjBigNum::is_bignum_obj(vmg_ val->val.obj))
1285             {
1286                 CVmObjBigNum *bn;
1287                 char *nump;
1288 
1289                 /* cast it to a BigNumber object */
1290                 bn = (CVmObjBigNum *)vm_objp(vmg_ val->val.obj);
1291 
1292                 /* set up a prefix to flag it as a BigNumber value */
1293                 sprintf(buf, "obj#%lx (BigNumber ", (ulong)val->val.obj);
1294 
1295                 /*
1296                  *   write at the end of our prefix, minus two bytes for
1297                  *   the length prefix that the string converter will
1298                  *   write into the buffer (we'll fix up the ovewritten
1299                  *   bytes later)
1300                  */
1301                 nump = buf + strlen(buf) - 2;
1302 
1303                 /* format it into our buffer */
1304                 if (bn->cvt_to_string_buf(vmg_ nump,
1305                                           sizeof(buf) - (nump - buf) - 4,
1306                                           64, -1, -1, -1, 0) != 0)
1307                 {
1308                     size_t len;
1309 
1310                     /* get the length prefix */
1311                     len = vmb_get_len(nump);
1312 
1313                     /* put the close of the string */
1314                     strcpy(nump + len + 2, ")");
1315 
1316                     /* fix up the bytes we overwrote with the length prefix */
1317                     *nump = 'r';
1318                     *(nump + 1) = ' ';
1319 
1320                     /* use the buffer */
1321                     p = buf;
1322 
1323                     /* handled */
1324                     break;
1325                 }
1326             }
1327 
1328             /*
1329              *   It's a dynamically-created object with no name.  Show it
1330              *   by object it.
1331              */
1332             sprintf(buf, "obj#%lx", (ulong)val->val.obj);
1333 
1334             /* if possible, add the name of the of the superclass */
1335             if (vm_objp(vmg_ val->val.obj)
1336                 ->get_superclass_count(vmg_ val->val.obj) > 0)
1337             {
1338                 vm_obj_id_t sc_obj;
1339 
1340                 /* get the superclass */
1341                 sc_obj = vm_objp(vmg_ val->val.obj)
1342                          ->get_superclass(vmg_ val->val.obj, 0);
1343 
1344                 /* if it's valid, look up the superclass name */
1345                 if (sc_obj != 0 && (p = objid_to_sym(sc_obj)) != 0)
1346                 {
1347                     /* add "(superclass name)" after the numeric ID */
1348                     sprintf(buf + strlen(buf), " (%.100s)", p);
1349                 }
1350             }
1351 
1352             /* use the buffer we formatted */
1353             p = buf;
1354         }
1355         break;
1356 
1357     case VM_PROP:
1358         /* property ID - get the property name */
1359         p = propid_to_sym(val->val.prop);
1360         if (p != 0)
1361         {
1362             /* we have a symbol - use it with an ampersand operator */
1363             sprintf(buf, "&%.127s", p);
1364         }
1365         else
1366         {
1367             /* there's no symbol, so use the numeric property ID */
1368             sprintf(buf, "prop#%x", (uint)val->val.prop);
1369         }
1370 
1371         /* use the text we built in the buffer */
1372         p = buf;
1373         break;
1374 
1375     case VM_ENUM:
1376         /* enum ID - get the enum name */
1377         p = enum_to_sym(val->val.enumval);
1378 
1379         /* if there's no s ymbol, use the numeric enum ID */
1380         if (p == 0)
1381         {
1382             sprintf(buf, "enum#%x", (uint)val->val.enumval);
1383             p = buf;
1384         }
1385         break;
1386 
1387     case VM_INT:
1388         /* integer */
1389         sprintf(buf, "%ld", val->val.intval);
1390         p = buf;
1391         break;
1392 
1393     case VM_SSTRING:
1394         /* length-prefixed string - get a pointer to the data */
1395         p = G_const_pool->get_ptr(val->val.ofs);
1396 
1397     format_string:
1398         /*
1399          *   first, add an open quote, if we minimally have room for the
1400          *   quote plus a null terminator
1401          */
1402         if (dstlen > 1)
1403         {
1404             *dst++ = '\'';
1405             --dstlen;
1406         }
1407 
1408         /* copy as much as we can */
1409         {
1410             size_t rem;
1411             utf8_ptr strp;
1412 
1413             /* read and skip the length prefix */
1414             rem = osrp2(p);
1415             p += 2;
1416 
1417             /* set up a utf-8 pointer to the source string */
1418             strp.set((char *)p);
1419 
1420             /*
1421              *   keep going until we run out of source text or space in
1422              *   our output buffer - leave ourselves 5 extra bytes (close
1423              *   quote, "..." if we run out of space, and null terminator)
1424              */
1425             for ( ; rem != 0 && dstlen > 5 ; strp.inc(&rem))
1426             {
1427                 wchar_t uni;
1428                 int mappable;
1429 
1430                 /* get the current unicode character code */
1431                 uni = strp.getch();
1432 
1433                 /*
1434                  *   determine if the character is mappable to the local
1435                  *   character set
1436                  */
1437                 mappable = G_cmap_to_ui->is_mappable(uni);
1438 
1439                 /*
1440                  *   If there's no mapping, insert a '\u' sequence for the
1441                  *   character.  If the character is in the non-printable
1442                  *   control character range, use a backslash
1443                  *   representation as well.  If the character is a
1444                  *   backslash or single quote, also represent it as a
1445                  *   backslash sequence, since it would need to be quoted
1446                  *   in a string submitted to the compiler.
1447                  */
1448                 if (uni < 32 || uni == '\\' || uni == '\'')
1449                 {
1450                     char esc;
1451                     unsigned int dig;
1452 
1453                     switch(uni)
1454                     {
1455                     case 9:
1456                         /* tab - '\t' */
1457                         esc = 't';
1458 
1459                     do_escape:
1460                         /* we need two bytes, plus our 5 reserve */
1461                         if (dstlen < 2 + 5)
1462                             goto out_of_str_space;
1463 
1464                         /* add the escape sequence */
1465                         *dst++ = '\\';
1466                         *dst++ = esc;
1467 
1468                         /* consume the two bytes */
1469                         dstlen -= 2;
1470                         break;
1471 
1472                     case 10:
1473                         /* newline - '\n' */
1474                         esc = 'n';
1475                         goto do_escape;
1476 
1477                     case 0x000F:
1478                         /* caps - '\^' */
1479                         esc = '^';
1480                         goto do_escape;
1481 
1482                     case 0x000E:
1483                         /* uncaps - '\v' */
1484                         esc = 'v';
1485                         goto do_escape;
1486 
1487                     case 0x000B:
1488                         /* blank - '\b' */
1489                         esc = 'b';
1490                         goto do_escape;
1491 
1492                     case 0x0015:
1493                         /* quoted space - '\ ' */
1494                         esc = ' ';
1495                         goto do_escape;
1496 
1497                     case '\\':
1498                     case '\'':
1499                         esc = (char)uni;
1500                         goto do_escape;
1501 
1502                     default:
1503                         /*
1504                          *   represent anything else as a hex sequence -
1505                          *   we need four bytes, plus the 5 reserve
1506                          */
1507                         if (dstlen < 4 + 5)
1508                             goto out_of_str_space;
1509 
1510                         /* add the backslash and 'x' */
1511                         *dst++ = '\\';
1512                         *dst++ = 'x';
1513 
1514                         /* add the the first hex digit */
1515                         dig = (uni >> 4) & 0xF;
1516                         *dst++ = dig + (dig < 10 ? '0' : 'A' - 10);
1517 
1518                         /* add the second hex digit */
1519                         dig = (uni & 0xF);
1520                         *dst++ = dig + (dig < 10 ? '0' : 'A' - 10);
1521 
1522                         /* consume the space */
1523                         dstlen -= 4;
1524                         break;
1525                     }
1526                 }
1527                 else if (!mappable)
1528                 {
1529                     int i;
1530                     unsigned int c;
1531 
1532                     /*
1533                      *   the '\u' sequence requires six bytes (backslash,
1534                      *   'u', and the four hex digits of the character
1535                      *   code), plus the 5 reserve we always leave, so if
1536                      *   we don't have room stop now
1537                      */
1538                     if (dstlen < 5 + 6)
1539                         break;
1540 
1541                     /* consume the space */
1542                     dstlen -= 6;
1543 
1544                     /* add the '\u' */
1545                     *dst++ = '\\';
1546                     *dst++ = 'u';
1547 
1548                     /* add the four hex digits */
1549                     for (c = (unsigned int)uni, i = 0 ; i < 4 ; ++i)
1550                     {
1551                         unsigned int dig;
1552 
1553                         /* get the current most significant digit's value */
1554                         dig = (c >> 12) & 0xf;
1555 
1556                         /* generate the next hex digit */
1557                         *dst++ = dig + (dig < 10 ? '0' : 'A' - 10);
1558 
1559                         /* shift up so the next digit is in current */
1560                         c <<= 4;
1561                     }
1562                 }
1563                 else
1564                 {
1565                     char buf[20];
1566                     size_t xlat_len;
1567 
1568                     /* get the translation of this character */
1569                     xlat_len = G_cmap_to_ui->map_char(uni, buf, sizeof(buf));
1570 
1571                     /* if there's no room, stop now */
1572                     if (dstlen < xlat_len + 5)
1573                         break;
1574 
1575                     /* copy the translation */
1576                     memcpy(dst, buf, xlat_len);
1577 
1578                     /* advance past the space we've used */
1579                     dst += xlat_len;
1580                     dstlen -= xlat_len;
1581                 }
1582             }
1583 
1584         out_of_str_space:
1585             /* add an ellipsis at the end if we ran out of space */
1586             if (rem != 0 && dstlen >= 5)
1587             {
1588                 *dst++ = '.';
1589                 *dst++ = '.';
1590                 *dst++ = '.';
1591                 dstlen -= 3;
1592             }
1593 
1594             /* add the close quote */
1595             if (dstlen > 1)
1596                 *dst++ = '\'';
1597 
1598             /* add the null terminator */
1599             if (dstlen > 0)
1600                 *dst++ = '\0';
1601         }
1602 
1603         /* we're done - don't bother with the normal copying */
1604         return;
1605 
1606     case VM_LIST:
1607         /* constant list - get the data pointer */
1608         p = G_const_pool->get_ptr(val->val.ofs);
1609 
1610     format_list:
1611         {
1612             size_t i;
1613             size_t cnt;
1614 
1615             /* get the element count */
1616             cnt = vmb_get_len(p);
1617 
1618             /* add the open bracket */
1619             if (dstlen >= 2)
1620             {
1621                 *dst++ = '[';
1622                 --dstlen;
1623             }
1624 
1625             /* scan through the elements (using a 1-based counter) */
1626             for (i = 1 ; i <= cnt && dstlen > 7 ; ++i)
1627             {
1628                 vm_val_t val;
1629                 size_t cur_len;
1630 
1631                 /* get this element */
1632                 CVmObjList::index_list(vmg_ &val, p, i);
1633 
1634                 /* add this element, leaving room for closing data */
1635                 format_val(vmg_ dst, dstlen - 7, &val);
1636 
1637                 /* skip past this value */
1638                 cur_len = strlen(dst);
1639                 dst += cur_len;
1640                 dstlen -= cur_len;
1641 
1642                 /* add a comma between elements if possible */
1643                 if (dstlen > 6 && i != cnt)
1644                 {
1645                     *dst++ = ',';
1646                     --dstlen;
1647                 }
1648             }
1649 
1650             /*
1651              *   if we didn't exhaust the list, and we have room, indicate
1652              *   that more follows
1653              */
1654             if (i <= cnt && dstlen >= 5)
1655             {
1656                 *dst++ = '.';
1657                 *dst++ = '.';
1658                 *dst++ = '.';
1659                 dstlen -= 3;
1660             }
1661 
1662             /* add the closing bracket if possible */
1663             if (dstlen >= 2)
1664                 *dst++ = ']';
1665 
1666             /* add the null terminator */
1667             *dst = '\0';
1668         }
1669 
1670         /* we're done - don't bother with normal copying */
1671         return;
1672 
1673     case VM_FUNCPTR:
1674         /* function pointer - get the function name */
1675         p = funcaddr_to_sym(val->val.ofs);
1676 
1677         /* if there's no symbol, use the numeric function address */
1678         if (p == 0)
1679         {
1680             sprintf(buf, "function#%08lx", (ulong)val->val.ofs);
1681             p = buf;
1682         }
1683         break;
1684 
1685     default:
1686         p = "?";
1687         break;
1688     }
1689 
1690     /* copy as much of the value as possible into the buffer */
1691     strncpy(dst, p, dstlen);
1692 
1693     /* make sure the buffer is null-terminated */
1694     dst[dstlen - 1] = '\0';
1695 }
1696 
1697 /* ------------------------------------------------------------------------ */
1698 /*
1699  *   Callback context structure for enum_props_eval_cb
1700  */
1701 struct enum_props_eval_cb_ctx
1702 {
1703     /* the UI callback function to invoke and its own context */
1704     void (*ui_cb)(void *, const char *, int, const char *);
1705     void *ui_cb_ctx;
1706 
1707     /* the original object whose properties we're enumerating */
1708     vm_obj_id_t obj;
1709 
1710     /* debugger object */
1711     CVmDebug *dbg;
1712 };
1713 
1714 /*
1715  *   Object property enumeration callback for evaluating an object-valued
1716  *   expression.
1717  */
enum_props_eval_cb(VMG_ void * ctx0,vm_obj_id_t self,vm_prop_id_t prop,const vm_val_t * val)1718 static void enum_props_eval_cb(VMG_ void *ctx0,
1719                                vm_obj_id_t self, vm_prop_id_t prop,
1720                                const vm_val_t *val)
1721 {
1722     enum_props_eval_cb_ctx *ctx = (enum_props_eval_cb_ctx *)ctx0;
1723     const char *prop_name;
1724     vm_val_t ov_val;
1725     vm_obj_id_t src_obj;
1726 
1727     /*
1728      *   The property enumerator tells us about all of the properties
1729      *   throughout the entire inheritance tree.  Make sure this one isn't
1730      *   overridden - if it is, we'll already have shown (or suppressed
1731      *   showing) the overriding copy of the property.  Since overridden
1732      *   properties can't be seen in the actual object, we don't want to show
1733      *   them here.
1734      */
1735     if (vm_objp(vmg_ ctx->obj)->get_prop(vmg_ prop, &ov_val,
1736                                          ctx->obj, &src_obj, 0)
1737         && src_obj != self)
1738     {
1739         /*
1740          *   we found the property, but it was defined in a different object
1741          *   than the object whose properties we're enumerating - this must
1742          *   be an inherited version of the property which the object
1743          *   overrides, so we don't want to show it among the original
1744          *   object's properties
1745          */
1746         return;
1747     }
1748 
1749     /* get the name of this property */
1750     prop_name = ctx->dbg->propid_to_sym(prop);
1751 
1752     /*
1753      *   if we couldn't get a name for the property, don't bother invoking
1754      *   the UI callback, since it won't be able to use the property
1755      *   anyway
1756      */
1757     if (prop_name == 0)
1758         return;
1759 
1760     /* ignore methods and properties containing self-printing strings */
1761     switch(val->typ)
1762     {
1763     case VM_NIL:
1764     case VM_TRUE:
1765     case VM_OBJ:
1766     case VM_PROP:
1767     case VM_INT:
1768     case VM_ENUM:
1769     case VM_SSTRING:
1770     case VM_LIST:
1771     case VM_FUNCPTR:
1772         /*
1773          *   these types are all valid for enumeration - invoke the UI
1774          *   callback
1775          */
1776         (*ctx->ui_cb)(ctx->ui_cb_ctx, prop_name, strlen(prop_name), ".");
1777         break;
1778 
1779     default:
1780         /* do not enumerate any other types */
1781         break;
1782     }
1783 }
1784 
1785 /* ------------------------------------------------------------------------ */
1786 /*
1787  *   Evaluate an expression
1788  */
eval_expr(VMG_ char * buf,size_t buflen,const char * expr,int level,int * is_lval,int * is_openable,void (* aggcb)(void *,const char *,int,const char *),void * aggctx,int speculative)1789 int CVmDebug::eval_expr(VMG_ char *buf, size_t buflen, const char *expr,
1790                         int level, int *is_lval, int *is_openable,
1791                         void (*aggcb)(void *, const char *,
1792                                       int, const char *),
1793                         void *aggctx, int speculative)
1794 {
1795     int err;
1796     CVmPoolDynObj *code_obj;
1797     vm_val_t expr_val;
1798     CVmDbgSymtab local_symtab;
1799     vmdbg_step_save_t old_step;
1800     vm_obj_id_t self_obj;
1801     vm_obj_id_t orig_target_obj;
1802     vm_obj_id_t defining_obj;
1803     vm_prop_id_t target_prop;
1804     vmrun_save_ctx run_ctx;
1805 
1806     /* presume it won't be an lvalue or openable */
1807     if (is_lval != 0)
1808         *is_lval = FALSE;
1809     if (is_openable != 0)
1810         *is_openable = FALSE;
1811 
1812     /* clear the buffer in case we can't evaluate the expression */
1813     buf[0] = '\0';
1814 
1815     /* get information on the indicated stack level */
1816     {
1817         CVmFuncPtr func_ptr;
1818         CVmDbgLinePtr line_ptr;
1819         CVmDbgTablePtr dbg_ptr;
1820         ulong stm_start;
1821         ulong stm_end;
1822 
1823         if (get_stack_level_info(vmg_ level, &func_ptr, &line_ptr,
1824                                  &stm_start, &stm_end)
1825             || !func_ptr.set_dbg_ptr(&dbg_ptr))
1826             return 1;
1827 
1828         /* set up our local symbol table interface for this context */
1829         local_symtab.init(vmg_ &dbg_ptr, line_ptr.get_frame_id(), level);
1830     }
1831 
1832     /* get the 'self' object for the selected level */
1833     self_obj = G_interpreter->get_self_at_level(vmg_ level);
1834 
1835     /* get the target property for the level */
1836     target_prop = G_interpreter->get_target_prop_at_level(vmg_ level);
1837 
1838     /* get the original target object and defining object at the level */
1839     orig_target_obj = G_interpreter->get_orig_target_obj_at_level(vmg_ level);
1840     defining_obj = G_interpreter->get_defining_obj_at_level(vmg_ level);
1841 
1842     /* compile the expression */
1843     if ((err = compile_expr(vmg_ expr, level, &local_symtab,
1844                             self_obj != VM_INVALID_OBJ,
1845                             speculative, is_lval,
1846                             &code_obj, buf, buflen)) != 0)
1847         return err;
1848 
1849     /* note the pre-call stack depth */
1850     G_interpreter->save_context(vmg_ &run_ctx);
1851 
1852     /* set up for the recursive execution */
1853     prepare_for_eval(&old_step);
1854 
1855     /* execute the code in a protected block */
1856     err_try
1857     {
1858         /* execute the code in a recursive call to the VM */
1859         G_interpreter->do_call(vmg_ 0, code_obj->get_ofs(), 0,
1860                                self_obj, target_prop,
1861                                orig_target_obj, defining_obj, "dbg eval");
1862     }
1863     err_catch(exc)
1864     {
1865         const char *msg;
1866         size_t msg_len;
1867         static const char prefix[] = "error: ";
1868 
1869         /* copy the prefix into the buffer if there's room */
1870         if (buflen > sizeof(prefix) + 20)
1871         {
1872             memcpy(buf, prefix, sizeof(prefix));
1873             buflen -= sizeof(prefix) - 1;
1874             buf += sizeof(prefix) - 1;
1875         }
1876 
1877         /* note the error */
1878         err = exc->get_error_code();
1879 
1880         /*
1881          *   if it's "unhandled exception," get the message from the
1882          *   exception object itself; otherwise, format the message for
1883          *   the error
1884          */
1885         if (err == VMERR_UNHANDLED_EXC)
1886         {
1887             /* get the message from the exception */
1888             msg = CVmRun::get_exc_message(vmg_ exc, &msg_len);
1889             if (msg == 0)
1890                 msg = "Unhandled program exception";
1891 
1892             /* limit the size to the caller's buffer size */
1893             if (msg_len > buflen - 1)
1894                 msg_len = buflen - 1;
1895 
1896             /* copy it into the caller's buffer and null-terminate it */
1897             memcpy(buf, msg, msg_len);
1898             buf[msg_len] = '\0';
1899         }
1900         else
1901         {
1902             /* format the VM error message into the caller's result buffer */
1903             msg = err_get_msg(vm_messages, vm_message_count, err, FALSE);
1904             err_format_msg(buf, buflen, msg, exc);
1905         }
1906     }
1907     err_end;
1908 
1909     /* restore the original execution mode */
1910     restore_from_eval(&old_step);
1911 
1912     /* restore the interpreter context */
1913     G_interpreter->restore_context(vmg_ &run_ctx);
1914 
1915     /* delete the byte code object */
1916     G_code_pool->get_dynamic_ifc()->dynpool_delete(code_obj);
1917 
1918     /* get the return value, if any */
1919     if (err == 0)
1920     {
1921         /* get the value from R0 */
1922         expr_val = *G_interpreter->get_r0();
1923 
1924         /* leave the value on the stack for gc protection */
1925         G_stk->push(&expr_val);
1926 
1927         /* format the value into the buffer */
1928         format_val(vmg_ buf, buflen, &expr_val);
1929     }
1930     else
1931     {
1932         /* return failure */
1933         return 1;
1934     }
1935 
1936     /* if an aggregation callback is provided, use it */
1937     if (aggcb != 0)
1938     {
1939         const char *p;
1940         size_t cnt;
1941         size_t i;
1942 
1943         /* determine if the object has contents to be iterated */
1944         switch(expr_val.typ)
1945         {
1946         case VM_LIST:
1947             /* get the pointer to the constant list data */
1948             p = G_const_pool->get_ptr(expr_val.val.ofs);
1949 
1950         agg_list:
1951             /* get the element count */
1952             cnt = vmb_get_len(p);
1953 
1954             /* iterate through the elements */
1955             for (i = 1 ; i <= cnt ; ++i)
1956             {
1957                 char idxbuf[30];
1958 
1959                 /* format an index operator for this index */
1960                 sprintf(idxbuf, "[%u]", i);
1961 
1962                 /* invoke the callback with this subitem */
1963                 (*aggcb)(aggctx, idxbuf, strlen(idxbuf), "");
1964             }
1965             break;
1966 
1967         case VM_OBJ:
1968             /* if it's a list object, use the list contents */
1969             if ((p = vm_objp(vmg_ expr_val.val.obj)->get_as_list()) != 0)
1970                 goto agg_list;
1971 
1972             /* if it's an array, handle it specially */
1973             if (CVmObjVector::is_vector_obj(vmg_ expr_val.val.obj))
1974             {
1975                 CVmObjVector *vec;
1976                 size_t cnt;
1977 
1978                 /* cast it to an array object */
1979                 vec = (CVmObjVector *)vm_objp(vmg_ expr_val.val.obj);
1980 
1981                 /* iterate through the elements */
1982                 cnt = vec->get_element_count();
1983                 for (i = 1 ; i <= cnt ; ++i)
1984                 {
1985                     char idxbuf[30];
1986 
1987                     /* foramt an index operator for this index */
1988                     sprintf(idxbuf, "[%u]", i);
1989 
1990                     /* invoke the callback with this subitem */
1991                     (*aggcb)(aggctx, idxbuf, strlen(idxbuf), "");
1992                 }
1993 
1994                 /* we've handled the item */
1995                 break;
1996             }
1997 
1998             /* determine if the object provides a property list */
1999             if (vm_objp(vmg_ expr_val.val.obj)->provides_props(vmg0_))
2000             {
2001                 enum_props_eval_cb_ctx cb_ctx;
2002 
2003                 /* set up our callback context */
2004                 cb_ctx.ui_cb = aggcb;
2005                 cb_ctx.ui_cb_ctx = aggctx;
2006                 cb_ctx.dbg = this;
2007                 cb_ctx.obj = expr_val.val.obj;
2008 
2009                 /* enumerate the properties */
2010                 vm_objp(vmg_ expr_val.val.obj)
2011                     ->enum_props(vmg_ expr_val.val.obj, &enum_props_eval_cb,
2012                                  &cb_ctx);
2013             }
2014             break;
2015 
2016         default:
2017             /* other types don't have sub-parts to iterate */
2018             break;
2019         }
2020     }
2021 
2022     /* check the openable status if the caller wants to know */
2023     if (is_openable != 0)
2024     {
2025         /*
2026          *   check to see if it's openable - it is if it's a TADS object
2027          *   or a list
2028          */
2029         switch(expr_val.typ)
2030         {
2031         case VM_LIST:
2032             /* lists are always openable */
2033             *is_openable = TRUE;
2034             break;
2035 
2036         case VM_OBJ:
2037             /*
2038              *   object - if it's a list or TADS object, it's openable;
2039              *   otherwise it's not
2040              */
2041             if (vm_objp(vmg_ expr_val.val.obj)->get_as_list() != 0
2042                 || CVmObjVector::is_vector_obj(vmg_ expr_val.val.obj)
2043                 || vm_objp(vmg_ expr_val.val.obj)->provides_props(vmg0_))
2044                 *is_openable = TRUE;
2045             else
2046                 *is_openable = FALSE;
2047             break;
2048 
2049         default:
2050             /* other types aren't openable */
2051             *is_openable = FALSE;
2052             break;
2053         }
2054     }
2055 
2056     /* discard the gc protection */
2057     G_stk->discard();
2058 
2059     /* success */
2060     return 0;
2061 }
2062 
2063 
2064 /*
2065  *   Compile an expression
2066  */
compile_expr(VMG_ const char * expr,int level,CVmDbgSymtab * local_symtab,int self_valid,int speculative,int * is_lval,CVmPoolDynObj ** code_obj,char * dstbuf,size_t dstbuflen)2067 int CVmDebug::compile_expr(VMG_ const char *expr,
2068                            int level, CVmDbgSymtab *local_symtab,
2069                            int self_valid, int speculative,
2070                            int *is_lval, CVmPoolDynObj **code_obj,
2071                            char *dstbuf, size_t dstbuflen)
2072 {
2073     int err;
2074     CTcPrsNode *expr_node;
2075     CTPNStmReturn *ret_stm;
2076     CTPNCodeBody *code_body;
2077     tcprsmem_state_t prsmem_state;
2078     CTcPrsDbgSymtab *old_symtab;
2079     CVmPoolDynamic *dyn_code;
2080     size_t copy_len;
2081     ulong copy_ofs;
2082     char *dst;
2083     tcpn_debug_info adjust_info;
2084     int need_err_msg;
2085 
2086     /* presume we won't need an error message formatted */
2087     need_err_msg = FALSE;
2088 
2089     /*
2090      *   get the dynamic code pool manager - if it's not available, we
2091      *   have no place to put the compiled code, so we can't continue
2092      */
2093     if ((dyn_code = G_code_pool->get_dynamic_ifc()) == 0)
2094         return 1;
2095 
2096     /* set up the parser with our local symbol table */
2097     old_symtab = G_prs->set_debug_symtab(local_symtab);
2098 
2099     /*
2100      *   save the parser memory pool state, so we can reset it when we're
2101      *   done (this allows us to discard any parser memory we allocate
2102      *   while we're working - we only need it while compiling, and can
2103      *   discard it when we're done)
2104      */
2105     G_prsmem->save_state(&prsmem_state);
2106 
2107     /* presume no error will occur */
2108     err = 0;
2109 
2110     /* presume we won't generate an expression node */
2111     expr_node = 0;
2112 
2113     /* catch any errors that occur during compilation or code generation */
2114     err_try
2115     {
2116         /* set up the tokenizer with the source buffer */
2117         G_tok->set_source_buf(expr);
2118 
2119         /* read the first token */
2120         G_tok->next();
2121 
2122         /* reset the compiler error counters for the new expression */
2123         G_tcmain->reset_error_counts();
2124 
2125         /*
2126          *   clear the message buffer in the host interface, so we will
2127          *   capture the text of the first error message generated after
2128          *   this point
2129          */
2130         hostifc_->reset_messages();
2131 
2132         /* compile the expression */
2133         expr_node = G_prs->parse_expr();
2134 
2135         /* don't proceed if compilation failed for any reason */
2136         if (expr_node == 0 || G_tcmain->get_error_count() != 0)
2137             goto compilation_done;
2138 
2139         /* fold constants in the expression */
2140         expr_node = expr_node->fold_constants(G_prs->get_global_symtab());
2141         if (expr_node == 0)
2142             goto compilation_done;
2143 
2144         /* adjust the expression for debugger execution */
2145         adjust_info.speculative = speculative;
2146         adjust_info.stack_level = level;
2147         expr_node = expr_node->adjust_for_debug(&adjust_info);
2148         if (expr_node == 0)
2149             goto compilation_done;
2150 
2151         /* check to see if the expression is an lvalue */
2152         if (is_lval != 0)
2153         {
2154             /* check the expression node to see if it's an lvalue */
2155             *is_lval = expr_node
2156                        ->check_lvalue_resolved(G_prs->get_global_symtab());
2157         }
2158 
2159         /*
2160          *   Put the expression within a 'return' statement, and put the
2161          *   'return' statement within a code body.  If 'self' is
2162          *   available, provide an object statement object, so that the
2163          *   code generator knows that 'self' is valid within the
2164          *   generated code.
2165          */
2166         ret_stm = new CTPNStmReturn(expr_node);
2167         code_body = new CTPNCodeBody(G_prs->get_global_symtab(),
2168                                      0, ret_stm, 0, FALSE, FALSE, 0, 0,
2169                                      self_valid, 0);
2170 
2171         /* set the appropriate debug modes in the code generator */
2172         G_cg->set_debug_eval(speculative, level);
2173 
2174         /* make the global symbol table active for code generation */
2175         G_cs->set_symtab(G_prs->get_global_symtab());
2176 
2177         /* generate code */
2178         code_body->gen_code(FALSE, FALSE);
2179 
2180     compilation_done:
2181         ;
2182     }
2183     err_catch(exc)
2184     {
2185         /* note the error code */
2186         err = exc->get_error_code();
2187 
2188         /*
2189          *   if the caller provided a result buffer, format the error
2190          *   message into the result buffer
2191          */
2192         if (G_tcmain->get_error_count() != 0)
2193         {
2194             /*
2195              *   a compilation error was logged - use the error message we
2196              *   captured in the host interface
2197              */
2198             need_err_msg = TRUE;
2199         }
2200         else if (dstbuf != 0)
2201         {
2202             const char *msg;
2203 
2204             /* get the message for the compiler error */
2205             msg = tcerr_get_msg(err, FALSE);
2206             err_format_msg(dstbuf, dstbuflen, msg, exc);
2207         }
2208     }
2209     err_end;
2210 
2211     /* if a compilation error occurred, return failure */
2212     if (err != 0)
2213     {
2214         /* parsing failed - no need to go on */
2215         goto done;
2216     }
2217     else if (G_tcmain->get_error_count() != 0 || expr_node == 0)
2218     {
2219         /* parse errors were reported - no need to go on */
2220         err = G_tcmain->get_first_error();
2221         need_err_msg = TRUE;
2222         goto done;
2223     }
2224 
2225     /* finish the code - if there's a value, return it */
2226     if (G_cg->get_sp_depth() != 0)
2227     {
2228         /* there's a value on the stack - return it */
2229         G_cg->write_op(OPC_RETVAL);
2230     }
2231     else
2232     {
2233         /* no value - just return nil */
2234         G_cg->write_op(OPC_RETNIL);
2235     }
2236 
2237     /* if any errors occurred generating code, fail */
2238     if (G_tcmain->get_error_count() != 0)
2239     {
2240         err = 1;
2241         need_err_msg = TRUE;
2242         goto done;
2243     }
2244 
2245     /*
2246      *   if the code stream is too large, we can't save the code in the
2247      *   code pool
2248      */
2249     if (G_cs->get_ofs() > G_code_pool->get_page_size())
2250     {
2251         err = 1;
2252         need_err_msg = TRUE;
2253         goto done;
2254     }
2255 
2256     /* get the amount of data to copy */
2257     copy_len = (size_t)G_cs->get_ofs();
2258 
2259     /* start reading at offset zero in the code stream */
2260     copy_ofs = 0;
2261 
2262     /* allocate code pool space for the code */
2263     *code_obj = dyn_code->dynpool_alloc(copy_len);
2264     if (*code_obj == 0)
2265     {
2266         err = 1;
2267         need_err_msg = TRUE;
2268         goto done;
2269     }
2270 
2271     /* get a writable pointer to the allocated code pool space */
2272     dst = G_code_pool->get_writable_ptr((*code_obj)->get_ofs());
2273     if (dst == 0)
2274     {
2275         /* release the code pool object */
2276         dyn_code->dynpool_delete(*code_obj);
2277 
2278         /* give up */
2279         err = 1;
2280         need_err_msg = TRUE;
2281         goto done;
2282     }
2283 
2284     /* move the generated code into the code pool */
2285     while (copy_len != 0)
2286     {
2287         const char *src;
2288         ulong avail_len;
2289 
2290         /* get the next block */
2291         src = G_cs->get_block_ptr(copy_ofs, copy_len, &avail_len);
2292 
2293         /* copy the data */
2294         memcpy(dst, src, (size_t)avail_len);
2295 
2296         /* advance past the copied data */
2297         dst += (size_t)avail_len;
2298         copy_len -= (size_t)avail_len;
2299         copy_ofs += avail_len;
2300     }
2301 
2302 done:
2303     /* restore the original symbol table in the parser */
2304     G_prs->set_debug_symtab(old_symtab);
2305 
2306     /* reset the tokenizer */
2307     G_tok->reset();
2308 
2309     /* reset the code generator streams */
2310     G_ds->reset();
2311     G_cs->reset();
2312     G_os->reset();
2313 
2314     /*
2315      *   reset the parser's memory pool, since we don't need any of the
2316      *   intermediate compilation data any longer
2317      */
2318     G_prsmem->reset(&prsmem_state);
2319 
2320     /* generate an error message if necessary */
2321     if (need_err_msg && dstbuf != 0)
2322     {
2323         /*
2324          *   if we logged a compiler error, report the first one we found;
2325          *   otherwise, use an empty message
2326          */
2327         if (G_tcmain->get_error_count())
2328         {
2329             /* get the first error message captured in our host interface */
2330             strncpy(dstbuf, hostifc_->get_error_msg(), dstbuflen - 1);
2331             dstbuf[dstbuflen - 1] = '\0';
2332         }
2333         else
2334         {
2335             /* we don't have an error code, so there's no message */
2336             dstbuf[0] = '\0';
2337         }
2338     }
2339 
2340     /* return the result */
2341     return err;
2342 }
2343 
2344 /* ------------------------------------------------------------------------ */
2345 /*
2346  *   Set step-over mode
2347  */
set_step_over(VMG0_)2348 void CVmDebug::set_step_over(VMG0_)
2349 {
2350     /* set the mode flags */
2351     single_step_ = TRUE;
2352     step_in_ = FALSE;
2353     step_out_ = FALSE;
2354 
2355     /*
2356      *   note the current stack frame depth - we won't stop until the
2357      *   stack frame is back at this same depth, or at an enclosing level
2358      */
2359     step_frame_depth_ = G_interpreter->get_frame_depth(vmg0_);
2360 }
2361 
2362 /*
2363  *   Set step-out mode
2364  */
set_step_out(VMG0_)2365 void CVmDebug::set_step_out(VMG0_)
2366 {
2367     /* set the mode flags for stepping over source lines */
2368     single_step_ = TRUE;
2369     step_in_ = FALSE;
2370     step_out_ = TRUE;
2371 
2372     /*
2373      *   Keep stepping until we're at an enclosing stack frame level - to
2374      *   do this, note the current frame depth, but decrement it, since we
2375      *   don't want to stop again until the frame depth is lower than it
2376      *   is now.
2377      */
2378     step_frame_depth_ = G_interpreter->get_frame_depth(vmg0_) - 1;
2379 }
2380 
2381 
2382 /* ------------------------------------------------------------------------ */
2383 /*
2384  *   Synchronize our internal memory of the last execution point
2385  */
sync_exec_pos(VMG_ const uchar * pc_ptr,pool_ofs_t method_start_ofs)2386 void CVmDebug::sync_exec_pos(VMG_ const uchar *pc_ptr,
2387                              pool_ofs_t method_start_ofs)
2388 {
2389     CVmFuncPtr func_ptr;
2390     CVmDbgLinePtr line_ptr;
2391     ulong stm_start, stm_end;
2392     unsigned int exec_ofs;
2393 
2394     /*
2395      *   if we're already in the debugger, don't change anything - this must
2396      *   be a recursive invocation due to expression evaluation
2397      */
2398     if (in_debugger_)
2399         return;
2400 
2401     /* set up a pointer to the current function's header */
2402     G_interpreter->set_current_func_ptr(vmg_ &func_ptr);
2403 
2404     /* get the byte-code offset of this instruction */
2405     pc_ = (pc_ptr == 0 ? 0 : G_interpreter->pc_to_code_ofs(vmg_ pc_ptr));
2406 
2407     /* for the UI, compute the offset from the start of the method */
2408     exec_ofs = (unsigned int)(pc_ - method_start_ofs);
2409 
2410     /* get the boundaries of the current source code statement */
2411     if (CVmRun::get_stm_bounds(vmg_ &func_ptr, exec_ofs, &line_ptr,
2412                                &stm_start, &stm_end))
2413     {
2414         /* remember the current function pointer */
2415         func_ptr_.copy_from(&func_ptr);
2416 
2417         /* remember the new line pointer */
2418         cur_stm_line_.copy_from(&line_ptr);
2419 
2420         /* remember the method start offset */
2421         entry_ofs_ = method_start_ofs;
2422 
2423         /* remember the statement bounds */
2424         cur_stm_start_ = stm_start;
2425         cur_stm_end_ = stm_end;
2426 
2427         /* ask the function pointer to set up the debug table pointer */
2428         dbg_ptr_valid_ = func_ptr_.set_dbg_ptr(&dbg_ptr_);
2429     }
2430     else
2431     {
2432         /* the statement has no valid bounds; clear the debug info */
2433         entry_ofs_ = 0;
2434         cur_stm_start_ = 0;
2435         cur_stm_end_ = 0;
2436     }
2437 }
2438 
2439 /* ------------------------------------------------------------------------ */
2440 /*
2441  *   Single-step interrupt
2442  */
step(VMG_ const uchar ** pc_ptr,pool_ofs_t method_start_ofs,int hit_bp,int error_code)2443 void CVmDebug::step(VMG_ const uchar **pc_ptr, pool_ofs_t method_start_ofs,
2444                     int hit_bp, int error_code)
2445 {
2446     unsigned int exec_ofs;
2447     int line_ptr_valid;
2448     CVmFuncPtr func_ptr;
2449     CVmDbgLinePtr line_ptr;
2450     int bpnum;
2451     int hit_global_bp;
2452     int stop_for_step;
2453     ulong stm_start, stm_end;
2454     int trace_over_bp;
2455     vm_val_t orig_r0;
2456     int released_stack_reserve = FALSE;
2457 
2458     /*
2459      *   If we're stepping over a breakpoint, put the breakpoint back into
2460      *   effect and restore the actual execution mode
2461      */
2462     if (step_over_bp_)
2463     {
2464         /* no longer stepping over a breakpoint */
2465         step_over_bp_ = FALSE;
2466 
2467         /* restore the original execution mode */
2468         single_step_ = orig_single_step_;
2469         step_in_ = orig_step_in_;
2470         step_out_ = orig_step_out_;
2471 
2472         /* restore the breakpoint */
2473         step_over_bp_bp_->set_bp_instr(vmg_ TRUE, TRUE);
2474     }
2475 
2476     /* if we're already in the debugger, don't re-enter */
2477     if (in_debugger_)
2478         return;
2479 
2480     /* note that we're in the debugger so that we don't recurse into here */
2481     in_debugger_ = TRUE;
2482 
2483     /*
2484      *   if this is a stack overflow error, release the debugger reserve, so
2485      *   that we can look at expressions and otherwise go about our business
2486      *   without encountering additional stack overflows
2487      */
2488     if (error_code == VMERR_STACK_OVERFLOW)
2489         released_stack_reserve = G_stk->release_reserve();
2490 
2491     /*
2492      *   Remember the value of R0, so we can restore it when we leave; also
2493      *   push it onto the stack, so that it's protected against any garbage
2494      *   collection that occurs while the debugger has control.
2495      */
2496     orig_r0 = *G_interpreter->get_r0();
2497     G_stk->push(&orig_r0);
2498 
2499     /* presume we will not trace over a breakpoint at exit */
2500     trace_over_bp = FALSE;
2501 
2502     /* get the byte-code offset of this instruction */
2503     pc_ = (pc_ptr == 0 ? 0 : G_interpreter->pc_to_code_ofs(vmg_ *pc_ptr));
2504 
2505     /*
2506      *   If we're in single-step mode, assume we'll stop for stepping.  If
2507      *   we're stopping for an error, also set single-step mode for now.
2508      */
2509     stop_for_step = single_step_ || (error_code != 0);
2510 
2511     /* if we hit a breakpoint, find it */
2512     if (hit_bp)
2513     {
2514         CVmDebugBp *bp;
2515 
2516         /* find the breakpoint */
2517         bp = find_bp(pc_);
2518 
2519         /* get the breakpoint number */
2520         bpnum = (bp == 0 ? 0 : (int)(bp - bp_) + 1);
2521 
2522         /*
2523          *   If the breakpoint has a condition attached to it, stop only
2524          *   if the condition is true.
2525          */
2526         if (bp != 0 && bp->has_condition() && !bp->eval_cond(vmg0_))
2527         {
2528             /*
2529              *   The condition is false - this means we must ignore the
2530              *   breakpoint; forget that we hit the breakpoint.  (We can't
2531              *   just decide to keep running now because there are
2532              *   numerous other reasons we might want to stop now, and we
2533              *   must consider the other possibilities before we jump back
2534              *   into the program.)
2535              */
2536             hit_bp = FALSE;
2537 
2538             /* we don't have a valid breakpoint number after all */
2539             bpnum = 0;
2540 
2541             /*
2542              *   Since we're skipping this breakpoint, we need to trace
2543              *   over it to continue running.
2544              */
2545             trace_over_bp = TRUE;
2546         }
2547     }
2548     else
2549     {
2550         /* we don't have a valid breakpoint */
2551         bpnum = 0;
2552     }
2553 
2554     /* presume we won't hit a global breakpoint */
2555     hit_global_bp = FALSE;
2556 
2557     /*
2558      *   If there are any global breakpoints, and we didn't hit a code
2559      *   breakpoint, check for a hit.  Don't bother with this if we've hit
2560      *   a real breakpoint, since we'll certainly stop in that case.
2561      *   Also, don't bother checking global breakpoints if we're in native
2562      *   code (i.e., pc_ptr == 0), since we can't stop right now if we
2563      *   are.
2564      */
2565     if (!hit_bp && pc_ptr != 0 && global_bp_cnt_ != 0)
2566     {
2567         CVmDebugBp *bp;
2568         size_t i;
2569 
2570         /* scan all of the breakpoints and look for a global breakpoint */
2571         for (i = 0, bp = bp_ ; i < VMDBG_BP_MAX ; ++i, ++bp)
2572         {
2573             /*
2574              *   if this breakpoint is enabled and global, evaluate its
2575              *   condition
2576              */
2577             if (bp->is_in_use() && !bp->is_disabled() && bp->is_global())
2578             {
2579                 /* evaluate the condition */
2580                 if (bp->eval_cond(vmg0_))
2581                 {
2582                     /*
2583                      *   we've hit a global breakpoint - note it, and
2584                      *   compute the breakpoint number (it's just the
2585                      *   breakpoint index adjusted to a 1-based index)
2586                      */
2587                     hit_global_bp = TRUE;
2588                     bpnum = (int)(i + 1);
2589 
2590                     /*
2591                      *   If this is a stop-when-true condition, automatically
2592                      *   disable the breakpoint.  Once a global
2593                      *   stop-when-true breakpoint hits, its condition will
2594                      *   probably remain true for some time, and it would be
2595                      *   pointless to have it keep hitting over and over
2596                      *   again now.  Note that this isn't necessary for
2597                      *   stop-on-change breakpoints, as the fact that the
2598                      *   value just changed doesn't tell us anything about
2599                      *   the likelihood of future changes.
2600                      */
2601                     if (!bp->stop_on_change())
2602                         bp->set_disabled(TRUE);
2603 
2604                     /*
2605                      *   there's no need to look further - we only need to
2606                      *   have one global breakpoint hit in order to stop
2607                      */
2608                     break;
2609                 }
2610             }
2611         }
2612     }
2613 
2614     /*
2615      *   Check to see if we're within the same source code statement that
2616      *   we were in the last time we were in this routine.  If so, we
2617      *   already have most of the information we need about the execution
2618      *   location, so we can avoid a little set-up work.  If we're not in
2619      *   the same statement any more, we have to figure out where we are.
2620      *   Note that we will also break if we're at a different stack level
2621      *   than we were last time, because in this case we're stepping out
2622      *   of a recursive invocation.
2623      */
2624     if (stop_for_step
2625         && pc_ >= cur_stm_start_ && pc_ < cur_stm_end_
2626         && G_interpreter->get_frame_depth(vmg0_) == step_frame_depth_)
2627     {
2628         /*
2629          *   No matter what source-stepping mode we're in, we never stop
2630          *   twice consecutively at the same source statement.  Make a
2631          *   note that we don't need to stop here.  However, don't just
2632          *   return now, since we might need to stop for a breakpoint or a
2633          *   global breakpoint.
2634          */
2635         stop_for_step = FALSE;
2636     }
2637 
2638     /*
2639      *   If we're in step-over mode, there's no need to stop if we're
2640      *   executing at any stack level nested within the original stack frame.
2641      *   If this is the case, we can simply return now.  Of course, if we're
2642      *   stopping because we hit an error, don't worry about our step mode.
2643      */
2644     if (stop_for_step
2645         && error_code == 0
2646         && single_step_ && !step_in_
2647         && G_interpreter->get_frame_depth(vmg0_) > step_frame_depth_)
2648     {
2649         /* we don't need to stop for stepping */
2650         stop_for_step = FALSE;
2651     }
2652 
2653     /* figure out where we're tracing, if we're in byte code */
2654     if (pc_ptr != 0)
2655     {
2656         /* set up a pointer to the current function's header */
2657         G_interpreter->set_current_func_ptr(vmg_ &func_ptr);
2658 
2659         /* for the UI, compute the offset from the start of the method */
2660         exec_ofs = (unsigned int)(pc_ - method_start_ofs);
2661 
2662         /* get the boundaries of the current source code statement */
2663         line_ptr_valid =
2664             CVmRun::get_stm_bounds(vmg_ &func_ptr, exec_ofs, &line_ptr,
2665                                    &stm_start, &stm_end);
2666 
2667         /* adjust the statement boundaries to absolute addresses */
2668         stm_start += method_start_ofs;
2669         stm_end += method_start_ofs;
2670     }
2671     else
2672     {
2673         /* we're in native code, so there is no valid byte code location */
2674         exec_ofs = 0;
2675         line_ptr_valid = FALSE;
2676         stm_start = stm_end = 0;
2677     }
2678 
2679     /*
2680      *   If we're within the same source line and at the same stack depth
2681      *   that we were at last time - even if the apparent byte-code location
2682      *   has us in a different line - don't stop for a single-step
2683      *   operation.  The same source line can generate byte code at
2684      *   different places in certain types of complex statements, and we
2685      *   don't want to confuse the user by stopping twice in a row at the
2686      *   same line (thus showing no apparent change in the execution
2687      *   location) in such cases.
2688      *
2689      *   However, if we're stopping due to a run-time error, do stop even if
2690      *   we're in the same location - it just means we got another error at
2691      *   the same place.
2692      */
2693     if (stop_for_step
2694         && error_code == 0
2695         && G_interpreter->get_frame_depth(vmg0_) == step_frame_depth_
2696         && line_ptr_valid
2697         && cur_stm_start_ != 0
2698         && line_ptr.get_source_id() == cur_stm_line_.get_source_id()
2699         && line_ptr.get_source_line() == cur_stm_line_.get_source_line())
2700     {
2701         /* note that we don't want to stop for single-step mode */
2702         stop_for_step = FALSE;
2703     }
2704 
2705     /*
2706      *   If we're in native code and we decided that we should stop,
2707      *   remember the current code location as the last stop location.
2708      *   Even though we can't actually stop here, we want to act as though
2709      *   we did, so that next time we step through actual byte code we'll
2710      *   notice that we are at a different location than we were for the
2711      *   last stop.
2712      */
2713     if (pc_ptr == 0 && stop_for_step)
2714     {
2715         /*
2716          *   set the current statement bounds to invalid, to ensure that
2717          *   we will notice we are in a different location for the next
2718          *   valid byte code location
2719          */
2720         cur_stm_start_ = cur_stm_end_ = 0;
2721     }
2722 
2723     /*
2724      *   If we're stopping for any reason (single-stepping, breakpoint, or
2725      *   global breakpoint), enter the interactive debugger user
2726      *   interface.  In any case, only stop if we have a valid line
2727      *   pointer, since we can't do anything at a location without a valid
2728      *   source line.
2729      *
2730      *   Note that we can never take control when we're stepping through
2731      *   native code (i.e., pc_ptr == 0).  We're called for native code
2732      *   execution only to advise us of the change in stack levels so that
2733      *   we can properly track step in/over/out modes properly through
2734      *   native code traversal.
2735      */
2736     if (line_ptr_valid && (stop_for_step || hit_bp || hit_global_bp))
2737     {
2738         /* remember the current function pointer */
2739         func_ptr_.copy_from(&func_ptr);
2740 
2741         /* remember the current frame depth */
2742         step_frame_depth_ = G_interpreter->get_frame_depth(vmg0_);
2743 
2744         /* remember the new line pointer */
2745         cur_stm_line_.copy_from(&line_ptr);
2746 
2747         /* remember the method start offset */
2748         entry_ofs_ = method_start_ofs;
2749 
2750         /* remember the statement bounds */
2751         cur_stm_start_ = stm_start;
2752         cur_stm_end_ = stm_end;
2753 
2754         /* ask the function pointer to set up the debug table pointer */
2755         dbg_ptr_valid_ = func_ptr_.set_dbg_ptr(&dbg_ptr_);
2756 
2757         /*
2758          *   remove all breakpoints from the code while we have control,
2759          *   so that the code is all the original instructions while the
2760          *   user is looking at it
2761          */
2762         suspend_all_bps(vmg0_);
2763 
2764         /*
2765          *   call the UI interactive command loop - this won't return
2766          *   until the user tells us to resume execution
2767          */
2768         CVmDebugUI::cmd_loop(vmg_ bpnum, error_code, &exec_ofs);
2769 
2770         /* restore breakpoints now that we're resuming execution */
2771         restore_all_bps(vmg0_);
2772 
2773         /*
2774          *   convert the UI's method offset back into an absolute code
2775          *   offset (we can never move the execution point outside of the
2776          *   current method, so we merely have to add the current method
2777          *   start entry address to the method offset)
2778          */
2779         pc_ = entry_ofs_ + exec_ofs;
2780 
2781         /*
2782          *   before returning, re-translate the code pool offset of the
2783          *   current execution point back into a memory address, in case
2784          *   we moved the execution point
2785          */
2786         *pc_ptr = (const uchar *)G_code_pool->get_ptr(pc_);
2787 
2788         /*
2789          *   get the boundaries of the current statement, in case it
2790          *   changed - we need this for the next time we enter the
2791          *   debugger, so we can tell (if we're stepping) whether we've
2792          *   left the most recent statement or not
2793          */
2794         if (CVmRun::get_stm_bounds(vmg_ &func_ptr_, exec_ofs,
2795                                    &cur_stm_line_,
2796                                    &cur_stm_start_, &cur_stm_end_))
2797         {
2798             /* adjust the statement boundaries to absolute addresses */
2799             cur_stm_start_ += entry_ofs_;
2800             cur_stm_end_ += entry_ofs_;
2801         }
2802         else
2803         {
2804             /* we have no information for this statement */
2805             cur_stm_start_ = cur_stm_end_ = 0;
2806         }
2807 
2808         /*
2809          *   note that we must trace over any breakpoint at this new
2810          *   location, since we want to execute the original instruction
2811          *   rather than just stopping again
2812          */
2813         trace_over_bp = TRUE;
2814     }
2815 
2816     /*
2817      *   if we released the stack reserve, put it back in reserve now that
2818      *   we're leaving the debugger - the reserve is for the debugger's use
2819      *   only, so we don't need it once we resume execution
2820      */
2821     if (released_stack_reserve)
2822         G_stk->recover_reserve();
2823 
2824     /* note that we're leaving the debugger */
2825     in_debugger_ = FALSE;
2826 
2827     /*
2828      *   If appropriate, check the current code location for a breakpoint;
2829      *   if there's a breakpoint, we need to go through a couple of extra
2830      *   steps to resume execution so that we don't jump right back into
2831      *   the debugger.
2832      *
2833      *   The problem is that if we go or step after hitting a breakpoint,
2834      *   and we were to simply leave the breakpoint in effect, we'd just
2835      *   hit the same breakpoint again.  What we actually want to do is
2836      *   execute the original instruction.  To do this, we must first
2837      *   remove the breakpoint, restoring the original instruction, then
2838      *   single-step the instruction, then, when the debugger regains
2839      *   control after the single-step, put the breakpoint back.  Finally,
2840      *   we can resume with whatever kind of stepping we were going to do
2841      *   in the first place.
2842      */
2843     if (trace_over_bp && **pc_ptr == (uchar)OPC_BP)
2844     {
2845         CVmDebugBp *bp;
2846 
2847         /* find the breakpoint */
2848         bp = find_bp(pc_);
2849 
2850         /* if we found the breakpoint, remove it temporarily */
2851         if (bp != 0)
2852         {
2853             /* restore the original instruction */
2854             bp->set_bp_instr(vmg_ FALSE, TRUE);
2855 
2856             /* note that we're in step-over-bp mode */
2857             step_over_bp_ = TRUE;
2858 
2859             /* remember the breakpoint we're stepping over */
2860             step_over_bp_bp_ = bp;
2861 
2862             /* remember the original execution mode */
2863             orig_single_step_ = single_step_;
2864             orig_step_in_ = step_in_;
2865             orig_step_out_ = step_out_;
2866 
2867             /*
2868              *   temporary set single-step mode, so that we get control
2869              *   immediately after executing this single instruction
2870              */
2871             single_step_ = TRUE;
2872             step_in_ = TRUE;
2873             step_out_ = FALSE;
2874         }
2875     }
2876 
2877     /*
2878      *   restore the original value of R0, and discard the copy we pushed
2879      *   onto the stack for protection against garbage collection
2880      */
2881     *G_interpreter->get_r0() = orig_r0;
2882     G_stk->discard();
2883 }
2884 
2885 /* ------------------------------------------------------------------------ */
2886 /*
2887  *   Suspend all breakpoints - removes all BP instructions from the code
2888  *   and restores the original instructions
2889  */
suspend_all_bps(VMG0_)2890 void CVmDebug::suspend_all_bps(VMG0_)
2891 {
2892     CVmDebugBp *bp;
2893     size_t i;
2894 
2895     /* loop over all breakpoints */
2896     for (i = 0, bp = bp_ ; i < VMDBG_BP_MAX ; ++i, ++bp)
2897     {
2898         /*
2899          *   if this breakpoint is active, restore its original
2900          *   instruction (since this routine sets up for switching in and
2901          *   out of the debugger, force the instruction update even if
2902          *   we're in debugger mode)
2903          */
2904         if (bp->is_in_use() && !bp->is_disabled())
2905             bp->set_bp_instr(vmg_ FALSE, TRUE);
2906     }
2907 }
2908 
2909 /*
2910  *   Restore all breakpoints - restores all BP instructions removed by
2911  *   suspend_all_bps()
2912  */
restore_all_bps(VMG0_)2913 void CVmDebug::restore_all_bps(VMG0_)
2914 {
2915     CVmDebugBp *bp;
2916     size_t i;
2917 
2918     /* loop over all breakpoints */
2919     for (i = 0, bp = bp_ ; i < VMDBG_BP_MAX ; ++i, ++bp)
2920     {
2921         /* if this breakpoint is active, put it back */
2922         if (bp->is_in_use() && !bp->is_disabled())
2923             bp->set_bp_instr(vmg_ TRUE, TRUE);
2924     }
2925 }
2926 
2927 /* ------------------------------------------------------------------------ */
2928 /*
2929  *   Determine if a code location is within the current active method
2930  */
is_in_current_method(VMG_ unsigned long code_addr)2931 int CVmDebug::is_in_current_method(VMG_ unsigned long code_addr)
2932 {
2933     ulong end_addr;
2934 
2935     /*
2936      *   if the current debug pointer isn't valid, we can't make this
2937      *   determination, so indicate that we're not in the active method
2938      */
2939     if (!dbg_ptr_valid_)
2940         return FALSE;
2941 
2942     /*
2943      *   Determine the endpoint of the method's code.  If the method has
2944      *   an exception table, it's the endpoint; otherwise, the debug table
2945      *   is the endpoint.
2946      */
2947     end_addr = entry_ofs_ + (func_ptr_.get_exc_ofs() != 0
2948                              ? func_ptr_.get_exc_ofs()
2949                              : func_ptr_.get_debug_ofs());
2950 
2951     /*
2952      *   if the code address is between the entrypoint address for the
2953      *   active method and the ending address we just calculated, it's in
2954      *   the active method; otherwise, it's somewhere else
2955      */
2956     return (code_addr >= entry_ofs_ && code_addr < end_addr);
2957 }
2958 
2959 
2960 
2961 /* ------------------------------------------------------------------------ */
2962 /*
2963  *   Set up a function pointer and line pointer, and get the statement
2964  *   bounds, for the function at the given stack level.  Level 0 is the
2965  *   currently executing function, 1 is the first enclosing stack level
2966  *   (which called the current function), and so on.  Returns true if
2967  *   successful, false if the information isn't available.
2968  */
get_stack_level_info(VMG_ int level,CVmFuncPtr * func_ptr,CVmDbgLinePtr * line_ptr,ulong * stm_start,ulong * stm_end) const2969 int CVmDebug::get_stack_level_info(VMG_ int level, CVmFuncPtr *func_ptr,
2970                                    CVmDbgLinePtr *line_ptr,
2971                                    ulong *stm_start, ulong *stm_end) const
2972 {
2973     /*
2974      *   if we're at level 0, it's the current method; otherwise, get the
2975      *   given enclosing frame from the stack
2976      */
2977     if (level == 0)
2978     {
2979         /*
2980          *   if we don't have a valid debug information pointer, we can't
2981          *   get any information on the current source location
2982          */
2983         if (!dbg_ptr_valid_)
2984             return 1;
2985 
2986         /* use the current statement */
2987         func_ptr->copy_from(&func_ptr_);
2988         line_ptr->copy_from(&cur_stm_line_);
2989         *stm_start = cur_stm_start_;
2990         *stm_end = cur_stm_end_;
2991 
2992         /* success */
2993         return 0;
2994     }
2995     else
2996     {
2997         vm_val_t *fp;
2998         ulong ret_ofs;
2999 
3000         /*
3001          *   Enclosing level - walk up the stack to the desired level.
3002          *   Note that we are actually looking for the return address, so
3003          *   we want to find the frame just within the desired level - for
3004          *   the first enclosing level, we actually want the current
3005          *   frame.
3006          */
3007         for (fp = G_interpreter->get_frame_ptr() ; level > 1 && fp != 0 ;
3008              fp = G_interpreter->get_enclosing_frame_ptr(vmg_ fp), --level) ;
3009 
3010         /*
3011          *   if we didn't reach the desired level, or the frame pointer is
3012          *   null, we didn't find the requested frame - return failure
3013          */
3014         if (level > 1 || fp == 0)
3015             return 1;
3016 
3017         /* set up a function pointer for the return address from this frame */
3018         G_interpreter->set_return_funcptr_from_frame(vmg_ func_ptr, fp);
3019 
3020         /* get the return address for the frame */
3021         ret_ofs = G_interpreter->get_return_ofs_from_frame(vmg_ fp);
3022 
3023         /*
3024          *   Find the source line information for the return address in
3025          *   the frame.  Return failure if there's no debug information
3026          *   for the method.
3027          */
3028         if (!CVmRun::get_stm_bounds(vmg_ func_ptr, ret_ofs,
3029                                     line_ptr, stm_start, stm_end))
3030             return 1;
3031 
3032         /* success */
3033         return 0;
3034     }
3035 }
3036 
3037 /* ------------------------------------------------------------------------ */
3038 /*
3039  *   Allocate the method header list
3040  */
alloc_method_header_list(ulong cnt)3041 void CVmDebug::alloc_method_header_list(ulong cnt)
3042 {
3043     /*
3044      *   if we already have a method header list, expand it; otherwise,
3045      *   allocate a new one
3046      */
3047     if (method_hdr_ != 0)
3048     {
3049         /*
3050          *   if we're growing the list, expand it; otherwise ignore the
3051          *   new allocation
3052          */
3053         if (cnt > method_hdr_cnt_)
3054         {
3055             /* expand the list */
3056             method_hdr_ = (ulong *)t3realloc(method_hdr_,
3057                                              cnt * sizeof(ulong));
3058 
3059             /* note the new size */
3060             method_hdr_cnt_ = cnt;
3061         }
3062     }
3063     else
3064     {
3065         /* allocate the new list */
3066         method_hdr_ = (ulong *)t3malloc(cnt * sizeof(ulong));
3067 
3068         /* note the size */
3069         method_hdr_cnt_ = cnt;
3070     }
3071 }
3072 
3073 /*
3074  *   Given a code pool address, find the method header containing the
3075  *   address.  This searches the method header list for the nearest method
3076  *   header whose address is less than the given address.
3077  */
find_method_header(pool_ofs_t ofs)3078 pool_ofs_t CVmDebug::find_method_header(pool_ofs_t ofs)
3079 {
3080     ulong lo;
3081     ulong hi;
3082 
3083     /* if there are no method headers, there's nothing to find */
3084     if (method_hdr_cnt_ == 0)
3085         return 0;
3086 
3087     /* perform a binary search of the method header list */
3088     lo = 0;
3089     hi = method_hdr_cnt_ - 1;
3090     while (lo <= hi)
3091     {
3092         ulong cur;
3093         pool_ofs_t addr;
3094         pool_ofs_t next_addr;
3095 
3096         /* split the difference to get the current entry */
3097         cur = lo + (hi - lo)/2;
3098 
3099         /* get this entry's address */
3100         addr = (pool_ofs_t)get_method_header(cur);
3101 
3102         /*
3103          *   get the next entry's address - if this is the last entry, the
3104          *   next entry's address is the highest possible address
3105          */
3106         next_addr = (cur + 1 >= method_hdr_cnt_
3107                      ? (pool_ofs_t)0xffffffff
3108                      : (pool_ofs_t)get_method_header(cur + 1));
3109 
3110         /* check how this value compares */
3111         if (ofs >= next_addr)
3112         {
3113             /* we need to go higher */
3114             lo = (cur == lo ? cur + 1 : cur);
3115         }
3116         else if (ofs < addr)
3117         {
3118             /* we need to go lower */
3119             hi = (cur == hi ? hi - 1 : cur);
3120         }
3121         else
3122         {
3123             /* found it - return this start address */
3124             return addr;
3125         }
3126     }
3127 
3128     /*
3129      *   didn't find anything - we don't have any way to indicate this, so
3130      *   just return the lowest code pool address
3131      */
3132     return 0;
3133 }
3134 
3135 
3136 /* ------------------------------------------------------------------------ */
3137 /*
3138  *   Reverse-mapping hash entry
3139  */
3140 
3141 /*
3142  *   Construct.  Note that we don't bother to convert the ulong key value
3143  *   to a portable representation, since this entry is never written to a
3144  *   file - it's purely an in-memory construct.  Note also that we don't
3145  *   care whether there are any embedded null bytes in the value, since
3146  *   the hash entry base class works entirely with counted-length strings.
3147  *
3148  *   Treating a ulong value as a character buffer might seem suspicious
3149  *   from a portability standpoint, but the C/C++ standards make this
3150  *   perfectly legitimate as long as we don't make assumptions about the
3151  *   size or byte ordering of the underlying representation, which we're
3152  *   not - the byte ordering is irrelevant, since we're simply treating
3153  *   this value as an identifying string of bytes (i.e., a hash key), and
3154  *   we're using sizeof() to ensure we use the correct local size for the
3155  *   type.  What we're doing is exactly the same thing that memcpy() would
3156  *   do if asked to copy a ulong value, and equally safe.
3157  */
CVmHashEntryDbgRev(ulong sym_val,const char * sym,size_t len)3158 CVmHashEntryDbgRev::CVmHashEntryDbgRev(ulong sym_val, const char *sym,
3159                                        size_t len)
3160     : CVmHashEntry((char *)&sym_val, sizeof(sym_val), TRUE)
3161 {
3162     /* make a copy of the symbol name */
3163     sym_ = lib_copy_str(sym, len);
3164     sym_len_ = len;
3165 }
3166 
3167 /*
3168  *   Destruct
3169  */
~CVmHashEntryDbgRev()3170 CVmHashEntryDbgRev::~CVmHashEntryDbgRev()
3171 {
3172     /* delete our copy of the symbol name */
3173     lib_free_str(sym_);
3174 }
3175 
3176 /*
3177  *   check for a match
3178  */
matches(const char * str,size_t len) const3179 int CVmHashEntryDbgRev::matches(const char *str, size_t len) const
3180 {
3181     /*
3182      *   it's a match if the strings are the same length and all
3183      *   characters match, treating case as significant
3184      */
3185     return (len == len_
3186             && memcmp(str, str_, len * sizeof(*str)) == 0);
3187 }
3188 
3189 /*
3190  *   Reverse-lookup hash function
3191  */
compute_hash(const char * s,size_t l) const3192 unsigned int CVmHashFuncDbgRev::compute_hash(const char *s, size_t l) const
3193 {
3194     uint acc;
3195 
3196     /* add up all the byte values in the string */
3197     for (acc = 0 ; l != 0 ; ++s, --l)
3198     {
3199         uchar c;
3200 
3201         c = (uchar)*s;
3202         acc += c;
3203     }
3204 
3205     /* return the accumulated value */
3206     return acc;
3207 }
3208 
3209 /* ------------------------------------------------------------------------ */
3210 /*
3211  *   Breakpoint object
3212  */
3213 
3214 /*
3215  *   instantiate
3216  */
CVmDebugBp()3217 CVmDebugBp::CVmDebugBp()
3218 {
3219     /* not yet in use */
3220     in_use_ = FALSE;
3221 
3222     /* no code address yet */
3223     code_addr_ = 0;
3224 
3225     /* not disabled */
3226     disabled_ = FALSE;
3227 
3228     /* no original instruction yet */
3229     orig_instr_ = (char)OPC_NOP;
3230 
3231     /* no condition expression or byte code object yet */
3232     cond_ = 0;
3233     cond_buf_len_ = 0;
3234     compiled_cond_ = 0;
3235     has_cond_ = FALSE;
3236     stop_on_change_ = FALSE;
3237     prv_val = 0;
3238 }
3239 
3240 
3241 /*
3242  *   delete
3243  */
~CVmDebugBp()3244 CVmDebugBp::~CVmDebugBp()
3245 {
3246     /* delete any condition text buffer */
3247     if (cond_ != 0)
3248         t3free(cond_);
3249 }
3250 
3251 /*
3252  *   terminate - this is called during VM termination so that we can
3253  *   delete our compiled condition code objects (we can't do this in the
3254  *   destructor because we need access to the VM globals)
3255  */
do_terminate(VMG0_)3256 void CVmDebugBp::do_terminate(VMG0_)
3257 {
3258     /* release our object table global variable, if we have one */
3259     if (prv_val != 0)
3260     {
3261         G_obj_table->delete_global_var(prv_val);
3262         prv_val = 0;
3263     }
3264 
3265 #if 0 // $$$
3266     // It's not necessary to delete my condition object explicitly -
3267     // when the code pool is unloaded, it'll delete all remaining
3268     // dynamic objects.  I'm not sure I like this approach - it seems
3269     // cleaner to have each object's owner explicitly free it, and
3270     // we're clearly the owner of this object - but there's no
3271     // practical reason to worry about it.  So, I'll leave it like
3272     // this for now and maybe revisit it later.
3273     //
3274     // (For future reference, what's needed is a pre-termination
3275     // cleanup pass that vmmain.cpp makes BEFORE calling loader->unload(),
3276     // because that routine will detach the backing stores and thereby
3277     // clean up the code pool's dynamic objects.  If we're going to
3278     // explicitly delete our object here, this needs to be done before
3279     // the code pool's backing store is detached.  Note that doing things
3280     // in that order will NOT result in redundant deletions, since deleting
3281     // our object will remove it from the code pool's dynamic object list
3282     // and the object thus won't be deleted again by the backing store
3283     // detach operation.  However, if we detach first and then delete
3284     // later, we'll have the redundant deletion problem.)
3285 
3286     /* if I have a compiled condition object, delete it */
3287     if (compiled_cond_ != 0)
3288     {
3289         /* delete the object */
3290         G_code_pool->get_dynamic_ifc()->dynpool_delete(compiled_cond_);
3291 
3292         /* forget the object */
3293         compiled_cond_ = 0;
3294     }
3295 #endif /* 0 */
3296 }
3297 
3298 /*
3299  *   Set the breakpoint's information
3300  */
set_info(VMG_ ulong code_addr,const char * cond,int change,int disabled,char * errbuf,size_t errbuflen)3301 int CVmDebugBp::set_info(VMG_ ulong code_addr, const char *cond, int change,
3302                          int disabled, char *errbuf, size_t errbuflen)
3303 {
3304     int err;
3305 
3306     /* remember the code address */
3307     code_addr_ = code_addr;
3308 
3309     /* remember the disabled status */
3310     disabled_ = disabled;
3311 
3312     /* remember the expression */
3313     if ((err = set_condition(vmg_ cond, change, errbuf, errbuflen)) != 0)
3314         return err;
3315 
3316     /* remember the original instruction at the code address */
3317     if (code_addr != 0)
3318     {
3319         char *code_ptr;
3320 
3321         /* get a writable pointer to the code location */
3322         code_ptr = G_code_pool->get_writable_ptr(code_addr_);
3323 
3324         /*
3325          *   if we got a valid code pointer, remember the original
3326          *   instruction at the code address, so that we can restore this
3327          *   instruction when we remove the BP instruction
3328          */
3329         if (code_ptr != 0)
3330             orig_instr_ = *code_ptr;
3331 
3332         /* set the BP instruction if appropriate */
3333         set_bp_instr(vmg_ TRUE, FALSE);
3334     }
3335 
3336     /* success */
3337     return 0;
3338 }
3339 
3340 /*
3341  *   Set the condition text
3342  */
set_condition(VMG_ const char * new_cond,int change,char * errbuf,size_t errbuflen)3343 int CVmDebugBp::set_condition(VMG_ const char *new_cond, int change,
3344                               char *errbuf, size_t errbuflen)
3345 {
3346     /* if we already have a compiled condition object, delete it */
3347     if (compiled_cond_ != 0)
3348     {
3349         /* delete the object */
3350         G_code_pool->get_dynamic_ifc()->dynpool_delete(compiled_cond_);
3351 
3352         /* forget the object */
3353         compiled_cond_ = 0;
3354     }
3355 
3356     /*
3357      *   check for an empty condition string - treat it as no condition if
3358      *   it's all blanks
3359      */
3360     if (new_cond != 0)
3361     {
3362         const char *p;
3363 
3364         /* scan for a non-blank character */
3365         for (p = new_cond ; is_space(*p) ; ++p) ;
3366 
3367         /*
3368          *   if we scanned the entire string without finding anything but
3369          *   spaces, treat this is no condition
3370          */
3371         if (*p == '\0')
3372             new_cond = 0;
3373     }
3374 
3375     /* if there's a new condition, set it up */
3376     if (new_cond != 0)
3377     {
3378         size_t new_cond_len;
3379         CVmDbgSymtab *symtab;
3380         CVmDbgSymtab local_symtab;
3381         int err;
3382 
3383         /* presume we won't find a local symbol table */
3384         symtab = 0;
3385 
3386         /*
3387          *   if there's a code address, set up a local symbol table to
3388          *   compile the expression in the scope of the code location;
3389          *   otherwise, compile with no local scope
3390          */
3391         if (code_addr_ != 0)
3392         {
3393             pool_ofs_t method_addr;
3394             CVmFuncPtr func_ptr;
3395             CVmDbgLinePtr line_ptr;
3396             CVmDbgTablePtr dbg_ptr;
3397             ulong stm_start;
3398             ulong stm_end;
3399 
3400             /* find the entry pointer for the selected code address */
3401             method_addr = G_debugger->find_method_header(code_addr_);
3402 
3403             /* set up a function pointer at the code location */
3404             func_ptr.set((const uchar *)G_code_pool->get_ptr(method_addr));
3405 
3406             /* set up the line pointer */
3407             if (CVmRun::get_stm_bounds(vmg_ &func_ptr,
3408                                        code_addr_ - method_addr,
3409                                        &line_ptr, &stm_start, &stm_end)
3410                 && func_ptr.set_dbg_ptr(&dbg_ptr))
3411             {
3412                 /*
3413                  *   We have a valid debug table for the code location -
3414                  *   set up a local symbol table object for the code.
3415                  *   Compile the expression for stack level zero, since we
3416                  *   will always evaluate the expression when this code is
3417                  *   active.
3418                  */
3419                 local_symtab.init(vmg_ &dbg_ptr, line_ptr.get_frame_id(), 0);
3420 
3421                 /* we now have a local symbol table for the code - use it */
3422                 symtab = &local_symtab;
3423             }
3424         }
3425 
3426         /*
3427          *   Compile the expression.  Compile at stack level zero, since
3428          *   we'll always execute this code immediately upon hitting the
3429          *   breakpoint, which means that the breakpoint location will be
3430          *   the active stack level whenever this code is executed.
3431          *
3432          *   Presume that 'self' will be valid in this context.  If it
3433          *   isn't, 'self' will be nil at run-time, so no harm will be done.
3434          */
3435         err = G_debugger->compile_expr(vmg_ new_cond, 0, symtab, TRUE,
3436                                        FALSE, 0, &compiled_cond_,
3437                                        errbuf, errbuflen);
3438 
3439         /* if an error occurred, return the error code */
3440         if (err != 0)
3441             return err;
3442 
3443         /* note that we have a condition */
3444         has_cond_ = TRUE;
3445 
3446         /* note whether or not it's a stop-on-change condition */
3447         stop_on_change_ = change;
3448 
3449         /* set the size of the new condition, including the trailing null */
3450         new_cond_len = strlen(new_cond) + 1;
3451 
3452         /* allocate or expand the condition buffer if required */
3453         if (cond_buf_len_ < new_cond_len)
3454         {
3455             /* allocate or reallocate the buffer */
3456             if (cond_ == 0)
3457                 cond_ = (char *)t3malloc(new_cond_len);
3458             else
3459                 cond_ = (char *)t3realloc(cond_, new_cond_len);
3460 
3461             /* note the new size of our condition buffer */
3462             cond_buf_len_ = new_cond_len;
3463         }
3464 
3465         /* copy the new condition text into our buffer */
3466         memcpy(cond_, new_cond, new_cond_len);
3467 
3468         /* if it's a stop-on-change condition, initialize the value */
3469         if (change)
3470         {
3471             /*
3472              *   Allocate an object table global variable to hold the
3473              *   previous value, if we haven't already.  We allocate a global
3474              *   for this so that our value is tracked in the garbage
3475              *   collector.
3476              */
3477             if (prv_val == 0)
3478                 prv_val = G_obj_table->create_global_var();
3479 
3480             /*
3481              *   set the value to 'empty' to indicate that we haven't
3482              *   evaluated our expression for the first time yet
3483              */
3484             prv_val->val.set_empty();
3485 
3486             /*
3487              *   evaluate the condition to initialize our memory of the
3488              *   current value; we'll break the next time we reach this
3489              *   breakpoint and the new value of the expression differs from
3490              *   this saved value
3491              */
3492             eval_cond(vmg0_);
3493         }
3494     }
3495     else
3496     {
3497         /* note that there's no condition */
3498         has_cond_ = FALSE;
3499 
3500         /* if we had a global variable for the condition, delete it */
3501         if (prv_val != 0)
3502         {
3503             G_obj_table->delete_global_var(prv_val);
3504             prv_val = 0;
3505         }
3506     }
3507 
3508     /* success */
3509     return 0;
3510 }
3511 
3512 /*
3513  *   Evaluate my condition
3514  */
eval_cond(VMG0_)3515 int CVmDebugBp::eval_cond(VMG0_)
3516 {
3517     int ret;
3518     vmrun_save_ctx run_ctx;
3519     vmdbg_step_save_t old_step;
3520 
3521     /* save the interpreter context */
3522     G_interpreter->save_context(vmg_ &run_ctx);
3523 
3524     /* set the debugger to non-stepping mode */
3525     G_debugger->prepare_for_eval(&old_step);
3526 
3527     /*
3528      *   if there's no condition, just return false, since there's nothing
3529      *   to evaluate
3530      */
3531     if (compiled_cond_ == 0)
3532         return FALSE;
3533 
3534     /* execute the code in a protected block */
3535     err_try
3536     {
3537         vm_obj_id_t self_obj;
3538         vm_obj_id_t orig_target_obj;
3539         vm_obj_id_t defining_obj;
3540         vm_prop_id_t target_prop;
3541         vm_val_t *valp;
3542 
3543         /* get the 'self' object at the current stack level */
3544         self_obj = G_interpreter->get_self_at_level(vmg_ 0);
3545 
3546         /* get the target property for the level */
3547         target_prop = G_interpreter->get_target_prop_at_level(vmg_ 0);
3548 
3549         /* get the original target object and defining object at the level */
3550         orig_target_obj =
3551             G_interpreter->get_orig_target_obj_at_level(vmg_ 0);
3552         defining_obj = G_interpreter->get_defining_obj_at_level(vmg_ 0);
3553 
3554         /* execute the code */
3555         G_interpreter->do_call(vmg_ 0, compiled_cond_->get_ofs(), 0,
3556                                self_obj, target_prop,
3557                                orig_target_obj, defining_obj, "dbg cond");
3558 
3559         /* get the return value */
3560         valp = G_interpreter->get_r0();
3561 
3562         /* check what kind of condition we have */
3563         if (stop_on_change_)
3564         {
3565             /*
3566              *   We're a stop-on-change condition - this means that we stop
3567              *   if the value of the expression differs from the previous
3568              *   value.  Check to see if the value differs, and return true
3569              *   (to indicate that the breakpoint condition has been met) if
3570              *   so.  If the previous value is 'empty', this means we've
3571              *   never evaluated the value before, so the value can't have
3572              *   changed; simply remember the new value in this case.
3573              */
3574             ret = (prv_val->val.typ != VM_EMPTY
3575                    && !prv_val->val.equals(vmg_ valp));
3576 
3577             /* remember the new value for next time */
3578             prv_val->val = *valp;
3579         }
3580         else
3581         {
3582             /* see what we have */
3583             switch(valp->typ)
3584             {
3585             case VM_INT:
3586                 /* if the number is zero, it's false, otherwise it's true */
3587                 ret = (valp->val.intval != 0);
3588                 break;
3589 
3590             case VM_NIL:
3591                 /* condition is false */
3592                 ret = FALSE;
3593                 break;
3594 
3595             default:
3596                 /* anything else is a true condition value */
3597                 ret = TRUE;
3598                 break;
3599             }
3600         }
3601     }
3602     err_catch(exc)
3603     {
3604         /*
3605          *   an error occurred - treat the condition as true so that we
3606          *   stop here
3607          */
3608         ret = TRUE;
3609     }
3610     err_end;
3611 
3612     /* restore the original execution mode */
3613     G_debugger->restore_from_eval(&old_step);
3614 
3615     /* rerstore the interpreter context */
3616     G_interpreter->restore_context(vmg_ &run_ctx);
3617 
3618     /* return the result */
3619     return ret;
3620 }
3621 
3622 
3623 /*
3624  *   Delete the breakpoint
3625  */
do_delete(VMG0_)3626 void CVmDebugBp::do_delete(VMG0_)
3627 {
3628     /* remove the BP instruction from the code if necessary */
3629     set_bp_instr(vmg_ FALSE, FALSE);
3630 
3631     /* mark myself as no longer active */
3632     in_use_ = FALSE;
3633 }
3634 
3635 /*
3636  *   Set or remove the BP instruction in the code
3637  */
set_bp_instr(VMG_ int set,int always)3638 void CVmDebugBp::set_bp_instr(VMG_ int set, int always)
3639 {
3640     /* if I have a code address, set or remove the BP instruction */
3641     if (code_addr_ != 0)
3642     {
3643         char *code_ptr;
3644 
3645         /* get a writable pointer to the code location */
3646         code_ptr = G_code_pool->get_writable_ptr(code_addr_);
3647 
3648         /* proceed only if we got a valid code pointer */
3649         if (code_ptr != 0)
3650         {
3651             /* check to see if we're setting or removing the BP instruction */
3652             if (set)
3653             {
3654                 /*
3655                  *   if we're not in the debugger, write the BP
3656                  *   instruction to the address - don't do this if we're
3657                  *   in the debugger, since we remove all BP instructions
3658                  *   from the code while the debugger has control to
3659                  *   prevent any recursive debugger invocations from
3660                  *   taking place
3661                  */
3662                 if (always || !G_debugger->is_in_debugger())
3663                     *code_ptr = (char)OPC_BP;
3664             }
3665             else
3666             {
3667                 /*
3668                  *   Restore the original instruction to that location.
3669                  *   This isn't necessary if we're in the debugger, since
3670                  *   we remove all BP's when the debugger takes control.
3671                  */
3672                 if (always || !G_debugger->is_in_debugger())
3673                     *code_ptr = orig_instr_;
3674             }
3675         }
3676     }
3677 }
3678