13

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

4

3 回答 3

22

最小调试器

我刚刚发现了一个非常有用的技巧。

<<
/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 模拟器做了这个。放入hook主循环,放入break一个程序以在下一个钩子处触发暂停(我将它放在可疑的 OPCODE procs 中,钩子是中断发挥其第一个作用的地方)。挂机呼叫暂停和暂停呼叫doprompt,为您提供单行“中断>”提示。在此处键入cont将清除钩子并继续旋转,break在遇到另一个暂停之前不会执行任何暂停。您还可以在提示符下检查值并执行代码,但是当您按 Enter 时, nbdoprompt执行会恢复,因此如果您需要其他行,请调用或pause在行尾。执行命令时会忽略错误(您不希望调试器使程序崩溃,那太愚蠢了!)。我想我可以结合 pause 和 doprompt 并删除一个名字;但这里的目标不是机器效率,而是清晰的概念集合:这段代码要在调试其他代码时完全有用,需要易于扫描和验证。

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

记住你已经得到===调查价值观。forallget破坏数组和东西。要真正找出你在哪里,请做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. ]


errordict 黑客

调查错误的另一种方法是更改​​错误处理程序。要调查上述/stackunderflow错误,我可以使用

errordict/stackunderflow{dup == /stackunderflow signalerror}put

而不是专门化forall。要了解后记这个相当神秘的方面,请继续阅读errordict stopstopped。并以交互方式查看errordict{exch =only ==}forall. signalerror在 Adob​​e 解释器中调用了 ghostscript .error。它的工作是拍摄堆栈的快照,然后调用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>

当然,这里的对象仍然在堆栈上。但是$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 两次。这增加了额外的错误处理级别(另一个stopped {handlerror} if在 exec 堆栈上)。这可能有助于寻找更奇怪的错误。

逐步调试器

我有一个源级逐步调试器,它应该在任何符合 2 级的 PostScript 解释器中运行。

它还可用于生成堆栈跟踪,如TeX.SE 上的此答案所示。

于 2012-09-15T06:19:32.497 回答
3

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

不过,这可能不是您想要的,因为它可能比您愿意使用的更难使用。但是,正确设置,为您想要监控的所有事情、脚本和宏做事情等使用不同的缓冲区,它会做您想做的一切。我不确定,但网络上其他地方可能有一些东西可以帮助你设置它。

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

于 2012-09-14T11:20:38.263 回答
3

在 OS X 10.7.5 上,预览在爆炸时没有给我任何细节,但/usr/bin/pstopdf确实给了我一个到标准输出的堆栈转储,这pstack也是。

如果我在预览中打开 pdf 文件,则在运行后更改回预览pstopdf将刷新新创建的 pdf 文件的视图。

它不是高科技,但您可以快速迭代。

于 2012-12-15T06:42:29.233 回答