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メインループに入れ、次のフックで一時停止をトリガーするプロシージャを入れます(疑わしいOPCODEbreakプロシージャに入れます。フックは、ブレークが最初の効果を発揮する場所です)。フックコールは一時停止し、コールを一時停止しますdoprompt。これにより、1行の「break>」プロンプトが表示されます。ここに入力すると、フックがクリアされて回転し続け、別のフックに遭遇contするまで一時停止を実行しません。breakプロンプトで値を調べてコードを実行することもできますが、 Enterキーを押すとnbの実行が再開されるため、追加の行が必要な場合は、dopromptまたはpause行の終わりに。コマンドの実行中はエラーは無視されます(デバッガーがプログラムをクラッシュさせたくない場合は、ばかげています!)。一時停止とプロンプトを組み合わせて名前を削除できると思います。ただし、ここでの目標は、マシンの効率ではなく、概念の明確なコレクションです。このコードは、他のコードのデバッグにまったく役立つため、スキャンと検証が簡単である必要があります。

これはどのようにデバッガーであり、行を読み取るだけですか?!

=値を==調査する必要があることを忘れないでください。forallアレイなどgetを破壊します。あなたがどこにいるのかを本当に知るために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

だから私が調査していた実際のエラーは

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 action}

エラー:-forall--の/ stackunderflow
オペランドスタック:
   --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"

[以前、ここで「スタック」の代わりに「=」を記述しました。悪い失敗。編集:不足popしているものをに追加しました/stack]


errordictハッキング

エラーを調査する別の方法は、エラーハンドラーを変更することです。/stackunderflow上記のエラーを調査するために、私は使用できたはずです

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

専門化する代わりにforall。追記のこのかなり難解な側面について学ぶために、とを読んでerrordict stopくださいstopped。そしてインタラクティブに、を覗いてみてerrordict{exch =only ==}forallください。signalerrorghostscript.errorではAdobeインタプリタで呼び出されます。その仕事は、スタックのスナップショットを取り、次にstopexecスタックをポップするために呼び出すことです。したがって、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。

取得できる非常に便利な情報の1つは、エラーが発生した時点での実行スタックのコピーである、の$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>

今ではそのほとんどがおそらくぎこちないものになり、上位のいくつかの部分は読めないかもしれません。この出力は、作成中の私自身のポストスクリプトインタープリターからのものであり、すべてのオブジェクトが完全にアクセスできます。しかし、上部を見ないでください。下部を見てください。配列の最後の要素は、スタックの最上位の要素です。/commandこれは、次に実行されなかったはずのコードです/errorname。その小さな追記の断片は、問題がソースのどこにあるかを見つけるのに役立ちます。上記の私の場合、エラーが何であれ、前に..が.maxmin前にある呼び出しをソースで検索する必要があります。pop

executiveプロンプトを取得するために呼び出す

プリンタのインタプリタとのシリアルセッションまたはTelnetセッションがある場合は、 Enterキーをexecutive数回入力して押すことができます。入力時にの文字がエコーされない場合がありexecutiveます。恐れることはありませんが、正しくつづります。それはあなたに挨拶とプロンプトを与えるはずです。

ghostscriptを使用すると、引数なしでプログラムを実行すると、同じ種類のエグゼクティブセッションが得られます。その後(yourfile)run、エラーが発生した後もプロンプトが表示され、上記のように$errorを検査できます。

それでも問題が解決しない場合は、executive 2回実行してみてください。これにより、エラー処理のレベルが追加されます(stopped {handlerror} ifexecスタックにもう1つ)。これは、より奇妙なエラーを探すのに役立つ場合があります。

段階的デバッガー

レベル2準拠のPostScriptインタープリターで実行する必要があるソースレベルの段階的デバッガーを利用できます。

TeX.SEのこの回答に示されているように、スタックトレースを生成するためにも使用できます。

于 2012-09-15T06:19:32.497 に答える
3

EmacsにはPostScriptツールが含まれています。これには、現在選択されているテキストをポストスクリプトインタプリタに送信するためのツールが含まれています。また、同じインタプリタにコマンドを直接入力して、たとえば、オペランドスタックをクエリすることもできます。

ただし、これはあなたが探しているものではないかもしれません。なぜなら、あなたが喜んで作業するよりも使いにくいかもしれないからです。ただし、監視したいすべてのことに対して異なるバッファー、何かを実行するためのスクリプトとマクロなどを使用して正しく設定すると、必要なすべてのことを実行できます。よくわかりませんが、ネット上の他の場所に設定に役立つものがあるかもしれません。

編集:私がEmacsを使用してpostscriptをデバッグする主な方法は、次のことです。プログラムをステップスルーする方法として、プログラムのセグメントをファイルバッファーからインタープリターバッファーにコピーアンドペーストできます。また、コマンドを使用してその内容を出力するなど、オペランドスタックについての情報を提供するためにも使用できます。プログラムが他の環境を使用して実行されているときdup ==に表示する方法を理解できなかったため、インタープリターバッファーに出力されるコード(など)にデバッグステートメントを追加することもできます。stdout

于 2012-09-14T11:20:38.263 に答える
3

OS X 10.7.5 では、プレビューは爆撃時に詳細を提供しませんが、/usr/bin/pstopdfstdout へのスタック ダンプを提供しpstackます。

pdf ファイルをプレビューで開いた場合、実行後にプレビューに戻すとpstopdf、新しく作成された pdf ファイルのビューが更新されます。

ハイテクではありませんが、かなり高速に反復できます。

于 2012-12-15T06:42:29.233 に答える