- 1. Debugging
- 1.1. Overview
- 1.2. Basic Debugging Techniques
- 1.3. Stack Debugging
- 1.4. Execution Tracing
- 1.5. Variable Inspection
- 1.6. Breakpoints
- 1.7. Error Analysis
- 1.8. Logging
- 1.9. Performance Debugging
- 1.10. Interactive Debugging
- 1.11. Assertions and Contracts
- 1.12. Test Utilities
- 1.13. Best Practices
- 1.14. Common Debugging Scenarios
- 1.15. See Also
1. Debugging
Debugging PostScript programs requires specialized techniques due to the stack-based nature and interpreted execution model. This guide covers essential debugging strategies, tools, and best practices.
1.1. Overview
Debugging techniques in PostScript include:
-
Print statements - Outputting values and messages
-
Stack inspection - Examining operand stack state
-
Execution tracing - Following program flow
-
Error analysis - Understanding error messages
-
Interactive debugging - Testing code incrementally
-
Logging - Recording program behavior
Effective debugging helps identify and fix issues quickly.
1.2. Basic Debugging Techniques
1.2.1. Print Debugging
The simplest debugging method:
% Print values
/x 42 def
(Value of x: ) print x =
% Print strings
(Debug: entering function) print
% Print with newlines
(Starting process) print ()
% Print array contents
[1 2 3 4 5] {
(Element: ) print =
} forall
1.2.2. The = Operator
Prints value and pops it:
% Print and consume value
5 10 add = % Prints: 15
% Print without consuming (use dup)
5 10 add dup =
% Value still on stack
1.3. Stack Debugging
1.3.1. Viewing Stack State
/showStack {
(=== Stack Contents ===) print
(Depth: ) print count =
count 0 gt {
pstack
} {
(Stack is empty) print
} ifelse
(====================) print
} def
% Usage
5 10 15
showStack
1.3.2. Stack Depth Monitoring
/withStackMonitor { % proc -> -
count /initialDepth exch def
exec
count initialDepth sub /delta exch def
delta 0 ne {
(Warning: Stack changed by ) print delta =
} if
} def
% Usage
{
5 10 add
% Forgot to consume result
20
} withStackMonitor
1.3.3. Stack Trace on Error
/debugExecute { % proc -> -
{
exec
} stopped {
(Error occurred!) print
(Stack trace:) print
pstack
(Error info:) print
$error /errorname get (Error: ) exch =string cvs concatstrings print
$error /command get (Command: ) exch =string cvs concatstrings print
} if
} def
% Usage
{
1 0 div % Will error
} debugExecute
1.4. Execution Tracing
1.4.1. Function Entry/Exit Logging
/traced { % name proc -> tracedProc
2 dict begin
/proc exch def
/name exch def
{
(>> Entering: ) print name print () print
proc exec
(<< Exiting: ) print name print () print
} bind
end
} def
% Usage
/myFunction {
(Processing...) print
5 10 add
} bind def
/myFunction (myFunction) myFunction traced def
myFunction % Will print entry/exit messages
1.4.2. Conditional Tracing
/debugLevel 0 def
/trace { % level message -> -
exch debugLevel le {
(TRACE: ) print print () print
} {
pop
} ifelse
} def
% Usage
/debugLevel 2 def
1 (Low detail) trace
2 (Medium detail) trace
3 (High detail - won't show) trace
1.4.3. Step-by-Step Execution
/step { % proc -> -
{
(Step - Stack: ) print pstack
(Press Enter to continue...) print
flush
(%stdin) (r) file 256 string readline pop pop closefile
exec
} stopped {
(Error during step execution) print
} if
} def
% Usage
{
5
{(Added 5) print} step
10
{(Added 10) print} step
add
{(Added them) print} step
} step
1.5. Variable Inspection
1.5.1. Inspecting Dictionaries
/inspectDict { % dict -> -
(=== Dictionary Contents ===) print
{
exch
dup type /nametype eq {
=string cvs
} {
(key) =string cvs
} ifelse
(: ) exch concatstrings print
dup type /nametype eq {
=string cvs print
} {
dup type /arraytype eq {
([array of ) exch length =string cvs
concatstrings
( elements]) concatstrings print
} {
=
} ifelse
} ifelse
} forall
(==========================) print
} def
% Usage
<< /name (John) /age 30 /scores [85 90 78] >>
inspectDict
1.5.2. Watch Variables
/Watcher {
10 dict begin
/watches <<>> def
/watch { % name value -> -
watches 3 1 roll put
} def
/update { % name value -> -
2 copy watches exch known {
watches 2 index get
1 index ne {
(Variable changed: ) print
2 index =string cvs print
( from ) print
watches 3 index get =string cvs print
( to ) print
dup =string cvs print
() print
} if
} if
watches 3 1 roll put
} def
/show {
(Watched variables:) print
watches {
exch =string cvs print
( = ) print
=
} forall
} def
currentdict
end
} def
% Usage
Watcher /watcher exch def
watcher /watch /x 0 exec
watcher /update /x 5 exec % Will print change
watcher /show exec
1.6. Breakpoints
1.6.1. Simple Breakpoints
/breakpoint { % message -> -
(BREAKPOINT: ) print print () print
(Stack contents:) print
pstack
(Continue? (y/n)) print
flush
(%stdin) (r) file 1 string readline pop pop
0 get 121 ne { % 121 = 'y'
stop
} if
} def
% Usage
/processData {
(Starting process) breakpoint
% ... processing
(Halfway through) breakpoint
% ... more processing
} def
1.7. Error Analysis
1.7.1. Error Reporter
/ErrorReporter {
<<
/report {
(=== ERROR REPORT ===) print
$error /errorname known {
(Error Type: ) print
$error /errorname get =string cvs print
() print
} if
$error /command known {
(Failed Command: ) print
$error /command get dup type /nametype eq {
=string cvs
} {
(complex)
} ifelse print
() print
} if
$error /ostack known {
(Operand Stack:) print
$error /ostack get {
( - ) print dup type =string cvs print
(: ) print
dup type /stringtype eq {
print
} {
=
} ifelse
} forall
} if
(===================) print
} bind
>>
} def
% Usage
{
1 0 div
} stopped {
ErrorReporter /report get exec
} if
1.8. Logging
1.8.1. Simple Logger
/Logger {
10 dict begin
/logLevel 2 def % 0=ERROR, 1=WARN, 2=INFO, 3=DEBUG
/logFile null def
/setLevel { /logLevel exch def } def
/setFile {
(a) file /logFile exch def
} def
/log { % level message -> -
2 dict begin
/msg exch def
/level exch def
level logLevel le {
/timestamp realtime def
/output
([) timestamp =string cvs concatstrings
(] ) concatstrings
level 0 eq { (ERROR: ) } if
level 1 eq { (WARN: ) } if
level 2 eq { (INFO: ) } if
level 3 eq { (DEBUG: ) } if
concatstrings
msg concatstrings
(\n) concatstrings
def
logFile null ne {
logFile output writestring
} {
output print
} ifelse
} if
end
} def
/error { 0 exch log } def
/warn { 1 exch log } def
/info { 2 exch log } def
/debug { 3 exch log } def
/close {
logFile null ne {
logFile closefile
/logFile null def
} if
} def
currentdict
end
} def
% Usage
Logger /log exch def
log /setLevel 3 exec
log /info (Application started) exec
log /debug (Processing data) exec
log /error (Something went wrong) exec
1.8.2. Structured Logging
/logEvent { % eventName data -> -
2 dict begin
/data exch def
/event exch def
([) print
realtime =string cvs print
(] EVENT: ) print
event print
data null ne {
( - Data: ) print
data type /stringtype eq {
data print
} {
data =
} ifelse
} if
() print
end
} def
% Usage
(UserAction) (clicked button) logEvent
(DataProcessed) 42 logEvent
1.9. Performance Debugging
1.9.1. Timing Functions
/timeIt { % label proc -> elapsedTime
2 dict begin
/proc exch def
/label exch def
usertime /startTime exch def
proc exec
usertime startTime sub /elapsed exch def
label print
( took ) print
elapsed =string cvs print
( ms) print
() print
elapsed
end
} def
% Usage
(Heavy computation) {
0 1 10000 {
dup mul pop
} for
} timeIt pop
1.9.2. Memory Profiling
/profileMemory { % label proc -> memoryDelta
2 dict begin
/proc exch def
/label exch def
vmstatus pop /before exch def
proc exec
vmstatus pop /after exch def
after before sub /delta exch def
label print
( used ) print
delta =string cvs print
( bytes) print
() print
delta
end
} def
% Usage
(Array allocation) {
10000 array
} profileMemory pop
1.10. Interactive Debugging
1.10.1. Debug REPL
/debugREPL {
(Debug REPL - Type 'quit' to exit) print
{
(debug> ) print flush
(%stdin) (r) file 256 string readline {
dup (quit) eq {
pop exit
} if
{
token {
exec
pstack
} {
pop
} ifelse
} stopped {
(Error executing command) print
} if
} {
pop exit
} ifelse
} loop
(Exiting debug REPL) print
} def
1.11. Assertions and Contracts
1.11.1. Assert Utility
/assert { % condition message -> -
exch not {
(ASSERTION FAILED: ) print print () print
pstack
stop
} {
pop
} ifelse
} def
% Usage
/divide { % a b -> a/b
dup 0 ne (Divisor must not be zero) assert
div
} def
1.11.2. Contract Checking
/withContract { % pre proc post -> -
3 dict begin
/post exch def
/proc exch def
/pre exch def
pre exec (Precondition failed) assert
proc exec
post exec (Postcondition failed) assert
end
} def
% Usage
{
count 2 ge % Need 2 values
} {
add
} {
count 1 ge % Should have 1 result
} withContract
1.12. Test Utilities
1.12.1. Unit Testing Framework
/TestRunner {
20 dict begin
/tests [] def
/passed 0 def
/failed 0 def
/addTest { % name testProc -> -
2 array astore
/tests [ tests aload pop 3 -1 roll ] def
} def
/run {
(Running tests...) print
tests {
aload pop
/proc exch def
/name exch def
(Test: ) print name print ( ... ) print flush
{
proc exec
(PASS) print
/passed passed 1 add def
} stopped {
(FAIL) print
/failed failed 1 add def
} ifelse
} forall
() print
(Results: ) print
passed =string cvs print
( passed, ) print
failed =string cvs print
( failed) print
} def
currentdict
end
} def
% Usage
TestRunner /runner exch def
runner /addTest (Addition) {
5 3 add 8 eq (5+3 should equal 8) assert
} exec
runner /run exec
1.12.2. Mocking
/Mock {
<<
/calls []
/record { % args -> -
/calls [ calls aload pop 3 -1 roll ] def
} bind
/getCalls {
calls
} bind
/reset {
/calls [] def
} bind
>>
} def
% Usage
Mock /mockFunction exch def
(arg1) mockFunction /record get exec
(arg2) mockFunction /record get exec
mockFunction /getCalls get exec length = % 2
1.13. Best Practices
1.13.1. Structured Logging Levels
% Use consistent logging levels
% ERROR: 0 - Critical errors
% WARN: 1 - Warnings
% INFO: 2 - Information
% DEBUG: 3 - Detailed debugging
/debug {
debugLevel 3 ge {
(DEBUG: ) print print () print
} {
pop
} ifelse
} def
1.14. Common Debugging Scenarios
1.14.1. Debugging Stack Issues
% Before suspicious operation
count /before exch def
pstack
% Suspicious operation
mysterio usOperation
% After
count /after exch def
after before sub /delta exch def
delta 0 ne {
(Stack changed by ) print delta =
} if
pstack
1.15. See Also
-
Error Handling - Error management
-
Advanced Error Handling - Complex error scenarios
-
Resource Management - Memory debugging
-
Stack Operations - Stack manipulation
-
Procedures - Procedure debugging