A Lightweight Interactive Debugger for Haskell Simon Marlow José Iborra Bernard Pope Andy Gill.

Slides:



Advertisements
Similar presentations
Module R2 CS450. Next Week R1 is due next Friday ▫Bring manuals in a binder - make sure to have a cover page with group number, module, and date. You.
Advertisements

Mehmet Can Vuran, Instructor University of Nebraska-Lincoln Acknowledgement: Overheads adapted from those provided by the authors of the textbook.
Debugging What can debuggers do? Run programs Make the program stops on specified places or on specified conditions Give information about current variables’
Programming Types of Testing.
Names and Scopes CS 351. Program Binding We should be familiar with this notion. A variable is bound to a method or current block e.g in C++: namespace.
Run time vs. Compile time
CS 201 Functions Debzani Deb.
CPS110: Implementing threads/locks on a uni-processor Landon Cox.
Homework Reading –Finish K&R Chapter 1 (if not done yet) –Start K&R Chapter 2 for next time. Programming Assignments –DON’T USE and string library functions,
Exceptions. Many problems in code are handled when the code is compiled, but not all Some are impossible to catch before the program is run  Must run.
P51UST: Unix and Software Tools Unix and Software Tools (P51UST) Compilers, Interpreters and Debuggers Ruibin Bai (Room AB326) Division of Computer Science.
Homework Reading Programming Assignments
DEBUGGING CHAPTER Topics  Getting Started with Debugging  Types of Bugs –Compile-Time Bugs –Bugs Attaching Scripts –Runtime Errors  Stepping.
Copyright © 2009 Techtronics'09 by GCECT 1 Presents, De Code C De Code C is a C Programming competition, which challenges the participants to solve problems.
BİL528 – Bilgisayar Programlama II Making Decisions, Loops, Debugging, Designing Objects Using Classes 1.
1 Names, Scopes and Bindings Aaron Bloomfield CS 415 Fall
Chapter 13 Recursion. Learning Objectives Recursive void Functions – Tracing recursive calls – Infinite recursion, overflows Recursive Functions that.
Debugging Dwight Deugo Nesa Matic
Debugging. 2 © 2003, Espirity Inc. Module Road Map 1.Eclipse Debugging  Debug Perspective  Debug Session  Breakpoint  Debug Views  Breakpoint Types.
Debugging in Java. Common Bugs Compilation or syntactical errors are the first that you will encounter and the easiest to debug They are usually the result.
Testing and Debugging Version 1.0. All kinds of things can go wrong when you are developing a program. The compiler discovers syntax errors in your code.
Testing. 2 Overview Testing and debugging are important activities in software development. Techniques and tools are introduced. Material borrowed here.
Programming for Beginners Martin Nelson Elizabeth FitzGerald Lecture 15: More-Advanced Concepts.
Interpretation Environments and Evaluation. CS 354 Spring Translation Stages Lexical analysis (scanning) Parsing –Recognizing –Building parse tree.
Testing and Debugging Session 9 LBSC 790 / INFM 718B Building the Human-Computer Interface.
1 Κατανεμημένες Διαδικτυακές Εφαρμογές Πολυμέσων Γιάννης Πετράκης.
Copyright © 2010 Certification Partners, LLC -- All Rights Reserved Perl Specialist.
Eagle: Maturation and Evolution 17th Annual Tcl Conference Joe Mistachkin.
Debugging and Profiling With some help from Software Carpentry resources.
CLIP Command Line InterPreter for a subset of C++.
Allegro CL Certification Program Lisp Programming Series Level I Session Basic Lisp Development in the IDE.
COMP3190: Principle of Programming Languages
The Software Development Process
CSE 351 GDB Introduction. Lab 1 Status? How is Lab 1 going? I’ll be available at the end of class to answer questions There are office hours later today.
Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University IWPSE 2003 Program.
Debuggers in Python. The Debugger Every programming IDE has a tool called a debugger. This application does NOT locate or fix your bugs for you! It slows.
CSE 425: Control Abstraction I Functions vs. Procedures It is useful to differentiate functions vs. procedures –Procedures have side effects but usually.
1 Debugging and Syntax Errors in C++. 2 Debugging – a process of finding and fixing bugs (errors or mistakes) in a computer program.
Precomputation- based Prefetching By James Schatz and Bashar Gharaibeh.
Copyright © 2003 ProsoftTraining. All rights reserved. Perl Fundamentals.
CSE 374 Programming Concepts & Tools Hal Perkins Fall 2015 Lecture 11 – gdb and Debugging.
Concurrency Properties. Correctness In sequential programs, rerunning a program with the same input will always give the same result, so it makes sense.
Eagle: Tcl Integration with the CLR 16th Annual Tcl Conference Joe Mistachkin.
First Order Haskell Neil Mitchell York University λ.
How to execute Program structure Variables name, keywords, binding, scope, lifetime Data types – type system – primitives, strings, arrays, hashes – pointers/references.
Chapter 5 Linked List by Before you learn Linked List 3 rd level of Data Structures Intermediate Level of Understanding for C++ Please.
Haskell Basics CSCE 314 Spring CSCE 314 – Programming Studio Using GHC and GHCi Log in to unix.cse.tamu.edu (or some other server) From a shell.
Recitation 2: Abhijit Warkhedi2/25/20161 Today’s Agenda u CVS u GDB.
1 Advanced.Net Debugging Using Visual Studio, R# and OzCode IT Week, Summer 2015.
1 CSC 533: Programming Languages Spring 2014 Subprogram implementation  subprograms (procedures/functions/subroutines)  subprogram linkage  parameter.
HP-SEE Debugging with GDB Vladimir Slavnic Research Assistant SCL, Institute of Physics Belgrade The HP-SEE initiative.
 Wind River Systems, Inc Chapter - 4 CrossWind.
