diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java index 8f0f513b5c7f3caaed7217d6668f7b97b57f04ac..55d968d8d5e47747aef3c697dfda3622a207b1e8 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java @@ -30,6 +30,7 @@ import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL; import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.MaterializedFrame; @@ -38,13 +39,16 @@ import com.oracle.truffle.r.nodes.builtin.NodeWithArgumentCasts.Casts; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.nodes.builtin.helpers.BrowserInteractNode; import com.oracle.truffle.r.nodes.builtin.helpers.BrowserInteractNodeGen; +import com.oracle.truffle.r.nodes.function.GetCallerFrameNode; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.RError; import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.SubstituteVirtualFrame; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.instrument.InstrumentationState.BrowserState; import com.oracle.truffle.r.runtime.instrument.InstrumentationState.BrowserState.HelperState; @@ -55,6 +59,7 @@ public class BrowserFunctions { public abstract static class BrowserNode extends RBuiltinNode.Arg4 { @Child private BrowserInteractNode browserInteractNode = BrowserInteractNodeGen.create(); + @Child private GetCallerFrameNode getCallerFrame; @Override public Object[] getDefaultParameterValues() { @@ -78,8 +83,18 @@ public class BrowserFunctions { browserState.push(new HelperState(text, condition)); MaterializedFrame mFrame = frame.materialize(); RCaller caller = RArguments.getCall(mFrame); + RFunction fun = RArguments.getFunction(mFrame); + VirtualFrame actualFrame = frame; + if (fun != null && fun.isBuiltin() && fun.getRBuiltin().getBuiltinNodeClass() == BrowserNode.class) { + if (getCallerFrame == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + getCallerFrame = insert(new GetCallerFrameNode()); + } + actualFrame = SubstituteVirtualFrame.create(getCallerFrame.execute(mFrame)); + caller = caller.getParent(); + } doPrint(caller); - browserInteractNode.execute(frame); + browserInteractNode.execute(actualFrame, caller); } finally { browserState.pop(); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java index 310290392d35cedc807fe9e4fc6893f3a3f51fed..d6e1d063a37b2c4ba96c5b24f5c95b958aedfebd 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/DebugFunctions.java @@ -139,9 +139,8 @@ public class DebugFunctions { casts.arg("clear").asLogicalVector().findFirst().map(toBoolean()); } - @SuppressWarnings("unused") @Specialization - protected Object setBreakpoint(String fileLine, RMissing lineNr, boolean clear) { + protected Object setBreakpoint(String fileLine, @SuppressWarnings("unused") RMissing lineNr, boolean clear) { int hashIdx = fileLine.lastIndexOf('#'); if (hashIdx != -1) { @@ -156,13 +155,16 @@ public class DebugFunctions { throw error(RError.Message.GENERIC, "Line number missing"); } - @SuppressWarnings("unused") @Specialization protected Object setBreakpoint(String fileName, int lineNr, boolean clear) { try { Source fromSrcfile = RSource.fromFileName(fileName, false); - DebugHandling.enableLineDebug(fromSrcfile, lineNr); + if (!clear) { + DebugHandling.enableLineDebug(fromSrcfile, lineNr); + } else { + DebugHandling.disableLineDebug(fromSrcfile, lineNr); + } return RDataFactory.createStringVectorFromScalar(fileName + "#" + lineNr); } catch (IOException e) { return RNull.instance; diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java index af83fd603a56f75d1caf484a3af591f654851582..a39f869d19371c557d545744c2b9d36f315a4699 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/BrowserInteractNode.java @@ -26,6 +26,7 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.r.nodes.attributes.GetFixedAttributeNode; import com.oracle.truffle.r.runtime.JumpToTopLevelException; import com.oracle.truffle.r.runtime.RArguments; @@ -48,7 +49,6 @@ import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.env.REnvironment; import com.oracle.truffle.r.runtime.instrument.InstrumentationState.BrowserState; import com.oracle.truffle.r.runtime.nodes.RCodeBuilder; -import com.oracle.truffle.r.runtime.nodes.RNode; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; /** @@ -62,7 +62,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; * </ol> * */ -public abstract class BrowserInteractNode extends RNode { +public abstract class BrowserInteractNode extends Node { public static final int STEP = 0; public static final int NEXT = 1; @@ -72,8 +72,10 @@ public abstract class BrowserInteractNode extends RNode { @Child private GetFixedAttributeNode getSrcRefAttrNode; @Child private GetFixedAttributeNode getSrcFileAttrNode; + public abstract int execute(VirtualFrame frame, RCaller caller); + @Specialization - protected int interact(VirtualFrame frame) { + protected int interact(VirtualFrame frame, RCaller caller) { CompilerDirectives.transferToInterpreter(); MaterializedFrame mFrame = frame.materialize(); ConsoleHandler ch = RContext.getInstance().getConsoleHandler(); @@ -81,9 +83,9 @@ public abstract class BrowserInteractNode extends RNode { String savedPrompt = ch.getPrompt(); RFunction callerFunction = RArguments.getFunction(frame); // we may be at top level where there is not caller - boolean callerIsDebugged = callerFunction == null ? false : DebugHandling.isDebugged(callerFunction); + boolean callerIsDebugged = callerFunction == null || DebugHandling.isDebugged(callerFunction); int exitMode = NEXT; - RCaller currentCaller = RArguments.getCall(mFrame); + RCaller currentCaller = caller; if (currentCaller == null) { currentCaller = RCaller.topLevel; } @@ -92,7 +94,7 @@ public abstract class BrowserInteractNode extends RNode { try { browserState.setInBrowser(browserCaller); LW: while (true) { - ch.setPrompt(browserPrompt(RArguments.getDepth(frame))); + ch.setPrompt(browserPrompt(currentCaller.getDepth())); String input = ch.readLine(); if (input != null) { input = input.trim(); @@ -130,7 +132,7 @@ public abstract class BrowserInteractNode extends RNode { case "Q": throw new JumpToTopLevelException(); case "where": { - if (RArguments.getDepth(mFrame) > 1) { + if (currentCaller.getDepth() > 1) { Object stack = Utils.createTraceback(0); // browser inverts frame depth int idepth = 1; 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 df64350f8341122a6ac76b962c8682a261631019..ff6f5b36d84fa8799557ff7c295026fcd2f53472 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 @@ -211,9 +211,13 @@ public class DebugHandling { @TruffleBoundary public static void disableLineDebug(Source fdn, int line) { LineBreakpointEventListener l = getLineBreakpointEventListener(fdn, line); - l.disable(); - l.fser.setParentListener(null); - l.fser.disable(); + if (l != null) { + l.disable(); + if (l.fser != null) { + l.fser.setParentListener(null); + l.fser.disable(); + } + } } private static FunctionStatementsEventListener ensureSingleStep(FunctionDefinitionNode fdn, LineBreakpointEventListener parentListener) { @@ -286,7 +290,7 @@ public class DebugHandling { } protected void browserInteract(Node node, VirtualFrame frame) { - int exitMode = (int) browserInteractNode.execute(frame); + int exitMode = browserInteractNode.execute(frame, RArguments.getCall(frame)); switch (exitMode) { case BrowserInteractNode.NEXT: break; @@ -602,12 +606,16 @@ public class DebugHandling { @Override public void onReturnValue(EventContext context, VirtualFrame frame, Object result) { - fser.enableChildren(); + if (fser != null) { + fser.enableChildren(); + } } @Override public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) { - fser.enableChildren(); + if (fser != null) { + fser.enableChildren(); + } } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/utils_overrides.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/utils_overrides.R index c3ccdbf25e546383613bc73d2b2437cf16a421ee..690fa8cf1384a1e6349532f93838cf094cd5777f 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/utils_overrides.R +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/utils/R/utils_overrides.R @@ -26,7 +26,7 @@ setBreakpoint <- function (srcfile, line, nameonly = TRUE, envir = parent.frame( { res <- .fastr.setBreakpoint(srcfile, line, clear) if(is.null(res)) - res <- structure(result, class="findLineNumResult") + res <- structure(list(), class="findLineNumResult") if (verbose) print(res, steps = !clear) } 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 3e3baf9305fc7532108f54053b3b2303fc7d52ef..9ee4692f3279091588feab1d1cbea31452ddb916 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 @@ -164,7 +164,7 @@ public class RSource { * Create an (external) source from the file system path {@code path}. */ public static Source fromFileName(String path, boolean internal) throws IOException { - Source.Builder<IOException, RuntimeException, RuntimeException> builder = Source.newBuilder(new File(path)).name(path).mimeType(RRuntime.R_APP_MIME); + Source.Builder<IOException, RuntimeException, RuntimeException> builder = Source.newBuilder(new File(path)).mimeType(RRuntime.R_APP_MIME); if (internal) { builder.internal(); } 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 855f7a4f5b1b688cad7304c617382f06ae0fed96..f619f3bbf734d21d66af179b62dd72a86cb43fe2 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 @@ -147031,6 +147031,26 @@ x + x ~ y + log(y) #update.formula(x ~ y, ~ . + x2) x ~ y + x2 +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testBrowser# +#browser()<<<NEWLINE>>>where<<<NEWLINE>>>c<<<NEWLINE>>> +Called from: top level + + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testBrowser# +#do.call('browser', list())<<<NEWLINE>>>c<<<NEWLINE>>> +Called from: do.call("browser", list()) + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testBrowser# +#foo <- function() { stop('error msg') }; tryCatch(foo(), error=browser)<<<NEWLINE>>>print(msg)<<<NEWLINE>>>c<<<NEWLINE>>> +Called from: tryCatchOne(expr, names, parentenv, handlers[[1L]]) +[1] "error msg" + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testBrowser# +#options(error=browser); prod('a')<<<NEWLINE>>>where<<<NEWLINE>>>c<<<NEWLINE>>> +Error in prod("a") : invalid 'type' (character) of argument +Called from: top level + + ##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" @@ -147245,6 +147265,33 @@ debug at #1: cat(x) exiting from: fun(3) +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testSetBreakpoint#Output.IgnoreDebugCallString#Output.IgnoreDebugPath# +#source('tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r'); setBreakpoint('tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r', 4, verbose=F); fun(10)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>> +debug.r#4 +Called from: fun(10) +debug: print("Hello") +[1] "Hello" +debug at tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r#5: for (i in seq(3)) print(i) +debug at tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r#5: print(i) +[1] 1 +debug at tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r#5: print(i) +[1] 2 +debug at tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r#5: print(i) +[1] 3 +debug at tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r#6: bar("World") +[1] "World" +debug at tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r#7: print(x) +[1] 10 + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testSetBreakpoint# +#source('tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r'); setBreakpoint('tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r', 4, verbose=F); setBreakpoint('tmptest/com.oracle.truffle.r.test.library.utils.rsrc/debug.r', 4, verbose=F, clear=T); fun(10)<<<NEWLINE>>> +[1] "Hello" +[1] 1 +[1] 2 +[1] 3 +[1] "World" +[1] 10 + ##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testSimple# #f <- function(x) {<<<NEWLINE>>> t <- x + 1<<<NEWLINE>>> print(t)<<<NEWLINE>>> t}<<<NEWLINE>>>debug(f)<<<NEWLINE>>>f(5)<<<NEWLINE>>>x<<<NEWLINE>>>n<<<NEWLINE>>>n<<<NEWLINE>>>t<<<NEWLINE>>>n<<<NEWLINE>>>n debugging in: f(5) 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 7c5e2b8fd0c558ecd15f8bba64459cee3c8a4f2a..05eeefd99daa132306adc2489903c7daf1da93ed 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 @@ -12,8 +12,10 @@ package com.oracle.truffle.r.test; import static org.junit.Assert.fail; +import java.io.BufferedReader; import java.io.File; import java.io.IOException; +import java.io.StringReader; import java.net.URL; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -29,6 +31,7 @@ import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; +import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -92,7 +95,8 @@ public class TestBase { IgnoreWhitespace, // removes all whitespace from the whole output 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>]") + IgnoreDebugDepth, // ignores call depth printed by the debugger ("Browse[<call depth>]") + IgnoreDebugCallString; // ignores the caller string like "debugging in:" or "Called from:" @Override public String getName() { @@ -600,22 +604,26 @@ public class TestBase { } String preprocessOutput(String out) { + String s = out; if (output.contains(Output.IgnoreWhitespace)) { - return out.replaceAll("\\s+", ""); + return s.replaceAll("\\s+", ""); } if (output.contains(Output.IgnoreCase)) { - return out.toLowerCase(); + return s.toLowerCase(); } if (output.contains(Output.ContainsReferences)) { - return convertReferencesInOutput(out); + return convertReferencesInOutput(s); } if (output.contains(Output.IgnoreDebugPath)) { - return convertDebugOutput(out); + s = convertDebugOutput(s); } if (output.contains(Output.IgnoreDebugDepth)) { - return removeDebugCallDepth(out); + s = removeDebugCallDepth(s); } - return out; + if (output.contains(Output.IgnoreDebugCallString)) { + s = removeDebugCallString(s); + } + return s; } } @@ -806,6 +814,10 @@ public class TestBase { return removeAllOccurrencesBetween(out, prefix, prefix.length(), "]", 0); } + private static String removeDebugCallString(String out) { + return removeLines(out, line -> line.startsWith("debugging in:") || line.startsWith("Called from:")); + } + private static String removeAllOccurrencesBetween(String out, String prefix, int prefixOffset, String suffix, int suffixOffset) { StringBuilder sb = new StringBuilder(out); @@ -819,6 +831,28 @@ public class TestBase { return sb.toString(); } + /** + * Removes the lines from the test output string matching the provided predicate. + */ + private static String removeLines(String out, Predicate<String> pred) { + StringBuilder sb = new StringBuilder(); + + BufferedReader r = new BufferedReader(new StringReader(out)); + String line; + try { + while ((line = r.readLine()) != null) { + if (!pred.test(line)) { + sb.append(line); + sb.append(System.lineSeparator()); + } + } + } catch (IOException e) { + // won't happen + } + + 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/library/utils/TestInteractiveDebug.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java index 0d5d1b076f0246c735de9e0f352deeefa0c75d38..c9b996f5905708159264f679708f2c0f5f829d62 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 @@ -22,6 +22,12 @@ */ package com.oracle.truffle.r.test.library.utils; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +import org.junit.BeforeClass; import org.junit.Test; import com.oracle.truffle.r.test.TestBase; @@ -88,4 +94,28 @@ public class TestInteractiveDebug extends TestBase { 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"); } + + @Test + public void testBrowser() { + assertEval("foo <- function() { stop('error msg') }; tryCatch(foo(), error=browser)\nprint(msg)\nc\n"); + assertEval("do.call('browser', list())\nc\n"); + assertEval("browser()\nwhere\nc\n"); + assertEval("options(error=browser); prod('a')\nwhere\nc\n"); + } + + private static Path debugFile; + + @BeforeClass + public static void setup() throws IOException { + Path testDir = TestBase.createTestDir("com.oracle.truffle.r.test.library.utils.rsrc"); + String content = "bar <- function(x) print(x)\n\nfun <- function(x) {\nprint('Hello')\nfor(i in seq(3)) print(i)\nbar('World')\nprint(x)\n}"; + debugFile = testDir.resolve("debug.r"); + Files.write(debugFile, content.getBytes(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } + + @Test + public void testSetBreakpoint() { + assertEval(Output.IgnoreDebugCallString, Output.IgnoreDebugPath, String.format("source('%s'); setBreakpoint('%s', 4, verbose=F); fun(10)\n\n\n\n\n\n\n\n", debugFile, debugFile)); + assertEval(String.format("source('%s'); setBreakpoint('%s', 4, verbose=F); setBreakpoint('%s', 4, verbose=F, clear=T); fun(10)\n", debugFile, debugFile, debugFile)); + } }