# HG changeset patch # User Michael Van De Vanter # Date 1389224958 28800 # Node ID d7af2296cebba181a209d18bb385a569f7b98ae0 # Parent 1894412de0ed295c2252b886d4417bfbd846e8dd# Parent 4fc8c8bb4c32878cc04b064d2ac9ad1fce1a85e0 Merge with 4fc8c8bb4c32878cc04b064d2ac9ad1fce1a85e0 diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ASTPrinter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ASTPrinter.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013, 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api; + +import java.io.*; + +import com.oracle.truffle.api.nodes.*; + +/** + * Language-agnostic access to AST-based debugging support. + *

+ * WARNING: this interface is under development and will change substantially. + */ +public interface ASTPrinter { + + /** + * Print a textual AST display, one line per node, with nesting. + * + * @param p + * @param node the root node of the display. + * @param maxDepth the maximum number of levels to print below the root + * @param markNode a node to mark with a textual arrow prefix, if present. + */ + void printTree(PrintWriter p, Node node, int maxDepth, Node markNode); + +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/DebugManager.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/DebugManager.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/DebugManager.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,16 +24,24 @@ */ package com.oracle.truffle.api; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.nodes.instrument.*; import com.oracle.truffle.api.source.*; /** * Language-agnostic access to AST-based debugging support. *

- * WARNING: this interface is under development and will change substantially. + * Disclaimer: this interface is under development and will change. */ public interface DebugManager { /** + * Gets a list of current breakpoints. + */ + LineBreakpoint[] getBreakpoints(); + + /** * Sets a breakpoint at a line-based location. */ LineBreakpoint setBreakpoint(SourceLineLocation lineLocation); @@ -45,9 +53,14 @@ LineBreakpoint setConditionalBreakpoint(SourceLineLocation lineLocation, String condition); /** - * Gets a list of current breakpoints. + * Sets a breakpoint at a line-based location that will remove itself when hit. */ - LineBreakpoint[] getBreakpoints(); + LineBreakpoint setOneShotBreakpoint(SourceLineLocation lineLocation); + + /** + * Is there a line-based breakpoint set at a location? + */ + boolean hasBreakpoint(SourceLineLocation lineLocation); /** * Removes a breakpoint at a line-based location. @@ -55,6 +68,19 @@ void removeBreakpoint(SourceLineLocation lineLocation); /** + * Notification from Truffle execution that execution should halt in a debugging context. + */ + /** + * Receives notification of a suspended execution context; execution resumes when this method + * returns. + * + * @param astNode a guest language AST node that represents the current execution site, assumed + * not to be any kind of {@link InstrumentationNode}, + * @param frame execution frame at the site where execution suspended + */ + void haltedAt(Node astNode, MaterializedFrame frame); + + /** * Description of a line-based breakpoint. */ interface LineBreakpoint { diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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,8 @@ /** * Information about the runtime context of a Truffle program. + *

+ * Disclaimer: this interface is under development and will change. */ public interface ExecutionContext { @@ -40,4 +42,9 @@ */ DebugManager getDebugManager(); + /** + * Gets access to utilities for printing different aspects of a Truffle AST. + */ + ASTPrinter getASTPrinter(); + } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/NullSourceSection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/NullSourceSection.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api; + +/** + * Marker for a special flavor of {@link SourceSection} that has no content and can be ignored. + */ +public interface NullSourceSection extends SourceSection { + +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -95,7 +95,7 @@ /** * Singleton instance with no content. */ - SourceSection NULL = new SourceSection() { + SourceSection NULL = new NullSourceSection() { public Source getSource() { return null; diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/EmptyProbe.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/EmptyProbe.java Wed Jan 08 22:59:53 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2013, 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 - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.nodes.instrument; - -import com.oracle.truffle.api.nodes.instrument.InstrumentationProbeNode.DefaultProbeNode; - -/** - * An "probe" that does nothing, used for testing. It relies on the - * {@link InstrumentationProbeNode.DefaultProbeNode} implementation to override every event with an - * empty method. - */ -public class EmptyProbe extends DefaultProbeNode { - -} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProbeNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProbeNode.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProbeNode.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,6 +24,8 @@ */ package com.oracle.truffle.api.nodes.instrument; +import java.util.*; + import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; @@ -50,6 +52,11 @@ return next == null ? 0 : next.countProbes() + 1; } + protected boolean isStepping() { + final InstrumentationProbeNode parent = (InstrumentationProbeNode) getParent(); + return parent.isStepping(); + } + /** * Add a probe to the end of this probe chain. */ @@ -69,6 +76,7 @@ this.next = null; } else { this.next = adoptChild(oldProbeNode.next); + oldProbeNode.next = null; } } else { next.internalRemoveProbe(oldProbeNode); @@ -186,6 +194,16 @@ */ public static class DefaultProbeNode extends InstrumentationProbeNode { + private final ExecutionContext executionContext; + + protected DefaultProbeNode(ExecutionContext context) { + this.executionContext = context; + } + + public ExecutionContext getContext() { + return executionContext; + } + public void enter(Node astNode, VirtualFrame frame) { } @@ -230,17 +248,28 @@ /** * Holder of a chain of {@linkplain InstrumentationProbeNode probes}: manages the * {@link Assumption} that the chain has not changed since checked checked. + *

+ * May be categorized by one or more {@linkplain NodePhylum node phyla}, signifying information + * useful for instrumentation about its AST location(s). */ - public static final class ProbeChain extends DefaultProbeNode { + public static final class ProbeChain extends DefaultProbeNode implements PhylumMarked { @CompilerDirectives.CompilationFinal private Assumption probeUnchanged; /** + * When in stepping mode, ordinary line breakpoints are ignored, but every entry at a line + * will cause a halt. + */ + @CompilerDirectives.CompilationFinal private boolean stepping; + + /** * Source information about the node to which this probe chain is attached; it isn't * otherwise available. A probe chain is shared by every copy made during runtime, so there * is no parent pointer. */ - @SuppressWarnings("unused") private final SourceSection sourceSection; + private final SourceSection probedSourceSection; + + private final Set phyla = EnumSet.noneOf(NodePhylum.class); private final String description; // for debugging @@ -249,9 +278,10 @@ * probes can be added/removed, and all of which will be notified of * {@linkplain InstrumentationProbeEvents events} when the chain is notified. */ - public ProbeChain(SourceSection sourceSection, String description) { + public ProbeChain(ExecutionContext context, SourceSection sourceSection, String description) { + super(context); this.probeUnchanged = Truffle.getRuntime().createAssumption(); - this.sourceSection = sourceSection; + this.probedSourceSection = sourceSection; this.description = description; this.next = null; } @@ -264,6 +294,51 @@ return description; } + public SourceSection getProbedSourceSection() { + return probedSourceSection; + } + + /** + * Mark this probe chain as being associated with an AST node in some category useful for + * debugging and other tools. + */ + public void markAs(NodePhylum phylum) { + assert phylum != null; + phyla.add(phylum); + } + + /** + * Is this probe chain as being associated with an AST node in some category useful for + * debugging and other tools. + */ + public boolean isMarkedAs(NodePhylum phylum) { + assert phylum != null; + return phyla.contains(phylum); + } + + /** + * In which categories is the AST (with which this probe is associated) marked? + */ + public Set getPhylumMarks() { + return phyla; + } + + /** + * Change stepping mode for statements. + */ + public void setStepping(boolean stepping) { + if (this.stepping != stepping) { + this.stepping = stepping; + probeUnchanged.invalidate(); + probeUnchanged = Truffle.getRuntime().createAssumption(); + } + } + + @Override + protected boolean isStepping() { + return stepping; + } + @Override protected int countProbes() { // The head of the chain does not itself hold a probe @@ -291,10 +366,13 @@ } public void notifyEnter(Node astNode, VirtualFrame frame) { - if (next != null) { + if (stepping || next != null) { if (!probeUnchanged.isValid()) { CompilerDirectives.transferToInterpreter(); } + if (stepping) { + getContext().getDebugManager().haltedAt(astNode, frame.materialize()); + } next.internalEnter(astNode, frame); } } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProxyNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProxyNode.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/InstrumentationProxyNode.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -24,6 +24,7 @@ */ package com.oracle.truffle.api.nodes.instrument; +import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.nodes.instrument.InstrumentationProbeNode.ProbeChain; /** @@ -36,7 +37,12 @@ * probe chain} which passes along {@linkplain InstrumentationProbeEvents events} to any * {@linkplain InstrumentationProbeNode probes} that might have been attached. */ -public interface InstrumentationProxyNode extends InstrumentationNode { +public interface InstrumentationProxyNode extends InstrumentationNode, PhylumMarked { + + /** + * Gets the non-instrumentation node being proxied. + */ + Node getChild(); /** * Gets the chain of probes to which events at this node are delegated. Note that a chain of diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/NodePhylum.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/NodePhylum.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.nodes.instrument; + +/** + * Categories of {@link InstrumentationProxyNode}s to be used for defining user-visible debugging + * and other simple tool behavior. These categories (phyla) should correspond to program + * structures that are meaningful to a programmer using the guest language. A Truffle node without a + * proxy carrying some phylum should be treated as an artifact of the guest language implementation + * and should never to the user of a guest language programming tool. + *

+ * Note that phyla are not intended to represent a partition of user-visible node categories, as the + * relative categorization of nodes can change with the particular programming tasks at hand. + *

+ * This is a somewhat language-agnostic set of phyla, suitable for conventional imperative + * languages, and is being developed incrementally. + *

+ * The need for alternative sets of phyla is likely to arise, perhaps for other families of + * languages (for example for mostly expression-oriented languages) or even for specific languages. + *

+ * These are listed alphabetically so that listing from some collection classes will come out in + * that order. + *

+ * Disclaimer: this interface is under development and will change. + */ +public enum NodePhylum { + + /** + * Marker for a proxy at a variable assignment. + */ + ASSIGNMENT, + + /** + * Marker for a proxy at a call site. + */ + CALL, + + /** + * Marker for a proxy at which ordinary "stepping" should halt. + */ + STATEMENT; + +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/PhylumMarked.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/instrument/PhylumMarked.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.nodes.instrument; + +import java.util.*; + +import com.oracle.truffle.api.nodes.*; + +/** + * A kind of {@link Node} that can be marked as belong to 0 or more {@linkplain NodePhylum phyla}. + */ +public interface PhylumMarked { + + /** + * Is this proxy tagged as belonging to a particular category of language constructs? + */ + boolean isMarkedAs(NodePhylum phylum); + + /** + * In which categories is this node tagged (empty set if none). + */ + Set getPhylumMarks(); + +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceLineLocation.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceLineLocation.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceLineLocation.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -38,11 +38,14 @@ public SourceLineLocation(Source source, int line) { assert source != null; - assert source != SourceSection.NULL; this.source = source; this.line = line; } + public SourceLineLocation(SourceSection sourceSection) { + this(sourceSection.getSource(), sourceSection.getStartLine()); + } + public Source getSource() { return source; } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -47,7 +47,7 @@ public final class SourceManager { // Only files and fake files are indexed. - private final Map sourceMap = new HashMap<>(); + private final Map pathToSource = new HashMap<>(); public SourceManager() { @@ -57,24 +57,29 @@ * Gets the canonical representation of a source file, whose contents will be read lazily and * then cached. * - * @param reset forces any existing {@link Source} cache to be cleared, forcing a re-read. + * @param reset forces any existing {@link Source} cache to be cleared, forcing a re-read */ public Source get(String fileName, boolean reset) { - SourceImpl source = sourceMap.get(fileName); + + SourceImpl source = pathToSource.get(fileName); if (source == null) { - String path = findPath(fileName); - if (path == null) { - throw new RuntimeException("Can't find file " + fileName); + final File file = new File(fileName); + String path = null; + if (file.exists()) { + try { + path = file.getCanonicalPath(); + } catch (IOException e) { + throw new RuntimeException("Can't find file " + fileName); + } } - source = sourceMap.get(path); + source = pathToSource.get(path); if (source == null) { - source = new FileSourceImpl(fileName, path); - sourceMap.put(path, source); + source = new FileSourceImpl(file, fileName, path); + pathToSource.put(path, source); } - } else { - if (reset) { - source.reset(); - } + } + if (reset) { + source.reset(); } return source; } @@ -111,22 +116,10 @@ */ public Source getFakeFile(String name, String code) { final SourceImpl source = new LiteralSourceImpl(name, code); - sourceMap.put(name, source); + pathToSource.put(name, source); return source; } - // If it names a real file, get the (canonical) normalized absolute path. - private static String findPath(String name) { - final File file = new File(name); - if (file.exists()) { - try { - return file.getCanonicalPath(); - } catch (IOException e) { - } - } - return null; - } - private static String readCode(Reader reader) throws IOException { final StringBuilder builder = new StringBuilder(); final char[] buffer = new char[1024]; @@ -142,8 +135,7 @@ return builder.toString(); } - // TODO (mlvdv) make this private once some related code changes propagate - public abstract static class SourceImpl implements Source { + private abstract static class SourceImpl implements Source { protected TextMap textMap = null; @@ -267,16 +259,17 @@ private static class FileSourceImpl extends SourceImpl { + private final File file; private final String name; // Name used originally to describe the source - private String code = null; private final String path; // Normalized path description of an actual file - private boolean readAttempted; - public FileSourceImpl(String name, String path) { + private String code = null; // A cache of the file's contents + private long timeStamp; // timestamp of the cache in the file system + + public FileSourceImpl(File file, String name, String path) { + this.file = file; this.name = name; this.path = path; - this.readAttempted = false; - } @Override @@ -286,10 +279,10 @@ @Override public String getCode() { - if (code == null && !readAttempted) { - readAttempted = true; + if (code == null || timeStamp != file.lastModified()) { try { code = readCode(getReader()); + timeStamp = file.lastModified(); } catch (IOException e) { } } @@ -303,11 +296,11 @@ @Override public Reader getReader() { - if (code != null) { + if (code != null && timeStamp == file.lastModified()) { return new StringReader(code); } try { - return new BufferedReader(new FileReader(path)); + return new FileReader(file); } catch (FileNotFoundException e) { throw new RuntimeException("Can't find file " + path); } @@ -316,7 +309,6 @@ @Override protected void reset() { this.code = null; - this.readAttempted = false; } } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/CoreSourceSection.java --- a/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/CoreSourceSection.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/CoreSourceSection.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -12,9 +12,9 @@ import com.oracle.truffle.api.*; /** - * Singleton source section used for core method nodes. + * Source sections used for core method nodes. */ -public final class CoreSourceSection implements SourceSection { +public final class CoreSourceSection implements NullSourceSection { private final String name; diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/CallNode.java --- a/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/CallNode.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/CallNode.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -152,4 +152,7 @@ return context.makeString("method"); } + public String getName() { + return name; + } } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyASTPrinter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyASTPrinter.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.debug; + +import java.io.*; +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.nodes.NodeUtil.NodeClass; +import com.oracle.truffle.api.nodes.NodeUtil.NodeField; +import com.oracle.truffle.api.nodes.NodeUtil.NodeFieldKind; +import com.oracle.truffle.api.nodes.instrument.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.nodes.literal.*; +import com.oracle.truffle.ruby.nodes.methods.*; + +/** + * Printers for Truffle-internal AST information. + */ +public final class RubyASTPrinter implements ASTPrinter { + + public RubyASTPrinter() { + } + + public void printTree(PrintWriter p, Node node, int maxDepth, Node markNode) { + printTree(p, node, maxDepth, markNode, 1); + p.println(); + p.flush(); + } + + public String printTreeToString(Node node, int maxDepth, Node markNode) { + StringWriter out = new StringWriter(); + printTree(new PrintWriter(out), node, maxDepth, markNode); + return out.toString(); + } + + public String printTreeToString(Node node, int maxDepth) { + return printTreeToString(node, maxDepth, null); + } + + private static void printTree(PrintWriter p, Node node, int maxDepth, Node markNode, int level) { + if (node == null) { + p.print("null"); + return; + } + + p.print(nodeName(node)); + + String sep = ""; + p.print("("); + + final SourceSection src = node.getSourceSection(); + if (src != null) { + if (!(src instanceof NullSourceSection)) { + p.print(src.getSource().getName() + ":" + src.getStartLine()); + } else if (src instanceof CoreSourceSection) { + final CoreSourceSection coreSection = (CoreSourceSection) src; + p.print("core=\"" + (coreSection == null ? "?" : coreSection.toString()) + "\""); + } + } + if (node instanceof PhylumMarked) { + final PhylumMarked markedNode = (PhylumMarked) node; + String prefix = ""; + for (NodePhylum phylum : markedNode.getPhylumMarks()) { + p.print(prefix); + prefix = ","; + p.print(phylum.toString()); + } + + } + + ArrayList childFields = new ArrayList<>(); + + for (NodeField field : NodeClass.get(node.getClass()).getFields()) { + if (field.getKind() == NodeFieldKind.CHILD || field.getKind() == NodeFieldKind.CHILDREN) { + childFields.add(field); + } else if (field.getKind() == NodeFieldKind.DATA) { + // p.print(sep); + // sep = ", "; + // + // final String fieldName = field.getName(); + // switch (fieldName) { + // + // } + // p.print(fieldName); + // p.print(" = "); + // p.print(field.loadValue(node)); + } + } + p.print(")"); + + if (level <= maxDepth) { + + if (childFields.size() != 0) { + p.print(" {"); + for (NodeField field : childFields) { + + Object value = field.loadValue(node); + if (value == null) { + printNewLine(p, level); + p.print(field.getName()); + p.print(" = null "); + } else if (field.getKind() == NodeFieldKind.CHILD) { + final Node valueNode = (Node) value; + printNewLine(p, level, valueNode == markNode); + p.print(field.getName()); + p.print(" = "); + printTree(p, valueNode, maxDepth, markNode, level + 1); + } else if (field.getKind() == NodeFieldKind.CHILDREN) { + printNewLine(p, level); + p.print(field.getName()); + Node[] children = (Node[]) value; + p.print(" = ["); + sep = ""; + for (Node child : children) { + p.print(sep); + sep = ", "; + printTree(p, child, maxDepth, markNode, level + 1); + } + p.print("]"); + } else { + printNewLine(p, level); + p.print(field.getName()); + } + } + printNewLine(p, level - 1); + p.print("}"); + } + } + } + + private static void printNewLine(PrintWriter p, int level, boolean mark) { + p.println(); + for (int i = 0; i < level; i++) { + if (mark && i == 0) { + p.print(" -->"); + } else { + p.print(" "); + } + } + } + + private static void printNewLine(PrintWriter p, int level) { + printNewLine(p, level, false); + } + + private static String nodeName(Node node) { + String nodeVal = null; + if (node instanceof CallNode) { + final CallNode callNode = (CallNode) node; + nodeVal = callNode.getName(); + + } else if (node instanceof FixnumLiteralNode) { + final FixnumLiteralNode fixnum = (FixnumLiteralNode) node; + nodeVal = Integer.toString(fixnum.getValue()); + } else if (node instanceof MethodDefinitionNode) { + final MethodDefinitionNode defNode = (MethodDefinitionNode) node; + nodeVal = defNode.getName(); + } + String result = node.getClass().getSimpleName(); + if (nodeVal != null) { + result = result + "[\"" + nodeVal + "\"]"; + } + return result; + } + +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyProxyNode.java --- a/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyProxyNode.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyProxyNode.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -10,6 +10,7 @@ package com.oracle.truffle.ruby.nodes.debug; import java.math.*; +import java.util.*; import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; @@ -34,9 +35,16 @@ public RubyProxyNode(RubyContext context, RubyNode child) { super(context, SourceSection.NULL); - this.child = adoptChild(child); assert !(child instanceof RubyProxyNode); - this.probeChain = new ProbeChain(child.getSourceSection(), null); + this.child = adoptChild(child); + this.probeChain = context.getDebugManager().getProbeChain(child.getSourceSection()); + } + + public RubyProxyNode(RubyContext context, RubyNode child, ProbeChain probeChain) { + super(context, SourceSection.NULL); + assert !(child instanceof RubyProxyNode); + this.child = adoptChild(child); + this.probeChain = probeChain; } @Override @@ -189,4 +197,16 @@ } } + public boolean isMarkedAs(NodePhylum phylum) { + return probeChain.isMarkedAs(phylum); + } + + public Set getPhylumMarks() { + return probeChain.getPhylumMarks(); + } + + public void markAs(NodePhylum phylum) { + probeChain.markAs(phylum); + } + } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/FixnumLiteralNode.java --- a/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/FixnumLiteralNode.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/FixnumLiteralNode.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -35,7 +35,6 @@ return value; } - // TODO(CS): remove this - shouldn't be fiddling with nodes from the outside public int getValue() { return value; } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/MethodDefinitionNode.java --- a/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/MethodDefinitionNode.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/MethodDefinitionNode.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -87,4 +87,8 @@ return executeMethod(frame); } + public String getName() { + return name; + } + } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/Translator.java --- a/graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/Translator.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/Translator.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -16,6 +16,8 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.impl.*; +import com.oracle.truffle.api.nodes.instrument.*; +import com.oracle.truffle.api.nodes.instrument.InstrumentationProbeNode.ProbeChain; import com.oracle.truffle.ruby.nodes.*; import com.oracle.truffle.ruby.nodes.call.*; import com.oracle.truffle.ruby.nodes.cast.*; @@ -87,6 +89,15 @@ nodeDefinedNames.put(org.jrubyparser.ast.DVarNode.class, "local-variable"); } + private static final Set debugIgnoredCalls = new HashSet<>(); + + static { + debugIgnoredCalls.add("downto"); + debugIgnoredCalls.add("each"); + debugIgnoredCalls.add("times"); + debugIgnoredCalls.add("upto"); + } + /** * Global variables which in common usage have frame local semantics. */ @@ -289,7 +300,20 @@ final ArgumentsAndBlockTranslation argumentsAndBlock = translateArgumentsAndBlock(sourceSection, block, args, extraArgument); - return new CallNode(context, sourceSection, node.getName(), receiverTranslated, argumentsAndBlock.getBlock(), argumentsAndBlock.isSplatted(), argumentsAndBlock.getArguments()); + RubyNode translated = new CallNode(context, sourceSection, node.getName(), receiverTranslated, argumentsAndBlock.getBlock(), argumentsAndBlock.isSplatted(), argumentsAndBlock.getArguments()); + + if (context.getConfiguration().getDebug()) { + final CallNode callNode = (CallNode) translated; + if (!debugIgnoredCalls.contains(callNode.getName())) { + + final RubyProxyNode proxy = new RubyProxyNode(context, translated); + proxy.markAs(NodePhylum.CALL); + proxy.getProbeChain().appendProbe(new RubyCallProbe(context, node.getName())); + translated = proxy; + } + } + + return translated; } protected class ArgumentsAndBlockTranslation { @@ -1147,6 +1171,7 @@ } else { proxy = new RubyProxyNode(context, translated); } + proxy.markAs(NodePhylum.ASSIGNMENT); context.getDebugManager().registerLocalDebugProxy(methodIdentifier, node.getName(), proxy.getProbeChain()); translated = proxy; @@ -1455,15 +1480,19 @@ if (context.getConfiguration().getDebug()) { RubyProxyNode proxy; - SourceSection sourceSection; if (translated instanceof RubyProxyNode) { proxy = (RubyProxyNode) translated; - sourceSection = proxy.getChild().getSourceSection(); + if (proxy.getChild() instanceof CallNode) { + // Special case; replace proxy with one registered by line, merge in information + final CallNode callNode = (CallNode) proxy.getChild(); + final ProbeChain probeChain = proxy.getProbeChain(); + + proxy = new RubyProxyNode(context, callNode, probeChain); + } } else { proxy = new RubyProxyNode(context, translated); - sourceSection = translated.getSourceSection(); } - context.getDebugManager().registerProbeChain(sourceSection, proxy.getProbeChain()); + proxy.markAs(NodePhylum.STATEMENT); translated = proxy; } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyContext.java --- a/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyContext.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyContext.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -42,6 +42,7 @@ private final AtExitManager atExitManager; private final RubyDebugManager debugManager; private final SourceManager sourceManager; + private final ASTPrinter astPrinter; private AtomicLong nextObjectID = new AtomicLong(0); @@ -50,14 +51,19 @@ private POSIX posix = POSIXFactory.getPOSIX(); public RubyContext(RubyParser parser) { - this(new Configuration(new ConfigurationBuilder()), parser); + this(new Configuration(new ConfigurationBuilder()), parser, null); } public RubyContext(Configuration configuration, RubyParser parser) { + this(configuration, parser, null); + } + + public RubyContext(Configuration configuration, RubyParser parser, ASTPrinter astPrinter) { assert configuration != null; this.configuration = configuration; this.parser = parser; + this.astPrinter = astPrinter; objectSpaceManager = new ObjectSpaceManager(this); traceManager = new TraceManager(this); @@ -91,6 +97,10 @@ return debugManager; } + public ASTPrinter getASTPrinter() { + return astPrinter; + } + public void implementationMessage(String format, Object... arguments) { System.err.println("rubytruffle: " + String.format(format, arguments)); } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakAfterLineProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakAfterLineProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A Ruby probe for halting execution at a line after a child execution method completes. + */ +public final class RubyBreakAfterLineProbe extends RubyLineProbe { + + /** + * Creates a probe that will cause a halt when child execution is complete; a {@code oneShot} + * probe will remove itself the first time it halts. + */ + public RubyBreakAfterLineProbe(RubyContext context, SourceLineLocation location, boolean oneShot) { + super(context, location, oneShot); + } + + @Override + public void leave(Node astNode, VirtualFrame frame) { + if (oneShot) { + // One-shot breakpoints retire after one activation. + context.getDebugManager().retireLineProbe(location, this); + } + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, boolean result) { + leave(astNode, frame); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, int result) { + leave(astNode, frame); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, double result) { + leave(astNode, frame); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, Object result) { + leave(astNode, frame); + } + + @Override + public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + leave(astNode, frame); + } + +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakAfterLocalProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakAfterLocalProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A Ruby probe for halting execution after a local assignment. + */ +public final class RubyBreakAfterLocalProbe extends RubyLocalProbe { + + public RubyBreakAfterLocalProbe(RubyContext context, MethodLocal local) { + super(context, local, false); + } + + @Override + public void leave(Node astNode, VirtualFrame frame) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, boolean result) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, int result) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, double result) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, Object result) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakAfterProbe.java --- a/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakAfterProbe.java Wed Jan 08 22:59:53 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This - * code is released under a tri EPL/GPL/LGPL license. You can use it, - * redistribute it and/or modify it under the terms of the: - * - * Eclipse Public License version 1.0 - * GNU General Public License version 2 - * GNU Lesser General Public License version 2.1 - */ -package com.oracle.truffle.ruby.runtime.debug; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.ruby.runtime.*; - -/** - * A Ruby probe for invoking a breakpoint shell after a child execution method completes. - */ -public final class RubyBreakAfterProbe extends RubyProbe { - - public RubyBreakAfterProbe(RubyContext context) { - super(context); - } - - @Override - public void leave(Node astNode, VirtualFrame frame) { - context.getDebugManager().haltedAt(astNode, frame.materialize()); - } - - @Override - public void leave(Node astNode, VirtualFrame frame, boolean result) { - context.getDebugManager().haltedAt(astNode, frame.materialize()); - } - - @Override - public void leave(Node astNode, VirtualFrame frame, int result) { - context.getDebugManager().haltedAt(astNode, frame.materialize()); - } - - @Override - public void leave(Node astNode, VirtualFrame frame, double result) { - context.getDebugManager().haltedAt(astNode, frame.materialize()); - } - - @Override - public void leave(Node astNode, VirtualFrame frame, Object result) { - context.getDebugManager().haltedAt(astNode, frame.materialize()); - } - - @Override - public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { - context.getDebugManager().haltedAt(astNode, frame.materialize()); - } - -} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakBeforeLineProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakBeforeLineProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A probe for halting execution at a line before a child execution method. + */ +public final class RubyBreakBeforeLineProbe extends RubyLineProbe { + + /** + * Creates a probe that will cause a halt just before child execution starts; a {@code oneShot} + * probe will remove itself the first time it halts. + */ + public RubyBreakBeforeLineProbe(RubyContext context, SourceLineLocation location, boolean oneShot) { + super(context, location, oneShot); + } + + @Override + public void enter(Node astNode, VirtualFrame frame) { + + if (!isStepping()) { + // Ordinary line breakpoints ignored during stepping so no double halts. + if (oneShot) { + // One-shot breakpoints retire after one activation. + context.getDebugManager().retireLineProbe(location, this); + } + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + } +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakBeforeProbe.java --- a/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakBeforeProbe.java Wed Jan 08 22:59:53 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This - * code is released under a tri EPL/GPL/LGPL license. You can use it, - * redistribute it and/or modify it under the terms of the: - * - * Eclipse Public License version 1.0 - * GNU General Public License version 2 - * GNU Lesser General Public License version 2.1 - */ -package com.oracle.truffle.ruby.runtime.debug; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.ruby.runtime.*; - -/** - * A probe for invoking a breakpoint shell before a child execution method. - */ -public final class RubyBreakBeforeProbe extends RubyProbe { - - public RubyBreakBeforeProbe(RubyContext context) { - super(context); - } - - @Override - public void enter(Node astNode, VirtualFrame frame) { - context.getDebugManager().haltedAt(astNode, frame.materialize()); - } - -} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyCallProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyCallProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +public final class RubyCallProbe extends RubyProbe { + + private final String name; + + public RubyCallProbe(RubyContext context, String name) { + super(context, false); + this.name = name; + } + + @Override + public void enter(Node astNode, VirtualFrame frame) { + context.getDebugManager().notifyCallEntry(astNode, name); + } + + @Override + public void leave(Node astNode, VirtualFrame frame) { + context.getDebugManager().notifyCallExit(astNode, name); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, boolean result) { + context.getDebugManager().notifyCallExit(astNode, name); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, int result) { + context.getDebugManager().notifyCallExit(astNode, name); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, double result) { + context.getDebugManager().notifyCallExit(astNode, name); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, Object result) { + context.getDebugManager().notifyCallExit(astNode, name); + } + + @Override + public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + context.getDebugManager().notifyCallExit(astNode, name); + } +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyDebugManager.java --- a/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyDebugManager.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyDebugManager.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -9,38 +9,113 @@ */ package com.oracle.truffle.ruby.runtime.debug; +import java.io.*; import java.util.*; +import java.util.Map.Entry; import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.nodes.instrument.InstrumentationProbeNode.ProbeChain; import com.oracle.truffle.api.nodes.instrument.*; -import com.oracle.truffle.api.nodes.instrument.InstrumentationProbeNode.ProbeChain; import com.oracle.truffle.api.source.*; import com.oracle.truffle.ruby.runtime.*; import com.oracle.truffle.ruby.runtime.core.*; import com.oracle.truffle.ruby.runtime.methods.*; /** - * Manager for Ruby AST execution. + * Manager for Ruby AST execution under debugging control. */ public final class RubyDebugManager implements DebugManager { - // TODO (mlvdv) no REPL support yet for debugging "locals"; only lines + // TODO (mlvdv) no REPL support yet for debugging "locals" (assignment to local variables); only + // line-based step/next/return + + private static final boolean TRACE = false; + private static final PrintStream OUT = System.out; + + private static enum ExecutionMode { + + /** + * Context: ordinary debugging execution, e.g. in response to a "Continue" request or a + * "Load-Run" request. + *

+ */ + CONTINUE, + + /** + * Context: per-statement stepping execution, e.g. in response to a "Step" request. + * + */ + STEP, + + /** + * Context: per-statement stepping in response to a "Next" request and when not nested in + * any function/method call. + * + */ + NEXT, + + /** + * Context: ordinary debugging execution in response to a "Next" requested and when nested + * at least one deep in function/method calls. + * + */ + NEXT_NESTED; + } private static enum BreakpointStatus { /** - * Created for a source location but not yet attached for some legitimate reason: new and - * not yet attached; new and the source file hasn't been loaded yet; old and the source file - * is in the process of being reloaded. + * Created for a source location but not yet attached for some legitimate reason: perhaps + * newly created and not yet attached; perhaps newly created and the source file hasn't been + * loaded yet; perhaps old and the source file is in the process of being reloaded. */ PENDING("Pending"), /** - * Has an active break probe in the AST. + * Has a {@link RubyProbe}, which is attached to a {@linkplain ProbeChain known location} in + * the AST. */ - ATTACHED("Active"), + ACTIVE("Active"), + + /** + * Has a {@link RubyProbe}, which is associated with a {@linkplain ProbeChain known + * location} in the AST, but which has been temporarily removed. + */ + DISABLED("Disabled"), /** * Should be attached, but the line location cannot be found in the source. @@ -64,19 +139,45 @@ } } - /** - * Map: Source lines ==> source chains known to be at line locations in an AST. - */ - private final Map linesToProbeChains = new HashMap<>(); - private final Set loadedSources = new HashSet<>(); private Source beingLoaded = null; /** - * Map: Source lines ==> attached Breakpoints/procs to be activated before execution at line. + * The current mode of execution. + */ + private ExecutionMode executionMode = ExecutionMode.CONTINUE; + + /** + * When running in "step" mode, this is the number of steps that haven't yet completed. + */ + private int unfinishedStepCount = 0; + + /** + * When running in "next" mode, this is the number of steps that haven't yet completed. + */ + private int unfinishedNextCount = 0; + /** + * When running in "next" mode, this is non-null when running a function/method that must be + * continued across. */ - private final Map linesToBreakpoints = new TreeMap<>(); + private Node nextNestedInCallNode = null; + + /** + * Map: SourceSection ==> probe chain associated with that source section in an AST. + */ + private final Map srcToProbeChain = new HashMap<>(); + + /** + * Map: Source lines ==> probe chains associated with source sections starting on the line. + */ + private final Map> lineToProbeChains = new HashMap<>(); + + /** + * Map: Source lines ==> attached Breakpoints/procs to be activated before execution at line. + * There should be no more than one line breakpoint associated with a line. + */ + private final Map lineToBreakpoint = new TreeMap<>(); /** * Map: Method locals in AST ==> Method local assignments where breakpoints can be attached. @@ -94,25 +195,58 @@ this.context = context; } + /** + * Gets the {@linkplain ProbeChain probe} associated with a particular {@link SourceSection + * source location}, creating a new one if needed. There should only be one probe associated + * with each {@linkplain SourceSection source location}. + */ + public ProbeChain getProbeChain(SourceSection sourceSection) { + assert sourceSection != null; + assert sourceSection.getSource().equals(beingLoaded); + + ProbeChain probeChain = srcToProbeChain.get(sourceSection); + + if (probeChain != null) { + return probeChain; + } + probeChain = new ProbeChain(context, sourceSection, null); + + // Register new ProbeChain by unique SourceSection + srcToProbeChain.put(sourceSection, probeChain); + + // Register new ProbeChain by source line, there may be more than one + // Create line location for map key + final SourceLineLocation lineLocation = new SourceLineLocation(sourceSection.getSource(), sourceSection.getStartLine()); + + Set probeChains = lineToProbeChains.get(lineLocation); + if (probeChains == null) { + probeChains = new HashSet<>(); + lineToProbeChains.put(lineLocation, probeChains); + } + probeChains.add(probeChain); + + return probeChain; + } + public void notifyStartLoading(Source source) { beingLoaded = source; - // Forget all the probe chains from previous loading - final List locations = new ArrayList<>(); - for (SourceLineLocation lineLocation : linesToProbeChains.keySet()) { - if (lineLocation.getSource().equals(beingLoaded)) { - locations.add(lineLocation); - } - } - for (SourceLineLocation lineLocation : locations) { - linesToProbeChains.remove(lineLocation); - } - - // Forget whatever we knew, and detach from old AST/ProbeChain if needed - for (RubyLineBreakpoint breakpoint : linesToBreakpoints.values()) { + /** + * We'd like to know when we're reloading a file if the old AST is completely dead, so that + * we can correctly identify the state of breakpoints related to it, but that doesn't seem + * possible. + * + * Before we start, find any breakpoints that never got attached, which get reported as + * errors. Revert them to "pending", in case their lines are found this time around. + */ + for (RubyLineBreakpoint breakpoint : lineToBreakpoint.values()) { if (breakpoint.getSourceLineLocation().getSource().equals(beingLoaded)) { - breakpoint.setPending(); + if (breakpoint.status == BreakpointStatus.ERROR) { + // It was an error, which means we have not yet found that line for this Source. + // It might show up while loading this time, so make it pending. + breakpoint.setPending(); + } } } } @@ -120,51 +254,66 @@ public void notifyFinishedLoading(Source source) { assert source == beingLoaded; - // Any pending breakpoints are now erroneous, didn't find the line. - for (RubyLineBreakpoint breakpoint : linesToBreakpoints.values()) { + // Update any pending breakpoints associated with this source + + for (RubyLineBreakpoint breakpoint : lineToBreakpoint.values()) { if (breakpoint.getSourceLineLocation().getSource().equals(beingLoaded)) { if (breakpoint.status == BreakpointStatus.PENDING) { - breakpoint.setError(); + final ProbeChain probeChain = findProbeChain(breakpoint.location); + if (probeChain == null) { + breakpoint.setError(); + } else { + breakpoint.attach(probeChain); + } } } } - loadedSources.add(source); beingLoaded = null; - } /** - * Notifies the manager about creation of a newly created probe chain associated with a proxy - * for an AST node at a specific location in the source text. + * Returns a {@link ProbeChain} associated with source that starts on a specified line; if there + * are more than one, return the one with the first character location. */ - public void registerProbeChain(SourceSection sourceSection, ProbeChain probeChain) { - - assert sourceSection.getSource().equals(beingLoaded); + private ProbeChain findProbeChain(SourceLineLocation lineLocation) { + ProbeChain probeChain = null; + final Set probeChains = lineToProbeChains.get(lineLocation); + if (probeChains != null) { + assert probeChains.size() > 0; + for (ProbeChain chain : probeChains) { + if (probeChain == null) { + probeChain = chain; + } else if (chain.getProbedSourceSection().getCharIndex() < probeChain.getProbedSourceSection().getCharIndex()) { + probeChain = chain; + } + } + } + return probeChain; + } - // Remember this probe chain, indexed by line number - final SourceLineLocation lineLocation = new SourceLineLocation(sourceSection.getSource(), sourceSection.getStartLine()); - linesToProbeChains.put(lineLocation, probeChain); + /** + * Remove a probe from a line location and retire it permanently. + */ + public void retireLineProbe(SourceLineLocation location, RubyLineProbe probe) { + final RubyLineBreakpoint breakpoint = lineToBreakpoint.get(location); + lineToBreakpoint.remove(location); + breakpoint.retire(probe); + } - final RubyLineBreakpoint breakpoint = linesToBreakpoints.get(lineLocation); - if (breakpoint != null && breakpoint.location.equals(lineLocation)) { - // We only register while we're loading; - // While we're loading, there should only be pending breakpoints for this source - assert breakpoint.status == BreakpointStatus.PENDING; - - // Found a line/probeChain where a pending breakpoint should be set - breakpoint.attach(probeChain); - } + @Override + public LineBreakpoint[] getBreakpoints() { + return lineToBreakpoint.values().toArray(new LineBreakpoint[0]); } @Override public RubyLineBreakpoint setBreakpoint(SourceLineLocation lineLocation) { - RubyLineBreakpoint breakpoint = linesToBreakpoints.get(lineLocation); + RubyLineBreakpoint breakpoint = lineToBreakpoint.get(lineLocation); if (breakpoint != null) { switch (breakpoint.status) { - case ATTACHED: + case ACTIVE: throw new RuntimeException("Breakpoint already set at line " + lineLocation); case PENDING: @@ -175,10 +324,10 @@ assert false; } } else { - breakpoint = new RubyLineBreakpoint(lineLocation, new RubyBreakBeforeProbe(context)); - linesToBreakpoints.put(lineLocation, breakpoint); + breakpoint = new RubyLineBreakpoint(lineLocation, new RubyBreakBeforeLineProbe(context, lineLocation, false)); + lineToBreakpoint.put(lineLocation, breakpoint); - final ProbeChain probeChain = linesToProbeChains.get(lineLocation); + final ProbeChain probeChain = findProbeChain(lineLocation); if (probeChain != null) { breakpoint.attach(probeChain); } @@ -193,30 +342,12 @@ } @Override - public LineBreakpoint[] getBreakpoints() { - return linesToBreakpoints.values().toArray(new LineBreakpoint[0]); - } - - @Override - public void removeBreakpoint(SourceLineLocation lineLocation) { - final RubyLineBreakpoint breakpoint = linesToBreakpoints.get(lineLocation); - if (breakpoint == null) { - throw new RuntimeException("No break/proc located at line " + lineLocation); - } - linesToBreakpoints.remove(lineLocation); - breakpoint.retire(); - } - - /** - * Sets a Ruby proc of no arguments to be run before a specified line is executed. - */ - public void setLineProc(SourceLineLocation lineLocation, RubyProc proc) { - - RubyLineBreakpoint breakpoint = linesToBreakpoints.get(lineLocation); + public LineBreakpoint setOneShotBreakpoint(SourceLineLocation lineLocation) { + RubyLineBreakpoint breakpoint = lineToBreakpoint.get(lineLocation); if (breakpoint != null) { switch (breakpoint.status) { - case ATTACHED: + case ACTIVE: throw new RuntimeException("Breakpoint already set at line " + lineLocation); case PENDING: @@ -227,18 +358,165 @@ assert false; } } else { - breakpoint = new RubyLineBreakpoint(lineLocation, new RubyProcBeforeProbe(context, proc)); - linesToBreakpoints.put(lineLocation, breakpoint); + breakpoint = new RubyLineBreakpoint(lineLocation, new RubyBreakBeforeLineProbe(context, lineLocation, true)); + lineToBreakpoint.put(lineLocation, breakpoint); + + final ProbeChain probeChain = findProbeChain(lineLocation); + if (probeChain != null) { + breakpoint.attach(probeChain); + } + } + + return breakpoint; + } + + public boolean hasBreakpoint(SourceLineLocation lineLocation) { + return lineToBreakpoint.get(lineLocation) != null; + } + + @Override + public void removeBreakpoint(SourceLineLocation lineLocation) { + final RubyLineBreakpoint breakpoint = lineToBreakpoint.get(lineLocation); + if (breakpoint == null) { + throw new RuntimeException("No break/proc located at line " + lineLocation); + } + lineToBreakpoint.remove(lineLocation); + breakpoint.retire(); + } + + private void removeOneShotBreakpoints() { + for (Entry entry : lineToBreakpoint.entrySet()) { + final RubyLineBreakpoint breakpoint = entry.getValue(); + if (breakpoint.probe.isOneShot()) { + lineToBreakpoint.remove(entry.getKey()); + breakpoint.retire(); + } + } + } + + /** + * Prepare to execute a "Continue": + *
    + *
  • Execution will continue until either: + *
      + *
    1. execution arrives at a node to which a breakpoint is attached, or:
    2. + *
    3. execution completes.
    4. + *
    + *
+ */ + public void setContinue() { + // Nothing to do here; "Continue" is the default, which should be restored after each halt. + } + + /** + * Prepare to execute a "Step": + *
    + *
  • User breakpoints are disabled.
  • + *
  • Execution will continue until either: + *
      + *
    1. execution arrives at a "Statement" node, or:
    2. + *
    3. execution completes.
    4. + *
    + * This status persists only through one execution, and reverts to + * {@link ExecutionMode#CONTINUE}. + *
+ */ + public void setStep(int stepCount) { + assert executionMode == ExecutionMode.CONTINUE; + disableLineBreakpoints(); + setStepping(true); + unfinishedStepCount = stepCount; + setMode(ExecutionMode.STEP); + } - final ProbeChain probeChain = linesToProbeChains.get(lineLocation); + /** + * Prepare to execute a "Next": + *
    + *
  • Execution will continue until either: + *
      + *
    1. execution arrives at a "Statement" node when not nested in one or more function/method + * calls, or:
    2. + *
    3. execution arrives at a node to which a breakpoint is attached and when nested in one or + * more function/method calls, or:
    4. + *
    5. execution completes.
    6. + *
    + * This status persists only through one execution, and reverts to + * {@link ExecutionMode#CONTINUE}. + *
+ */ + public void setNext(int nextCount) { + assert executionMode == ExecutionMode.CONTINUE; + disableLineBreakpoints(); + setStepping(true); + unfinishedNextCount = nextCount; + setMode(ExecutionMode.NEXT); + } + + private void disableLineBreakpoints() { + for (RubyLineBreakpoint breakpoint : lineToBreakpoint.values()) { + if (breakpoint.status == BreakpointStatus.ACTIVE) { + breakpoint.disable(); + } + } + } + + private void enableLineBreakpoints() { + for (RubyLineBreakpoint breakpoint : lineToBreakpoint.values()) { + if (breakpoint.status == BreakpointStatus.DISABLED) { + breakpoint.enable(); + } + } + } + + private void setStepping(boolean isStepping) { + // Set the "stepping" flag on every statement probe. + for (ProbeChain probeChain : srcToProbeChain.values()) { + if (probeChain.isMarkedAs(NodePhylum.STATEMENT)) { + probeChain.setStepping(isStepping); + } + } + } + + private void setMode(ExecutionMode mode) { + if (TRACE) { + OUT.println("DebugManager: " + executionMode.toString() + "-->" + mode.toString()); + } + executionMode = mode; + } + + /** + * Sets a Ruby proc of no arguments to be run before a specified line is executed. + */ + public void setLineProc(SourceLineLocation lineLocation, RubyProc proc) { + + RubyLineBreakpoint breakpoint = lineToBreakpoint.get(lineLocation); + + if (breakpoint != null) { + switch (breakpoint.status) { + case ACTIVE: + throw new RuntimeException("Breakpoint already set at line " + lineLocation); + + case PENDING: + case ERROR: + throw new RuntimeException("Breakpoint already pending at line " + lineLocation); + + default: + assert false; + } + } else { + breakpoint = new RubyLineBreakpoint(lineLocation, new RubyProcBeforeLineProbe(context, lineLocation, proc)); + lineToBreakpoint.put(lineLocation, breakpoint); + + final ProbeChain probeChain = findProbeChain(lineLocation); if (probeChain != null) { breakpoint.attach(probeChain); } } } + // TODO (mlvdv) rework locals (watchpoints) to work like breaks; I doubt it is even correct now /** - * Registers the chain of probes associated with a method local variable in the AST. + * Registers the chain of probes associated with a method local variable assignment in the AST. */ public void registerLocalDebugProxy(UniqueMethodIdentifier methodIdentifier, String localName, ProbeChain probeChain) { final MethodLocal methodLocal = new MethodLocal(methodIdentifier, localName); @@ -258,7 +536,7 @@ if (probe != null) { throw new RuntimeException("Breakpoint already set on method local " + methodLocal); } - probe = new RubyBreakAfterProbe(context); + probe = new RubyBreakAfterLocalProbe(context, methodLocal); localsToAttachedBreakpoints.put(methodLocal, probe); probeChain.appendProbe(probe); } @@ -277,7 +555,7 @@ if (probe != null) { throw new RuntimeException("Assignment proc already set on method local " + methodLocal); } - probe = new RubyProcAfterProbe(context, proc); + probe = new RubyProcAfterLocalProbe(context, methodLocal, proc); localsToAttachedBreakpoints.put(methodLocal, probe); probeChain.appendProbe(probe); } @@ -295,24 +573,72 @@ localsToAttachedBreakpoints.remove(methodLocal); } - /** - * Receives notification of a suspended execution context; execution resumes when this method - * returns. - * - * @param astNode a guest language AST node that represents the current execution site, assumed - * not to be any kind of {@link InstrumentationNode}, - * @param frame execution frame at the site where execution suspended - */ public void haltedAt(Node astNode, MaterializedFrame frame) { + switch (executionMode) { + case CONTINUE: + case NEXT_NESTED: + // User breakpoints should already be enabled + // Stepping should be false + nextNestedInCallNode = null; + break; + case STEP: + unfinishedStepCount--; + if (unfinishedStepCount > 0) { + return; + } + // Revert to default mode. + enableLineBreakpoints(); + setStepping(false); + break; + case NEXT: + unfinishedNextCount--; + if (unfinishedNextCount > 0) { + return; + } + // Revert to default mode. + enableLineBreakpoints(); + setStepping(false); + break; + default: + assert false; // Should not happen + break; + + } + // Clean up, just in cased the one-shot breakpoints got confused + removeOneShotBreakpoints(); + + setMode(ExecutionMode.CONTINUE); + + // Return control to the debug client context.haltedAt(astNode, frame); + } - private static final class RubyLineBreakpoint implements DebugManager.LineBreakpoint, Comparable { + private RubyProbe createReplacement(RubyProbe probe) { + // Should be a specialized replacement for any kind of probe created. + // Ugly, but there's no other way to reset the parent pointer and reuse a probe node. + if (probe instanceof RubyBreakBeforeLineProbe) { + final RubyBreakBeforeLineProbe oldProbe = (RubyBreakBeforeLineProbe) probe; + return new RubyBreakBeforeLineProbe(context, oldProbe.getLineLocation(), oldProbe.isOneShot()); + } + if (probe instanceof RubyProcBeforeLineProbe) { + final RubyProcBeforeLineProbe oldProbe = (RubyProcBeforeLineProbe) probe; + return new RubyProcBeforeLineProbe(context, oldProbe.getLineLocation(), oldProbe.getProc()); + } + assert false; + return null; + } + + /** + * A breakpoint of the sort that would be created by a client, with a life-cycle represented by + * {@link BreakpointStatus}. + */ + private final class RubyLineBreakpoint implements DebugManager.LineBreakpoint, Comparable { private final SourceLineLocation location; private RubyProbe probe; // non-null until RETIRED, but may get replaced. - private ProbeChain probeChain = null; // only non-null when ATTACHED + private ProbeChain probeChain = null; private BreakpointStatus status = BreakpointStatus.PENDING; public RubyLineBreakpoint(SourceLineLocation location, RubyProbe probe) { @@ -332,7 +658,11 @@ @Override public String getDebugStatus() { - return status == null ? "" : status.name; + String result = status == null ? "" : status.name; + if (probe.isOneShot()) { + result = result + ", " + "One-Shot"; + } + return result; } private void attach(ProbeChain chain) { @@ -341,27 +671,29 @@ probeChain = chain; probeChain.appendProbe(probe); - status = BreakpointStatus.ATTACHED; + status = BreakpointStatus.ACTIVE; + } + + private void disable() { + assert status == BreakpointStatus.ACTIVE; + + probeChain.removeProbe(probe); + status = BreakpointStatus.DISABLED; + } + + private void enable() { + assert status == BreakpointStatus.DISABLED; + + // Can't re-attach to probe chain, because can't re-assign parent. + probe = createReplacement(probe); + probeChain.appendProbe(probe); + status = BreakpointStatus.ACTIVE; } private void setPending() { - switch (status) { - case ATTACHED: - detach(); - // TODO (mlvdv) replace the probe - status = BreakpointStatus.PENDING; - break; - case ERROR: - status = BreakpointStatus.PENDING; - break; - case PENDING: - break; - case RETIRED: - assert false; - break; - default: - assert false; - } + assert status == BreakpointStatus.ERROR; + + status = BreakpointStatus.PENDING; } public void setError() { @@ -370,18 +702,9 @@ status = BreakpointStatus.ERROR; } - private void detach() { - assert status == BreakpointStatus.ATTACHED; - - probeChain.removeProbe(probe); - probeChain = null; - - status = BreakpointStatus.PENDING; - } - private void retire() { - if (probeChain != null) { + if (status == BreakpointStatus.ACTIVE) { probeChain.removeProbe(probe); } probe = null; @@ -389,6 +712,151 @@ status = BreakpointStatus.RETIRED; } + + private void retire(RubyProbe retiredProbe) { + + assert this.probe == retiredProbe; + retire(); + } + } + + private static final class CallRecord { + + final SourceSection section; + @SuppressWarnings("unused") final String name; + final CallRecord predecessor; + + public CallRecord(SourceSection section, String name, CallRecord predecessor) { + this.section = section; + this.name = name; + this.predecessor = predecessor; + } + } + + private CallRecord callStack = null; + + public void notifyCallEntry(Node astNode, String name) { + if (TRACE) { + OUT.println("DebugManager: ENTER \"" + name + "\" " + nodeToString(astNode)); + } + if (executionMode == ExecutionMode.NEXT && nextNestedInCallNode == null) { + // In "Next" mode, where we have been "stepping", but are about to enter a call. + // Switch modes to be like "Continue" until/if return from this call + nextNestedInCallNode = astNode; + enableLineBreakpoints(); + setStepping(false); + setMode(ExecutionMode.NEXT_NESTED); + } + + callStack = new CallRecord(astNode.getSourceSection(), name, callStack); + } + + public void notifyCallExit(Node astNode, String name) { + if (TRACE) { + OUT.println("DebugManager: EXIT \"" + name + "\" " + nodeToString(astNode)); + } + + if (executionMode == ExecutionMode.NEXT_NESTED) { + assert nextNestedInCallNode != null; + if (nextNestedInCallNode == astNode) { + // In "Next" mode while nested in a function/method call, but about to return. + // Switch modes to be like "Step" until/if enter another function/method call. + nextNestedInCallNode = null; + disableLineBreakpoints(); + setStepping(true); + setMode(ExecutionMode.NEXT); + } + } + + final SourceSection section = astNode.getSourceSection(); + if (section instanceof NullSourceSection) { + if (TRACE) { + OUT.println("Ignoring call exit \"" + name + "\" " + nodeToString(astNode)); + } + } + callStack = callStack.predecessor; + } + + /** + * Sets a one-shot breakpoint to halt just after the completion of the call site at the top of + * the current call stack. + */ + public boolean setReturnBreakpoint() { + if (callStack == null) { + return false; + } + final SourceLineLocation lineLocation = new SourceLineLocation(callStack.section); + RubyLineBreakpoint breakpoint = lineToBreakpoint.get(lineLocation); + if (breakpoint != null) { + return true; + } + final ProbeChain probeChain = findProbeChain(lineLocation); + if (probeChain != null) { + breakpoint = new RubyLineBreakpoint(lineLocation, new RubyBreakAfterLineProbe(context, lineLocation, true)); + lineToBreakpoint.put(lineLocation, breakpoint); + breakpoint.attach(probeChain); + return true; + } + return false; + } + + /** + * Notifies that a new execution is about to start, i.e. running a program or an eval. + */ + @SuppressWarnings("static-method") + public void startExecution(String name) { + if (TRACE) { + OUT.println("RubyDebugManager: START " + name); + } + // TODO (mlvdv) push the current call stack onto a stack; start new empty call stack + } + + /** + * Notifies that the current execution has ended. + */ + public void endExecution(String name) { + if (TRACE) { + OUT.println("RubyDebugManager: END " + name); + } + + // TODO (mlvdv) pop the current call stack, restore previous + + switch (executionMode) { + case CONTINUE: + case NEXT_NESTED: + // User breakpoints should already be enabled + // Stepping should be false + nextNestedInCallNode = null; + break; + case STEP: + // Revert to default mode. + enableLineBreakpoints(); + setStepping(false); + unfinishedStepCount = 0; + break; + case NEXT: + // Revert to default mode. + enableLineBreakpoints(); + setStepping(false); + unfinishedNextCount = 0; + break; + default: + assert false; // Should not happen + break; + } + // Clean up, just in cased the one-shot breakpoints got confused + removeOneShotBreakpoints(); + + setMode(ExecutionMode.CONTINUE); + } + + @SuppressWarnings("static-method") + private String nodeToString(Node astNode) { + final SourceSection sourceSection = astNode.getSourceSection(); + if (sourceSection != null) { + return Integer.toString(sourceSection.getStartLine()) + ":" + astNode; + } + return astNode.toString(); } } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyLineProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyLineProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A Ruby probe situated at a Ruby "line". + */ +public abstract class RubyLineProbe extends RubyProbe { + + protected final SourceLineLocation location; + + /** + * Creates a probe that will cause a halt just before child execution starts; a {@code oneShot} + * probe will remove itself the first time it halts. + */ + public RubyLineProbe(RubyContext context, SourceLineLocation location, boolean oneShot) { + super(context, oneShot); + this.location = location; + } + + public SourceLineLocation getLineLocation() { + return location; + } + +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyLocalProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyLocalProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.ruby.runtime.*; + +/** + * A Ruby probe situated at a Ruby local assignment. + */ +public abstract class RubyLocalProbe extends RubyProbe { + + protected final MethodLocal local; + + public RubyLocalProbe(RubyContext context, MethodLocal local, boolean oneShot) { + super(context, oneShot); + this.local = local; + } + +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProbe.java --- a/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProbe.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -18,14 +18,24 @@ */ public abstract class RubyProbe extends InstrumentationProbeNode.DefaultProbeNode { + protected final boolean oneShot; + protected final RubyContext context; - public RubyProbe(RubyContext context) { + /** + * OneShot is this a one-shot (self-removing) probe? + */ + public RubyProbe(RubyContext context, boolean oneShot) { + super(context); + this.oneShot = oneShot; this.context = context; - assert context != null; } - public RubyContext getContext() { - return context; + /** + * Is this a one-shot (self-removing) probe? If so, it will remove itself the first time + * activated. + */ + public boolean isOneShot() { + return oneShot; } } diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcAfterLineProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcAfterLineProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * A probe for instrumenting a Ruby program with a Ruby procedure to run on the return value from + * node execution at a line. + */ +public final class RubyProcAfterLineProbe extends RubyLineProbe { + + private final RubyProc proc; + + public RubyProcAfterLineProbe(RubyContext context, SourceLineLocation location, RubyProc proc) { + super(context, location, false); + this.proc = proc; + } + + @Override + public void leave(Node astNode, VirtualFrame frame) { + proc.call(frame.pack()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, boolean result) { + proc.call(frame.pack(), result); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, int result) { + proc.call(frame.pack(), result); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, double result) { + proc.call(frame.pack(), result); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, Object result) { + proc.call(frame.pack(), result); + } + + @Override + public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + proc.call(frame.pack()); + } +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcAfterLocalProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcAfterLocalProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * A probe for instrumenting a Ruby program with a Ruby procedure to run on the return value from a + * local assignment. + */ +public final class RubyProcAfterLocalProbe extends RubyLocalProbe { + + private final RubyProc proc; + + public RubyProcAfterLocalProbe(RubyContext context, MethodLocal local, RubyProc proc) { + super(context, local, false); + this.proc = proc; + } + + @Override + public void leave(Node astNode, VirtualFrame frame) { + proc.call(frame.pack()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, boolean result) { + proc.call(frame.pack(), result); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, int result) { + proc.call(frame.pack(), result); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, double result) { + proc.call(frame.pack(), result); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, Object result) { + proc.call(frame.pack(), result); + } + + @Override + public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + proc.call(frame.pack()); + } +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcAfterProbe.java --- a/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcAfterProbe.java Wed Jan 08 22:59:53 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This - * code is released under a tri EPL/GPL/LGPL license. You can use it, - * redistribute it and/or modify it under the terms of the: - * - * Eclipse Public License version 1.0 - * GNU General Public License version 2 - * GNU Lesser General Public License version 2.1 - */ -package com.oracle.truffle.ruby.runtime.debug; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.ruby.runtime.*; -import com.oracle.truffle.ruby.runtime.core.*; - -/** - * A probe for instrumenting a Ruby program with a Ruby procedure to run on the return value from - * node execution. - */ -public final class RubyProcAfterProbe extends RubyProbe { - - private final RubyProc proc; - - public RubyProcAfterProbe(RubyContext context, RubyProc proc) { - super(context); - this.proc = proc; - } - - @Override - public void leave(Node astNode, VirtualFrame frame) { - proc.call(frame.pack()); - } - - @Override - public void leave(Node astNode, VirtualFrame frame, boolean result) { - proc.call(frame.pack(), result); - } - - @Override - public void leave(Node astNode, VirtualFrame frame, int result) { - proc.call(frame.pack(), result); - } - - @Override - public void leave(Node astNode, VirtualFrame frame, double result) { - proc.call(frame.pack(), result); - } - - @Override - public void leave(Node astNode, VirtualFrame frame, Object result) { - proc.call(frame.pack(), result); - } - - @Override - public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { - proc.call(frame.pack()); - } -} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcBeforeLineProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcBeforeLineProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * A probe for instrumenting a Ruby program with a Ruby procedure to run before a calling a child + * node at a line. + */ +public final class RubyProcBeforeLineProbe extends RubyLineProbe { + + private final RubyProc proc; + + public RubyProcBeforeLineProbe(RubyContext context, SourceLineLocation location, RubyProc proc) { + super(context, location, false); + this.proc = proc; + } + + @Override + public void enter(Node astNode, VirtualFrame frame) { + proc.call(frame.pack()); + } + + public RubyProc getProc() { + return proc; + } + +} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcBeforeProbe.java --- a/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcBeforeProbe.java Wed Jan 08 22:59:53 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This - * code is released under a tri EPL/GPL/LGPL license. You can use it, - * redistribute it and/or modify it under the terms of the: - * - * Eclipse Public License version 1.0 - * GNU General Public License version 2 - * GNU Lesser General Public License version 2.1 - */ -package com.oracle.truffle.ruby.runtime.debug; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.ruby.runtime.*; -import com.oracle.truffle.ruby.runtime.core.*; - -/** - * A probe for instrumenting a Ruby program with a Ruby procedure to run before a call. - */ -public final class RubyProcBeforeProbe extends RubyProbe { - - private final RubyProc proc; - - public RubyProcBeforeProbe(RubyContext context, RubyProc proc) { - super(context); - this.proc = proc; - } - - @Override - public void enter(Node astNode, VirtualFrame frame) { - proc.call(frame.pack()); - } - -} diff -r 4fc8c8bb4c32 -r d7af2296cebb graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyTraceProbe.java --- a/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyTraceProbe.java Wed Jan 08 22:59:53 2014 +0100 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyTraceProbe.java Wed Jan 08 15:49:18 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * @@ -26,7 +26,7 @@ @CompilerDirectives.CompilationFinal private boolean tracingEverEnabled = false; public RubyTraceProbe(RubyContext context) { - super(context); + super(context, false); this.notTracingAssumption = context.getTraceManager().getNotTracingAssumption(); }