Skip to content

Commit

Permalink
Added support for condition, hit, and log break point.
Browse files Browse the repository at this point in the history
  • Loading branch information
ylussaud committed Apr 23, 2024
1 parent 16cc38c commit ba47b2c
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -51,11 +52,14 @@
import org.eclipse.acceleo.debug.AbstractDSLDebugger;
import org.eclipse.acceleo.debug.DSLSource;
import org.eclipse.acceleo.debug.event.IDSLDebugEventProcessor;
import org.eclipse.acceleo.debug.ls.DSLDebugServer;
import org.eclipse.acceleo.debug.util.FrameVariable;
import org.eclipse.acceleo.debug.util.StackFrame;
import org.eclipse.acceleo.query.AQLUtils;
import org.eclipse.acceleo.query.AQLUtils.AcceleoAQLResult;
import org.eclipse.acceleo.query.ast.VariableDeclaration;
import org.eclipse.acceleo.query.ide.QueryPlugin;
import org.eclipse.acceleo.query.runtime.EvaluationResult;
import org.eclipse.acceleo.query.runtime.namespace.IQualifiedNameQueryEnvironment;
import org.eclipse.acceleo.query.runtime.namespace.IQualifiedNameResolver;
import org.eclipse.core.resources.IContainer;
Expand All @@ -74,6 +78,7 @@
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.swt.widgets.Display;

