ghci debugger :trace command does not always extend trace
history
Peter Hercek
phercek at gmail.com
Tue Jan 6 17:30:47 EST 2009
Hi Simon ... and whoever thinks he/she can help me with the right way of
debugging with ghci,
Sorry for the long post but I cannot explain more shortly what I would
like to be able to do ... and maybe you find my ":find" and ":monitor"
commands at the end useful ... and maybe you can propose some other
useful user defined commands for ghci which were not mentioned in Claus'
tutorial :-)
Ok, so it is not a bug because each trace command creates a new context
and trace history is context specific. This means my question now is:
How can I get access to trace history of a nested trace command which
just finished?
Here are some explanations so that we understand each other:
The top level trace of my old example is ":trace test 0".
The nested trace of my old example is ":trace z".
I would like to get access to the trace history of the nested trace
after the nested trace finishes (e.g. after the command ":trace z"
finished by printing the z value).
Or the other option would be: How to launch ":trace z" so that a new
nested trace is not created but the current trace (the one before
launching ":trace z") is extended?
I know I can use ":trace" to continue the current trace but this
requires that I have some suitable breakpoint later on a place where
value z is already forced and before the interesting trace history is
not replaced with newer stuff. It may not be trivial to find such a place.
Other option would be to place breakpoints on all the interesting places
which lead to the computation of the result being forced with ":trace
<varName>" and investigate the traces in the nested context. The problem
here is that it is more work. If I would know the result first then I
could focus only on the places which are the probable reason of the
wrong value, moreover the lure of ":find" is tempting (I explain ":find"
later). Typically the situation is more complicated (there are more
options) than my small example.
My requests/questions may look strange, but here is reasoning why I
think I need the functionality. If you have better approach to debugging
please let me know. Maybe there is still some interesting tutorial I did
not read about ghc debugging or I just forgot something.
When I debug, I typically know what is probably wrong based on the
program output. So I place a breakpoint at one logical step before the
output. When stopped at the breakpoint I force variables which do not
need to stay lazy and from which the program output is printed. Then I
can see if some part of the force result/output is unexpected. Then I
would like to search the trace history to see values of variables which
probably contributed to the wrong value of the force result/output. I
hoped that using :trace instead of force would append the trace history.
This backward search for a bug means less work since I need to
investigate only the path from the dynamic evaluation tree root (the
wrong program result) to a given node where the bug is. An ability to
access trace history of a value I just forced would save me some program
restarts. How much restarts would be saved depends on the trace history
depth and my ability not to get lost in the order of lazy calls. If I do
not have access to the trace history, the only option is to put a
breakpoint at a place where the variable value I wanted to check is
available and restart the program so that I can check the value.
Since there is no way to print partially applied functions this value
based approach is probably the only one working. This is supported by
recommendations to use printf debugging.
One more reason I would like to learn how to access the trace history of
a forced value is that my :monitor commands conflict with ghci trace
history. I was able to implement :monitor and :find commands based on
Claus' ghci scripting tutorial:
:monitor displays value of a given variable at a given location while
discarding all the other output. It is a way to do printf debugging
solely from ghci without changing source code (i.e. without adding
Debug.Trace.trace calls). :monitor (used together with ":x") is not
always better than printf debugging because it discards program output
too, and cannot add an identifier to scope. But often it is ... at least
for me because it separates printf debugging (defined using ":monitor"
commands in a script I source with ":.") from the source code.
:find finds a variable name in trace history, prints its value, and a
location where it was found. The location is needed here because of name
conflicts. The value located may not be the right one. Taking static
scopes into account could discard a lot of false finds (not all of
them). I think that taking static scopes and (maybe more detailed) trace
history into account could rule out all false positives. But nobody
wants to implement this in a ghci script ... I guess :-)
Currently my :monitor command makes holes in trace history, lowering the
value of :find command :-(
If you are interested you can find :find and :monitor implementations
here: http://www.hck.sk/users/peter/pub/.ghci
It looks to me that the way tracing is done is unfortunate from the
point of view of scripting custom ghci commands. It would be better to
have a global trace history which would be activated/deactivated with
command like ":trace on/off" and this would modify the behavior of the
rest of the ghci commands appropriately. E.g. :continue would behave
like :trace (without arguments) when tracing is on. It would be probably
interesting to have more tracing buffers (but I did not think about this
much). Now, it is done in a cool functional way which means more
complicated user defined ghci commands ... unless I'm missing something :-)
Thanks,
Peter.
Simon Marlow wrote:
> Peter Hercek wrote:
>> Hi,
>>
>> I expected ":trace expr" to always add data to the trace history but
>> it does not do so for CAFs (which are not reduced yet).
>> My point is that the command ":trace z" did not add anything to the
>> trace history and I cannot check why value z is 2, because value of y
>> is not in the trace history. Is this the expected behavior? If it is,
>> how can I make ghci to extend the trace history when "forcing" variables?
>>
>> Peter.
>>
>> Here is the example:
>>
>> status:0 peter at metod [765] ~/tmp
>> % cat a.hs
>> test :: Int -> Int
>> test x =
>> let y = x+1 in
>> let z = y+1 in
>> z
>> status:0 peter at metod [766] ~/tmp
>> % ghci a.hs
>> GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help
>> Loading package base ... linking ... done.
>> [1 of 1] Compiling Main ( a.hs, interpreted )
>> Ok, modules loaded: Main.
>> *Main> :break Main 5
>> Breakpoint 0 activated at a.hs:5:2
>> *Main> :trace test 0
>> Stopped at a.hs:5:2
>> _result :: Int = _
>> z :: Int = _
>> 4 let z = y+1 in
>> 5 z
>> 6
>> [a.hs:5:2] *Main> :back
>> Logged breakpoint at a.hs:(2,0)-(5,2)
>> _result :: Int
>> 1 test :: Int -> Int
>> 2 test x =
>> 3 let y = x+1 in
>> 4 let z = y+1 in
>> 5 z
>> 6
>> [-1: a.hs:(2,0)-(5,2)] *Main> :back
>> no more logged breakpoints
>
> ok so far - y and z have not been evaluated.
>
>> [-1: a.hs:(2,0)-(5,2)] *Main> :forward
>> Stopped at a.hs:5:2
>> _result :: Int
>> z :: Int
>> 4 let z = y+1 in
>> 5 z
>> 6
>> [a.hs:5:2] *Main> :trace z
>> 2
>
> this evaluates z.
>
>> [a.hs:5:2] *Main> :back
>> Logged breakpoint at a.hs:(2,0)-(5,2)
>> _result :: Int
>> 1 test :: Int -> Int
>> 2 test x =
>> 3 let y = x+1 in
>> 4 let z = y+1 in
>> 5 z
>> 6
>
> You are going back in the original context, but I presume you were
> expecting to go back in the evaluation of z. You can only go back in
> the context of the current evaluation, however.
>
> Try this:
>
> *Main> :break 3
> Breakpoint 4 activated at trace.hs:3:10-12
> *Main> :trace test 0
> Stopped at trace.hs:3:10-12
> _result :: Int = _
> x :: Int = 90
> 2 test x =
> 3 let y = x+1 in
> 4 let z = y+1 in
> [trace.hs:3:10-12] *Main> :history
> -1 : test (trace.hs:4:10-12)
> -2 : test (trace.hs:5:2)
> -3 : test (trace.hs:(2,0)-(5,2))
> <end of history>
> [trace.hs:3:10-12] *Main> :back
> Logged breakpoint at trace.hs:4:10-12
> _result :: Int
> y :: Int
> 3 let y = x+1 in
> 4 let z = y+1 in
> 5 z
> [-1: trace.hs:4:10-12] *Main> :back
> Logged breakpoint at trace.hs:5:2
> _result :: Int
> z :: Int
> 4 let z = y+1 in
> 5 z
> 6
>
> Cheers,
> Simon
More information about the Glasgow-haskell-users
mailing list