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