Debugging using By: Samuel Ashby. What is debugging?  A bug is an error in either a program or the hardware itself.  Debugging is first locating and.
Quiz 1 A sample quiz 1 is linked to the grading page on the course web site. Everything up to and including this Friday’s lecture except that conditionals.
Eagle: Tcl Integration with the CLR
14 Compilers, Interpreters and Debuggers
CS1101X Programming Methodology
Debugging Dwight Deugo
Testing and Debugging.
Debugging Haskell by Observing Intermediate Data Structures
Important terms Black-box testing White-box testing Regression testing
Important terms Black-box testing White-box testing Regression testing
CSC 533: Programming Languages Spring 2015
CSc 352 Debugging Tools Saumya Debray Dept. of Computer Science
Homework Reading Programming Assignments Finish K&R Chapter 1
Eagle: Maturation and Evolution
Debugging Dwight Deugo
CSC 533: Programming Languages Spring 2018
CSC 533: Programming Languages Spring 2019
Dynamic Binary Translators and Instrumenters
Presentation transcript:

A Lightweight Interactive Debugger for Haskell Simon Marlow José Iborra Bernard Pope Andy Gill

We don’t need no debugger! We have A type system! QuickCheck and HUnit! GHCi and Hugs! NO BUGS!

but… A debugger was still the #1 requested feature in the GHC survey (2005) New users often want a way to visualise execution to understand what’s happening When you missed out a test, diagnosing the failure is hard: deep bugs head [] 

But we have debuggers! There exist debugging tools for Haskell, but for various reasons they are often too hard to use: – limited language support – require recompiling libraries – time/space overhead – etc. (see paper for comparison)

Goals We want a debugger that: – Is always available – works with everything that GHC can compile – doesn’t add significant overheads We might sacrifice some functionality to achieve accessibility. Aim for a high power-to-weight ratio. This won’t necessarily supercede other Haskell debuggers: the goal is to provide some debugging functionality that everyone can use easily. Secondary goals: – can be re-used in IDE frontends

