调试PostScript

Mar*_*son 13 debugging postscript

如何在PostScript中进行调试?我可以在Linux上使用GhostView/GhostScript,但我无法查看堆栈,词典等.

lus*_*oog 20

[ 这个答案是关于使用postscript语言调试"小".我的另一个答案是(完全)成熟的"调试器" ]

最小的调试器

我刚刚发现了一个非常有用的黑客.

<<
/break{ /hook /pause load store }
/cont{ /hook {} store }
/doprompt{
    (\nbreak>)print
    flush(%lineedit)(r)file
    cvx {exec}stopped pop }
/pause{ doprompt }
/hook{}
>> begin
Run Code Online (Sandbox Code Playgroud)

它是一个8行EMBEDDABLE调试器.我为我的8086模拟器做了这个.放入hook主循环,放入break一个程序来触发下一个挂钩的暂停(我将它置于可疑的OPCODE触发器中,并且挂钩是中断发挥其第一个效果的地方).挂钩调用暂停和暂停调用doprompt,这会给你一个单行的"break>"提示.cont在此处键入将清除挂钩并保持旋转,不会执行任何暂停,直到break遇到另一个.您还可以在提示符处检查值并执行代码,但是当您按Enter键时会恢复执行nb,因此如果您需要其他行,请致电dopromptpause在行尾.执行命令时会忽略错误(您不希望调试程序崩溃程序,这是愚蠢的!).我想我可以结合暂停和多普勒,并消除一个名字; 但这里的目标不是机器效率,而是一个明确的概念集合:这个代码在调试其他代码时非常有用,需要易于扫描和验证.

这是一个调试器,它只是读取一行?!

记住你已经得到=并且==要研究价值观.forallget破坏数组和东西.为了真正找到你所在的位置,可以countexecstack array execstack ==对整个caboodle进行可读的转储.也就是说,执行堆栈中当前位置的回溯,其包含所有部分执行的过程的尾部和当前帧返回时等待恢复的文件.


的 "printf"

有很多调试可以在没有调试器本身的情况下完成,仅仅通过检测程序(可以printf这么说添加s).

刚才我遇到了一个错误,我的调试器无法帮助我,因为调试器本身就像一些过于聪明的东西一样崩溃了

/E [ 0 0 10 ] def %eye point
/crackE { % set pointers into E
    /ex E 0 1 getinterval cvx def
    /ey E 1 1 getinterval cvx def
    /ez E 2 1 getinterval cvx def
} def crackE
Run Code Online (Sandbox Code Playgroud)

所以我正在调查的实际错误是

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.
Error: /stackunderflow in --forall--
Operand stack:
   --nostringval--
Execution stack:
   %interp_exit   .runexec2   --nostringval--   --nostringval--   --nostringval--   2   %stopped_push   --nostringval--   --nostringval--   --nostringval--   false   1   %stopped_push   1905   1   3   %oparray_pop   1904   1   3   %oparray_pop   1888   1   3   %oparray_pop   1771   1   3   %oparray_pop   --nostringval--   %errorexec_pop   .runexec2   --nostringval--   --nostringval--   --nostringval--   2   %stopped_push   --nostringval--   --nostringval--   --nostringval--   0.238095   0.047619   0.952381   --nostringval--   %for_real_continue   68.5714   17.1429   360.048   --nostringval--   %for_real_continue   --nostringval--
Dictionary stack:
   --dict:1151/1684(ro)(G)--   --dict:0/20(G)--   --dict:121/200(L)--   --dict:13/20(L)--   --dict:1/2(L)--
Current allocation mode is local
Last OS error: 2
Current file position is 3241
GPL Ghostscript 8.62: Unrecoverable error, exit code 1
Run Code Online (Sandbox Code Playgroud)

而我真正需要知道的是--nostringval--操作数堆栈中的那个东西到底是什么.

所以我把它放在程序的开头

/forall { pstack()= forall } bind def
Run Code Online (Sandbox Code Playgroud)

然后再次运行它

{MO matmul 0 --get-- --aload-- --pop-- proj action}

Error: /stackunderflow in --forall--
Operand stack:
   --nostringval--
