1413 lines
22 KiB
Plaintext
1413 lines
22 KiB
Plaintext
-- front
|
|
6.828 Shells Lecture
|
|
|
|
Hello.
|
|
|
|
-- intro
|
|
Bourne shell
|
|
|
|
Simplest shell: run cmd arg arg ...
|
|
fork
|
|
exec in child
|
|
wait in parent
|
|
|
|
More functionality:
|
|
file redirection: cmd >file
|
|
open file as fd 1 in child before exec
|
|
|
|
Still more functionality:
|
|
pipes: cmd | cmd | cmd ...
|
|
create pipe,
|
|
run first cmd with pipe on fd 1,
|
|
run second cmd with other end of pipe on fd 0
|
|
|
|
More Bourne arcana:
|
|
$* - command args
|
|
"$@" - unexpanded command args
|
|
environment variables
|
|
macro substitution
|
|
if, while, for
|
|
||
|
|
&&
|
|
"foo $x"
|
|
'foo $x'
|
|
`cat foo`
|
|
|
|
-- rc
|
|
Rc Shell
|
|
|
|
|
|
No reparsing of input (except explicit eval).
|
|
|
|
Variables as explicit lists.
|
|
|
|
Explicit concatenation.
|
|
|
|
Multiple input pipes <{cmd} - pass /dev/fd/4 as file name.
|
|
|
|
Syntax more like C, less like Algol.
|
|
|
|
diff <{echo hi} <{echo bye}
|
|
|
|
-- es
|
|
Es shell
|
|
|
|
|
|
rc++
|
|
|
|
Goal is to override functionality cleanly.
|
|
|
|
Rewrite input like cmd | cmd2 as %pipe {cmd} {cmd2}.
|
|
|
|
Users can redefine %pipe, etc.
|
|
|
|
Need lexical scoping and let to allow new %pipe refer to old %pipe.
|
|
|
|
Need garbage collection to collect unreachable code.
|
|
|
|
Design principle:
|
|
minimal functionality + good defaults
|
|
allow users to customize implementations
|
|
|
|
emacs, exokernel
|
|
|
|
-- apps
|
|
Applications
|
|
|
|
Shell scripts are only as good as the programs they use.
|
|
(What good are pipes without cat, grep, sort, wc, etc.?)
|
|
|
|
The more the scripts can access, the more powerful they become.
|
|
|
|
-- acme
|
|
Acme, Plan 9 text editor
|
|
|
|
Make window system control files available to
|
|
everything, including shell.
|
|
|
|
Can write shell scripts to script interactions.
|
|
|
|
/home/rsc/bin/Slide
|
|
/home/rsc/bin/Slide-
|
|
/home/rsc/bin/Slide+
|
|
|
|
/usr/local/plan9/bin/adict
|
|
|
|
win
|
|
|
|
-- javascript
|
|
JavaScript
|
|
|
|
Very powerful
|
|
- not because it's a great language
|
|
- because it has a great data set
|
|
- Google Maps
|
|
- Gmail
|
|
- Ymail
|
|
- etc.
|
|
|
|
-- greasemonkey
|
|
GreaseMonkey
|
|
|
|
// ==UserScript==
|
|
// @name Google Ring
|
|
// @namespace http://swtch.com/greasemonkey/
|
|
// @description Changes Google Logo
|
|
// @include http://*.google.*/*
|
|
// ==/UserScript==
|
|
|
|
(function() {
|
|
for(var i=0; i<document.images.length; i++){
|
|
if(document.images[i].src == "http://www.google.com/intl/en/images/logo.gif")
|
|
document.images[i].src = "http://swtch.com/googlering.png";
|
|
}
|
|
})();
|
|
|
|
-- webscript0
|
|
Webscript
|
|
|
|
Why can't I script my web interactions?
|
|
|
|
/home/rsc/plan9/bin/rc/fedex
|
|
|
|
webscript /home/rsc/src/webscript/a3
|
|
/home/rsc/src/webscript/a2
|
|
|
|
-- acid
|
|
Acid, a programmable (scriptable) debugger
|
|
|
|
defn stopped(pid)
|
|
{
|
|
pfixstop(pid);
|
|
pstop(pid);
|
|
}
|
|
|
|
defn pfixstop(pid)
|
|
{
|
|
if *fmt(*PC-1, 'b') == 0xCC then {
|
|
// Linux stops us after the breakpoint, not at it
|
|
*PC = *PC-1;
|
|
}
|
|
}
|
|
|
|
/usr/local/plan9/acid/port:/^defn.bpset
|
|
/usr/local/plan9/acid/port:/^defn.step
|
|
|
|
defn checkpdb(pdb)
|
|
{
|
|
loop 1,768 do {
|
|
if *pdb != 0 then { print(pdb\X, " ", *pdb\X, "\n"); }
|
|
pdb = pdb +4;
|
|
}
|
|
}
|
|
|
|
-- guis
|
|
GUIs
|
|
|
|
Can we script guis? Not as clear.
|
|
|
|
Acme examples show one way:
|
|
turn events into file (pipe) to read.
|
|
|
|
Tcl/tk is close too.
|
|
|
|
Eventually everyone turns to C.
|
|
|
|
-- others
|
|
Honorable Mentions
|
|
|
|
Scheme
|
|
|
|
Lisp
|
|
|
|
AutoCAD
|
|
|
|
Viaweb RTML
|
|
|
|
-- c
|
|
"Real" programming languages vs. Scripts
|
|
|
|
Why does everyone eventually rewrite scripts in C?
|
|
(aka C++, C#, any compiled language)
|
|
|
|
What do you need C for now?
|
|
|
|
How could you make it accessible to a script language?
|
|
|
|
-- /home/rsc/bin/Slide
|
|
#!/usr/local/plan9/bin/rc
|
|
|
|
echo name `{pwd}^/$1 | 9p write acme/$winid/ctl
|
|
echo clean | 9p write acme/$winid/ctl
|
|
echo get | 9p write acme/$winid/ctl
|
|
|
|
-- /home/rsc/bin/Slide-
|
|
#!/usr/local/plan9/bin/rc
|
|
|
|
name=$%
|
|
current=`{basename $name}
|
|
currentx=`{9 grep -n '^'$current'([ ]|$)' index | sed 's/:.*//'}
|
|
|
|
pagex=`{echo $currentx - 1 | hoc}
|
|
if(~ $pagex 0){
|
|
echo no such page
|
|
exit 0
|
|
}
|
|
page=`{sed -n $pagex^p index | awk '{print $1}'}
|
|
if(~ $#page 0){
|
|
echo no such page
|
|
exit 0
|
|
}
|
|
|
|
Slide $page
|
|
-- /home/rsc/bin/Slide+
|
|
#!/usr/local/plan9/bin/rc
|
|
|
|
name=$%
|
|
current=`{basename $name}
|
|
currentx=`{9 grep -n '^'$current'([ ]|$)' index | sed 's/:.*//'}
|
|
|
|
pagex=`{echo $currentx + 1 | hoc}
|
|
page=`{sed -n $pagex^p index | awk '{print $1}'}
|
|
if(~ $#page 0){
|
|
echo no such page
|
|
exit 0
|
|
}
|
|
|
|
Slide $page
|
|
-- /usr/local/plan9/bin/adict
|
|
#!/usr/local/plan9/bin/rc
|
|
|
|
. 9.rc
|
|
. $PLAN9/lib/acme.rc
|
|
|
|
fn event {
|
|
# $1 - c1 origin of event
|
|
# $2 - c2 type of action
|
|
# $3 - q0 beginning of selection
|
|
# $4 - q1 end of selection
|
|
# $5 - eq0 beginning of expanded selection
|
|
# $6 - eq1 end of expanded selection
|
|
# $7 - flag
|
|
# $8 - nr number of runes in $7
|
|
# $9 - text
|
|
# $10 - chorded argument
|
|
# $11 - origin of chorded argument
|
|
|
|
switch($1$2){
|
|
case E* # write to body or tag
|
|
case F* # generated by ourselves; ignore
|
|
case K* # type away we do not care
|
|
case Mi # mouse: text inserted in tag
|
|
case MI # mouse: text inserted in body
|
|
case Md # mouse: text deleted from tag
|
|
case MD # mouse: text deleted from body
|
|
|
|
case Mx MX # button 2 in tag or body
|
|
winwriteevent $*
|
|
|
|
case Ml ML # button 3 in tag or body
|
|
{
|
|
if(~ $dict NONE)
|
|
dictwin /adict/$9/ $9
|
|
if not
|
|
dictwin /adict/$dict/$9 $dict $9
|
|
} &
|
|
}
|
|
}
|
|
|
|
fn dictwin {
|
|
newwindow
|
|
winname $1
|
|
switch($#*){
|
|
case 1
|
|
dict -d '?' >[2=1] | sed 1d | winwrite body
|
|
case 2
|
|
dict=$2
|
|
case 3
|
|
dict=$2
|
|
dict -d $dict $3 >[2=1] | winwrite body
|
|
}
|
|
winctl clean
|
|
wineventloop
|
|
}
|
|
|
|
dict=NONE
|
|
if(~ $1 -d){
|
|
shift
|
|
dict=$2
|
|
shift
|
|
}
|
|
if(~ $1 -d*){
|
|
dict=`{echo $1 | sed 's/-d//'}
|
|
shift
|
|
}
|
|
if(~ $1 -*){
|
|
echo 'usage: adict [-d dict] [word...]' >[1=2]
|
|
exit usage
|
|
}
|
|
|
|
switch($#*){
|
|
case 0
|
|
if(~ $dict NONE)
|
|
dictwin /adict/
|
|
if not
|
|
dictwin /adict/$dict/ $dict
|
|
case *
|
|
if(~ $dict NONE){
|
|
dict=`{dict -d'?' | 9 sed -n 's/^ ([^\[ ]+).*/\1/p' | sed 1q}
|
|
if(~ $#dict 0){
|
|
echo 'no dictionaries present on this system' >[1=2]
|
|
exit nodict
|
|
}
|
|
}
|
|
for(i)
|
|
dictwin /adict/$dict/$i $dict $i
|
|
}
|
|
|
|
-- /usr/local/plan9/lib/acme.rc
|
|
fn newwindow {
|
|
winctl=`{9p read acme/new/ctl}
|
|
winid=$winctl(1)
|
|
winctl noscroll
|
|
}
|
|
|
|
fn winctl {
|
|
echo $* | 9p write acme/acme/$winid/ctl
|
|
}
|
|
|
|
fn winread {
|
|
9p read acme/acme/$winid/$1
|
|
}
|
|
|
|
fn winwrite {
|
|
9p write acme/acme/$winid/$1
|
|
}
|
|
|
|
fn windump {
|
|
if(! ~ $1 - '')
|
|
winctl dumpdir $1
|
|
if(! ~ $2 - '')
|
|
winctl dump $2
|
|
}
|
|
|
|
fn winname {
|
|
winctl name $1
|
|
}
|
|
|
|
fn winwriteevent {
|
|
echo $1$2$3 $4 | winwrite event
|
|
}
|
|
|
|
fn windel {
|
|
if(~ $1 sure)
|
|
winctl delete
|
|
if not
|
|
winctl del
|
|
}
|
|
|
|
fn wineventloop {
|
|
. <{winread event >[2]/dev/null | acmeevent}
|
|
}
|
|
-- /home/rsc/plan9/rc/bin/fedex
|
|
#!/bin/rc
|
|
|
|
if(! ~ $#* 1) {
|
|
echo usage: fedex 123456789012 >[1=2]
|
|
exit usage
|
|
}
|
|
|
|
rfork e
|
|
|
|
fn bgrep{
|
|
pattern=`{echo $1 | sed 's;/;\\&;'}
|
|
shift
|
|
|
|
@{ echo 'X {
|
|
$
|
|
a
|
|
|
|
.
|
|
}
|
|
X ,x/(.+\n)+\n/ g/'$pattern'/p' |
|
|
sam -d $* >[2]/dev/null
|
|
}
|
|
}
|
|
|
|
fn awk2 {
|
|
awk 'NR%2==1 { a=$0; }
|
|
NR%2==0 { b=$0; printf("%-30s %s\n", a, b); }
|
|
' $*
|
|
}
|
|
|
|
fn awk3 {
|
|
awk '{line[NR] = $0}
|
|
END{
|
|
i = 4;
|
|
while(i < NR){
|
|
what=line[i++];
|
|
when=line[i];
|
|
comment="";
|
|
if(!(when ~ /..\/..\/.... ..:../)){
|
|
# out of sync
|
|
printf("%s\n", what);
|
|
continue;
|
|
}
|
|
i++;
|
|
if(!(line[i+1] ~ /..\/..\/.... ..:../) &&
|
|
(i+2 > NR || line[i+2] ~ /..\/..\/.... ..:../)){
|
|
what = what ", " line[i++];
|
|
}
|
|
printf("%s %s\n", when, what);
|
|
}
|
|
}' $*
|
|
}
|
|
|
|
# hget 'http://www.fedex.com/cgi-bin/track_it?airbill_list='$1'&kurrent_airbill='$1'&language=english&cntry_code=us&state=0' |
|
|
hget 'http://www.fedex.com/cgi-bin/tracking?action=track&language=english&cntry_code=us&initial=x&mps=y&tracknumbers='$1 |
|
|
htmlfmt >/tmp/fedex.$pid
|
|
sed -n '/Tracking number/,/^$/p' /tmp/fedex.$pid | awk2
|
|
echo
|
|
sed -n '/Reference number/,/^$/p' /tmp/fedex.$pid | awk2
|
|
echo
|
|
sed -n '/Date.time/,/^$/p' /tmp/fedex.$pid | sed 1,4d | fmt -l 4000 | sed 's/ [A-Z][A-Z] /&\n/g'
|
|
rm /tmp/fedex.$pid
|
|
-- /home/rsc/src/webscript/a3
|
|
#!./o.webscript
|
|
|
|
load "http://www.ups.com/WebTracking/track?loc=en_US"
|
|
find textbox "InquiryNumber1"
|
|
input "1z30557w0340175623"
|
|
find next checkbox
|
|
input "yes"
|
|
find prev form
|
|
submit
|
|
if(find "Delivery Information"){
|
|
find outer table
|
|
print
|
|
}else if(find "One or more"){
|
|
print
|
|
}else{
|
|
print "Unexpected results."
|
|
find page
|
|
print
|
|
}
|
|
-- /home/rsc/src/webscript/a2
|
|
#load "http://apc-reset/outlets.htm"
|
|
load "apc.html"
|
|
print
|
|
print "\n=============\n"
|
|
find "yoshimi"
|
|
find outer row
|
|
find next select
|
|
input "Immediate Reboot"
|
|
submit
|
|
print
|
|
-- /usr/local/plan9/acid/port
|
|
// portable acid for all architectures
|
|
|
|
defn pfl(addr)
|
|
{
|
|
print(pcfile(addr), ":", pcline(addr), "\n");
|
|
}
|
|
|
|
defn
|
|
notestk(addr)
|
|
{
|
|
local pc, sp;
|
|
complex Ureg addr;
|
|
|
|
pc = addr.pc\X;
|
|
sp = addr.sp\X;
|
|
|
|
print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " ");
|
|
pfl(pc);
|
|
_stk({"PC", pc, "SP", sp, linkreg(addr)}, 1);
|
|
}
|
|
|
|
defn
|
|
notelstk(addr)
|
|
{
|
|
local pc, sp;
|
|
complex Ureg addr;
|
|
|
|
pc = addr.pc\X;
|
|
sp = addr.sp\X;
|
|
|
|
print("Note pc:", pc, " sp:", sp, " ", fmt(pc, 'a'), " ");
|
|
pfl(pc);
|
|
_stk({"PC", pc, "SP", sp, linkreg(addr)}, 1);
|
|
}
|
|
|
|
defn params(param)
|
|
{
|
|
while param do {
|
|
sym = head param;
|
|
print(sym[0], "=", itoa(sym[1], "%#ux"));
|
|
param = tail param;
|
|
if param then
|
|
print (",");
|
|
}
|
|
}
|
|
|
|
stkprefix = "";
|
|
stkignore = {};
|
|
stkend = 0;
|
|
|
|
defn locals(l)
|
|
{
|
|
local sym;
|
|
|
|
while l do {
|
|
sym = head l;
|
|
print(stkprefix, "\t", sym[0], "=", itoa(sym[1], "%#ux"), "\n");
|
|
l = tail l;
|
|
}
|
|
}
|
|
|
|
defn _stkign(frame)
|
|
{
|
|
local file;
|
|
|
|
file = pcfile(frame[0]);
|
|
s = stkignore;
|
|
while s do {
|
|
if regexp(head s, file) then
|
|
return 1;
|
|
s = tail s;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// print a stack trace
|
|
//
|
|
// in a run of leading frames in files matched by regexps in stkignore,
|
|
// only print the last one.
|
|
defn _stk(regs, dolocals)
|
|
{
|
|
local stk, frame, pc, fn, done, callerpc, paramlist, locallist;
|
|
|
|
stk = strace(regs);
|
|
if stkignore then {
|
|
while stk && tail stk && _stkign(head tail stk) do
|
|
stk = tail stk;
|
|
}
|
|
|
|
callerpc = 0;
|
|
done = 0;
|
|
while stk && !done do {
|
|
frame = head stk;
|
|
stk = tail stk;
|
|
fn = frame[0];
|
|
pc = frame[1];
|
|
callerpc = frame[2];
|
|
paramlist = frame[3];
|
|
locallist = frame[4];
|
|
|
|
print(stkprefix, fmt(fn, 'a'), "(");
|
|
params(paramlist);
|
|
print(")");
|
|
if pc != fn then
|
|
print("+", itoa(pc-fn, "%#ux"));
|
|
print(" ");
|
|
pfl(pc);
|
|
if dolocals then
|
|
locals(locallist);
|
|
if fn == var("threadmain") || fn == var("p9main") then
|
|
done=1;
|
|
if fn == var("threadstart") || fn == var("scheduler") then
|
|
done=1;
|
|
if callerpc == 0 then
|
|
done=1;
|
|
}
|
|
if callerpc && !done then {
|
|
print(stkprefix, fmt(callerpc, 'a'), " ");
|
|
pfl(callerpc);
|
|
}
|
|
}
|
|
|
|
defn findsrc(file)
|
|
{
|
|
local lst, src;
|
|
|
|
if file[0] == '/' then {
|
|
src = file(file);
|
|
if src != {} then {
|
|
srcfiles = append srcfiles, file;
|
|
srctext = append srctext, src;
|
|
return src;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
lst = srcpath;
|
|
while head lst do {
|
|
src = file(head lst+file);
|
|
if src != {} then {
|
|
srcfiles = append srcfiles, file;
|
|
srctext = append srctext, src;
|
|
return src;
|
|
}
|
|
lst = tail lst;
|
|
}
|
|
}
|
|
|
|
defn line(addr)
|
|
{
|
|
local src, file;
|
|
|
|
file = pcfile(addr);
|
|
src = match(file, srcfiles);
|
|
|
|
if src >= 0 then
|
|
src = srctext[src];
|
|
else
|
|
src = findsrc(file);
|
|
|
|
if src == {} then {
|
|
print("no source for ", file, "\n");
|
|
return {};
|
|
}
|
|
line = pcline(addr)-1;
|
|
print(file, ":", src[line], "\n");
|
|
}
|
|
|
|
defn addsrcdir(dir)
|
|
{
|
|
dir = dir+"/";
|
|
|
|
if match(dir, srcpath) >= 0 then {
|
|
print("already in srcpath\n");
|
|
return {};
|
|
}
|
|
|
|
srcpath = {dir}+srcpath;
|
|
}
|
|
|
|
defn source()
|
|
{
|
|
local l;
|
|
|
|
l = srcpath;
|
|
while l do {
|
|
print(head l, "\n");
|
|
l = tail l;
|
|
}
|
|
l = srcfiles;
|
|
|
|
while l do {
|
|
print("\t", head l, "\n");
|
|
l = tail l;
|
|
}
|
|
}
|
|
|
|
defn Bsrc(addr)
|
|
{
|
|
local lst;
|
|
|
|
lst = srcpath;
|
|
file = pcfile(addr);
|
|
if file[0] == '/' && access(file) then {
|
|
rc("B "+file+":"+itoa(pcline(addr)));
|
|
return {};
|
|
}
|
|
while head lst do {
|
|
name = head lst+file;
|
|
if access(name) then {
|
|
rc("B "+name+":"+itoa(pcline(addr)));
|
|
return {};
|
|
}
|
|
lst = tail lst;
|
|
}
|
|
print("no source for ", file, "\n");
|
|
}
|
|
|
|
defn srcline(addr)
|
|
{
|
|
local text, cline, line, file, src;
|
|
file = pcfile(addr);
|
|
src = match(file,srcfiles);
|
|
if (src>=0) then
|
|
src = srctext[src];
|
|
else
|
|
src = findsrc(file);
|
|
if (src=={}) then
|
|
{
|
|
return "(no source)";
|
|
}
|
|
return src[pcline(addr)-1];
|
|
}
|
|
|
|
defn src(addr)
|
|
{
|
|
local src, file, line, cline, text;
|
|
|
|
file = pcfile(addr);
|
|
src = match(file, srcfiles);
|
|
|
|
if src >= 0 then
|
|
src = srctext[src];
|
|
else
|
|
src = findsrc(file);
|
|
|
|
if src == {} then {
|
|
print("no source for ", file, "\n");
|
|
return {};
|
|
}
|
|
|
|
cline = pcline(addr)-1;
|
|
print(file, ":", cline+1, "\n");
|
|
line = cline-5;
|
|
loop 0,10 do {
|
|
if line >= 0 then {
|
|
if line == cline then
|
|
print(">");
|
|
else
|
|
print(" ");
|
|
text = src[line];
|
|
if text == {} then
|
|
return {};
|
|
print(line+1, "\t", text, "\n");
|
|
}
|
|
line = line+1;
|
|
}
|
|
}
|
|
|
|
defn step() // single step the process
|
|
{
|
|
local lst, lpl, addr, bput;
|
|
|
|
bput = 0;
|
|
if match(*PC, bplist) >= 0 then { // Sitting on a breakpoint
|
|
bput = fmt(*PC, bpfmt);
|
|
*bput = @bput;
|
|
}
|
|
|
|
lst = follow(*PC);
|
|
|
|
lpl = lst;
|
|
while lpl do { // place break points
|
|
*(head lpl) = bpinst;
|
|
lpl = tail lpl;
|
|
}
|
|
|
|
startstop(pid); // do the step
|
|
|
|
while lst do { // remove the breakpoints
|
|
addr = fmt(head lst, bpfmt);
|
|
*addr = @addr;
|
|
lst = tail lst;
|
|
}
|
|
if bput != 0 then
|
|
*bput = bpinst;
|
|
}
|
|
|
|
defn bpset(addr) // set a breakpoint
|
|
{
|
|
if status(pid) != "Stopped" then {
|
|
print("Waiting...\n");
|
|
stop(pid);
|
|
}
|
|
if match(addr, bplist) >= 0 then
|
|
print("breakpoint already set at ", fmt(addr, 'a'), "\n");
|
|
else {
|
|
*fmt(addr, bpfmt) = bpinst;
|
|
bplist = append bplist, addr;
|
|
}
|
|
}
|
|
|
|
defn bptab() // print a table of breakpoints
|
|
{
|
|
local lst, addr;
|
|
|
|
lst = bplist;
|
|
while lst do {
|
|
addr = head lst;
|
|
print("\t", fmt(addr, 'X'), " ", fmt(addr, 'a'), " ", fmt(addr, 'i'), "\n");
|
|
lst = tail lst;
|
|
}
|
|
}
|
|
|
|
defn bpdel(addr) // delete a breakpoint
|
|
{
|
|
local n, pc, nbplist;
|
|
|
|
if addr == 0 then {
|
|
while bplist do {
|
|
pc = head bplist;
|
|
pc = fmt(pc, bpfmt);
|
|
*pc = @pc;
|
|
bplist = tail bplist;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
n = match(addr, bplist);
|
|
if n < 0 then {
|
|
print("no breakpoint at ", fmt(addr, 'a'), "\n");
|
|
return {};
|
|
}
|
|
|
|
addr = fmt(addr, bpfmt);
|
|
*addr = @addr;
|
|
|
|
nbplist = {}; // delete from list
|
|
while bplist do {
|
|
pc = head bplist;
|
|
if pc != addr then
|
|
nbplist = append nbplist, pc;
|
|
bplist = tail bplist;
|
|
}
|
|
bplist = nbplist; // delete from memory
|
|
}
|
|
|
|
defn cont() // continue execution
|
|
{
|
|
local addr;
|
|
|
|
addr = fmt(*PC, bpfmt);
|
|
if match(addr, bplist) >= 0 then { // Sitting on a breakpoint
|
|
*addr = @addr;
|
|
step(); // Step over
|
|
*addr = bpinst;
|
|
}
|
|
startstop(pid); // Run
|
|
}
|
|
|
|
defn stopped(pid) // called from acid when a process changes state
|
|
{
|
|
pfixstop(pid);
|
|
pstop(pid); // stub so this is easy to replace
|
|
}
|
|
|
|
defn procs() // print status of processes
|
|
{
|
|
local c, lst, cpid;
|
|
|
|
cpid = pid;
|
|
lst = proclist;
|
|
while lst do {
|
|
np = head lst;
|
|
setproc(np);
|
|
if np == cpid then
|
|
c = '>';
|
|
else
|
|
c = ' ';
|
|
print(fmt(c, 'c'), np, ": ", status(np), " at ", fmt(*PC, 'a'), " setproc(", np, ")\n");
|
|
lst = tail lst;
|
|
}
|
|
pid = cpid;
|
|
if pid != 0 then
|
|
setproc(pid);
|
|
}
|
|
|
|
_asmlines = 30;
|
|
|
|
defn asm(addr)
|
|
{
|
|
local bound;
|
|
|
|
bound = fnbound(addr);
|
|
|
|
addr = fmt(addr, 'i');
|
|
loop 1,_asmlines do {
|
|
print(fmt(addr, 'a'), " ", fmt(addr, 'X'));
|
|
print("\t", @addr++, "\n");
|
|
if bound != {} && addr > bound[1] then {
|
|
lasmaddr = addr;
|
|
return {};
|
|
}
|
|
}
|
|
lasmaddr = addr;
|
|
}
|
|
|
|
defn casm()
|
|
{
|
|
asm(lasmaddr);
|
|
}
|
|
|
|
defn xasm(addr)
|
|
{
|
|
local bound;
|
|
|
|
bound = fnbound(addr);
|
|
|
|
addr = fmt(addr, 'i');
|
|
loop 1,_asmlines do {
|
|
print(fmt(addr, 'a'), " ", fmt(addr, 'X'));
|
|
print("\t", *addr++, "\n");
|
|
if bound != {} && addr > bound[1] then {
|
|
lasmaddr = addr;
|
|
return {};
|
|
}
|
|
}
|
|
lasmaddr = addr;
|
|
}
|
|
|
|
defn xcasm()
|
|
{
|
|
xasm(lasmaddr);
|
|
}
|
|
|
|
defn win()
|
|
{
|
|
local npid, estr;
|
|
|
|
bplist = {};
|
|
notes = {};
|
|
|
|
estr = "/sys/lib/acid/window '0 0 600 400' "+textfile;
|
|
if progargs != "" then
|
|
estr = estr+" "+progargs;
|
|
|
|
npid = rc(estr);
|
|
npid = atoi(npid);
|
|
if npid == 0 then
|
|
error("win failed to create process");
|
|
|
|
setproc(npid);
|
|
stopped(npid);
|
|
}
|
|
|
|
defn win2()
|
|
{
|
|
local npid, estr;
|
|
|
|
bplist = {};
|
|
notes = {};
|
|
|
|
estr = "/sys/lib/acid/transcript '0 0 600 400' '100 100 700 500' "+textfile;
|
|
if progargs != "" then
|
|
estr = estr+" "+progargs;
|
|
|
|
npid = rc(estr);
|
|
npid = atoi(npid);
|
|
if npid == 0 then
|
|
error("win failed to create process");
|
|
|
|
setproc(npid);
|
|
stopped(npid);
|
|
}
|
|
|
|
printstopped = 1;
|
|
defn new()
|
|
{
|
|
local a;
|
|
|
|
bplist = {};
|
|
newproc(progargs);
|
|
a = var("p9main");
|
|
if a == {} then
|
|
a = var("main");
|
|
if a == {} then
|
|
return {};
|
|
bpset(a);
|
|
while *PC != a do
|
|
cont();
|
|
bpdel(a);
|
|
}
|
|
|
|
defn stmnt() // step one statement
|
|
{
|
|
local line;
|
|
|
|
line = pcline(*PC);
|
|
while 1 do {
|
|
step();
|
|
if line != pcline(*PC) then {
|
|
src(*PC);
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
|
|
defn func() // step until we leave the current function
|
|
{
|
|
local bound, end, start, pc;
|
|
|
|
bound = fnbound(*PC);
|
|
if bound == {} then {
|
|
print("cannot locate text symbol\n");
|
|
return {};
|
|
}
|
|
|
|
pc = *PC;
|
|
start = bound[0];
|
|
end = bound[1];
|
|
while pc >= start && pc < end do {
|
|
step();
|
|
pc = *PC;
|
|
}
|
|
}
|
|
|
|
defn next()
|
|
{
|
|
local sp, bound, pc;
|
|
|
|
sp = *SP;
|
|
bound = fnbound(*PC);
|
|
if bound == {} then {
|
|
print("cannot locate text symbol\n");
|
|
return {};
|
|
}
|
|
stmnt();
|
|
pc = *PC;
|
|
if pc >= bound[0] && pc < bound[1] then
|
|
return {};
|
|
|
|
while (pc < bound[0] || pc > bound[1]) && sp >= *SP do {
|
|
step();
|
|
pc = *PC;
|
|
}
|
|
src(*PC);
|
|
}
|
|
|
|
defn maps()
|
|
{
|
|
local m, mm;
|
|
|
|
m = map();
|
|
while m != {} do {
|
|
mm = head m;
|
|
m = tail m;
|
|
print(mm[2]\X, " ", mm[3]\X, " ", mm[4]\X, " ", mm[0], " ", mm[1], "\n");
|
|
}
|
|
}
|
|
|
|
defn dump(addr, n, fmt)
|
|
{
|
|
loop 0, n do {
|
|
print(fmt(addr, 'X'), ": ");
|
|
addr = mem(addr, fmt);
|
|
}
|
|
}
|
|
|
|
defn mem(addr, fmt)
|
|
{
|
|
|
|
local i, c, n;
|
|
|
|
i = 0;
|
|
while fmt[i] != 0 do {
|
|
c = fmt[i];
|
|
n = 0;
|
|
while '0' <= fmt[i] && fmt[i] <= '9' do {
|
|
n = 10*n + fmt[i]-'0';
|
|
i = i+1;
|
|
}
|
|
if n <= 0 then n = 1;
|
|
addr = fmt(addr, fmt[i]);
|
|
while n > 0 do {
|
|
print(*addr++, " ");
|
|
n = n-1;
|
|
}
|
|
i = i+1;
|
|
}
|
|
print("\n");
|
|
return addr;
|
|
}
|
|
|
|
defn symbols(pattern)
|
|
{
|
|
local l, s;
|
|
|
|
l = symbols;
|
|
while l do {
|
|
s = head l;
|
|
if regexp(pattern, s[0]) then
|
|
print(s[0], "\t", s[1], "\t", s[2], "\t", s[3], "\n");
|
|
l = tail l;
|
|
}
|
|
}
|
|
|
|
defn havesymbol(name)
|
|
{
|
|
local l, s;
|
|
|
|
l = symbols;
|
|
while l do {
|
|
s = head l;
|
|
l = tail l;
|
|
if s[0] == name then
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
defn spsrch(len)
|
|
{
|
|
local addr, a, s, e;
|
|
|
|
addr = *SP;
|
|
s = origin & 0x7fffffff;
|
|
e = etext & 0x7fffffff;
|
|
loop 1, len do {
|
|
a = *addr++;
|
|
c = a & 0x7fffffff;
|
|
if c > s && c < e then {
|
|
print("src(", a, ")\n");
|
|
pfl(a);
|
|
}
|
|
}
|
|
}
|
|
|
|
defn acidtypes()
|
|
{
|
|
local syms;
|
|
local l;
|
|
|
|
l = textfile();
|
|
if l != {} then {
|
|
syms = "acidtypes";
|
|
while l != {} do {
|
|
syms = syms + " " + ((head l)[0]);
|
|
l = tail l;
|
|
}
|
|
includepipe(syms);
|
|
}
|
|
}
|
|
|
|
defn getregs()
|
|
{
|
|
local regs, l;
|
|
|
|
regs = {};
|
|
l = registers;
|
|
while l != {} do {
|
|
regs = append regs, var(l[0]);
|
|
l = tail l;
|
|
}
|
|
return regs;
|
|
}
|
|
|
|
defn setregs(regs)
|
|
{
|
|
local l;
|
|
|
|
l = registers;
|
|
while l != {} do {
|
|
var(l[0]) = regs[0];
|
|
l = tail l;
|
|
regs = tail regs;
|
|
}
|
|
return regs;
|
|
}
|
|
|
|
defn resetregs()
|
|
{
|
|
local l;
|
|
|
|
l = registers;
|
|
while l != {} do {
|
|
var(l[0]) = register(l[0]);
|
|
l = tail l;
|
|
}
|
|
}
|
|
|
|
defn clearregs()
|
|
{
|
|
local l;
|
|
|
|
l = registers;
|
|
while l != {} do {
|
|
var(l[0]) = refconst(~0);
|
|
l = tail l;
|
|
}
|
|
}
|
|
|
|
progargs="";
|
|
print(acidfile);
|
|
|
|
-- /usr/local/plan9/acid/386
|
|
// 386 support
|
|
|
|
defn acidinit() // Called after all the init modules are loaded
|
|
{
|
|
bplist = {};
|
|
bpfmt = 'b';
|
|
|
|
srcpath = {
|
|
"./",
|
|
"/sys/src/libc/port/",
|
|
"/sys/src/libc/9sys/",
|
|
"/sys/src/libc/386/"
|
|
};
|
|
|
|
srcfiles = {}; // list of loaded files
|
|
srctext = {}; // the text of the files
|
|
}
|
|
|
|
defn linkreg(addr)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
defn stk() // trace
|
|
{
|
|
_stk({"PC", *PC, "SP", *SP}, 0);
|
|
}
|
|
|
|
defn lstk() // trace with locals
|
|
{
|
|
_stk({"PC", *PC, "SP", *SP}, 1);
|
|
}
|
|
|
|
defn gpr() // print general(hah hah!) purpose registers
|
|
{
|
|
print("AX\t", *AX, " BX\t", *BX, " CX\t", *CX, " DX\t", *DX, "\n");
|
|
print("DI\t", *DI, " SI\t", *SI, " BP\t", *BP, "\n");
|
|
}
|
|
|
|
defn spr() // print special processor registers
|
|
{
|
|
local pc;
|
|
local cause;
|
|
|
|
pc = *PC;
|
|
print("PC\t", pc, " ", fmt(pc, 'a'), " ");
|
|
pfl(pc);
|
|
print("SP\t", *SP, " ECODE ", *ECODE, " EFLAG ", *EFLAGS, "\n");
|
|
print("CS\t", *CS, " DS\t ", *DS, " SS\t", *SS, "\n");
|
|
print("GS\t", *GS, " FS\t ", *FS, " ES\t", *ES, "\n");
|
|
|
|
cause = *TRAP;
|
|
print("TRAP\t", cause, " ", reason(cause), "\n");
|
|
}
|
|
|
|
defn regs() // print all registers
|
|
{
|
|
spr();
|
|
gpr();
|
|
}
|
|
|
|
defn mmregs()
|
|
{
|
|
print("MM0\t", *MM0, " MM1\t", *MM1, "\n");
|
|
print("MM2\t", *MM2, " MM3\t", *MM3, "\n");
|
|
print("MM4\t", *MM4, " MM5\t", *MM5, "\n");
|
|
print("MM6\t", *MM6, " MM7\t", *MM7, "\n");
|
|
}
|
|
|
|
defn pfixstop(pid)
|
|
{
|
|
if *fmt(*PC-1, 'b') == 0xCC then {
|
|
// Linux stops us after the breakpoint, not at it
|
|
*PC = *PC-1;
|
|
}
|
|
}
|
|
|
|
|
|
defn pstop(pid)
|
|
{
|
|
local l;
|
|
local pc;
|
|
local why;
|
|
|
|
pc = *PC;
|
|
|
|
// FIgure out why we stopped.
|
|
if *fmt(pc, 'b') == 0xCC then {
|
|
why = "breakpoint";
|
|
|
|
// fix up instruction for print; will put back later
|
|
*pc = @pc;
|
|
} else if *(pc-2\x) == 0x80CD then {
|
|
pc = pc-2;
|
|
why = "system call";
|
|
} else
|
|
why = "stopped";
|
|
|
|
if printstopped then {
|
|
print(pid,": ", why, "\t");
|
|
print(fmt(pc, 'a'), "\t", *fmt(pc, 'i'), "\n");
|
|
}
|
|
|
|
if why == "breakpoint" then
|
|
*fmt(pc, bpfmt) = bpinst;
|
|
|
|
if printstopped && notes then {
|
|
if notes[0] != "sys: breakpoint" then {
|
|
print("Notes pending:\n");
|
|
l = notes;
|
|
while l do {
|
|
print("\t", head l, "\n");
|
|
l = tail l;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
aggr Ureg
|
|
{
|
|
'U' 0 di;
|
|
'U' 4 si;
|
|
'U' 8 bp;
|
|
'U' 12 nsp;
|
|
'U' 16 bx;
|
|
'U' 20 dx;
|
|
'U' 24 cx;
|
|
'U' 28 ax;
|
|
'U' 32 gs;
|
|
'U' 36 fs;
|
|
'U' 40 es;
|
|
'U' 44 ds;
|
|
'U' 48 trap;
|
|
'U' 52 ecode;
|
|
'U' 56 pc;
|
|
'U' 60 cs;
|
|
'U' 64 flags;
|
|
{
|
|
'U' 68 usp;
|
|
'U' 68 sp;
|
|
};
|
|
'U' 72 ss;
|
|
};
|
|
|
|
defn
|
|
Ureg(addr) {
|
|
complex Ureg addr;
|
|
print(" di ", addr.di, "\n");
|
|
print(" si ", addr.si, "\n");
|
|
print(" bp ", addr.bp, "\n");
|
|
print(" nsp ", addr.nsp, "\n");
|
|
print(" bx ", addr.bx, "\n");
|
|
print(" dx ", addr.dx, "\n");
|
|
print(" cx ", addr.cx, "\n");
|
|
print(" ax ", addr.ax, "\n");
|
|
print(" gs ", addr.gs, "\n");
|
|
print(" fs ", addr.fs, "\n");
|
|
print(" es ", addr.es, "\n");
|
|
print(" ds ", addr.ds, "\n");
|
|
print(" trap ", addr.trap, "\n");
|
|
print(" ecode ", addr.ecode, "\n");
|
|
print(" pc ", addr.pc, "\n");
|
|
print(" cs ", addr.cs, "\n");
|
|
print(" flags ", addr.flags, "\n");
|
|
print(" sp ", addr.sp, "\n");
|
|
print(" ss ", addr.ss, "\n");
|
|
};
|
|
sizeofUreg = 76;
|
|
|
|
aggr Linkdebug
|
|
{
|
|
'X' 0 version;
|
|
'X' 4 map;
|
|
};
|
|
|
|
aggr Linkmap
|
|
{
|
|
'X' 0 addr;
|
|
'X' 4 name;
|
|
'X' 8 dynsect;
|
|
'X' 12 next;
|
|
'X' 16 prev;
|
|
};
|
|
|
|
defn
|
|
linkdebug()
|
|
{
|
|
local a;
|
|
|
|
if !havesymbol("_DYNAMIC") then
|
|
return 0;
|
|
|
|
a = _DYNAMIC;
|
|
while *a != 0 do {
|
|
if *a == 21 then // 21 == DT_DEBUG
|
|
return *(a+4);
|
|
a = a+8;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
defn
|
|
dynamicmap()
|
|
{
|
|
if systype == "linux" || systype == "freebsd" then {
|
|
local r, m, n;
|
|
|
|
r = linkdebug();
|
|
if r then {
|
|
complex Linkdebug r;
|
|
m = r.map;
|
|
n = 0;
|
|
while m != 0 && n < 100 do {
|
|
complex Linkmap m;
|
|
if m.name && *(m.name\b) && access(*(m.name\s)) then
|
|
print("textfile({\"", *(m.name\s), "\", ", m.addr\X, "});\n");
|
|
m = m.next;
|
|
n = n+1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
defn
|
|
acidmap()
|
|
{
|
|
// dynamicmap();
|
|
acidtypes();
|
|
}
|
|
|
|
print(acidfile);
|