Real programmers want to set breakpoints Our debugger is “online”: you debug the running program, as opposed to “post-mortem” debugging (eg. Hat). The basic model is “execute, stop, inspect, continue”. Advantages: – we can let you evaluate arbitrary expressions involving runtime values (e.g. look up a key in a finite map). – no limit on the runtime of the program Disadvantages: – we can’t easily go back in time – evaluation order is exposed (possibly an advantage?) Our debugger is integrated into GHCi

Walkthrough GHCi, version : :? for help Loading package base... linking... done. Prelude> :load foldl [1 of 1] Compiling Test ( foldl.hs, interpreted ) Ok, modules loaded: Test. *Test> :list foldl foldl f c xs = go c xs 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 *Test> :break foldl Breakpoint 1 activated at foldl.hs:(11,0)-(13,34) *Test> :show breaks [0] Test foldl.hs:(11,0)-(13,34) *Test> foldl (+) 0 [1..5] Stopped at foldl.hs:(11,0)-(13,34) _result :: t1 = _ [foldl.hs:(11,0)-(13,34)] *Test> [foldl.hs:(11,0)-(13,34)] *Test> :list foldl f c xs = go c xs 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 breakpoints can be set in any interpreted module, on any function definition or expression.

Walkthrough [foldl.hs:(11,0)-(13,34)] *Test> :set stop :list [foldl.hs:(11,0)-(13,34)] *Test> :step Stopped at foldl.hs:11:15-21 _result :: a = _ c :: a = _ go :: a -> [b] -> a = _ xs :: [b] = _ foldl f c xs = go c xs 12 where go c [] = c [foldl.hs:11:15-21] *Test> length xs 5 we can take a single step, which executes until the next breakpoint location - next expression or function call. this is the expression to be evaluated next new bindings for variables in scope at the breakpoint. Underscore means “unevaluated” we can evaluate expressions involving the local variables

Walkthrough [foldl.hs:11:15-21] *Test> :type c c :: a [foldl.hs:11:15-21] *Test> c :1:0: Ambiguous type variable `a' in the constraint: `Show a' arising from a use of `print' at :1:0 Cannot resolve unknown runtime types: a Use :print or :force to determine these types some variables have polymorphic types. What does that mean? At runtime, c is bound to a value with a monomorphic type. The compiler doesn’t know the runtime type of c, so it doesn’t know which Show instance to use. ‘a’ is a runtime type variable Defaulting does not apply to these type variables. Note that ‘length xs’ worked, however.

Walkthrough [foldl.hs:11:15-21] *Test> :print c c = (_t1::a) [foldl.hs:11:15-21] *Test> :force c c = 0 [foldl.hs:11:15-21] *Test> :type c c :: Integer [foldl.hs:11:15-21] *Test> :show bindings _result :: Integer _t1 :: Integer c :: Integer go :: Integer -> [Integer] -> Integer it :: Int xs :: [Integer] :print displays a value without knowing its type, and without forcing evaluation. c is completely unevaluated, so we don't see any new information. :force is like :print, but forces evaluation. the type of ‘c’ is now known! and the type information has been propagated to the other bindings (type variable ‘a’ is now bound to ‘Integer’) ‘b’ was resolved to Integer earlier by ‘length xs’. GHCi automatically propagates type information as it becomes available.

Walkthrough [foldl.hs:11:15-21] *Test> :step Stopped at foldl.hs:(12,8)-(13,34) _result :: a = _ f :: a -> b -> a = _ 11 foldl f c xs = go c xs 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 [foldl.hs:(12,8)-(13,34)] *Test> :step Stopped at foldl.hs:13:22-34 _result :: Integer = _ c :: Integer = 0 f :: Integer -> Integer -> Integer = _ x :: Integer = 1 xs :: [Integer] = [2,3,4,5] 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 most of the types are concrete, because GHCi has inspected the values of the free variables to determine their types.

