1*22565Sdist /* 2*22565Sdist * Copyright (c) 1980 Regents of the University of California. 3*22565Sdist * All rights reserved. The Berkeley software License Agreement 4*22565Sdist * specifies the terms and conditions for redistribution. 5*22565Sdist */ 65546Slinton 7*22565Sdist #ifndef lint 8*22565Sdist static char sccsid[] = "@(#)tracestop.c 5.1 (Berkeley) 06/06/85"; 9*22565Sdist #endif not lint 105546Slinton 115546Slinton /* 125546Slinton * Handle trace and stop commands. 135546Slinton */ 145546Slinton 155546Slinton #include "defs.h" 165546Slinton #include "breakpoint.h" 175546Slinton #include "sym.h" 185546Slinton #include "tree.h" 195546Slinton #include "runtime.h" 205546Slinton #include "source.h" 215546Slinton #include "object.h" 225546Slinton #include "mappings.h" 235546Slinton #include "machine.h" 245546Slinton #include "tree.rep" 255546Slinton 265546Slinton LOCAL SYM *tcontainer(); 275546Slinton 285546Slinton /* 295546Slinton * Process a trace/untrace command, basically checking arguments 305546Slinton * and translate to a call of the appropriate routine. 315546Slinton */ 325546Slinton 335546Slinton trace(cmd, exp, where, cond) 345546Slinton int cmd; 355546Slinton NODE *exp; 365546Slinton NODE *where; 375546Slinton NODE *cond; 385546Slinton { 395546Slinton if (exp == NIL) { 405546Slinton traceall(cmd, where, cond); 415546Slinton } else if (exp->op == O_LCON || exp->op == O_QLINE) { 425546Slinton traceinst(cmd, exp, where, cond); 435546Slinton } else if (where!=NIL && (where->op==O_QLINE || where->op==O_LCON)) { 445546Slinton traceat(cmd, exp, where, cond); 455546Slinton } else { 465546Slinton tracedata(cmd, exp, where, cond); 475546Slinton } 485546Slinton if (where != NIL) { 495546Slinton tfree(where); 505546Slinton } 515546Slinton } 525546Slinton 535546Slinton /* 545546Slinton * Set a breakpoint that will turn on tracing. 555546Slinton * 565546Slinton * A line number of 0 in the breakpoint information structure 575546Slinton * means it's a normal trace. 585546Slinton * 595546Slinton * A line number of -1 indicates that we want to trace at the instruction 605546Slinton * rather than source line level. 615546Slinton * 625546Slinton * If location is NIL, turn on tracing because if the user 635546Slinton * has the program stopped somewhere and says "trace", 645546Slinton * he/she wants to see tracing after continuing execution. 655546Slinton */ 665546Slinton 675546Slinton LOCAL traceall(cmd, where, cond) 685546Slinton int cmd; 695546Slinton NODE *where; 705546Slinton NODE *cond; 715546Slinton { 725546Slinton SYM *s; 735546Slinton LINENO line; 745546Slinton 755546Slinton if (where != NIL && where->op != O_NAME) { 765546Slinton error("bad location for trace"); 775546Slinton } 785546Slinton if (cmd == O_TRACE) { 795546Slinton line = 0; 805546Slinton } else { 815546Slinton line = -1; 825546Slinton } 835546Slinton if (where == NIL) { 845546Slinton switch (cmd) { 855546Slinton case O_TRACE: 865546Slinton if (tracing != 0) { 875546Slinton error("already tracing lines"); 885546Slinton } 895546Slinton tracing++; 905546Slinton addcond(TRPRINT, cond); 915546Slinton break; 925546Slinton 935546Slinton case O_TRACEI: 945546Slinton if (inst_tracing != 0) { 955546Slinton error("already tracing instructions"); 965546Slinton } 975546Slinton inst_tracing++; 985546Slinton addcond(TRPRINT, cond); 995546Slinton break; 1005546Slinton 1015546Slinton default: 1025546Slinton panic("bad cmd in traceall"); 1035546Slinton break; 1045546Slinton } 1055546Slinton s = program; 1065546Slinton } else if (where->op != O_NAME) { 1075546Slinton trerror("found %t, expected procedure or function", where); 1085546Slinton } else { 1095546Slinton s = where->nameval; 1105546Slinton if (!isblock(s)) { 1115546Slinton error("\"%s\" is not a procedure or function", name(s)); 1125546Slinton } 1135546Slinton } 1145546Slinton addbp(codeloc(s), ALL_ON, s, cond, NIL, line); 1155546Slinton } 1165546Slinton 1175546Slinton /* 1185546Slinton * Set up the appropriate breakpoint for tracing an instruction. 1195546Slinton */ 1205546Slinton 1215546Slinton LOCAL traceinst(cmd, exp, where, cond) 1225546Slinton int cmd; 1235546Slinton NODE *exp; 1245546Slinton NODE *where; 1255546Slinton NODE *cond; 1265546Slinton { 1275546Slinton LINENO line; 1285546Slinton ADDRESS addr; 1295546Slinton 1305546Slinton if (where != NIL) { 1315546Slinton error("unexpected \"at\" or \"in\""); 1325546Slinton } 1335546Slinton if (cmd == O_TRACEI) { 1345546Slinton if (exp->op == O_QLINE) { 1355546Slinton addr = (ADDRESS) exp->right->lconval; 1365546Slinton } else if (exp->op == O_LCON) { 1375546Slinton addr = (ADDRESS) exp->lconval; 1385546Slinton } else { 1395546Slinton trerror("expected integer constant, found %t", exp); 1405546Slinton } 1415546Slinton line = -1; 1425546Slinton } else { 1435546Slinton if (exp->op == O_QLINE) { 1445546Slinton line = (LINENO) exp->right->lconval; 1455546Slinton addr = objaddr(line, exp->left->sconval); 1465546Slinton } else { 1475546Slinton line = (LINENO) exp->lconval; 1485546Slinton addr = objaddr(line, cursource); 1495546Slinton } 1505546Slinton if (addr == (ADDRESS) -1) { 1515546Slinton error("can't trace line %d", line); 1525546Slinton } 1535546Slinton } 1545546Slinton tfree(exp); 1555546Slinton addbp(addr, INST, NIL, cond, NIL, line); 1565546Slinton } 1575546Slinton 1585546Slinton /* 1595546Slinton * set a breakpoint to print an expression at a given line or address 1605546Slinton */ 1615546Slinton 1625546Slinton LOCAL traceat(cmd, exp, where, cond) 1635546Slinton int cmd; 1645546Slinton NODE *exp; 1655546Slinton NODE *where; 1665546Slinton NODE *cond; 1675546Slinton { 1685546Slinton LINENO line; 1695546Slinton ADDRESS addr; 1705546Slinton 1715546Slinton if (cmd == O_TRACEI) { 1725546Slinton if (where->op != O_LCON) { 1735546Slinton trerror("expected integer constant, found %t", where); 1745546Slinton } 1755546Slinton line = -1; 1765546Slinton addr = (ADDRESS) where->lconval; 1775546Slinton } else { 1785546Slinton line = (LINENO) where->right->lconval; 1795546Slinton addr = objaddr(line, where->left->sconval); 1805546Slinton if (addr == (ADDRESS) -1) { 1815546Slinton error("can't trace at line %d", line); 1825546Slinton } 1835546Slinton } 1845546Slinton addbp(addr, AT_BP, NIL, cond, exp, line); 1855546Slinton } 1865546Slinton 1875546Slinton /* 1885546Slinton * Set up breakpoint for tracing data. 1895546Slinton * 1905546Slinton * The tracing of blocks lies somewhere between instruction and data; 1915546Slinton * it's here since a block cannot be distinguished from other terms. 1925546Slinton * 1935546Slinton * As in "traceall", if the "block" is the main program then the 1945546Slinton * user didn't actually specify a block. This means we want to 1955546Slinton * turn tracing on ourselves because if the program is stopped 1965546Slinton * we want to be on regardless of whether they say "cont" or "run". 1975546Slinton */ 1985546Slinton 1995546Slinton LOCAL tracedata(cmd, exp, block, cond) 2005546Slinton int cmd; 2015546Slinton NODE *exp; 2025546Slinton NODE *block; 2035546Slinton NODE *cond; 2045546Slinton { 2055546Slinton SYM *s, *t; 2065546Slinton 2075546Slinton if (exp->op != O_RVAL && exp->op != O_CALL) { 2085546Slinton error("can't trace expressions"); 2095546Slinton } 2105546Slinton if (block == NIL) { 2115546Slinton t = tcontainer(exp->left); 2125546Slinton } else if (block->op == O_NAME) { 2135546Slinton t = block->nameval; 2145546Slinton } else { 2155546Slinton trerror("found %t, expected procedure or function", block); 2165546Slinton } 2175546Slinton if (exp->left->op == O_NAME) { 2185546Slinton s = exp->left->nameval; 2195546Slinton if (isblock(s)) { 2205546Slinton addbp(codeloc(t), BLOCK_ON, t, cond, exp->left, 0); 2215546Slinton if (t == program) { 2225546Slinton addbp(codeloc(s), CALL, s, cond, NIL, 0); 2235546Slinton } 2245546Slinton return; 2255546Slinton } 2265546Slinton } 2275546Slinton addbp(codeloc(t), TERM_ON, t, cond, exp, 0); 2285546Slinton if (curfunc == t) { 2295546Slinton var_tracing++; 2305546Slinton addvar(TRPRINT, exp, cond); 2315546Slinton addbp(return_addr(), TERM_OFF, t, cond, exp, 0); 2325546Slinton } 2335546Slinton } 2345546Slinton 2355546Slinton /* 2365546Slinton * Setting and unsetting of stops. 2375546Slinton */ 2385546Slinton 2395546Slinton stop(cmd, exp, where, cond) 2405546Slinton int cmd; 2415546Slinton NODE *exp; 2425546Slinton NODE *where; 2435546Slinton NODE *cond; 2445546Slinton { 2455546Slinton SYM *s; 2465546Slinton LINENO n; 2475546Slinton 2485546Slinton if (exp != NIL) { 2495546Slinton stopvar(cmd, exp, where, cond); 2505546Slinton } else if (cond != NIL) { 2515546Slinton if (where == NIL) { 2525546Slinton s = program; 2535546Slinton } else if (where->op == O_NAME) { 2545546Slinton s = where->nameval; 2555546Slinton } else { 2565546Slinton error("bad location for stop"); 2575546Slinton } 2585546Slinton n = codeloc(s); 2595546Slinton addbp(n, STOP_ON, s, cond, NIL, n); 2605546Slinton addcond(TRSTOP, cond); 2615546Slinton var_tracing++; 2625546Slinton } else if (where->op == O_NAME) { 2635546Slinton s = where->nameval; 2645546Slinton if (!isblock(s)) { 2655546Slinton error("\"%s\" is not a procedure or function", name(s)); 2665546Slinton } 2675546Slinton n = codeloc(s); 2685546Slinton addbp(n, STOP_BP, s, cond, NIL, srcline(firstline(s))); 2695546Slinton } else { 2705546Slinton stopinst(cmd, where, cond); 2715546Slinton } 2725546Slinton if (where != NIL) { 2735546Slinton tfree(where); 2745546Slinton } 2755546Slinton } 2765546Slinton 2775546Slinton LOCAL stopinst(cmd, where, cond) 2785546Slinton int cmd; 2795546Slinton NODE *where; 2805546Slinton NODE *cond; 2815546Slinton { 2825546Slinton LINENO line; 2835546Slinton ADDRESS addr; 2845546Slinton 2855546Slinton if (where->op != O_QLINE) { 2865546Slinton error("expected line number"); 2875546Slinton } 2885546Slinton if (cmd == O_STOP) { 2895546Slinton line = (LINENO) where->right->lconval; 2905546Slinton addr = objaddr(line, where->left->sconval); 2915546Slinton if (addr == (ADDRESS) -1) { 2925546Slinton error("can't stop at that line"); 2935546Slinton } 2945546Slinton } else { 2955546Slinton line = -1; 2965546Slinton addr = (ADDRESS) where->right->lconval; 2975546Slinton } 2985546Slinton addbp(addr, STOP_BP, NIL, cond, NIL, line); 2995546Slinton } 3005546Slinton 3015546Slinton /* 3025546Slinton * Implement stopping on assignment to a variable by adding it to 3035546Slinton * the variable list. 3045546Slinton */ 3055546Slinton 3065546Slinton LOCAL stopvar(cmd, exp, where, cond) 3075546Slinton int cmd; 3085546Slinton NODE *exp; 3095546Slinton NODE *where; 3105546Slinton NODE *cond; 3115546Slinton { 3125546Slinton SYM *s; 3135546Slinton 3145546Slinton if (exp->op != O_RVAL) { 3155546Slinton trerror("found %t, expected variable", exp); 3165546Slinton } 3175546Slinton if (cmd == O_STOPI) { 3185546Slinton inst_tracing++; 3195546Slinton } 3205546Slinton var_tracing++; 3215546Slinton addvar(TRSTOP, exp, cond); 3225546Slinton if (where == NIL) { 3235546Slinton s = program; 3245546Slinton } else if (where->op == O_NAME) { 3255546Slinton s = where->nameval; 3265546Slinton } else { 3275546Slinton error("bad location for stop"); 3285546Slinton } 3295546Slinton addbp(codeloc(s), STOP_ON, s, cond, exp, 0); 3305546Slinton } 3315546Slinton 3325546Slinton /* 3335546Slinton * Figure out the block that contains the symbols 3345546Slinton * in the given variable expression. 3355546Slinton */ 3365546Slinton 3375546Slinton LOCAL SYM *tcontainer(var) 3385546Slinton NODE *var; 3395546Slinton { 3405546Slinton NODE *p; 3415546Slinton 3425546Slinton p = var; 3435546Slinton while (p->op != O_NAME) { 3445546Slinton if (isleaf(p->op)) { 3455546Slinton panic("unexpected op %d in tcontainer", p->op); 3465546Slinton /* NOTREACHED */ 3475546Slinton } 3485546Slinton p = p->left; 3495546Slinton } 3505546Slinton return container(p->nameval); 3515546Slinton } 352