Re: level 2 postscript debugger steps through procs and files
- From: luserXtrog <mijoryx@xxxxxxxxx>
- Date: Mon, 20 Sep 2010 01:04:44 -0700 (PDT)
A much improved version, I think. You can now step into
a proc or file or just execute it. A replacement currentfile
will deliver the correct file which should allow debugging
files with images.
For variety, the transcript comes first. Hitting just enter
at the step prompt will re-do the previous command.
Unfortunately, if the last thing you did was quit (to play
with the stack, maybe) then enter will quit again.
ctrl-D will quit.
9(1)02:47 AM:ps 0> gsnd db4.ps
GPL Ghostscript 8.62 (2008-02-29)
Copyright (C) 2008 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
GS>(buggy.ps) stepon debug
db: debugging buggy.ps
db: starting
db: Stack:
run
(meas.ps)
db: Step run
defined as --run--
db: execute step? (continue|next|bypass|step|quit)?n
db: finished
db: Stack:
def
{moveto lineto stroke showpage}
/doit
db: Step def
defined as --def--
db: execute step? (continue|next|bypass|step|quit)?n
db: Stack:
in
1
db: Step in
defined as {72 mul}
db: execute step? (continue|next|bypass|step|quit)?n
db: Stack:
in
1
72
db: Step in
defined as {72 mul}
db: execute step? (continue|next|bypass|step|quit)?n
db: Stack:
doit
0
72
72
db: Step doit
defined as {moveto lineto stroke showpage}
db: execute step? (continue|next|bypass|step|quit)?s
db: Stack:
moveto
0
72
72
db: Step moveto
defined as --moveto--
db: execute step? (continue|next|bypass|step|quit)?q
GS<3>0
GS<4>resume
db: resuming
db: Stack:
moveto
0
0
72
72
db: Step moveto
defined as --moveto--
db: execute step? (continue|next|bypass|step|quit)?n
db: Stack:
lineto
72
72
db: Step lineto
defined as --lineto--
db: execute step? (continue|next|bypass|step|quit)?
db: Stack:
stroke
db: Step stroke
defined as --stroke--
db: execute step? (continue|next|bypass|step|quit)?
db: Stack:
showpage
db: Step showpage
defined as --showpage--
db: execute step? (continue|next|bypass|step|quit)?
db: finished
GS>quit
10(1)02:49 AM:ps 0> for i in db4.ps ds.ps meas.ps buggy.ps; do echo;
echo %--- $i ----; cat $i; echo %--- eof ----; echo; echo; done;
%--- db4.ps ----
%!
{ currentfile token pop /ENDINTRO eq { exit } if } loop
debug.ps - A postscript debugger
The debugger takes a file and executes each token within
a stopped context, catching errors and retaining the rest
of the program. It requires an interactive session with
the interpreter. Oh, and a file system, probably.
Switches
--------
stepon enable single-stepping
stepoff disable single-stepping
steppstackon enable display of stack before each step
steppstackoff disable display of stack before each step
traceon enable tracing (echo each token before executing)
traceoff disable tracing
With 'traceon stepon' you might want to 'steppstackoff'.
to avoid too much information. Default is on.
Procedures
----------
(filename) debug
Begin debugging file.
resume
Resume debugging after 'exit'ing from an error prompt.
or 'q'uiting from single-step.
skip
Skip the current object.
object wherevalue dict key true
false
Perform reverse lookup for object in the dictionary stack.
dicttrace
Display the dictionary stack using wherevalue to obtain
dictionary names.
ENDINTRO
(ds.ps) run
11 dict begin
%
% internal names
% will be undef'ed later
%
% print error message
/errmsg {
(Error /) print
$error /errorname get ( ) cvs print
( occurred attempting to execute ) print
} def
% the debugger state
/dbdict <<
/nexttoken null
/stack 250 array
/ptr -1
/trace false
/step false
/steppstack true
def
% push a source (file or array or even string)
% on the debug stack (defined as the typical size of the
% exec stack from the red book).
/pushs { % proc|file pushs
//dbdict /ptr 2 copy get 1 add put
//dbdict /stack get
//dbdict /ptr get
3 -1 roll put
} def
% remove a source from the stack
% if it's a file, close it.
/pops {
//dbdict /stack get
//dbdict /ptr get
get dup type /filetype eq {
closefile
}{ pop } ifelse
//dbdict /ptr 2 copy get 1 sub put
} def
% fetch the next token from the
% array on top of the stack
% TODO: there's some duplication here with getnexttoken
/pulltoken { % pulltoken token true | false
//dbdict /stack get
//dbdict /ptr get
get
dup length 0 gt {
dup 0 get exch
dup length 1 gt {
//dbdict /stack get
//dbdict /ptr get
3 -1 roll
1 1 index length 1 sub getinterval put
}{ %else length 1 eq
pop
//dbdict /stack get
//dbdict /ptr get {} put
//pops exec
} ifelse
true
}{
//pops exec
false
} ifelse
} def
% fetch next token from
% top of stack
/getnexttoken {
//dbdict /nexttoken get
dup null eq {
pop
//dbdict /stack get
//dbdict /ptr get
get
dup type /filetype eq {
token
}{
pop
//pulltoken exec
} ifelse
}{ true } ifelse
} def
% handerr
% print message and present the error prompt
%
% some things you can do at the error prompt:
% skip - this will clear the /nexttoken entry
% in the dictionary so the next loop
% will fetch the next thing
% (may cause problems later if the
% program needed this to happen)
% exit - you're only in one loop so the regular
% postscript exit operator will exit the
% debugger function and take you back to
% the outer level from which you invoked
% the debug procedure.you can 'skip' here
% too and then 'resume' to restart the
% debugger where you left off.
% This is a legitimate postscript prompt. So if the error
% is a typecheck or a stack underflow, you can fix the
% stack, hit enter, and the debugger will try again to
% execute the token. If the stack manipulation succeeds,
% then that's what the program needs to do at that point.
% TODO: make a separate trace output that inserts
% error commands into the text of the debugged program.
/handerr {
$error /errorname get /invalidexit eq {
exit
}{
(db:) print
//errmsg exec
//dbdict /nexttoken get ==
(Stack:\n) print
pstack
dicttrace
(\n) print
{
(db-error>) print flush
(%lineedit) (r) file cvx
exec
} stopped {
$error /errorname get /invalidexit ne
$error /errorname get /undefinedfilename ne
and {
//errmsg exec (user command\n) print
} if
exit
} if
} ifelse
} def
% the debug loop performs this on each executable object
/debugheart {
dup /run eq {
pop (r) file //pushs exec
}{
load
dup type /arraytype eq {
dup length 0 eq { pop } //pushs ifelse
}{
exec
} ifelse
} ifelse
} def
% these are the single-character codes for the stepwise
% prompt you can type full words if you like. it only
% reads the first character. the initial default is next.
% whereupon the token in question will be executed
% in a stopped context in the big loop.
% default is changed to whatever happened last.
/stepcommands <<
(n) 0 get { %next
/stepon load //pushs exec stepoff //debugheart exec
}
/default 1 index
(c) 0 get { stepoff //debugheart exec } %continue
(b) 0 get { pop skip } %bypass
(s) 0 get //debugheart % :)
(q) 0 get { pop exit } %quit
def
% present the step prompt,
% read a line from the user
% execute the matching step command
/stepheart {
//dbdict /steppstack get { (db: Stack: \n) print pstack } if
(db: Step ) print
dup ==
(defined as ) print
dup { load } stopped { pop (/undefined\n) print } { == } ifelse
(db: execute step? (continue|next|bypass|step|quit)?) print flush
(%lineedit) (r) { file } stopped { % ctrl-D
pop (\n) print exit
}{
read { % something typed
//stepcommands
exch 2 copy known not { pop /default } if
get
//stepcommands /default 2 index put
exec
}{ % blank line
//stepcommands /default get exec
} ifelse
} ifelse
} def
% the big ol' loop
/debugloop {
//getnexttoken exec
{ %ifelse
//dbdict /nexttoken 3 -1 roll put
{ %stopped
//dbdict /nexttoken get
//dbdict /trace get { dup == } if
dup xcheck {
dup type /arraytype ne {
//dbdict /step get
//stepheart
//debugheart
ifelse
} if %else leave on stack
} if %else leave on stack
//dbdict /nexttoken null put
} stopped //handerr if
}{ %else no tokens
//pops exec
//dbdict /nexttoken null put
(db: finished\n) print
//dbdict /ptr get 0 lt { exit } if
} ifelse
} def
%
% External names
%
% the dict of internal names is juggled off the dictstack
% so they're available during scanning but not present
% when the 'def'ing.
%
% enable/disable tracing/stepwise-prompting
/traceon { //dbdict /trace true put }
currentdict end 3 1 roll def begin
/traceoff { //dbdict /trace false put }
currentdict end 3 1 roll def begin
/stepon { //dbdict /step true put }
currentdict end 3 1 roll def begin
/stepoff { //dbdict /step false put }
currentdict end 3 1 roll def begin
/steppstackon { //dbdict /steppstack true put }
currentdict end 3 1 roll def begin
/steppstackoff { /dbdict /steppstack false put }
currentdict end 3 1 roll def begin
% the main entry point
% i could probably modify this to take a proc as well
/debug { % (filename) debug -
//dbdict /trace get
{ (db: tracing ) }{ (db: debugging ) } ifelse
dup print (\n) print
{ %stopped
(r) file
} stopped { %if
(db:Error unable to open input file\n) print
//errmsg exec (\n) print
}{
(db: starting\n) print
//pushs exec
//debugloop loop
}
ifelse
} currentdict end 3 1 roll def begin
% resume exit'ed session
/resume {
//dbdict /ptr get 0 lt {
(db: nothing to resume.\n) print
}{
(db: resuming) print
//dbdict /trace get { ( trace) print } if
(\n) print
//debugloop loop
} ifelse
} currentdict end 3 1 roll def begin
% skip the current token
% if used outside the debug loop (having exit'ed),
% it will clear the current token so the 'resume'ing
% will fetch the next thing from whereever it's
% getting them (array or file).
/skip {
//dbdict /nexttoken null put
} currentdict end 3 1 roll def begin
%
% interfaced builtin operator
%
/oldcurrentfile /currentfile load
currentdict end 3 1 roll def begin
/currentfile {
//dbdict /ptr get dup 0 ge {
-1 0 { %for
//dbdict /stack get exch get
dup type /filetype eq {
exit
} if
pop
} for
}{
oldcurrentfile
} ifelse
} currentdict end 3 1 roll def begin
end % discard internal names
% debug crashes upon attempting to debug its own array
%stepon traceon (db3.ps) debug
%stepon (buggy.ps) debug
%eof
%--- eof ----
%--- ds.ps ----
%!
% value wherevalue dict key true
% false
% find dictionary and key for value
/wherevalue {
MYDICT
countdictstack array dictstack % val my-dict [ds]
exch begin
/found false def
dup length 1 sub -1 0 { % val [ds] i
1 index exch get % val [ds] dict
dup { % val [ds] dict key val
dup type /stringtype eq {
dup rcheck {
4 index eq {
/found true def
exit
} if
}{
pop
} ifelse
}{
4 index eq { % val [ds] dict key
/found true def
exit %forall
} if % val [ds] dict key
} ifelse
pop % val [ds] dict
} forall % val [ds] (found: dict key) (not: dict)
found {
exit %for
} if % val [ds] dict
pop % val [ds]
} for % (found: val [ds] dict key) (not: val [ds])
found {
4 2 roll pop pop true
}{
pop pop false
} ifelse
end
} dup 0 1 dict put def
% print names of dictionaries on the stack
/dicttrace {
(Dict Stack:\n) print
countdictstack array dictstack
dup length 1 sub -1 0 {
1 index exch get
wherevalue {
exch pop ( ) cvs print
(\n) print
}{
(-anonymous- ) print
} ifelse
} for
pop
} def
%--- eof ----
%--- meas.ps ----
%!
/in {72 mul} def
/cm {in 2.54 mul} def
%--- eof ----
%--- buggy.ps ----
(meas.ps) run
/doit { moveto lineto stroke showpage } def
1 in 1 in
0
doit
%--- eof ----
11(1)02:53 AM:ps 0>
--
these posts are getting long
I had to learn a new way to select text.
hurray for the legacy of athena widgets!
.
- Follow-Ups:
- Re: level 2 postscript debugger steps through procs and files
- From: luserXtrog
- Re: level 2 postscript debugger steps through procs and files
- References:
- level 2 postscript debugger steps through procs and files
- From: luserXtrog
- level 2 postscript debugger steps through procs and files
- Prev by Date: Re: Feeling on cell
- Next by Date: Re: level 2 postscript debugger steps through procs and files
- Previous by thread: Re: level 2 postscript debugger steps through procs and files
- Next by thread: Re: level 2 postscript debugger steps through procs and files
- Index(es):
Relevant Pages
|