...

就在错误发生之前,最后一个stackdump(using ==)告诉我,我有一个程序体缺少它的数据集.

pstack 与这样的事情相比,有点生硬

/args { dup 1 add copy -1 1 { -1 roll ==only ( ) print } for } def
/forall { 2 args (forall)= forall } bind def
Run Code Online (Sandbox Code Playgroud)

这对于跟踪明显正常工作的代码中的错误数据会更有用.这也是Distiller的早期版本通过仅定义要自行转储的绘图操作来生成优化的.ps文件的方式,其余的计算是"蒸馏"出来的.

一些技巧

()= %print a newline
=string %built-in 128-byte buffer used by = and ==
/object =string cvs print %convert object to string and print without newline
/stack { count dup 1 add copy { = } repeat pop } def % this is the code for the stack operator
66 (#) dup 0 3 index put print %m non-destructively print a "char"
Run Code Online (Sandbox Code Playgroud)

[我之前写过'='而不是'stack'.一个糟糕的错误.编辑:添加缺少的pop/stack.]


错误的黑客攻击

调查错误的另一种方法是更改​​错误处理程序.为了调查/stackunderflow上面描述的错误我可以使用

errordict/stackunderflow{dup == /stackunderflow signalerror}put
Run Code Online (Sandbox Code Playgroud)

而不是专业化forall.要了解后记这个相当神秘的方面,请阅读errordict stopstopped.交互式地,看一看errordict{exch =only ==}forall.signalerror在ghostscript中调用.errorAdobe解释器.它的工作是获取堆栈的快照,然后调用stop以弹出exec堆栈.所以dup ==这里和pstack上面的错误基本上是相同的"时刻" stop.您的交互式会话(以及gs正常模式中的前一个程序)在exec堆栈上更加深入,等效于//your-program stopped { handleerror } if.它handleerror使用快照(在程序被清除之后)使用其无信息的堆栈打印输出错误报告.

您可以找到替换handleerror,您可以(ehandle.ps)run在错误的程序开始时生成不同的样式错误报告.

检查$ error

我刚刚在这里重新阅读我的例子时发现了这一点.如果解释器仍然提示您,您还可以在错误之后调查堆栈.错误信息保存在$error字典中,包括堆栈的快照.

GS>[ 1 2 3 ] [4 5 6] bogus
Error: /undefined in bogus
Operand stack:
   --nostringval--   --nostringval--
Execution stack:
   %interp_exit   .runexec2   --nostringval--   --nostringval--   --nostringval--   2   %stopped_push   --nostringval--   --nostringval--   %loop_continue   --nostringval--   --nostringval--   false   1   %stopped_push   .runexec2   --nostringval--   --nostringval--   --nostringval--   2   %stopped_push   --nostringval--
Dictionary stack:
   --dict:1168/1684(ro)(G)--   --dict:0/20(G)--   --dict:77/200(L)--
Current allocation mode is local
Current file position is 24
GS<2>

GS<2>$error{pop ==}forall
/dstack
/recordstacks
/globalmode
/newerror
/.nosetlocal
/estack
/errorinfo
/.inerror
/SubstituteFont
/position
/binary
/ostack
/command
/errorname
GS<2>$error/ostack get ==
[[1 2 3] [4 5 6]]
GS<2>
Run Code Online (Sandbox Code Playgroud)

当然,这里的对象仍在堆栈中.但$error有人可以偷看.不要试试这个:$error ===.TMI.

您可以获得的一个非常有用的信息$error/estack在错误点处执行堆栈的副本.

PS<3>$error /estack get ==
[ --quit--{ pop --quit--} false { quitflag false --def---dict- /
execdepth 2 --copy----get--1 --sub----put----end---dict- /doclose false 
--put--interrupt } --loop----cvx--[ /quitflag false --def---dict- /
newerror false --put--/prompt --load----stopped--{ (
Error during prompt execution
)--print--handleerror --exit--} --if--{ 
mark /stmtfile (%statementedit)(r)--file----def--} --stopped--{ --
cleartomark---dict- /newerror --get--{ -dict- /errorname --get--/
undefinedfilename --ne--{ handleerror } --if---dict- /newerror false --
put----exit--} --if--} { --pop--stmtfile --end--{ --cvx----exec--} --
stopped---dict- --begin--{ handleerror stmtfile --closefile--} --if--} 
--ifelse--checkquit ] { checkquit } { -dict- --begin--{ handleerror 
stmtfile --closefile--} --if--} false -file- -file- -file- --repeat----
cvx--[ randcurve randwidth randcolor stroke ] 1 { flushpage newpath } { 
newpath } --forall----cvx--[ dup length 2 gt { [ currentcolordict DEVICE
 /nativecolorspace get get exec counttomark 2 add -1 roll DEVICE dup /
FillPoly get exec pop pstack ()= flushpage } { pop } ifelse ] [ ] { pop 
pstack ()= flushpage } { x_max width 0.50 add def (
intersect polygon edges with scanlines)= /P poly poly length 1 sub get 
def [ poly { Q exch def x_max miny floor cvi 0.50 add 1 maxy ceiling cvi
 0.50 sub { 1 index exch -0.50 1 index 4 2 roll P aload pop Q aload pop 
.intersect { 2 array astore exch } if } for pop /P Q def } forall ] (
sort scanline intersection list)= dup { 1 index 1 get 1 index 1 get eq 
{ exch 0 get exch 0 get lt } { exch 1 get exch 1 get lt } ifelse } qsort
 (set pixels on each scanline)= aload length 2 idiv { exch aload pop 3 2
 roll aload pop /USEDRAWLINE where { pop r g b 7 3 roll currentdict 
DrawLine } { pop 3 2 roll exch 1 exch dup width ge { pop width 1 sub } 
if { r g b 4 3 roll 2 index currentdict PutPix } for pop } ifelse } 
repeat end } --forall----cvx--[ aload pop .maxmin ] [ [ 16 154 ] [ 16 
154 ] ] { pop .maxmin } ] 
PS<3>
Run Code Online (Sandbox Code Playgroud)

现在大部分都可能是胡言乱语,而最重要的部分甚至可能都不可读.这个输出来自我自己的postscript解释器,它正在构建中,所有对象都具有完全访问权限.但是不要看顶部.看看底部.数组的最后一个元素是堆栈的最顶层元素.接下来的代码片段/command没有完成/errorname.那个小的postscript片段可以帮助你找到问题所在的源头.在上面的例子中,我需要在我的源代码中搜索.maxmin前面的调用,pop无论错误是什么.

打电话executive来获得提示

如果您在打印机中与解释器进行了串行或telnet会话,则可以键入executive并按Enter键几次.它可能不会在executive您键入时回显字母.没有恐惧,但拼写正确.它应该给你一个问候和提示.

使用ghostscript,运行没有参数的程序将为您提供相同类型的执行会话.然后(yourfile)run,您可以在出错后仍然得到提示,允许您检查$ error,如上所述.

如果这不起作用,您可以尝试运行executive 两次.这增加了额外级别的错误处理(stopped {handlerror} ifexec堆栈上的另一个).这可能有助于寻找更奇怪的错误.


AJM*_*eld 3

Emacs 包含 PostScript 工具。它包括用于向 postscript 解释器发送当前选择的文本的工具,您还可以直接在同一解释器中输入命令,例如查询操作数堆栈或类似的操作。

但这可能不是您想要的,因为它可能比您愿意使用的更难使用。不过,正确设置后,对于您想要监视的所有内容、执行操作的脚本和宏等使用不同的缓冲区,它将执行您想要的所有操作。我不确定,但网上其他地方可能有一些东西可以帮助您进行设置。

编辑:我使用 Emacs 调试 postscript 的主要方法是执行以下操作:我可以将程序的片段从文件缓冲区复制粘贴到解释器缓冲区中,作为逐步执行程序的一种方法。我还可以使用它来告诉我有关操作数堆栈的信息,使用命令打印其内容等。我还可以将调试语句添加到将输出到解释器缓冲区的代码中(例如dup ==等),因为我有点无法弄清楚如何在stdout使用其他环境执行程序时查看。