Walkthrough [foldl.hs:13:22-34] *Test> :step Stopped at foldl.hs:(12,8)-(13,34) _result :: a = _ f :: a -> b -> a = _ 11 foldl f c xs = go c xs 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 [foldl.hs:(12,8)-(13,34)] *Test> :step Stopped at foldl.hs:13:22-34 _result :: a = _ c :: a = _ f :: a -> Integer -> a = _ x :: Integer = 2 xs :: [Integer] = [3,4,5] 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 [foldl.hs:13:22-34] *Test> :step Stopped at foldl.hs:(12,8)-(13,34) _result :: a = _ f :: a -> b -> a = _ 11 foldl f c xs = go c xs 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 [foldl.hs:(12,8)-(13,34)] *Test> :step Stopped at foldl.hs:13:22-34 _result :: a = _ c :: a = _ f :: a -> Integer -> a = _ x :: Integer = 3 xs :: [Integer] = [4,5] 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 note that ‘c’ is unevaluated now. foldl is building up a chain of thunks.

Walkthrough [foldl.hs:13:22-34] *Test> :step c :1:0: Ambiguous type variable `a' in the constraint: `Show a' arising from a use of `print' at :1:0 Cannot resolve unknown runtime types: a Use :print or :force to determine these types [foldl.hs:13:22-34] *Test> :step c `seq` () Stopped at foldl.hs:13:26-30 _result :: a = _ c :: a = _ f :: a -> Integer -> a = _ x :: Integer = 2 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs [foldl.hs:13:26-30] *Test>... [foldl.hs:13:26-30] *Test> :show context --> foldl (+) 0 [1..5] Stopped at foldl.hs:13: > c `seq` () Stopped at foldl.hs:13:26-30 We can single-step the evaluation of ‘c’ separately, :step takes an expression to evaluate as an argument oops, not like that! but this works “…” in the prompt indicates that we are now in a nested debugging session. lists the current debugging sessions

Walkthrough... [foldl.hs:13:26-30] *Test> :step Stopped at foldl.hs:13:26-30 _result :: Integer = _ c :: Integer = 0 f :: Integer -> Integer -> Integer = _ x :: Integer = 1 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs [foldl.hs:13:26-30] *Test> :step () [foldl.hs:13:22-34] *Test> :continue 15 *Test> continue the evaluation of c result of c `seq` () is () continue the original evaluation, result is 15

Walkthrough *Test> :delete * *Test> foldl (+) 0 [ ] *** Exception: stack overflow let’s try something else… first delete all breakpoints foldl has a commonly- encountered problem with stack overflow (I’m using a reduced stack limit here).

Walkthrough *Test> :set -fbreak-on-error *Test> :trace foldl (+) 0 [ ] Stopped at _exception :: e = stack overflow [ ] *Test> :history -1 : foldl (foldl.hs:13:26-30) -2 : foldl (foldl.hs:13:26-30) -3 : foldl (foldl.hs:13:26-30) -4 : foldl (foldl.hs:13:26-30) -5 : foldl (foldl.hs:13:26-30) -6 : foldl (foldl.hs:13:26-30) -7 : foldl (foldl.hs:13:26-30) -8 : foldl (foldl.hs:13:26-30) -9 : foldl (foldl.hs:13:26-30) -10 : foldl (foldl.hs:13:26-30) -11 : foldl (foldl.hs:13:26-30) -12 : foldl (foldl.hs:13:26-30) -13 : foldl (foldl.hs:13:26-30) -14 : foldl (foldl.hs:13:26-30) -15 : foldl (foldl.hs:13:26-30) -16 : foldl (foldl.hs:13:26-30) -17 : foldl (foldl.hs:13:26-30) -18 : foldl (foldl.hs:13:26-30) -19 : foldl (foldl.hs:13:26-30) -20 : foldl (foldl.hs:13:26-30)... Uncaught exceptions now trigger a breakpoint :trace evaluates as normal, but remembers the 50 most recent steps :history shows the history of evaluation steps when stopped

