Ответ 1
минимальный отладчик
Я только что обнаружил действительно полезный хак.
<<
/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, и хук - это то место, где разрыв получает свой первый эффект). Хук-вызовы pause и pause звонки doprompt
что дает вам однострочное приглашение "break>". Печатание cont
здесь очистит крючок и продолжит вращаться, не делая никаких пауз, пока не встретится другой break
. Вы также можете проверить значения и выполнить код в doprompt
строке, но выполнение nb возобновляется, когда вы doprompt
Enter, поэтому, если вам нужны дополнительные строки, вызовите doprompt
или pause
в конце строки. Ошибки игнорируются при выполнении команды (вы не хотите, чтобы отладчик аварийно завершал работу программы, это было бы глупо!). Я полагаю, я мог бы объединить паузу и допромпт и исключить имя; но цель здесь не в эффективности машины, а в четком наборе понятий: этот код, чтобы вообще быть полезным при отладке другого кода, должен легко сканироваться и проверяться.
Как это отладчик, он просто читает строку?!
Помните, что у вас есть =
и ==
для изучения значений. forall
и get
бюст до массивов и прочее. Чтобы по-настоящему выяснить, где вы находитесь, countexecstack array execstack ==
для удобочитаемого дампа всего кабачка. То есть обратная трассировка текущей позиции в стеке выполнения, которая содержит хвосты всех частично выполненных процедур и файлов, ожидающих возобновления при возвращении текущего кадра.
"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} Error: /stackunderflow in --forall-- Operand stack: --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
.]
ошибочный взлом
Другой способ исследовать ошибку - изменить обработчик ошибок. Чтобы исследовать описанную выше ошибку /stackunderflow
я мог бы использовать
errordict/stackunderflow{dup == /stackunderflow signalerror}put
вместо того, чтобы специализировать forall
. Чтобы узнать об этом довольно загадочном аспекте приписки, читать на errordict
stop
и stopped
. И в интерактивном режиме errordict{exch =only ==}forall
на errordict{exch =only ==}forall
. signalerror
ошибка в ghostscript называется .error
в интерпретаторах Adobe. Его задача - сделать снимки стеков, а затем вызвать stop
чтобы вытолкнуть стек exec. Таким образом, dup ==
здесь и pstack
выше по сути являются одним и тем же "моментом" ошибки до stop
. Ваш интерактивный сеанс (и предыдущая программа в обычном режиме gs) заключены в скобки глубже в стеке exec с эквивалентом //your-program stopped { handleerror } if
. Именно handleerror
использует моментальные снимки (после 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
перед которыми .maxmin
pop
перед... независимо от ошибки.
позвонить executive
чтобы получить подсказку
Если у вас есть сеанс последовательного или telnet с переводчиком на принтере, вы можете набрать executive
и нажать несколько раз. Это может не повторять исполнительные письма при executive
. Не бойтесь, но пишите правильно. Это должно дать вам приветствие и подсказку.
С ghostscript запуск программы без аргументов даст вам тот же вид исполнительной сессии. Затем вы можете (yourfile)run
и вы все равно должны получить сообщение об ошибках, позволяющее вам проверить $ error, как описано выше.
Если это не сработает, вы можете попробовать запустить executive
дважды. Это добавляет дополнительный уровень обработки ошибок (еще один stopped {handlerror} if
в стеке exec). Это может помочь в поиске более странных ошибок.
пошаговый отладчик
У меня есть пошаговый отладчик на уровне исходного кода, который должен работать в любом интерпретаторе PostScript, совместимом с уровнем 2.
Его также можно использовать для генерации трассировки стека, как показано в этом ответе на TeX.SE.