diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
index a5d042faf374d097999e16727ca2c7b1cb009846..378c3219ae107baa59c2e42256b5a50579516fdb 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
@@ -54,6 +54,7 @@ import com.oracle.truffle.r.nodes.builtin.helpers.DebugHandling;
 import com.oracle.truffle.r.nodes.builtin.helpers.TraceHandling;
 import com.oracle.truffle.r.nodes.control.AbstractLoopNode;
 import com.oracle.truffle.r.nodes.control.BlockNode;
+import com.oracle.truffle.r.nodes.control.ForNode;
 import com.oracle.truffle.r.nodes.control.IfNode;
 import com.oracle.truffle.r.nodes.control.ReplacementDispatchNode;
 import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
@@ -532,7 +533,9 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
                     return true;
                 } else {
                     // single statement block, variable parent
-                    return parent instanceof FunctionDefinitionNode || parent instanceof IfNode || parent instanceof AbstractLoopNode;
+                    // note: RepeatingNode is not a RSyntaxElement but the body of a loop is
+                    // under the repeating node !
+                    return parent instanceof FunctionDefinitionNode || parent instanceof IfNode || parent instanceof AbstractLoopNode || ForNode.isLoopBody(node);
                 }
             }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
index 80f96d66aa3d6895cfc9aee862ccb5d78126473b..75684febae6ee8df37d7d1fd758169b6a918569f 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java
@@ -45,6 +45,7 @@ import com.oracle.truffle.r.nodes.control.AbstractLoopNode;
 import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode;
 import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation;
 import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags;
+import com.oracle.truffle.r.runtime.JumpToTopLevelException;
 import com.oracle.truffle.r.runtime.RArguments;
 import com.oracle.truffle.r.runtime.RDeparse;
 import com.oracle.truffle.r.runtime.RError;
@@ -186,17 +187,18 @@ public class DebugHandling {
 
             @Override
             protected Void visit(RSyntaxFunction element) {
+                accept(element.getSyntaxBody());
                 return null;
             }
         }.accept(fdn);
         return fser;
     }
 
