Giter VIP home page Giter VIP logo

debug's Introduction

Haskell Debugger Hackage version Stackage version Linux build status Windows build status

A library for debugging Haskell programs. To use, take the functions that you are interested in debugging, e.g.:

module QuickSort(quicksort) where
import Data.List

quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (x:xs) = quicksort lt ++ [x] ++ quicksort gt
    where (lt, gt) = partition (<= x) xs

Turn on the TemplateHaskell, ViewPatterns and PartialTypeSignatures extensions, import Debug, indent your code and place it under a call to debug, e.g.:

{-# LANGUAGE TemplateHaskell, ViewPatterns, PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
module QuickSort(quicksort) where
import Data.List
import Debug

debug [d|
   quicksort :: Ord a => [a] -> [a]
   quicksort [] = []
   quicksort (x:xs) = quicksort lt ++ [x] ++ quicksort gt
       where (lt, gt) = partition (<= x) xs
   |]

We can now run our debugger with:

$ ghci QuickSort.hs
GHCi, version 8.2.1: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling QuickSort        ( QuickSort.hs, interpreted )
Ok, 1 module loaded.
*QuickSort> quicksort "haskell"
"aehklls"
*QuickSort> debugView

The call to debugView starts a web browser to view the recorded information, looking something like:

Debug view output

You can look and play with the example results for various examples:

Build tool: debug-pp

debug-pp is a Haskell source preprocessor for streamlining the debug instrumentation of a module or a package. It performs the steps described above automatically. That is:

  • append an import for the Debug module,
  • wrap the body in a debug splice using a TH declaration quasiquote, and
  • add the required GHC extensions.

To instrument a module, add the following pragma to the top of the file:

{-# OPTIONS -F -pgmF debug-pp #-}

To instrument an entire program, add the following line to your stack descriptor, or if you don't use stack, to your cabal descriptor:

ghc-options: -F -pgmF debug-pp

In both cases you will also need to modify your Cabal descriptor in order to

  • add a dependency on the debug package
  • (optional) add a build tool depends on debug-pp (required Cabal 2.0) :
Library
  ...
  build-tool-depends: debug-pp:debug-pp

Configuration

debug-pp tries to find a config file in the following locations (from higher to lower precedence):

  1. .debug-pp.yaml in the current directory (useful for per-directory settings)
  2. .debug-pp.yaml in the nearest ancestor directory (useful for per-project settings)
  3. debug-pp/config.yaml in the platform’s configuration directory (on Windows, it is %APPDATA%, elsewhere it defaults to ~/.config and can be overridden by the XDG_CONFIG_HOME environment variable; useful for user-wide settings)
  4. .debug-pp.yaml in your home directory (useful for user-wide settings)
  5. The default settings.

Use debug-pp --defaults > .debug-pp.yaml to dump a well-documented default configuration to a file, this way you can get started quickly.

The configuration options include:

  • Exclude modules by name.
  • Instrument the main function with debugRun.
  • Choice of backend.
  • In the case of the Hoed backend, whether to enable the automatic deriving of Generic and Observable instances.

Debug backends

This package offers two alternative backends for generating the debug trace:

  • import Debug

    This is the default backend, which relies on Show instances to observe values strictly. If your program relies on laziness, it will probably crash or loop.

  • import Debug.Hoed

    A new experimental backend built on top of Hoed. Requires GHC 8.2 or higher

    Fully lazy, able to observe function values and provide call stacks: example. The instrumentation is simpler, so it is known to work in more cases. It relies on Observable instances which are derivable (the TH wrapper can take care of this automatically). Note that it will probably not work in multi threaded environments yet.

Requirements

  • Polymorphic functions must have type signatures, otherwise GHC will fail to infer an unambiguous type when annotated for debugging.
  • Types under observation must have Show (or Observable) instances, otherwise they will fall back to the default <?>.
  • Calling the debugged function inside GHCi records the results for viewing inside the UI.

The function can be called multiple times with different parameters, and the results of each individual run can be selected inside the UI.

Notes

  • You can create multiple debug [d|...] blocks inside a module and you can also put more than one function inside a single block.

A function being debugged can refer to another function also being debugged, but due to a limitation of Template Haskell, the definition of the function being called must occur above the point of its reference in the source module.

Due to constant applicative forms (CAFs) distorting the debug trace, it is not advisable to run the debugger twice in the same GHCi session.

Limitations

This tool is quite new, so it has both limitations, places it is incomplete and bugs. Please report all the issues you find and help us make it better.

Alternatives

For practical alternatives for debugging Haskell programs you may wish to consider:

  • GHCi debugger, simple imperative-style debugger in which you can stop a running computation in order to examine the values of variables. The debugger is integrated into GHCi. Robust, reliable, somewhat difficult to use.
  • Hood and Hoed, a value-based observational debugger with a difficult user interface, deals well with laziness.
  • Hat, good ideas, but I've never got it working.

Compared to the above, debug stresses simplicity of integration and user experience.

FAQ

Q: debugView fails talking about Wine?

A: If you get wine: invalid directory "/home/f/.wine" in WINEPREFIX: not an absolute path when running debugView that means xdg-open is handled by Wine. Fix that and it will work once more.

Q: debugView fails with "error: Variable not in scope: debugView"?

A: Explicitly load the Debug module in GHCi via :m + Debug

debug's People

Contributors

dwijnand avatar ffaf1 avatar marklnichols avatar ndmitchell avatar pepeiborra avatar sholland1 avatar vaibhavsagar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

debug's Issues

Show onward calls

There should be an option to show onward calls, e.g. if map f (x:xs) calls map f xs that should be presented as an onward call, which is possible since debug tracks which f is being passed. Using this onward call information can build a call stack, giving a more traditional style of debugger.

Refactor the default backend into a module

I would like to reorganise the module structure to:

  • move the trace recording (getDebugTrace) from Debug.Record and the TH bits from Debug into a new module Debug.Backend.Default
  • Debug.Record gets renamed to Debug.Types
  • Debug simply reexports Debug.Backend.Default

Apply to nested lambdas/functions

Currently only top-level declarations are trapped. However, it should be anything that looks lambda-like, so anything nested as well. e.g.

foo x y = ...
   where
      bar x y = ...

Should annotate both foo and bar.

Document Hoed better

Specifically there are no docs for:

data Config
class Observable a
observer :: Observable a => a -> Parent -> a
constrain :: Observable a => a -> a -> a
observe :: Observable a => String -> a -> a
data HoedOptions :: *
defaultHoedOptions :: HoedOptions

My checking script complains, which explains the Travis GHC 8.2 error.

Illegal variable name when splicing a TH declaration

I'm trying to use debug to instrument a function. I have followed the instructions, so the top of my source file looks like this:

{-# LANGUAGE TemplateHaskell, ViewPatterns, PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}

module Day2 (
    -- ...
) where

import Util
import Debug

I'm instrumenting the function as follows:

debug [d|
    _seek_from :: (Integer, Integer) -> Integer -> [Integer] -> Maybe (Integer, Integer)
    _seek_from (a, b) t [] = Nothing
    _seek_from (a, b) t mem
      | a > bound && b > bound = Nothing
      | a < 0 || b < 0 = Nothing
      | a > bound && b <= bound = _seek_from (0, b + 1) t mem
      | otherwise =
            let r = test_inputs (a, b) mem in
            if r == t then Just (a, b) else _seek_from (a + 1, b) t mem
      where bound = let x = toInteger . ((-) 1) . length $ mem in if x > 99 then 99 else x
      |]

When I build (stack build) I get the following error:

Illegal variable name: ‘’Illegal variable name: ‘’
    When splicing a TH declaration

It specifically has a problem on the debug [d| line.

Hoed test error

I'm seeing:

Test suite failure for package debug-0.0.2
debug-hoed-test: exited with: ExitFailure 1

Deal properly with name clashes

If you write:

foo x = case x of
   x:xs -> ...

Then it records x twice as different values, overwriting each other. They should be handled uniquely.

Multiple observations of the same variable in a function call

Consider the example

debug [d|
       listcomp y = sum [x | x <- [1..y]]
  |]

The expression listcomp 3
Produces the trace

     * listcomp 3 = 6
     x = 1
     x = 2
     x = 3
     $arg1 = 3
     y = 3
     sum = 6
     $result = 6

This is not unique to list comprehensions, it happens also for a mapped lambda:

debug [d|
       listmap y = sum $ map (\x -> x) [1..y]
  |]

listmap 3 produces the same trace as listcomp 3

The Hoed backend is not affected by this.

Externalize the JSON format

Can we define a schema for the JSON format via Haskell types and derived instances, so that it becomes easier to reuse by external tools and packages ?

Make a release

What do we need to make a release ? The number of new features and improvements since 0.1 is huge.

Doesnt work in IE 11

Not sure about any other versions, but the functions, variables, and code are all missing in IE 11.

Debug not compatible with mdo blocks?

I'm getting the following when trying to instrument an entire file:

/home/george/haskell-tinywl//tmp/ghc23679_0/ghc_1.hspp:708:64: error
:                                                                  
    mdo, monad comprehension and [: :] not (yet) handled by Template
 Haskell                                                           

I use mdo blocks throughout my module. I'm assuming this means Debug isn't compatible with these mdo blocks? If so, will changing all mdo blocks to rec statements make it work?

Add a representation for function arguments

The DebugTrace datatype does not provide an encoding for functions, so the Hoed backend has been representing them as strings, for instance:

$arg1 = { \ 'l'  -> True, \ 'l'  -> True}

Having a representation would allow the frontend to render these more effectively (and choosing when to display them), and would increase sharing, making the trace more compact.

Not showing intermediate values when used with stack

Hi,
first let me thank you for this amazing tool. Regarding the quicksort example,
if I use this inside a stack setup the intermediate values like lt, gt are not shown.
With cabal they will be shown. Is this a matter of configuration or
is it not possible to show the intermediate values inside a stack setup?

Document TemplateHaskell debug block restrictions

This code causes a compile error:

-- (The first line is line 84)
debug [d|
f :: Int -> Int
f n = g n * h n
|]

debug [d|
g :: Int -> Int
g n = n * 3
|]

h :: Int -> Int
h n = n + 2

:\stack_root\haskell-examples\src\ProjectEuler\Problem19.hs:84:1: error:
* Variable not in scope: g :: t -> a
* `g' (splice on lines 89-92) is not in scope before line 89

C:\stack_root\haskell-examples\src\ProjectEuler\Problem19.hs:84:1: error:
* Variable not in scope: h :: t -> a
* `h' (line 95) is not in scope before the splice on lines 89-92

Is this calling functions from block to block legitimate and fixable? Or just something that needs to
be avoided and documented?

Integrate location

The location function in TemplateHaskell can given me the source file, and location of the Debug block. Some kind of smart lexer and matching algorithm could give the original code, precisely, with exact locations and layout as per the original.

Is this alive?

I'm new in Haskell and this package is interesting. I just would like to know is this package alive or maybe this functionality went to the HLS or an other technik is preferred instead of this? I'm asking it because the last commit is 4 years old...

Ovelapping Show instances for monadic values

Got another one for you.

Try this:

debug [d|
    f :: Int -> m Int
    f _ = undefined
    |]

You get an Overlapping instances for Show (m0 Int) error. This can be "fixed" by adding a instance {-# INCOHERENT #-} Show (m Int) somewhere because the arcane rules of instance selection pick this one.

I'm not really sure what you can really do about this.

GHC 8.0 compatibility

Should we provide that? With the latest commits Debug.Hoed generates things using deriving strategies, which is a GHC 8.2 only feature. We should either fix that, or declare 8.0 unsupported.

CC @pepeiborra @marklnichols

Add subexpressions

If you call a function whose results are not otherwise immediately available they should be added as a separate expression. e.g. if you have: foo x y = bar x y + foo y z then it would be useful to have dedicated values for bar x y and foo y z than than just $result.

Function param names missing

For example, with this function

debug [d|
    lcm_gcd :: (Integral a) => a -> a -> Double
    lcm_gcd x y =
        let least = lcm x y
        in fromIntegral least ^^ gcd x y
    |]

We still get $arg1 and $arg2, but x and y are missing.

GHCi error

stack init
stack build
stack ghci

gives:
Loaded GHCi configuration from \debug.ghci

: error:
module `main:DebugPP' is defined in multiple files

Parse error when using the pragma

I put this at the top of small test file:
{-# OPTIONS -F -pgmF debug-pp #-}

I did a build and launched GHCi. The new module wasn't pre-loaded by GHCI, but when I tried to load the source file via -l I got:

parse error on input `import'

Show which branches are taken

When there is an if or pattern match it should highlight the taken path in yellow. When there is a boolean predicate it should highlight it in green/blue according to the result (perhaps, might not be necessary with the yellow color).

Note this isn't intended to be a HPC style "what got evaluated" in terms of lazy evaluation, more just which call path was taken.

JSON serialisation is broken

This means that the frontend is broken right now, of course.

It's my fault, the toEncoding implementation was missing the arguments. I'll send a fix asap.

Limit number of "shadowed" variables

Situations like this are a problem if the list is larger than a few elements:

image

I can put in a fix to limit the number of (') vars added, but we probably also need a way to show that there really were more values but they aren't being shown.

Something like this maybe:

x'''(+) =  ...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.