breakpoint: Set breakpoints using a GHC plugin

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

Warnings:

A plugin that allows you to set breakpoints for debugging purposes.


[Skip to Readme]

Properties

Versions 0.1.0.0, 0.1.1.0, 0.1.1.1, 0.1.2.0, 0.1.2.1, 0.1.2.2, 0.1.3.0, 0.1.3.0, 0.1.3.1
Change log CHANGELOG.md
Dependencies ansi-terminal (>=1.0 && <1.1), base (>=4.16.0.0 && <4.20.0.0), containers (>=0.6.5 && <0.7), deepseq (>=1.0 && <1.6), ghc (>=9.2.0 && <9.9), haskeline (>=0.8.2 && <0.9), mtl (>=2.2.2 && <2.4), pretty-simple (>=4.1.2 && <4.2), template-haskell (>=2.18.0 && <2.22), text (>=1.2.5 && <2.2), transformers (>=0.5.6 && <0.7) [details]
License MIT
Author Aaron Allen
Maintainer aaronallen8455@gmail.com
Category Development
Bug tracker https://github.com/aaronallen8455/breakpoint/issues
Uploaded by aaronallen8455 at 2023-11-25T04:51:29Z

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for breakpoint-0.1.3.0

[back to package description]

Breakpoint

The ability to set breakpoints in a program can provide valuable insights when debugging. While GHCi has built-in support for setting breakpoints, it is not actively maintained and suffers from several critical limitations:

The breakpoint library solves these problems by implementing breakpoints as a GHC plugin.

Usage

Add breakpoint as a dependency to your project then enable breakpoints in a module by adding {-# OPTIONS_GHC -fplugin Debug.Breakpoint #-} to the top of the file and importing the Debug.Breakpoint module. You can then use the breakpoint, breakpointIO, or breakpointM functions as appropriate to set a breakpoint.

breakpoint and breakpointM both use unsafePerformIO which means they are at the mercy of the simplifier and all the other pitfalls of lazy IO. For this reason, it's generally preferable to use breakpointIO in contexts that support it.

Here's an example module:

{-# OPTIONS_GHC -fplugin Debug.Breakpoint #-}

import Debug.Breakpoint

main :: IO ()
main = do
  x <- getLine
  let y = 2 :: Int
      z = id :: Bool -> Bool
  breakpointIO
  pure ()

When the breakpoint expression gets evaluated, you will see terminal output such as

### Breakpoint Hit ###
(app/Main.hs:24:3-6)
x =
  "input"

y =
  2

z =
  <Bool -> Bool>

Press enter to continue

showing the location of the breakpoint and the free variables that are visible from the callsite, this includes function arguments, let bindings, where binds, monadic binds, pattern binds, etc.

If the type of a value has a Show instance then that will be used to generate the printed value, otherwise the output will contain the type of the value within angle brackets.

Execution of the program effectively halts on waiting for user input. In concurrent programs, all threads will be stopped, not just the one executing the breakpoint.

Querying variables

In contrast to the standard breakpoint functions which print out the values for all current variables, the queryVars, queryVarsM, and queryVarsIO functions first print the variables names and then initiate a prompt where you can enter a specific variable name to have its value printed.

This is useful if you are only interested in certain values or if printing one or more values would result in a non-terminating process (an infinite data structure for example).

You can tab-complete variable names at the query prompt. Only the current thread is blocked while the prompt is active. To resume execution, press enter with a blank line.

Caveats