From c1533dad8eb45512fc3f8750b92196222c3f8e85 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 14 Sep 2023 14:38:50 +0200 Subject: [PATCH 1/6] Make IdExecutionEventNode smaller by having a reference to factory and its fields --- .../instrument/IdExecutionInstrument.java | 553 ++++++++---------- 1 file changed, 260 insertions(+), 293 deletions(-) diff --git a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java index 5a0bdff7d8d6..d9b2a3430b23 100644 --- a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java +++ b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java @@ -1,26 +1,17 @@ package org.enso.interpreter.instrument; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.RootCallTarget; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.exception.AbstractTruffleException; -import com.oracle.truffle.api.frame.FrameInstance; -import com.oracle.truffle.api.frame.FrameInstanceVisitor; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrumentation.*; -import com.oracle.truffle.api.interop.InteropException; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.nodes.Node; import java.util.HashMap; import java.util.Map; import java.util.UUID; +import java.util.function.Consumer; import org.enso.interpreter.instrument.profiling.ExecutionTime; import org.enso.interpreter.instrument.profiling.ProfilingInfo; +import org.enso.interpreter.node.ClosureRootNode; import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode; import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode; +import org.enso.interpreter.runtime.Module; import org.enso.interpreter.runtime.callable.UnresolvedSymbol; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.control.TailCallException; @@ -29,15 +20,32 @@ import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.state.State; +import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag; import org.enso.interpreter.runtime.tag.IdentifiedTag; import org.enso.interpreter.runtime.type.Constants; -import org.enso.interpreter.runtime.Module; -import java.util.function.Consumer; -import org.enso.interpreter.node.ClosureRootNode; -import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.exception.AbstractTruffleException; +import com.oracle.truffle.api.frame.FrameInstance; +import com.oracle.truffle.api.frame.FrameInstanceVisitor; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrumentation.EventBinding; +import com.oracle.truffle.api.instrumentation.EventContext; +import com.oracle.truffle.api.instrumentation.ExecutionEventNode; +import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory; +import com.oracle.truffle.api.instrumentation.SourceSectionFilter; +import com.oracle.truffle.api.instrumentation.StandardTags; +import com.oracle.truffle.api.instrumentation.TruffleInstrument; +import com.oracle.truffle.api.interop.InteropException; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.nodes.Node; -/** An instrument for getting values from AST-identified expressions. */ +/** + * An instrument for getting values from AST-identified expressions. + */ @TruffleInstrument.Registration( id = IdExecutionService.INSTRUMENT_ID, services = IdExecutionService.class) @@ -59,81 +67,20 @@ protected void onCreate(Env env) { /** Factory for creating new id event nodes **/ private static class IdEventNodeFactory implements ExecutionEventNodeFactory { - private final CallTarget entryCallTarget; - private final Consumer functionCallCallback; - private final Consumer onComputedCallback; - private final Consumer onCachedCallback; - private final Consumer onExceptionalCallback; - private final RuntimeCache cache; - private final MethodCallsCache methodCallsCache; - private final UpdatesSynchronizationState syncState; - private final UUID nextExecutionItem; - private final Map calls = new HashMap<>(); - private final Timer timer; - - /** - * Creates a new event node factory. - * - * @param entryCallTarget the call target being observed. - * @param cache the precomputed expression values. - * @param methodCallsCache the storage tracking the executed method calls. - * @param syncState the synchronization state of runtime updates. - * @param nextExecutionItem the next item scheduled for execution. - * @param functionCallCallback the consumer of function call events. - * @param onComputedCallback the consumer of the computed value events. - * @param onCachedCallback the consumer of the cached value events. - * @param onExceptionalCallback the consumer of the exceptional events. - * @param timer the timer for timing execution - */ - public IdEventNodeFactory( - CallTarget entryCallTarget, - RuntimeCache cache, - MethodCallsCache methodCallsCache, - UpdatesSynchronizationState syncState, - UUID nextExecutionItem, // The expression ID - Consumer functionCallCallback, - Consumer onComputedCallback, - Consumer onCachedCallback, - Consumer onExceptionalCallback, - Timer timer) { - this.entryCallTarget = entryCallTarget; - this.cache = cache; - this.methodCallsCache = methodCallsCache; - this.syncState = syncState; - this.nextExecutionItem = nextExecutionItem; - this.functionCallCallback = functionCallCallback; - this.onComputedCallback = onComputedCallback; - this.onCachedCallback = onCachedCallback; - this.onExceptionalCallback = onExceptionalCallback; - this.timer = timer; - } - - @Override - public ExecutionEventNode create(EventContext context) { - return new IdExecutionEventNode(context, entryCallTarget, cache, methodCallsCache, syncState, - nextExecutionItem, calls, functionCallCallback, onComputedCallback, onCachedCallback, onExceptionalCallback, timer); - } - } - - /** The execution event node class used by this instrument. */ - private static class IdExecutionEventNode extends ExecutionEventNode { - private final EventContext context; private final CallTarget entryCallTarget; private final Consumer functionCallCallback; private final Consumer onComputedCallback; private final Consumer onCachedCallback; private final Consumer onExceptionalCallback; private final RuntimeCache cache; - private final MethodCallsCache callsCache; + private final MethodCallsCache methodCallsCache; private final UpdatesSynchronizationState syncState; private final UUID nextExecutionItem; - private final Map calls; + private final Map calls = new HashMap<>(); private final Timer timer; - private long nanoTimeElapsed = 0; - private @Child TypeOfNode typeOfNode = TypeOfNode.build(); /** - * Creates a new event node. + * Creates a new event node factory. * * @param entryCallTarget the call target being observed. * @param cache the precomputed expression values. @@ -146,24 +93,21 @@ private static class IdExecutionEventNode extends ExecutionEventNode { * @param onExceptionalCallback the consumer of the exceptional events. * @param timer the timer for timing execution */ - public IdExecutionEventNode( - EventContext context, - CallTarget entryCallTarget, - RuntimeCache cache, - MethodCallsCache methodCallsCache, - UpdatesSynchronizationState syncState, - UUID nextExecutionItem, // The expression ID - Map calls, - Consumer functionCallCallback, - Consumer onComputedCallback, - Consumer onCachedCallback, - Consumer onExceptionalCallback, - Timer timer) { - this.context = context; + IdEventNodeFactory( + CallTarget entryCallTarget, + RuntimeCache cache, + MethodCallsCache methodCallsCache, + UpdatesSynchronizationState syncState, + UUID nextExecutionItem, // The expression ID + Consumer functionCallCallback, + Consumer onComputedCallback, + Consumer onCachedCallback, + Consumer onExceptionalCallback, + Timer timer + ) { this.entryCallTarget = entryCallTarget; this.cache = cache; - this.calls = calls; - this.callsCache = methodCallsCache; + this.methodCallsCache = methodCallsCache; this.syncState = syncState; this.nextExecutionItem = nextExecutionItem; this.functionCallCallback = functionCallCallback; @@ -174,212 +118,236 @@ public IdExecutionEventNode( } @Override - public Object onUnwind(VirtualFrame frame, Object info) { - return info; + public ExecutionEventNode create(EventContext context) { + return new IdExecutionEventNode(context); } - @Override - public void onEnter(VirtualFrame frame) { - if (!isTopFrame(entryCallTarget)) { - return; - } - onEnterImpl(); - } + /** + * The execution event node class used by this instrument. + */ + private class IdExecutionEventNode extends ExecutionEventNode { + + private final EventContext context; + private long nanoTimeElapsed = 0; + private @Child + TypeOfNode typeOfNode = TypeOfNode.build(); - @CompilerDirectives.TruffleBoundary - private void onEnterImpl() { - UUID nodeId = getNodeId(context.getInstrumentedNode()); - - // Add a flag to say it was cached. - // An array of `ProfilingInfo` in the value update. - - Object result = cache.get(nodeId); - // When executing the call stack we need to capture the FunctionCall of the next (top) stack - // item in the `functionCallCallback`. We allow to execute the cached `stackTop` value to be - // able to continue the stack execution, and unwind later from the `onReturnValue` callback. - if (result != null && !nodeId.equals(nextExecutionItem)) { - - onCachedCallback.accept( - new ExpressionValue( - nodeId, - result, - cache.getType(nodeId), - typeOf(result), - calls.get(nodeId), - cache.getCall(nodeId), - new ProfilingInfo[] {ExecutionTime.empty()}, - true)); - throw context.createUnwind(result); + /** + * Creates a new event node. + * + * @param context location where the node is being inserted + */ + IdExecutionEventNode(EventContext context) { + this.context = context; } - nanoTimeElapsed = timer.getTime(); - } + @Override + public Object onUnwind(VirtualFrame frame, Object info) { + return info; + } - /** - * Triggered when a node (either a function call sentry or an identified expression) finishes - * execution. - * - * @param frame the current execution frame. - * @param result the result of executing the node this method was triggered for. - */ - @Override - public void onReturnValue(VirtualFrame frame, Object result) { - nanoTimeElapsed = timer.getTime() - nanoTimeElapsed; - if (!isTopFrame(entryCallTarget)) { - return; + @Override + public void onEnter(VirtualFrame frame) { + if (!isTopFrame(entryCallTarget)) { + return; + } + onEnterImpl(); } - Node node = context.getInstrumentedNode(); - - if (node instanceof FunctionCallInstrumentationNode - && result instanceof FunctionCallInstrumentationNode.FunctionCall functionCall) { - UUID nodeId = ((FunctionCallInstrumentationNode) node).getId(); - onFunctionReturn(nodeId, functionCall, context); - } else if (node instanceof ExpressionNode) { - onExpressionReturn(result, node, context); + + @CompilerDirectives.TruffleBoundary + private void onEnterImpl() { + UUID nodeId = getNodeId(context.getInstrumentedNode()); + + // Add a flag to say it was cached. + // An array of `ProfilingInfo` in the value update. + Object result = cache.get(nodeId); + // When executing the call stack we need to capture the FunctionCall of the next (top) stack + // item in the `functionCallCallback`. We allow to execute the cached `stackTop` value to be + // able to continue the stack execution, and unwind later from the `onReturnValue` callback. + if (result != null && !nodeId.equals(nextExecutionItem)) { + var value = new ExpressionValue( + nodeId, + result, + cache.getType(nodeId), + typeOf(result), + calls.get(nodeId), + cache.getCall(nodeId), + new ProfilingInfo[]{ExecutionTime.empty()}, + true + ); + onCachedCallback.accept(value); + throw context.createUnwind(result); + } + + nanoTimeElapsed = timer.getTime(); } - } - @Override - public void onReturnExceptional(VirtualFrame frame, Throwable exception) { - if (exception instanceof TailCallException) { - onTailCallReturn(exception, Function.ArgumentsHelper.getState(frame.getArguments())); - } else if (exception instanceof PanicException panicException) { - onReturnValue(frame, new PanicSentinel(panicException, context.getInstrumentedNode())); - } else if (exception instanceof AbstractTruffleException) { - onReturnValue(frame, exception); + /** + * Triggered when a node (either a function call sentry or an identified + * expression) finishes execution. + * + * @param frame the current execution frame. + * @param result the result of executing the node this method was + * triggered for. + */ + @Override + public void onReturnValue(VirtualFrame frame, Object result) { + nanoTimeElapsed = timer.getTime() - nanoTimeElapsed; + if (!isTopFrame(entryCallTarget)) { + return; + } + Node node = context.getInstrumentedNode(); + + if (node instanceof FunctionCallInstrumentationNode + && result instanceof FunctionCallInstrumentationNode.FunctionCall functionCall) { + UUID nodeId = ((FunctionCallInstrumentationNode) node).getId(); + onFunctionReturn(nodeId, functionCall, context); + } else if (node instanceof ExpressionNode) { + onExpressionReturn(result, node, context); + } } - } - private void onExpressionReturn(Object result, Node node, EventContext context) throws ThreadDeath { - boolean isPanic = result instanceof AbstractTruffleException && !(result instanceof DataflowError); - UUID nodeId = ((ExpressionNode) node).getId(); - - String resultType = typeOf(result); - String cachedType = cache.getType(nodeId); - FunctionCallInfo call = functionCallInfoById(nodeId); - FunctionCallInfo cachedCall = cache.getCall(nodeId); - ProfilingInfo[] profilingInfo = new ProfilingInfo[] {new ExecutionTime(nanoTimeElapsed)}; - - ExpressionValue expressionValue = - new ExpressionValue(nodeId, result, resultType, cachedType, call, cachedCall, profilingInfo, false); - syncState.setExpressionUnsync(nodeId); - syncState.setVisualizationUnsync(nodeId); - - // Panics are not cached because a panic can be fixed by changing seemingly unrelated code, - // like imports, and the invalidation mechanism can not always track those changes and - // appropriately invalidate all dependent expressions. - if (!isPanic) { - cache.offer(nodeId, result); - cache.putCall(nodeId, call); + @Override + public void onReturnExceptional(VirtualFrame frame, Throwable exception) { + if (exception instanceof TailCallException) { + onTailCallReturn(exception, Function.ArgumentsHelper.getState(frame.getArguments())); + } else if (exception instanceof PanicException panicException) { + onReturnValue(frame, new PanicSentinel(panicException, context.getInstrumentedNode())); + } else if (exception instanceof AbstractTruffleException) { + onReturnValue(frame, exception); + } } - cache.putType(nodeId, resultType); - - passExpressionValueToCallback(expressionValue); - if (isPanic) { - // We mark the node as executed so that it is not reported as not executed call after the - // program execution is complete. If we clear the call from the cache instead, it will mess - // up the `typeChanged` field of the expression update. - callsCache.setExecuted(nodeId); - throw context.createUnwind(result); + + private void onExpressionReturn(Object result, Node node, EventContext context) throws ThreadDeath { + boolean isPanic = result instanceof AbstractTruffleException && !(result instanceof DataflowError); + UUID nodeId = ((ExpressionNode) node).getId(); + + String resultType = typeOf(result); + String cachedType = cache.getType(nodeId); + FunctionCallInfo call = functionCallInfoById(nodeId); + FunctionCallInfo cachedCall = cache.getCall(nodeId); + ProfilingInfo[] profilingInfo = new ProfilingInfo[]{new ExecutionTime(nanoTimeElapsed)}; + + ExpressionValue expressionValue + = new ExpressionValue(nodeId, result, resultType, cachedType, call, cachedCall, profilingInfo, false); + syncState.setExpressionUnsync(nodeId); + syncState.setVisualizationUnsync(nodeId); + + // Panics are not cached because a panic can be fixed by changing seemingly unrelated code, + // like imports, and the invalidation mechanism can not always track those changes and + // appropriately invalidate all dependent expressions. + if (!isPanic) { + cache.offer(nodeId, result); + cache.putCall(nodeId, call); + } + cache.putType(nodeId, resultType); + + passExpressionValueToCallback(expressionValue); + if (isPanic) { + // We mark the node as executed so that it is not reported as not executed call after the + // program execution is complete. If we clear the call from the cache instead, it will mess + // up the `typeChanged` field of the expression update. + methodCallsCache.setExecuted(nodeId); + throw context.createUnwind(result); + } } - } - private String typeOf(Object value) { - String resultType; - if (value instanceof UnresolvedSymbol) { + private String typeOf(Object value) { + String resultType; + if (value instanceof UnresolvedSymbol) { resultType = Constants.UNRESOLVED_SYMBOL; - } else { + } else { Object typeResult = typeOfNode.execute(value); if (typeResult instanceof Type t) { - resultType = t.getQualifiedName().toString(); + resultType = t.getQualifiedName().toString(); } else { - resultType = null; + resultType = null; } + } + return resultType; } - return resultType; - } - @CompilerDirectives.TruffleBoundary - private void passExpressionValueToCallback(ExpressionValue expressionValue) { + @CompilerDirectives.TruffleBoundary + private void passExpressionValueToCallback(ExpressionValue expressionValue) { onComputedCallback.accept(expressionValue); - } + } - @CompilerDirectives.TruffleBoundary - private FunctionCallInfo functionCallInfoById(UUID nodeId) { + @CompilerDirectives.TruffleBoundary + private FunctionCallInfo functionCallInfoById(UUID nodeId) { return calls.get(nodeId); - } + } - @CompilerDirectives.TruffleBoundary - private void onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall result, EventContext context) throws ThreadDeath { + @CompilerDirectives.TruffleBoundary + private void onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall result, EventContext context) throws ThreadDeath { calls.put(nodeId, FunctionCallInfo.fromFunctionCall(result)); functionCallCallback.accept(new ExpressionCall(nodeId, result)); // Return cached value after capturing the enterable function call in `functionCallCallback` Object cachedResult = cache.get(nodeId); if (cachedResult != null) { - throw context.createUnwind(cachedResult); + throw context.createUnwind(cachedResult); } - callsCache.setExecuted(nodeId); - } + methodCallsCache.setExecuted(nodeId); + } - @CompilerDirectives.TruffleBoundary - private void onTailCallReturn(Throwable exception, State state) { + @CompilerDirectives.TruffleBoundary + private void onTailCallReturn(Throwable exception, State state) { try { - TailCallException tailCallException = (TailCallException) exception; - FunctionCallInstrumentationNode.FunctionCall functionCall = - new FunctionCallInstrumentationNode.FunctionCall( - tailCallException.getFunction(), - state, - tailCallException.getArguments()); - Object result = InteropLibrary.getFactory().getUncached().execute(functionCall); - onReturnValue(null, result); + TailCallException tailCallException = (TailCallException) exception; + FunctionCallInstrumentationNode.FunctionCall functionCall + = new FunctionCallInstrumentationNode.FunctionCall( + tailCallException.getFunction(), + state, + tailCallException.getArguments()); + Object result = InteropLibrary.getFactory().getUncached().execute(functionCall); + onReturnValue(null, result); } catch (InteropException e) { - onExceptionalCallback.accept(e); + onExceptionalCallback.accept(e); } - } - - /** - * Checks if we're not inside a recursive call, i.e. the {@link #entryCallTarget} only appears - * in the stack trace once. - * - * @return {@code true} if it's not a recursive call, {@code false} otherwise. - */ - private boolean isTopFrame(CallTarget entryCallTarget) { - Object result = - Truffle.getRuntime() - .iterateFrames( - new FrameInstanceVisitor() { - boolean seenFirst = false; - - @Override - public Object visitFrame(FrameInstance frameInstance) { - CallTarget ct = frameInstance.getCallTarget(); - if (ct != entryCallTarget) { - return null; - } - if (seenFirst) { - return new Object(); - } else { - seenFirst = true; - return null; - } - } - }); - return result == null; - } + } - private UUID getNodeId(Node node) { - if (node instanceof ExpressionNode) { - return ((ExpressionNode) node).getId(); + /** + * Checks if we're not inside a recursive call, i.e. the + * {@link #entryCallTarget} only appears in the stack trace once. + * + * @return {@code true} if it's not a recursive call, {@code false} + * otherwise. + */ + private boolean isTopFrame(CallTarget entryCallTarget) { + Object result = Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() { + boolean seenFirst = false; + + @Override + public Object visitFrame(FrameInstance frameInstance) { + CallTarget ct = frameInstance.getCallTarget(); + if (ct != entryCallTarget) { + return null; + } + if (seenFirst) { + return new Object(); + } else { + seenFirst = true; + return null; + } + } + }); + return result == null; } - if (node instanceof FunctionCallInstrumentationNode) { - return ((FunctionCallInstrumentationNode) node).getId(); + + private UUID getNodeId(Node node) { + if (node instanceof ExpressionNode) { + return ((ExpressionNode) node).getId(); + } + if (node instanceof FunctionCallInstrumentationNode) { + return ((FunctionCallInstrumentationNode) node).getId(); + } + return null; } - return null; } } /** - * Attach a new event node factory to observe identified nodes within given function. + * Attach a new event node factory to observe identified nodes within given + * function. * * @param module module that contains the code * @param entryCallTarget the call target being observed. @@ -396,44 +364,43 @@ private UUID getNodeId(Node node) { */ @Override public EventBinding bind( - Module module, - CallTarget entryCallTarget, - RuntimeCache cache, - MethodCallsCache methodCallsCache, - UpdatesSynchronizationState syncState, - Timer timer, - UUID nextExecutionItem, - Consumer functionCallCallback, - Consumer onComputedCallback, - Consumer onCachedCallback, - Consumer onExceptionalCallback) { + Module module, + CallTarget entryCallTarget, + RuntimeCache cache, + MethodCallsCache methodCallsCache, + UpdatesSynchronizationState syncState, + Timer timer, + UUID nextExecutionItem, + Consumer functionCallCallback, + Consumer onComputedCallback, + Consumer onCachedCallback, + Consumer onExceptionalCallback + ) { var builder = SourceSectionFilter.newBuilder() - .tagIs(StandardTags.ExpressionTag.class, StandardTags.CallTag.class) - .tagIs(IdentifiedTag.class) - .tagIsNot(AvoidIdInstrumentationTag.class) - .sourceIs(module::isModuleSource); + .tagIs(StandardTags.ExpressionTag.class, StandardTags.CallTag.class) + .tagIs(IdentifiedTag.class) + .tagIsNot(AvoidIdInstrumentationTag.class) + .sourceIs(module::isModuleSource); if (entryCallTarget instanceof RootCallTarget r && r.getRootNode() instanceof ClosureRootNode c && c.getSourceSection() != null) { final int firstFunctionLine = c.getSourceSection().getStartLine(); final int afterFunctionLine = c.getSourceSection().getEndLine() + 1; builder.lineIn(SourceSectionFilter.IndexRange.between(firstFunctionLine, afterFunctionLine)); } - SourceSectionFilter filter = builder.build(); - - return env.getInstrumenter() - .attachExecutionEventFactory( - filter, - new IdEventNodeFactory( - entryCallTarget, - cache, - methodCallsCache, - syncState, - nextExecutionItem, - functionCallCallback, - onComputedCallback, - onCachedCallback, - onExceptionalCallback, - timer)); + var filter = builder.build(); + + var factory = new IdEventNodeFactory( + entryCallTarget, + cache, + methodCallsCache, + syncState, + nextExecutionItem, + functionCallCallback, + onComputedCallback, + onCachedCallback, + onExceptionalCallback, + timer + ); + return env.getInstrumenter().attachExecutionEventFactory(filter, factory); } - } From e617fd6f3222a9bd7bd996e88cacb4ba7cdba036 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 14 Sep 2023 15:48:12 +0200 Subject: [PATCH 2/6] Isolating manipulation with caches into Callbacks class --- .../instrument/IdExecutionService.java | 14 +- .../interpreter/service/ExecutionService.java | 45 ++-- .../instrument/IdExecutionInstrument.java | 238 ++++++++++-------- 3 files changed, 166 insertions(+), 131 deletions(-) diff --git a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java index 16d3fd03a420..9abd55bddf4c 100644 --- a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java +++ b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java @@ -1,13 +1,10 @@ package org.enso.interpreter.instrument; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.instrumentation.EventBinding; -import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory; -import com.oracle.truffle.api.nodes.RootNode; import java.util.Arrays; import java.util.Objects; import java.util.UUID; import java.util.function.Consumer; + import org.enso.interpreter.instrument.profiling.ProfilingInfo; import org.enso.interpreter.node.MethodRootNode; import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode; @@ -21,6 +18,11 @@ import org.enso.logger.masking.MaskedString; import org.enso.pkg.QualifiedName; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.instrumentation.EventBinding; +import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory; +import com.oracle.truffle.api.nodes.RootNode; + public interface IdExecutionService { String INSTRUMENT_ID = "id-value-extractor"; @@ -54,7 +56,7 @@ EventBinding bind( Consumer onExceptionalCallback); /** A class for notifications about functions being called in the course of execution. */ - class ExpressionCall { + final class ExpressionCall { private final UUID expressionId; private final FunctionCallInstrumentationNode.FunctionCall call; @@ -81,7 +83,7 @@ public FunctionCallInstrumentationNode.FunctionCall getCall() { } /** A class for notifications about identified expressions' values being computed. */ - class ExpressionValue { + final class ExpressionValue { private final UUID expressionId; private final Object value; private final String type; diff --git a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionService.java b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionService.java index 84950b7400e0..a913c05f47be 100644 --- a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionService.java +++ b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionService.java @@ -1,27 +1,19 @@ package org.enso.interpreter.service; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.TruffleLogger; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrumentation.EventBinding; -import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory; -import com.oracle.truffle.api.interop.ArityException; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.UnknownIdentifierException; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.interop.UnsupportedTypeException; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.SourceSection; +import java.io.File; +import java.io.IOException; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Consumer; + import org.enso.compiler.context.SimpleUpdate; import org.enso.interpreter.instrument.Endpoint; import org.enso.interpreter.instrument.IdExecutionService; import org.enso.interpreter.instrument.MethodCallsCache; import org.enso.interpreter.instrument.NotificationHandler; import org.enso.interpreter.instrument.RuntimeCache; -import org.enso.interpreter.instrument.UpdatesSynchronizationState; import org.enso.interpreter.instrument.Timer; +import org.enso.interpreter.instrument.UpdatesSynchronizationState; import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode; import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNodeGen; import org.enso.interpreter.runtime.EnsoContext; @@ -31,28 +23,37 @@ import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.scope.ModuleScope; import org.enso.interpreter.runtime.state.State; -import org.enso.interpreter.service.error.TypeNotFoundException; import org.enso.interpreter.service.error.FailedToApplyEditsException; import org.enso.interpreter.service.error.MethodNotFoundException; import org.enso.interpreter.service.error.ModuleNotFoundException; import org.enso.interpreter.service.error.SourceNotFoundException; +import org.enso.interpreter.service.error.TypeNotFoundException; import org.enso.lockmanager.client.ConnectedLockManager; import org.enso.polyglot.LanguageInfo; import org.enso.polyglot.MethodNames; import org.enso.text.editing.JavaEditorAdapter; import org.enso.text.editing.model; -import java.io.File; -import java.io.IOException; -import java.util.Optional; -import java.util.UUID; -import java.util.function.Consumer; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.TruffleLogger; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrumentation.EventBinding; +import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory; +import com.oracle.truffle.api.interop.ArityException; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.SourceSection; /** * A service allowing externally-triggered code execution, registered by an instance of the * language. */ -public class ExecutionService { +public final class ExecutionService { private static final String MAIN_METHOD = "main"; private final EnsoContext context; diff --git a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java index d9b2a3430b23..af68f8675f81 100644 --- a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java +++ b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java @@ -66,17 +66,129 @@ protected void onCreate(Env env) { /** Factory for creating new id event nodes **/ private static class IdEventNodeFactory implements ExecutionEventNodeFactory { + private static final class Callbacks { + private final UUID nextExecutionItem; + private final RuntimeCache cache; + private final MethodCallsCache methodCallsCache; + private final UpdatesSynchronizationState syncState; + private final Map calls = new HashMap<>(); + private final Consumer onCachedCallback; + private final Consumer onComputedCallback; + private final Consumer functionCallCallback; + + Callbacks( + UUID nextExecutionItem, + RuntimeCache cache, MethodCallsCache methodCallsCache, UpdatesSynchronizationState syncState, + Consumer onCachedCallback, Consumer onComputedCallback, + Consumer functionCallCallback + ) { + this.nextExecutionItem = nextExecutionItem; + this.cache = cache; + this.methodCallsCache = methodCallsCache; + this.syncState = syncState; + this.onCachedCallback = onCachedCallback; + this.onComputedCallback = onComputedCallback; + this.functionCallCallback = functionCallCallback; + } + + Object findCachedResult(UUID nodeId) { + // Add a flag to say it was cached. + // An array of `ProfilingInfo` in the value update. + Object result = cache.get(nodeId); + // When executing the call stack we need to capture the FunctionCall of the next (top) stack + // item in the `functionCallCallback`. We allow to execute the cached `stackTop` value to be + // able to continue the stack execution, and unwind later from the `onReturnValue` callback. + if (result != null && !nodeId.equals(nextExecutionItem)) { + var value = new ExpressionValue( + nodeId, + result, + cache.getType(nodeId), + typeOf(result), + calls.get(nodeId), + cache.getCall(nodeId), + new ProfilingInfo[]{ExecutionTime.empty()}, + true + ); + onCachedCallback.accept(value); + return result; + } + return null; + } + + void updateCachedResult(Object result, UUID nodeId, boolean isPanic, long nanoTimeElapsed) { + String resultType = typeOf(result); + String cachedType = cache.getType(nodeId); + FunctionCallInfo call = functionCallInfoById(nodeId); + FunctionCallInfo cachedCall = cache.getCall(nodeId); + ProfilingInfo[] profilingInfo = new ProfilingInfo[]{new ExecutionTime(nanoTimeElapsed)}; + + ExpressionValue expressionValue + = new ExpressionValue(nodeId, result, resultType, cachedType, call, cachedCall, profilingInfo, false); + syncState.setExpressionUnsync(nodeId); + syncState.setVisualizationUnsync(nodeId); + + // Panics are not cached because a panic can be fixed by changing seemingly unrelated code, + // like imports, and the invalidation mechanism can not always track those changes and + // appropriately invalidate all dependent expressions. + if (!isPanic) { + cache.offer(nodeId, result); + cache.putCall(nodeId, call); + } + cache.putType(nodeId, resultType); + + passExpressionValueToCallback(expressionValue); + if (isPanic) { + // We mark the node as executed so that it is not reported as not executed call after the + // program execution is complete. If we clear the call from the cache instead, it will mess + // up the `typeChanged` field of the expression update. + methodCallsCache.setExecuted(nodeId); + } + } + + @CompilerDirectives.TruffleBoundary + Object onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall result, EventContext context) throws ThreadDeath { + calls.put(nodeId, FunctionCallInfo.fromFunctionCall(result)); + functionCallCallback.accept(new ExpressionCall(nodeId, result)); + // Return cached value after capturing the enterable function call in `functionCallCallback` + Object cachedResult = cache.get(nodeId); + if (cachedResult != null) { + return cachedResult; + } + methodCallsCache.setExecuted(nodeId); + return null; + } + + @CompilerDirectives.TruffleBoundary + private void passExpressionValueToCallback(ExpressionValue expressionValue) { + onComputedCallback.accept(expressionValue); + } + + @CompilerDirectives.TruffleBoundary + private FunctionCallInfo functionCallInfoById(UUID nodeId) { + return calls.get(nodeId); + } + + + private String typeOf(Object value) { + String resultType; + if (value instanceof UnresolvedSymbol) { + resultType = Constants.UNRESOLVED_SYMBOL; + } else { + var typeOfNode = TypeOfNode.getUncached(); + Object typeResult = typeOfNode.execute(value); + if (typeResult instanceof Type t) { + resultType = t.getQualifiedName().toString(); + } else { + resultType = null; + } + } + return resultType; + } + } private final CallTarget entryCallTarget; - private final Consumer functionCallCallback; - private final Consumer onComputedCallback; - private final Consumer onCachedCallback; private final Consumer onExceptionalCallback; - private final RuntimeCache cache; - private final MethodCallsCache methodCallsCache; - private final UpdatesSynchronizationState syncState; - private final UUID nextExecutionItem; - private final Map calls = new HashMap<>(); + private final Callbacks callbacks; private final Timer timer; /** @@ -84,7 +196,7 @@ private static class IdEventNodeFactory implements ExecutionEventNodeFactory { * * @param entryCallTarget the call target being observed. * @param cache the precomputed expression values. - * @param methodCallsCache the storage tracking the executed method calls. + * @param methodCallsCache the storage tracking the executed updateCachedResult calls. * @param syncState the synchronization state of runtime updates. * @param nextExecutionItem the next item scheduled for execution. * @param functionCallCallback the consumer of function call events. @@ -106,13 +218,11 @@ private static class IdEventNodeFactory implements ExecutionEventNodeFactory { Timer timer ) { this.entryCallTarget = entryCallTarget; - this.cache = cache; - this.methodCallsCache = methodCallsCache; - this.syncState = syncState; - this.nextExecutionItem = nextExecutionItem; - this.functionCallCallback = functionCallCallback; - this.onComputedCallback = onComputedCallback; - this.onCachedCallback = onCachedCallback; + this.callbacks = new Callbacks( + nextExecutionItem, + cache, methodCallsCache, syncState, + onCachedCallback, onComputedCallback, functionCallCallback + ); this.onExceptionalCallback = onExceptionalCallback; this.timer = timer; } @@ -129,8 +239,6 @@ private class IdExecutionEventNode extends ExecutionEventNode { private final EventContext context; private long nanoTimeElapsed = 0; - private @Child - TypeOfNode typeOfNode = TypeOfNode.build(); /** * Creates a new event node. @@ -157,28 +265,10 @@ public void onEnter(VirtualFrame frame) { @CompilerDirectives.TruffleBoundary private void onEnterImpl() { UUID nodeId = getNodeId(context.getInstrumentedNode()); - - // Add a flag to say it was cached. - // An array of `ProfilingInfo` in the value update. - Object result = cache.get(nodeId); - // When executing the call stack we need to capture the FunctionCall of the next (top) stack - // item in the `functionCallCallback`. We allow to execute the cached `stackTop` value to be - // able to continue the stack execution, and unwind later from the `onReturnValue` callback. - if (result != null && !nodeId.equals(nextExecutionItem)) { - var value = new ExpressionValue( - nodeId, - result, - cache.getType(nodeId), - typeOf(result), - calls.get(nodeId), - cache.getCall(nodeId), - new ProfilingInfo[]{ExecutionTime.empty()}, - true - ); - onCachedCallback.accept(value); + var result = callbacks.findCachedResult(nodeId); + if (result != null) { throw context.createUnwind(result); } - nanoTimeElapsed = timer.getTime(); } @@ -201,9 +291,12 @@ public void onReturnValue(VirtualFrame frame, Object result) { if (node instanceof FunctionCallInstrumentationNode && result instanceof FunctionCallInstrumentationNode.FunctionCall functionCall) { UUID nodeId = ((FunctionCallInstrumentationNode) node).getId(); - onFunctionReturn(nodeId, functionCall, context); + var cachedResult = callbacks.onFunctionReturn(nodeId, functionCall, context); + if (cachedResult != null) { + throw context.createUnwind(cachedResult); + } } else if (node instanceof ExpressionNode) { - onExpressionReturn(result, node, context); + onExpressionReturn(result, node, context, nanoTimeElapsed); } } @@ -218,77 +311,16 @@ public void onReturnExceptional(VirtualFrame frame, Throwable exception) { } } - private void onExpressionReturn(Object result, Node node, EventContext context) throws ThreadDeath { + private void onExpressionReturn(Object result, Node node, EventContext context, long length) throws ThreadDeath { boolean isPanic = result instanceof AbstractTruffleException && !(result instanceof DataflowError); UUID nodeId = ((ExpressionNode) node).getId(); - String resultType = typeOf(result); - String cachedType = cache.getType(nodeId); - FunctionCallInfo call = functionCallInfoById(nodeId); - FunctionCallInfo cachedCall = cache.getCall(nodeId); - ProfilingInfo[] profilingInfo = new ProfilingInfo[]{new ExecutionTime(nanoTimeElapsed)}; - - ExpressionValue expressionValue - = new ExpressionValue(nodeId, result, resultType, cachedType, call, cachedCall, profilingInfo, false); - syncState.setExpressionUnsync(nodeId); - syncState.setVisualizationUnsync(nodeId); - - // Panics are not cached because a panic can be fixed by changing seemingly unrelated code, - // like imports, and the invalidation mechanism can not always track those changes and - // appropriately invalidate all dependent expressions. - if (!isPanic) { - cache.offer(nodeId, result); - cache.putCall(nodeId, call); - } - cache.putType(nodeId, resultType); - - passExpressionValueToCallback(expressionValue); + callbacks.updateCachedResult(result, nodeId, isPanic, length); if (isPanic) { - // We mark the node as executed so that it is not reported as not executed call after the - // program execution is complete. If we clear the call from the cache instead, it will mess - // up the `typeChanged` field of the expression update. - methodCallsCache.setExecuted(nodeId); throw context.createUnwind(result); } } - private String typeOf(Object value) { - String resultType; - if (value instanceof UnresolvedSymbol) { - resultType = Constants.UNRESOLVED_SYMBOL; - } else { - Object typeResult = typeOfNode.execute(value); - if (typeResult instanceof Type t) { - resultType = t.getQualifiedName().toString(); - } else { - resultType = null; - } - } - return resultType; - } - - @CompilerDirectives.TruffleBoundary - private void passExpressionValueToCallback(ExpressionValue expressionValue) { - onComputedCallback.accept(expressionValue); - } - - @CompilerDirectives.TruffleBoundary - private FunctionCallInfo functionCallInfoById(UUID nodeId) { - return calls.get(nodeId); - } - - @CompilerDirectives.TruffleBoundary - private void onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall result, EventContext context) throws ThreadDeath { - calls.put(nodeId, FunctionCallInfo.fromFunctionCall(result)); - functionCallCallback.accept(new ExpressionCall(nodeId, result)); - // Return cached value after capturing the enterable function call in `functionCallCallback` - Object cachedResult = cache.get(nodeId); - if (cachedResult != null) { - throw context.createUnwind(cachedResult); - } - methodCallsCache.setExecuted(nodeId); - } - @CompilerDirectives.TruffleBoundary private void onTailCallReturn(Throwable exception, State state) { try { From 84b8e0759eb532d6d6d8f2c716207bd75e5a7846 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 14 Sep 2023 16:06:24 +0200 Subject: [PATCH 3/6] All callbacks moved into IdExecutionCallbacks --- .../instrument/IdExecutionCallbacks.java | 157 ++++++++++++++ .../instrument/IdExecutionInstrument.java | 193 ++---------------- 2 files changed, 177 insertions(+), 173 deletions(-) create mode 100644 engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java diff --git a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java new file mode 100644 index 000000000000..b61980db56a3 --- /dev/null +++ b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java @@ -0,0 +1,157 @@ +package org.enso.interpreter.instrument; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; + +import org.enso.interpreter.instrument.IdExecutionService.ExpressionCall; +import org.enso.interpreter.instrument.IdExecutionService.ExpressionValue; +import org.enso.interpreter.instrument.IdExecutionService.FunctionCallInfo; +import org.enso.interpreter.instrument.profiling.ExecutionTime; +import org.enso.interpreter.instrument.profiling.ProfilingInfo; +import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode; +import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode; +import org.enso.interpreter.runtime.callable.UnresolvedSymbol; +import org.enso.interpreter.runtime.data.Type; +import org.enso.interpreter.runtime.type.Constants; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.instrumentation.EventContext; + +final class IdExecutionCallbacks { + + private final UUID nextExecutionItem; + private final RuntimeCache cache; + private final MethodCallsCache methodCallsCache; + private final UpdatesSynchronizationState syncState; + private final Map calls = new HashMap<>(); + private final Consumer onCachedCallback; + private final Consumer onComputedCallback; + private final Consumer functionCallCallback; + private final Consumer onExceptionalCallback; + + /** Creates callbacks instance. + * + * @param cache the precomputed expression values. + * @param methodCallsCache the storage tracking the executed updateCachedResult calls. + * @param syncState the synchronization state of runtime updates. + * @param nextExecutionItem the next item scheduled for execution. + * @param functionCallCallback the consumer of function call events. + * @param onComputedCallback the consumer of the computed value events. + * @param onCachedCallback the consumer of the cached value events. + * @param onExceptionalCallback the consumer of the exceptional events. + */ + IdExecutionCallbacks( + UUID nextExecutionItem, + RuntimeCache cache, MethodCallsCache methodCallsCache, UpdatesSynchronizationState syncState, + Consumer onCachedCallback, Consumer onComputedCallback, + Consumer functionCallCallback, Consumer onExceptionalCallback + ) { + this.nextExecutionItem = nextExecutionItem; + this.cache = cache; + this.methodCallsCache = methodCallsCache; + this.syncState = syncState; + this.onCachedCallback = onCachedCallback; + this.onComputedCallback = onComputedCallback; + this.functionCallCallback = functionCallCallback; + this.onExceptionalCallback = onExceptionalCallback; + } + + Object findCachedResult(UUID nodeId) { + // Add a flag to say it was cached. + // An array of `ProfilingInfo` in the value update. + Object result = cache.get(nodeId); + // When executing the call stack we need to capture the FunctionCall of the next (top) stack + // item in the `functionCallCallback`. We allow to execute the cached `stackTop` value to be + // able to continue the stack execution, and unwind later from the `onReturnValue` callback. + if (result != null && !nodeId.equals(nextExecutionItem)) { + var value = new ExpressionValue( + nodeId, + result, + cache.getType(nodeId), + typeOf(result), + calls.get(nodeId), + cache.getCall(nodeId), + new ProfilingInfo[]{ExecutionTime.empty()}, + true + ); + onCachedCallback.accept(value); + return result; + } + return null; + } + + void updateCachedResult(Object result, UUID nodeId, boolean isPanic, long nanoTimeElapsed) { + String resultType = typeOf(result); + String cachedType = cache.getType(nodeId); + FunctionCallInfo call = functionCallInfoById(nodeId); + FunctionCallInfo cachedCall = cache.getCall(nodeId); + ProfilingInfo[] profilingInfo = new ProfilingInfo[]{new ExecutionTime(nanoTimeElapsed)}; + + ExpressionValue expressionValue + = new ExpressionValue(nodeId, result, resultType, cachedType, call, cachedCall, profilingInfo, false); + syncState.setExpressionUnsync(nodeId); + syncState.setVisualizationUnsync(nodeId); + + // Panics are not cached because a panic can be fixed by changing seemingly unrelated code, + // like imports, and the invalidation mechanism can not always track those changes and + // appropriately invalidate all dependent expressions. + if (!isPanic) { + cache.offer(nodeId, result); + cache.putCall(nodeId, call); + } + cache.putType(nodeId, resultType); + + passExpressionValueToCallback(expressionValue); + if (isPanic) { + // We mark the node as executed so that it is not reported as not executed call after the + // program execution is complete. If we clear the call from the cache instead, it will mess + // up the `typeChanged` field of the expression update. + methodCallsCache.setExecuted(nodeId); + } + } + + @CompilerDirectives.TruffleBoundary + Object onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall result, EventContext context) throws ThreadDeath { + calls.put(nodeId, FunctionCallInfo.fromFunctionCall(result)); + functionCallCallback.accept(new ExpressionCall(nodeId, result)); + // Return cached value after capturing the enterable function call in `functionCallCallback` + Object cachedResult = cache.get(nodeId); + if (cachedResult != null) { + return cachedResult; + } + methodCallsCache.setExecuted(nodeId); + return null; + } + + void onExceptionalCallback(Exception e) { + onExceptionalCallback.accept(e); + } + + @CompilerDirectives.TruffleBoundary + private void passExpressionValueToCallback(ExpressionValue expressionValue) { + onComputedCallback.accept(expressionValue); + } + + @CompilerDirectives.TruffleBoundary + private FunctionCallInfo functionCallInfoById(UUID nodeId) { + return calls.get(nodeId); + } + + private String typeOf(Object value) { + String resultType; + if (value instanceof UnresolvedSymbol) { + resultType = Constants.UNRESOLVED_SYMBOL; + } else { + var typeOfNode = TypeOfNode.getUncached(); + Object typeResult = typeOfNode.execute(value); + if (typeResult instanceof Type t) { + resultType = t.getQualifiedName().toString(); + } else { + resultType = null; + } + } + return resultType; + } +} diff --git a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java index af68f8675f81..9bcdeb6c783e 100644 --- a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java +++ b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java @@ -1,28 +1,20 @@ package org.enso.interpreter.instrument; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; import java.util.function.Consumer; -import org.enso.interpreter.instrument.profiling.ExecutionTime; -import org.enso.interpreter.instrument.profiling.ProfilingInfo; import org.enso.interpreter.node.ClosureRootNode; import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode; -import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode; import org.enso.interpreter.runtime.Module; -import org.enso.interpreter.runtime.callable.UnresolvedSymbol; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.control.TailCallException; -import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.state.State; import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag; import org.enso.interpreter.runtime.tag.IdentifiedTag; -import org.enso.interpreter.runtime.type.Constants; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; @@ -66,164 +58,25 @@ protected void onCreate(Env env) { /** Factory for creating new id event nodes **/ private static class IdEventNodeFactory implements ExecutionEventNodeFactory { - private static final class Callbacks { - private final UUID nextExecutionItem; - private final RuntimeCache cache; - private final MethodCallsCache methodCallsCache; - private final UpdatesSynchronizationState syncState; - private final Map calls = new HashMap<>(); - private final Consumer onCachedCallback; - private final Consumer onComputedCallback; - private final Consumer functionCallCallback; - - Callbacks( - UUID nextExecutionItem, - RuntimeCache cache, MethodCallsCache methodCallsCache, UpdatesSynchronizationState syncState, - Consumer onCachedCallback, Consumer onComputedCallback, - Consumer functionCallCallback - ) { - this.nextExecutionItem = nextExecutionItem; - this.cache = cache; - this.methodCallsCache = methodCallsCache; - this.syncState = syncState; - this.onCachedCallback = onCachedCallback; - this.onComputedCallback = onComputedCallback; - this.functionCallCallback = functionCallCallback; - } - - Object findCachedResult(UUID nodeId) { - // Add a flag to say it was cached. - // An array of `ProfilingInfo` in the value update. - Object result = cache.get(nodeId); - // When executing the call stack we need to capture the FunctionCall of the next (top) stack - // item in the `functionCallCallback`. We allow to execute the cached `stackTop` value to be - // able to continue the stack execution, and unwind later from the `onReturnValue` callback. - if (result != null && !nodeId.equals(nextExecutionItem)) { - var value = new ExpressionValue( - nodeId, - result, - cache.getType(nodeId), - typeOf(result), - calls.get(nodeId), - cache.getCall(nodeId), - new ProfilingInfo[]{ExecutionTime.empty()}, - true - ); - onCachedCallback.accept(value); - return result; - } - return null; - } - - void updateCachedResult(Object result, UUID nodeId, boolean isPanic, long nanoTimeElapsed) { - String resultType = typeOf(result); - String cachedType = cache.getType(nodeId); - FunctionCallInfo call = functionCallInfoById(nodeId); - FunctionCallInfo cachedCall = cache.getCall(nodeId); - ProfilingInfo[] profilingInfo = new ProfilingInfo[]{new ExecutionTime(nanoTimeElapsed)}; - - ExpressionValue expressionValue - = new ExpressionValue(nodeId, result, resultType, cachedType, call, cachedCall, profilingInfo, false); - syncState.setExpressionUnsync(nodeId); - syncState.setVisualizationUnsync(nodeId); - - // Panics are not cached because a panic can be fixed by changing seemingly unrelated code, - // like imports, and the invalidation mechanism can not always track those changes and - // appropriately invalidate all dependent expressions. - if (!isPanic) { - cache.offer(nodeId, result); - cache.putCall(nodeId, call); - } - cache.putType(nodeId, resultType); - - passExpressionValueToCallback(expressionValue); - if (isPanic) { - // We mark the node as executed so that it is not reported as not executed call after the - // program execution is complete. If we clear the call from the cache instead, it will mess - // up the `typeChanged` field of the expression update. - methodCallsCache.setExecuted(nodeId); - } - } - - @CompilerDirectives.TruffleBoundary - Object onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall result, EventContext context) throws ThreadDeath { - calls.put(nodeId, FunctionCallInfo.fromFunctionCall(result)); - functionCallCallback.accept(new ExpressionCall(nodeId, result)); - // Return cached value after capturing the enterable function call in `functionCallCallback` - Object cachedResult = cache.get(nodeId); - if (cachedResult != null) { - return cachedResult; - } - methodCallsCache.setExecuted(nodeId); - return null; - } - - @CompilerDirectives.TruffleBoundary - private void passExpressionValueToCallback(ExpressionValue expressionValue) { - onComputedCallback.accept(expressionValue); - } - - @CompilerDirectives.TruffleBoundary - private FunctionCallInfo functionCallInfoById(UUID nodeId) { - return calls.get(nodeId); - } - - - private String typeOf(Object value) { - String resultType; - if (value instanceof UnresolvedSymbol) { - resultType = Constants.UNRESOLVED_SYMBOL; - } else { - var typeOfNode = TypeOfNode.getUncached(); - Object typeResult = typeOfNode.execute(value); - if (typeResult instanceof Type t) { - resultType = t.getQualifiedName().toString(); - } else { - resultType = null; - } - } - return resultType; - } - } private final CallTarget entryCallTarget; - private final Consumer onExceptionalCallback; - private final Callbacks callbacks; + private final IdExecutionCallbacks callbacks; private final Timer timer; /** * Creates a new event node factory. * * @param entryCallTarget the call target being observed. - * @param cache the precomputed expression values. - * @param methodCallsCache the storage tracking the executed updateCachedResult calls. - * @param syncState the synchronization state of runtime updates. - * @param nextExecutionItem the next item scheduled for execution. - * @param functionCallCallback the consumer of function call events. - * @param onComputedCallback the consumer of the computed value events. - * @param onCachedCallback the consumer of the cached value events. - * @param onExceptionalCallback the consumer of the exceptional events. + * @param callbacks communication with users * @param timer the timer for timing execution */ IdEventNodeFactory( CallTarget entryCallTarget, - RuntimeCache cache, - MethodCallsCache methodCallsCache, - UpdatesSynchronizationState syncState, - UUID nextExecutionItem, // The expression ID - Consumer functionCallCallback, - Consumer onComputedCallback, - Consumer onCachedCallback, - Consumer onExceptionalCallback, + IdExecutionCallbacks callbacks, Timer timer ) { this.entryCallTarget = entryCallTarget; - this.callbacks = new Callbacks( - nextExecutionItem, - cache, methodCallsCache, syncState, - onCachedCallback, onComputedCallback, functionCallCallback - ); - this.onExceptionalCallback = onExceptionalCallback; + this.callbacks = callbacks; this.timer = timer; } @@ -333,7 +186,7 @@ private void onTailCallReturn(Throwable exception, State state) { Object result = InteropLibrary.getFactory().getUncached().execute(functionCall); onReturnValue(null, result); } catch (InteropException e) { - onExceptionalCallback.accept(e); + callbacks.onExceptionalCallback(e); } } @@ -396,17 +249,17 @@ private UUID getNodeId(Node node) { */ @Override public EventBinding bind( - Module module, - CallTarget entryCallTarget, - RuntimeCache cache, - MethodCallsCache methodCallsCache, - UpdatesSynchronizationState syncState, - Timer timer, - UUID nextExecutionItem, - Consumer functionCallCallback, - Consumer onComputedCallback, - Consumer onCachedCallback, - Consumer onExceptionalCallback + Module module, + CallTarget entryCallTarget, + RuntimeCache cache, + MethodCallsCache methodCallsCache, + UpdatesSynchronizationState syncState, + Timer timer, + UUID nextExecutionItem, + Consumer functionCallCallback, + Consumer onComputedCallback, + Consumer onCachedCallback, + Consumer onExceptionalCallback ) { var builder = SourceSectionFilter.newBuilder() .tagIs(StandardTags.ExpressionTag.class, StandardTags.CallTag.class) @@ -421,18 +274,12 @@ public EventBinding bind( } var filter = builder.build(); - var factory = new IdEventNodeFactory( - entryCallTarget, - cache, - methodCallsCache, - syncState, + var callbacks = new IdExecutionCallbacks( nextExecutionItem, - functionCallCallback, - onComputedCallback, - onCachedCallback, - onExceptionalCallback, - timer + cache, methodCallsCache, syncState, + onCachedCallback, onComputedCallback, functionCallCallback, onExceptionalCallback ); + var factory = new IdEventNodeFactory(entryCallTarget, callbacks, timer); return env.getInstrumenter().attachExecutionEventFactory(filter, factory); } } From a59e82341822dadf5a5a2e90d15917644820f746 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 14 Sep 2023 16:29:55 +0200 Subject: [PATCH 4/6] Hiding actual callbacks behind an interface --- .../instrument/IdExecutionService.java | 37 +++++++++++++++++++ .../instrument/IdExecutionCallbacks.java | 14 ++++--- .../instrument/IdExecutionInstrument.java | 10 ++--- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java index 9abd55bddf4c..e3d4b00bfa75 100644 --- a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java +++ b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java @@ -26,6 +26,43 @@ public interface IdExecutionService { String INSTRUMENT_ID = "id-value-extractor"; + public interface Callbacks { + /** Finds out previously computed result for given id. If + * a result is returned, then the execution of given node is skipped + * and the value is returned back. + * + * @param nodeId identification of the node to be computed + * @return {@code null} should the execution of the node be performed; + * any other value to skip the execution and return the value as a + * result. + */ + Object findCachedResult(UUID nodeId); + + /** Notifies when an execution of a node is over. + * @param nodeId identification of the node to be computed + * @param result the just computed result + * @param isPanic was the result a panic? + * @param nanoElapsedTime how long it took to compute the result? + */ + void updateCachedResult(UUID nodeId, Object result, boolean isPanic, long nanoElapsedTime); + + /** + * + * @param nodeId identification of the node to be computed + * @param result info about function call + * @return {@code null} should the execution of the node be performed; + * any other value to skip the execution and return the value as a + * result. + */ + Object onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall result); + + /** Notification on an exception. + * @param e the reported exception + */ + void onExceptionalCallback(Exception e); + } + + /** * Attach a new event node factory to observe identified nodes within given function. * diff --git a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java index b61980db56a3..81965afd13e7 100644 --- a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java +++ b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java @@ -17,9 +17,8 @@ import org.enso.interpreter.runtime.type.Constants; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.instrumentation.EventContext; -final class IdExecutionCallbacks { +final class IdExecutionCallbacks implements IdExecutionService.Callbacks { private final UUID nextExecutionItem; private final RuntimeCache cache; @@ -58,7 +57,8 @@ final class IdExecutionCallbacks { this.onExceptionalCallback = onExceptionalCallback; } - Object findCachedResult(UUID nodeId) { + @CompilerDirectives.TruffleBoundary + public final Object findCachedResult(UUID nodeId) { // Add a flag to say it was cached. // An array of `ProfilingInfo` in the value update. Object result = cache.get(nodeId); @@ -82,7 +82,8 @@ Object findCachedResult(UUID nodeId) { return null; } - void updateCachedResult(Object result, UUID nodeId, boolean isPanic, long nanoTimeElapsed) { + @CompilerDirectives.TruffleBoundary + public final void updateCachedResult(UUID nodeId, Object result, boolean isPanic, long nanoTimeElapsed) { String resultType = typeOf(result); String cachedType = cache.getType(nodeId); FunctionCallInfo call = functionCallInfoById(nodeId); @@ -113,7 +114,7 @@ void updateCachedResult(Object result, UUID nodeId, boolean isPanic, long nanoTi } @CompilerDirectives.TruffleBoundary - Object onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall result, EventContext context) throws ThreadDeath { + public final Object onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCall result) { calls.put(nodeId, FunctionCallInfo.fromFunctionCall(result)); functionCallCallback.accept(new ExpressionCall(nodeId, result)); // Return cached value after capturing the enterable function call in `functionCallCallback` @@ -125,7 +126,8 @@ Object onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNode.FunctionCal return null; } - void onExceptionalCallback(Exception e) { + @CompilerDirectives.TruffleBoundary + public final void onExceptionalCallback(Exception e) { onExceptionalCallback.accept(e); } diff --git a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java index 9bcdeb6c783e..84b81751e15c 100644 --- a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java +++ b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java @@ -60,7 +60,7 @@ protected void onCreate(Env env) { private static class IdEventNodeFactory implements ExecutionEventNodeFactory { private final CallTarget entryCallTarget; - private final IdExecutionCallbacks callbacks; + private final Callbacks callbacks; private final Timer timer; /** @@ -72,7 +72,7 @@ private static class IdEventNodeFactory implements ExecutionEventNodeFactory { */ IdEventNodeFactory( CallTarget entryCallTarget, - IdExecutionCallbacks callbacks, + Callbacks callbacks, Timer timer ) { this.entryCallTarget = entryCallTarget; @@ -144,7 +144,7 @@ public void onReturnValue(VirtualFrame frame, Object result) { if (node instanceof FunctionCallInstrumentationNode && result instanceof FunctionCallInstrumentationNode.FunctionCall functionCall) { UUID nodeId = ((FunctionCallInstrumentationNode) node).getId(); - var cachedResult = callbacks.onFunctionReturn(nodeId, functionCall, context); + var cachedResult = callbacks.onFunctionReturn(nodeId, functionCall); if (cachedResult != null) { throw context.createUnwind(cachedResult); } @@ -164,11 +164,11 @@ public void onReturnExceptional(VirtualFrame frame, Throwable exception) { } } - private void onExpressionReturn(Object result, Node node, EventContext context, long length) throws ThreadDeath { + private void onExpressionReturn(Object result, Node node, EventContext context, long howLong) throws ThreadDeath { boolean isPanic = result instanceof AbstractTruffleException && !(result instanceof DataflowError); UUID nodeId = ((ExpressionNode) node).getId(); - callbacks.updateCachedResult(result, nodeId, isPanic, length); + callbacks.updateCachedResult(nodeId, result, isPanic, howLong); if (isPanic) { throw context.createUnwind(result); } From 203a65dc8e20ed3d232f82ddcd3837c864ba84bf Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Thu, 14 Sep 2023 16:57:55 +0200 Subject: [PATCH 5/6] Moving implementation of Callbacks next to ExecutionService --- .../instrument/IdExecutionService.java | 24 ++-------- .../service/ExecutionCallbacks.java} | 11 +++-- .../interpreter/service/ExecutionService.java | 48 ++++++++----------- .../instrument/IdExecutionInstrument.java | 30 ++---------- 4 files changed, 38 insertions(+), 75 deletions(-) rename engine/{runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java => runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java} (94%) diff --git a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java index e3d4b00bfa75..994b93542eaf 100644 --- a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java +++ b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/instrument/IdExecutionService.java @@ -3,7 +3,6 @@ import java.util.Arrays; import java.util.Objects; import java.util.UUID; -import java.util.function.Consumer; import org.enso.interpreter.instrument.profiling.ProfilingInfo; import org.enso.interpreter.node.MethodRootNode; @@ -46,7 +45,7 @@ public interface Callbacks { */ void updateCachedResult(UUID nodeId, Object result, boolean isPanic, long nanoElapsedTime); - /** + /** Notification when a returned value is a function. * * @param nodeId identification of the node to be computed * @param result info about function call @@ -68,29 +67,16 @@ public interface Callbacks { * * @param module module that contains the code * @param entryCallTarget the call target being observed. - * @param cache the precomputed expression values. - * @param methodCallsCache the storage tracking the executed method calls. - * @param syncState the synchronization state of runtime updates. + * @param callbacks the interface to receive notifications * @param timer the execution timer. - * @param nextExecutionItem the next item scheduled for execution. - * @param functionCallCallback the consumer of function call events. - * @param onComputedCallback the consumer of the computed value events. - * @param onCachedCallback the consumer of the cached value events. - * @param onExceptionalCallback the consumer of the exceptional events. * @return a reference to the attached event node factory. */ EventBinding bind( Module module, CallTarget entryCallTarget, - RuntimeCache cache, - MethodCallsCache methodCallsCache, - UpdatesSynchronizationState syncState, - Timer timer, - UUID nextExecutionItem, - Consumer functionCallCallback, - Consumer onComputedCallback, - Consumer onCachedCallback, - Consumer onExceptionalCallback); + Callbacks callbacks, + Timer timer + ); /** A class for notifications about functions being called in the course of execution. */ final class ExpressionCall { diff --git a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java similarity index 94% rename from engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java rename to engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java index 81965afd13e7..5a684d6ec161 100644 --- a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionCallbacks.java +++ b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java @@ -1,13 +1,17 @@ -package org.enso.interpreter.instrument; +package org.enso.interpreter.service; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.function.Consumer; +import org.enso.interpreter.instrument.IdExecutionService; import org.enso.interpreter.instrument.IdExecutionService.ExpressionCall; import org.enso.interpreter.instrument.IdExecutionService.ExpressionValue; import org.enso.interpreter.instrument.IdExecutionService.FunctionCallInfo; +import org.enso.interpreter.instrument.MethodCallsCache; +import org.enso.interpreter.instrument.RuntimeCache; +import org.enso.interpreter.instrument.UpdatesSynchronizationState; import org.enso.interpreter.instrument.profiling.ExecutionTime; import org.enso.interpreter.instrument.profiling.ProfilingInfo; import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode; @@ -18,7 +22,7 @@ import com.oracle.truffle.api.CompilerDirectives; -final class IdExecutionCallbacks implements IdExecutionService.Callbacks { +final class ExecutionCallbacks implements IdExecutionService.Callbacks { private final UUID nextExecutionItem; private final RuntimeCache cache; @@ -41,7 +45,7 @@ final class IdExecutionCallbacks implements IdExecutionService.Callbacks { * @param onCachedCallback the consumer of the cached value events. * @param onExceptionalCallback the consumer of the exceptional events. */ - IdExecutionCallbacks( + ExecutionCallbacks( UUID nextExecutionItem, RuntimeCache cache, MethodCallsCache methodCallsCache, UpdatesSynchronizationState syncState, Consumer onCachedCallback, Consumer onComputedCallback, @@ -127,6 +131,7 @@ public final Object onFunctionReturn(UUID nodeId, FunctionCallInstrumentationNod } @CompilerDirectives.TruffleBoundary + @Override public final void onExceptionalCallback(Exception e) { onExceptionalCallback.accept(e); } diff --git a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionService.java b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionService.java index a913c05f47be..eb29045ccd34 100644 --- a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionService.java +++ b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionService.java @@ -162,21 +162,17 @@ public void execute( if (src == null) { throw new SourceNotFoundException(call.getFunction().getName()); } + var callbacks = new ExecutionCallbacks( + nextExecutionItem, cache, methodCallsCache, syncState, + onCachedCallback, onComputedCallback, funCallCallback, onExceptionalCallback + ); Optional> eventNodeFactory = - idExecutionInstrument.map( - service -> - service.bind( - module, - call.getFunction().getCallTarget(), - cache, - methodCallsCache, - syncState, - this.timer, - nextExecutionItem, - funCallCallback, - onComputedCallback, - onCachedCallback, - onExceptionalCallback)); + idExecutionInstrument.map(service -> service.bind( + module, + call.getFunction().getCallTarget(), + callbacks, + this.timer + )); Object p = context.getThreadManager().enter(); try { execute.getCallTarget().call(call); @@ -309,21 +305,17 @@ public Object callFunctionWithInstrument( Consumer onExceptionalCallback = (value) -> context.getLogger().finest("_ON_ERROR " + value); + var callbacks = new ExecutionCallbacks( + nextExecutionItem, cache, methodCallsCache, syncState, + onCachedCallback, onComputedCallback, funCallCallback, onExceptionalCallback + ); Optional> eventNodeFactory = - idExecutionInstrument.map( - service -> - service.bind( - module, - entryCallTarget, - cache, - methodCallsCache, - syncState, - this.timer, - nextExecutionItem, - funCallCallback, - onComputedCallback, - onCachedCallback, - onExceptionalCallback)); + idExecutionInstrument.map(service -> service.bind( + module, + entryCallTarget, + callbacks, + this.timer + )); Object p = context.getThreadManager().enter(); try { return call.getCallTarget().call(function, arguments); diff --git a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java index 84b81751e15c..80df24b1596e 100644 --- a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java +++ b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java @@ -236,30 +236,16 @@ private UUID getNodeId(Node node) { * * @param module module that contains the code * @param entryCallTarget the call target being observed. - * @param cache the precomputed expression values. - * @param methodCallsCache the storage tracking the executed method calls. - * @param syncState the synchronization state of runtime updates. + * @param callbacks the precomputed expression values. * @param timer the execution timer. - * @param nextExecutionItem the next item scheduled for execution. - * @param functionCallCallback the consumer of function call events. - * @param onComputedCallback the consumer of the computed value events. - * @param onCachedCallback the consumer of the cached value events. - * @param onExceptionalCallback the consumer of the exceptional events. * @return a reference to the attached event node factory. */ @Override public EventBinding bind( - Module module, - CallTarget entryCallTarget, - RuntimeCache cache, - MethodCallsCache methodCallsCache, - UpdatesSynchronizationState syncState, - Timer timer, - UUID nextExecutionItem, - Consumer functionCallCallback, - Consumer onComputedCallback, - Consumer onCachedCallback, - Consumer onExceptionalCallback + Module module, + CallTarget entryCallTarget, + Callbacks callbacks, + Timer timer ) { var builder = SourceSectionFilter.newBuilder() .tagIs(StandardTags.ExpressionTag.class, StandardTags.CallTag.class) @@ -273,12 +259,6 @@ public EventBinding bind( builder.lineIn(SourceSectionFilter.IndexRange.between(firstFunctionLine, afterFunctionLine)); } var filter = builder.build(); - - var callbacks = new IdExecutionCallbacks( - nextExecutionItem, - cache, methodCallsCache, syncState, - onCachedCallback, onComputedCallback, functionCallCallback, onExceptionalCallback - ); var factory = new IdEventNodeFactory(entryCallTarget, callbacks, timer); return env.getInstrumenter().attachExecutionEventFactory(filter, factory); } From e557a95f5e184ef9f4a78ff0517c266ebc70d9ee Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Fri, 15 Sep 2023 11:08:01 +0200 Subject: [PATCH 6/6] Extract ID from a Node via switch --- build.sbt | 1 + .../instrument/IdExecutionInstrument.java | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build.sbt b/build.sbt index 96c6a256a5a4..4a78eb743875 100644 --- a/build.sbt +++ b/build.sbt @@ -1495,6 +1495,7 @@ lazy val `runtime-instrument-common` = lazy val `runtime-instrument-id-execution` = (project in file("engine/runtime-instrument-id-execution")) .settings( + frgaalJavaCompilerSetting, inConfig(Compile)(truffleRunOptionsSettings), instrumentationSettings ) diff --git a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java index 7c2f99cdd547..534addb25274 100644 --- a/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java +++ b/engine/runtime-instrument-id-execution/src/main/java/org/enso/interpreter/instrument/IdExecutionInstrument.java @@ -216,14 +216,13 @@ public Object visitFrame(FrameInstance frameInstance) { return result == null; } - private UUID getNodeId(Node node) { - if (node instanceof ExpressionNode) { - return ((ExpressionNode) node).getId(); - } - if (node instanceof FunctionCallInstrumentationNode) { - return ((FunctionCallInstrumentationNode) node).getId(); - } - return null; + private static UUID getNodeId(Node node) { + return switch (node) { + case ExpressionNode n -> n.getId(); + case FunctionCallInstrumentationNode n -> n.getId(); + case null -> null; + default -> null; + }; } } }