【发布时间】:2012-09-11 23:03:43
【问题描述】:
如何在 PostScript 中进行调试?我可以在 Linux 上使用 GhostView/GhostScript,但无法查看堆栈、字典等。
【问题讨论】:
标签: debugging postscript
如何在 PostScript 中进行调试?我可以在 Linux 上使用 GhostView/GhostScript,但无法查看堆栈、字典等。
【问题讨论】:
标签: debugging postscript
我刚刚发现了一个非常有用的技巧。
<<
/break{ /hook /pause load store }
/cont{ /hook {} store }
/doprompt{
(\nbreak>)print
flush(%lineedit)(r)file
cvx {exec}stopped pop }
/pause{ doprompt }
/hook{}
>> begin
这是一个 8 行 EMBEDDABLE 调试器。我为我的8086 emulator 做了这个。将hook 放在主循环中,将break 放在一个过程中以触发下一个钩子的暂停(我把它放在可疑的OPCODE procs 中,钩子是中断第一个作用的地方)。挂钩调用 pause 和 pause 调用doprompt 为您提供单行“break>”提示。在此处键入cont 将清除钩子并继续旋转,在遇到另一个break 之前不会执行任何暂停。您还可以在提示符下检查值并执行代码,但是 n.b. 会在您按 Enter 时恢复执行,因此如果您需要其他行,请在行尾调用 doprompt 或 pause。执行命令时会忽略错误(您不希望调试器使程序崩溃,那太愚蠢了!)。 我想我可以结合 pause 和 doprompt 并删除一个名字;但这里的目标不是机器效率,而是清晰的概念集合:这段代码要在调试其他代码时完全有用,需要易于扫描和验证。
这是一个调试器,它只是读取一行?!
请记住,您有 = 和 == 来调查值。 forall 和 get 破坏数组和东西。要真正找出您所在的位置,请通过 countexecstack array execstack == 获取整个 caboodle 的可读转储。也就是说,执行堆栈中当前位置的回溯,其中包含所有部分执行的过程的尾部和等待当前帧返回时恢复的文件。
有相当多的调试可以在没有调试器的情况下完成,只需检测您的程序(添加printfs,可以这么说)。
刚才我遇到了一个错误,我的调试器无法帮助我解决,因为调试器本身在一些过于聪明的东西上崩溃了,比如
/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
所以我调查的实际错误是
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
而我真正需要知道的是操作数堆栈上的 --nostringval-- 到底是什么。
所以我把它放在程序的开头
/forall { pstack()= forall } bind def
然后再次运行它
{MO matmul 0 --get-- --aload-- --pop-- proj 动作}
错误:/stackunderflow 在 --forall--
操作数栈:
--nostringval--
...
就在错误之前是最终的堆栈转储(使用==),它告诉我有一个过程主体缺少其数据集。
pstack 和这样的东西相比有点生硬
/args { dup 1 add copy -1 1 { -1 roll ==only ( ) print } for } def
/forall { 2 args (forall)= forall } bind def
这对于在明显有效的代码中追踪错误数据更有用。这也是非常早期版本的 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"
[我之前在这里写了 '=' 而不是 'stack'。一个糟糕的错误。 编辑:将缺少的 pop 添加到 /stack。]
另一种调查错误的方法是更改错误处理程序。为了调查上面描述的/stackunderflow 错误,我可以使用
errordict/stackunderflow{dup == /stackunderflow signalerror}put
而不是专攻forall。要了解后记这个相当神秘的方面,请阅读errordictstop 和stopped。并以交互方式查看errordict{exch =only ==}forall。 Ghostscript 中的 signalerror 在 Adobe 解释器中称为 .error。它的工作是拍摄堆栈的快照,然后调用stop 来弹出执行堆栈。所以这里的dup == 和上面的pstack 在stop 之前基本上是相同的错误“时刻”。您的交互式会话(以及前面在 gs 的正常模式下的程序)在 exec 堆栈上的括号更深,相当于//your-program stopped { handleerror } if。它是handleerror,它使用快照(在程序被清除后)打印错误报告及其无信息的堆栈打印输出。
您可以找到handleerror 的替代品,您可以在错误程序的开头使用(ehandle.ps)run 来生成不同样式的错误报告。
我只是在重新阅读这里的示例时发现了这一点。如果解释器仍在给您提示,您还可以在错误后调查堆栈。错误信息保存在$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>
当然,这里的对象仍然在堆栈上。但是$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>
现在大部分内容可能都是胡言乱语,前几部分甚至可能无法阅读。此输出来自我自己的 postscript 解释器,该解释器正在建设中,所有对象都具有完全访问权限。但不要看顶部。看看底部。数组的最后一个元素是堆栈的最顶部元素。如果/command 没有消失并完成/errorname,那么接下来会出现这段代码。那个小后记片段可以帮助您找到问题的根源所在。在我上面的例子中,我需要在我的来源中搜索对 .maxmin 的调用,前面是 pop,前面是 .. 不管错误是什么。
executive获取提示如果您与打印机中的解释器进行串行或远程登录会话,您可以键入 executive 并按回车数次。它可能不会在您键入时回显executive 的字母。不要害怕,但拼写正确。它应该给你一个问候和一个提示。
使用ghostscript,运行不带参数的程序将为您提供相同类型的执行会话。然后您可以(yourfile)run 并且在出现错误后仍然会收到提示,允许您按上述方式检查 $error。
如果这不起作用,您可以尝试运行executive两次。这增加了额外的错误处理级别(exec 堆栈上的另一个stopped {handlerror} if)。这可能有助于寻找更奇怪的错误。
我有一个source-level stepwise debugger,它应该在任何符合 2 级标准的 PostScript 解释器中运行。
它还可用于生成堆栈跟踪,如answer on TeX.SE 所示。
【讨论】:
/forall { pstack()= forall } bind def 不是 PLRM 推荐的。以这种方式使用 bind 重新定义运算符是不正确的,因为许多运算符可能被实现为过程,而这种方法对于那些将失败。而且很难预测哪些是在任何给定的解释器下。
在 OS X 10.7.5 上,预览在爆炸时没有给我任何详细信息,但 /usr/bin/pstopdf 确实给了我到标准输出的堆栈转储,这也是 pstack 的所在。
如果我在预览中打开 pdf 文件,则在运行 pstopdf 后更改回预览将刷新新创建的 pdf 文件的视图。
这不是高科技,但您可以快速迭代。
【讨论】:
Emacs 包含 PostScript 工具。它包括用于将您当前选择的文本发送到 postscript 解释器的工具,您还可以将命令直接输入到同一个解释器中,例如查询操作数堆栈或类似的事情。
不过,这可能不是您想要的,因为它可能比您愿意使用的更难使用。但是,正确设置,为您想要监控的所有事情、脚本和宏做事情等使用不同的缓冲区,它会做您想做的一切。我不确定,但网络上的其他地方可能有一些东西可以帮助您进行设置。
编辑: 我使用 Emacs 调试 postscript 的主要方法是执行以下操作:
我可以将程序的片段从文件缓冲区复制粘贴到解释器缓冲区中,作为单步执行我的程序的一种方式。我还可以使用它来告诉我有关操作数堆栈的信息,使用命令打印出它的内容等。我还可以将调试语句添加到将输出到解释器缓冲区的代码(如dup == 等),因为在使用其他环境执行程序时,我有点无法弄清楚如何查看stdout。
【讨论】:
:!gs %);运行一行,捕获输出 (!!gs -q -)。但这就是我所知道的一切。