-    private static void ensureSingleStep(FunctionDefinitionNode fdn) {
+    private static FunctionStatementsEventListener ensureSingleStep(FunctionDefinitionNode fdn) {
         FunctionStatementsEventListener fser = getFunctionStatementsEventListener(fdn);
         if (fser == null) {
             // attach a "once" listener
-            fser = attachDebugHandler(fdn, null, null, true, false);
+            fser = attachDebugHandler(fdn, null, null, true, true);
         } else {
             if (fser.disabled()) {
                 fser.enable();
@@ -204,6 +206,7 @@ public class DebugHandling {
                 fser.enabledForStepInto = true;
             }
         }
+        return fser;
     }
 
     private abstract static class DebugEventListener implements ExecutionEventListener {
@@ -293,8 +296,9 @@ public class DebugHandling {
 
         @TruffleBoundary
         private void attachStepInto() {
-            stepIntoInstrument = RInstrumentation.getInstrumenter().attachListener(SourceSectionFilter.newBuilder().tagIs(StandardTags.RootTag.class).build(),
-                            new StepIntoInstrumentListener(getFunctionStatementsEventListener(functionDefinitionNode)));
+            FunctionStatementsEventListener parentListener = getFunctionStatementsEventListener(functionDefinitionNode);
+            parentListener.stepIntoInstrument = RInstrumentation.getInstrumenter().attachListener(SourceSectionFilter.newBuilder().tagIs(StandardTags.RootTag.class).build(),
+                            new StepIntoInstrumentListener(parentListener));
 
         }
 
@@ -426,7 +430,7 @@ public class DebugHandling {
         public void onReturnValue(EventContext context, VirtualFrame frame, Object result) {
             if (!disabled()) {
                 CompilerDirectives.transferToInterpreter();
-                returnCleanup(frame);
+                returnCleanup(frame, false);
             }
         }
 
@@ -434,12 +438,12 @@ public class DebugHandling {
         public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) {
             if (!disabled()) {
                 CompilerDirectives.transferToInterpreter();
-                returnCleanup(frame);
+                returnCleanup(frame, exception instanceof JumpToTopLevelException);
             }
         }
 
-        private void returnCleanup(VirtualFrame frame) {
-            if (!implicit) {
+        private void returnCleanup(VirtualFrame frame, boolean jumpToTopLevel) {
+            if (!implicit && !once && !jumpToTopLevel) {
                 print("exiting from: ", false);
                 printCall(frame);
             }
@@ -502,7 +506,7 @@ public class DebugHandling {
                     return;
                 }
                 printNode(node, false);
-                browserInteract(context.getInstrumentedNode(), frame);
+                browserInteract(node, frame);
             }
         }
 
@@ -511,6 +515,9 @@ public class DebugHandling {
         }
     }
 
+    /**
+     * Handles the loop header and there is one instance registered for each loop.
+     */
     private static class LoopStatementEventListener extends StatementEventListener {
 
         private boolean finishing;
@@ -529,7 +536,7 @@ public class DebugHandling {
 
         @Override
         public void onEnter(EventContext context, VirtualFrame frame) {
-            if (!disabled()) {
+            if (!disabled() && context.getInstrumentedNode() == loopNode) {
                 super.onEnter(context, frame);
             }
         }
@@ -544,7 +551,7 @@ public class DebugHandling {
 
         @Override
         public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) {
-            if (!disabled()) {
+            if (!disabled() && context.getInstrumentedNode() == loopNode) {
                 CompilerDirectives.transferToInterpreter();
                 returnCleanup();
             }
@@ -552,7 +559,7 @@ public class DebugHandling {
 
         @Override
         public void onReturnValue(EventContext context, VirtualFrame frame, Object result) {
-            if (!disabled()) {
+            if (!disabled() && context.getInstrumentedNode() == loopNode) {
                 CompilerDirectives.transferToInterpreter();
                 returnCleanup();
             }
@@ -592,9 +599,10 @@ public class DebugHandling {
             if (!RContext.getInstance().stateInstrumentation.debugGloballyDisabled()) {
                 CompilerDirectives.transferToInterpreter();
                 FunctionDefinitionNode fdn = (FunctionDefinitionNode) context.getInstrumentedNode().getRootNode();
-                ensureSingleStep(fdn);
+                FunctionStatementsEventListener ensureSingleStep = ensureSingleStep(fdn);
+
                 functionStatementsEventListener.clearStepInstrument();
-                functionStatementsEventListener.onEnter(context, frame);
+                ensureSingleStep.onEnter(context, frame);
             }
         }
 
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
index d50558d6209356a2ae00089374cf22c521aea35f..27575a9f1759ab5d3f3cec301318bc2c55573205 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java
@@ -24,6 +24,7 @@ package com.oracle.truffle.r.nodes.control;
 
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrumentation.InstrumentableFactory.WrapperNode;
 import com.oracle.truffle.api.nodes.LoopNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RepeatingNode;
@@ -159,4 +160,16 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn
     public ArgumentsSignature getSyntaxSignature() {
         return ArgumentsSignature.empty(3);
     }
+
+    /**
+     * Tests if the provided node is a loop-body node (also considering wrappers).
+     */
+    public static boolean isLoopBody(Node n) {
+        Node parent = n.getParent();
+        if (parent instanceof WrapperNode) {
+            Node grandparent = parent.getParent();
+            return grandparent instanceof ForRepeatingNode && ((ForRepeatingNode) grandparent).body == parent;
+        }
+        return parent instanceof ForRepeatingNode && ((ForRepeatingNode) parent).body == n;
+    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java
index 41ea6dacdaef4fa6b2282d0bc842ecc33a2f4a50..e1ef2d2fc9afa9196652aa5e6cec3fb6600fdc12 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java
@@ -172,6 +172,13 @@ public class RSource {
         return Source.newBuilder(file).name(file.getName()).mimeType(RRuntime.R_APP_MIME).build();
     }
 
+    /**
+     * Create a source from the file system path denoted by {@code file}.
+     */
+    public static Source fromTempFile(File file) throws IOException {
+        return Source.newBuilder(file).name(file.getName()).mimeType(RRuntime.R_APP_MIME).internal().build();
+    }
+
     /**
      * Create an (external) source from {@code url}.
      */
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
index 3ab87815debd8fab265f490b00b710d2c0efc683..9d4f0c63c3bc00a0e5221bc8df84c6586300ebd2 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test
@@ -143303,6 +143303,63 @@ x + x ~ y + log(y)
 #update.formula(x ~ y, ~ . + x2)
 x ~ y + x2
 
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testConditionalBreakpoint#
+#fun <- function(x) { cat('x='); cat(x); cat('\n') }; trace(fun, quote(if (x > 10) browser())); fun(10)<<<NEWLINE>>>; fun(11)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>
+[1] "fun"
+Tracing fun(10) on entry
+x=10
+Error: unexpected ';' in ";"
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testContinue#
+#fun0 <- function() { print('fun0') }; fun1 <- function() { print('enter fun1'); fun0(); print('exit fun1') }; fun2 <- function() { print('enter fun2'); fun1(); print('exit fun2') }; debug(fun2); fun2()<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>s<<<NEWLINE>>>n<<<NEWLINE>>><<<NEWLINE>>>s<<<NEWLINE>>>c<<<NEWLINE>>>c<<<NEWLINE>>>c<<<NEWLINE>>>
+debugging in: fun2()
+debug at #1: {
+    print("enter fun2")
+    fun1()
+    print("exit fun2")
+}
+debug at #1: print("enter fun2")
+[1] "enter fun2"
+debug at #1: fun1()
+debugging in: fun1()
+debug at #1: {
+    print("enter fun1")
+    fun0()
+    print("exit fun1")
+}
+debug at #1: print("enter fun1")
+[1] "enter fun1"
+debug at #1: fun0()
+debugging in: fun0()
+debug at #1: {
+    print("fun0")
+}
+[1] "fun0"
+debug at #1: print("exit fun1")
+[1] "exit fun1"
+debug at #1: print("exit fun2")
+[1] "exit fun2"
+exiting from: fun2()
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testDebugOnce#
+#fun0 <- function() { print('fun0') }; debugonce(fun0); fun0()<<<NEWLINE>>>c<<<NEWLINE>>>
+debugging in: fun0()
+debug at #1: {
+    print("fun0")
+}
+[1] "fun0"
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testDebugOnce#
+#fun0 <- function() { print('fun0') }; fun1 <- function() { print('en'); fun0(); fun0(); print('ex') }; debugonce(fun0); fun1()<<<NEWLINE>>>c<<<NEWLINE>>>
+[1] "en"
+debugging in: fun0()
+debug at #1: {
+    print("fun0")
+}
+[1] "fun0"
+[1] "fun0"
+[1] "ex"
+
 ##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testInvalidName#
 #f <- function(x) {<<<NEWLINE>>>  `123t` <- x + 1<<<NEWLINE>>>  print(`123t`)<<<NEWLINE>>>  `123t`}<<<NEWLINE>>>debug(f)<<<NEWLINE>>>f(5)<<<NEWLINE>>>x<<<NEWLINE>>>n<<<NEWLINE>>>n<<<NEWLINE>>>`123t`<<<NEWLINE>>>n<<<NEWLINE>>>n
 debugging in: f(5)
@@ -143320,6 +143377,120 @@ debug at #4: `123t`
 exiting from: f(5)
 [1] 6
 
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testLoop#
+#fun <- function(x) { for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>
+debugging in: fun(3)
+debug at #1: {
+    for (i in seq(x)) cat(i)
+    cat(5)
+}
+debug at #1: for (i in seq(x)) cat(i)
+debug at #1: cat(i)
+1debug at #1: cat(i)
+2debug at #1: cat(i)
+3debug at #1: cat(5)
+5exiting from: fun(3)
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testLoop#
+#fun <- function(x) { for(i in seq(x)) { cat(i) }; cat(5) }; debug(fun); fun(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>
+debugging in: fun(3)
+debug at #1: {
+    for (i in seq(x)) {
+        cat(i)
+    }
+    cat(5)
+}
+debug at #1: for (i in seq(x)) {
+    cat(i)
+}
+debug at #1: cat(i)
+1debug at #1: cat(i)
+2debug at #1: cat(i)
+3debug at #1: cat(5)
+5exiting from: fun(3)
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testLoop#
+#fun <- function(x) { for(i in seq(x)) { cat(i) }; cat(5) }; debug(fun); fun(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>f<<<NEWLINE>>><<<NEWLINE>>>
+debugging in: fun(3)
+debug at #1: {
+    for (i in seq(x)) {
+        cat(i)
+    }
+    cat(5)
+}
+debug at #1: for (i in seq(x)) {
+    cat(i)
+}
+debug at #1: cat(i)
+1debug at #1: cat(i)
+23debug at #1: cat(5)
+5exiting from: fun(3)
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testLoop#
+#fun <- function(x) { for(j in seq(2)) for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>
+debugging in: fun(3)
+debug at #1: {
+    for (j in seq(2)) for (i in seq(x)) cat(i)
+    cat(5)
+}
+debug at #1: for (j in seq(2)) for (i in seq(x)) cat(i)
+debug at #1: for (i in seq(x)) cat(i)
+debug at #1: cat(i)
+1debug at #1: cat(i)
+2debug at #1: cat(i)
+3debug at #1: for (i in seq(x)) cat(i)
+debug at #1: cat(i)
+1debug at #1: cat(i)
+2debug at #1: cat(i)
+3debug at #1: cat(5)
+5exiting from: fun(3)
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testLoop#
+#fun <- function(x) { for(j in seq(2)) for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>f<<<NEWLINE>>>f<<<NEWLINE>>><<<NEWLINE>>>
+debugging in: fun(3)
+debug at #1: {
+    for (j in seq(2)) for (i in seq(x)) cat(i)
+    cat(5)
+}
+debug at #1: for (j in seq(2)) for (i in seq(x)) cat(i)
+debug at #1: for (i in seq(x)) cat(i)
+debug at #1: cat(i)
+123debug at #1: for (i in seq(x)) cat(i)
+123debug at #1: cat(5)
+5exiting from: fun(3)
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testNestedDebugging#Output.IgnoreDebugPath#
+#foo <- function(rCode) { eval(parse(text=rCode)); print('foo done') }; debug(foo); foo("bar <- function() { print('bar') }; debug(bar); bar()")<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>
+debugging in: foo("bar <- function() { print('bar') }; debug(bar); bar()")
+debug at #1: {
+    eval(parse(text = rCode))
+    print("foo done")
+}
+debug at #1: eval(parse(text = rCode))
+debugging in: bar()
+debug at <text>#1: {
+    print("bar")
+}
+debug at <text>#1: print("bar")
+[1] "bar"
+exiting from: bar()
+debug at #1: print("foo done")
+[1] "foo done"
+exiting from: foo("bar <- function() { print('bar') }; debug(bar); bar()")
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testNestedDebugging#Output.IgnoreDebugPath#
+#foo <- function(rCode) { eval(parse(text=rCode)); print('foo done') }; debug(foo); foo("bar <- function() { print('bar') }; debug(bar); bar()")<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>Q<<<NEWLINE>>>
+debugging in: foo("bar <- function() { print('bar') }; debug(bar); bar()")
+debug at #1: {
+    eval(parse(text = rCode))
+    print("foo done")
+}
+debug at #1: eval(parse(text = rCode))
+debugging in: bar()
+debug at <text>#1: {
+    print("bar")
+}
+
 ##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testNoBracket#
 #f <- function(x) print(x)<<<NEWLINE>>>debug(f)<<<NEWLINE>>>f(5)<<<NEWLINE>>>x<<<NEWLINE>>>n<<<NEWLINE>>>
 debugging in: f(5)