Expand All @@ -100,7 +105,8 @@ public void run() {
if (isNoDebug()) {
generateNoDebug(queryEnvironment, module, model);
} else {
evaluator = new AcceleoDebugEvaluator(queryEnvironment, newLine);
evaluator = new AcceleoDebugEvaluator(queryEnvironment, newLine,
getBreakPointsHitCounts());

final IQualifiedNameResolver resolver = queryEnvironment.getLookupEngine().getResolver();
resolver.clearLoaders();
Expand Down Expand Up @@ -129,6 +135,7 @@ public IAcceleoWriter createWriterFor(URI uri, OpenModeKind openMode, Charset ch
// process. By launching the termination in the UI thread in sync we allow the jobs to
// finish first.
Display.getDefault().syncExec(new Runnable() {

@Override
public void run() {
terminate(threadID);
Expand All @@ -152,6 +159,29 @@ public void run() {
}
}
}

/**
* Gets the breakpoint hit counts map.
*
* @return the breakpoint hit counts map
*/
private Map<URI, Integer> getBreakPointsHitCounts() {
final Map<URI, Integer> res = new HashMap<>();

for (Entry<URI, Map<String, String>> entry : getBreakpoints().entrySet()) {
final String hitCondition = entry.getValue().get(
DSLDebugServer.HIT_CONDITION_BREAKPOINT_ATTRIBUTE);
if (hitCondition != null) {
try {
res.put(entry.getKey(), Integer.valueOf(hitCondition));
} catch (NumberFormatException e) {
consolePrint("hit condition is not an integer: " + hitCondition);
}
}
}

return res;
}
}

/**
Expand All @@ -171,16 +201,25 @@ private final class AcceleoDebugEvaluator extends AcceleoEvaluator {
*/
private Deque<List<String>> blockTextLists = new ArrayDeque<>();

/**
* The mapping from an instruction {@link URI} to its hit count.
*/
private final Map<URI, Integer> breakPointHitCounts;

/**
* Constructor.
*
* @param queryEnvironment
* the {@link IQualifiedNameQueryEnvironment}
* @param newLine
* the new line {@link String}
* @param breakPointHitCounts
* the break point hit count map
*/
AcceleoDebugEvaluator(IQualifiedNameQueryEnvironment queryEnvironment, String newLine) {
AcceleoDebugEvaluator(IQualifiedNameQueryEnvironment queryEnvironment, String newLine,
Map<URI, Integer> breakPointHitCounts) {
super(queryEnvironment.getLookupEngine(), newLine);
this.breakPointHitCounts = breakPointHitCounts;
}

/**
Expand Down Expand Up @@ -239,6 +278,83 @@ protected List<String> createBlockTextsList(Block block) {
return blockTextLists.peekLast();
}

/**
* Tells if we should break on the given {@link EObject instruction}.
*
* @param instruction
* the {@link EObject instruction}
* @return <code>true</code> if we should break on the given {@link EObject instruction},
* <code>false</code> otherwise
*/
public boolean shouldBreak(EObject instruction) {
boolean res;

// check the condition expression
final String conditionExpression = getBreakpointAttributes(instruction,
DSLDebugServer.CONDITION_BREAKPOINT_ATTRIBUTE);
if (conditionExpression != null) {
final Object value = evaluateExpression(conditionExpression);
res = value == null || (value instanceof Boolean && ((Boolean)value));
} else {
res = true;
}

// check hit count
if (res) {
final URI uri = EcoreUtil.getURI(instruction);
final Integer count = breakPointHitCounts.get(uri);
if (count != null && count > 0) {
breakPointHitCounts.put(uri, count - 1);
res = false;
}
}

// check log expression
if (res) {
final String logExpression = getBreakpointAttributes(instruction,
DSLDebugServer.LOG_MESSAGE_BREAKPOINT_ATTRIBUTE);
if (logExpression != null) {
final Object value = evaluateExpression(logExpression);
if (value != null) {
consolePrint(value.toString());
}
res = false;
}
}

return res;
}

/**
* Parses and evaluate the given AQL expression in the current debug context.
*
* @param expression
* the AQL expression
* @return the evaluated {@link Object}
*/
private Object evaluateExpression(final String expression) {
final Object res;

final AcceleoAQLResult result = AQLUtils.parseWhileAqlExpression(expression.toString());
if (result.getAstResult().getDiagnostic().getSeverity() == Diagnostic.ERROR) {
consolePrint("parsing error: " + expression);
printDiagnostic(result.getAstResult().getDiagnostic(), "");
res = null;
} else {
final EvaluationResult value = evaluator.getAqlEngine().eval(result.getAstResult(),
peekVariables());
if (value.getDiagnostic().getSeverity() == Diagnostic.ERROR) {
consolePrint("evaluation error: " + expression);
printDiagnostic(result.getAstResult().getDiagnostic(), "");
res = null;
} else {
res = value.getResult();
}
}

return res;
}

}

/**
Expand Down Expand Up @@ -669,4 +785,17 @@ public FrameVariable getFrameVariable(String name, Object value) {
return res;
}

@Override
public boolean shouldBreak(EObject instruction) {
final boolean res;

if (super.shouldBreak(instruction)) {
res = evaluator.shouldBreak(instruction);
} else {
res = false;
}

return res;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.eclipse.acceleo.debug.event.debugger.VariableReply;
import org.eclipse.acceleo.debug.event.model.AbstractModelEventProcessor;
import org.eclipse.acceleo.debug.util.FrameVariable;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
Expand Down Expand Up @@ -128,6 +129,21 @@

public class DSLDebugServer extends AbstractModelEventProcessor implements IDebugProtocolServer {

/**
* The condition breakpoint attribute name.
*/
public static final String CONDITION_BREAKPOINT_ATTRIBUTE = "condition";

/**
* The hit condition breakpoint attribute name.
*/
public static final String HIT_CONDITION_BREAKPOINT_ATTRIBUTE = "hitCondition";

/**
* The log message breakpoint attribute name.
*/
public static final String LOG_MESSAGE_BREAKPOINT_ATTRIBUTE = "logMessage";

/**
* The {@link IDebugProtocolClient}.
*/
Expand Down Expand Up @@ -214,31 +230,50 @@ private Capabilities getCapabilities() {
final Capabilities res = new Capabilities();

// TODO
res.setSupportsCompletionsRequest(true);
res.setSupportsConditionalBreakpoints(true);
res.setSupportsConfigurationDoneRequest(true);
res.setSupportsDataBreakpoints(false);
res.setSupportsDelayedStackTraceLoading(false);
res.setSupportsDisassembleRequest(false);
res.setSupportsEvaluateForHovers(false);
res.setSupportsExceptionInfoRequest(false);
res.setSupportsExceptionOptions(false);
res.setSupportsFunctionBreakpoints(false);
res.setSupportsGotoTargetsRequest(false);
res.setSupportsConditionalBreakpoints(true);
res.setSupportsHitConditionalBreakpoints(true);
res.setSupportsLoadedSourcesRequest(false);
res.setSupportsLogPoints(false);
res.setSupportsModulesRequest(false);
res.setSupportsReadMemoryRequest(false);
res.setSupportsEvaluateForHovers(false);
// ExceptionBreakpointsFilter[]
res.setExceptionBreakpointFilters(null);
res.setSupportsStepBack(false);
res.setSupportsSetVariable(false);
res.setSupportsRestartFrame(false);
res.setSupportsGotoTargetsRequest(false);
res.setSupportsStepInTargetsRequest(false);
res.setSupportsCompletionsRequest(true);
// String[]
res.setCompletionTriggerCharacters(null);
res.setSupportsModulesRequest(false);
// ColumnDescriptor[]
res.setAdditionalModuleColumns(null);
// ChecksumAlgorithm[]
res.setSupportedChecksumAlgorithms(null);
res.setSupportsRestartRequest(false);
res.setSupportsExceptionOptions(false);
res.setSupportsValueFormattingOptions(true);
res.setSupportsExceptionInfoRequest(false);
res.setSupportTerminateDebuggee(null);
res.setSupportSuspendDebuggee(null);
res.setSupportsDelayedStackTraceLoading(false);
res.setSupportsLoadedSourcesRequest(false);
res.setSupportsLogPoints(true);
res.setSupportsTerminateThreadsRequest(true);
res.setSupportsSetExpression(false);
res.setSupportsSetVariable(false);
res.setSupportsStepBack(false);
res.setSupportsStepInTargetsRequest(false);
res.setSupportsTerminateRequest(true);
res.setSupportsTerminateThreadsRequest(true);
res.setSupportsValueFormattingOptions(false);
res.setSupportsDataBreakpoints(false);
res.setSupportsReadMemoryRequest(false);
res.setSupportsWriteMemoryRequest(null);
res.setSupportsDisassembleRequest(false);
res.setSupportsCancelRequest(null);
res.setSupportsBreakpointLocationsRequest(null);
res.setSupportsClipboardContext(null);
res.setSupportsSteppingGranularity(null);
res.setSupportsInstructionBreakpoints(null);
res.setSupportsExceptionFilterOptions(null);
res.setSupportsSingleThreadExecutionRequests(null);

return res;
}

Expand Down Expand Up @@ -284,7 +319,21 @@ private SetBreakpointsResponse getSetBreakpointResponse(final SetBreakpointsArgu
final EObject instruction = debugger.getInstruction(args.getSource().getPath(),
requestedBreakpoint.getLine(), column);
if (instruction != null) {
debugger.addBreakPoint(EcoreUtil.getURI(instruction));
final URI instructionURI = EcoreUtil.getURI(instruction);
debugger.addBreakPoint(instructionURI);
final String condition = requestedBreakpoint.getCondition();
if (condition != null) {
debugger.changeBreakPoint(instructionURI, CONDITION_BREAKPOINT_ATTRIBUTE, condition);
}
final String hitCondition = requestedBreakpoint.getHitCondition();
if (hitCondition != null) {
debugger.changeBreakPoint(instructionURI, HIT_CONDITION_BREAKPOINT_ATTRIBUTE,
hitCondition);
}
final String logMessage = requestedBreakpoint.getLogMessage();
if (logMessage != null) {
debugger.changeBreakPoint(instructionURI, LOG_MESSAGE_BREAKPOINT_ATTRIBUTE, logMessage);
}
final Breakpoint responseBreakpoint = new Breakpoint();
final DSLSource dslSource = debugger.getSource(instruction);
if (dslSource != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
*******************************************************************************/
package org.eclipse.acceleo.debug;

import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
Expand Down Expand Up @@ -87,7 +86,7 @@ public abstract class AbstractDSLDebugger implements IDSLDebugger {
/**
* Instructions marked as breakpoints with their attributes.
*/
private final Map<URI, Map<String, Serializable>> breakpoints = new HashMap<URI, Map<String, Serializable>>();
private final Map<URI, Map<String, String>> breakpoints = new HashMap<URI, Map<String, String>>();

/**
* Mapping from the thread id to is {@link StackFrame}.
Expand Down Expand Up @@ -116,7 +115,7 @@ public AbstractDSLDebugger(IDSLDebugEventProcessor target) {
*
* @return the mapping of instructions marked as breakpoints to their attributes
*/
protected Map<URI, Map<String, Serializable>> getBreakpoints() {
protected Map<URI, Map<String, String>> getBreakpoints() {
return breakpoints;
}

Expand Down Expand Up @@ -331,10 +330,10 @@ public boolean shouldBreak(EObject instruction) {
* the attribute
* @return the value of the given breakpoint attribute if any, <code>null</code> otherwise
*/
protected Serializable getBreakpointAttributes(EObject instruction, String attribute) {
final Serializable res;
protected String getBreakpointAttributes(EObject instruction, String attribute) {
final String res;

Map<String, Serializable> attributes = breakpoints.get(EcoreUtil.getURI(instruction));
Map<String, String> attributes = breakpoints.get(EcoreUtil.getURI(instruction));
if (attributes != null) {
res = attributes.get(attribute);
} else {
Expand All @@ -345,7 +344,7 @@ protected Serializable getBreakpointAttributes(EObject instruction, String attri
}

public void addBreakPoint(URI instruction) {
breakpoints.put(instruction, new HashMap<String, Serializable>());
breakpoints.put(instruction, new LinkedHashMap<>());
}

public void removeBreakPoint(URI instruction) {
Expand All @@ -356,8 +355,8 @@ public void clearBreakPoints() {
breakpoints.clear();
}

public void changeBreakPoint(URI instruction, String attribute, Serializable value) {
final Map<String, Serializable> attributes = breakpoints.get(instruction);
public void changeBreakPoint(URI instruction, String attribute, String value) {
final Map<String, String> attributes = breakpoints.get(instruction);
attributes.put(attribute, value);
}

Expand Down
Loading

0 comments on commit ba47b2c

Please sign in to comment.