1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
|
/*
$Header: d:/cvsroot/tads/TADS2/DBG.H,v 1.3 1999/07/11 00:46:29 MJRoberts Exp $
*/
/*
* Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
dbg.h - debug interface
Function
Debugger interface definitions
Notes
The Debugger implementation is split into two parts: the "engine",
which implements the parts of the debugger that are independent of
user interface; and the UI. The UI is in a separate module so that
several user interfaces can be provided, and a particular one chosen
at link time. The UI section should contain nothing that is generic,
but only the parts specific to that user interface.
Modified
04/11/99 CNebel - Move extern C.
03/28/92 MJRoberts - creation
*/
#ifndef DBG_INCLUDED
#define DBG_INCLUDED
#ifndef OBJ_INCLUDED
#include "obj.h"
#endif
#ifndef PRP_INCLUDED
#include "prp.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* forward declarations */
struct bifcxdef;
struct toksdef;
struct toktdef;
struct tokcxdef;
/* stack frame record */
struct dbgfdef
{
struct runsdef *dbgfbp; /* base pointer of frame */
objnum dbgfself; /* 'self' object (MCMONINV for functions) */
objnum dbgftarg; /* actual target object */
prpnum dbgfprop; /* property being evalutated */
int dbgfargc; /* number of arguments */
int dbgfbif; /* set to built-in function number if in built-in */
uint dbgffr; /* offset in object of local frame symbol table */
uint dbgflin; /* OPCLINE operand of latest line */
};
typedef struct dbgfdef dbgfdef;
/* max number of frames to store in debug frame memory */
#define DBGMAXFRAME 100
/* maximum number of breakpoints set concurrently */
#define DBGBPMAX 50
/* breakpoint structure */
struct dbgbpdef
{
objnum dbgbpself; /* the "self" object for the breakpoint */
objnum dbgbptarg; /* actual target object for the breakpoint */
uint dbgbpofs; /* offset in object of the breakpoint */
uint dbgbpflg; /* breakpoint flags */
# define DBGBPFUSED 0x01 /* breakpoint has been set */
# define DBGBPFNAME 0x02 /* name of address has been stored */
# define DBGBPFCOND 0x04 /* breakpoint has a condition attached */
# define DBGBPFDISA 0x08 /* breakpoint is disabled */
# define DBGBPFCONDNAME 0x10 /* condition name string has been stored */
uint dbgbpnam; /* offset of address name within dbgcxnam buffer */
uint dbgbpcondnam; /* offset of condition string within buffer */
objnum dbgbpcond; /* object containing compiled condition for bp */
};
typedef struct dbgbpdef dbgbpdef;
/* maximum number of watch expressions set concurrently */
#define DBGWXMAX 30
/* watch expression structure */
struct dbgwxdef
{
objnum dbgwxobj; /* object containing compiled expression */
objnum dbgwxself; /* 'self' for the expression */
uint dbgwxnam; /* offset of expression text within dbgcxnam buffer */
uint dbgwxflg; /* flags for this watch expression slot */
# define DBGWXFUSED 0x01 /* watch slot is in use */
# define DBGWXFNAME 0x02 /* name of watch has been stored */
};
typedef struct dbgwxdef dbgwxdef;
/* amount of space for bp names (original address strings from user) */
#define DBGCXNAMSIZ 2048
/* debug context */
struct dbgcxdef
{
struct tiocxdef *dbgcxtio; /* text i/o context */
struct tokthdef *dbgcxtab; /* symbol table */
struct mcmcxdef *dbgcxmem; /* memory cache manager context */
struct errcxdef *dbgcxerr; /* error handling context */
struct lindef *dbgcxlin; /* chain of line sources */
int dbgcxfcn; /* number of frames in use */
int dbgcxdep; /* actual depth (if overflow frame buffer) */
int dbgcxfid; /* source file serial number */
dbgfdef dbgcxfrm[DBGMAXFRAME]; /* stack frames */
int dbgcxflg; /* flags for debug session */
# define DBGCXFSS 0x01 /* single-stepping source lines */
# define DBGCXFSO 0x02 /* stepping over a function/method call */
# define DBGCXFOK 0x04 /* debugger is linked in */
# define DBGCXFIND 0x08 /* in debugger - suppress stack trace on err */
# define DBGCXFGBP 0x10 /* global breakpoints in effect */
# define DBGCXFTRC 0x20 /* call tracing activated */
# define DBGCXFLIN2 0x40 /* new-style line records (line numbers) */
int dbgcxsof; /* frame depth at step-over time */
dbgbpdef dbgcxbp[DBGBPMAX]; /* breakpoints */
dbgwxdef dbgcxwx[DBGWXMAX]; /* watch expressions */
struct prscxdef *dbgcxprs; /* parsing context */
struct runcxdef *dbgcxrun; /* execution context */
uint dbgcxnamf; /* next free byte of dbgcxnam buffer */
uint dbgcxnams; /* size of dbgcxnam buffer */
char *dbgcxnam; /* space for bp address names */
char *dbgcxhstp; /* call history buffer */
uint dbgcxhstl; /* history buffer length */
uint dbgcxhstf; /* offset of next free byte of history */
/*
* This member is for the use of the user interface code. If the
* user interface implementation needs to store additional context,
* it can allocate a structure of its own (it should probably do
* this in dbguini()) and store a pointer to that structure here.
* Since the user interface entrypoints always have the debugger
* context passed as a parameter, the user interface code can
* recover its extra context information by following this pointer
* and casting it to its private structure type. The TADS code
* won't do anything with this pointer except initialize it to null
* when initializing the debugger context.
*/
void *dbgcxui;
};
typedef struct dbgcxdef dbgcxdef;
/* ======================================================================== */
/*
* Compiler interface. These routines are called by the compiler to
* inform the debug record generator about important events as
* compilation proceeds.
*/
/*
* Tell the current line source that we're compiling an executable
* line, and tell it the object number and offset of the code within the
* object.
*/
void dbgclin(struct tokcxdef *tokctx, objnum objn, uint ofs);
/* size of information given to line source via lincmpinf method */
#define DBGLINFSIZ 4
/* ======================================================================== */
/*
* Run-time interface. These routines are called by the run-time
* system to apprise the debugger of important events during execution.
*/
/*
* Determine if the debugger is present. Returns true if so, false if
* not. This should return false for any stand-alone version of the
* executable that isn't linked with the debugger. If this returns
* true, dbgucmd() must not have a trivial implementation -- dbgucmd()
* must at least let the user quit out of the game.
*
* This can be switched at either link time or compile time. If DBG_OFF
* is defined, we'll force this to return false; otherwise, we'll let
* the program define the appropriate implementation through the linker.
*/
#ifdef DBG_OFF
#define dbgpresent() (FALSE)
#else
int dbgpresent();
#endif
/* add a debug tracing record */
/* void dbgenter(dbgcxdef *ctx, runsdef *bp, objnum self, objnum target,
prpnum prop, int binum, int argc); */
/* tell debugger where the current line's local frame table is located */
/* void dbgframe(dbgcxdef *ctx, uint ofsfr, ofslin); */
/*
* Single-step interrupt: the run-time has reached a new source line.
* ofs is the offset from the start of the object of the line record,
* and p is the current execution pointer. *p can be changed upon
* return, in which case the run-time will continue from the new
* position; however, the new address must be within the same function
* or method as it was originally.
*/
/* void dbgssi(dbgcxdef *ctx, uint ofs, int instr,
int err, uchar *noreg *p); */
/* pop debug trace level */
/* void dbgleave(dbgcxdef *ctx, int exittype); */
#define DBGEXRET 0 /* return with no value */
#define DBGEXVAL 1 /* return with a value */
#define DBGEXPASS 2 /* use 'pass' to exit a function */
/* dump the stack into text output */
/* void dbgdump(dbgcxdef *ctx); */
/* reset debug stack (throw away entire contents) */
/* void dbgrst(dbgcxdef *ctx); */
/* activate debugger if possible; returns TRUE if no debugger is present */
int dbgstart(dbgcxdef *ctx);
/* add a string to the history buffer */
void dbgaddhist(dbgcxdef *ctx, char *buf, int bufl);
/*
* Find a base pointer, given the object+offset of the frame. If the
* frame is not active, this routine signals ERR_INACTFR; otherwise, the
* bp value for the frame is returned.
*/
struct runsdef *dbgfrfind(dbgcxdef *ctx, objnum frobj, uint frofs);
/* ======================================================================== */
/*
* User Interface Support routines. These routines are called by the
* user interface layer to get information from the debugger and perform
* debugging operations.
*/
/* get a symbol name; returns length of name */
int dbgnam(dbgcxdef *ctx, char *outbuf, int typ, int val);
/*
* Get information about current line. It is assumed that the caller
* knows the size of the line information .
*/
void dbglget(dbgcxdef *ctx, uchar *buf);
/*
* Get information about a line in an enclosing stack frame. Level 0 is
* the current line, level 1 is the first enclosing frame, and so on.
* Returns 0 on success, non-zero if the frame level is invalid.
*/
int dbglgetlvl(dbgcxdef *ctx, uchar *buf, int level);
/*
* Set a breakpoint by symbolic address: "function" or
* "object.property". The string may contain whitespace characters
* around each symbol; it must be null-terminated. If an error occurs,
* the error number is returned. bpnum returns with the breakpoint
* number if err == 0. If the condition string is given (and is not an
* empty string), the condition is compiled in the scope of the
* breakpoint and attached as the breakpoint condition.
*/
int dbgbpset(dbgcxdef *ctx, char *addr, int *bpnum);
/*
* Set a breakpoint at an object + offset location. If 'toggle' is
* true, and there's already a breakpoint at the given location, we'll
* clear the breakpoint; in this case, *did_set will return false to
* indicate that an existing breakpoint was cleared rather than a new
* breakpoint created. *did_set will return true if a new breakpoint
* was set.
*/
int dbgbpat(dbgcxdef *ctx, objnum objn, objnum self,
uint ofs, int *bpnum, char *bpname, int toggle,
char *condition, int *did_set);
/*
* Set a breakpoint at an object + offset location, optionally with a
* condition, using an existing breakpoint slot. If the slot is already
* in use, we'll return an error.
*/
int dbgbpatid(dbgcxdef *ctx, int bpnum, objnum target, objnum self,
uint ofs, char *bpname, int toggle, char *cond,
int *did_set);
/*
* Determine if there's a breakpoint at a given code location. Fills in
* *bpnum with the breakpoint identifier and returns true if a
* breakpoint is found at the given location; returns false if there are
* no breakpoints matching the description.
*/
int dbgisbp(dbgcxdef *ctx, objnum target, objnum self, uint ofs, int *bpnum);
/*
* Determine if the given breakpoint is enabled
*/
int dbgisbpena(dbgcxdef *ctx, int bpnum);
/*
* Delete a breakpoint by breakpoint number (as returned from
* dbgbpset). Returns error number, or 0 for success.
*/
int dbgbpdel(dbgcxdef *ctx, int bpnum);
/* disable or enable a breakpoint, by breakpoint number; returns error num */
int dbgbpdis(dbgcxdef *ctx, int bpnum, int disable);
/*
* Set a new condition for the given breakpoint. Replaces any existing
* condition. If an error occurs, we'll leave the old condition as it
* was and return a non-zero error code; on success, we'll update the
* condition and return zero.
*/
int dbgbpsetcond(dbgcxdef *ctx, int bpnum, char *cond);
/* list breakpoints, using user callback to do display */
void dbgbplist(dbgcxdef *ctx,
void (*dispfn)(void *ctx, const char *str, int len),
void *dispctx);
/* enumerate breakpoints */
void dbgbpenum(dbgcxdef *ctx,
void (*cbfunc)(void *cbctx, int bpnum, const char *desc,
const char *cond, int disabled), void *cbctx);
/* call callback with lindef data for each breakpoint currently set */
void dbgbpeach(dbgcxdef *ctx,
void (*fn)(void *, int, uchar *, uint),
void *fnctx);
/*
* Get information on a specific breakpoint. Returns zero on success,
* non-zero on failure.
*/
int dbgbpgetinfo(dbgcxdef *ctx, int bpnum, char *descbuf, size_t descbuflen,
char *condbuf, size_t condbuflen);
/*
* Evaluate an expression (a text string to be parsed) at a particular
* stack context level; returns error number. Invokes the callback
* function repeatedly to display the value string, and ends the display
* with a newline. If showtype is true, we'll include a type name
* prefix, otherwise we'll simply display the value.
*/
int dbgeval(dbgcxdef *ctx, char *expr,
void (*dispfn)(void *dispctx, const char *str, int strl),
void *dispctx, int level, int showtype);
/*
* Evaluate an expression, extended version. For aggregate values
* (objects, lists), we'll invoke a callback function for each value
* contained by the aggregate value, passing the callback the name and
* relationship of the subitem. The relationship is simply the operator
* that should be used to join the parent expression and the subitem
* name to form the full subitem expression; for objects, it's ".", and
* for lists it's null (because for lists the subitem names will include
* brackets). 'speculative' is passed to dbgcompile; see the comments
* there for information on the purpose of this flag.
*/
int dbgevalext(dbgcxdef *ctx, char *expr,
void (*dispfn)(void *dispctx, const char *str, int strl),
void *dispctx, int level, int showtype, dattyp *dat,
void (*aggcb)(void *aggctx, const char *subname,
int subnamelen, const char *relationship),
void *aggctx, int speculative);
/*
* enumerate local variables at a given stack context level by calling
* the given function once for each local variable
*/
void dbgenumlcl(dbgcxdef *ctx, int level,
void (*func)(void *ctx, const char *lclnam, size_t lclnamlen),
void *cbctx);
/*
* Compile an expression in a given frame context. Returns an error
* number. Allocates a new object to contain the compiled code, and
* returns the object number in *objn; the caller is responsible for
* freeing the object when done with it.
*
* If 'speculative' is set to true, we'll prohibit the expression from
* making any assignments or calling any methods or functions. This
* mode can be used to try compiling an expression that the user could
* conceivably be interested in but has not expressly evaluated; for
* example, this can be used to implement "tooltip evaluation," where
* the debugger automatically shows a little pop-up window with the
* expression under the mouse cursor if the mouse cursor is left
* hovering over some text for a few moments. In such cases, since the
* user hasn't explicitly requested evaluation, it would be bad to make
* any changes to game state, hence the prohibition of assignments or
* calls.
*/
int dbgcompile(dbgcxdef *ctx, char *expr, dbgfdef *fr, objnum *objn,
int speculative);
/* display a stack traceback through a user callback */
void dbgstktr(dbgcxdef *ctx,
void (*dispfn)(void *dispctx, const char *str, int strl),
void *dispctx, int level, int toponly, int include_markers);
/* format a display of where execution is stopped into a buffer */
void dbgwhere(dbgcxdef *ctx, char *buf);
/* set a watch expression; returns error or 0 for success */
int dbgwxset(dbgcxdef *ctx, char *expr, int *wxnum, int level);
/* delete a watch expression */
int dbgwxdel(dbgcxdef *ctx, int wxnum);
/* update all watch expressions */
void dbgwxupd(dbgcxdef *ctx,
void (*dispfn)(void *dispctx, const char *txt, int len),
void *dispctx);
/* switch to a new active lindef */
void dbgswitch(struct lindef **linp, struct lindef *newlin);
/* ======================================================================== */
/*
* User Interface Routines. The routines are called by the debugger
* to perform user interaction.
*/
/*
* Debugger user interface initialization, phase one. TADS calls this
* routine during startup, before reading the .GAM file, to let the user
* interface perform any initialization it requires before the .GAM file
* is loaded.
*/
void dbguini(dbgcxdef *ctx, const char *game_filename);
/*
* Debugger user interface initialization, phase two. TADS calls this
* routine during startup, after read the .GAM file. The debugger user
* interface code can perform any required initialization that depends
* on the .GAM file having been read.
*/
void dbguini2(dbgcxdef *ctx);
/*
* Determine if the debugger can resume from a run-time error. This
* reflects the capabilities of the user interface of the debugger. In
* particular, if the UI provides a way to change the instruction
* pointer, then the debugger can resume from an error, since the user
* can always move past the run-time error and continue execution. If
* the UI doesn't let the user change the instruction pointer, resuming
* from an error won't work, since the program will keep hitting the
* same error and re-entering the debugger. If this returns false, the
* run-time will trap to the debugger on an error, but will simply abort
* the current command when the debugger returns. If this returns true,
* the run-time will trap to the debugger on an error with the
* instruction pointer set back to the start of the line containing the
* error, and will thus re-try the same line of code when the debugger
* returns, unless the debugger explicitly moves the instruction pointer
* before returning.
*/
int dbgu_err_resume(dbgcxdef *ctx);
/*
* Find a source file. origname is the name of the source file as it
* appears in the game's debugging information; this routine should
* figure out where the file actually is, and put the fully-qualified
* path to the file in fullname. The debugger calls this after it
* exhausts all of its other methods of finding a source file (such as
* searching the include path).
*
* Return true if the source file should be considered valid, false if
* not. Most implementations will simply return true if the file was
* found, false if not; however, this approach will cause the debugger
* to terminate with an error at start-up if the user hasn't set up the
* debugger's include path correctly before running the debugger. Some
* implementations, in particular GUI implementations, may wish to wait
* to find a file until the file is actually needed, rather than pester
* the user with file search dialogs repeatedly at start-up.
*
* must_find_file specifies how to respond if we can't find the file.
* If must_find_file is true, we should always return false if we can't
* find the file. If must_find_file is false, however, we can
* optionally return true even if we can't find the file. Doing so
* indicates that the debugger UI will defer locating the file until it
* is actually needed.
*
* If this routine returns true without actually finding the file, it
* should set fullname[0] to '\0' to indicate that fullname doesn't
* contain a valid filename.
*/
int dbgu_find_src(const char *origname, int origlen,
char *fullname, size_t full_len, int must_find_file);
/*
* Debugger user interface main command loop. If err is non-zero, the
* debugger was entered because a run-time error occurred; otherwise, if
* bphit is non-zero, it's the number of the breakpoint that was
* encountered; otherwise, the debugger was entered through a
* single-step of some kind. exec_ofs is the byte offset within the
* target object of the next instruction to be executed. This can be
* changed upon return, in which case execution will continue from the
* new offset, but the offset must be within the same method of the same
* object (or within the same function) as it was upon entry.
*/
void dbgucmd(dbgcxdef *ctx, int bphit, int err, unsigned int *exec_ofs);
/*
* Debugger UI - quitting game. The runtime calls this routine just
* before the play loop is about to terminate after the game code has
* called the "quit" built-in function. If the debugger wants, it can
* take control here (just as with dbgucmd()) for as long as it wants.
* If the debugger wants to restart the game, it should call bifrst().
* If this routine returns without signalling a RUN_RESTART error, TADS
* will terminate. If a RUN_RESTART error is signalled, TADS will
* resume the play loop.
*/
void dbguquitting(dbgcxdef *ctx);
/*
* debugger user interface termination - this routine is called when the
* debugger is about to terminate, so that the user interface can close
* itself down (close windows, release memory, etc)
*/
void dbguterm(dbgcxdef *ctx);
/*
* Debugger user interface: display an error. This is called mainly so
* that the debugger can display an error using special output
* formatting if the error occurs while debugging.
*/
void dbguerr(dbgcxdef *ctx, int errnum, char *msg);
/* turn hidden output tracing on/off */
void trchid(void);
void trcsho(void);
/* ======================================================================== */
/*
* optional debugger macros - these compile to nothing when compiling a
* version for use without the debugger
*/
#ifdef DBG_OFF
# define dbgenter(ctx, bp, self, target, prop, binum, argc)
# define dbgleave(ctx, exittype)
# define dbgdump(ctx)
# define dbgrst(ctx) ((void)0)
# define dbgframe(ctx, frofs, linofs)
# define dbgssi(ctx, ofs, instr, err, p)
#else /* DBG_OFF */
# define dbgenter(ctx, bp, self, target, prop, binum, argc) \
dbgent(ctx, bp, self, target, prop, binum, argc)
# define dbgleave(ctx, exittype) dbglv(ctx, exittype)
# define dbgdump(ctx) dbgds(ctx)
# define dbgrst(ctx) ((ctx)->dbgcxfcn = (ctx)->dbgcxdep = 0)
# define dbgframe(ctx, frofs, linofs) \
(((ctx)->dbgcxfrm[(ctx)->dbgcxfcn - 1].dbgffr = (frofs)), \
((ctx)->dbgcxfrm[(ctx)->dbgcxfcn - 1].dbgflin = (linofs)))
# define dbgssi(ctx, ofs, instr, err, p) dbgss(ctx, ofs, instr, err, p)
#endif /* DBG_OFF */
/* ======================================================================== */
/* private internal routines */
void dbgent(dbgcxdef *ctx, struct runsdef *bp, objnum self, objnum target,
prpnum prop, int binum, int argc);
void dbglv(dbgcxdef *ctx, int exittype);
void dbgds(dbgcxdef *ctx);
void dbgss(dbgcxdef *ctx, uint ofs, int instr, int err, uchar *noreg *p);
void dbgpval(struct dbgcxdef *ctx, struct runsdef *val,
void (*dispfn)(void *, const char *, int),
void *dispctx, int showtype);
int dbgtabsea(struct toktdef *tab, char *name, int namel, int hash,
struct toksdef *ret);
#ifdef __cplusplus
}
#endif
#endif /* DBG_INCLUDED */
|