4272 lines
111 KiB
C
4272 lines
111 KiB
C
static char sccsid[] = "@(#)40 1.59.3.68 src/bos/usr/ccs/lib/libdbx/eval.c, libdbx, bos41J, 9510A_all 2/20/95 12:39:59";
|
|
/*
|
|
* COMPONENT_NAME: CMDDBX
|
|
*
|
|
* FUNCTIONS: assign
|
|
* binary_op
|
|
* boolean_operation
|
|
* canpush
|
|
* changepc
|
|
* chkenable
|
|
* chksp
|
|
* cond
|
|
* dereference
|
|
* do_operation
|
|
* do_unary_operation
|
|
* eval
|
|
* evalcmdlist
|
|
* func
|
|
* getcase
|
|
* gripe
|
|
* ispic
|
|
* isredirected
|
|
* isspecialpic
|
|
* list
|
|
* listi
|
|
* lowercase
|
|
* lval
|
|
* popintarg
|
|
* poplonglong
|
|
* poplonglongarg
|
|
* poprealarg
|
|
* popsmall
|
|
* prflregs
|
|
* prhexflt
|
|
* printpc
|
|
* pushregvalue
|
|
* pushsmall
|
|
* registers
|
|
* rpush
|
|
* screen
|
|
* set
|
|
* setout
|
|
* stop
|
|
* stop1
|
|
* stopinst
|
|
* stopvar
|
|
* topeval
|
|
* touch_sym
|
|
* trace
|
|
* trace1
|
|
* traceall
|
|
* traceat
|
|
* tracedata
|
|
* traceinst
|
|
* traceproc
|
|
* unary_op
|
|
* unary_operation
|
|
* unsetout
|
|
* uppercase
|
|
* watch
|
|
*
|
|
* ORIGINS: 26,27, 83
|
|
*
|
|
*
|
|
* (C) COPYRIGHT International Business Machines Corp. 1988, 1994
|
|
* All Rights Reserved
|
|
* Licensed Materials - Property of IBM
|
|
* US Government Users Restricted Rights - Use, duplication or
|
|
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1982 Regents of the University of California
|
|
*/
|
|
/*
|
|
* LEVEL 1, 5 Years Bull Confidential Information
|
|
*/
|
|
|
|
#ifdef KDBXRT
|
|
#include "rtnls.h" /* MUST BE FIRST */
|
|
#endif
|
|
/* include file for message texts */
|
|
#include "dbx_msg.h"
|
|
nl_catd scmc_catd; /* Cat descriptor for scmc conversion */
|
|
|
|
/*
|
|
* Tree evaluation.
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include "envdefs.h"
|
|
#include "tree.h"
|
|
#include "operators.h"
|
|
#include "debug.h"
|
|
#include "eval.h"
|
|
#include "events.h"
|
|
#include "symbols.h"
|
|
#include "scanner.h"
|
|
#include "source.h"
|
|
#include "object.h"
|
|
#include "mappings.h"
|
|
#include "process.h"
|
|
#include "runtime.h"
|
|
#include "machine.h"
|
|
#include "main.h"
|
|
#include "frame.h"
|
|
#include "execute.h"
|
|
#include "resolve.h"
|
|
#include "cplusplus.h"
|
|
#include "cma_thread.h"
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
#include "k_thread.h"
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <setjmp.h>
|
|
#include <sys/mode.h>
|
|
#include "ops.h"
|
|
|
|
#ifdef KDBX
|
|
public Boolean new_command = true; /* if command = "\n" => reset to false */
|
|
public int is_diss; /* set to true while disassembling */
|
|
#endif /* KDBX */
|
|
|
|
public Stack stack[STACKSIZE];
|
|
public Stack *sp = &stack[0];
|
|
public Boolean useInstLoc = false; /* are we doing instruction step or next? */
|
|
public Boolean ScrUsed = false; /* Indicates oper switched screen */
|
|
public cases casemode = filedep;
|
|
public Boolean indirect = false; /* Indicates indirect addressing mode */
|
|
public Address assign_addr = NOADDR;
|
|
public Address assign_size = 0;
|
|
public Boolean ptrtomemberfunction = false;
|
|
public boolean notderefed = true;
|
|
|
|
/* filecmdcursrc is set to cursource in eval for the
|
|
file command. The purpose for this is so we
|
|
know whether or not to use the "use" path when
|
|
the user enters a fullpath name */
|
|
char *filecmdcursrc = NULL;
|
|
|
|
extern Node findvar();
|
|
extern int stepcount;
|
|
extern double pow();
|
|
extern int sourcefiles;
|
|
extern int inclfiles;
|
|
extern int *envptr; /* setjmp/longjmp data */
|
|
extern int eventId;
|
|
extern boolean isunsignedpic();
|
|
extern Boolean is_fortran_padded();
|
|
extern int lazy;
|
|
extern int initializestring;
|
|
|
|
private traceall();
|
|
private traceinst();
|
|
private traceat();
|
|
private traceproc();
|
|
private tracedata();
|
|
private stopinst();
|
|
private stopvar();
|
|
private set();
|
|
private list();
|
|
private listi();
|
|
private func();
|
|
int ScrPid; /* the pid of the process
|
|
which open a virtual terminal for screen command */
|
|
extern int newdebuggee;
|
|
|
|
/*
|
|
* Variables for subarrays
|
|
*/
|
|
struct subdim {
|
|
long ub;
|
|
long lb;
|
|
struct subdim *next, *back;
|
|
};
|
|
extern struct subdim *subdim_head;
|
|
extern struct subdim *subdim_tail;
|
|
boolean subarray; /* eval'ing a subarray */
|
|
extern Address array_addr;
|
|
extern Symbol array_sym;
|
|
extern boolean specificptrtomember;
|
|
extern Symbol Subar_save_sym;
|
|
extern Address Subar_offset;
|
|
extern Ttyinfo ttyinfo;
|
|
|
|
#define isfunc(s) ((s->class == FUNC) || (s->class == CSECTFUNC))
|
|
|
|
#define chksp() \
|
|
{ \
|
|
if (sp < &stack[0]) { \
|
|
panic( catgets(scmc_catd, MS_eval, MSG_77, "stack underflow")); \
|
|
} \
|
|
}
|
|
|
|
#define poprealarg(n, fr, qr) { \
|
|
Symbol nt; \
|
|
eval(p->value.arg[n]); \
|
|
nt = p->value.arg[n]->nodetype; /* Save, fixCobol*Arg may change it */ \
|
|
fixCobolFloatArg(p->value.arg[n]); \
|
|
if (size(p->value.arg[n]->nodetype) == sizeof(float)) { \
|
|
fr = pop(float); \
|
|
} else if (size(p->value.arg[n]->nodetype) == sizeof(double)) { \
|
|
fr = pop(double); \
|
|
} else { \
|
|
qr.val[1] = pop(double); \
|
|
qr.val[0] = pop(double); \
|
|
} \
|
|
p->value.arg[n]->nodetype = nt; /* Restore it for later evaluation */ \
|
|
}
|
|
#define popintarg(n, r) { \
|
|
Symbol nt; \
|
|
eval(p->value.arg[n]); \
|
|
nt = p->value.arg[n]->nodetype; /* Save, fixCobol*Arg may change it */ \
|
|
fixCobolIntArg(p->value.arg[n]); \
|
|
r = popsmall(p->value.arg[n]->nodetype); \
|
|
p->value.arg[n]->nodetype = nt; /* Restore it for later evaluation */ \
|
|
}
|
|
#define poplonglongarg(n, r) { \
|
|
Symbol nt; \
|
|
eval(p->value.arg[n]); \
|
|
nt = p->value.arg[n]->nodetype; /* Save, fixCobol*Arg may change it */ \
|
|
fixCobolIntArg(p->value.arg[n]); \
|
|
r = poplonglong(p->value.arg[n]->nodetype); \
|
|
p->value.arg[n]->nodetype = nt; /* Restore it for later evaluation */ \
|
|
}
|
|
|
|
#define Boolrep char /* underlying representation type for booleans */
|
|
|
|
/* this macro is used for the '-' and '~' unary
|
|
operators. It should be used if there is
|
|
ever a new unary operator that returns the
|
|
same type as its operand */
|
|
#define do_unary_operation(op) \
|
|
switch(operand_type) \
|
|
{ \
|
|
case ISLONG: \
|
|
push(long, op r0); \
|
|
break; \
|
|
case ISUNSIGNED: \
|
|
push(unsigned long, op ur0); \
|
|
break; \
|
|
case ISLONGLONG: \
|
|
push(LongLong, op llr0); \
|
|
break; \
|
|
case ISUNSIGNEDLONGLONG: \
|
|
push(uLongLong, op ullr0); \
|
|
break; \
|
|
}
|
|
|
|
/* this macro is used for the (double) and '!'
|
|
unary operators. It should be used for
|
|
any unary operator that will return a
|
|
type independent of the operand type */
|
|
#define unary_operation(result_type, op) \
|
|
switch(operand_type) \
|
|
{ \
|
|
case ISLONG: \
|
|
push(result_type, op r0); \
|
|
break; \
|
|
case ISUNSIGNED: \
|
|
push(result_type, op ur0); \
|
|
break; \
|
|
case ISLONGLONG: \
|
|
push (result_type, op llr0); \
|
|
break; \
|
|
case ISUNSIGNEDLONGLONG: \
|
|
push (result_type, op ullr0); \
|
|
break; \
|
|
}
|
|
|
|
/* this macro should be used for any binary
|
|
operation that returns the same type
|
|
as its operands */
|
|
#define do_operation(op) \
|
|
switch(operand_type) \
|
|
{ \
|
|
case ISLONG: \
|
|
push(long, r0 op r1); \
|
|
break; \
|
|
case ISUNSIGNED: \
|
|
push(unsigned long, ur0 op ur1); \
|
|
break; \
|
|
case ISLONGLONG: \
|
|
push(LongLong, llr0 op llr1); \
|
|
break; \
|
|
case ISUNSIGNEDLONGLONG: \
|
|
push(uLongLong, ullr0 op ullr1); \
|
|
break; \
|
|
}
|
|
|
|
/* this macro should be used for any binary
|
|
operation that returns a Boolrep */
|
|
#define boolean_operation(op) \
|
|
switch(operand_type) \
|
|
{ \
|
|
case ISLONG: \
|
|
push(Boolrep, r0 op r1); \
|
|
break; \
|
|
case ISUNSIGNED: \
|
|
push(Boolrep, ur0 op ur1); \
|
|
break; \
|
|
case ISLONGLONG: \
|
|
push (Boolrep, llr0 op llr1); \
|
|
break; \
|
|
case ISUNSIGNEDLONGLONG: \
|
|
push (Boolrep, ullr0 op ullr1); \
|
|
break; \
|
|
}
|
|
|
|
#define ispic(t) (((t)->class == PIC) || ((t)->class == RPIC))
|
|
#define isspecialpic(t) ((((t)->class == PIC) || ((t)->class == RPIC)) &&\
|
|
(((t)->symvalue.usage.storetype >= 'a') &&\
|
|
((t)->symvalue.usage.storetype <= 'q')))
|
|
|
|
|
|
/* Read local debug info for file of variable */
|
|
public touch_sym(s)
|
|
Symbol s;
|
|
{
|
|
Symbol file_sym;
|
|
|
|
if (s->class != MODULE) {
|
|
for (file_sym = s;
|
|
(file_sym != nil) && (file_sym->class != MODULE);
|
|
file_sym = file_sym->block);
|
|
if (file_sym == nil)
|
|
return;
|
|
} else {
|
|
file_sym = s;
|
|
}
|
|
if (file_sym->symvalue.funcv.untouched)
|
|
touch_file(file_sym);
|
|
}
|
|
|
|
|
|
/*
|
|
* Command-level evaluation.
|
|
*/
|
|
|
|
public Node topnode;
|
|
|
|
public topeval(p)
|
|
Node p;
|
|
{
|
|
if (traceeval)
|
|
{
|
|
(*rpt_error)(stderr, "topeval(");
|
|
prtree(rpt_error, stderr, p);
|
|
(*rpt_error)(stderr, ")\n");
|
|
}
|
|
eval(topnode = traverse(p, 0));
|
|
|
|
cpp_emptyVirtualList();
|
|
if( topnode != p ) {
|
|
tfree( p );
|
|
}
|
|
tfree( topnode );
|
|
}
|
|
|
|
/*
|
|
* NAME: eval
|
|
*
|
|
* FUNCTION: Evaluate a parse tree leaving the value on the
|
|
* top of the stack.
|
|
*
|
|
* PARAMETERS:
|
|
* p - input Node
|
|
*
|
|
* RECOVERY OPERATION: NONE NEEDED
|
|
*
|
|
* DATA STRUCTURES: NONE
|
|
*
|
|
* RETURNS: nothing
|
|
*/
|
|
|
|
#define SETLEN 32
|
|
|
|
public eval (p)
|
|
register Node p;
|
|
{
|
|
int i;
|
|
char *rr;
|
|
long r0, r1;
|
|
Address addr = 0;
|
|
long n;
|
|
unsigned len = 0;
|
|
Symbol s;
|
|
Node n1, n2;
|
|
boolean b;
|
|
File file;
|
|
String str;
|
|
int TEMPCNT;
|
|
int errcond;
|
|
int pid;
|
|
ExecStruct **dpi_info;
|
|
struct TraceStruct trdata;
|
|
Boolean isFPTEE = false;
|
|
long *athisptr, *newthis;
|
|
Symbol theclass, theptrmem, ptrtomemclasstype;
|
|
|
|
#ifdef KDBX
|
|
int nbp;
|
|
#define MAX_XCALL_ARGS 4
|
|
long xcall_args[MAX_XCALL_ARGS];
|
|
extern do_switch();
|
|
extern do_cpu();
|
|
extern use_local_breaks; /* When set, the breakpoints set in the */
|
|
/* kernel debugger are local, to ensure */
|
|
/* a right behavior of step and next */
|
|
|
|
use_local_breaks = 0;
|
|
is_diss = 0;
|
|
#endif /* KDBX */
|
|
|
|
checkref(p);
|
|
if (traceeval) {
|
|
(*rpt_error)(stderr, "begin eval %s\n", opname(p->op));
|
|
}
|
|
|
|
switch (degree(p->op)) {
|
|
case BINARY:
|
|
binary_op(p);
|
|
break;
|
|
|
|
case UNARY:
|
|
unary_op(p);
|
|
break;
|
|
|
|
default:
|
|
/* handle everything that is not unary or binary */
|
|
switch (p->op) {
|
|
case O_SYM:
|
|
s = p->value.sym;
|
|
|
|
if (lazy) /* If in lazy reading mode, */
|
|
touch_sym(s); /* read local debug info automatically */
|
|
if (s == retaddrsym) {
|
|
push(long, return_addr());
|
|
} else if (isvariable(s)) {
|
|
if (s != program && s->storage != EXT &&
|
|
!isactive(container(s))) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_82,
|
|
"\"%s\" is not active"), symname(s));
|
|
}
|
|
|
|
if (is_f90_sym(s))
|
|
s = p->value.sym = convert_f90_sym(s, NULL);
|
|
|
|
/* Process fortran "Cray" pointee variable */
|
|
/* Actual address is stored in its pointer (chain) */
|
|
if (s->class == FPTEE) {
|
|
s = s->chain;
|
|
isFPTEE = true;
|
|
}
|
|
addr = address(s, nil);
|
|
if (isvarparam(s)) {
|
|
if (s->storage == INREG || preg(s, nil) != -1) {
|
|
pushregvalue(s, addr, nil, sizeof(Address));
|
|
} else {
|
|
rpush(addr, sizeof(Address));
|
|
}
|
|
} else {
|
|
push(Address, addr);
|
|
}
|
|
/* If we are dealing with pascal file type, all we have */
|
|
/* is the address of the pascal file type descriptor */
|
|
/* block. We need to look into the structure and get the */
|
|
/* real address of the file buffer. At this point, this */
|
|
/* address is fixed at 96 bytes off the top for XLP. */
|
|
if (rtype(s->type)->class == FILET and
|
|
s->language == pascalLang)
|
|
{
|
|
addr = pop(Address);
|
|
addr = addr + 96;
|
|
push(Address, addr);
|
|
}
|
|
else {
|
|
if (s->param && rtype(s)->class == CLASS &&
|
|
!(rtype(s)->symvalue.class.passedByValue))
|
|
{
|
|
dereference();
|
|
notderefed = true;
|
|
}
|
|
}
|
|
if (isFPTEE) {
|
|
addr = pop(Address);
|
|
rpush(addr, sizeof(Address));
|
|
isFPTEE = false;
|
|
}
|
|
} else if (s->class == COMMON) {
|
|
addr = s->symvalue.common.com_addr;
|
|
push(Address, addr);
|
|
} else if (isblock(s) || (s->class == LABEL &&
|
|
s->language == pascalLang)) {
|
|
push(Symbol, s);
|
|
} else if (isconst(s)) {
|
|
eval(constval(s));
|
|
} else {
|
|
error( catgets(scmc_catd, MS_eval, MSG_83,
|
|
"cannot evaluate a %s"), classname(s));
|
|
}
|
|
break;
|
|
|
|
case O_LCON:
|
|
case O_CCON:
|
|
r0 = p->value.lcon;
|
|
pushsmall(size(p->nodetype), r0);
|
|
break;
|
|
|
|
case O_ULCON:
|
|
push(unsigned long, p->value.lcon);
|
|
break;
|
|
|
|
case O_LLCON:
|
|
push(LongLong, p->value.llcon);
|
|
break;
|
|
|
|
case O_ULLCON:
|
|
push(uLongLong, p->value.llcon);
|
|
break;
|
|
|
|
case O_FCON:
|
|
push(double, p->value.fcon);
|
|
break;
|
|
|
|
case O_QCON:
|
|
push(double, p->value.qcon.val[0]);
|
|
push(double, p->value.qcon.val[1]);
|
|
break;
|
|
|
|
case O_KCON:
|
|
push(double, p->value.kcon.real);
|
|
push(double, p->value.kcon.imag);
|
|
break;
|
|
|
|
case O_QKCON:
|
|
push(double, p->value.qkcon.real.val[0]);
|
|
push(double, p->value.qkcon.real.val[1]);
|
|
push(double, p->value.qkcon.imag.val[0]);
|
|
push(double, p->value.qkcon.imag.val[1]);
|
|
break;
|
|
|
|
case O_SETCON:
|
|
rr = (char *) p->value.lcon;
|
|
if (p->nodetype->type->class == RANGE)
|
|
{
|
|
len = SETLEN;
|
|
}
|
|
else
|
|
if (p->nodetype->type->class == SCAL)
|
|
{
|
|
len = p->nodetype->type->symvalue.iconval;
|
|
len = (len + BITSPERBYTE - 1) / BITSPERBYTE;
|
|
}
|
|
for (i=0; i<len; i++)
|
|
{
|
|
pushsmall(1, *rr);
|
|
rr++;
|
|
}
|
|
break;
|
|
|
|
case O_SCON:
|
|
len = size(p->nodetype);
|
|
mov(p->value.scon, sp, len);
|
|
sp += len;
|
|
break;
|
|
|
|
case O_INDEX:
|
|
s = p->value.arg[0]->nodetype;
|
|
p->value.arg[0]->nodetype = t_addr;
|
|
|
|
if (p->value.arg[1] != nil &&
|
|
p->value.arg[1]->op == O_DOTDOT) { /* have a subarray */
|
|
Node q = p->value.arg[0];
|
|
while(q->op == O_INDEX) /* look past multi-D array stuff */
|
|
q = q->value.arg[0];
|
|
if (q->op != O_DOT && /* for structs and pointers: */
|
|
q->op != O_INDIR && /* only set subarray *after* */
|
|
q->op != O_INDIRA && /* eval of left node */
|
|
q->op != O_ADD && /* O_ADD is for ptr to array */
|
|
q->op != O_DOTSTAR)
|
|
subarray = true;
|
|
eval(p->value.arg[0]);
|
|
subarray = true;
|
|
}
|
|
else
|
|
eval(p->value.arg[0]);
|
|
|
|
p->value.arg[0]->nodetype = s;
|
|
|
|
if (!subarray)
|
|
{
|
|
n = pop(Address);
|
|
eval(p->value.arg[1]);
|
|
evalindex(s, n, popsmall(p->value.arg[1]->nodetype));
|
|
}
|
|
else
|
|
{
|
|
struct subdim *last_ptr = nil;
|
|
struct subdim *ptr = nil;
|
|
|
|
while (p->op == O_INDEX)
|
|
|
|
{
|
|
ptr = (struct subdim *)malloc(sizeof(struct subdim));
|
|
if (!subdim_head)
|
|
subdim_head = ptr;
|
|
if (last_ptr)
|
|
last_ptr->next = ptr;
|
|
/*
|
|
* Temporarily turn this off so it doesn't
|
|
* interfere with the upcoming calls to eval
|
|
*/
|
|
subarray = false;
|
|
if (p->value.arg[1]->op == O_DOTDOT)
|
|
{
|
|
Node lower = p->value.arg[1]->value.arg[0];
|
|
Node upper = p->value.arg[1]->value.arg[1];
|
|
|
|
eval(lower);
|
|
if (isconst(lower->nodetype))
|
|
ptr->lb =
|
|
(long) popsmall(constval(lower->nodetype)->nodetype);
|
|
else
|
|
ptr->lb = (long) popsmall(lower->nodetype);
|
|
eval(upper);
|
|
if (isconst(upper->nodetype))
|
|
ptr->ub =
|
|
(long) popsmall(constval(upper->nodetype)->nodetype);
|
|
else
|
|
ptr->ub = (long) popsmall(upper->nodetype);
|
|
ptr->back = last_ptr;
|
|
ptr->next = nil;
|
|
}
|
|
else
|
|
{
|
|
eval(p->value.arg[1]);
|
|
ptr->lb = ptr->ub = (long) pop(long);
|
|
ptr->back = last_ptr;
|
|
ptr->next = nil;
|
|
}
|
|
subarray = true; /* turn it back on */
|
|
p = p->value.arg[0];
|
|
last_ptr = ptr;
|
|
}
|
|
subdim_tail = last_ptr;
|
|
array_sym = p->nodetype;
|
|
array_addr = pop(Address);
|
|
push(Address, array_addr);
|
|
}
|
|
break;
|
|
|
|
case O_DOT:
|
|
s = p->value.arg[1]->value.sym;
|
|
if (p->nodetype->class == FUNC) {
|
|
push(Address, p->nodetype);
|
|
}
|
|
else {
|
|
if (s->class == FUNC) {
|
|
push(Address, s->symvalue.funcv.beginaddr);
|
|
}
|
|
else {
|
|
int Subar_of;
|
|
eval(p->value.arg[0]);
|
|
n = pop(long);
|
|
|
|
if (s->class == BASECLASS)
|
|
{
|
|
push(long, n + (s->symvalue.baseclass.offset))
|
|
Subar_of=(s->symvalue.baseclass.offset);
|
|
}
|
|
else
|
|
{
|
|
push(long, n + (s->symvalue.field.offset / 8));
|
|
Subar_of=(s->symvalue.field.offset / 8);
|
|
}
|
|
if(subarray)
|
|
Subar_offset=n+Subar_of;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case O_DOTSTAR:
|
|
theclass = p->value.arg[0]->nodetype;
|
|
theptrmem = p->value.arg[1]->nodetype;
|
|
/* get to the pointer to member symbol */
|
|
s = rtype(theptrmem);
|
|
/* evaluate the address of the specific */
|
|
/* instance of the class */
|
|
eval(p->value.arg[0]);
|
|
if (rtype(theptrmem)->class == CPPREF) {
|
|
theptrmem = rtype(theptrmem)->type;
|
|
}
|
|
/* get the address of the instace (this ptr)*/
|
|
athisptr = (long *) pop(long);
|
|
/* evaluate the member pointer portion of */
|
|
/* the expression */
|
|
eval(p->value.arg[1]);
|
|
/* we are evaluating a specific instance */
|
|
specificptrtomember = true;
|
|
/* if we are processing a pointer to member */
|
|
/* function then
|
|
/* leave function and this pointers on stack */
|
|
if (s->type->class == FFUNC) {
|
|
long function;
|
|
long ttdisp, ppdisp, ffdisp;
|
|
struct ve {
|
|
long tdisp;
|
|
long faddr;
|
|
} *ventry;
|
|
ptrtomemberfunction = true;
|
|
/* determine what type of pointer to member */
|
|
/* function we are using */
|
|
/* see C++ "mapping.document" for details on */
|
|
/* the following formats */
|
|
if (s->symvalue.ptrtomem.hasVBases) { /* mvp3 format */
|
|
ppdisp = pop(long);
|
|
ttdisp = pop(long);
|
|
rpush((char *)athisptr + ppdisp,sizeof(long));
|
|
newthis = (long *) ((char *) pop(long));
|
|
newthis = (long *) ((char *) newthis + ttdisp);
|
|
}
|
|
else {
|
|
pop(long); /* get rid of ppdisp */
|
|
if (s->symvalue.ptrtomem.hasMultiBases) {
|
|
ttdisp = pop(long);
|
|
newthis = (long *)((char *) athisptr + ttdisp);
|
|
}
|
|
else {
|
|
pop(long); /* get rid of ttdisp */
|
|
newthis = athisptr;
|
|
}
|
|
}
|
|
ffdisp = pop(long);
|
|
function = pop(long);
|
|
if (ffdisp == -1 && function == 0) {
|
|
specificptrtomember = false;
|
|
ptrtomemberfunction = false;
|
|
beginerrmsg();
|
|
(*rpt_error)(stderr, catgets(scmc_catd, MS_eval, MSG_84,
|
|
"reference through nil pointer"));
|
|
enderrmsg();
|
|
}
|
|
if (s->type->symvalue.member.attrs.func.isVirtual
|
|
!= CPPVIRTUAL) {
|
|
rpush(function,sizeof(long));
|
|
push(long,newthis);
|
|
}
|
|
else {
|
|
ventry = (struct ve*)((char *)(*newthis) + ffdisp);
|
|
rpush(ventry->faddr, sizeof(long));
|
|
rpush(ventry->tdisp, sizeof(long));
|
|
}
|
|
}
|
|
else {
|
|
long mmdisp, ppdisp;
|
|
/* otherwise we are processing a pointer to */
|
|
/* a data element -- just leave the pointer */
|
|
/* to the data on the stack */
|
|
if (!(s->symvalue.ptrtomem.hasVBases)) {
|
|
pop(long); /* get rid of ppdisp */
|
|
mmdisp = pop(long);
|
|
if (mmdisp == -1) {
|
|
specificptrtomember = false;
|
|
beginerrmsg();
|
|
(*rpt_error)(stderr, catgets(scmc_catd, MS_eval, MSG_84,
|
|
"reference through nil pointer"));
|
|
enderrmsg();
|
|
}
|
|
push(long, ((char *) athisptr + mmdisp));
|
|
}
|
|
else {
|
|
ppdisp = pop(long);
|
|
mmdisp = pop(long);
|
|
if (mmdisp == -1) {
|
|
specificptrtomember = false;
|
|
beginerrmsg();
|
|
(*rpt_error)(stderr, catgets(scmc_catd, MS_eval, MSG_84,
|
|
"reference through nil pointer"));
|
|
enderrmsg();
|
|
}
|
|
rpush((char *) athisptr + ppdisp, sizeof(long));
|
|
newthis = (long *) ((char *) pop(long));
|
|
newthis = (long *) ((char *) newthis + mmdisp);
|
|
push (long, newthis);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case O_COMMA:
|
|
eval(p->value.arg[0]);
|
|
if (p->value.arg[1] != nil) {
|
|
eval(p->value.arg[1]);
|
|
}
|
|
break;
|
|
|
|
case O_ASSIGN:
|
|
assign(p->value.arg[0], p->value.arg[1]);
|
|
#ifdef CMA_THREAD
|
|
/* If we have cma threads, we need to alert libpthreads */
|
|
/* that we might have changed something... */
|
|
if (!isfinished(process) && running_thread)
|
|
setInconsistency();
|
|
#endif /* CMA_THREAD */
|
|
break;
|
|
|
|
case O_CHFILE:
|
|
if (p->value.scon == nil) {
|
|
char *filepointer;
|
|
|
|
/* find the path of the current file */
|
|
filepointer = findsource (cursource, NULL);
|
|
|
|
if (filepointer == NULL)
|
|
filepointer = cursource;
|
|
|
|
/* strip off the beginning "./" if it exists */
|
|
if (!strncmp(filepointer, "./", 2))
|
|
filepointer += 2;
|
|
|
|
/* print out the path of the current file */
|
|
(*rpt_output)(stdout, "%s\n", filepointer);
|
|
} else {
|
|
unsigned char search_filetable = FILE_CMD;
|
|
|
|
if (lazy) {
|
|
char *x, *y;
|
|
Symbol filesym;
|
|
x = strdup(p->value.scon);
|
|
y = rindex(x, '.');
|
|
if (y) *y = '\0';
|
|
filesym = lookup(identname(x,true));
|
|
touch_sym(filesym);
|
|
}
|
|
file = opensource(p->value.scon, &search_filetable);
|
|
if (file == nil) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_90,
|
|
"cannot read \"%s\""), p->value.scon);
|
|
} else {
|
|
fclose(file);
|
|
/* if cursource did not get set in findsource */
|
|
if (search_filetable)
|
|
{
|
|
/* set it here */
|
|
setsource(p->value.scon);
|
|
filecmdcursrc = cursource;
|
|
}
|
|
}
|
|
action_mask |= LISTING;
|
|
}
|
|
break;
|
|
|
|
case O_CONT:
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
/* Switch context back to running thread if CMA thread exists */
|
|
if (OK_TO_SWITCH)
|
|
switchThread(running_thread);
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
cont(p->value.arg[0]->value.lcon);
|
|
if (!isstopped)
|
|
printnews(false);
|
|
action_mask |= EXECUTION;
|
|
action_mask &= ~CONTEXT_CHANGE;
|
|
action_mask &= ~LISTING;
|
|
action_mask &= ~ELISTING;
|
|
break;
|
|
|
|
case O_KLOAD:
|
|
#ifdef KDBX
|
|
update_load_maps();
|
|
#endif
|
|
break;
|
|
|
|
case O_LISTI:
|
|
listi(p);
|
|
break;
|
|
|
|
case O_LIST:
|
|
list(p);
|
|
break;
|
|
|
|
case O_LLDB:
|
|
#ifdef KDBX
|
|
if (p->value.arg[0] != nil) {
|
|
kdbx_lldb(p->value.arg[0]->value.scon);
|
|
} else {
|
|
kdbx_lldb(nil);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case O_FUNC:
|
|
func(p->value.arg[0]);
|
|
if (p->value.arg[0] != nil) {
|
|
action_mask |= LISTING;
|
|
action_mask |= FUNC_CHANGE;
|
|
}
|
|
break;
|
|
|
|
case O_THREAD:
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
{
|
|
thread_op th_op;
|
|
checkref(p->value.arg[0]);
|
|
assert(p->value.arg[0]->op == O_LCON);
|
|
th_op = (thread_op)(p->value.arg[0]->value.lcon);
|
|
switch (th_op) {
|
|
case th_default:
|
|
case th_info:
|
|
threads(th_op, p->value.arg[1]);
|
|
break;
|
|
case th_hold:
|
|
case th_unhold:
|
|
threads(th_op, p->value.arg[1]);
|
|
action_mask |= THREAD_CHANGE;
|
|
break;
|
|
case th_run:
|
|
case th_ready:
|
|
case th_susp:
|
|
#ifdef K_THREADS
|
|
case th_wait:
|
|
#endif /* K_THREADS */
|
|
case th_term:
|
|
if (p->value.arg[1] != nil) {
|
|
if (lib_type == KERNEL_THREAD) {
|
|
(*rpt_error)(stderr,
|
|
catgets(scmc_catd,MS_pthread,MSG_774,
|
|
"Usage: \"thread [run | wait | susp | term]\" takes no id\n"));
|
|
break;
|
|
} else {
|
|
(*rpt_error)(stderr,
|
|
catgets(scmc_catd,MS_eval,MSG_731,
|
|
"Usage: \"thread [run | ready | susp | term]\" takes no id\n"));
|
|
break;
|
|
}
|
|
}
|
|
threads(th_op, nil);
|
|
break;
|
|
case th_current:
|
|
if (p->value.arg[1]) {
|
|
/* if one argument is provided, make sure it is id */
|
|
if (p->value.arg[1]->op != O_LCON) {
|
|
(*rpt_error)(stderr,
|
|
catgets(scmc_catd, MS_eval, MSG_732,
|
|
"Usage: \"thread current\" takes zero or one id\n"));
|
|
} else {
|
|
/* if one thread id is provided, switch to it... */
|
|
threads(th_current, p->value.arg[1]);
|
|
action_mask |= THREAD_CHANGE;
|
|
}
|
|
} else {
|
|
/* else (no arg), display what is current thread. */
|
|
threads(th_current, nil);
|
|
}
|
|
break;
|
|
case th_run_next:
|
|
/* Check and make sure only one element in list */
|
|
if (p->value.arg[1] == nil ||
|
|
p->value.arg[1]->op != O_LCON) {
|
|
(*rpt_error)(stderr,
|
|
catgets(scmc_catd, MS_eval, MSG_733,
|
|
"Usage: \"thread run_next\" takes one id\n"));
|
|
break;
|
|
}
|
|
threads(th_run_next, p->value.arg[1]);
|
|
break;
|
|
default:
|
|
/* do nothing, should never reach here. */
|
|
break;
|
|
}
|
|
}
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
break;
|
|
|
|
case O_ATTRIBUTE:
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
{
|
|
attribute_op attr_op;
|
|
checkref(p->value.arg[0]);
|
|
assert(p->value.arg[0]->op == O_LCON);
|
|
attr_op = (attribute_op)(p->value.arg[0]->value.lcon);
|
|
switch (attr_op) {
|
|
case attr_default:
|
|
attribute(attr_default, p->value.arg[1]);
|
|
break;
|
|
default:
|
|
/* do nothing, should never reach here. */
|
|
break;
|
|
}
|
|
}
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
break;
|
|
|
|
case O_CONDITION:
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
{
|
|
condition_op cv_op;
|
|
checkref(p->value.arg[0]);
|
|
assert(p->value.arg[0]->op == O_LCON);
|
|
cv_op = (condition_op)(p->value.arg[0]->value.lcon);
|
|
switch (cv_op) {
|
|
case cv_default:
|
|
condition(cv_default, p->value.arg[1]);
|
|
break;
|
|
case cv_wait:
|
|
case cv_nowait:
|
|
if (p->value.arg[1] != nil) {
|
|
(*rpt_error)(stderr,
|
|
catgets(scmc_catd, MS_eval, MSG_734,
|
|
"Usage: \"condition { wait | nowait }\" takes no id\n"));
|
|
break;
|
|
}
|
|
condition(cv_op, nil);
|
|
default:
|
|
/* do nothing, should never reach here. */
|
|
break;
|
|
}
|
|
}
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
break;
|
|
|
|
case O_MUTEX:
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
{
|
|
mutex_op mu_op;
|
|
checkref(p->value.arg[0]);
|
|
assert(p->value.arg[0]->op == O_LCON);
|
|
mu_op = (mutex_op)(p->value.arg[0]->value.lcon);
|
|
switch (mu_op) {
|
|
case mu_default:
|
|
mutex(mu_default, p->value.arg[1]);
|
|
break;
|
|
case mu_wait:
|
|
case mu_nowait:
|
|
case mu_lock:
|
|
case mu_unlock:
|
|
if (p->value.arg[1] != nil) {
|
|
if (lib_type == KERNEL_THREAD) {
|
|
(*rpt_error)(stderr,
|
|
catgets(scmc_catd, MS_pthread, MSG_775,
|
|
"Usage: \"mutex { lock | unlock }\" takes no id \n"));
|
|
break;
|
|
} else {
|
|
(*rpt_error)(stderr,
|
|
catgets(scmc_catd, MS_eval, MSG_735,
|
|
"Usage: \"mutex { wait | nowait | lock | unlock }\" takes no id \n"));
|
|
break;
|
|
}
|
|
|
|
}
|
|
mutex(mu_op, nil);
|
|
default:
|
|
/* do nothing, should never reach here. */
|
|
break;
|
|
}
|
|
}
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
break;
|
|
|
|
case O_EXAMINE:
|
|
eval(p->value.examine.beginaddr);
|
|
r0 = pop(long);
|
|
if (p->value.examine.endaddr == nil) {
|
|
n = p->value.examine.count;
|
|
if (n == 0) {
|
|
printvalue(r0, p->value.examine.mode);
|
|
} else if (streq(p->value.examine.mode, "i")) {
|
|
#ifdef KDBX
|
|
is_diss = 1;
|
|
if (varIsSet("$progress") && ! new_command)
|
|
printninst(n, prtaddr);
|
|
else
|
|
#endif /* KDBX */
|
|
printninst(n, (Address) r0);
|
|
} else {
|
|
#ifdef KDBX
|
|
if (varIsSet("$progress") && ! new_command)
|
|
printndata(n, prtaddr, p->value.examine.mode);
|
|
else
|
|
#endif /* KDBX */
|
|
printndata(n, (Address) r0, p->value.examine.mode);
|
|
}
|
|
} else {
|
|
eval(p->value.examine.endaddr);
|
|
r1 = pop(long);
|
|
if (streq(p->value.examine.mode, "i")) {
|
|
#ifdef KDBX
|
|
is_diss = 1;
|
|
#endif /* KDBX */
|
|
printinst((Address)r0, (Address)r1);
|
|
} else {
|
|
printdata((Address)r0, (Address)r1, p->value.examine.mode);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case O_MOVE:
|
|
list(p);
|
|
break;
|
|
|
|
case O_PRINT:
|
|
Subar_offset=0;
|
|
Subar_save_sym=0;
|
|
for (n1 = p->value.arg[0]; n1 != nil; n1 = n1->value.arg[1]) {
|
|
eval(n1->value.arg[0]);
|
|
printval(n1->value.arg[0]->nodetype, 0);
|
|
(*rpt_output)(stdout, " " );
|
|
}
|
|
(*rpt_output)(stdout, "\n" );
|
|
break;
|
|
|
|
case O_PSYM:
|
|
if (p->value.arg[0]->op == O_SYM) {
|
|
psym(p->value.arg[0]->value.sym);
|
|
} else {
|
|
psym(p->value.arg[0]->nodetype);
|
|
}
|
|
break;
|
|
|
|
case O_SYMTYPE:
|
|
if( (p->value.arg[0])->op == O_CPPREF ) {
|
|
symboltype( (p->value.arg[0])->value.arg[0]->nodetype );
|
|
} else {
|
|
symboltype(p->value.arg[0]->nodetype);
|
|
}
|
|
break;
|
|
|
|
case O_QLINE:
|
|
eval(p->value.arg[1]);
|
|
break;
|
|
|
|
case O_SIZEOF:
|
|
if (p->value.arg[0]->op == O_SYM) {
|
|
push(long,size(p->value.arg[0]->value.sym));
|
|
} else {
|
|
push(long,size(p->value.arg[0]->nodetype));
|
|
}
|
|
break;
|
|
|
|
#define STOPPED 0177
|
|
|
|
case O_STEP:
|
|
#ifdef KDBX
|
|
if (p->value.step.source && (nlines_total == 0)) {
|
|
(*rpt_output)(stdout,
|
|
"Stepping at source level impossible (no source file compiled with -g)\n");
|
|
break;
|
|
}
|
|
use_local_breaks = 1;
|
|
#endif /* KDBX */
|
|
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
/* Switch context back to running thread if CMA thread exists */
|
|
if (OK_TO_SWITCH)
|
|
switchThread(running_thread);
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
b = inst_tracing;
|
|
inst_tracing = (Boolean) (not p->value.step.source);
|
|
for (; stepcount > 0; stepcount--) {
|
|
if (p->value.step.skipcalls) {
|
|
next();
|
|
} else {
|
|
stepc();
|
|
}
|
|
if (process->status != STOPPED)
|
|
break;
|
|
}
|
|
stepcount = 1;
|
|
inst_tracing = b;
|
|
useInstLoc = (Boolean) (not p->value.step.source);
|
|
action_mask |= EXECUTION;
|
|
action_mask &= ~CONTEXT_CHANGE;
|
|
action_mask &= ~LISTING;
|
|
action_mask &= ~ELISTING;
|
|
printnews(false);
|
|
break;
|
|
|
|
case O_WHATIS:
|
|
{
|
|
Node n;
|
|
Symbol s;
|
|
n = p->value.arg[0];
|
|
/*
|
|
* Clear the second operand, since it is not a freeable entity
|
|
*/
|
|
p->value.arg[1] = NULL;
|
|
if (n->op == O_RVAL)
|
|
n = n->value.arg[0];
|
|
if (n->op == O_CPPREF)
|
|
n = n->value.arg[0];
|
|
s = n->nodetype;
|
|
if (s->class == CPPSYMLIST)
|
|
{
|
|
cppSymList list = s->symvalue.sList;
|
|
for (; list != nil; list = list->next)
|
|
printdecl(list->sym);
|
|
}
|
|
else
|
|
{
|
|
if (n->op == O_SYM)
|
|
{
|
|
if (lazy)
|
|
touch_sym(n->value.sym);
|
|
printdecl(n->value.sym);
|
|
}
|
|
else
|
|
printdecl(s);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case O_WHERE:
|
|
wherecmd();
|
|
break;
|
|
|
|
case O_WHEREIS:
|
|
assert(p->value.arg[0]->op == O_NAME);
|
|
printwhereis( rpt_output, stdout, p->value.arg[0]->value.name);
|
|
break;
|
|
|
|
case O_WHICH:
|
|
{
|
|
Node n;
|
|
Symbol s;
|
|
n = p->value.arg[0];
|
|
if (n->op == O_CPPREF)
|
|
n = n->value.arg[0];
|
|
s = n->nodetype;
|
|
if (s->class == CPPSYMLIST)
|
|
{
|
|
cppSymList list = s->symvalue.sList;
|
|
|
|
/* find the funcList node */
|
|
Node p = n, q = nil;
|
|
int argument = 0;
|
|
|
|
if (p->op == O_CPPREF)
|
|
{
|
|
q = p;
|
|
p = p->value.arg[0];
|
|
}
|
|
while (p->op == O_RVAL)
|
|
{
|
|
q = p;
|
|
p = p->value.arg[0];
|
|
}
|
|
if (p->op == O_DOT)
|
|
{
|
|
argument = 1;
|
|
q = p;
|
|
}
|
|
|
|
for (; list != nil; list = list->next)
|
|
{
|
|
if (q != nil)
|
|
q->value.arg[argument] = build(O_SYM, list->sym);
|
|
else
|
|
n = build(O_SYM, list->sym);
|
|
prtree(rpt_output, stdout, n);
|
|
(*rpt_output)(stdout, "\n" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (n->op == O_SYM)
|
|
printwhich(rpt_output, stdout, n->value.sym, true);
|
|
else
|
|
{
|
|
if (s->language == cppLang)
|
|
prtree(rpt_output, stdout, n);
|
|
else
|
|
printwhich(rpt_output, stdout, s, true);
|
|
}
|
|
(*rpt_output)(stdout, "\n" );
|
|
}
|
|
break;
|
|
}
|
|
|
|
case O_ALIAS:
|
|
n1 = p->value.arg[0];
|
|
n2 = p->value.arg[1];
|
|
/*
|
|
* Since the operands will be used for the keywords table, clear
|
|
* them so we don't try to free them as part of the Node
|
|
*/
|
|
p->value.arg[0] = p->value.arg[1] = NULL;
|
|
if (n2 == nil) {
|
|
if (n1 == nil) {
|
|
alias(nil, nil, nil);
|
|
} else {
|
|
alias(n1->value.name, nil, nil);
|
|
}
|
|
} else if (n2->op == O_NAME) {
|
|
str = ident(n2->value.name);
|
|
alias(n1->value.name, nil, strdup(str));
|
|
} else {
|
|
if (n1->op == O_COMMA) {
|
|
alias(
|
|
n1->value.arg[0]->value.name,
|
|
(List) n1->value.arg[1]->value.arg[0],
|
|
n2->value.scon
|
|
);
|
|
if (tracetree) {
|
|
(*rpt_error)(stderr, "name list = ");
|
|
prtree( rpt_error, stderr, n1->value.arg[1] );
|
|
(*rpt_error)(stderr, "\n");
|
|
}
|
|
} else {
|
|
alias(n1->value.name, nil, n2->value.scon);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case O_UNALIAS:
|
|
unalias(p->value.arg[0]->value.name);
|
|
break;
|
|
|
|
case O_CALLPROC:
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
/* Switch context back to running thread if CMA thread exists */
|
|
if (OK_TO_SWITCH)
|
|
switchThread(running_thread);
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
callproc(p, false);
|
|
break;
|
|
|
|
case O_CALL:
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
/* Switch context back to running thread if CMA thread exists */
|
|
if (OK_TO_SWITCH)
|
|
switchThread(running_thread);
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
callproc(p, true);
|
|
break;
|
|
|
|
case O_CASE:
|
|
if (p->value.lcon == -1)
|
|
switch(casemode) {
|
|
case mixed:
|
|
(*rpt_output)(stdout, catgets(scmc_catd,
|
|
MS_eval, MSG_95, "Symbols are not folded (mixed).\n"));
|
|
break;
|
|
case lower:
|
|
(*rpt_output)(stdout, catgets(scmc_catd, MS_eval,
|
|
MSG_96, "Symbols are folded to lower case.\n"));
|
|
break;
|
|
case upper:
|
|
(*rpt_output)(stdout, catgets(scmc_catd, MS_eval,
|
|
MSG_97, "Symbols are folded to upper case.\n"));
|
|
break;
|
|
case filedep:
|
|
default:
|
|
(*rpt_output)(stdout, catgets(scmc_catd, MS_eval,
|
|
MSG_98,
|
|
"Symbols are folded based upon current language.\n"));
|
|
switch(symcase) {
|
|
case mixed:
|
|
(*rpt_output)(stdout, catgets(scmc_catd,
|
|
MS_eval, MSG_95,
|
|
"Symbols are not folded (mixed).\n"));
|
|
break;
|
|
case lower:
|
|
(*rpt_output)(stdout, catgets(scmc_catd,
|
|
MS_eval, MSG_96,
|
|
"Symbols are folded to lower case.\n"));
|
|
break;
|
|
case upper:
|
|
(*rpt_output)(stdout, catgets(scmc_catd,
|
|
MS_eval, MSG_97,
|
|
"Symbols are folded to upper case.\n"));
|
|
break;
|
|
default:
|
|
(*rpt_output)(stdout, catgets(scmc_catd,
|
|
MS_eval, MSG_95,
|
|
"Symbols are not folded (mixed).\n"));
|
|
}
|
|
break;
|
|
}
|
|
else {
|
|
symcase = casemode = (cases) p->value.lcon;
|
|
if (symcase == filedep)
|
|
srcfilename(pc);
|
|
action_mask |= CONFIGURATION;
|
|
}
|
|
break;
|
|
|
|
case O_CATCH:
|
|
if (p->value.arg[0]->value.lcon == 0) {
|
|
printsigscaught(process);
|
|
} else {
|
|
psigtrace(process, p->value.arg[0]->value.lcon, true);
|
|
action_mask |= CONFIGURATION;
|
|
}
|
|
break;
|
|
|
|
case O_CLEAR:
|
|
if (clearbps(p))
|
|
action_mask |= BREAKPOINT;
|
|
break;
|
|
|
|
case O_CLEARI:
|
|
if (clearbps_i(p))
|
|
action_mask |= BREAKPOINT;
|
|
break;
|
|
|
|
case O_EDIT:
|
|
edit(p->value.scon);
|
|
break;
|
|
|
|
case O_DEBUG:
|
|
debug(p);
|
|
break;
|
|
|
|
case O_DUMP:
|
|
if (p->value.arg[0] == nil)
|
|
dumpall();
|
|
else
|
|
{
|
|
s = p->value.arg[0]->nodetype;
|
|
dump(s == curfunc ? nil : s);
|
|
}
|
|
break;
|
|
|
|
case O_GOTO:
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
/* Switch context back to running thread if CMA thread exists */
|
|
if (OK_TO_SWITCH)
|
|
switchThread(running_thread);
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
changepc(p);
|
|
break;
|
|
|
|
case O_HELP:
|
|
help(p);
|
|
break;
|
|
|
|
case O_IGNORE:
|
|
if (p->value.arg[0]->value.lcon == 0) {
|
|
printsigsignored(process);
|
|
} else {
|
|
psigtrace(process, p->value.arg[0]->value.lcon, false);
|
|
action_mask |= CONFIGURATION;
|
|
}
|
|
break;
|
|
|
|
case O_MAP:
|
|
printloaderinfo();
|
|
break;
|
|
|
|
case O_PROMPT:
|
|
if (p->value.arg[0] != nil) {
|
|
free(prompt);
|
|
prompt = p->value.arg[0]->value.scon;
|
|
|
|
/*
|
|
* Since the scon value will get freed if the prompt is changed
|
|
* again, clear the operand so we don't try to free it as part
|
|
* of the Node
|
|
*/
|
|
p->value.arg[0]->value.scon = NULL;
|
|
}
|
|
else
|
|
(*rpt_output)(stdout, "%s\n", prompt);
|
|
break;
|
|
|
|
case O_RETURN:
|
|
#if defined (CMA_THREAD) || defined (K_THREADS)
|
|
/* Switch context back to running thread if CMA thread exists */
|
|
if (OK_TO_SWITCH)
|
|
switchThread(running_thread);
|
|
#endif /* CMA_THREAD || K_THREADS */
|
|
rtnfunc(p->value.arg[0] == nil ? nil : p->value.arg[0]->nodetype);
|
|
break;
|
|
|
|
case O_REGS:
|
|
registers();
|
|
break;
|
|
|
|
case O_RUN:
|
|
/* dead code ?? Does run subcommand always get executed in commands.y? */
|
|
#ifdef KDBX
|
|
(*rpt_error)(stderr, "run or rerun not allowed in kdbx\n");
|
|
#else /* KDBX */
|
|
if (norun) {
|
|
(*rpt_error)(stderr, catgets(scmc_catd, MS_eval, MSG_106,
|
|
"run or rerun allowed only on initial process"));
|
|
}
|
|
else {
|
|
run();
|
|
action_mask |= EXECUTION;
|
|
action_mask &= ~CONTEXT_CHANGE;
|
|
action_mask &= ~LISTING;
|
|
action_mask &= ~ELISTING;
|
|
}
|
|
#endif /* KDBX */
|
|
break;
|
|
|
|
case O_SET:
|
|
set(p->value.arg[0], p->value.arg[1]);
|
|
/*
|
|
* Set to NULL so we won't try to free in tfree(). This value is
|
|
* used for storing the value of the set variable, and freed when
|
|
* the set variable is unset.
|
|
*/
|
|
p->value.arg[1] = NULL;
|
|
break;
|
|
|
|
case O_SEARCH:
|
|
search(p->value.arg[0]->value.lcon, p->value.arg[1]->value.scon);
|
|
action_mask |= ELISTING;
|
|
break;
|
|
|
|
case O_SOURCE:
|
|
setinput(p->value.scon);
|
|
break;
|
|
|
|
case O_STATUS:
|
|
status();
|
|
break;
|
|
|
|
case O_TRACE:
|
|
case O_TRACEI:
|
|
trace(p);
|
|
action_mask |= TRACE_ON;
|
|
break;
|
|
|
|
case O_STOP:
|
|
case O_STOPI:
|
|
stop(p);
|
|
action_mask |= BREAKPOINT;
|
|
break;
|
|
|
|
case O_UNSET:
|
|
undefvar(p->value.arg[0]->value.name);
|
|
break;
|
|
|
|
case O_WATCH:
|
|
watch(p);
|
|
break;
|
|
|
|
case O_ADDEVENT:
|
|
addevent(p->value.trace.cond, p->value.trace.actions);
|
|
break;
|
|
|
|
case O_DELETE:
|
|
n1 = p->value.arg[0];
|
|
while (n1->op == O_COMMA) {
|
|
n2 = n1->value.arg[0];
|
|
assert(n2->op == O_LCON);
|
|
if (not delevent((unsigned int) n2->value.lcon)) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_107,
|
|
"unknown event %ld"), n2->value.lcon);
|
|
} else
|
|
action_mask |= BREAKPOINT;
|
|
n1 = n1->value.arg[1];
|
|
}
|
|
assert(n1->op == O_LCON);
|
|
if (not delevent((unsigned int) n1->value.lcon)) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_107,
|
|
"unknown event %ld"), n1->value.lcon);
|
|
} else
|
|
action_mask |= BREAKPOINT;
|
|
break;
|
|
|
|
case O_DELALL:
|
|
if ( deleteall())
|
|
action_mask |= BREAKPOINT;
|
|
break;
|
|
|
|
case O_ENDX:
|
|
endprogram();
|
|
action_mask |= EXECUTION_COMPLETED;
|
|
break;
|
|
|
|
case O_IF:
|
|
if (cond(p->value.trace.cond)) {
|
|
evalcmdlist(p->value.trace.actions, false);
|
|
}
|
|
break;
|
|
|
|
case O_ONCE:
|
|
event_once(copynode(p->value.trace.cond), p->value.trace.actions);
|
|
break;
|
|
|
|
case O_PRINTCALL:
|
|
printcall(p->value.sym, whatblock(return_addr()));
|
|
break;
|
|
|
|
case O_PRINTIFCHANGED:
|
|
printifchanged(p->value.arg[0]);
|
|
break;
|
|
|
|
case O_PRINTRTN:
|
|
printrtn(p->value.sym);
|
|
break;
|
|
|
|
case O_PRINTSRCPOS:
|
|
getsrcpos();
|
|
|
|
/*
|
|
* Start of trace output. Indicate by setting
|
|
* eventnum to nil.
|
|
*/
|
|
trdata.reporting_trace_output = 1;
|
|
(*rpt_trace)( &trdata );
|
|
|
|
dpi_current_location( &trdata );
|
|
trdata.eventnum = eventId;
|
|
trdata.token = nil;
|
|
trdata.value = nil;
|
|
|
|
if (p->value.arg[0] == nil) {
|
|
printsrcpos();
|
|
(*rpt_output)(stdout, "\n" );
|
|
printlines(curline, curline);
|
|
} else if (p->value.arg[0]->op == O_QLINE) {
|
|
/* Check filename to find out if this is a "tracei" */
|
|
if ((p->value.arg[0]->value.arg[1]->value.lcon == 0) ||
|
|
(p->value.arg[0]->value.arg[0]->op == O_SCON &&
|
|
p->value.arg[0]->value.arg[0]->value.scon == nil)) {
|
|
(*rpt_output)(stdout, "tracei: ");
|
|
printinst(pc, pc);
|
|
} else {
|
|
if (canReadSource()) {
|
|
(*rpt_output)(stdout, "trace");
|
|
if ((sourcefiles > 1) || inclfiles)
|
|
(*rpt_output)(stdout, " in %s:",
|
|
basefile(cursource));
|
|
else
|
|
(*rpt_output)(stdout, ":");
|
|
printlines(curline, curline);
|
|
}
|
|
}
|
|
} else {
|
|
eval(p->value.arg[0]);
|
|
|
|
printsrcpos();
|
|
|
|
msgbegin;
|
|
prtree( rpt_output, stdout, p->value.arg[0]);
|
|
msgend( trdata.token );
|
|
|
|
msgbegin;
|
|
printval(p->value.arg[0]->nodetype, 0);
|
|
msgend( trdata.value );
|
|
|
|
(*rpt_output)(stdout, ": %s = %s\n",
|
|
trdata.token, trdata.value );
|
|
}
|
|
trdata.reporting_trace_output = 0;
|
|
(*rpt_trace)( &trdata );
|
|
dispose( trdata.token );
|
|
dispose( trdata.value );
|
|
break;
|
|
|
|
case O_PROCRTN:
|
|
procreturn(p->value.sym);
|
|
action_mask |= EXECUTION;
|
|
action_mask &= ~CONTEXT_CHANGE;
|
|
action_mask &= ~LISTING;
|
|
action_mask &= ~ELISTING;
|
|
break;
|
|
|
|
case O_STOPIFCHANGED:
|
|
stopifchanged(p);
|
|
break;
|
|
|
|
case O_STOPX:
|
|
isstopped = true;
|
|
break;
|
|
|
|
case O_TRACEON:
|
|
traceon(p->value.trace.inst, p->value.trace.event,
|
|
p->value.trace.actions);
|
|
break;
|
|
|
|
case O_TRACEOFF:
|
|
traceoff(p->value.lcon);
|
|
break;
|
|
|
|
case O_SCREEN:
|
|
if(ScrUsed) {
|
|
warning(catgets(scmc_catd, MS_eval, MSG_714,
|
|
"screen subcommand can only be invoked once.\n"));
|
|
}
|
|
else if(norun) {
|
|
warning(catgets(scmc_catd, MS_eval, MSG_713,
|
|
"screen subcommand can only be invoked from the originating process.\n"));
|
|
}
|
|
else {
|
|
int fd;
|
|
fd = screen(1); /* return -1 if fail to open an Xwindow */
|
|
if(fd != -1) reset_debuggee_fd(fd);
|
|
}
|
|
break;
|
|
|
|
case O_MULTPROC:
|
|
pid = curpid();
|
|
if (p->value.arg[0]->value.lcon == 0)
|
|
{
|
|
switch(multproc)
|
|
{
|
|
case on :
|
|
(*rpt_output)(stdout,
|
|
"Multi-process debugging is enabled\n");
|
|
break;
|
|
case parent :
|
|
(*rpt_output)(stdout,
|
|
"Multi-process debugging is enabled to follow the parent\n");
|
|
break;
|
|
case child :
|
|
(*rpt_output)(stdout,
|
|
"Multi-process debugging is enabled to follow the child\n");
|
|
break;
|
|
case off :
|
|
(*rpt_output)(stdout,
|
|
"Multi-process debugging is disabled\n");
|
|
break;
|
|
}
|
|
} else {
|
|
fork_type new_multproc;
|
|
new_multproc = (fork_type) p->value.arg[0]->value.lcon;
|
|
if (new_multproc == off)
|
|
errcond = ptrace(MULTI, pid, 0, 0, 0);
|
|
else
|
|
errcond = ptrace(MULTI, pid, 0, 1, 0);
|
|
|
|
if (errcond < 0)
|
|
panic( catgets(scmc_catd, MS_eval, MSG_124,
|
|
"Could not alter multi-processing mode."));
|
|
else {
|
|
multproc = new_multproc;
|
|
action_mask |= CONFIGURATION;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case O_DETACH:
|
|
bpfree();
|
|
errcond = detach(curpid(), p->value.arg[0]->value.lcon);
|
|
if (errcond < 0)
|
|
warning( catgets(scmc_catd, MS_eval, MSG_125,
|
|
"Could not detach from process. Use quit."));
|
|
else
|
|
{
|
|
action_mask |= DETACHED;
|
|
}
|
|
break;
|
|
|
|
case O_OBJECT:
|
|
objname = p->value.scon;
|
|
objfree();
|
|
symbols_init();
|
|
|
|
/*
|
|
types_reinit();
|
|
*/
|
|
readobj(objname);
|
|
break;
|
|
|
|
case O_SWITCH:
|
|
#ifdef KDBX
|
|
checkref(p->value.arg[0]);
|
|
assert(p->value.arg[0]->op == O_LCON);
|
|
do_switch(p->value.arg[0]->value.lcon);
|
|
#endif /* KDBX */
|
|
break;
|
|
|
|
case O_CPU:
|
|
#ifdef KDBX
|
|
checkref(p->value.arg[0]);
|
|
assert(p->value.arg[0]->op == O_LCON);
|
|
do_cpu(p->value.arg[0]->value.lcon);
|
|
#endif /* KDBX */
|
|
break;
|
|
|
|
case O_XCALL:
|
|
#ifdef KDBX
|
|
assert(p->value.arg[0]->op == O_NAME);
|
|
|
|
nbp =0;
|
|
for (n1 = p->value.arg[1]; n1 != nil; n1 = n1->value.arg[1]) {
|
|
eval(n1->value.arg[0]);
|
|
xcall_args[nbp] = pop(long);
|
|
if (nbp == MAX_XCALL_ARGS) break;
|
|
else nbp++;
|
|
}
|
|
if (nbp == MAX_XCALL_ARGS) {
|
|
printf("Invalid argument number (%d) to print function\n", nbp);
|
|
} else {
|
|
do_xcall(p->value.arg[0]->value.name, nbp, xcall_args);
|
|
}
|
|
#endif /* KDBX */
|
|
break;
|
|
|
|
default:
|
|
panic( catgets(scmc_catd, MS_eval, MSG_126, "eval: bad op %d"),
|
|
p->op);
|
|
}
|
|
/* if we are not evaluating data */
|
|
if (p->op != O_SYM && p->op != O_RVAL && p->op != O_DOT &&
|
|
p->op != O_DOTSTAR) {
|
|
/* reset the defered and ptr to */
|
|
/* member flags */
|
|
notderefed = true;
|
|
specificptrtomember = false;
|
|
}
|
|
if (traceeval) {
|
|
(*rpt_error)(stderr, "end eval %s\n", opname(p->op));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NAME: unary_op
|
|
*
|
|
* FUNCTION: Handle unary operations.
|
|
*
|
|
* PARAMETERS:
|
|
* p - input Node
|
|
*
|
|
* MACROS: poprealarg, popintarg, poplonglongarg,
|
|
* do_unary_operation, unary_operation
|
|
*
|
|
* RECOVERY OPERATION: NONE NEEDED
|
|
*
|
|
* DATA STRUCTURES: NONE
|
|
*
|
|
* RETURNS: 0
|
|
*/
|
|
|
|
int unary_op(p)
|
|
Node p;
|
|
{
|
|
int i;
|
|
double fr0, fr1;
|
|
quadf qr0, qr1;
|
|
|
|
if (isreal(p->op))
|
|
{
|
|
poprealarg(0, fr0, qr0);
|
|
|
|
switch (p->op)
|
|
{
|
|
case O_FTOQ:
|
|
push(double, (double) fr0);
|
|
push(double, (double) 0);
|
|
break;
|
|
|
|
case O_NEGQ:
|
|
qsub(&qr0, (double) 0, (double) 0, qr0.val[0], qr0.val[1]);
|
|
push(double, qr0.val[0]);
|
|
push(double, qr0.val[1]);
|
|
break;
|
|
|
|
case O_NEGF:
|
|
push(double, -fr0);
|
|
break;
|
|
|
|
}
|
|
}
|
|
else if (isint(p->op))
|
|
{
|
|
long r0;
|
|
unsigned long ur0;
|
|
LongLong llr0;
|
|
uLongLong ullr0;
|
|
Symbol op_type;
|
|
unsigned char operand_type = ISLONG;
|
|
|
|
op_type = rtype(p->value.arg[0]->nodetype);
|
|
|
|
if (op_type->symvalue.rangev.is_unsigned)
|
|
operand_type |= ISUNSIGNED;
|
|
if (op_type->symvalue.rangev.size == sizeofLongLong)
|
|
operand_type |= ISLONGLONG;
|
|
|
|
switch (operand_type)
|
|
{
|
|
case ISLONG:
|
|
popintarg(0, r0);
|
|
break;
|
|
case ISUNSIGNEDLONG:
|
|
popintarg(0, ur0);
|
|
break;
|
|
case ISLONGLONG:
|
|
poplonglongarg(0, llr0);
|
|
break;
|
|
case ISUNSIGNEDLONGLONG:
|
|
poplonglongarg(0, ullr0);
|
|
break;
|
|
}
|
|
|
|
switch (p->op)
|
|
{
|
|
case O_ITOF:
|
|
case O_ITOQ:
|
|
unary_operation(double, (double))
|
|
|
|
if (p->op == O_ITOQ)
|
|
push(double, (double) 0);
|
|
break;
|
|
|
|
case O_NEG:
|
|
do_unary_operation(-);
|
|
break;
|
|
|
|
case O_NOT:
|
|
unary_operation(Boolrep, !)
|
|
break;
|
|
|
|
case O_COMP:
|
|
do_unary_operation(~);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Symbol s;
|
|
Address addr = 0;
|
|
unsigned len = 0;
|
|
|
|
eval (p->value.arg[0]);
|
|
|
|
switch (p->op)
|
|
{
|
|
/*
|
|
* Pop an address and push the contents back on the stack.
|
|
* If the address is actually a register number, then push
|
|
* the (possibly saved on stack) register value(s).
|
|
*/
|
|
|
|
case O_CPPREF:
|
|
rpush(pop(Address), sizeof(Address));
|
|
break;
|
|
|
|
case O_INDIR:
|
|
case O_INDIRA:
|
|
if (p->nodetype->class == STRINGPTR) break;
|
|
case O_RVAL:
|
|
s = p->nodetype;
|
|
|
|
if (is_f90_sym(s))
|
|
s = p->nodetype = convert_f90_sym(s, NULL);
|
|
|
|
/* don't have to do this for label */
|
|
if (s->class == LABEL || s->class == FUNC) break;
|
|
|
|
/* or a pointer to a member function*/
|
|
if (ptrtomemberfunction) {
|
|
ptrtomemberfunction = false;
|
|
break;
|
|
}
|
|
|
|
/* or string constants */
|
|
if (s->class == ARRAY && s->type == t_char &&
|
|
p->value.arg[0]->op == O_SCON)
|
|
{
|
|
/*
|
|
* This case is really for dbx vars that have been
|
|
* set to string constants, for example
|
|
* set $stepignore="function". We want the quotes to
|
|
* be there if you print $stepignore.
|
|
*/
|
|
if (s->language == primlang)
|
|
s->language = cLang;
|
|
break;
|
|
}
|
|
|
|
/* if this is a debugger variable */
|
|
if (s == t_int || s == t_char || s == t_boolean
|
|
|| s == dt_uint || s == t_longlong || s == t_ulonglong
|
|
|| s == t_quad)
|
|
break;
|
|
|
|
indirect = true;
|
|
if (sp > &stack[0])
|
|
addr = pop(long);
|
|
/* In case of pascal pointer, there are two words in stack. */
|
|
/* The first one is the size of what the pointer is pointing */
|
|
/* at, and the second one is the actual address we want. */
|
|
if (sp > &stack[0] and s->language == pascalLang and
|
|
(p->op == O_INDIR or p->op == O_INDIRA))
|
|
addr = pop(long);
|
|
if (subarray) {
|
|
if (s->class == ARRAY)
|
|
error( catgets(scmc_catd, MS_eval, MSG_116,
|
|
"Subarray of pointer to array not supported."));
|
|
else /* Break because we don't need to do this */
|
|
break; /* when we are processing a subarray. */
|
|
}
|
|
if (p->op == O_INDIRA)
|
|
len = sizeof(Address);
|
|
else
|
|
len = size(s);
|
|
if (s->class != REF &&
|
|
(s->storage == INREG || (s->param && preg(s, nil) != -1)))
|
|
pushregvalue(s, addr, nil, len);
|
|
else
|
|
{
|
|
if (addr == 0)
|
|
error( catgets(scmc_catd, MS_eval, MSG_84,
|
|
"reference through nil pointer"));
|
|
/* To acount for read/write offset within a file. The */
|
|
/* offset is stored at next address after the file */
|
|
/* pointer itself. For Pascal ONLY! */
|
|
if (s->language == pascalLang and
|
|
(p->op == O_RVAL and rtype(s)->class == FILET))
|
|
{
|
|
int offset;
|
|
dread(&offset, addr+sizeof(Address), sizeof(Word));
|
|
dread(&addr, addr, len);
|
|
offset = (offset > 0) ? offset-1 : 0;
|
|
push(Address, addr+offset)
|
|
}
|
|
else
|
|
rpush(addr, len);
|
|
}
|
|
break;
|
|
|
|
case O_TYPERENAME:
|
|
typecast(p);
|
|
break;
|
|
|
|
case O_UP:
|
|
checkref(p->value.arg[0]);
|
|
assert(p->value.arg[0]->op == O_LCON);
|
|
pop(long);
|
|
up(p->value.arg[0]->value.lcon);
|
|
break;
|
|
|
|
case O_DOWN:
|
|
checkref(p->value.arg[0]);
|
|
assert(p->value.arg[0]->op == O_LCON);
|
|
pop(long);
|
|
down(p->value.arg[0]->value.lcon);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* NAME: binary_op
|
|
*
|
|
* FUNCTION: Handle binary operations.
|
|
*
|
|
* PARAMETERS:
|
|
* p - input Node
|
|
*
|
|
* MACROS: poprealarg, popintarg, poplonglongarg,
|
|
* do_operation, boolean_operation
|
|
*
|
|
* RECOVERY OPERATION: NONE NEEDED
|
|
*
|
|
* DATA STRUCTURES: NONE
|
|
*
|
|
* RETURNS: 0
|
|
*/
|
|
|
|
int binary_op(p)
|
|
Node p;
|
|
{
|
|
int i;
|
|
double fr0, fr1;
|
|
quadf qr0, qr1;
|
|
|
|
if (isreal(p->op))
|
|
{
|
|
poprealarg(1, fr1, qr1);
|
|
poprealarg(0, fr0, qr0);
|
|
|
|
switch (p->op)
|
|
{
|
|
case O_ADDF:
|
|
push(double, fr0+fr1);
|
|
break;
|
|
|
|
case O_ADDQ:
|
|
qadd(&qr0, qr0.val[0], qr0.val[1], qr1.val[0], qr1.val[1]);
|
|
push(double, qr0.val[0]);
|
|
push(double, qr0.val[1]);
|
|
break;
|
|
|
|
case O_SUBQ:
|
|
qsub(&qr0, qr0.val[0], qr0.val[1], qr1.val[0], qr1.val[1]);
|
|
push(double, qr0.val[0]);
|
|
push(double, qr0.val[1]);
|
|
break;
|
|
|
|
case O_MULQ:
|
|
qmul(&qr0, qr0.val[0], qr0.val[1], qr1.val[0], qr1.val[1]);
|
|
push(double, qr0.val[0]);
|
|
push(double, qr0.val[1]);
|
|
break;
|
|
|
|
case O_DIVQ:
|
|
qdiv(&qr0, qr0.val[0], qr0.val[1], qr1.val[0], qr1.val[1]);
|
|
push(double, qr0.val[0]);
|
|
push(double, qr0.val[1]);
|
|
break;
|
|
|
|
case O_EXPQ:
|
|
qpow(&qr0, qr0.val[0], qr0.val[1], qr1.val[0], qr1.val[1]);
|
|
push(double, qr0.val[0]);
|
|
push(double, qr0.val[1]);
|
|
break;
|
|
|
|
case O_LTQ:
|
|
/* _qdbcmp() - quad comparsion routine */
|
|
/* returns -1 when a < b */
|
|
/* 0 when a == b */
|
|
/* 1 when a > b */
|
|
/* -2 when a or b is NaN */
|
|
push(Boolrep, _qdbcmp(&qr0, &qr1) == -1);
|
|
break;
|
|
|
|
case O_LEQ:
|
|
i = _qdbcmp(&qr0, &qr1);
|
|
push(Boolrep, (i == -1 or i == 0));
|
|
break;
|
|
|
|
case O_GTQ:
|
|
push(Boolrep, _qdbcmp(&qr0, &qr1) == 1 );
|
|
break;
|
|
|
|
case O_GEQ:
|
|
i = _qdbcmp(&qr0, &qr1);
|
|
push(Boolrep, (i == 1 or i == 0));
|
|
break;
|
|
|
|
case O_EQQ:
|
|
push(Boolrep, _qdbcmp(&qr0, &qr1) == 0 );
|
|
break;
|
|
|
|
case O_NEQ:
|
|
push(Boolrep, _qdbcmp(&qr0, &qr1) != 0 );
|
|
break;
|
|
|
|
case O_EXP:
|
|
push(double, pow(fr0,fr1));
|
|
break;
|
|
|
|
case O_SUBF:
|
|
push(double, fr0-fr1);
|
|
break;
|
|
|
|
case O_MULF:
|
|
push(double, fr0*fr1);
|
|
break;
|
|
|
|
case O_DIVF:
|
|
if (fr1 == 0) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_85,
|
|
"error: division by 0"));
|
|
}
|
|
push(double, fr0 / fr1);
|
|
break;
|
|
|
|
case O_LTF:
|
|
push(Boolrep, fr0 < fr1);
|
|
break;
|
|
|
|
case O_LEF:
|
|
push(Boolrep, fr0 <= fr1);
|
|
break;
|
|
|
|
case O_GTF:
|
|
push(Boolrep, fr0 > fr1);
|
|
break;
|
|
|
|
case O_GEF:
|
|
push(Boolrep, fr0 >= fr1);
|
|
break;
|
|
|
|
case O_EQF:
|
|
push(Boolrep, fr0 == fr1);
|
|
break;
|
|
|
|
case O_NEF:
|
|
push(Boolrep, fr0 != fr1);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
long r0, r1 = 0;
|
|
unsigned long ur0, ur1 = 0;
|
|
LongLong llr0, llr1 = 0;
|
|
uLongLong ullr0, ullr1 = 0;
|
|
Symbol op_type;
|
|
unsigned char operand_type = ISLONG;
|
|
|
|
if (p->nodetype == t_boolean)
|
|
{
|
|
op_type = get_output_nodetype (rtype(p->value.arg[0]->nodetype),
|
|
rtype(p->value.arg[1]->nodetype));
|
|
op_type = rtype(op_type);
|
|
}
|
|
else
|
|
{
|
|
op_type = rtype(p->nodetype);
|
|
}
|
|
|
|
if (op_type->symvalue.rangev.is_unsigned)
|
|
operand_type |= ISUNSIGNED;
|
|
if (op_type->symvalue.rangev.size == sizeofLongLong)
|
|
operand_type |= ISLONGLONG;
|
|
|
|
switch (operand_type)
|
|
{
|
|
case ISLONG:
|
|
popintarg(1, r1);
|
|
popintarg(0, r0);
|
|
break;
|
|
case ISUNSIGNEDLONG:
|
|
popintarg(1, ur1);
|
|
popintarg(0, ur0);
|
|
break;
|
|
case ISLONGLONG:
|
|
poplonglongarg(1, llr1);
|
|
poplonglongarg(0, llr0);
|
|
break;
|
|
case ISUNSIGNEDLONGLONG:
|
|
poplonglongarg(1, ullr1);
|
|
poplonglongarg(0, ullr0);
|
|
break;
|
|
}
|
|
|
|
switch (p->op)
|
|
{
|
|
case O_ADD:
|
|
do_operation(+);
|
|
break;
|
|
|
|
case O_BOR:
|
|
do_operation(|);
|
|
break;
|
|
|
|
case O_BAND:
|
|
do_operation(&);
|
|
break;
|
|
|
|
case O_BXOR:
|
|
do_operation(^);
|
|
break;
|
|
|
|
case O_SUB:
|
|
do_operation(-);
|
|
break;
|
|
|
|
case O_MUL:
|
|
do_operation(*);
|
|
break;
|
|
|
|
case O_DIV:
|
|
if ((r1 == 0) && (ur1 == 0) && (llr1 == 0) && (ullr1 == 0)){
|
|
error( catgets(scmc_catd, MS_eval, MSG_85,
|
|
"error: division by 0"));
|
|
}
|
|
do_operation(/);
|
|
break;
|
|
|
|
case O_MOD:
|
|
if ((r1 == 0) && (ur1 == 0) && (llr1 == 0) && (ullr1 == 0)){
|
|
error( catgets(scmc_catd, MS_eval, MSG_88,
|
|
"error: mod by 0"));
|
|
}
|
|
|
|
do_operation(%);
|
|
break;
|
|
|
|
case O_LT:
|
|
boolean_operation(<);
|
|
break;
|
|
|
|
case O_LE:
|
|
boolean_operation(<=);
|
|
break;
|
|
|
|
case O_GT:
|
|
boolean_operation(>);
|
|
break;
|
|
|
|
case O_GE:
|
|
boolean_operation(>=);
|
|
break;
|
|
|
|
case O_EQ:
|
|
boolean_operation(==);
|
|
break;
|
|
|
|
case O_NE:
|
|
boolean_operation(!=);
|
|
break;
|
|
|
|
case O_AND:
|
|
boolean_operation(&&);
|
|
break;
|
|
|
|
case O_OR:
|
|
boolean_operation(||);
|
|
break;
|
|
|
|
case O_SL:
|
|
do_operation(<<);
|
|
break;
|
|
|
|
case O_SR:
|
|
do_operation(>>);
|
|
break;
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int detach(int pid, int signal)
|
|
{
|
|
#ifdef K_THREADS
|
|
int stat; /* status returned by pwait() */
|
|
extern tid_running_thread; /* use by ptrace(PTT_CONTINUE) */
|
|
extern nb_k_threads_sig; /* number of threads with signal */
|
|
struct ptthreads buf_ptthreads; /* buffer for ptrace(PTT_CONTINUE)*/
|
|
#endif /* K_THREADS */
|
|
int errcond;
|
|
|
|
#ifdef K_THREADS
|
|
/* nb_k_threads_sig:number of threads with TTRCSIG (pending signal)*/
|
|
/* if there are more than one thread witch have received the */
|
|
/* break-point : continue to clear the signal */
|
|
while ((nb_k_threads_sig > 1) && process->is_bp )
|
|
{
|
|
buf_ptthreads.th[0] = NULL;
|
|
if (traceexec)
|
|
(*rpt_output)(stdout, "!! ptrace(%d,%d,%d,%d,%x)\n",
|
|
PTT_CONTINUE, tid_running_thread, 1, 0, &buf_ptthreads);
|
|
if (ptrace(PTT_CONTINUE,tid_running_thread, 1, 0,&buf_ptthreads) < 0)
|
|
panic( catgets(scmc_catd, MS_process, MSG_285,
|
|
"error %d trying to continue process"), errno);
|
|
|
|
pwait(pid, &stat);
|
|
if (traceexec)
|
|
(*rpt_output)(stdout, "!! wait status = 0x%x, errno = %d\n",
|
|
stat, errno);
|
|
getinfo(process, stat);
|
|
}
|
|
#endif /* K_THREADS */
|
|
|
|
if (traceexec)
|
|
(*rpt_output)(stdout, "!! ptrace(%d,0x%x,%d,%d,%x)\n",
|
|
DETACH, pid, 1, signal, 0);
|
|
|
|
errcond = ptrace(DETACH, pid, 1, signal, 0);
|
|
return errcond;
|
|
}
|
|
|
|
|
|
/*
|
|
* Dereference the pointer on the stack.
|
|
*/
|
|
|
|
/* this routine pops the last value on the stack, determines what this */
|
|
/* address is pointing to and pushes what it is pointing to back on */
|
|
/* the stack */
|
|
|
|
void dereference()
|
|
{
|
|
Address addr;
|
|
|
|
addr = pop(Address);
|
|
dread(&addr, addr, sizeof(Address));
|
|
notderefed = false;
|
|
push(Address, addr);
|
|
}
|
|
|
|
/*
|
|
* Evaluate a list of commands.
|
|
*/
|
|
|
|
public evalcmdlist(cl, first_line)
|
|
Cmdlist cl;
|
|
Boolean first_line;
|
|
{
|
|
Command c;
|
|
|
|
foreach (Command, c, cl)
|
|
/* if not first_line or it is the first line and tracing in
|
|
a function */
|
|
if (!first_line
|
|
|| ((c->op == O_PRINTSRCPOS) && (c->value.arg[1] != NULL)))
|
|
evalcmd(c);
|
|
endfor
|
|
}
|
|
|
|
/*
|
|
* Push "len" bytes onto the expression stack from address "addr"
|
|
* in the process. If there isn't room on the stack, print an error message.
|
|
*/
|
|
|
|
public rpush(addr, len)
|
|
Address addr;
|
|
unsigned len;
|
|
{
|
|
if (not canpush(len)) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_128,
|
|
"expression too large to evaluate"));
|
|
} else {
|
|
chksp();
|
|
dread(sp, addr, len);
|
|
sp += len;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if the stack has n bytes available.
|
|
*/
|
|
|
|
public Boolean canpush(n)
|
|
Integer n;
|
|
{
|
|
return (Boolean) (sp + n < &stack[STACKSIZE]);
|
|
}
|
|
|
|
/*
|
|
* Push a small scalar of the given type onto the stack.
|
|
*/
|
|
|
|
public pushsmall(s, v)
|
|
int s;
|
|
long v;
|
|
{
|
|
switch (s) {
|
|
case sizeof(char):
|
|
push(char, v);
|
|
break;
|
|
|
|
case sizeof(short):
|
|
push(short, v);
|
|
break;
|
|
|
|
case sizeof(long):
|
|
push(long, v);
|
|
break;
|
|
|
|
default:
|
|
panic( catgets(scmc_catd, MS_eval, MSG_129,
|
|
"bad size %d in pushsmall"), s);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Push a value in a register for the given symbol, perhaps on the stack.
|
|
* If the length (in words) is greater than one, then push
|
|
* consecutive registers.
|
|
*/
|
|
|
|
public pushregvalue (s, r, f, n)
|
|
Symbol s;
|
|
int r;
|
|
Frame f;
|
|
unsigned n;
|
|
{
|
|
register int i, j;
|
|
register Frame frp;
|
|
register Symbol b;
|
|
|
|
r = (r < IAR) ? r : ((r < FPR0) ? SYSREGNO(r) : FPREGNO(r));
|
|
frp = f;
|
|
if (frp == nil) {
|
|
b = s->block;
|
|
while (b != nil && b->class == MODULE) {
|
|
b = b->block;
|
|
}
|
|
if (b == nil) {
|
|
frp = nil;
|
|
} else {
|
|
frp = findframe(b);
|
|
if (frp == nil) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_130,
|
|
"[internal error: nil frame for %s]"), symname(s));
|
|
}
|
|
}
|
|
}
|
|
if (r < NGREG + NSYS) {
|
|
j = r + n / sizeof(Word);
|
|
for (i = r; i < j; i++) {
|
|
push(Word, savereg(i, frp));
|
|
}
|
|
j = n % sizeof(Word);
|
|
if (j > 0) {
|
|
pushsmall(j, savereg(i, frp));
|
|
}
|
|
}
|
|
else {
|
|
r -= (NGREG + NSYS);
|
|
j = r + n / sizeof(double);
|
|
for (i = r; i < j; i++) {
|
|
push(double, savefreg(i, frp));
|
|
}
|
|
j = n % sizeof(double);
|
|
if (j > 3) {
|
|
push(Word, savefreg(i, frp));
|
|
j -= 4;
|
|
}
|
|
if (j > 0) {
|
|
pushsmall(j, savefreg(i, frp));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NAME: popsmall
|
|
*
|
|
* FUNCTION: Pop an item of the given type from the stack
|
|
* and convert it to a long.
|
|
*
|
|
* PARAMETERS:
|
|
* t - input Symbol
|
|
*
|
|
* RECOVERY OPERATION: NONE NEEDED
|
|
*
|
|
* DATA STRUCTURES: NONE
|
|
*
|
|
* RETURNS: a long integer
|
|
*/
|
|
|
|
public long popsmall(t)
|
|
Symbol t;
|
|
{
|
|
register integer n;
|
|
long r = 0;
|
|
|
|
n = size(t);
|
|
if (n == sizeof(char)) {
|
|
if (( (t->class == RANGE or t->class == PACKRANGE)
|
|
and t->symvalue.rangev.lower >= 0) ||
|
|
((t->class == VAR) && (t->type == dt_uchar)) ||
|
|
isunsignedpic(t) ||
|
|
(t->class == TYPE &&
|
|
((streq(ident(t->name), "char"))
|
|
|| streq(ident(t->name), "unsigned char")))) {
|
|
r = (long) pop(unsigned char);
|
|
} else {
|
|
r = (long) pop(signed char);
|
|
}
|
|
} else if (n == sizeof(short)) {
|
|
if (( (t->class == RANGE or t->class == PACKRANGE)
|
|
and t->symvalue.rangev.lower >= 0) ||
|
|
((t->class == VAR) && (t->type == dt_ushort)) ||
|
|
isunsignedpic(t) ||
|
|
(t->class == TYPE && streq(ident(t->name), "unsigned short"))) {
|
|
r = (long) pop(unsigned short);
|
|
} else {
|
|
r = (long) pop(short);
|
|
}
|
|
} else if (n == 3) { /* This case can only happens when */
|
|
char *ptr; /* it's a packed subrange of integers */
|
|
/* where int is packed into 3 bytes. */
|
|
ptr = (char *) &r;
|
|
popn(n, ptr+1);
|
|
} else if (n == sizeof(long)) {
|
|
r = pop(long);
|
|
} else if (n == sizeofLongLong) {
|
|
r = (long) pop(LongLong);
|
|
} else {
|
|
error( catgets(scmc_catd, MS_eval, MSG_131,
|
|
"[internal error: size %d in popsmall]"), n);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* NAME: poplonglong
|
|
*
|
|
* FUNCTION: Pop an item of the given type from the stack
|
|
* and convert it to a longlong.
|
|
*
|
|
* PARAMETERS:
|
|
* t - input Symbol
|
|
*
|
|
* RECOVERY OPERATION: NONE NEEDED
|
|
*
|
|
* DATA STRUCTURES: NONE
|
|
*
|
|
* RETURNS: a long long integer
|
|
*/
|
|
|
|
LongLong poplonglong(t)
|
|
Symbol t;
|
|
{
|
|
Symbol rtype_t = rtype(t);
|
|
register integer n;
|
|
LongLong r = 0;
|
|
|
|
n = size(t);
|
|
|
|
if (n < sizeofLongLong)
|
|
{
|
|
if (rtype_t->symvalue.rangev.is_unsigned)
|
|
r = (LongLong) (unsigned long) popsmall(t);
|
|
else
|
|
r = (LongLong) popsmall(t);
|
|
}
|
|
else
|
|
r = pop(LongLong);
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Evaluate a conditional expression.
|
|
*/
|
|
|
|
public Boolean cond(p)
|
|
Node p;
|
|
{
|
|
Boolean b;
|
|
int i;
|
|
|
|
if (p == nil) {
|
|
b = true;
|
|
} else {
|
|
eval(p);
|
|
i = pop(Boolrep);
|
|
b = (Boolean) i;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
* Return the address corresponding to a given tree.
|
|
*/
|
|
|
|
public Address lval(p)
|
|
Node p;
|
|
{
|
|
if (p->op == O_RVAL) {
|
|
eval(p->value.arg[0]);
|
|
} else {
|
|
eval(p);
|
|
}
|
|
return (Address) (pop(long));
|
|
}
|
|
|
|
/*
|
|
* Process a trace command, translating into the appropriate events
|
|
* and associated actions.
|
|
*/
|
|
|
|
public trace(p)
|
|
Node p;
|
|
{
|
|
Node exp, place, cond;
|
|
|
|
exp = p->value.arg[0];
|
|
cond = p->value.arg[2];
|
|
if (cond != nil)
|
|
chkboolean(cond);
|
|
place = p->value.arg[1];
|
|
|
|
/*
|
|
* Set to NULL so we won't try to free in tfree(). These Nodes are used
|
|
* for printing events, and freed when the event is cleared.
|
|
*/
|
|
p->value.arg[0] = p->value.arg[1] = p->value.arg[2] = NULL;
|
|
|
|
if (exp != nil && place == nil)
|
|
{
|
|
/* we must handle the possibility of a C++ multiple function list */
|
|
if (exp->nodetype->class != CPPSYMLIST)
|
|
{
|
|
if (exp->op != O_SYM && exp->nodetype->class == FUNC)
|
|
trace1(p, build(O_SYM, exp->nodetype), nil, cond);
|
|
else
|
|
trace1(p, exp, nil, cond);
|
|
}
|
|
else
|
|
{
|
|
cppSymList list = exp->nodetype->symvalue.sList;
|
|
for (; list != nil; list = list->next)
|
|
trace1(p, build(O_SYM, list->sym), nil, cond);
|
|
}
|
|
}
|
|
else if (place != nil)
|
|
{
|
|
/* we must handle the possibility of a C++ multiple function list */
|
|
if (place->nodetype->class != CPPSYMLIST)
|
|
if (place->op != O_SYM && place->nodetype->class == FUNC)
|
|
trace1(p, exp, build(O_SYM, place->nodetype), cond);
|
|
else
|
|
trace1(p, exp, place, cond);
|
|
else
|
|
{
|
|
cppSymList list = place->nodetype->symvalue.sList;
|
|
for (; list != nil; list = list->next)
|
|
trace1(p, exp, build(O_SYM, list->sym), cond);
|
|
}
|
|
}
|
|
else
|
|
trace1(p, nil, nil, cond);
|
|
}
|
|
|
|
public trace1(p, exp, place, cond)
|
|
Node p, exp, place, cond;
|
|
{
|
|
Node left;
|
|
|
|
if (exp != nil && exp->nodetype->class == MEMBER &&
|
|
exp->nodetype->symvalue.member.type == FUNCM ||
|
|
place != nil && place->nodetype->class == MEMBER &&
|
|
place->nodetype->symvalue.member.type == FUNCM)
|
|
{
|
|
error(catgets(scmc_catd, MS_eval, MSG_600,
|
|
"Cannot set a breakpoint in a pure virtual function."));
|
|
}
|
|
/* check and see if "exp" is sometime we can trace or stop */
|
|
else if (exp != nil && exp->nodetype->class == TAG) {
|
|
error(catgets(scmc_catd, MS_eval, MSG_639,
|
|
"Cannot trace or stop a %s."), classname(exp->nodetype));
|
|
}
|
|
|
|
if (exp == nil) {
|
|
if (place->op == O_QLINE or place->op == O_LCON)
|
|
{
|
|
if (place == nil)
|
|
place = build(O_SYM, program);
|
|
traceinst(p->op, place, cond);
|
|
}
|
|
else
|
|
{
|
|
traceall(p->op, place, cond);
|
|
}
|
|
} else if (exp->op == O_QLINE or
|
|
(exp->op == O_LCON and p->op == O_TRACE and place == nil)) {
|
|
traceinst(p->op, exp, cond);
|
|
} else if (place != nil &&
|
|
((place->op == O_QLINE) || (place->op == O_LCON))) {
|
|
traceat(p->op, exp, place, cond);
|
|
} else {
|
|
if (place == nil)
|
|
place = build(O_SYM, program);
|
|
left = exp;
|
|
if (left->op == O_RVAL or left->op == O_CALL) {
|
|
left = left->value.arg[0];
|
|
}
|
|
if (left->op == O_CPPREF)
|
|
{
|
|
/* dereference the reference, and trace the resulting */
|
|
/* address. */
|
|
eval(left);
|
|
exp->op = O_LCON;
|
|
if ((exp->value.lcon = pop(Address)) == nil)
|
|
error(catgets(scmc_catd, MS_eval, MSG_637,
|
|
"Cannot trace an uninitialized reference variable."));
|
|
trace1(p, exp, place, cond);
|
|
}
|
|
else if (left->op == O_SYM and isblock(left->value.sym)) {
|
|
traceproc(p->op, left->value.sym, exp, place, cond);
|
|
} else {
|
|
tracedata(p->op, exp, place, cond);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process a watch command, translating into appropriate events and actions
|
|
*/
|
|
|
|
public watch (p)
|
|
Node p;
|
|
{
|
|
Node exp, place, cond;
|
|
Node left;
|
|
|
|
exp = p->value.arg[0];
|
|
place = p->value.arg[1];
|
|
cond = p->value.arg[2];
|
|
if (cond != nil)
|
|
chkboolean(cond);
|
|
if (place == nil)
|
|
place = build(O_SYM, program);
|
|
if (place->op == O_QLINE)
|
|
traceat(p->op, exp, place, cond);
|
|
else
|
|
tracedata(p->op, exp, place, cond);
|
|
}
|
|
|
|
/*
|
|
* Set a breakpoint that will turn on tracing.
|
|
*/
|
|
|
|
private traceall(op, place, cond)
|
|
Operator op;
|
|
Node place;
|
|
Node cond;
|
|
{
|
|
Symbol s;
|
|
Node event;
|
|
Command action;
|
|
|
|
if (place == nil) {
|
|
s = program;
|
|
} else {
|
|
s = place->value.sym;
|
|
}
|
|
event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, s));
|
|
action = build(O_PRINTSRCPOS,
|
|
build(O_QLINE, nil, build(O_LCON, (op == O_TRACE) ? 1 : 0)),
|
|
place);
|
|
if (cond != nil) {
|
|
action = build(O_IF, cond, buildcmdlist(action));
|
|
}
|
|
action = build(O_TRACEON, (op == O_TRACEI), buildcmdlist(action));
|
|
action->value.trace.event = addevent(event, buildcmdlist(action));
|
|
action->value.trace.event->op = op;
|
|
action->value.trace.event->cond = cond;
|
|
if (isstdin()) {
|
|
printevent(action->value.trace.event);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set up the appropriate breakpoint for tracing an instruction.
|
|
*/
|
|
|
|
private traceinst(op, exp, cond)
|
|
Operator op;
|
|
Node exp;
|
|
Node cond;
|
|
{
|
|
Node event, wh;
|
|
Command action;
|
|
Event e;
|
|
|
|
if (exp->op == O_LCON) {
|
|
/* Use the filename to distinguish "trace" and "tracei" */
|
|
if (op == O_TRACEI)
|
|
wh = build(O_QLINE, build(O_SCON, nil, 0), exp);
|
|
else
|
|
wh = build(O_QLINE, build(O_SCON, strdup(cursource), 0), exp);
|
|
} else {
|
|
wh = exp;
|
|
}
|
|
if (op == O_TRACEI) {
|
|
event = build(O_EQ, build(O_SYM, pcsym), wh);
|
|
} else {
|
|
event = build(O_EQ, build(O_SYM, linesym), wh);
|
|
}
|
|
action = build(O_PRINTSRCPOS, wh, NULL);
|
|
if (cond) {
|
|
action = build(O_IF, cond, buildcmdlist(action));
|
|
}
|
|
e = addevent(event, buildcmdlist(action));
|
|
e->op = op;
|
|
e->cond = cond;
|
|
if (isstdin()) {
|
|
printevent(e);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set a breakpoint to print an expression at a given line or address.
|
|
*/
|
|
|
|
private traceat(op, exp, place, cond)
|
|
Operator op;
|
|
Node exp;
|
|
Node place;
|
|
Node cond;
|
|
{
|
|
Node event;
|
|
Command action;
|
|
Event e;
|
|
|
|
if (op == O_TRACEI) {
|
|
event = build(O_EQ, build(O_SYM, pcsym), place);
|
|
} else {
|
|
event = build(O_EQ, build(O_SYM, linesym), place);
|
|
}
|
|
action = build(O_PRINTSRCPOS, exp, place);
|
|
if (cond != nil) {
|
|
action = build(O_IF, cond, buildcmdlist(action));
|
|
}
|
|
e = addevent(event, buildcmdlist(action));
|
|
e->op = op;
|
|
e->exp = exp;
|
|
e->cond = cond;
|
|
if (isstdin()) {
|
|
printevent(e);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Construct event for tracing a procedure.
|
|
*
|
|
* What we want here is
|
|
*
|
|
* when $proc = p do
|
|
* if <condition> then
|
|
* printcall;
|
|
* once $pc = $retaddr do
|
|
* printrtn;
|
|
* end;
|
|
* end if;
|
|
* end;
|
|
*
|
|
* Note that "once" is like "when" except that the event
|
|
* deletes itself as part of its associated action.
|
|
*/
|
|
|
|
private traceproc(op, p, exp, place, cond)
|
|
Operator op;
|
|
Symbol p;
|
|
Node exp;
|
|
Node place;
|
|
Node cond;
|
|
{
|
|
Node event;
|
|
Command action;
|
|
Cmdlist actionlist;
|
|
Event e;
|
|
|
|
action = build(O_PRINTCALL, p);
|
|
actionlist = list_alloc();
|
|
cmdlist_append(action, actionlist);
|
|
event = build(O_EQ, build(O_SYM, pcsym), build(O_SYM, retaddrsym));
|
|
action = build(O_ONCE, event, buildcmdlist(build(O_PRINTRTN, p)));
|
|
cmdlist_append(action, actionlist);
|
|
if (cond != nil) {
|
|
actionlist = buildcmdlist(build(O_IF, cond, actionlist));
|
|
}
|
|
event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, p));
|
|
e = addevent(event, actionlist);
|
|
e->op = op;
|
|
e->exp = exp;
|
|
e->cond = cond;
|
|
if (isstdin()) {
|
|
printevent(e);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set up breakpoint for tracing data.
|
|
*/
|
|
|
|
private tracedata (op, exp, place, cond)
|
|
Operator op;
|
|
Node exp;
|
|
Node place;
|
|
Node cond;
|
|
{
|
|
Symbol p;
|
|
Node event;
|
|
Command action;
|
|
|
|
if (size(exp->nodetype) > MAXTRSIZE) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_132,
|
|
"expression too large to trace (limit is %d bytes)"), MAXTRSIZE);
|
|
}
|
|
p = (place == nil) ? tcontainer(exp) : place->value.sym;
|
|
if (p == nil) {
|
|
p = program;
|
|
}
|
|
action = build(O_PRINTIFCHANGED, exp, build(O_LCON,(long) 0));
|
|
if (cond != nil) {
|
|
action = build(O_IF, cond, buildcmdlist(action));
|
|
}
|
|
action = build(O_TRACEON, (op == O_TRACEI && exp->op == O_QLINE),
|
|
buildcmdlist(action));
|
|
event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, p));
|
|
action->value.trace.event = addevent(event, buildcmdlist(action));
|
|
action->value.trace.event->op = op;
|
|
action->value.trace.event->exp = exp;
|
|
action->value.trace.event->cond = cond;
|
|
if (isstdin()) {
|
|
printevent(action->value.trace.event);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Setting and unsetting of stops.
|
|
*/
|
|
|
|
public stop(p)
|
|
Node p;
|
|
{
|
|
Node exp, place, cond;
|
|
|
|
exp = p->value.arg[0];
|
|
cond = p->value.arg[2];
|
|
if (cond != nil)
|
|
chkboolean(cond);
|
|
place = p->value.arg[1];
|
|
|
|
/*
|
|
* Set to NULL so we won't try to free in tfree(). These Nodes are used
|
|
* for printing events, and freed when the event is cleared.
|
|
*/
|
|
p->value.arg[0] = p->value.arg[1] = p->value.arg[2] = NULL;
|
|
if (place != nil)
|
|
{
|
|
/* we must handle the possibility of a C++ multiple function list */
|
|
if (place->nodetype->class != CPPSYMLIST)
|
|
if (place->op != O_SYM && isfunc(place->nodetype))
|
|
stop1(p, exp, build(O_SYM, place->nodetype), cond);
|
|
else
|
|
stop1(p, exp, place, cond);
|
|
else
|
|
{
|
|
cppSymList list = place->nodetype->symvalue.sList;
|
|
for (; list != nil; list = list->next)
|
|
stop1(p, exp, build(O_SYM, list->sym), cond);
|
|
}
|
|
}
|
|
else
|
|
stop1(p, exp, nil, cond);
|
|
}
|
|
|
|
public stop1(p, exp, place, cond)
|
|
Node p, exp, place, cond;
|
|
{
|
|
Node t;
|
|
Symbol s;
|
|
Command action;
|
|
Event e;
|
|
|
|
if (place != nil && place->nodetype->class == MEMBER &&
|
|
place->nodetype->symvalue.member.type == FUNCM)
|
|
{
|
|
error(catgets(scmc_catd, MS_eval, MSG_600,
|
|
"Cannot set a breakpoint in a pure virtual function."));
|
|
}
|
|
/* check and see if "exp" is sometime we can trace or stop */
|
|
else if (exp != nil && exp->nodetype->class == TAG) {
|
|
error(catgets(scmc_catd, MS_eval, MSG_639,
|
|
"Cannot trace or stop a %s."), classname(exp->nodetype));
|
|
}
|
|
|
|
if (exp != nil) {
|
|
stopvar(p->op, exp, place, cond);
|
|
} else {
|
|
action = build(O_STOPX);
|
|
if (cond != nil) {
|
|
action = build(O_IF, cond, buildcmdlist(action));
|
|
}
|
|
if (place == nil or place->op == O_SYM) {
|
|
if (place == nil) {
|
|
s = program;
|
|
} else {
|
|
s = place->value.sym;
|
|
}
|
|
t = build(O_EQ, build(O_SYM, procsym), build(O_SYM, s));
|
|
if (cond != nil) {
|
|
action = build(O_TRACEON, (p->op == O_STOPI),
|
|
buildcmdlist(action));
|
|
e = addevent(t, buildcmdlist(action));
|
|
action->value.trace.event = e;
|
|
} else {
|
|
e = addevent(t, buildcmdlist(action));
|
|
}
|
|
e->op = p->op;
|
|
e->cond = cond;
|
|
if (isstdin()) {
|
|
printevent(e);
|
|
}
|
|
} else {
|
|
stopinst(p->op, place, cond, action);
|
|
}
|
|
}
|
|
}
|
|
|
|
private stopinst(op, place, cond, action)
|
|
Operator op;
|
|
Node place;
|
|
Node cond;
|
|
Command action;
|
|
{
|
|
Node event;
|
|
Event e;
|
|
|
|
if (op == O_STOP) {
|
|
event = build(O_EQ, build(O_SYM, linesym), place);
|
|
} else {
|
|
event = build(O_EQ, build(O_SYM, pcsym), place);
|
|
}
|
|
e = addevent(event, buildcmdlist(action));
|
|
e->op = op;
|
|
e->cond = cond;
|
|
if (isstdin()) {
|
|
printevent(e);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Implement stopping on assignment to a variable by adding it to
|
|
* the variable list.
|
|
*/
|
|
|
|
private stopvar(op, exp, place, cond)
|
|
Operator op;
|
|
Node exp;
|
|
Node place;
|
|
Node cond;
|
|
{
|
|
Symbol p, tracesym;
|
|
Node event;
|
|
Command action;
|
|
|
|
if (size(exp->nodetype) > MAXTRSIZE) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_132,
|
|
"expression too large to trace (limit is %d bytes)"), MAXTRSIZE);
|
|
}
|
|
if (exp->op == O_SYM && isblock(exp->value.sym)) {
|
|
warning( catgets(scmc_catd, MS_eval, MSG_138,
|
|
"%s is an unusual operand for examining modification"),
|
|
symname(exp->value.sym));
|
|
}
|
|
if (place == nil) {
|
|
if (exp->op == O_LCON) {
|
|
p = program;
|
|
} else {
|
|
p = tcontainer(exp);
|
|
if ((p == nil) || ismodule(p)) {
|
|
p = program;
|
|
}
|
|
}
|
|
} else {
|
|
p = place->value.sym;
|
|
}
|
|
action = build(O_STOPIFCHANGED, exp, cond);
|
|
|
|
if (place && place->op != O_SYM)
|
|
{
|
|
if (op == O_STOP)
|
|
event = build(O_EQ, build(O_SYM, linesym), place);
|
|
else
|
|
event = build(O_EQ, build(O_SYM, pcsym), place);
|
|
|
|
/* Find variable being traced */
|
|
if (exp && exp->op == O_RVAL)
|
|
tracesym = exp->value.arg[0]->value.sym;
|
|
else if (exp && exp->op == O_SYM)
|
|
tracesym = exp->value.sym;
|
|
|
|
/* if the variable is active */
|
|
if (isvariable(tracesym) && tracesym != program
|
|
&& isactive(container(tracesym)))
|
|
{
|
|
/* save the 'initial' value */
|
|
initialize_trinfo(exp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
action = build(O_TRACEON, (op == O_STOPI), buildcmdlist(action));
|
|
event = build(O_EQ, build(O_SYM, procsym), build(O_SYM, p));
|
|
}
|
|
action->value.trace.event = addevent(event, buildcmdlist(action));
|
|
action->value.trace.event->op = op;
|
|
action->value.trace.event->exp = exp;
|
|
action->value.trace.event->cond = cond;
|
|
if (isstdin()) {
|
|
printevent(action->value.trace.event);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NAME: assign
|
|
*
|
|
* FUNCTION: assign the value of an expression to a variable
|
|
* (or term).
|
|
*
|
|
* PARAMETERS:
|
|
* var - Node containing variable
|
|
* exp - Node containing expression
|
|
*
|
|
* RECOVERY OPERATION: NONE NEEDED
|
|
*
|
|
* DATA STRUCTURES: NONE
|
|
*
|
|
* RETURNS: nothing
|
|
*/
|
|
|
|
extern Boolean call_command;
|
|
|
|
public assign (var, exp)
|
|
Node var;
|
|
Node exp;
|
|
{
|
|
Address addr, tempaddr;
|
|
int r, varsize, expsize;
|
|
char c;
|
|
short s, bad_allign;
|
|
int i;
|
|
LongLong l;
|
|
union {
|
|
float f;
|
|
double d;
|
|
Word w[2];
|
|
} u;
|
|
Boolean bitfield = false;
|
|
unsigned int bitoffset, bitflength, bitfval, bitshift;
|
|
unsigned int bitexpval = 0;
|
|
Symbol svar, sexp, t;
|
|
Boolean padvar, padexp;
|
|
Stack *oldsp;
|
|
extern boolean subarray_seen;
|
|
|
|
/* if we are assigning to a c++ constant, then we put out an */
|
|
/* error message to indicate that this is not allowed. */
|
|
|
|
if (var->nodetype->language == cppLang &&
|
|
!varIsSet("$unsafeassign") &&
|
|
var->nodetype->type->isConst)
|
|
{
|
|
error(catgets(scmc_catd, MS_eval, MSG_601,
|
|
"Cannot assign a value to a const variable."));
|
|
}
|
|
|
|
#ifdef K_THREADS
|
|
/* if we are assigning a $ti (thread symbol ) with kernel threads */
|
|
/* error message to indicate that this is not allowed. */
|
|
if (
|
|
!varIsSet("$unsafeassign") &&
|
|
var->nodetype->isConst)
|
|
{
|
|
error(catgets(scmc_catd, MS_eval, MSG_601,
|
|
"Cannot assign a value to a const variable."));
|
|
}
|
|
#endif /* K_THREADS */
|
|
/* if we are assigning to or from a variable that is a c++ */
|
|
/* reference find the size of the real variable not the size of */
|
|
/* the reference. */
|
|
/* the dereferencing will occur naturally in evaluation of the */
|
|
/* symbols. */
|
|
|
|
t = rtype(var->nodetype);
|
|
/* if we have a subarray [..] */
|
|
if (t->class == RANGE && subarray_seen) {
|
|
/* output an error message */
|
|
error(catgets(scmc_catd, MS_eval, MSG_638,
|
|
"Cannot assign to a subarray of variables."));
|
|
/* if we have a ptr to member */
|
|
} else if (t->class == PTRTOMEM) {
|
|
/* output an error message */
|
|
error(catgets(scmc_catd, MS_eval, MSG_602,
|
|
"Cannot assign to a pointer to member variable."));
|
|
}
|
|
else {
|
|
varsize = size(var->nodetype);
|
|
}
|
|
if (isconst(exp->nodetype))
|
|
expsize = size(exp->nodetype->symvalue.constval->nodetype);
|
|
else
|
|
expsize = size(exp->nodetype);
|
|
|
|
/* account for fortran padded type when AUTODBL is on */
|
|
if ((svar = var->nodetype)->class == VAR) svar=svar->type;
|
|
if ((sexp = exp->nodetype)->class == VAR) sexp=sexp->type;
|
|
padvar = is_fortran_padded(svar);
|
|
padexp = is_fortran_padded(sexp);
|
|
if ((padvar || padexp) && !(padvar && padexp)) {
|
|
if (padvar)
|
|
varsize = svar->symvalue.field.length;
|
|
else
|
|
expsize = sexp->symvalue.field.length;
|
|
}
|
|
|
|
svar = rtype(var->nodetype);
|
|
sexp = rtype(exp->nodetype);
|
|
if (var->op == O_SYM) {
|
|
r = regnum(var->value.sym);
|
|
if (r != -1) {
|
|
eval(exp);
|
|
if (varsize == sizeof(double)) {
|
|
if (expsize == sizeof(char)) {
|
|
u.d = (double) pop(char);
|
|
} else if (expsize == sizeof(short)) {
|
|
u.d = (double) pop(short);
|
|
} else if (expsize == sizeof(float)) {
|
|
u.d = pop(float);
|
|
} else if (expsize == sizeof(double)) {
|
|
u.d = pop(double);
|
|
} else {
|
|
sp -= expsize;
|
|
error( catgets(scmc_catd, MS_check, MSG_1,
|
|
"incompatible types"));
|
|
}
|
|
if (r < (NGREG+NSYS)) {
|
|
setreg(r, u.w[0]);
|
|
setreg(r+1, u.w[1]);
|
|
}
|
|
else {
|
|
setreg(r, u.w[0]);
|
|
setreg(-r, u.w[1]);
|
|
}
|
|
} else {
|
|
if (expsize == sizeof(char)) {
|
|
u.f = (Word) pop(char);
|
|
} else if (expsize == sizeof(short)) {
|
|
u.f = (Word) pop(short);
|
|
} else if (expsize == sizeof(double)) {
|
|
if (sexp->class == REAL)
|
|
u.f = pop(double);
|
|
else
|
|
u.w[0] = (Word) pop (LongLong);
|
|
} else if (expsize == sizeof(Word)) {
|
|
u.w[0] = pop(Word);
|
|
} else {
|
|
sp -= expsize;
|
|
error( catgets(scmc_catd, MS_check, MSG_1,
|
|
"incompatible types"));
|
|
}
|
|
setreg(r, u.w[0]);
|
|
}
|
|
assign_addr = NOADDR;
|
|
action_mask |= ASSIGNMENT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* lval() should never change sp, workaround here for assignment */
|
|
/* with casting, e.g. casting struct to int for assignment. */
|
|
oldsp = sp;
|
|
addr = lval(var);
|
|
sp = oldsp;
|
|
|
|
assign_addr = addr;
|
|
assign_size = varsize;
|
|
/* reset pointer to member flag that*/
|
|
/* was set during the evaluation of */
|
|
/* the variable or expression */
|
|
specificptrtomember = false;
|
|
|
|
/* adjustment for length bytes of pascal STRING */
|
|
/* and put in new length bytes */
|
|
if ((svar->class == STRING) and (sexp->class != STRING)) {
|
|
s = (short) expsize;
|
|
/* account for the null end of C string */
|
|
if (istypename(sexp->type,"$char")) s--;
|
|
dwrite((char *) &s, addr, sizeof(short));
|
|
addr = addr + 2;
|
|
}
|
|
|
|
if (bitfield = isbitfield(var->nodetype)) {
|
|
dread(&bitfval, addr, sizeof(Word));
|
|
/* Clear out the assignee bits so that they may be ored in later */
|
|
bitoffset = var->nodetype->symvalue.field.offset % BITSPERBYTE;
|
|
bitflength = var->nodetype->symvalue.field.length;
|
|
bitshift = BITSPERWORD - bitoffset - bitflength;
|
|
bitfval &= ~(((1 << bitflength) - 1) << bitshift);
|
|
}
|
|
eval(exp);
|
|
|
|
if (svar->class == PACKRANGE)
|
|
{
|
|
sp -= expsize;
|
|
if (expsize <= varsize)
|
|
{
|
|
dwrite(sp, addr, expsize);
|
|
}
|
|
else
|
|
{
|
|
dwrite(sp+(expsize-varsize),addr,varsize);
|
|
}
|
|
}
|
|
else if ((var->nodetype->type->class==PTR)
|
|
&& (exp->nodetype->class==ARRAY) &&
|
|
ischartype(var->nodetype->type) &&
|
|
!strcmp(exp->nodetype->type->name->identifier,"$char"))
|
|
{ /* This section is for assigning strings */
|
|
call_command = true; /* to character pointers. */
|
|
if ( bad_allign = 4 - (expsize % 4))
|
|
{
|
|
for (i = 0; i < bad_allign; i++)
|
|
{
|
|
*sp++ = '\0';
|
|
expsize++;
|
|
}
|
|
}
|
|
tempaddr = debugee_malloc((unsigned int)expsize); /* get new memory */
|
|
/* Check for bad return code here ..... */
|
|
if (tempaddr == 0) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_139,
|
|
"Unable to allocate memory for character pointer"));
|
|
return;
|
|
}
|
|
dwrite((char *)&tempaddr, addr, sizeof(tempaddr)); /* assign new mem. */
|
|
sp -= expsize;
|
|
dwrite(sp, tempaddr, expsize); /* assign string */
|
|
call_command = false;
|
|
} else if (isspecialpic(svar) && !ispic(sexp)) {
|
|
cobolassign(var, exp);
|
|
} else if (bitfield) {
|
|
/* Value to be changed is left-justified at the byte level.
|
|
* The expression is left justified as well, but it can be of any
|
|
* length. So, what to do? An easy, though not necessarily optimal
|
|
* method is to change the expression to be right-justified in a word,
|
|
* shifting the expression over the appropriate number of bits, masking
|
|
* off the excess bits, then or'ing in the new value.
|
|
*/
|
|
sp -= expsize;
|
|
/* Get value, right-justified */
|
|
for (i = 0; i < expsize; i++)
|
|
bitexpval = (bitexpval << BITSPERBYTE) | sp[i];
|
|
/* Mask off excess bits, and shift into position */
|
|
bitexpval = (bitexpval & ((1 << bitflength) - 1)) << bitshift;
|
|
/* Or in the new value, and write it out. Done. */
|
|
bitfval |= bitexpval;
|
|
dwrite(&bitfval, addr, sizeof(Word));
|
|
}
|
|
else if ((svar->class == COMPLEX) && (sexp->class == COMPLEX))
|
|
{
|
|
double real_pad = 0.0, real_exp;
|
|
double imag_pad = 0.0, imag_exp;
|
|
float real_var, imag_var;
|
|
|
|
switch (expsize)
|
|
{
|
|
case sizeof (double) :
|
|
imag_exp = (double) pop(float);
|
|
real_exp = (double) pop(float);
|
|
break;
|
|
|
|
case 2 * sizeof (double) :
|
|
imag_exp = pop(double);
|
|
real_exp = pop(double);
|
|
break;
|
|
|
|
case 4 * sizeof (double) :
|
|
imag_pad = pop(double);
|
|
imag_exp = pop(double);
|
|
real_pad = pop(double);
|
|
real_exp = pop(double);
|
|
break;
|
|
}
|
|
|
|
switch (varsize)
|
|
{
|
|
case sizeof (double) :
|
|
real_var = (float) real_exp;
|
|
imag_var = (float) imag_exp;
|
|
|
|
dwrite((char *)&real_var, addr, sizeof(float));
|
|
dwrite((char *)&imag_var, addr + sizeof (float),
|
|
sizeof(float));
|
|
break;
|
|
|
|
case 2 * sizeof (double) :
|
|
dwrite((char *) &real_exp, addr, sizeof (double));
|
|
dwrite((char *) &imag_exp, addr + sizeof (double),
|
|
sizeof (double));
|
|
break;
|
|
|
|
case 4 * sizeof (double) :
|
|
dwrite((char *) &real_exp, addr, sizeof (double));
|
|
dwrite((char *) &real_pad, addr + sizeof(double),
|
|
sizeof (double));
|
|
dwrite((char *) &imag_exp, addr + 2 * sizeof (double),
|
|
sizeof (double));
|
|
dwrite((char *) &imag_pad, addr + 3 * sizeof (double),
|
|
sizeof (double));
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int rc;
|
|
char *save_sp;
|
|
|
|
expsize = size(sexp);
|
|
|
|
save_sp = sp - expsize;
|
|
|
|
rc = convert(save_sp, sexp, save_sp, svar);
|
|
if (rc == 0)
|
|
{
|
|
dwrite(save_sp, addr, varsize);
|
|
sp = save_sp;
|
|
}
|
|
else
|
|
{
|
|
sp -= expsize;
|
|
if (expsize <= varsize)
|
|
{
|
|
if (initializestring)
|
|
{
|
|
initializestring = 0;
|
|
for (i=0;i<varsize-expsize;i++)
|
|
*(sp+expsize+i)='\0';
|
|
expsize=varsize;
|
|
}
|
|
dwrite(sp, addr, expsize);
|
|
}
|
|
else
|
|
{
|
|
dwrite(sp, addr, varsize);
|
|
}
|
|
}
|
|
}
|
|
action_mask |= ASSIGNMENT;
|
|
}
|
|
|
|
/*
|
|
* Set a debugger variable.
|
|
*/
|
|
|
|
private set (var, exp)
|
|
Node var, exp;
|
|
{
|
|
Symbol t;
|
|
|
|
if (var == nil) {
|
|
defvar(nil, nil);
|
|
} else if (exp == nil) {
|
|
defvar(var->value.name, nil);
|
|
} else if (var->value.name == identname("$frame", true)) {
|
|
t = exp->nodetype;
|
|
if (not compatible(t, t_int) and not compatible(t, t_addr)) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_140,
|
|
"$frame must be an address"));
|
|
}
|
|
eval(exp);
|
|
getnewregs(pop(Address));
|
|
} else {
|
|
defvar(var->value.name, unrval(exp));
|
|
}
|
|
}
|
|
|
|
extern Lineno lastlinenum;
|
|
/*
|
|
* Execute a list command.
|
|
*/
|
|
|
|
private list (p)
|
|
Node p;
|
|
{
|
|
Symbol f;
|
|
Address addr;
|
|
Lineno line, l1, l2;
|
|
Node windownode;
|
|
|
|
if (p->value.arg[0]->op == O_LCON) {
|
|
eval(p->value.arg[0]);
|
|
l1 = (Lineno) (pop(long));
|
|
eval(p->value.arg[1]);
|
|
l2 = (Lineno) (pop(long));
|
|
if ((l1 < 0) || (l2 < 0)) {
|
|
beginerrmsg();
|
|
(*rpt_error)(stderr, catgets(scmc_catd, MS_eval, MSG_143,
|
|
"line numbers must be positive\n"));
|
|
return;
|
|
}
|
|
if (l2 == 0) {
|
|
if (l1 == 0 && p->op != O_MOVE) { /* No line numbers specified */
|
|
windownode = findvar(identname("$listwindow",true));
|
|
if (windownode == nil)
|
|
windowsize = 10;
|
|
else {
|
|
eval(windownode);
|
|
windowsize = pop(integer);
|
|
}
|
|
l1 = cursrcline;
|
|
l2 = cursrcline + windowsize - 1;
|
|
}
|
|
else { /* Only 1st line specified */
|
|
l2 = l1;
|
|
}
|
|
} /* No ending line specified */
|
|
}
|
|
else {
|
|
f = p->value.arg[0]->nodetype;
|
|
|
|
if (nosource(f)) {
|
|
error(catgets(scmc_catd, MS_eval, MSG_141,
|
|
"no source lines for \"%s\""), symname(f));
|
|
}
|
|
addr = firstline(f);
|
|
if (addr == NOADDR) {
|
|
error(catgets(scmc_catd, MS_eval, MSG_141,
|
|
"no source lines for \"%s\""), symname(f));
|
|
}
|
|
setsource(srcfilename(addr));
|
|
line = srcline(addr);
|
|
getsrcwindow(line, &l1, &l2);
|
|
}
|
|
if (p->op != O_MOVE)
|
|
printlines(l1, l2);
|
|
else {
|
|
/*
|
|
* If both operands are the same, clear one so we don't try to free them
|
|
* both
|
|
*/
|
|
if( p->value.arg[0] == p->value.arg[1] ) {
|
|
p->value.arg[1] = NULL;
|
|
}
|
|
|
|
if( canReadSource() == false || lastlinenum == 0 ) {
|
|
beginerrmsg();
|
|
(*rpt_error)(stderr, catgets(scmc_catd, MS_eval, MSG_145,
|
|
"No file to move within\n"));
|
|
return;
|
|
} else if ((l1 <= 0) || (l2 > lastlinenum)) {
|
|
beginerrmsg();
|
|
(*rpt_error)(stderr, catgets(scmc_catd, MS_eval, MSG_146,
|
|
"Line specified is not within range of %s\n"),
|
|
basefile(cursource));
|
|
return;
|
|
}
|
|
if( isXDE ) {
|
|
/*
|
|
* The DPI debuggers expect cursrcline to be the line one past the
|
|
* line moved to
|
|
*/
|
|
cursrcline = l1 + 1;
|
|
} else {
|
|
cursrcline = l1;
|
|
}
|
|
action_mask |= ELISTING;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Execute a listi command.
|
|
*/
|
|
|
|
private listi (p)
|
|
Node p;
|
|
{
|
|
static Address old_pc;
|
|
Address addr1, addr2;
|
|
Node windownode;
|
|
Symbol f;
|
|
|
|
#ifdef KDBX
|
|
is_diss = 1;
|
|
#endif /* KDBX */
|
|
/*
|
|
* Get current value of $listwindow to determine how many instructions
|
|
* to display
|
|
*/
|
|
windownode = findvar(identname("$listwindow",true));
|
|
if (windownode == nil) {
|
|
inst_windowsize = 10;
|
|
} else {
|
|
eval(windownode);
|
|
inst_windowsize = pop(integer);
|
|
}
|
|
|
|
if (p->value.arg[0]->op == O_SYM)
|
|
{
|
|
f = p->value.arg[0]->nodetype;
|
|
addr1 = firstline(f);
|
|
if (addr1 == NOADDR) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_141,
|
|
"no source lines for \"%s\""), symname(f));
|
|
}
|
|
printninst(inst_windowsize,addr1);
|
|
}
|
|
else
|
|
{
|
|
eval(p->value.arg[0]);
|
|
addr1 = (Address) (pop(long));
|
|
eval(p->value.arg[1]);
|
|
addr2 = (Address) (pop(long));
|
|
if (addr2 == 0)
|
|
{
|
|
if (addr1 == 0) /* No line numbers specified */
|
|
{
|
|
if (prtaddr && (old_pc == pc))
|
|
printninst(inst_windowsize,prtaddr);
|
|
else
|
|
printninst(inst_windowsize,pc);
|
|
}
|
|
else /* Only 1st line specified */
|
|
{
|
|
printninst(inst_windowsize,addr1);
|
|
}
|
|
} /* end if no ending line specified */
|
|
else
|
|
{
|
|
if (addr1 == 0) /* print assembly code given source line # */
|
|
{
|
|
addr1 = objaddr(addr2,cursource);
|
|
if (addr1 == NOADDR) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_149,
|
|
"No assembly code for that source line number"));
|
|
}
|
|
printninst(inst_windowsize,addr1);
|
|
}
|
|
else if (addr2 < addr1)
|
|
{
|
|
beginerrmsg();
|
|
(*rpt_error)(stderr, catgets(scmc_catd, MS_eval, MSG_144,
|
|
"second number must be greater than first\n"));
|
|
return;
|
|
}
|
|
else /* Both lines specified */
|
|
{
|
|
if ((addr2 - addr1) < inst_windowsize)
|
|
printninst(inst_windowsize,addr1);
|
|
else
|
|
printinst(addr1,addr2);
|
|
}
|
|
}
|
|
}
|
|
old_pc = pc;
|
|
}
|
|
|
|
/*
|
|
* Execute a registers command to print out the registers in readable form.
|
|
*/
|
|
|
|
registers () {
|
|
int p,i;
|
|
char **real_regnames; /* Used to point to the list of
|
|
* registers to be displayed;
|
|
* This defaults to using
|
|
* regnames
|
|
*/
|
|
|
|
setregs(process);
|
|
|
|
/* if running on a 601 */
|
|
if (current_hardware & UNQ_601)
|
|
real_regnames = regnames_601;
|
|
|
|
/* else if running on some other powerpc machine */
|
|
else if (current_hardware & PPC)
|
|
real_regnames = regnames_ppc;
|
|
|
|
else
|
|
real_regnames = regnames;
|
|
|
|
for (p=0; p < NGREG;) {
|
|
for (i=0;i<4;i++) {
|
|
(*rpt_output)(stdout, "%5s: ", real_regnames[p]);
|
|
(*rpt_output)(stdout, "0x%08x ", reg(p));
|
|
p++;
|
|
}
|
|
(*rpt_output)(stdout, "\n");
|
|
}
|
|
for (p = NGREG; p < NGREG+NSYS-FPSTATREG;) {
|
|
for (i=0;i<4;i++) {
|
|
if( real_regnames[p] == NULL ) {
|
|
p++;
|
|
} else {
|
|
if (p >= NGREG+NSYS-FPSTATREG) {
|
|
(*rpt_output)(stdout, "\n");
|
|
break;
|
|
}
|
|
(*rpt_output)(stdout, "%5s: ", real_regnames[p]);
|
|
(*rpt_output)(stdout, "0x%08x ", reg(p));
|
|
p++;
|
|
}
|
|
}
|
|
(*rpt_output)(stdout, "\n");
|
|
}
|
|
printcond();
|
|
if (!varIsSet("$noflregs"))
|
|
prflregs();
|
|
else
|
|
(*rpt_output)(stdout, catgets(scmc_catd, MS_eval, MSG_545,
|
|
"\t[unset $noflregs to view floating point registers]"));
|
|
(*rpt_output)(stdout, "\n");
|
|
getpc(&pc);
|
|
printloc();
|
|
(*rpt_output)(stdout, "\n");
|
|
prtaddr = printop(pc,0);
|
|
}
|
|
|
|
/*
|
|
* Print out the values of the floating point registers.
|
|
*/
|
|
prflregs()
|
|
{
|
|
int i;
|
|
switch(fpregs) {
|
|
case 32:
|
|
for (i = 0; i <= fpregs; ) {
|
|
(*rpt_output)(stdout, "%s$fr%d:", (i>9)?" ":" ",i);
|
|
prhexflt(fpregval(i++),0);
|
|
(*rpt_output)(stdout, "%s$fr%d:", (i>9)?" ":" ",i);
|
|
prhexflt(fpregval(i++),0);
|
|
if (i == fpregs) {
|
|
#ifdef FPSCR
|
|
(*rpt_output)(stdout, " $fpscr: ");
|
|
(*rpt_output)(stdout, "0x%08x ", reg(SYSREGNO(FPSCR)));
|
|
i++;
|
|
#else
|
|
(*rpt_output)(stdout, " $fst: ");
|
|
prhexflt(fpregval(i++),1);
|
|
#endif
|
|
}
|
|
else {
|
|
(*rpt_output)(stdout, "%s$fr%d: ", (i>9)?" ":" ",i);
|
|
prhexflt(fpregval(i++),0);
|
|
}
|
|
(*rpt_output)(stdout, "\n");
|
|
}
|
|
break;
|
|
case 7:
|
|
case 8:
|
|
default:
|
|
(*rpt_output)(stdout, "\n");
|
|
for (i=0;i<fpregs;i++) {
|
|
(*rpt_output)(stdout, " $fr%d: ", i);
|
|
prhexflt(fpregval(i),0);
|
|
if ((i+1) % 3 == 0)
|
|
(*rpt_output)(stdout, "\n");
|
|
}
|
|
(*rpt_output)(stdout, " $fst: ");
|
|
prhexflt(fpregval(i),1);
|
|
(*rpt_output)(stdout, "\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
prhexflt(d,stat_reg)
|
|
double d;
|
|
int stat_reg;
|
|
{
|
|
int *dptr = (int *)&d;
|
|
(*rpt_output)(stdout, "0x%8.8x",*dptr++);
|
|
if (!stat_reg)
|
|
(*rpt_output)(stdout, "%8.8x",*dptr);
|
|
}
|
|
|
|
printpc(pc)
|
|
Address pc;
|
|
{
|
|
printinst(pc,pc);
|
|
}
|
|
|
|
/*
|
|
* Execute a func command.
|
|
*/
|
|
|
|
private func (p)
|
|
Node p;
|
|
{
|
|
Symbol s, f;
|
|
Address addr;
|
|
extern boolean heat_shrunk;
|
|
struct Frame frp;
|
|
|
|
if (p == nil) {
|
|
if (isinline(curfunc))
|
|
(*rpt_output)(stdout, "unnamed block ");
|
|
/* if this is a heat shrunk executable and the function
|
|
name is found in the traceback table */
|
|
if (heat_shrunk && (getcurframe(&frp) == 0)
|
|
&& (frp.tb.name_present && frp.name))
|
|
{
|
|
/* print the function name found in the traceback table */
|
|
(*rpt_output)(stdout, "%s", frp.name);
|
|
}
|
|
else
|
|
printname( rpt_output, stdout, curfunc, false);
|
|
(*rpt_output)(stdout, "\n" );
|
|
} else {
|
|
s = p->nodetype;
|
|
if (isroutine(s)) {
|
|
setcurfunc(s);
|
|
} else {
|
|
find(f, s->name) where isroutine(f) endfind(f);
|
|
if (f == nil) {
|
|
error( catgets(scmc_catd, MS_eval, MSG_180,
|
|
"%s is not a procedure or function"), symname(s));
|
|
}
|
|
setcurfunc(f);
|
|
}
|
|
addr = codeloc(curfunc);
|
|
if (addr != NOADDR) {
|
|
if (!nosource(curfunc)) {
|
|
setsource(srcfilename(addr));
|
|
cursrcline = srcline(addr);
|
|
if( isXDE ) {
|
|
/*
|
|
* The DPI debuggers expect cursrcline to be one past the
|
|
* first line of the function
|
|
*/
|
|
cursrcline++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the program counter to be at a particular place in the code.
|
|
*/
|
|
extern Boolean never_ran, noexec;
|
|
public changepc (p)
|
|
Node p;
|
|
{
|
|
Address goaddr;
|
|
String fn;
|
|
long ln;
|
|
Symbol oldfunc;
|
|
|
|
if (noexec)
|
|
error( catgets(scmc_catd, MS_eval, MSG_181,
|
|
"program is not executable"));
|
|
oldfunc = curfunc;
|
|
if (p->value.arg[0]->op == O_QLINE) {
|
|
fn = p->value.arg[0]->value.arg[0]->value.scon;
|
|
ln = p->value.arg[0]->value.arg[1]->value.lcon;
|
|
goaddr = objaddr(ln, fn);
|
|
} else {
|
|
/*
|
|
Commented this out to give gotoi more capabilities.
|
|
goaddr = (Address) p->value.arg[0]->value.lcon;
|
|
*/
|
|
eval(p->value.arg[0]);
|
|
goaddr = pop(long);
|
|
}
|
|
if (goaddr != NOADDR) {
|
|
setcurfunc(whatblock(goaddr));
|
|
if ((curfunc != oldfunc) && (!(varIsSet("$unsafegoto")))) {
|
|
curfunc = oldfunc;
|
|
error( catgets(scmc_catd, MS_eval, MSG_782,
|
|
"Goto address is not within current function or block. \
|
|
(set $unsafegoto to override)"));
|
|
} else {
|
|
setreg(SYSREGNO(PROGCTR), pc = goaddr);
|
|
getsrcpos();
|
|
action_mask |= EXECUTION;
|
|
action_mask &= ~CONTEXT_CHANGE;
|
|
action_mask &= ~LISTING;
|
|
action_mask &= ~ELISTING;
|
|
printstatus();
|
|
}
|
|
} else {
|
|
beginerrmsg();
|
|
(*rpt_error)(stderr, catgets(scmc_catd, MS_events,
|
|
MSG_101, "no executable code at line "));
|
|
(*rpt_error)(stderr, "%d", ln);
|
|
enderrmsg();
|
|
}
|
|
}
|
|
/*
|
|
* Send a message to the current support person.
|
|
*/
|
|
|
|
public gripe()
|
|
{
|
|
# ifdef MAINTAINER
|
|
typedef Operation();
|
|
Operation *old;
|
|
int pid, status;
|
|
extern int versionNumber;
|
|
char subject[100];
|
|
|
|
puts("Type control-D to end your message. Be sure to include");
|
|
puts("your name and the name of the file you are debugging.");
|
|
(*rpt_output)(stdout, "\n" );
|
|
old = signal(SIGINT, SIG_DFL);
|
|
sprintf(subject, "dbx (version 3.%d) gripe", versionNumber);
|
|
pid = back("Mail", stdin, stdout, "-s", subject, MAINTAINER, nil);
|
|
signal(SIGINT, SIG_IGN);
|
|
pwait(pid, &status);
|
|
signal(SIGINT, old);
|
|
if (status == 0) {
|
|
puts("Thank you.");
|
|
} else {
|
|
puts("\nMail not sent.");
|
|
}
|
|
# else
|
|
puts("Sorry, no dbx maintainer available to gripe to.");
|
|
puts("Try contacting your system manager.");
|
|
# endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Divert output to the given file name.
|
|
* Cannot redirect to an existing file.
|
|
*/
|
|
|
|
private int so_fd;
|
|
Boolean notstdout;
|
|
extern enum redirect { CREATE, APPEND, OVERWRITE } stdout_mode;
|
|
|
|
public setout(filename)
|
|
String filename;
|
|
{
|
|
File f;
|
|
int oflag;
|
|
|
|
switch (stdout_mode)
|
|
{
|
|
case CREATE:
|
|
f = fopen(filename, "r");
|
|
oflag = O_RDWR | O_CREAT | O_TRUNC;
|
|
break;
|
|
case APPEND:
|
|
f = fopen(filename, "a");
|
|
oflag = O_RDWR | O_CREAT | O_APPEND;
|
|
break;
|
|
case OVERWRITE:
|
|
f = fopen(filename, "w");
|
|
oflag = O_RDWR | O_CREAT | O_TRUNC;
|
|
break;
|
|
}
|
|
if ((f != nil) && (stdout_mode == CREATE)) {
|
|
fclose(f);
|
|
error( catgets(scmc_catd, MS_eval, MSG_210,
|
|
"%s: file already exists, use \">!\""), filename);
|
|
} else {
|
|
so_fd = dup(1);
|
|
close(1);
|
|
if (open(filename, oflag, 0664) == -1) {
|
|
unsetout();
|
|
error( catgets(scmc_catd, MS_eval, MSG_211,
|
|
"cannot create %s"), filename);
|
|
}
|
|
notstdout = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NAME: screen
|
|
*
|
|
* FUNCTION: set up an Xwindow for screen subcommand or
|
|
* for child process when in multprocess debugging.
|
|
*
|
|
* PARAMETERS:
|
|
* is_screen: input parameter,
|
|
* 1 for screen subcommand, 0 for multprocess debugging.
|
|
*
|
|
* RECOVERY OPERATION: NONE NEEDED
|
|
*
|
|
* DATA STRUCTURES: NONE
|
|
*
|
|
* RETURNS: -1 when not able to setup an Xwindow.
|
|
*
|
|
The following diagram is the processes relationship for dbx multprocess
|
|
debugging. Single line indicates parent-child relationship, double line
|
|
indicates debugger-debuggee relationship, triple line indicates both of
|
|
the above relationship.
|
|
|
|
originating dbx --------------- child dbx
|
|
fork a child fork a child
|
|
then exec debuggee then exec aixterm
|
|
||| |
|
|
||| |
|
|
||| |
|
|
||| dbx
|
|
||| ||
|
|
||| ||
|
|
||| ||
|
|
debuggee ----------------- child debuggee
|
|
|
|
|
|
The following diagram is the processes relationship for dbx subcommand
|
|
screen.
|
|
|
|
originating dbx --------------- child dbx
|
|
fork a child exec aixterm then
|
|
then exec debuggee redir dbx I/O to this aixterm
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
debuggee
|
|
*/
|
|
public int screen(is_screen)
|
|
Boolean is_screen;
|
|
{
|
|
Node p;
|
|
int oldfd, fd, this_pid, fork_return;
|
|
char name_buffer[23];
|
|
char *tmp_pipe_name = &name_buffer[0];
|
|
int pid_open_tty = 0; /* pid of the process which will open an Xwindow */
|
|
|
|
/* NOTE : mktemp modifies the input parameter - therefore, we
|
|
cannot pass "/tmp/dbxtmppipe.XXXXXX" directly */
|
|
strcpy (tmp_pipe_name, "/tmp/dbxtmppipe.XXXXXX");
|
|
tmp_pipe_name = mktemp(tmp_pipe_name);
|
|
|
|
if(mknod(tmp_pipe_name, _S_IFIFO | S_IREAD | S_IWRITE, 0) == -1)
|
|
{
|
|
perror("dbx: mknod()");
|
|
return(-1);
|
|
}
|
|
oldfd = dup(0);
|
|
|
|
/* For the screen command, the child should create the new XWindow.
|
|
For the multproc command, the parent should. */
|
|
|
|
/* NOTE : fork returns 0 to the child process and the child's process
|
|
id to the parent process */
|
|
|
|
this_pid = getpid();
|
|
fork_return = fork();
|
|
|
|
/* if this is the screen command and the parent */
|
|
if (is_screen && (fork_return > 0))
|
|
pid_open_tty = ScrPid = fork_return;
|
|
|
|
/* else if this is multproc and the child */
|
|
else if (!is_screen && (fork_return == 0))
|
|
pid_open_tty = this_pid;
|
|
|
|
/* if this is the screen command and the child or
|
|
multproc and the parent */
|
|
if(pid_open_tty == 0)
|
|
{
|
|
char *sp;
|
|
char *pp[20]; /* store arguments to the command which open Xwindow */
|
|
char *term_name;
|
|
int z = 0;
|
|
|
|
term_name = getenv("TERM");
|
|
|
|
pp[z++] = (term_name == NULL) ? "aixterm" : term_name;
|
|
|
|
if(!is_screen) {
|
|
sp=(char *)(malloc(16)); /* 16 chars is big enough to hold
|
|
the int typed newdebuggee in ascii
|
|
plus the string "dbx " and null char */
|
|
sprintf(sp,"dbx %d",newdebuggee);
|
|
pp[z++]= "-T";
|
|
pp[z++] = sp;
|
|
pp[z++]= "-n";
|
|
pp[z++] = sp;
|
|
}
|
|
p = findvar(identname("$xdisplay", false));
|
|
|
|
if (p != NULL)
|
|
{
|
|
pp[z++] = "-display";
|
|
pp[z++] = p->value.scon;
|
|
}
|
|
|
|
pp[z++] = "-name";
|
|
pp[z++] = "dbx_xterm";
|
|
pp[z++]= "-e";
|
|
pp[z++]= graphical_debugger;
|
|
pp[z++]= "-b";
|
|
pp[z++] = tmp_pipe_name;
|
|
pp[z] = NULL;
|
|
execvp(pp[0],pp);
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
char name_tty[30];
|
|
char *sp;
|
|
fd = open(tmp_pipe_name, O_RDWR);
|
|
fcntl(fd, F_SETFL, O_NDELAY);
|
|
|
|
while(1) {
|
|
if(getpri(pid_open_tty) < 0) {
|
|
/* fail to open Xwindow */
|
|
if(is_screen)
|
|
warning(catgets(scmc_catd, MS_eval, MSG_712,
|
|
"dbx subcommand screen fails. dbx continued\n"));
|
|
else
|
|
warning(catgets(scmc_catd, MS_eval, MSG_711,
|
|
"dbx multproc fails. dbx continued with multproc disabled\n"));
|
|
close(fd);
|
|
remove(tmp_pipe_name);
|
|
return(-1);
|
|
}
|
|
if(read(fd,name_tty,30) > 0) {
|
|
/* Xwindow is open successfully */
|
|
close(fd);
|
|
remove(tmp_pipe_name);
|
|
break;
|
|
}
|
|
}
|
|
fd = open(name_tty, O_RDWR);
|
|
/* restore the tty to the originating tty */
|
|
ioctl(fd, TCSETA, &(ttyinfo.ttyinfo));
|
|
(void) fcntl(fd, F_SETFL, ttyinfo.fcflags);
|
|
close(0); /* reset fd 0, 1, & 2 */
|
|
dup(fd);
|
|
close(1);
|
|
dup(0);
|
|
close(2);
|
|
dup(0);
|
|
close(fd);
|
|
if(is_screen) ScrUsed = true;
|
|
return oldfd;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Revert output to standard output.
|
|
*/
|
|
|
|
public unsetout()
|
|
{
|
|
/* flush if stdout is not closed */
|
|
if (fcntl(stdout->_file, F_GETFL) != -1)
|
|
fflush(stdout);
|
|
close(1);
|
|
if (dup(so_fd) != 1) {
|
|
panic( catgets(scmc_catd, MS_eval, MSG_212,
|
|
"standard out dup failed"));
|
|
}
|
|
close(so_fd);
|
|
notstdout = false;
|
|
}
|
|
|
|
/*
|
|
* Determine is standard output is currently being redirected
|
|
* to a file (as far as we know).
|
|
*/
|
|
|
|
public Boolean isredirected()
|
|
{
|
|
return notstdout;
|
|
}
|
|
|
|
public chkenable (s)
|
|
char *s;
|
|
{
|
|
extern int strcmpi();
|
|
if (!strcmpi(s,"on"))
|
|
return (int) on;
|
|
if (!strcmpi(s,"parent"))
|
|
return (int) parent;
|
|
if (!strcmpi(s,"child"))
|
|
return (int) child;
|
|
if (!strcmpi(s,"off"))
|
|
return (int) off;
|
|
|
|
(*rpt_error)(stderr,"Usage: multproc { on | parent | child | off }\n");
|
|
return 0;
|
|
}
|
|
|
|
public getcase (s)
|
|
char *s;
|
|
{
|
|
lowercase(s);
|
|
if (!strcmp(s,"mixed"))
|
|
return (int) mixed;
|
|
else if (!strcmp(s,"lower"))
|
|
return (int) lower;
|
|
else if (!strcmp(s,"upper"))
|
|
return (int) upper;
|
|
else if (!strcmp(s,"default"))
|
|
return (int) filedep;
|
|
else {
|
|
(*rpt_error)(stderr,
|
|
"Usage: case { default | mixed | lower | upper }\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
public lowercase (s)
|
|
char *s;
|
|
{
|
|
char *c;
|
|
for (c = s; *c; c++)
|
|
*c = (char) tolower(*c);
|
|
}
|
|
|
|
public uppercase (s)
|
|
char *s;
|
|
{
|
|
char *c;
|
|
for (c = s; *c; c++)
|
|
*c = (char) toupper(*c);
|
|
}
|