@@ -143345,6 +143516,74 @@ debug at #4: t
 exiting from: f(5)
 [1] 6
 
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testStepInto#
+#bar <- function(x) { cat(x); cat('\n') }; foo <- function(x) { cat('foo entry\n'); bar(x); cat('foo exit\n') }; debug(foo); foo(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>s<<<NEWLINE>>>n<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>
+debugging in: foo(3)
+debug at #1: {
+    cat("foo entry\n")
+    bar(x)
+    cat("foo exit\n")
+}
+debug at #1: cat("foo entry\n")
+foo entry
+debug at #1: bar(x)
+debugging in: bar(x)
+debug at #1: {
+    cat(x)
+    cat("\n")
+}
+debug at #1: cat(x)
+3debug at #1: cat("\n")
+
+debug at #1: cat("foo exit\n")
+foo exit
+exiting from: foo(3)
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testStepInto#
+#bar <- function(x) { cat(x); cat('\n') }; foo <- function(x) { cat('foo entry\n'); bar(x); cat('foo exit\n') }; debug(foo); foo(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>s<<<NEWLINE>>>n<<<NEWLINE>>>Q<<<NEWLINE>>>
+debugging in: foo(3)
+debug at #1: {
+    cat("foo entry\n")
+    bar(x)
+    cat("foo exit\n")
+}
+debug at #1: cat("foo entry\n")
+foo entry
+debug at #1: bar(x)
+debugging in: bar(x)
+debug at #1: {
+    cat(x)
+    cat("\n")
+}
+debug at #1: cat(x)
+
+##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testStepInto#
+#bar <- function(x) { for(i in seq(x)) print(x) }; foo <- function(x) { cat('foo entry\n'); bar(x); cat('foo exit\n') }; debug(foo); foo(5)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>s<<<NEWLINE>>>n<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>f<<<NEWLINE>>><<<NEWLINE>>>
+debugging in: foo(5)
+debug at #1: {
+    cat("foo entry\n")
+    bar(x)
+    cat("foo exit\n")
+}
+debug at #1: cat("foo entry\n")
+foo entry
+debug at #1: bar(x)
+debugging in: bar(x)
+debug at #1: {
+    for (i in seq(x)) print(x)
+}
+debug at #1: for (i in seq(x)) print(x)
+debug at #1: print(x)
+[1] 5
+debug at #1: print(x)
+[1] 5
+[1] 5
+[1] 5
+[1] 5
+debug at #1: cat("foo exit\n")
+foo exit
+exiting from: foo(5)
+
 ##com.oracle.truffle.r.test.library.utils.TestTrace.testCondTrace#
 #f <- function(x) {}; (if (exists('.fastr.trace')) .fastr.trace else trace)(f, tracer=quote(if (x == 3 || x == 7) print(x))); g <- function() for (i in 1:10) f(i); g()
 [1] "f"
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
index b0ed490c9833296de281c67815cde0a615ea7d8e..21d6e39b32118f9e58899eee2962eff22cb8e6ad 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java
@@ -77,7 +77,9 @@ public class TestBase {
         MayIgnoreWarningContext,
         ContainsReferences, // replaces references in form of 0xbcdef1 for numbers
         IgnoreWhitespace, // removes all whitespace from the whole output
-        IgnoreCase; // ignores upper/lower case differences
+        IgnoreCase, // ignores upper/lower case differences
+        IgnoreDebugPath, // ignores <path> in debug output like "debug at <path> #..."
+        IgnoreDebugDepth; // ignores call depth printed by the debugger ("Browse[<call depth>]")
 
         @Override
         public String getName() {
@@ -608,6 +610,12 @@ public class TestBase {
             if (output.contains(Output.ContainsReferences)) {
                 return convertReferencesInOutput(out);
             }
+            if (output.contains(Output.IgnoreDebugPath)) {
+                return convertDebugOutput(out);
+            }
+            if (output.contains(Output.IgnoreDebugDepth)) {
+                return removeDebugCallDepth(out);
+            }
             return out;
         }
     }
@@ -731,6 +739,29 @@ public class TestBase {
         return result;
     }
 
+    private static String convertDebugOutput(String out) {
+        String prefix = "debug at ";
+        return removeAllOccurrencesBetween(out, prefix, prefix.length(), "#", 0);
+    }
+
+    private static String removeDebugCallDepth(String out) {
+        String prefix = "Browse[";
+        return removeAllOccurrencesBetween(out, prefix, prefix.length(), "]", 0);
+    }
+
+    private static String removeAllOccurrencesBetween(String out, String prefix, int prefixOffset, String suffix, int suffixOffset) {
+        StringBuilder sb = new StringBuilder(out);
+
+        int idxPrefix = -1;
+        int idxSuffix = -1;
+
+        while ((idxPrefix = sb.indexOf(prefix, idxPrefix + 1)) > 0 && (idxSuffix = sb.indexOf(suffix, idxPrefix)) > idxPrefix) {
+            sb.replace(idxPrefix + prefixOffset, idxSuffix + suffixOffset, "");
+        }
+
+        return sb.toString();
+    }
+
     private boolean searchWhiteLists(WhiteList[] whiteLists, String input, String expected, String result, TestTraitsSet testTraits) {
         if (whiteLists == null) {
             return false;
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java
index eec8974833cac7fa384d397106682b90355d237b..0b51a3d92da5c30b1872b21f4e31c429aeaa6403 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java
@@ -36,6 +36,7 @@ import com.oracle.truffle.api.debug.SuspendedEvent;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
 import com.oracle.truffle.r.runtime.ExitException;
+import com.oracle.truffle.r.runtime.JumpToTopLevelException;
 import com.oracle.truffle.r.runtime.RCmdOptions;
 import com.oracle.truffle.r.runtime.RCmdOptions.Client;
 import com.oracle.truffle.r.runtime.RError;
@@ -224,8 +225,9 @@ public final class FastRSession implements RSession {
             }
         } catch (ParseException e) {
             e.report(consoleHandler);
-        } catch (ExitException e) {
-            // exit exceptions are legitimate if a test case calls "q()"
+        } catch (ExitException | JumpToTopLevelException e) {
+            // exit and jumpToTopLevel exceptions are legitimate if a test case calls "q()" or "Q"
+            // during debugging
         } catch (RError e) {
             // nothing to do
         } catch (Throwable t) {
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java
index 7d32146ecb76c6ce69082ef78ed32e6e1b54ff2c..e74647fcf24fff7e8ffb7e4de186873b31aa74d4 100644
--- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@ import org.junit.Test;
 
 import com.oracle.truffle.r.test.TestBase;
 
+// Checkstyle: stop line length check
 public class TestInteractiveDebug extends TestBase {
     @Test
     public void testSimple() {
@@ -41,4 +42,44 @@ public class TestInteractiveDebug extends TestBase {
     public void testNoBracket() {
         assertEval("f <- function(x) print(x)\ndebug(f)\nf(5)\nx\nn\n");
     }
+
+    @Test
+    public void testLoop() {
+        assertEval("fun <- function(x) { for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)\n\n\n\n\n\n\n");
+        assertEval("fun <- function(x) { for(i in seq(x)) { cat(i) }; cat(5) }; debug(fun); fun(3)\n\n\n\n\n\n\n");
+        assertEval("fun <- function(x) { for(i in seq(x)) { cat(i) }; cat(5) }; debug(fun); fun(3)\n\n\n\nf\n\n");
+        assertEval("fun <- function(x) { for(j in seq(2)) for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)\n\n\n\n\n\n\n\n\n\n\n\n");
+        assertEval("fun <- function(x) { for(j in seq(2)) for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)\n\n\n\nf\nf\n\n");
+    }
+
+    @Test
+    public void testStepInto() {
+        assertEval("bar <- function(x) { cat(x); cat('\\n') }; foo <- function(x) { cat('foo entry\\n'); bar(x); cat('foo exit\\n') }; debug(foo); foo(3)\n\n\ns\nn\n\n\n\n");
+        assertEval("bar <- function(x) { cat(x); cat('\\n') }; foo <- function(x) { cat('foo entry\\n'); bar(x); cat('foo exit\\n') }; debug(foo); foo(3)\n\n\ns\nn\nQ\n");
+        assertEval("bar <- function(x) { for(i in seq(x)) print(x) }; foo <- function(x) { cat('foo entry\\n'); bar(x); cat('foo exit\\n') }; debug(foo); foo(5)\n\n\ns\nn\n\n\nf\n\n");
+    }
+
+    @Test
+    public void testNestedDebugging() {
+        assertEval(Output.IgnoreDebugPath,
+                        "foo <- function(rCode) { eval(parse(text=rCode)); print('foo done') }; debug(foo); foo(\"bar <- function() { print('bar') }; debug(bar); bar()\")\n\n\n\n\n\n");
+        assertEval(Output.IgnoreDebugPath,
+                        "foo <- function(rCode) { eval(parse(text=rCode)); print('foo done') }; debug(foo); foo(\"bar <- function() { print('bar') }; debug(bar); bar()\")\n\n\nQ\n");
+    }
+
+    @Test
+    public void testConditionalBreakpoint() {
+        assertEval("fun <- function(x) { cat('x='); cat(x); cat('\\n') }; trace(fun, quote(if (x > 10) browser())); fun(10)\n; fun(11)\n\n\n\n\n\n");
+    }
+
+    @Test
+    public void testContinue() {
+        assertEval("fun0 <- function() { print('fun0') }; fun1 <- function() { print('enter fun1'); fun0(); print('exit fun1') }; fun2 <- function() { print('enter fun2'); fun1(); print('exit fun2') }; debug(fun2); fun2()\n\n\ns\nn\n\ns\nc\nc\nc\n");
+    }
+
+    @Test
+    public void testDebugOnce() {
+        assertEval("fun0 <- function() { print('fun0') }; fun1 <- function() { print('en'); fun0(); fun0(); print('ex') }; debugonce(fun0); fun1()\nc\n");
+        assertEval("fun0 <- function() { print('fun0') }; debugonce(fun0); fun0()\nc\n");
+    }
 }