CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-graalvm-polyglot--polyglot

GraalVM Polyglot API for embedding multiple programming languages in Java applications with secure language interoperability

Pending
Overview
Eval results
Files

execution-monitoring.mddocs/

Execution Monitoring

Execution monitoring provides advanced capabilities for observing, profiling, and debugging polyglot execution environments. It enables detailed execution events, performance metrics, and instrumentation across all supported languages with minimal performance overhead.

Capabilities

Execution Listener

Monitor execution events across polyglot contexts.

public final class ExecutionListener implements AutoCloseable {
    public static ExecutionListener.Builder newBuilder();
    public ExecutionListener attach(Engine engine);
    public void close();
}

public static final class ExecutionListener.Builder {
    public ExecutionListener.Builder onEnter(Consumer<ExecutionEvent> callback);
    public ExecutionListener.Builder onReturn(Consumer<ExecutionEvent> callback);
    public ExecutionListener.Builder expressions(boolean enabled);
    public ExecutionListener.Builder statements(boolean enabled);
    public ExecutionListener.Builder roots(boolean enabled);
    public ExecutionListener.Builder sourceFilter(Predicate<Source> filter);
    public ExecutionListener.Builder rootNameFilter(Predicate<String> filter);
    public ExecutionListener build();
}

Usage:

// Basic execution monitoring
ExecutionListener listener = ExecutionListener.newBuilder()
    .onEnter(event -> {
        System.out.println("Entering: " + event.getLocation());
    })
    .onReturn(event -> {
        System.out.println("Returning: " + event.getReturnValue());
    })
    .statements(true)
    .expressions(true)
    .build();

try (Engine engine = Engine.create()) {
    ExecutionListener attached = listener.attach(engine);
    
    try (Context context = Context.newBuilder("js").engine(engine).build()) {
        // All execution in this context will be monitored
        context.eval("js", "function add(a, b) { return a + b; } add(2, 3);");
    }
    
    attached.close();
}

Execution Events

Access detailed information about execution events.

public final class ExecutionEvent {
    public SourceSection getLocation();
    public Value getReturnValue();
    public RuntimeException getException();
    public List<Value> getInputValues();
    public boolean hasReturnValue();
    public boolean hasException();
}

Usage:

ExecutionListener listener = ExecutionListener.newBuilder()
    .onEnter(event -> {
        SourceSection location = event.getLocation();
        System.out.println("Executing at " + location.getSource().getName() + 
            ":" + location.getStartLine());
        
        // Access input values (function parameters, etc.)
        List<Value> inputs = event.getInputValues();
        System.out.println("Input values: " + inputs);
    })
    .onReturn(event -> {
        if (event.hasReturnValue()) {
            Value returnValue = event.getReturnValue();
            System.out.println("Returned: " + returnValue);
        }
        
        if (event.hasException()) {
            RuntimeException exception = event.getException();
            System.out.println("Exception: " + exception.getMessage());
        }
    })
    .statements(true)
    .build();

Filtered Monitoring

Monitor specific sources or functions only.

// Monitor only specific sources
ExecutionListener sourceFiltered = ExecutionListener.newBuilder()
    .sourceFilter(source -> source.getName().endsWith(".js"))
    .onEnter(event -> System.out.println("JS execution: " + event.getLocation()))
    .statements(true)
    .build();

// Monitor only specific function names
ExecutionListener functionFiltered = ExecutionListener.newBuilder()
    .rootNameFilter(name -> name.equals("criticalFunction"))
    .onEnter(event -> System.out.println("Critical function called"))
    .roots(true)
    .build();

// Monitor expressions only (no statements)
ExecutionListener expressionOnly = ExecutionListener.newBuilder()
    .expressions(true)
    .statements(false)
    .onReturn(event -> {
        if (event.hasReturnValue()) {
            System.out.println("Expression result: " + event.getReturnValue());
        }
    })
    .build();

Advanced Monitoring Patterns

Performance Profiling

Create custom profilers using execution monitoring:

public class SimpleProfiler {
    private final Map<String, Long> executionTimes = new ConcurrentHashMap<>();
    private final Map<String, AtomicLong> callCounts = new ConcurrentHashMap<>();
    private final ThreadLocal<Long> enterTime = new ThreadLocal<>();
    