Walkthrough [ ] *Test> :back Logged breakpoint at foldl.hs:13:26-30 _result :: a c :: a f :: a -> Integer -> a x :: Integer 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 [-1: foldl.hs:13:26-30] *Test> :back Logged breakpoint at foldl.hs:13:26-30 _result :: a c :: a f :: a -> Integer -> a x :: Integer 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 [-2: foldl.hs:13:26-30] *Test> :back Logged breakpoint at foldl.hs:13:26-30 _result :: a c :: a f :: a -> Integer -> a x :: Integer 12 where go c [] = c 13 go c (x:xs) = go (f c x) xs 14 [-3: foldl.hs:13:26-30] *Test> every step in the history is evaluating ‘f c x’

:trace/:history The :trace/:history functionality is a poor substitute for a proper lexical call stack. Maintaining a proper lexical call stack is hard, but :trace/:history were easy to implement. Still, it is useful for finding the source of ‘head []’, pattern-match failure, stack overflow, and infinite loops. For infinite loops, hit Control-C which generates an exception, then use :history.

Implementation A problem with implementing a debugger is during execution answering the question “where in the source code am I right now?”. Approach 1: annotate each expression in the object code with the source expression it orignated from. – Problem: with extensive optimisation and transformation, it is hard to maintain this information accurately, and it requires careful attention to all the transformations. – cf. debugging optimised C with gdb.

Annotating compiled code Approach 2: use a systematic source-to- source transformation to yield a program with the same semantics, but with additional side-effects, e.g. reporting the current source location – Used by several existing Haskell debuggers, also the HPC coverage tool. – Two challenges: the annotations are side-effects, we don’t want those side-effects to be lost or moved by transformations in the compiler do this without impacting performance too much – we want the debugger to be “always on”

Ticks Has the same semantics as E tick is a side-effect: – in coverage it records that this expression was evaluated – in the debugger it checks whether this breakpoint is enabled GHC’s middle-stage optimisations are already side-effect-aware; they won’t – eliminate the side-effect, or – perform it speculatively but otherwise transformations are unaffected. case tick of DEFAULT -> E

Compared to coverage analysis… The debugger puts ticks in different places – not on variables (too many ticks) – in let-expressions, coverage puts it here: – the debugger puts it here: – so that x is in scope at the breakpoint case tick of DEFAULT -> let x = E in E’ let x = E in case tick of DEFAULT -> E’

Compiling ticks Ideally we want ticks to imply no extra allocation. e.g. in f gives rise to a single byte-code-object (BCO), the first instruction of which implements the tick. Sadly, not all ticks are quite so easy to implement. e.g. Every tick must be at a safe-point, i.e. at the beginning of a BCO, because execution may be suspended (just like a GC). We need to manufacture an extra safe point: let f = \x -> case tick of DEFAULT -> E in … let x = E in case tick of DEFAULT -> E’ let x = E in let z = case tick of DEFAULT -> E’ in z

Performance Interpreted code already runs 15-20x slower than optimised compiled code (but compiles 2-3x quicker) Performance of interpreted code is affected: – at compile-time, we need to insert ticks (+15%) – at runtime, check breakpoint status at each breakpoint, and extra safe-points (+45%) But safe points do unnecessary updates, we expect to be able to reduce this – debugger cannot be disabled, but individual modules can be compiled rather than interpreted.

The GHC API The debugger is implemented mostly in Haskell; breakpoints are implemented using threads. Debugging functionality is exposed via the GHC API. GHCi is just a textual front-end built on top of this API, IDEs could also talk directly to the GHC API to provide interactive debugging.

Future Work High priority: a real lexical call stack Get feedback from GHC users – is exposing the evaluation order confusing or helpful? Performance can be improved – breakpoints in compiled code should be possible API interface means it can be built into an IDE Concurrency debugging

Conclusion We have an instant always-on debugger that works with everything that GHCi can compile Functionality is limited compared to other debuggers – no backtrace, no algorithmic debugging etc. Online debugging has some benefits over offline debugging: – evaluate arbitrary expressions involving runtime values – no need to store the entire trace of the program – no need to switch tools if you’re already using GHCi