1implement Stackv; 2 3include "sys.m"; 4 sys: Sys; 5include "draw.m"; 6include "debug.m"; 7 debug: Debug; 8 Prog, Module, Exp: import debug; 9 Tadt, Tarray, Tbig, Tbyte, Treal, 10 Tfn, Tint, Tlist, 11 Tref, Tstring, Tslice: import Debug; 12include "arg.m"; 13include "bufio.m"; 14 bufio: Bufio; 15 Iobuf: import bufio; 16 17stderr: ref Sys->FD; 18stdout: ref Iobuf; 19 20hasht := array[97] of (int, array of int); 21 22Stackv: module { 23 init: fn(ctxt: ref Draw->Context, argv: list of string); 24}; 25 26maxrecur := 16r7ffffffe; 27 28badmodule(p: string) 29{ 30 sys->fprint(stderr, "stackv: cannot load %q: %r\n", p); 31 raise "fail:bad module"; 32} 33 34currp: ref Prog; 35showtypes := 1; 36showsource := 0; 37showmodule := 0; 38 39init(nil: ref Draw->Context, argv: list of string) 40{ 41 42 sys = load Sys Sys->PATH; 43 stderr = sys->fildes(2); 44 debug = load Debug Debug->PATH; 45 if(debug == nil) 46 badmodule(Debug->PATH); 47 bufio = load Bufio Bufio->PATH; 48 if (bufio == nil) 49 badmodule(Bufio->PATH); 50 arg := load Arg Arg->PATH; 51 if (arg == nil) 52 badmodule(Arg->PATH); 53 stdout = bufio->fopen(sys->fildes(1), Sys->OWRITE); 54 55 arg->init(argv); 56 arg->setusage("stackv [-Tlm] [-r maxdepth] [-s dis sbl]... [pid[.sym]...] ..."); 57 sblfile := ""; 58 while((opt := arg->opt()) != 0){ 59 case opt { 60 's' => 61 arg->earg(); # XXX make it a list of maps from dis to sbl later 62 sblfile = arg->earg(); 63 'l' => 64 showsource = 1; 65 'm' => 66 showmodule = 1; 67 'r' => 68 maxrecur = int arg->earg(); 69 'T' => 70 showtypes = 0; 71 * => 72 arg->usage(); 73 } 74 } 75 debug->init(); 76 argv = arg->argv(); 77 printpids := len argv > 1; 78 if(printpids) 79 maxrecur++; 80 for(; argv != nil; argv = tl argv) 81 db(sys->tokenize(hd argv, ".").t1, printpids); 82} 83 84db(toks: list of string, printpid: int): int 85{ 86 if(toks == nil){ 87 sys->fprint(stderr, "stackv: bad pid\n"); 88 return -1; 89 } 90 if((pid := int hd toks) <= 0){ 91 sys->fprint(stderr, "stackv: bad pid %q\n", hd toks); 92 return -1; 93 } 94 err: string; 95 p: ref Prog; 96 97 # reuse process if possible 98 if(currp == nil || currp.id != pid){ 99 (currp, err) = debug->prog(pid); 100 if(err != nil){ 101 sys->fprint(stderr, "stackv: %s\n", err); 102 return -1; 103 } 104 if(currp == nil){ 105 sys->fprint(stderr, "stackv: nil prog from pid %d\n", pid); 106 return -1; 107 } 108 } 109 p = currp; 110 stk: array of ref Exp; 111 (stk, err) = p.stack(); 112 if(err != nil){ 113 sys->fprint(stderr, "stackv: %s\n", err); 114 return -1; 115 } 116 for (i := 0; i < len stk; i++) { 117 stk[i].m.stdsym(); 118 stk[i].findsym(); 119 } 120 depth := 0; 121 if(printpid){ 122 stdout.puts(sys->sprint("prog %d {\n", pid)); # } 123 depth++; 124 } 125 pexp(stk, tl toks, depth); 126 if(printpid) 127 stdout.puts("}\n"); 128 stdout.flush(); 129 return 0; 130} 131 132pexp(stk: array of ref Exp, toks: list of string, depth: int) 133{ 134 if(toks == nil){ 135 for (i := 0; i < len stk; i++) 136 pfn(stk[i], depth); 137 }else{ 138 exp := stackfindsym(stk, toks, depth); 139 if(exp == nil) 140 return; 141 pname(exp, depth, nil); 142 stdout.putc('\n'); 143 } 144} 145 146stackfindsym(stk: array of ref Exp, toks: list of string, depth: int): ref Exp 147{ 148 fname := hd toks; 149 toks = tl toks; 150 for(i := 0; i < len stk; i++){ 151 s := stk[i].name; 152 if(s == fname) 153 break; 154 if(hasdot(s) && toks != nil && s == fname+"."+hd toks){ 155 fname += "."+hd toks; 156 toks = tl toks; 157 break; 158 } 159 } 160 if(i == len stk){ 161 indent(depth); 162 stdout.puts("function not found\n"); 163 return nil; 164 } 165 if(toks == nil) 166 return stk[i]; 167 stk = stk[i].expand(); 168 if(hd toks == "module"){ 169 if((e := getname(stk, "module")) == nil){ 170 indent(depth); 171 stdout.puts(sys->sprint("no module declarations in function %q\n", fname)); 172 }else if((e = symfindsym(e, tl toks, depth)) != nil) 173 return e; 174 return nil; 175 } 176 for(t := "locals" :: "args" :: "module" :: nil; t != nil; t = tl t){ 177 if((e := getname(stk, hd t)) == nil) 178 continue; 179 if((e = symfindsym(e, toks, depth)) != nil) 180 return e; 181 } 182 indent(depth); 183 stdout.puts(sys->sprint("symbol %q not found in function %q\n", hd toks, fname)); 184 return nil; 185} 186 187hasdot(s: string): int 188{ 189 for(i := 0; i < len s; i++) 190 if(s[i] == '.') 191 return 1; 192 return 0; 193} 194 195symfindsym(e: ref Exp, toks: list of string, depth: int): ref Exp 196{ 197 if(toks == nil) 198 return e; 199 exps := e.expand(); 200 for(i := 0; i < len exps; i++) 201 if(exps[i].name == hd toks) 202 return symfindsym(exps[i], tl toks, depth); 203 return nil; 204} 205 206pfn(exp: ref Exp, depth: int) 207{ 208 (v, w) := exp.val(); 209 if(!w || v == nil){ 210 indent(depth); 211 stdout.puts(sys->sprint("no value for fn %q\n", exp.name)); 212 return; 213 } 214 exps := exp.expand(); 215 indent(depth); 216 stdout.puts("["+exp.srcstr()+"]\n"); 217 indent(depth); 218 stdout.puts(symname(exp)+"("); 219 if((e := getname(exps, "args")) != nil){ 220 args := e.expand(); 221 for(i := 0; i < len args; i++){ 222 pname(args[i], depth+1, nil); 223 if(i != len args - 1) 224 stdout.puts(", "); 225 } 226 } 227 stdout.puts(")\n"); 228 indent(depth); 229 stdout.puts("{\n"); # } 230 if((e = getname(exps, "locals")) != nil){ 231 locals := e.expand(); 232 for(i := 0; i < len locals; i++){ 233 indent(depth+1); 234 pname(locals[i], depth+1, nil); 235 stdout.puts("\n"); 236 } 237 } 238 if(showmodule && (e = getname(exps, "module")) != nil){ 239 mvars := e.expand(); 240 for(i := 0; i < len mvars; i++){ 241 indent(depth+1); 242 pname(mvars[i], depth+1, "module."); 243 stdout.puts("\n"); 244 } 245 } 246 indent(depth); 247 stdout.puts("}\n"); 248} 249 250getname(exps: array of ref Exp, name: string): ref Exp 251{ 252 for(i := 0; i < len exps; i++) 253 if(exps[i].name == name) 254 return exps[i]; 255 return nil; 256} 257 258strval(v: string): string 259{ 260 for(i := 0; i < len v; i++) 261 if(v[i] == '"') 262 break; 263 if(i < len v) 264 v = v[i:]; 265 return v; 266} 267 268pname(exp: ref Exp, depth: int, prefix: string) 269{ 270 name := prefix+symname(exp); 271 (v, w) := exp.val(); 272 if (!w && v == nil) { 273 stdout.puts(sys->sprint("%s: %s = novalue", symname(exp), exp.typename())); 274 return; 275 } 276 case exp.kind() { 277 Tfn => 278 pfn(exp, depth); 279 Tint => 280 stdout.puts(sys->sprint("%s := %s", name, v)); 281 Tstring => 282 stdout.puts(sys->sprint("%s := %s", name, strval(v))); 283 Tbyte or 284 Tbig or 285 Treal => 286 stdout.puts(sys->sprint("%s := %s %s", name, exp.typename(), v)); 287 * => 288 if(showtypes) 289 stdout.puts(sys->sprint("%s: %s = ", name, exp.typename())); 290 else 291 stdout.puts(sys->sprint("%s := ", name)); 292 pval(exp, v, w, depth); 293 } 294} 295 296srcstr(src: ref Debug->Src): string 297{ 298 if(src == nil) 299 return nil; 300 if(src.start.file != src.stop.file) 301 return sys->sprint("%q:%d.%d,%q:%d.%d", src.start.file, src.start.line, src.start.pos, src.stop.file, src.stop.line, src.stop.pos); 302 if(src.start.line != src.stop.line) 303 return sys->sprint("%q:%d.%d,%d.%d", src.start.file, src.start.line, src.start.pos, src.stop.line, src.stop.pos); 304 return sys->sprint("%q:%d.%d,%d", src.start.file, src.start.line, src.start.pos, src.stop.pos); 305} 306 307pval(exp: ref Exp, v: string, w: int, depth: int) 308{ 309 if(depth >= maxrecur){ 310 stdout.puts(v); 311 return; 312 } 313 case exp.kind() { 314 Tarray => 315 if(pref(v)){ 316 if(depth+1 >= maxrecur) 317 stdout.puts(v+"{...}"); 318 else{ 319 stdout.puts(v+"{\n"); 320 indent(depth+1); 321 parray(exp, depth+1); 322 stdout.puts("\n"); 323 indent(depth); 324 stdout.puts("}"); 325 } 326 } 327 Tlist => 328 if(v == "nil") 329 stdout.puts("nil"); 330 else 331 if(depth+1 >= maxrecur) 332 stdout.puts(v+"{...}"); 333 else{ 334 stdout.puts("{\n"); 335 indent(depth+1); 336 plist(exp, v, w, depth+1); 337 stdout.puts("\n"); 338 indent(depth); 339 stdout.puts("}"); 340 } 341 Tadt => 342 pgenval(exp, nil, w, depth); 343 Tref => 344 if(pref(v)) 345 pgenval(exp, v, w, depth); 346 Tstring => 347 stdout.puts(strval(v)); 348 * => 349 pgenval(exp, v, w, depth); 350 } 351} 352 353parray(exp: ref Exp, depth: int) 354{ 355 exps := exp.expand(); 356 for(i := 0; i < len exps; i++){ 357 e := exps[i]; 358 (v, w) := e.val(); 359 if(e.kind() == Tslice) 360 parray(e, depth); 361 else{ 362 pval(e, v, w, depth); 363 stdout.puts(", "); 364 } 365 } 366} 367 368plist(exp: ref Exp, v: string, w: int, depth: int) 369{ 370 while(w && v != "nil"){ 371 exps := exp.expand(); 372 h := getname(exps, "hd"); 373 if(h == nil) 374 break; 375 (hv, vw) := h.val(); 376 if(pref(v) == 0) 377 return; 378 stdout.puts(v+"("); 379 pval(h, hv, vw, depth); 380 stdout.puts(") :: "); 381 h = nil; 382 exp = getname(exps, "tl"); 383 (v, w) = exp.val(); 384 } 385 stdout.puts("nil"); 386} 387 388pgenval(exp: ref Exp, v: string, w: int, depth: int) 389{ 390 if(w){ 391 exps := exp.expand(); 392 if(len exps == 0) 393 stdout.puts(v); 394 else{ 395 stdout.puts(v+"{\n"); # } 396 if (len exps > 0){ 397 if(depth >= maxrecur){ 398 indent(depth); 399 stdout.puts(sys->sprint("...[%d]\n", len exps)); 400 }else{ 401 for (i := 0; i < len exps; i++){ 402 indent(depth+1); 403 pname(exps[i], depth+1, nil); 404 stdout.puts("\n"); 405 } 406 } 407 } 408 indent(depth); # { 409 stdout.puts("}"); 410 } 411 }else 412 stdout.puts(v); 413} 414 415symname(exp: ref Exp): string 416{ 417 if(showsource == 0) 418 return exp.name; 419 return exp.name+"["+srcstr(exp.src())+"]"; 420} 421 422indent(n: int) 423{ 424 while(n-- > 0) 425 stdout.putc('\t'); 426} 427 428ref2int(v: string): int 429{ 430 if(v == nil) 431 error("bad empty value for ref"); 432 i := 0; 433 n := len v; 434 if(v[0] == '@') 435 i = 1; 436 else{ 437 # skip array bounds 438 if(v[0] == '['){ 439 for(; i < n && v[i] != ']'; i++) 440 ; 441 if(i >= n - 2 || v[i+1] != ' ' || v[i+2] != '@') 442 error("bad value for ref: "+v); 443 i += 3; 444 } 445 } 446 if(n - i > 8) 447 error("64-bit pointers?"); 448 p := 0; 449 for(; i < n; i++){ 450 c := v[i]; 451 case c { 452 '0' to '9' => 453 p = (p << 4) + (c - '0'); 454 'a' to 'f' => 455 p = (p << 4) + (c - 'a' + 10); 456 * => 457 error("bad value for ref: "+v); 458 } 459 } 460 return p; 461} 462 463pref(v: string): int 464{ 465 if(v == "nil"){ 466 stdout.puts("nil"); 467 return 0; 468 } 469 if(addref(ref2int(v)) == 0){ 470 stdout.puts(v); 471 stdout.puts("(qv)"); 472 return 0; 473 } 474 return 1; 475} 476 477# hash table implementation that tries to be reasonably 478# parsimonious on memory usage. 479addref(v: int): int 480{ 481 slot := (v & 16r7fffffff) % len hasht; 482 (n, a) := hasht[slot]; 483 for(i := 0; i < n; i++) 484 if(a[i] == v) 485 return 0; 486 if(n == len a){ 487 if(n == 0) 488 n = 3; 489 t := array[n*3/2] of int; 490 t[0:] = a; 491 hasht[slot].t1 = t; 492 a = t; 493 } 494 a[hasht[slot].t0++] = v; 495 return 1; 496} 497 498error(e: string) 499{ 500 sys->fprint(sys->fildes(2), "stackv: error: %s\n", e); 501 raise "fail:error"; 502} 503