1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
2	"http://www.w3.org/TR/REC-html40/loose.dtd">
3<html>
4
5<head>
6<title>Source Level Debugging in Poly/ML</title>
7</head>
8
9<body bgcolor="#FFFFFF">
10
11<h2>Source Level Debugging in Poly/ML</h2>
12
13<p>Poly/ML includes a source-level debugger. &nbsp; You can use it to set breakpoints 
14  in the program and print the values of local variables.&nbsp; To turn on debugging 
15  for a particular piece of code set the compiler variable <tt>PolyML.Compiler.debug</tt> 
16  to true.&nbsp; You can freely mix functions compiled with debugging on with 
17  functions compiled with debugging off, you simply can't set a breakpoint in 
18  a non-debuggable function or print its internal state. &nbsp; The debugging 
19  control functions are all in the <tt>PolyML.Debug</tt> structure<tt>. </tt>It 
20  is often convenient to open this structure before debugging a program.</p>
21
22<h3><big>An Example Session.</big></h3>
23
24<p>To see how debugging works we'll follow through an example session.&nbsp; We have a
25small function that is supposed to add together a list of pairs of integers but it has an
26error in it which causes it not to terminate.&nbsp; We turn on debugging and compile in
27the program.</p>
28
29<p><tt>&gt; <strong>PolyML.Compiler.debug := true;</strong><br>
30val it = () : unit<br>
31&gt; <strong>fun addList a l =<br>
32&nbsp;&nbsp;&nbsp; let<br>
33&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fun f (b,c) = a+b+c<br>
34&nbsp;&nbsp;&nbsp; in<br>
35&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case l of<br>
36&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [] =&gt; a<br>
37&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | (x::y) =&gt;<br>
38&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
39let<br>
40&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
41val v = f x<br>
42&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
43val l' = y<br>
44&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
45in<br>
46&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
47addList v l<br>
48&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
49end<br>
50&nbsp;&nbsp;&nbsp; end;</strong><br>
51<br>
52val addList = fn : int -&gt; (int * int) list -&gt; int<br>
53&gt; <strong>open PolyML.Debug;</strong></tt></p>
54
55<p>We set a breakpoint in the function<tt> f</tt> using the <tt>breakIn</tt> function and
56apply the function to some arguments.</p>
57
58<p><tt>&gt; <strong>breakIn &quot;f&quot;;</strong><br>
59val it = () : unit<br>
60&gt; <strong>addList 0 [(1,2), (3, 4)];</strong></tt></p>
61
62<p>The function prints the line number and stops at the breakpoint.</p>
63
64<p><tt>line:3 function:addList()f<br>
65debug&gt; </tt></p>
66
67<p>The name <tt>addList()f</tt> is the full name of the function and we could have used
68this in place of <tt>f</tt> when setting the breakpoint.&nbsp; The <tt>&quot;debug&gt;&quot;</tt>
69prompt is almost identical to the normal Poly/ML <tt>&quot;&gt;&quot;</tt> prompt. &nbsp;
70The only difference is that variables which are in scope at the breakpoint are available
71as though they were declared globally.&nbsp; So we can print the values of <tt>a</tt>, <tt>b</tt>,
72<tt>c</tt> and <tt>l</tt>.</p>
73
74<p><tt>debug&gt; <strong>a;</strong><br>
75val it = 0 : int<br>
76debug&gt; <strong>b;</strong><br>
77val it = 1 : int<br>
78debug&gt; <strong>c;</strong><br>
79val it = 2 : int<br>
80debug&gt; <strong>l;</strong><br>
81val it = [(1, 2), (3, 4)] : (int * int) list<br>
82debug&gt; </tt></p>
83
84<p>In addition anything in the original top level is also available, provided it is not
85masked by a local declaration, so we can continue the program by calling the <tt>continue</tt>
86function which we opened from <tt>PolyML.Debug</tt>.</p>
87
88<p><tt>debug&gt; <strong>continue();</strong><br>
89val it = () : unit<br>
90line:3 function:addList()f<br>
91debug&gt;</tt></p>
92
93<p>This continues and we find ourselves back in <tt>f</tt> at the breakpoint again. &nbsp;
94We expect that and check the value of <tt>a</tt>.</p>
95
96<p><tt>debug&gt; <strong>a;</strong><br>
97val it = 3 : int<br>
98debug&gt;</tt></p>
99
100<p>However when we check <tt>b</tt> something seems to be wrong and printing <tt>l</tt>
101confirms it.</p>
102
103<p><tt>debug&gt; <strong>b;</strong><br>
104val it = 1 : int<br>
105debug&gt; <strong>l;</strong><br>
106val it = [(1, 2), (3, 4)] : (int * int) list<br>
107debug&gt;</tt></p>
108
109<p>We don't seem to be making any progress.&nbsp; We go up the stack to the recursive call
110of <tt>addList</tt> in order to check that the value of <tt>l'</tt> is correct.&nbsp; We
111have to go up twice because <tt>l'</tt> is not local to <tt>f</tt> nor is it available at
112the point where <tt>f</tt> was called.&nbsp; It is only available at the point where <tt>addList</tt>
113was called recursively.</p>
114
115<p><tt>debug&gt; <strong>up();</strong><br>
116line:9 function:addList<br>
117val it = () : unit<br>
118debug&gt; <strong>up();</strong><br>
119line:12 function:addList<br>
120val it = () : unit<br>
121debug&gt;<strong> l';</strong><br>
122val it = [(3, 4)] : (int * int) list<br>
123debug&gt; </tt></p>
124
125<p>This looks fine so the problem was not that <tt>l</tt>' had the wrong value.&nbsp; We
126print the values of everything using the <tt>dump</tt> function to see if that helps.</p>
127
128<p><tt>debug&gt; <strong>dump();</strong><br>
129Function addList()f: c = 2 b = 1 l = [(1, 2), (3, 4)] a = 3 <br>
130Function addList: x = (1, 2) y = [(3, 4)] f = fn l = [(1, 2), (3, 4)] a = 3 <br>
131Function addList: l' = [(3, 4)] v = 3 x = (1, 2) y = [(3, 4)] f = fn<br>
132l = [(1, 2), (3, 4)] a = 0 <br>
133<br>
134val it = () : unit</tt></p>
135
136<p>At this stage it is clear that we are passing the original value of <tt>l</tt> rather
137than what we intended,<tt> l'</tt>.&nbsp; We abort the function by typing control-C and f.</p>
138
139<p><tt>debug&gt; <strong>^C</strong><br>
140=&gt;<strong>f</strong><br>
141Compilation interrupted<br>
142Pass exception to function being debugged (y/n)?<strong>y</strong><br>
143Exception- Interrupt raised<br>
144&gt; </tt></p>
145
146<p>This returns us to the top level.&nbsp; We now fix the error and clear the breakpoint. </p>
147
148<p><tt>&gt; <strong>fun addList a l =<br>
149&nbsp;&nbsp;&nbsp; let<br>
150&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fun f (b,c) = a+b+c<br>
151&nbsp;&nbsp;&nbsp; in<br>
152&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case l of<br>
153&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [] =&gt; a<br>
154&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | (x::y) =&gt;<br>
155&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
156let<br>
157&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
158val v = f x<br>
159&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
160val l' = y<br>
161&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
162in<br>
163&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
164addList v l'<br>
165&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
166end<br>
167&nbsp;&nbsp;&nbsp; end;</strong><br>
168val addList = fn : int -&gt; (int * int) list -&gt; int<br>
169&gt; <strong>clearIn &quot;f&quot;;</strong><br>
170val it = () : unit</tt></p>
171
172<p>As a final check we turn on tracing to check that the values are as we expect and run
173the program with the same input as before.</p>
174
175<p><tt>&gt; <strong>trace true;</strong><br>
176val it = () : unit<br>
177&gt; <strong>addList 0 [(1,2), (3,4)];</strong><br>
178addList entered l = [(1, 2), (3, 4)] a = 0 <br>
179&nbsp; addList()f entered c = 2 b = 1 l = [(1, 2), (3, 4)] a = 0 <br>
180&nbsp; addList()f returned 3<br>
181&nbsp; addList entered l = [(3, 4)] a = 3 <br>
182&nbsp;&nbsp; addList()f entered c = 4 b = 3 l = [(3, 4)] a = 3 <br>
183&nbsp;&nbsp; addList()f returned 10<br>
184&nbsp;&nbsp; addList entered l = [] a = 10 <br>
185&nbsp;&nbsp; addList returned 10<br>
186&nbsp; addList returned 10<br>
187addList returned 10<br>
188val it = 10 : int<br>
189&gt; </tt></p>
190
191<p>This seems to have worked fine so we can now turn off <tt>PolyML.Compiler.debug</tt>
192and compile the function without debugging.</p>
193
194<p>&nbsp;</p>
195
196<h3><big>Reference</big></h3>
197
198<h3>Tracing program execution</h3>
199
200<p><tt>&nbsp;&nbsp;&nbsp; val trace = fn : bool -&gt; unit<br>
201  </tt>The <tt>trace</tt> function turns on and off function tracing.&nbsp; Function 
202  tracing prints the arguments and results of every debuggable function.</p>
203
204<h3>Breakpoints</h3>
205
206<p><tt>&nbsp;&nbsp;&nbsp; val breakAt = fn : string * int -&gt; unit<br>
207  &nbsp;&nbsp;&nbsp; val breakIn = fn : string -&gt; unit<br>
208  &nbsp;&nbsp;&nbsp; val breakEx = fn : exn -&gt; unit<br>
209  &nbsp;&nbsp;&nbsp; val clearAt = fn : string * int -&gt; unit<br>
210  &nbsp;&nbsp;&nbsp; val clearIn = fn : string -&gt; unit<br/>
211  &nbsp;&nbsp;&nbsp; val clearEx = fn : exn -&gt; unit</tt></p>
212
213<p>Breakpoints can be set with the <tt>breakAt</tt>, <tt>breakIn</tt> or <tt>breakEx</tt> 
214  functions and cleared with <tt>clearAt</tt>, clearIn or <tt>clearEx</tt>.&nbsp; 
215  <tt>breakAt</tt> takes a file name and line number and <tt>breakIn</tt> a function 
216  name.&nbsp; The file name and line have to given exactly otherwise the breakpoint 
217  will not work.&nbsp; <tt>breakIn</tt> is more flexible about the function name.&nbsp; 
218  It can be the function name used in the declaration (e.g. <tt>f</tt>) or the 
219  full &quot;path name&quot;.&nbsp; The latter is useful when the program contains 
220  several functions with the same name since setting a breakpoint in <tt>f</tt> 
221  stops in any function called <tt>f</tt>.&nbsp; <tt>breakIn</tt> and <tt>breakAt</tt> 
222  simply record that you want a breakpoint.&nbsp; There is no check when they 
223  are called that the appropriate location actually exists and you can set a breakpoint 
224  for a function and then compile it later. <tt>breakEx</tt> sets a breakpoint 
225  for a particular exception and causes the program to stop at the end of the 
226  function that raises the exception unless it is also handled there. The argument 
227  is the exception to trap. It is possible to trap exceptions that take a parameter. 
228  Just provide it with a dummy parameter to create a value of type <tt>exn</tt> 
229  that can be passed to <tt>breakEx</tt>. The actual parameter value will be ignored 
230  and the debugger will be entered whenever the exception is raised with any parameter 
231  value. </p>
232
233<p>When a breakpoint is reached the program stops with a <tt>debug&gt;</tt> prompt. &nbsp;
234This is a normal Poly/ML top-level and you can type any ML expression.&nbsp; The only
235difference is that local variables in the function being debugged are available as though
236they had been declared at the top-level.&nbsp; You can print them or use them in any way
237that you could with a normal variable.&nbsp; This includes local functions which can be
238applied to local values, constants or globals. You cannot change the value of a variable
239unless it is a reference.&nbsp; At a breakpoint you can continue or single-step the
240program or you can raise an exception.&nbsp; This is usually the most convenient way of
241aborting a program and getting back to the normal top-level unless the program has a
242handler for the exception you raise.</p>
243
244<h3>Single Stepping and Continuing</h3>
245
246<p><tt>val continue = fn : unit -&gt; unit<br>
247val step = fn : unit -&gt; unit<br>
248val stepOver = fn : unit -&gt; unit<br>
249val stepOut = fn : unit -&gt; unit</tt></p>
250
251<p><tt>continue</tt> runs the program to the next breakpoint.<tt>&nbsp; step</tt>,&nbsp; <tt>stepOver</tt>
252and <tt>stepOut</tt> are different ways of single-stepping through a function.
253&nbsp;&nbsp; <tt>step</tt> runs the program up to the next breakable point which is often
254the next source line.&nbsp; If evaluation involves calling a function then it may stop at
255the beginning of the function.&nbsp; By contrast <tt>stepOver</tt> stops at the next line
256within the current function only.&nbsp; <tt>stepOut</tt> goes further and stops only
257within the function which called the current function.&nbsp; If a called function includes
258a breakpoint they always stop there.</p>
259
260<h3>Examining and Traversing the Stack</h3>
261
262<p><tt>val up = fn : unit -&gt; unit<br>
263val down = fn : unit -&gt; unit<br>
264val dump = fn : unit -&gt; unit<br>
265val variables = fn : unit -&gt; unit</tt></p>
266
267<p><tt>up</tt> and <tt>down</tt> move the focus between called and calling functions
268allowing you to view local variables at different levels.&nbsp; This is particularly
269useful for recursive functions.&nbsp; <tt>The variables</tt> function prints all the
270variables in scope at the current point.&nbsp; <tt>dump</tt> gives a complete stack trace.</p>
271
272<h3>Notes</h3>
273
274<p>The current implementation includes most values but not types or structures. &nbsp;
275&nbsp; A recursive function is not available within the function itself.</p>
276
277<p>The compiler adds debugging information which considerably increases the run time of
278the program.&nbsp; It is probably best to turn debugging on only when it is needed.</p>
279
280<p>The example shows debugging when all the variables have monomorphic types.&nbsp; The
281debugger does not have access to any run-time type information so it cannot always know
282how to print a value inside a polymorphic type.&nbsp; For example</p>
283
284<p><tt>&gt; fun map f [] = [] | map f (a::b) = f a :: map f b;<br>
285val map = fn : ('a -&gt; 'b) -&gt; 'a list -&gt; 'b list<br>
286&gt; breakIn &quot;map&quot;;<br>
287val it = () : unit<br>
288&gt; map (fn i =&gt; i+1) [1,2,3];<br>
289line:1 function:map<br>
290debug&gt; a;<br>
291val it = ? : 'a</tt></p>
292
293<p>If you know the type is int you can add a type constraint.</p>
294
295<p><tt>debug&gt; a:int;<br>
296val it = 1 : int<br>
297debug&gt; </tt></p>
298
299<p>It is though equally possible to use the wrong constraint and crash Poly/ML. 
300  &nbsp; Future versions of Poly/ML may treat polymorphic variables as opaque 
301  which would prevent this but also prevent &quot;safe&quot; coercions.</p>
302<p>&nbsp;</p>
303
304</body>
305</html>
306