New abstractions for concurrency make writing programs easier by moving away from threads and locks, but debugging such programs becomes harder. The call-stack, an essential tool in understanding why and how control flow reached a certain point in the program, loses meaning when inspected in traditional debuggers. Futures, actors or iteratees make code easier to write and reason about, and in this talk I'll show a simple solution to make them easier to debug. The tool I present integrates well with the Eclipse plugin for Scala, and shows how a "reactive debugger" might look like.
3. Reactive applications
• Different parts of the application run in different logical
threads
• ..the programmer must ensure correct coordination and
communication
4.
5.
6. Concurrency
• Threads and Locks are replaced by
• Futures
• Actors
• parallel collections (fork-join frameworks)
8. Futures
Execution
continues without
waiting for all tweets
val fTweets = Future {
getAllTweets(user)
}
!
// also a future
val nrOfTweets = fTweets.map(ts => ts.size)
!
nrOfTweets onSuccess { println }
Futures can be
chained
Callback for
successful completion
10. Detective work
• go from undesirable effects
back to causes
• attempt a fix
• repeat
11. The Call Stack
java.lang.Exception
at test.FuturesTest$$anonfun$simpleUse$1$$anonfun$2$$anonfun$apply$1.apply$mcI$sp(Futur..
at test.FuturesTest$$anonfun$simpleUse$1$$anonfun$2$$anonfun$apply$1.apply(FuturesTest.sca..
at test.FuturesTest$$anonfun$simpleUse$1$$anonfun$2$$anonfun$apply$1.apply(FuturesTest.sca..
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at scala.concurrent.impl.ExecutionContextImpl$$anon$3.exec(ExecutionContextImpl.scala:107)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Futures use a thread pool for execution!
12. The Problem
• The call-stack used to record the flow of control of
sequential programs
• futures (and actors) execute each computation step on a
different thread
• on a mostly uninteresting call stack
13. Can we recover the async stack?
Record call stack when a future is created
14. Under the hood
• Intercept Future instantiation
• Save stack contents (all of it)
• Resume
• When a breakpoint is hit
• check async stacks store
• (overhead is comparable to
conditional breakpoint)
17. Debugging Actors
• Can we find out where a message was sent?
• what was the state at that point?
• IT’S THE SAME THING!
18. Not just futures
• message sends (Akka, Scala actors)
• Play iteratees
• any composable asynchronous structure
• user-defined/data-flow debugging
19. Debugging Actors
• Message-send is like an asynchronous method call
• “I’d really like to step-into that message send”
20. Step-with-a-bang
• resume execution until the next message sent is
received by an actor
• the receiving actor might run on a different thread
• other messages might be exchanged in the meantime
26. Lithium release (4.0)
quiescence type-checking
quick fixes (implement
abstract member)
multiple Scala version support
name hashing
stable API for plugins
30. Multiple Scala Version support
• Starting with Scala 2.11.0 and IDE 4.0-M2
• Presentation compiler works in (2.10) compatibility mode
• The build compiler is 2.10
• Can have multiple Scala versions in the same workspace