1 /*- 2 * Copyright (c) 1980 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)tracestop.c 5.3 (Berkeley) 04/16/91"; 10 #endif /* not lint */ 11 12 /* 13 * Handle trace and stop commands. 14 */ 15 16 #include "defs.h" 17 #include "breakpoint.h" 18 #include "sym.h" 19 #include "tree.h" 20 #include "runtime.h" 21 #include "source.h" 22 #include "object.h" 23 #include "mappings.h" 24 #include "machine.h" 25 #include "tree.rep" 26 27 LOCAL SYM *tcontainer(); 28 29 /* 30 * Process a trace/untrace command, basically checking arguments 31 * and translate to a call of the appropriate routine. 32 */ 33 34 trace(cmd, exp, where, cond) 35 int cmd; 36 NODE *exp; 37 NODE *where; 38 NODE *cond; 39 { 40 if (exp == NIL) { 41 traceall(cmd, where, cond); 42 } else if (exp->op == O_LCON || exp->op == O_QLINE) { 43 traceinst(cmd, exp, where, cond); 44 } else if (where!=NIL && (where->op==O_QLINE || where->op==O_LCON)) { 45 traceat(cmd, exp, where, cond); 46 } else { 47 tracedata(cmd, exp, where, cond); 48 } 49 if (where != NIL) { 50 tfree(where); 51 } 52 } 53 54 /* 55 * Set a breakpoint that will turn on tracing. 56 * 57 * A line number of 0 in the breakpoint information structure 58 * means it's a normal trace. 59 * 60 * A line number of -1 indicates that we want to trace at the instruction 61 * rather than source line level. 62 * 63 * If location is NIL, turn on tracing because if the user 64 * has the program stopped somewhere and says "trace", 65 * he/she wants to see tracing after continuing execution. 66 */ 67 68 LOCAL traceall(cmd, where, cond) 69 int cmd; 70 NODE *where; 71 NODE *cond; 72 { 73 SYM *s; 74 LINENO line; 75 76 if (where != NIL && where->op != O_NAME) { 77 error("bad location for trace"); 78 } 79 if (cmd == O_TRACE) { 80 line = 0; 81 } else { 82 line = -1; 83 } 84 if (where == NIL) { 85 switch (cmd) { 86 case O_TRACE: 87 if (tracing != 0) { 88 error("already tracing lines"); 89 } 90 tracing++; 91 addcond(TRPRINT, cond); 92 break; 93 94 case O_TRACEI: 95 if (inst_tracing != 0) { 96 error("already tracing instructions"); 97 } 98 inst_tracing++; 99 addcond(TRPRINT, cond); 100 break; 101 102 default: 103 panic("bad cmd in traceall"); 104 break; 105 } 106 s = program; 107 } else if (where->op != O_NAME) { 108 trerror("found %t, expected procedure or function", where); 109 } else { 110 s = where->nameval; 111 if (!isblock(s)) { 112 error("\"%s\" is not a procedure or function", name(s)); 113 } 114 } 115 addbp(codeloc(s), ALL_ON, s, cond, NIL, line); 116 } 117 118 /* 119 * Set up the appropriate breakpoint for tracing an instruction. 120 */ 121 122 LOCAL traceinst(cmd, exp, where, cond) 123 int cmd; 124 NODE *exp; 125 NODE *where; 126 NODE *cond; 127 { 128 LINENO line; 129 ADDRESS addr; 130 131 if (where != NIL) { 132 error("unexpected \"at\" or \"in\""); 133 } 134 if (cmd == O_TRACEI) { 135 if (exp->op == O_QLINE) { 136 addr = (ADDRESS) exp->right->lconval; 137 } else if (exp->op == O_LCON) { 138 addr = (ADDRESS) exp->lconval; 139 } else { 140 trerror("expected integer constant, found %t", exp); 141 } 142 line = -1; 143 } else { 144 if (exp->op == O_QLINE) { 145 line = (LINENO) exp->right->lconval; 146 addr = objaddr(line, exp->left->sconval); 147 } else { 148 line = (LINENO) exp->lconval; 149 addr = objaddr(line, cursource); 150 } 151 if (addr == (ADDRESS) -1) { 152 error("can't trace line %d", line); 153 } 154 } 155 tfree(exp); 156 addbp(addr, INST, NIL, cond, NIL, line); 157 } 158 159 /* 160 * set a breakpoint to print an expression at a given line or address 161 */ 162 163 LOCAL traceat(cmd, exp, where, cond) 164 int cmd; 165 NODE *exp; 166 NODE *where; 167 NODE *cond; 168 { 169 LINENO line; 170 ADDRESS addr; 171 172 if (cmd == O_TRACEI) { 173 if (where->op != O_LCON) { 174 trerror("expected integer constant, found %t", where); 175 } 176 line = -1; 177 addr = (ADDRESS) where->lconval; 178 } else { 179 line = (LINENO) where->right->lconval; 180 addr = objaddr(line, where->left->sconval); 181 if (addr == (ADDRESS) -1) { 182 error("can't trace at line %d", line); 183 } 184 } 185 addbp(addr, AT_BP, NIL, cond, exp, line); 186 } 187 188 /* 189 * Set up breakpoint for tracing data. 190 * 191 * The tracing of blocks lies somewhere between instruction and data; 192 * it's here since a block cannot be distinguished from other terms. 193 * 194 * As in "traceall", if the "block" is the main program then the 195 * user didn't actually specify a block. This means we want to 196 * turn tracing on ourselves because if the program is stopped 197 * we want to be on regardless of whether they say "cont" or "run". 198 */ 199 200 LOCAL tracedata(cmd, exp, block, cond) 201 int cmd; 202 NODE *exp; 203 NODE *block; 204 NODE *cond; 205 { 206 SYM *s, *t; 207 208 #ifdef lint 209 cmd = cmd; 210 #endif 211 if (exp->op != O_RVAL && exp->op != O_CALL) { 212 error("can't trace expressions"); 213 } 214 if (block == NIL) { 215 t = tcontainer(exp->left); 216 } else if (block->op == O_NAME) { 217 t = block->nameval; 218 } else { 219 trerror("found %t, expected procedure or function", block); 220 } 221 if (exp->left->op == O_NAME) { 222 s = exp->left->nameval; 223 if (isblock(s)) { 224 addbp(codeloc(t), BLOCK_ON, t, cond, exp->left, 0); 225 if (t == program) { 226 addbp(codeloc(s), CALL, s, cond, NIL, 0); 227 } 228 return; 229 } 230 } 231 addbp(codeloc(t), TERM_ON, t, cond, exp, 0); 232 if (curfunc == t) { 233 var_tracing++; 234 addvar(TRPRINT, exp, cond); 235 addbp(return_addr(), TERM_OFF, t, cond, exp, 0); 236 } 237 } 238 239 /* 240 * Setting and unsetting of stops. 241 */ 242 243 stop(cmd, exp, where, cond) 244 int cmd; 245 NODE *exp; 246 NODE *where; 247 NODE *cond; 248 { 249 SYM *s; 250 LINENO n; 251 252 if (exp != NIL) { 253 stopvar(cmd, exp, where, cond); 254 } else if (cond != NIL) { 255 if (where == NIL) { 256 s = program; 257 } else if (where->op == O_NAME) { 258 s = where->nameval; 259 } else { 260 error("bad location for stop"); 261 } 262 n = codeloc(s); 263 addbp(n, STOP_ON, s, cond, NIL, n); 264 addcond(TRSTOP, cond); 265 var_tracing++; 266 } else if (where->op == O_NAME) { 267 s = where->nameval; 268 if (!isblock(s)) { 269 error("\"%s\" is not a procedure or function", name(s)); 270 } 271 n = codeloc(s); 272 addbp(n, STOP_BP, s, cond, NIL, srcline(firstline(s))); 273 } else { 274 stopinst(cmd, where, cond); 275 } 276 if (where != NIL) { 277 tfree(where); 278 } 279 } 280 281 LOCAL stopinst(cmd, where, cond) 282 int cmd; 283 NODE *where; 284 NODE *cond; 285 { 286 LINENO line; 287 ADDRESS addr; 288 289 if (where->op != O_QLINE) { 290 error("expected line number"); 291 } 292 if (cmd == O_STOP) { 293 line = (LINENO) where->right->lconval; 294 addr = objaddr(line, where->left->sconval); 295 if (addr == (ADDRESS) -1) { 296 error("can't stop at that line"); 297 } 298 } else { 299 line = -1; 300 addr = (ADDRESS) where->right->lconval; 301 } 302 addbp(addr, STOP_BP, NIL, cond, NIL, line); 303 } 304 305 /* 306 * Implement stopping on assignment to a variable by adding it to 307 * the variable list. 308 */ 309 310 LOCAL stopvar(cmd, exp, where, cond) 311 int cmd; 312 NODE *exp; 313 NODE *where; 314 NODE *cond; 315 { 316 SYM *s; 317 318 if (exp->op != O_RVAL) { 319 trerror("found %t, expected variable", exp); 320 } 321 if (cmd == O_STOPI) { 322 inst_tracing++; 323 } 324 var_tracing++; 325 addvar(TRSTOP, exp, cond); 326 if (where == NIL) { 327 s = program; 328 } else if (where->op == O_NAME) { 329 s = where->nameval; 330 } else { 331 error("bad location for stop"); 332 } 333 addbp(codeloc(s), STOP_ON, s, cond, exp, 0); 334 } 335 336 /* 337 * Figure out the block that contains the symbols 338 * in the given variable expression. 339 */ 340 341 LOCAL SYM *tcontainer(var) 342 NODE *var; 343 { 344 NODE *p; 345 346 p = var; 347 while (p->op != O_NAME) { 348 if (isleaf(p->op)) { 349 panic("unexpected op %d in tcontainer", p->op); 350 /* NOTREACHED */ 351 } 352 p = p->left; 353 } 354 return container(p->nameval); 355 } 356