    public ExecutionListener createListener() {
        return ExecutionListener.newBuilder()
            .onEnter(event -> {
                enterTime.set(System.nanoTime());
            })
            .onReturn(event -> {
                long duration = System.nanoTime() - enterTime.get();
                String location = getLocationKey(event.getLocation());
                
                executionTimes.merge(location, duration, Long::sum);
                callCounts.computeIfAbsent(location, k -> new AtomicLong(0)).incrementAndGet();
            })
            .statements(true)
            .roots(true)
            .build();
    }
    
    public void printProfile() {
        System.out.println("Execution Profile:");
        executionTimes.entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
            .forEach(entry -> {
                String location = entry.getKey();
                long totalTime = entry.getValue();
                long callCount = callCounts.get(location).get();
                long avgTime = totalTime / callCount;
                
                System.out.printf("%s: %d calls, %d ns total, %d ns avg%n",
                    location, callCount, totalTime, avgTime);
            });
    }
    
    private String getLocationKey(SourceSection location) {
        return location.getSource().getName() + ":" + location.getStartLine();
    }
}

// Usage
SimpleProfiler profiler = new SimpleProfiler();
ExecutionListener listener = profiler.createListener();

try (Engine engine = Engine.create()) {
    ExecutionListener attached = listener.attach(engine);
    
    try (Context context = Context.newBuilder("js").engine(engine).build()) {
        context.eval("js", "function fib(n) { return n < 2 ? n : fib(n-1) + fib(n-2); } fib(10);");
    }
    
    profiler.printProfile();
    attached.close();
}

Code Coverage Analysis

Track code coverage across polyglot execution:

public class CoverageAnalyzer {
    private final Set<String> executedLines = ConcurrentHashMap.newKeySet();
    private final Map<String, Set<Integer>> sourceLines = new ConcurrentHashMap<>();
    
    public ExecutionListener createListener() {
        return ExecutionListener.newBuilder()
            .onEnter(event -> {
                SourceSection location = event.getLocation();
                String sourceName = location.getSource().getName();
                int lineNumber = location.getStartLine();
                
                executedLines.add(sourceName + ":" + lineNumber);
                
                // Track all lines in this source
                sourceLines.computeIfAbsent(sourceName, k -> ConcurrentHashMap.newKeySet())
                    .add(lineNumber);
            })
            .statements(true)
            .build();
    }
    
    public void printCoverage() {
        System.out.println("Code Coverage Report:");
        sourceLines.forEach((sourceName, lines) -> {
            long totalLines = lines.size();
            long executedCount = lines.stream()
                .map(line -> sourceName + ":" + line)
                .mapToLong(key -> executedLines.contains(key) ? 1 : 0)
                .sum();
            
            double coverage = (double) executedCount / totalLines * 100;
            System.out.printf("%s: %.1f%% (%d/%d lines)%n", 
                sourceName, coverage, executedCount, totalLines);
        });
    }
}

Exception Tracking

Monitor and analyze exceptions across polyglot execution:

public class ExceptionTracker {
    private final List<ExceptionInfo> exceptions = new CopyOnWriteArrayList<>();
    
    public ExecutionListener createListener() {
        return ExecutionListener.newBuilder()
            .onReturn(event -> {
                if (event.hasException()) {
                    RuntimeException exception = event.getException();
                    SourceSection location = event.getLocation();
                    
                    exceptions.add(new ExceptionInfo(
                        exception.getClass().getSimpleName(),
                        exception.getMessage(),
                        location.getSource().getName(),
                        location.getStartLine(),
                        System.currentTimeMillis()
                    ));
                }
            })
            .statements(true)
            .expressions(true)
            .build();
    }
    
    public void printExceptionSummary() {
        Map<String, Long> exceptionCounts = exceptions.stream()
            .collect(Collectors.groupingBy(
                ExceptionInfo::getType,
                Collectors.counting()
            ));
        
        System.out.println("Exception Summary:");
        exceptionCounts.entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
            .forEach(entry -> {
                System.out.printf("%s: %d occurrences%n", entry.getKey(), entry.getValue());
            });
    }
    
    private static class ExceptionInfo {
        private final String type;
        private final String message;
        private final String source;
        private final int line;
        private final long timestamp;
        
        // Constructor and getters...
    }
}

Real-time Monitoring

Create real-time monitoring dashboards:

public class RealTimeMonitor {
    private final AtomicLong executionCount = new AtomicLong(0);
    private final AtomicLong exceptionCount = new AtomicLong(0);
    private final ConcurrentHashMap<String, AtomicLong> languageStats = new ConcurrentHashMap<>();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    
    public ExecutionListener createListener() {
        // Start periodic reporting
        scheduler.scheduleAtFixedRate(this::printStats, 0, 5, TimeUnit.SECONDS);
        
        return ExecutionListener.newBuilder()
            .onEnter(event -> {
                executionCount.incrementAndGet();
                
                String language = event.getLocation().getSource().getLanguage();
                languageStats.computeIfAbsent(language, k -> new AtomicLong(0))
                    .incrementAndGet();
            })
            .onReturn(event -> {
                if (event.hasException()) {
                    exceptionCount.incrementAndGet();
                }
            })
            .statements(true)
            .build();
    }
    
    private void printStats() {
        System.out.println("=== Real-time Stats ===");
        System.out.println("Total executions: " + executionCount.get());
        System.out.println("Total exceptions: " + exceptionCount.get());
        System.out.println("By language:");
        languageStats.forEach((lang, count) -> {
            System.out.println("  " + lang + ": " + count.get());
        });
        System.out.println();
    }
    
    public void shutdown() {
        scheduler.shutdown();
    }
}

Multi-Context Monitoring

Monitor execution across multiple contexts:

public class MultiContextMonitor {
    private final Map<Context, String> contextNames = new ConcurrentHashMap<>();
    private final Map<String, AtomicLong> contextStats = new ConcurrentHashMap<>();
    
    public void registerContext(Context context, String name) {
        contextNames.put(context, name);
        contextStats.put(name, new AtomicLong(0));
    }
    
    public ExecutionListener createListener() {
        return ExecutionListener.newBuilder()
            .onEnter(event -> {
                Context currentContext = Context.getCurrent();
                String contextName = contextNames.get(currentContext);
                if (contextName != null) {
                    contextStats.get(contextName).incrementAndGet();
                }
            })
            .statements(true)
            .build();
    }
    
    public void printContextStats() {
        System.out.println("Context Execution Stats:");
        contextStats.forEach((name, count) -> {
            System.out.println(name + ": " + count.get() + " statements");
        });
    }
}

Integration with Development Tools

IDE Integration

Export execution data for IDE consumption:

public class IDEIntegration {
    public void exportExecutionTrace(List<ExecutionEvent> events, Path outputFile) {
        try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(outputFile))) {
            writer.println("timestamp,source,line,event_type,value");
            
            events.forEach(event -> {
                SourceSection location = event.getLocation();
                writer.printf("%d,%s,%d,%s,%s%n",
                    System.currentTimeMillis(),
                    location.getSource().getName(),
                    location.getStartLine(),
                    event.hasReturnValue() ? "return" : "enter",
                    event.hasReturnValue() ? event.getReturnValue().toString() : ""
                );
            });
        } catch (IOException e) {
            throw new RuntimeException("Failed to export execution trace", e);
        }
    }
}

Performance Testing

Integrate with performance testing frameworks:

public class PerformanceTestIntegration {
    public void runPerformanceTest(String testName, Runnable test) {
        ExecutionListener listener = ExecutionListener.newBuilder()
            .onEnter(event -> recordMetric("execution.enter", 1))
            .onReturn(event -> {
                recordMetric("execution.return", 1);
                if (event.hasException()) {
                    recordMetric("execution.exception", 1);
                }
            })
            .statements(true)
            .build();
        
        try (Engine engine = Engine.create()) {
            ExecutionListener attached = listener.attach(engine);
            
            long startTime = System.nanoTime();
            test.run();
            long duration = System.nanoTime() - startTime;
            
            recordMetric("test.duration", duration);
            attached.close();
        }
    }
    
    private void recordMetric(String name, long value) {
        // Integration with metrics collection system
        // MetricsRegistry.record(name, value);
    }
}

Performance Considerations

  • Selective Monitoring: Only monitor what you need; excessive monitoring can impact performance
  • Filtering: Use source and root name filters to reduce monitoring overhead
  • Async Processing: Process monitoring data asynchronously to minimize execution impact
  • Memory Management: Be careful with data collection in long-running applications
  • Sampling: Consider sampling for high-frequency applications

Thread Safety

All monitoring components are thread-safe and can be used across multiple contexts and threads simultaneously. However, be careful with shared data structures and consider using concurrent collections for custom monitoring implementations.

Install with Tessl CLI

npx tessl i tessl/maven-org-graalvm-polyglot--polyglot

docs

context-management.md

engine-management.md

execution-monitoring.md

index.md

io-abstractions.md

proxy-system.md

security-configuration.md

source-management.md

value-operations.md

tile.json