summaryrefslogtreecommitdiff
path: root/appl/wm/deb.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/wm/deb.b')
-rw-r--r--appl/wm/deb.b1444
1 files changed, 1444 insertions, 0 deletions
diff --git a/appl/wm/deb.b b/appl/wm/deb.b
new file mode 100644
index 00000000..fa8208b0
--- /dev/null
+++ b/appl/wm/deb.b
@@ -0,0 +1,1444 @@
+implement WmDebugger;
+
+include "sys.m";
+ sys: Sys;
+ stderr: ref Sys->FD;
+
+include "string.m";
+ str: String;
+
+include "arg.m";
+ arg: Arg;
+
+include "readdir.m";
+ readdir: Readdir;
+
+include "draw.m";
+ draw: Draw;
+
+include "tk.m";
+ tk: Tk;
+
+include "tkclient.m";
+ tkclient: Tkclient;
+
+include "dialog.m";
+ dialog: Dialog;
+
+include "selectfile.m";
+ selectfile: Selectfile;
+
+include "tabs.m";
+ tabs: Tabs;
+
+include "debug.m";
+ debug: Debug;
+ Prog, Exp, Module, Src, Sym: import debug;
+
+include "wmdeb.m";
+ debdata: DebData;
+ Vars: import debdata;
+ debsrc: DebSrc;
+ opendir, Mod: import debsrc;
+
+WmDebugger: module
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+icondir : con "debug/";
+
+tkconfig := array[] of {
+ "frame .m -relief raised -bd 1",
+ "frame .p -padx 2",
+ "frame .ctls -padx 2",
+ "frame .body",
+
+ # menu bar
+ "menubutton .m.file -text File -menu .m.file.menu",
+ "menubutton .m.search -text Search -menu .m.search.menu",
+ "button .m.stack -text Stack -command {send m stack}",
+ "pack .m.file .m.search .m.stack -side left",
+
+ # file menu
+ "menu .m.file.menu",
+ ".m.file.menu add command -label Open... -command {send m open}",
+ ".m.file.menu add command -label Thread... -command {send m pickup}",
+ ".m.file.menu add command -label Options... -command {send m options}",
+ ".m.file.menu add separator",
+
+ # search menu
+ "menu .m.search.menu",
+ ".m.search.menu add command -state disabled"+
+ " -label Look -command {send m look}",
+ ".m.search.menu add command -state disabled"+
+ " -label {Search For} -command {send m search}",
+
+ # program control
+ "image create bitmap Detach -file "+icondir+
+ "detach.bit -maskfile "+icondir+"detach.mask",
+ "image create bitmap Kill -file "+icondir+
+ "kill.bit -maskfile "+icondir+"kill.mask",
+ "image create bitmap Run -file "+icondir+
+ "run.bit -maskfile "+icondir+"run.mask",
+ "image create bitmap Stop -file "+icondir+
+ "stop.bit -maskfile "+icondir+"stop.mask",
+ "image create bitmap Bpt -file "+icondir+
+ "break.bit -maskfile "+icondir+"break.mask",
+ "image create bitmap Stepop -file "+icondir+
+ "stepop.bit -maskfile "+icondir+"stepop.mask",
+ "image create bitmap Stepin -file "+icondir+
+ "stepin.bit -maskfile "+icondir+"stepin.mask",
+ "image create bitmap Stepout -file "+icondir+
+ "stepout.bit -maskfile "+icondir+"stepout.mask",
+ "image create bitmap Stepover -file "+icondir+
+ "stepover.bit -maskfile "+icondir+"stepover.mask",
+ "button .p.kill -image Kill -command {send m killall}"+
+ " -state disabled -relief sunken",
+ "bind .p.kill <Enter> +{.p.status configure -text {kill current process}}",
+ "bind .p.kill <Leave> +{.p.status configure -text {}}",
+ "button .p.detach -image Detach -command {send m detach}"+
+ " -state disabled -relief sunken",
+ "bind .p.detach <Enter> +{.p.status configure -text {stop debugging current process}}",
+ "bind .p.detach <Leave> +{.p.status configure -text {}}",
+ "button .p.run -image Run -command {send m run}"+
+ " -state disabled -relief sunken",
+ "bind .p.run <Enter> +{.p.status configure -text {run to breakpoint}}",
+ "bind .p.run <Leave> +{.p.status configure -text {}}",
+ "button .p.step -image Stepop -command {send m step}"+
+ " -state disabled -relief sunken",
+ "bind .p.step <Enter> +{.p.status configure -text {step one operation}}",
+ "bind .p.step <Leave> +{.p.status configure -text {}}",
+ "button .p.stmt -image Stepin -command {send m stmt}"+
+ " -state disabled -relief sunken",
+ "bind .p.stmt <Enter> +{.p.status configure -text {step one statement}}",
+ "bind .p.stmt <Leave> +{.p.status configure -text {}}",
+ "button .p.over -image Stepover -command {send m over}"+
+ " -state disabled -relief sunken",
+ "bind .p.over <Enter> +{.p.status configure -text {step over calls}}",
+ "bind .p.over <Leave> +{.p.status configure -text {}}",
+ "button .p.out -image Stepout -command {send m out}"+
+ " -state disabled -relief sunken",
+ "bind .p.out <Enter> +{.p.status configure -text {step out of fn}}",
+ "bind .p.out <Leave> +{.p.status configure -text {}}",
+ "button .p.bpt -image Bpt -command {send m setbpt}"+
+ " -state disabled -relief sunken",
+ "bind .p.bpt <Enter> +{.p.status configure -text {set/clear breakpoint}}",
+ "bind .p.bpt <Leave> +{.p.status configure -text {}}",
+ "frame .p.steps",
+ "label .p.status -anchor w",
+ "pack .p.step .p.stmt .p.over .p.out -in .p.steps -side left -fill y",
+ "pack .p.kill .p.detach .p.run .p.steps .p.bpt -side left -padx 5 -fill y",
+ "pack .p.status -side left -expand 1 -fill x",
+
+ # progs
+ "frame .prog",
+ "label .prog.l -text Threads",
+ "canvas .prog.d -height 1 -width 1 -relief sunken -bd 2",
+ "frame .prog.v",
+ ".prog.d create window 0 0 -window .prog.v -anchor nw",
+ "pack .prog.l -side top -anchor w",
+ "pack .prog.d -side left -fill both -expand 1",
+
+ # breakpoints
+ "frame .bpt",
+ "label .bpt.l -text Break",
+ "canvas .bpt.d -height 1 -width 1 -relief sunken -bd 2",
+ "frame .bpt.v",
+ ".bpt.d create window 0 0 -window .bpt.v -anchor nw",
+ "pack .bpt.l -side top -anchor w",
+ "pack .bpt.d -side left -fill both -expand 1",
+
+ "pack .prog .bpt -side top -fill both -expand 1 -in .ctls",
+
+ # test body
+ "frame .body.ft -bd 1 -relief sunken -width 60w -height 20h",
+ "scrollbar .body.scy",
+ "pack .body.scy -side right -fill y",
+
+ "pack .body.ft -side top -expand 1 -fill both",
+ "pack propagate .body.ft 0",
+
+ "pack .m .p -side top -fill x",
+ "pack .ctls -side left -fill y",
+
+ "scrollbar .body.scx -orient horizontal",
+ "pack .body.scx -side bottom -fill x",
+
+ "pack .body -expand 1 -fill both",
+
+ "pack propagate . 0",
+
+ "raise .; update; cursor -default"
+};
+
+# commands for disabling or enabling buttons
+searchoff := array[] of {
+ ".m.search.menu entryconfigure 0 -state disabled",
+ ".m.search.menu entryconfigure 1 -state disabled",
+ ".m.search.menu entryconfigure 2 -state disabled",
+};
+searchon := array[] of {
+ ".m.search.menu entryconfigure 0 -state normal",
+ ".m.search.menu entryconfigure 1 -state normal",
+ ".m.search.menu entryconfigure 2 -state normal",
+};
+tkstopped := array[] of {
+ ".p.bpt configure -state normal -relief raised",
+ ".p.detach configure -state normal -relief raised",
+ ".p.kill configure -state normal -relief raised",
+ ".p.out configure -state normal -relief raised",
+ ".p.over configure -state normal -relief raised",
+ ".p.run configure -state normal -relief raised -image Run -command {send m run}",
+ ".p.step configure -state normal -relief raised",
+ ".p.stmt configure -state normal -relief raised",
+};
+tkrunning := array[] of {
+ ".p.bpt configure -state normal -relief raised",
+ ".p.detach configure -state normal -relief raised",
+ ".p.kill configure -state normal -relief raised",
+ ".p.out configure -state disabled -relief sunken",
+ ".p.over configure -state disabled -relief sunken",
+ ".p.run configure -state normal -relief raised -image Stop -command {send m stop}",
+ ".p.step configure -state disabled -relief sunken",
+ ".p.stmt configure -state disabled -relief sunken",
+};
+tkexited := array[] of {
+ ".p.bpt configure -state normal -relief raised",
+ ".p.detach configure -state normal -relief raised",
+ ".p.kill configure -state normal -relief raised",
+ ".p.out configure -state disabled -relief sunken",
+ ".p.over configure -state disabled -relief sunken",
+ ".p.run configure -state disabled -relief sunken -image Run -command {send m run}",
+ ".p.step configure -state disabled -relief sunken",
+ ".p.stmt configure -state disabled -relief sunken",
+ ".p.stop configure -state disabled -relief sunken",
+};
+tkloaded := array[] of {
+ ".p.bpt configure -state normal -relief raised",
+ ".p.detach configure -state disabled -relief sunken",
+ ".p.kill configure -state disabled -relief sunken",
+ ".p.out configure -state disabled -relief sunken",
+ ".p.over configure -state disabled -relief sunken",
+ ".p.run configure -state normal -relief raised -image Run -command {send m run}",
+ ".p.step configure -state disabled -relief sunken",
+ ".p.stmt configure -state disabled -relief sunken",
+};
+tknobody := array[] of {
+ ".p.bpt configure -state disabled -relief sunken",
+ ".p.detach configure -state disabled -relief sunken",
+ ".p.kill configure -state disabled -relief sunken",
+ ".p.out configure -state disabled -relief sunken",
+ ".p.over configure -state disabled -relief sunken",
+ ".p.run configure -state disabled -relief sunken -image Run -command {send m run}",
+ ".p.step configure -state disabled -relief sunken",
+ ".p.stmt configure -state disabled -relief sunken",
+};
+
+#tk option dialog
+tkoptpack := array[] of {
+ "frame .buts",
+
+ "pack .opts -side left -padx 10 -pady 5",
+};
+
+tkoptions := array[] of {
+ # general options
+ "frame .gen",
+ "frame .mod",
+ "label .modlab -text 'Source of executable module",
+ "entry .modent",
+ "pack .modlab -in .mod -anchor w",
+ "pack .modent -in .mod -fill x",
+
+ "frame .arg",
+ "label .arglab -text 'Program Arguments",
+ "entry .argent -width 300",
+ "pack .arglab -in .arg -anchor w",
+ "pack .argent -in .arg -fill x",
+
+ "frame .wd",
+ "label .wdlab -text 'Working Directory",
+ "entry .wdent",
+ "pack .wdlab -in .wd -anchor w",
+ "pack .wdent -in .wd -fill x",
+
+ "pack .mod .arg .wd -fill x -anchor w -pady 10 -in .gen",
+
+ # thread control options
+ "frame .prog",
+ "frame .new",
+ "radiobutton .new.run -variable new -value r -text 'Run new threads",
+ "radiobutton .new.block -variable new -value b -text 'Block new threads",
+ "pack .new.block .new.run -anchor w",
+ "frame .x",
+ "radiobutton .x.kill -variable exit -value k -text 'Kill threads on exit",
+ "radiobutton .x.detach -variable exit -value d -text 'Detach threads on exit",
+ "pack .x.kill .x.detach -anchor w",
+ "pack .new .x -expand 1 -anchor w -in .prog",
+
+ # layout options
+ "frame .layout",
+ "frame .line",
+ "radiobutton .line.wrap -variable wrap -value w -text 'Wrap lines",
+ "radiobutton .line.scroll -variable wrap -value s -text 'Horizontal scroll",
+ "pack .line.wrap .line.scroll -anchor w",
+ "frame .crlf",
+ "radiobutton .crlf.no -variable crlf -value n -text 'CR/LF as is",
+ "radiobutton .crlf.yes -variable crlf -value y -text 'CR/LF -> LF",
+ "pack .crlf.no .crlf.yes -anchor w",
+ "pack .line .crlf -expand 1 -anchor w -in .layout",
+};
+
+tkopttabs := array[] of {
+ ("General", ".gen"),
+ ("Thread", ".prog"),
+ ("Layout", ".layout"),
+};
+
+# prog listing dialog box
+tkpicktab := array[] of {
+ "frame .progs",
+ "scrollbar .progs.s -command '.progs.p yview",
+ "listbox .progs.p -width 35w -yscrollcommand '.progs.s set",
+ "bind .progs.p <Double-Button-1> 'send cmd prog",
+ "pack .progs.s -side right -fill y",
+ "pack .progs.p -fill both -expand 1",
+
+ "frame .buts",
+ "button .buts.prog -text {Add Thread} -command 'send cmd prog",
+ "button .buts.grp -text {Add Group} -command 'send cmd group",
+ "pack .buts.prog .buts.grp -expand 1 -side left -fill x -padx 4 -pady 4",
+
+ "pack .progs -fill both -expand 1",
+ "pack .buts -fill x",
+ "pack propagate . 0",
+};
+
+Bpt: adt
+{
+ id: int;
+ m: ref Mod;
+ pc: int;
+};
+
+Recv, Send, Alt, Running, Stopped, Exited, Broken, Killing, Killed: con iota;
+status := array[] of
+{
+ Running => "Running",
+ Recv => "Receive",
+ Send => "Send",
+ Alt => "Alt",
+ Stopped => "Stopped",
+ Exited => "Exited",
+ Broken => "Broken",
+ Killing => "Killed",
+ Killed => "Killed",
+};
+
+tktools : array of array of string;
+toolstate : array of string;
+
+KidGrab, KidStep, KidStmt, KidOver, KidOut, KidKill, KidRun: con iota;
+Kid: adt
+{
+ state: int;
+ prog: ref Prog;
+ watch: int; # pid of watching prog
+ run: int; # pid of stepping prog
+ pickup: int; # picking up this kid?
+ cmd: chan of int;
+ stack: ref Vars;
+};
+
+Options: adt
+{
+ start: string; # src of module to start
+ mod: ref Mod; # module to start
+ wm: int; # program is a wm program?
+ path: array of string;# search path for .src and .sbl
+ args: list of string; # argument for starting a kid
+ dir: string; # . for kid
+ tabs: int; # options to show
+ nrun: int; # run new kids?
+ xkill: int; # kill kids on exit?
+ xscroll: int; # horizontal scrolling
+ remcr: int; # CR/LF -> LF
+};
+
+tktop: ref Tk->Toplevel;
+kids: list of ref Kid;
+kid: ref Kid;
+kidctxt: ref Draw->Context;
+kidack: chan of (ref Kid, string);
+kidevent: chan of (ref Kid, string);
+bpts: list of ref Bpt;
+bptid:= 1;
+title: string;
+runok := 0;
+context: ref Draw->Context;
+opts: ref Options;
+dbpid: int;
+searchfor: string;
+initsrc: string;
+
+badmodule(p: string)
+{
+ sys->fprint(sys->fildes(2), "deb: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ if (ctxt == nil) {
+ sys->fprint(sys->fildes(2), "deb: no window context\n");
+ raise "fail:bad context";
+ }
+ draw = load Draw Draw->PATH;
+ tk = load Tk Tk->PATH;
+ tkclient = load Tkclient Tkclient->PATH;
+ if(tkclient == nil)
+ badmodule(Tkclient->PATH);
+ selectfile = load Selectfile Selectfile->PATH;
+ if(selectfile == nil)
+ badmodule(Selectfile->PATH);
+ dialog = load Dialog Dialog->PATH;
+ if(dialog == nil)
+ badmodule(Dialog->PATH);
+ tabs = load Tabs Tabs->PATH;
+ if(tabs == nil)
+ badmodule(Tabs->PATH);
+ str = load String String->PATH;
+ if(str == nil)
+ badmodule(String->PATH);
+ readdir = load Readdir Readdir->PATH;
+ if(readdir == nil)
+ badmodule(Readdir->PATH);
+ debug = load Debug Debug->PATH;
+ if(debug == nil)
+ badmodule(Debug->PATH);
+ debdata = load DebData DebData->PATH;
+ if(debdata == nil)
+ badmodule(DebData->PATH);
+ debsrc = load DebSrc DebSrc->PATH;
+ if(debsrc == nil)
+ badmodule(DebSrc->PATH);
+ arg = load Arg Arg->PATH;
+ if(arg == nil)
+ badmodule(Arg->PATH);
+ dbpid = sys->pctl(Sys->NEWPGRP, nil);
+ opts = ref Options;
+ opts.tabs = 0;
+ opts.nrun = 0;
+ opts.xkill = 1;
+ opts.xscroll = 0;
+ opts.remcr = 0;
+ readopts(opts);
+ sysnam := sysname();
+ context = ctxt;
+
+ grabpids: list of int;
+ arg->init(argv);
+ arg->setusage("wmdeb [-p pid]");
+ while((opt := arg->opt()) != 0){
+ case opt {
+ 'f' =>
+ initsrc = arg->earg();
+ 'p' =>
+ grabpids = int arg->earg() :: grabpids;
+ * =>
+ arg->usage();
+ }
+ }
+ for(argv = arg->argv(); argv != nil; argv = tl argv)
+ grabpids = int hd argv :: grabpids;
+ arg = nil;
+
+ pickdummy := chan of int;
+ pickchan := pickdummy;
+ optdummy := chan of ref Options;
+ optchan := optdummy;
+
+ tktools = array[] of {
+ Running => tkrunning,
+ Recv => tkrunning,
+ Send => tkrunning,
+ Alt => tkrunning,
+ Stopped => tkstopped,
+ Exited => tkexited,
+ Broken => tkexited,
+ Killing => tkexited,
+ Killed => tkexited,
+ };
+
+
+ tkclient->init();
+ selectfile->init();
+ dialog->init();
+ tabs->init();
+
+ title = sysnam+":Wmdeb";
+ titlebut := chan of string;
+ (tktop, titlebut) = tkclient->toplevel(context, nil, title, Tkclient->Appl);
+ tkcmd("cursor -bitmap cursor.wait");
+
+ debug->init();
+ kidctxt = ctxt;
+
+ stderr = sys->fildes(2);
+
+ debsrc->init(context, tktop, tkclient, selectfile, dialog, str, debug, opts.xscroll, opts.remcr);
+ (datatop, datactl, datatitle) := debdata->init(context, nil, debsrc, str, debug);
+
+ m := chan of string;
+ tk->namechan(tktop, m, "m");
+ toolstate = tknobody;
+ tkcmds(tktop, tkconfig);
+ if(!opts.xscroll){
+ tkcmd("pack forget .body.scx");
+ tkcmd("pack .body -expand 1 -fill both; update");
+ }
+
+ tkcmd("cursor -default");
+ tkclient->onscreen(tktop, nil);
+ tkclient->startinput(tktop, "kbd" :: "ptr" :: nil);
+
+ kids = nil;
+ kid = nil;
+ kidack = chan of (ref Kid, string);
+ kidevent = chan of (ref Kid, string);
+
+ # pick up a src file, a kid?
+ if(initsrc != nil)
+ open1(initsrc);
+ else if(grabpids != nil)
+ for(; grabpids != nil; grabpids = tl grabpids)
+ pickup(hd grabpids);
+
+ for(exiting := 0; !exiting || kids != nil; ){
+ tkcmd("update");
+ alt {
+ c := <-tktop.ctxt.kbd =>
+ tk->keyboard(tktop, c);
+ p := <-tktop.ctxt.ptr =>
+ tk->pointer(tktop, *p);
+ s := <-tktop.ctxt.ctl or
+ s = <-tktop.wreq or
+ s = <-titlebut =>
+ case s{
+ "exit" =>
+ if(!exiting){
+ if(opts.xkill)
+ killkids();
+ else
+ detachkids();
+ tkcmd("destroy .");
+ }
+ exiting = 1;
+ break;
+ "task" =>
+ spawn task(tktop);
+ * =>
+ tkclient->wmctl(tktop, s);
+ }
+ c := <-datatop.ctxt.kbd =>
+ tk->keyboard(datatop, c);
+ p := <-datatop.ctxt.ptr =>
+ tk->pointer(datatop, *p);
+ s := <-datactl =>
+ debdata->ctl(s);
+ s := <-datatop.wreq or
+ s = <-datatop.ctxt.ctl or
+ s = <-datatitle =>
+ case s{
+ "task" =>
+ spawn debdata->wmctl(s);
+ * =>
+ debdata->wmctl(s);
+ }
+ o := <-optchan =>
+ if(o != nil && checkopts(o))
+ opts = o;
+ optchan = optdummy;
+ p := <-pickchan =>
+ if(p < 0){
+ pickchan = pickdummy;
+ break;
+ }
+ k := pickup(p);
+ if(k != nil && k != kid){
+ kid = k;
+ refresh(k);
+ }
+ s := <-m =>
+ case s {
+ "open" =>
+ open();
+ "pickup" =>
+ if(pickchan == pickdummy){
+ pickchan = chan of int;
+ spawn pickprog(pickchan);
+ }
+ "options" =>
+ if(optchan == optdummy){
+ optchan = chan of ref Options;
+ spawn options(opts, optchan);
+ }
+ "step" =>
+ step(kid, KidStep);
+ "over" =>
+ step(kid, KidOver);
+ "out" =>
+ step(kid, KidOut);
+ "stmt" =>
+ step(kid, KidStmt);
+ "run" =>
+ step(kid, KidRun);
+ "stop" =>
+ if(kid != nil)
+ kid.prog.stop();
+ "killall" =>
+ killkids();
+ "kill" =>
+ killkid(kid);
+ "detach" =>
+ detachkid(kid);
+ "setbpt" =>
+ setbpt();
+ "look" =>
+ debsrc->search(debsrc->snarf());
+ "search" =>
+ s = dialog->getstring(context, tktop.image, "Search For");
+ if(s == ""){
+ tkcmd(".m.search.menu delete 2");
+ }else{
+ if(searchfor == "")
+ tkcmd(".m.search.menu add command -command {send m research}");
+ tkcmd(".m.search.menu entryconfigure 2 -label '/"+s);
+ debsrc->search(s);
+ }
+ searchfor = s;
+ "research" =>
+ debsrc->search(searchfor);
+ "stack" =>
+ if(debdata != nil)
+ debdata->raisex();
+ * =>
+ if(str->prefix("open ", s))
+ debsrc->showstrsrc(s[len "open ":]);
+ else if(str->prefix("seeprog ", s))
+ seekid(int s[len "seeprog ":]);
+ else if(str->prefix("seebpt ", s))
+ seebpt(int s[len "seebpt ":]);
+ }
+ (k, s) := <-kidevent =>
+ case s{
+ "recv" =>
+ if(k.state == Running)
+ k.state = Recv;
+ "send" =>
+ if(k.state == Running)
+ k.state = Send;
+ "alt" =>
+ if(k.state == Running)
+ k.state = Alt;
+ "run" =>
+ if(k.state == Recv || k.state == Send || k.state == Alt)
+ k.state = Running;
+ "exited" =>
+ k.state = Exited;
+ "interrupted" or
+ "killed" =>
+ alert("Thread "+string k.prog.id+" "+s);
+ k.state = Exited;
+ * =>
+ if(str->prefix("new ", s)){
+ nk := newkid(int s[len "new ":]);
+ if(opts.nrun)
+ step(nk, KidRun);
+ break;
+ }
+ if(str->prefix("load ", s)){
+ s = s[len "load ":];
+ if(s != nil && s[0] != '$')
+ loaded(s);
+ break;
+ }
+ if(str->prefix("child: ", s))
+ s = s[len "child: ":];
+
+ if(str->prefix("broken: ", s))
+ k.state = Broken;
+ alert("Thread "+string k.prog.id+" "+s);
+ }
+ if(k == kid && k.state != Running)
+ refresh(k);
+ k = nil;
+ (k, s) := <-kidack =>
+ if(k.state == Killing){
+ k.state = Killed;
+ k.cmd <-= KidKill;
+ k = nil;
+ break;
+ }
+ if(k.state == Killed){
+ delkid(k);
+ k = nil;
+ break;
+ }
+ case s{
+ "" or "child: breakpoint" or "child: stopped" =>
+ k.state = Stopped;
+ k.prog.unstop();
+ "prog broken" =>
+ k.state = Broken;
+ * =>
+ if(!str->prefix("child: ", s))
+ alert("Debugger error "+status[k.state]+" "+string k.prog.id+" '"+s+"'");
+ }
+ if(k == kid)
+ refresh(k);
+ if(k.pickup && opts.nrun){
+ k.pickup = 0;
+ if(k.state == Stopped)
+ step(k, KidRun);
+ }
+ k = nil;
+ }
+ }
+ exitdb();
+}
+
+task(top: ref Tk->Toplevel)
+{
+ tkclient->wmctl(top, "task");
+}
+
+open()
+{
+ pattern := list of {
+ "*.b (Limbo source files)",
+ "* (All files)"
+ };
+
+ file := selectfile->filename(context, tktop.image, "Open source file", pattern, opendir);
+ if(file != nil)
+ open1(file);
+}
+
+open1(file: string)
+{
+ (opendir, nil) = str->splitr(file, "/");
+ if(opendir == "")
+ opendir = ".";
+ m := debsrc->loadsrc(file, 1);
+ if(m == nil){
+ alert("Can't open "+file);
+ return;
+ }
+ debsrc->showmodsrc(m, ref Src((file, 1, 0), (file, 1, 0)));
+ kidstate();
+ if(opts.start == nil){
+ opts.start = file;
+ opts.mod = m;
+ }
+ if(opts.dir == "")
+ opts.dir = opendir;
+}
+
+options(oo: ref Options, r: chan of ref Options)
+{
+ (t, titlebut) := tkclient->toplevel(context, nil, "Wmdeb Options", tkclient->OK);
+
+ tkcmds(t, tkoptions);
+ tabsctl := tabs->mktabs(t, ".opts", tkopttabs, oo.tabs);
+ tkcmds(t, tkoptpack);
+
+ o := ref *oo;
+ if(o.start != nil)
+ tk->cmd(t, ".modent insert end '"+o.start);
+ args := "";
+ for(oa := o.args; oa != nil; oa = tl oa){
+ if(args == "")
+ args = hd oa;
+ else
+ args += " " + hd oa;
+ }
+ tk->cmd(t, ".argent insert end '"+args);
+ tk->cmd(t, ".wdent insert end '"+o.dir);
+ if(o.xkill)
+ tk->cmd(t, ".x.kill invoke");
+ else
+ tk->cmd(t, ".x.detach invoke");
+ if(o.nrun)
+ tk->cmd(t, ".new.run invoke");
+ else
+ tk->cmd(t, ".new.block invoke");
+ if(o.xscroll)
+ tk->cmd(t, ".line.scroll invoke");
+ else
+ tk->cmd(t, ".line.wrap invoke");
+ if(o.remcr)
+ tk->cmd(t, ".crlf.yes invoke");
+ else
+ tk->cmd(t, ".crlf.no invoke");
+
+ tk->cmd(t, ".killkids configure -command 'send cmd kill");
+ tk->cmd(t, ".runkids configure -command 'send cmd run");
+ tkclient->onscreen(t, nil);
+ tkclient->startinput(t, "ptr" :: "kbd" :: nil);
+
+out: for(;;){
+ tk->cmd(t, "update");
+ alt{
+ c := <-t.ctxt.kbd =>
+ tk->keyboard(t, c);
+ m := <-t.ctxt.ptr =>
+ tk->pointer(t, *m);
+ s := <-tabsctl =>
+ o.tabs = tabs->tabsctl(t, ".opts", tkopttabs, o.tabs, s);
+ s := <-t.ctxt.ctl or
+ s = <-t.wreq or
+ s = <-titlebut =>
+ case s{
+ "exit" =>
+ r <-= nil;
+ exit;
+ "ok" =>
+ break out;
+ }
+ tkclient->wmctl(t, s);
+ }
+ }
+ xscroll := o.xscroll;
+ o.start = tk->cmd(t, ".modent get");
+ (nil, o.args) = sys->tokenize(tk->cmd(t, ".argent get"), " \t\n");
+ o.dir = tk->cmd(t, ".wdent get");
+ case tk->cmd(t, "variable new"){
+ "r" => o.nrun = 1;
+ "b" => o.nrun = 0;
+ }
+ case tk->cmd(t, "variable exit"){
+ "k" => o.xkill = 1;
+ "d" => o.xkill = 0;
+ }
+ case tk->cmd(t, "variable wrap"){
+ "s" => o.xscroll = 1;
+ "w" => o.xscroll = 0;
+ }
+ case tk->cmd(t, "variable crlf"){
+ "y" => o.remcr = 1;
+ "n" => o.remcr = 0;
+ }
+ if(o.xscroll != xscroll){
+ if(o.xscroll)
+ tkcmd("pack .body.scx -side bottom -fill x");
+ else
+ tkcmd("pack forget .body.scx");
+ tkcmd("pack .body -expand 1 -fill both; update");
+ }
+ debsrc->reinit(o.xscroll, o.remcr);
+ writeopts(o);
+ r <-= o;
+}
+
+checkopts(o: ref Options): int
+{
+ if(o.start != ""){
+ o.mod = debsrc->loadsrc(o.start, 1);
+ if(o.mod == nil)
+ o.start = "";
+ }
+ return 1;
+}
+
+pickprog(c: chan of int)
+{
+ (t, titlebut) := tkclient->toplevel(context, nil, "Wmdeb Thread List", 0);
+ cmd := chan of string;
+ tk->namechan(t, cmd, "cmd");
+
+ tkcmds(t, tkpicktab);
+ tk->cmd(t, "update");
+ ids := addpickprogs(t);
+ tkclient->onscreen(t, nil);
+ tkclient->startinput(t, "ptr" :: "kbd" :: nil);
+
+ for(;;){
+ tk->cmd(t, "update");
+ alt{
+ key := <-t.ctxt.kbd =>
+ tk->keyboard(t, key);
+ m := <-t.ctxt.ptr =>
+ tk->pointer(t, *m);
+ s := <-t.ctxt.ctl or
+ s = <-t.wreq or
+ s = <-titlebut =>
+ if(s == "exit"){
+ c <-= -1;
+ exit;
+ }
+ tkclient->wmctl(t, s);
+ s := <-cmd =>
+ case s{
+ "ok" =>
+ c <-= -1;
+ exit;
+ "prog" =>
+ sel := tk->cmd(t, ".progs.p curselection");
+ if(sel == "")
+ break;
+ pid := int tk->cmd(t, ".progs.p get "+sel);
+ c <-= pid;
+ "group" =>
+ sel := tk->cmd(t, ".progs.p curselection");
+ if(sel == "")
+ break;
+ nid := int sel;
+ if(nid > len ids || nid < 0)
+ break;
+ (nil, gid) := ids[nid];
+ nid = len ids;
+ for(i := 0; i < nid; i++){
+ (p, g) := ids[i];
+ if(g == gid)
+ c <-= p;
+ }
+ }
+ }
+ }
+}
+
+addpickprogs(t: ref Tk->Toplevel): array of (int, int)
+{
+ (d, n) := readdir->init("/prog", Readdir->NONE);
+ if(n <= 0)
+ return nil;
+ a := array[n] of { * => (-1, -1) };
+ for(i := 0; i < n; i++){
+ (p, nil) := debug->prog(int d[i].name);
+ if(p == nil)
+ continue;
+ (grp, nil, st, code) := debug->p.status();
+ if(grp < 0)
+ continue;
+ a[i] = (p.id, grp);
+ tk->cmd(t, ".progs.p insert end '"+
+ sys->sprint("%4d %4d %8s %s", p.id, grp, st, code));
+ }
+ return a;
+}
+
+step(k: ref Kid, cmd: int)
+{
+ if(k == nil){
+ if(kids != nil){
+ alert("No current thread");
+ return;
+ }
+ k = spawnkid(opts);
+ kid = k;
+ if(k != nil)
+ refresh(k);
+ return;
+ }
+ case k.state{
+ Stopped =>
+ k.cmd <-= cmd;
+ k.state = Running;
+ if(k == kid)
+ kidstate();
+ Running or Send or Recv or Alt or Exited or Broken =>
+ ;
+ * =>
+ sys->print("bad debug step state %d\n", k.state);
+ }
+}
+
+setbpt()
+{
+ (m, pc) := debsrc->getsel();
+ if(m == nil)
+ return;
+ s := m.sym.pctosrc(pc);
+ if(s == nil){
+ alert("No pc is appropriate");
+ return;
+ }
+
+ # if the breakpoint is already there, delete it
+ for(bl := bpts; bl != nil; bl = tl bl){
+ b := hd bl;
+ if(b.m == m && b.pc == pc){
+ bpts = delbpt(b, bpts);
+ return;
+ }
+ }
+
+ b := ref Bpt(bptid++, m, pc);
+ bpts = b :: bpts;
+ debsrc->attachdis(m);
+ for(kl := kids; kl != nil; kl = tl kl){
+ k := hd kl;
+ k.prog.setbpt(m.dis, pc);
+ }
+
+ # mark the breakpoint text
+ tkcmd(m.tk+" tag add bpt "+string s.start.line+"."+string s.start.pos+" "+string s.stop.line+"."+string s.stop.pos);
+
+ # add the kid to the breakpoint window
+ me := ".bpt.v."+string b.id;
+ tkcmd("label "+me+" -text "+string b.id);
+ tkcmd("pack "+me+" -side top -fill x");
+ tkcmd("bind "+me+" <ButtonRelease-1> {send m seebpt "+string b.id+"}");
+ updatebpts();
+}
+
+seebpt(bpt: int)
+{
+ for(bl := bpts; bl != nil; bl = tl bl){
+ b := hd bl;
+ if(b.id == bpt){
+ s := b.m.sym.pctosrc(b.pc);
+ debsrc->showmodsrc(b.m, s);
+ return;
+ }
+ }
+}
+
+delbpt(b: ref Bpt, bpts: list of ref Bpt): list of ref Bpt
+{
+ if(bpts == nil)
+ return nil;
+ hb := hd bpts;
+ tb := tl bpts;
+ if(b == hb){
+ # remove mark from breakpoint text
+ s := b.m.sym.pctosrc(b.pc);
+ tkcmd(b.m.tk+" tag remove bpt "+string s.start.line+"."+string s.start.pos+" "+string s.stop.line+"."+string s.stop.pos);
+
+ # remove the breakpoint window
+ tkcmd("destroy .bpt.v."+string b.id);
+
+ # remove from kids
+ disablebpt(b);
+ return tb;
+ }
+ return hb :: delbpt(b, tb);
+
+}
+
+disablebpt(b: ref Bpt)
+{
+ for(kl := kids; kl != nil; kl = tl kl){
+ k := hd kl;
+ k.prog.delbpt(b.m.dis, b.pc);
+ }
+}
+
+updatebpts()
+{
+tkcmd("update");
+ tkcmd(".bpt.d configure -scrollregion {0 0 [.bpt.v cget -width] [.bpt.v cget -height]}");
+}
+
+seekid(pid: int)
+{
+ for(kl := kids; kl != nil; kl = tl kl){
+ k := hd kl;
+ if(k.prog.id == pid){
+ kid = k;
+ kid.stack.show();
+ refresh(kid);
+ return;
+ }
+ }
+}
+
+delkid(k: ref Kid)
+{
+ kids = rdelkid(k, kids);
+ if(kid == k){
+ if(kids == nil){
+ kid = nil;
+ kidstate();
+ }else{
+ kid = hd kids;
+ refresh(kid);
+ }
+ }
+}
+
+rdelkid(k: ref Kid, kids: list of ref Kid): list of ref Kid
+{
+ if(kids == nil)
+ return nil;
+ hk := hd kids;
+ t := tl kids;
+ if(k == hk){
+ # remove kid from display
+ k.stack.delete();
+ tkcmd("destroy .prog.v."+string k.prog.id);
+ updatekids();
+ return t;
+ }
+ return hk :: rdelkid(k, t);
+}
+
+updatekids()
+{
+tkcmd("update");
+ tkcmd(".prog.d configure -scrollregion {0 0 [.prog.v cget -width] [.prog.v cget -height]}");
+}
+
+killkids()
+{
+ for(kl := kids; kl != nil; kl = tl kl)
+ killkid(hd kl);
+}
+
+killkid(k: ref Kid)
+{
+ if(k.watch >= 0){
+ killpid(k.watch);
+ k.watch = -1;
+ }
+ case k.state{
+ Exited or Broken or Stopped =>
+ k.cmd <-= KidKill;
+ k.state = Killed;
+ Running or Send or Recv or Alt or Killing =>
+ k.prog.kill();
+ k.state = Killing;
+ * =>
+ sys->print("unknown state %d in killkid\n", k.state);
+ }
+}
+
+freekids(): int
+{
+ r := 0;
+ for(kl := kids; kl != nil; kl = tl kl){
+ k := hd kl;
+ if(k.state == Exited || k.state == Killing || k.state == Killed){
+ r ++;
+ detachkid(k);
+ }
+ }
+ return r;
+}
+
+detachkids()
+{
+ for(kl := kids; kl != nil; kl = tl kl)
+ detachkid(hd kl);
+}
+
+detachkid(k: ref Kid)
+{
+ if(k == nil){
+ alert("No current thread");
+ return;
+ }
+ if(k.state == Exited){
+ killkid(k);
+ return;
+ }
+
+ # kill off the debugger progs
+ killpid(k.watch);
+ killpid(k.run);
+ err := k.prog.start();
+ if(err != "")
+ alert("Detaching thread: "+err);
+
+ delkid(k);
+}
+
+kidstate()
+{
+ ts : array of string;
+ if(kid == nil){
+ tkcmd(".Wm_t.title configure -text '"+title);
+ if(debsrc->packed == nil){
+ tkcmds(tktop, searchoff);
+ ts = tknobody;
+ }else{
+ ts = tkloaded;
+ tkcmds(tktop, searchon);
+ }
+ }else{
+ tkcmd(".Wm_t.title configure -text '"+title+" "+string kid.prog.id+" "+status[kid.state]);
+ ts = tktools[kid.state];
+ tkcmds(tktop, searchon);
+ }
+ if(ts != toolstate){
+ toolstate = ts;
+ tkcmds(tktop, ts);
+ }
+}
+
+#
+# update the stack an src displays
+# to reflect the current state of k
+#
+refresh(k: ref Kid)
+{
+ if(k.state == Killing || k.state == Killed){
+ kidstate();
+ return;
+ }
+ (s, err) := k.prog.stack();
+ if(s == nil && err == "")
+ err = "No stack";
+ if(err != ""){
+ kidstate();
+ return;
+ }
+ for(i := 0; i < len s; i++){
+ debsrc->findmod(s[i].m);
+ s[i].findsym();
+ }
+ err = s[0].findsym();
+ src := s[0].src();
+ kidstate();
+ m := s[0].m;
+ if(src == nil && len s > 1){
+ dis := s[0].m.dis();
+ if(len dis > 0 && dis[0] == '$'){
+ m = s[1].m;
+ s[1].findsym();
+ src = s[1].src();
+ }
+ }
+ debsrc->showmodsrc(debsrc->findmod(m), src);
+ k.stack.refresh(s);
+ k.stack.show();
+}
+
+pickup(pid: int): ref Kid
+{
+ for(kl := kids; kl != nil; kl = tl kl)
+ if((hd kl).prog.id == pid)
+ return hd kl;
+ k := newkid(pid);
+ if(k == nil)
+ return nil;
+ k.cmd <-= KidGrab;
+ k.state = Running;
+ k.pickup = 1;
+ if(kid == nil){
+ kid = k;
+ refresh(kid);
+ }
+ return k;
+}
+
+loaded(s: string)
+{
+ for(bl := bpts; bl != nil; bl = tl bl){
+ b := hd bl;
+ debsrc->attachdis(b.m);
+ if(s == b.m.dis){
+ for(kl := kids; kl != nil; kl = tl kl)
+ (hd kl).prog.setbpt(s, b.pc);
+ }
+ }
+}
+
+Enofd: con "no free file descriptors\n";
+
+newkid(pid: int): ref Kid
+{
+ (p, err) := debug->prog(pid);
+ if(err != ""){
+ n := len err - len Enofd;
+ if(n >= 0 && err[n: ] == Enofd && freekids()){
+ (p, err) = debug->prog(pid);
+ if(err == "")
+ return mkkid(p);
+ }
+ alert("Can't pick up thread "+err);
+ return nil;
+ }
+ return mkkid(p);
+}
+
+mkkid(p: ref Prog): ref Kid
+{
+ for(bl := bpts; bl != nil; bl = tl bl){
+ b := hd bl;
+ debsrc->attachdis(b.m);
+ p.setbpt(b.m.dis, b.pc);
+ }
+ k := ref Kid(Stopped, p, -1, -1, 0, chan of int, Vars.create());
+ kids = k :: kids;
+ c := chan of int;
+ spawn kidslave(k, c);
+ k.run = <- c;
+ spawn kidwatch(k, c);
+ k.watch = <-c;
+ me := ".prog.v."+string p.id;
+ tkcmd("label "+me+" -text "+string p.id);
+ tkcmd("pack "+me+" -side top -fill x");
+ tkcmd("bind "+me+" <ButtonRelease-1> {send m seeprog "+string p.id+"}");
+ tkcmd(".prog.d configure -scrollregion {0 0 [.prog.v cget -width] [.prog.v cget -height]}");
+ return k;
+}
+
+spawnkid(o: ref Options): ref Kid
+{
+ m := o.mod;
+ if(m == nil){
+ alert("No module to run");
+ return nil;
+ }
+
+ if(!debsrc->attachdis(m)){
+ alert("Can't load Dis file "+m.dis);
+ return nil;
+ }
+
+ (p, err) := debug->startprog(m.dis, o.dir, kidctxt, m.dis :: o.args);
+ if(err != nil){
+ alert(m.dis+" is not a debuggable Dis command module: "+err);
+ return nil;
+ }
+
+ return mkkid(p);
+}
+
+xlate := array[] of {
+ KidStep => Debug->StepExp,
+ KidStmt => Debug->StepStmt,
+ KidOver => Debug->StepOver,
+ KidOut => Debug->StepOut,
+};
+
+kidslave(k: ref Kid, me: chan of int)
+{
+ me <-= sys->pctl(0, nil);
+ me = nil;
+ for(;;){
+ c := <-k.cmd;
+ case c{
+ KidGrab =>
+ err := k.prog.grab();
+ kidack <-= (k, err);
+ KidStep or KidStmt or KidOver or KidOut =>
+ err := k.prog.step(xlate[c]);
+ kidack <-= (k, err);
+ KidKill =>
+ err := "kill "+k.prog.kill();
+ k.prog.kill(); # kill again to slay blocked progs
+ kidack <-= (k, err);
+ exit;
+ KidRun =>
+ err := k.prog.cont();
+ kidack <-= (k, err);
+ * =>
+ sys->print("kidslave: bad command %d\n", c);
+ exit;
+ }
+ }
+}
+
+kidwatch(k: ref Kid, me: chan of int)
+{
+ me <-= sys->pctl(0, nil);
+ me = nil;
+ for(;;)
+ kidevent <-= (k, k.prog.event());
+}
+
+alert(m: string)
+{
+ dialog->prompt(context, tktop.image, "warning -fg yellow",
+ "Debugger Alert", m, 0, "Dismiss"::nil);
+}
+
+tkcmd(cmd: string): string
+{
+ s := tk->cmd(tktop, cmd);
+# if(len s != 0 && s[0] == '!')
+# sys->print("%s '%s'\n", s, cmd);
+ return s;
+}
+
+sysname(): string
+{
+ fd := sys->open("#c/sysname", sys->OREAD);
+ if(fd == nil)
+ return "Anon";
+ buf := array[128] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return "Anon";
+ return string buf[:n];
+}
+
+tkcmds(top: ref Tk->Toplevel, cmds: array of string)
+{
+ for(i := 0; i < len cmds; i++)
+ tk->cmd(top, cmds[i]);
+}
+
+exitdb()
+{
+ fd := sys->open("#p/"+string dbpid+"/ctl", sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "killgrp");
+ exit;
+}
+
+killpid(pid: int)
+{
+ fd := sys->open("#p/"+string pid+"/ctl", sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "kill");
+}
+
+getuser(): string
+{
+ fd := sys->open("/dev/user", Sys->OREAD);
+ if(fd == nil)
+ return "";
+ buf := array[128] of byte;
+ n := sys->read(fd, buf, len buf);
+ if(n < 0)
+ return "";
+ return string buf[0:n];
+}
+
+debconf(): string
+{
+ return "/usr/" + getuser() + "/lib/deb";
+}
+
+readopts(o: ref Options)
+{
+ fd := sys->open(debconf(), Sys->OREAD);
+ if(fd == nil)
+ return;
+ b := array[4] of byte;
+ if(sys->read(fd, b, 4) != 4)
+ return;
+ o.nrun = int b[0]-'0';
+ o.xkill = int b[1]-'0';
+ o.xscroll = int b[2]-'0';
+ o.remcr = int b[3]-'0';
+}
+
+writeopts(o: ref Options)
+{
+ fd := sys->create(debconf(), Sys->OWRITE, 8r660);
+ if(fd == nil)
+ return;
+ b := array[4] of byte;
+ b[0] = byte (o.nrun+'0');
+ b[1] = byte (o.xkill+'0');
+ b[2] = byte (o.xscroll+'0');
+ b[3] = byte (o.remcr+'0');
+ sys->write(fd, b, 4);
+}