Skip to content

Commit 3c68e3d

Browse files
committed
Add support for finding precisely requested source section even when trying to approximate
- added sectionLength for breakpoints to make them precise - this also uses perfect matches when possible to create breakpoint filter Signed-off-by: Stefan Marr <git@stefan-marr.de>
1 parent 6998026 commit 3c68e3d

File tree

3 files changed

+111
-27
lines changed

3 files changed

+111
-27
lines changed

truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/Breakpoint.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,7 @@ public final class Builder {
790790
private int line = -1;
791791
private SuspendAnchor anchor = SuspendAnchor.BEFORE;
792792
private int column = -1;
793+
private int sectionLength = -1;
793794
private ResolveListener resolveListener;
794795
private int ignoreCount;
795796
private boolean oneShot;
@@ -869,6 +870,36 @@ public Builder columnIs(@SuppressWarnings("hiding") int column) {
869870
return this;
870871
}
871872

873+
/**
874+
* Specifies the breakpoint's section length and makes this breakpoint precise. This means
875+
* that Truffle won't try to adjust the breakpoint to a possible adjacent location.
876+
*
877+
* <p>
878+
* Requires {@link #columnIs(int) starting column} to be set as well.
879+
*
880+
* <p>
881+
* Can only be invoked once per builder. Cannot be used together with
882+
* {@link Breakpoint#newBuilder(SourceSection)}.
883+
*
884+
* @param length number of characters in the source section
885+
* @throws IllegalStateException if {@code length < 1}
886+
*
887+
* @since smarr/debugger
888+
*/
889+
public Builder sectionLength(int length) {
890+
if (length <= 0) {
891+
throw new IllegalArgumentException("Length argument must be > 0.");
892+
}
893+
if (this.sectionLength != -1) {
894+
throw new IllegalStateException("SectionLength can only be called once per breakpoint builder.");
895+
}
896+
if (sourceSection != null) {
897+
throw new IllegalArgumentException("SectionLength cannot be used with source section based breakpoint. ");
898+
}
899+
this.sectionLength = length;
900+
return this;
901+
}
902+
872903
/**
873904
* Set a resolve listener. The listener is called when the breakpoint is resolved at the
874905
* target location. A breakpoint is not resolved till the target source section is loaded.
@@ -954,14 +985,20 @@ public Builder tag(Class<? extends Tag> filterTag) {
954985
* @since 0.17
955986
*/
956987
public Breakpoint build() {
988+
if (sectionLength != -1) {
989+
if (column == -1) {
990+
throw new IllegalArgumentException("If sectionLength is set, we also need column to be set.");
991+
}
992+
}
993+
957994
if (sourceElements == null) {
958995
sourceElements = new SourceElement[]{SourceElement.STATEMENT};
959996
}
960997
BreakpointLocation location;
961998
if (sourceSection != null) {
962999
location = BreakpointLocation.create(key, sourceElements, sourceSection, tag);
9631000
} else {
964-
location = BreakpointLocation.create(key, sourceElements, line, column, tag);
1001+
location = BreakpointLocation.create(key, sourceElements, line, column, sectionLength, tag);
9651002
}
9661003
Breakpoint breakpoint = new Breakpoint(location, anchor, oneShot, null, resolveListener);
9671004
breakpoint.setIgnoreCount(ignoreCount);

truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/BreakpointLocation.java

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ static BreakpointLocation create(Object key, SourceElement[] sourceElements, Sou
5656
return new BreakpointSourceLocation(key, sourceElements, sourceSection, tag);
5757
}
5858

59-
static BreakpointLocation create(Object key, SourceElement[] sourceElements, int line, int column, Class<? extends Tag> tag) {
60-
return new BreakpointSourceLocation(key, sourceElements, line, column, tag);
59+
static BreakpointLocation create(Object key, SourceElement[] sourceElements, int line, int column, int sectionLength, Class<? extends Tag> tag) {
60+
return new BreakpointSourceLocation(key, sourceElements, line, column, sectionLength, tag);
6161
}
6262

6363
static BreakpointLocation create(SourceElement[] sourceElements, SuspensionFilter filter) {
@@ -88,6 +88,9 @@ private static final class BreakpointSourceLocation extends BreakpointLocation {
8888
private final SourceSection sourceSection;
8989
private int line;
9090
private int column;
91+
private int sectionLength;
92+
93+
private SourceSection perfectMatch;
9194

9295
private final Class<? extends Tag> tag;
9396

@@ -102,6 +105,7 @@ private static final class BreakpointSourceLocation extends BreakpointLocation {
102105
this.sourceSection = sourceSection;
103106
this.line = -1;
104107
this.column = -1;
108+
this.sectionLength = -1;
105109
this.tag = tag;
106110
}
107111

@@ -110,14 +114,15 @@ private static final class BreakpointSourceLocation extends BreakpointLocation {
110114
* @param line 1-based line number
111115
* @param column 1-based column number, -1 for unspecified
112116
*/
113-
BreakpointSourceLocation(Object key, SourceElement[] sourceElements, int line, int column, Class<? extends Tag> tag) {
117+
BreakpointSourceLocation(Object key, SourceElement[] sourceElements, int line, int column, int sectionLength, Class<? extends Tag> tag) {
114118
assert key instanceof Source || key instanceof URI;
115119
assert line > 0;
116120
assert column > 0 || column == -1;
117121
this.key = key;
118122
this.sourceElements = sourceElements;
119123
this.line = line;
120124
this.column = column;
125+
this.sectionLength = sectionLength;
121126
this.sourceSection = null;
122127
this.tag = tag;
123128
}
@@ -127,6 +132,7 @@ private BreakpointSourceLocation() {
127132
this.sourceElements = null;
128133
this.line = -1;
129134
this.column = -1;
135+
this.sectionLength = -1;
130136
this.sourceSection = null;
131137
this.tag = null;
132138
}
@@ -164,27 +170,39 @@ SourceSection adjustLocation(Source source, TruffleInstrument.Env env, SuspendAn
164170
if (sourceSection != null) {
165171
return sourceSection;
166172
}
173+
if (perfectMatch != null) {
174+
return perfectMatch;
175+
}
167176
if (key == null) {
168177
return null;
169178
}
170179
boolean hasColumn = column > 0;
171-
SourceSection location = SuspendableLocationFinder.findNearest(source, sourceElements, line, column, tag, suspendAnchor, env);
180+
boolean hasLength = sectionLength > 0;
181+
SourceSection location = SuspendableLocationFinder.findNearest(source, sourceElements, line, column, sectionLength, tag, suspendAnchor, env);
172182
if (location != null) {
173-
switch (suspendAnchor) {
174-
case BEFORE:
175-
line = location.getStartLine();
176-
if (hasColumn) {
177-
column = location.getStartColumn();
178-
}
179-
break;
180-
case AFTER:
181-
line = location.getEndLine();
182-
if (hasColumn) {
183-
column = location.getEndColumn();
184-
}
185-
break;
186-
default:
187-
throw new IllegalArgumentException("Unknown suspend anchor: " + suspendAnchor);
183+
if (hasLength && hasColumn &&
184+
line == location.getStartLine() &&
185+
column == location.getStartColumn() &&
186+
sectionLength == location.getCharLength()) {
187+
assert perfectMatch == null;
188+
perfectMatch = location;
189+
} else {
190+
switch (suspendAnchor) {
191+
case BEFORE:
192+
line = location.getStartLine();
193+
if (hasColumn) {
194+
column = location.getStartColumn();
195+
}
196+
break;
197+
case AFTER:
198+
line = location.getEndLine();
199+
if (hasColumn) {
200+
column = location.getEndColumn();
201+
}
202+
break;
203+
default:
204+
throw new IllegalArgumentException("Unknown suspend anchor: " + suspendAnchor);
205+
}
188206
}
189207
}
190208
return location;
@@ -201,7 +219,7 @@ SourceSectionFilter createLocationFilter(Source source, SuspendAnchor suspendAnc
201219
} else {
202220
f.sourceFilter(createSourceFilter());
203221
}
204-
if (line != -1) {
222+
if (perfectMatch == null && line != -1) {
205223
switch (suspendAnchor) {
206224
case BEFORE:
207225
f.lineStartsIn(IndexRange.byLength(line, 1));
@@ -221,6 +239,9 @@ SourceSectionFilter createLocationFilter(Source source, SuspendAnchor suspendAnc
221239
}
222240
if (sourceSection != null) {
223241
f.sourceSectionEquals(sourceSection);
242+
assert perfectMatch == null;
243+
} else if (perfectMatch != null) {
244+
f.sourceSectionEquals(perfectMatch);
224245
}
225246
setTags(f, sourceElements, tag);
226247
return f.build();
@@ -238,7 +259,7 @@ public String toString() {
238259
} else {
239260
keyDescription = key.toString();
240261
}
241-
return keyDescription + ", line=" + line + ", column=" + column;
262+
return keyDescription + ", line=" + line + ", column=" + column + ", length=" + sectionLength;
242263
}
243264

244265
}

truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/SuspendableLocationFinder.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ final class SuspendableLocationFinder {
5959
private SuspendableLocationFinder() {
6060
}
6161

62-
static SourceSection findNearest(Source source, SourceElement[] sourceElements, int line, int column, Class<? extends Tag> tag, SuspendAnchor anchor, TruffleInstrument.Env env) {
62+
static SourceSection findNearest(Source source, SourceElement[] sourceElements, int line, int column, int sectionLength, Class<? extends Tag> tag, SuspendAnchor anchor,
63+
TruffleInstrument.Env env) {
6364
int boundLine = line;
6465
int boundColumn = column;
6566
int maxLine = source.getLineCount();
@@ -70,7 +71,7 @@ static SourceSection findNearest(Source source, SourceElement[] sourceElements,
7071
if (boundColumn > maxColumn) {
7172
boundColumn = maxColumn;
7273
}
73-
return findNearestBound(source, getElementTags(sourceElements, tag), boundLine, boundColumn, anchor, env);
74+
return findNearestBound(source, getElementTags(sourceElements, tag), boundLine, boundColumn, sectionLength, anchor, env);
7475
}
7576

7677
private static Set<Class<? extends Tag>> getElementTags(SourceElement[] sourceElements, Class<? extends Tag> tag) {
@@ -88,12 +89,12 @@ private static Set<Class<? extends Tag>> getElementTags(SourceElement[] sourceEl
8889
}
8990

9091
private static SourceSection findNearestBound(Source source, Set<Class<? extends Tag>> elementTags,
91-
int line, int column, SuspendAnchor anchor, TruffleInstrument.Env env) {
92+
int line, int column, int sectionLength, SuspendAnchor anchor, TruffleInstrument.Env env) {
9293
int offset = source.getLineStartOffset(line);
9394
if (column > 0) {
9495
offset += column - 1;
9596
}
96-
NearestSections sectionsCollector = new NearestSections(elementTags, (column <= 0) ? line : 0, offset, anchor);
97+
NearestSections sectionsCollector = new NearestSections(elementTags, (column <= 0) ? line : 0, offset, sectionLength, anchor);
9798
// All SourceSections of the Source are loaded already when the source was executed
9899
env.getInstrumenter().visitLoadedSourceSections(
99100
SourceSectionFilter.newBuilder().sourceIs(source).build(),
@@ -124,7 +125,9 @@ private static class NearestSections implements LoadSourceSectionListener {
124125
private final Set<Class<? extends Tag>> elementTags;
125126
private final int line;
126127
private final int offset;
128+
private final int sectionLength;
127129
private final SuspendAnchor anchor;
130+
private SourceSection perfectMatch;
128131
private SourceSection exactLineMatch;
129132
private SourceSection exactIndexMatch;
130133
private SourceSection containsMatch;
@@ -134,10 +137,11 @@ private static class NearestSections implements LoadSourceSectionListener {
134137
private SourceSection nextMatch;
135138
private LinkedNodes nextNode;
136139

137-
NearestSections(Set<Class<? extends Tag>> elementTags, int line, int offset, SuspendAnchor anchor) {
140+
NearestSections(Set<Class<? extends Tag>> elementTags, int line, int offset, int sectionLength, SuspendAnchor anchor) {
138141
this.elementTags = elementTags;
139142
this.line = line;
140143
this.offset = offset;
144+
this.sectionLength = sectionLength;
141145
this.anchor = anchor;
142146
}
143147

@@ -149,6 +153,10 @@ public void onLoad(LoadSourceSectionEvent event) {
149153
}
150154
InstrumentableNode node = (InstrumentableNode) eventNode;
151155
SourceSection sourceSection = event.getSourceSection();
156+
if (matchPerfectly(node, sourceSection)) {
157+
// We have a perfect match, we do not need to do anything more
158+
return;
159+
}
152160
if (matchSectionLine(node, sourceSection)) {
153161
// We have exact line match, we do not need to do anything more
154162
return;
@@ -168,6 +176,21 @@ public void onLoad(LoadSourceSectionEvent event) {
168176
findOffsetApproximation(node, sourceSection, o1, o2);
169177
}
170178

179+
private boolean matchPerfectly(InstrumentableNode node, SourceSection sourceSection) {
180+
if (sectionLength > 0) {
181+
if (perfectMatch == null &&
182+
sourceSection.getCharIndex() == offset &&
183+
sourceSection.getCharLength() == sectionLength &&
184+
isTaggedWith(node, elementTags)) {
185+
perfectMatch = sourceSection;
186+
}
187+
if (perfectMatch != null) {
188+
return true;
189+
}
190+
}
191+
return false;
192+
}
193+
171194
private boolean matchSectionLine(InstrumentableNode node, SourceSection sourceSection) {
172195
if (line > 0) {
173196
int l;
@@ -261,6 +284,9 @@ private static boolean isTaggedWith(InstrumentableNode node, Set<Class<? extends
261284
}
262285

263286
SourceSection getExactSection() {
287+
if (perfectMatch != null) {
288+
return perfectMatch;
289+
}
264290
if (exactLineMatch != null) {
265291
return exactLineMatch;
266292
}

0 commit comments

Comments
 (0)