[go: nahoru, domu]

Skip to content

Commit

Permalink
Add Locations Filter for Instrumented Locations (enso-org#1564)
Browse files Browse the repository at this point in the history
PR adds the location filter that excludes lambdas 
and local functions from the instrumentation.
  • Loading branch information
4e6 authored Mar 12, 2021
1 parent 13c9939 commit b0680d0
Show file tree
Hide file tree
Showing 10 changed files with 1,088 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import org.enso.interpreter.instrument.execution.LocationFilter;
import org.enso.interpreter.instrument.execution.Timer;
import org.enso.interpreter.instrument.profiling.ExecutionTime;
import org.enso.interpreter.instrument.profiling.ProfilingInfo;
Expand Down Expand Up @@ -461,8 +462,7 @@ private UUID getNodeId(Node node) {
* Attach a new listener to observe identified nodes within given function.
*
* @param entryCallTarget the call target being observed.
* @param funSourceStart the source start of the observed range of ids.
* @param funSourceLength the length of the observed source range.
* @param locationFilter the location filter.
* @param cache the precomputed expression values.
* @param methodCallsCache the storage tracking the executed method calls.
* @param nextExecutionItem the next item scheduled for execution.
Expand All @@ -474,8 +474,7 @@ private UUID getNodeId(Node node) {
*/
public EventBinding<ExecutionEventListener> bind(
CallTarget entryCallTarget,
int funSourceStart,
int funSourceLength,
LocationFilter locationFilter,
RuntimeCache cache,
MethodCallsCache methodCallsCache,
UUID nextExecutionItem,
Expand All @@ -487,7 +486,7 @@ public EventBinding<ExecutionEventListener> bind(
SourceSectionFilter.newBuilder()
.tagIs(StandardTags.ExpressionTag.class, StandardTags.CallTag.class)
.tagIs(IdentifiedTag.class)
.indexIn(funSourceStart, funSourceLength)
.sourceSectionEquals(locationFilter.getSections())
.build();

return env.getInstrumenter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,6 @@
import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleLanguage.Env;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.enso.compiler.Compiler;
import org.enso.home.HomeManager;
import org.enso.interpreter.Language;
Expand All @@ -28,6 +18,10 @@
import org.enso.pkg.QualifiedName;
import org.enso.polyglot.RuntimeOptions;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
* The language context is the internal state of the language that is associated with each thread in
* a running Enso program.
Expand Down Expand Up @@ -241,6 +235,20 @@ public Optional<Module> findModule(String moduleName) {
return getTopScope().getModule(moduleName);
}

/**
* Find a module containing the given expression id.
*
* @param expressionId the expression id to lookup.
* @return the relevant module, if exists.
*/
public Optional<Module> findModuleByExpressionId(UUID expressionId) {
return getTopScope().getModules().stream()
.filter(
module ->
module.getIr().preorder().exists(ir -> ir.getExternalId().contains(expressionId)))
.findFirst();
}

/**
* Finds the package the provided module belongs to.
*
Expand Down Expand Up @@ -325,6 +333,4 @@ public ResourceManager getResourceManager() {
public boolean isCachingDisabled() {
return isCachingDisabled;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.enso.interpreter.instrument.IdExecutionInstrument;
import org.enso.interpreter.instrument.MethodCallsCache;
import org.enso.interpreter.instrument.RuntimeCache;
import org.enso.interpreter.instrument.execution.LocationFilter;
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNodeGen;
import org.enso.interpreter.runtime.Context;
Expand All @@ -20,7 +21,6 @@
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.data.EmptyMap;
import org.enso.interpreter.service.error.*;
import org.enso.pkg.QualifiedName;
import org.enso.polyglot.LanguageInfo;
import org.enso.polyglot.MethodNames;
import org.enso.text.buffer.Rope;
Expand Down Expand Up @@ -88,6 +88,7 @@ private FunctionCallInstrumentationNode.FunctionCall prepareFunctionCall(
/**
* Executes a function with given arguments, represented as runtime language-level objects.
*
* @param module the module where the call is defined
* @param call the call metadata.
* @param cache the precomputed expression values.
* @param methodCallsCache the storage tracking the executed method calls.
Expand All @@ -98,6 +99,7 @@ private FunctionCallInstrumentationNode.FunctionCall prepareFunctionCall(
* @param onExceptionalCallback the consumer of the exceptional events.
*/
public void execute(
Module module,
FunctionCallInstrumentationNode.FunctionCall call,
RuntimeCache cache,
MethodCallsCache methodCallsCache,
Expand All @@ -112,11 +114,12 @@ public void execute(
if (src == null) {
throw new SourceNotFoundException(call.getFunction().getName());
}
LocationFilter locationFilter = LocationFilter.create(module.getIr(), src);

EventBinding<ExecutionEventListener> listener =
idExecutionInstrument.bind(
call.getFunction().getCallTarget(),
src.getCharIndex(),
src.getCharLength(),
locationFilter,
cache,
methodCallsCache,
nextExecutionItem,
Expand Down Expand Up @@ -164,6 +167,7 @@ public void execute(
FunctionCallInstrumentationNode.FunctionCall call =
prepareFunctionCall(module, consName, methodName);
execute(
module,
call,
cache,
methodCallsCache,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.enso.interpreter.service.error;

import java.util.UUID;

/** Thrown when a module containing the given expression id can not be found. */
public class ModuleNotFoundForExpressionIdException extends ModuleNotFoundException {

/**
* Create new instance of this error.
*
* @param expressionId the expression identifier.
*/
public ModuleNotFoundForExpressionIdException(UUID expressionId) {
super("containing expression " + expressionId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package org.enso.interpreter.instrument.execution

import com.oracle.truffle.api.source.{Source, SourceSection}
import org.enso.compiler.core.IR
import org.enso.syntax.text.Location

import scala.collection.mutable

/** Contains instrumentable source locations.
*
* @param sections the list of source sections to instrument
*/
case class LocationFilter(sections: Set[SourceSection]) {

/** Get the list of source sections to instrument. */
def getSections: Array[SourceSection] =
sections.toArray
}

object LocationFilter {

private type Builder = mutable.Set[Location]

/** Create the location filter.
*
* @param ir the module ir
* @param span the source section to instrument
* @return new location filter
*/
def create(ir: IR.Module, span: SourceSection): LocationFilter = {
val location = Location(span.getCharIndex, span.getCharEndIndex)
val targetNode = LocationResolver.findByLocation(ir, location).getOrElse(ir)
val locations = createLocations(targetNode)

LocationFilter(locations.map(toSection(span.getSource, _)))
}

private def createLocations(ir: IR): Set[Location] = {
val builder = mutable.Set.empty[Location]
analyzeEnterable(ir, builder)
builder.result().toSet
}

private def analyzeEnterable(ir: IR, builder: Builder): Unit =
ir match {
case module: IR.Module =>
module.bindings.foreach(analyzeModuleDefinition(_, builder))
case definition: IR.Module.Scope.Definition =>
analyzeModuleDefinition(definition, builder)
case function: IR.Function =>
analyzeBody(function.body, builder)
case expression: IR.Expression =>
analyzeExpression(expression, builder)
case _ =>
}

private def analyzeModuleDefinition(
binding: IR.Module.Scope.Definition,
builder: Builder
): Unit =
binding match {
case method: IR.Module.Scope.Definition.Method.Explicit =>
analyzeBody(method.body, builder)
case _ =>
}

@scala.annotation.tailrec
private def analyzeBody(ir: IR.Expression, builder: Builder): Unit =
ir match {
case function: IR.Function =>
analyzeBody(function.body, builder)
case expression =>
analyzeExpression(expression, builder)
}

private def analyzeExpression(expression: IR, builder: Builder): Unit = {
@scala.annotation.tailrec
def go(queue: mutable.Queue[IR]): Unit = {
if (queue.nonEmpty) {
val element = queue.dequeue()
val location = getLocation(element)

element match {
case IR.Expression.Binding(_, function: IR.Function, _, _, _)
if isSynthetic(function) =>
builder ++= getLocation(function)
case IR.Expression.Binding(_, block: IR.Expression.Block, _, _, _) =>
builder ++= getLocation(block)
case function: IR.Function =>
if (!isSynthetic(function)) {
builder ++= location
}
case _ =>
builder ++= location
queue ++= element.children
}

go(queue)
}
}

go(mutable.Queue(expression))
}

private def isSynthetic(function: IR.Function): Boolean = {
val irLocation = getLocation(function)
irLocation.isDefined && irLocation == getLocation(function.body)
}

private def toSection(source: Source, location: Location): SourceSection =
source.createSection(location.start, location.length)

private def getLocation(ir: IR): Option[Location] =
ir.location.map(_.location)
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ object LocationResolver {
ir: IR,
location: Location
): Option[ExpressionId] =
ir.preorder
.find(_.location.map(_.location).contains(location))
.flatMap(getExpressionId)
findByLocation(ir, location).flatMap(getExpressionId)

/** Get the id of the given `IR`.
*
Expand All @@ -86,6 +84,15 @@ object LocationResolver {
def getExpressionId(ir: IR): Option[ExpressionId] =
ir.getExternalId.map(ExpressionId(ir.getId, _))

/** Find the expression by its location.
*
* @param ir the `IR` to get the expression from
* @param location the expression location
* @return the expression with the given location
*/
def findByLocation(ir: IR, location: Location): Option[IR] =
ir.preorder.find(_.location.map(_.location).contains(location))

/** Convert truffle source section to the range of text.
*
* @param section the source section
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.enso.interpreter.instrument.job

import java.util.UUID

import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode
import org.enso.pkg.QualifiedName
import org.enso.polyglot.runtime.Runtime.Api
Expand Down Expand Up @@ -38,8 +40,11 @@ object ExecutionItem {

/** The call data captured during the program execution.
*
* @param expressionId the expression identifier
* @param callData the fucntion call data
*/
case class CallData(callData: FunctionCallInstrumentationNode.FunctionCall)
extends ExecutionItem
case class CallData(
expressionId: UUID,
callData: FunctionCallInstrumentationNode.FunctionCall
) extends ExecutionItem
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import org.enso.interpreter.runtime.`type`.Types
import org.enso.interpreter.service.error.{
ConstructorNotFoundException,
MethodNotFoundException,
ModuleNotFoundForExpressionIdException,
ServiceException
}
import org.enso.polyglot.LanguageInfo
Expand Down Expand Up @@ -90,8 +91,18 @@ trait ProgramExecutionSupport {
onCachedCallback,
onExceptionalCallback
)
case ExecutionFrame(ExecutionItem.CallData(callData), cache) =>
case ExecutionFrame(
ExecutionItem.CallData(expressionId, callData),
cache
) =>
val module =
ctx.executionService.getContext
.findModuleByExpressionId(expressionId)
.orElseThrow(() =>
new ModuleNotFoundForExpressionIdException(expressionId)
)
ctx.executionService.execute(
module,
callData,
cache,
methodCallsCache,
Expand Down Expand Up @@ -127,7 +138,10 @@ trait ProgramExecutionSupport {
enterables.get(item.expressionId) match {
case Some(call) =>
executeProgram(
ExecutionFrame(ExecutionItem.CallData(call), item.cache),
ExecutionFrame(
ExecutionItem.CallData(item.expressionId, call),
item.cache
),
tail,
onCachedMethodCallCallback,
onComputedCallback,
Expand Down Expand Up @@ -237,7 +251,7 @@ trait ProgramExecutionSupport {
)(implicit ctx: RuntimeContext): Api.ExecutionResult = {
val itemName = item match {
case ExecutionItem.Method(_, _, function) => function
case ExecutionItem.CallData(call) => call.getFunction.getName
case ExecutionItem.CallData(_, call) => call.getFunction.getName
}
val executionUpdate = getExecutionOutcome(error)
executionUpdate match {
Expand Down
Loading

0 comments on commit b0680d0

Please sign in to comment.