Skip to content

Commit b722701

Browse files
committed
[GR-62872] WebAssembly debugger fixes and extensions.
PullRequest: graal/20485
2 parents f63ba17 + 8435a37 commit b722701

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1918
-553
lines changed

tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/InspectorDebugger.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -55,6 +55,8 @@
5555
import java.util.regex.PatternSyntaxException;
5656

5757
import org.graalvm.collections.Pair;
58+
import org.graalvm.shadowed.org.json.JSONArray;
59+
import org.graalvm.shadowed.org.json.JSONObject;
5860

5961
import com.oracle.truffle.api.debug.Breakpoint;
6062
import com.oracle.truffle.api.debug.DebugException;
@@ -94,8 +96,6 @@
9496
import com.oracle.truffle.tools.chromeinspector.types.Script;
9597
import com.oracle.truffle.tools.chromeinspector.types.StackTrace;
9698
import com.oracle.truffle.tools.chromeinspector.util.LineSearch;
97-
import org.graalvm.shadowed.org.json.JSONArray;
98-
import org.graalvm.shadowed.org.json.JSONObject;
9999

100100
public final class InspectorDebugger extends DebuggerDomain {
101101

@@ -338,9 +338,15 @@ public Params getScriptSource(String scriptId) throws CommandProcessException {
338338
if (scriptId == null) {
339339
throw new CommandProcessException("A scriptId required.");
340340
}
341-
CharSequence characters = getScript(scriptId).getCharacters();
341+
Script script = getScript(scriptId);
342342
JSONObject json = new JSONObject();
343-
json.put("scriptSource", characters.toString());
343+
if (script.hasWasmSource()) {
344+
CharSequence bytecode = script.getWasmBytecode();
345+
json.put("bytecode", bytecode.toString());
346+
} else {
347+
CharSequence characters = script.getCharacters();
348+
json.put("scriptSource", characters.toString());
349+
}
344350
return new Params(json);
345351
}
346352

tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/types/Script.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
2525
package com.oracle.truffle.tools.chromeinspector.types;
2626

2727
import java.text.MessageFormat;
28+
import java.util.Base64;
2829

2930
import com.oracle.truffle.api.source.Source;
3031

@@ -62,6 +63,10 @@ public Source getSourceLoaded() {
6263
return sourceLoaded;
6364
}
6465

66+
public boolean hasWasmSource() {
67+
return source.hasBytes() && "application/wasm".equals(source.getMimeType());
68+
}
69+
6570
public CharSequence getCharacters() {
6671
if (source.hasCharacters()) {
6772
return source.getCharacters();
@@ -72,6 +77,19 @@ public CharSequence getCharacters() {
7277
}
7378
}
7479

80+
/**
81+
* @return A base64 encoded representation of the wasm bytecode inside this script.
82+
*/
83+
public CharSequence getWasmBytecode() {
84+
if (source.hasBytes()) {
85+
return Base64.getEncoder().encodeToString(source.getBytes().toByteArray());
86+
} else {
87+
return MessageFormat.format("Can not load source from {0}\n" +
88+
"Please use the --inspect.SourcePath option to point to the source locations.\n" +
89+
"Example: --inspect.SourcePath=/home/joe/project/src\n", source.getURI().toString());
90+
}
91+
}
92+
7593
public String getHash() {
7694
CharSequence code = getCharacters();
7795
// See

wasm/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
This changelog summarizes major changes to the WebAssembly engine implemented in GraalVM (GraalWasm).
44

5+
## Version 25.0.0
6+
7+
* Implemented support for editing primitive values during debugging. Fixed several debugger-related issues.
8+
59
## Version 24.2.0
610

711
* Updated developer metadata of Maven artifacts.

wasm/src/org.graalvm.wasm.test/src/org/graalvm/wasm/test/suites/debugging/DebugObjectFactorySuite.java

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -41,9 +41,10 @@
4141

4242
package org.graalvm.wasm.test.suites.debugging;
4343

44-
import com.oracle.truffle.api.source.Source;
44+
import java.nio.file.Path;
45+
import java.util.ArrayList;
46+
4547
import org.graalvm.collections.EconomicMap;
46-
import org.graalvm.wasm.WasmLanguage;
4748
import org.graalvm.wasm.collection.LongArrayList;
4849
import org.graalvm.wasm.debugging.DebugLineMap;
4950
import org.graalvm.wasm.debugging.data.DebugFunction;
@@ -60,9 +61,6 @@
6061
import org.junit.Assert;
6162
import org.junit.Test;
6263

63-
import java.nio.file.Path;
64-
import java.util.ArrayList;
65-
6664
/**
6765
* Test suite for debug entries based on the specification in the
6866
* <a href="https://dwarfstd.org/doc/DWARF4.pdf">DWARF Debug Information format</a>.
@@ -118,7 +116,7 @@ private static DebugParserContext parseCompilationUnit(TestObjectFactory factory
118116
return parseCompilationUnit(factory, null, null, children);
119117
}
120118

121-
private static DebugParserContext parseCompilationUnit(TestObjectFactory factory, DebugLineMap lineMap, Source source, DebugData... children) {
119+
private static DebugParserContext parseCompilationUnit(TestObjectFactory factory, DebugLineMap lineMap, Path path, DebugData... children) {
122120
final byte[] data = {};
123121
final DebugData compUnit = getCompilationUnit(children);
124122
final DebugLineMap[] lineMaps;
@@ -127,13 +125,13 @@ private static DebugParserContext parseCompilationUnit(TestObjectFactory factory
127125
} else {
128126
lineMaps = null;
129127
}
130-
final Source[] sources;
131-
if (source != null) {
132-
sources = new Source[]{source};
128+
final Path[] paths;
129+
if (path != null) {
130+
paths = new Path[]{path};
133131
} else {
134-
sources = null;
132+
paths = null;
135133
}
136-
final DebugParserContext context = new DebugParserContext(data, 0, getEntryData(compUnit), lineMaps, sources);
134+
final DebugParserContext context = new DebugParserContext(data, 0, getEntryData(compUnit), lineMaps, paths, "wasm", factory);
137135
final DebugParserScope scope = DebugParserScope.createGlobalScope();
138136
for (DebugData d : compUnit.children()) {
139137
factory.parse(context, scope, d);
@@ -1073,29 +1071,29 @@ public void testEnumTypeMissingBaseType() {
10731071

10741072
@Test
10751073
public void testFunction() {
1076-
final DebugLineMap lineMap = new DebugLineMap(Path.of(""));
1074+
final Path p = Path.of("");
1075+
final DebugLineMap lineMap = new DebugLineMap(p);
10771076
lineMap.add(0, 1);
10781077
lineMap.add(10, 2);
1079-
final Source s = Source.newBuilder(WasmLanguage.ID, "", "test").internal(true).build();
10801078
final AttributeBuilder funcAttr = AttributeBuilder.create().add(Attributes.DECL_FILE, 0x0F, 0).add(Attributes.NAME, 0x08, "func").add(Attributes.LOW_PC, 0x0F, 0).add(Attributes.HIGH_PC, 0x0F,
10811079
10).add(Attributes.FRAME_BASE, 0x09, new byte[]{});
10821080
final DebugData func = new DebugData(Tags.SUBPROGRAM, 1, funcAttr.attributeInfo(), funcAttr.attributeValues(), new DebugData[0]);
10831081

10841082
final TestObjectFactory factory = new TestObjectFactory();
1085-
final DebugParserContext context = parseCompilationUnit(factory, lineMap, s, func);
1083+
final DebugParserContext context = parseCompilationUnit(factory, lineMap, p, func);
10861084

10871085
Assert.assertEquals(1, context.functions().size());
10881086
}
10891087

10901088
@Test
10911089
public void testFunctionMissingLineMap() {
1092-
final Source s = Source.newBuilder(WasmLanguage.ID, "", "test").internal(true).build();
1090+
final Path p = Path.of("");
10931091
final AttributeBuilder funcAttr = AttributeBuilder.create().add(Attributes.DECL_FILE, 0x0F, 0).add(Attributes.NAME, 0x08, "func").add(Attributes.LOW_PC, 0x0F, 0).add(Attributes.HIGH_PC, 0x0F,
10941092
10).add(Attributes.FRAME_BASE, 0x09, new byte[]{});
10951093
final DebugData func = new DebugData(Tags.SUBPROGRAM, 1, funcAttr.attributeInfo(), funcAttr.attributeValues(), new DebugData[0]);
10961094

10971095
final TestObjectFactory factory = new TestObjectFactory();
1098-
final DebugParserContext context = parseCompilationUnit(factory, null, s, func);
1096+
final DebugParserContext context = parseCompilationUnit(factory, null, p, func);
10991097

11001098
Assert.assertEquals(0, context.functions().size());
11011099
}
@@ -1117,42 +1115,42 @@ public void testFunctionMissingSource() {
11171115

11181116
@Test
11191117
public void testInlinedFunction() {
1120-
final DebugLineMap lineMap = new DebugLineMap(Path.of(""));
1118+
final Path p = Path.of("");
1119+
final DebugLineMap lineMap = new DebugLineMap(p);
11211120
lineMap.add(0, 1);
11221121
lineMap.add(10, 2);
1123-
final Source s = Source.newBuilder(WasmLanguage.ID, "", "test").internal(true).build();
11241122
final AttributeBuilder funcAttr = AttributeBuilder.create().add(Attributes.DECL_FILE, 0x0F, 0).add(Attributes.NAME, 0x08, "func").add(Attributes.LOW_PC, 0x0F, 0).add(Attributes.HIGH_PC, 0x0F,
11251123
10).add(Attributes.FRAME_BASE, 0x09, new byte[]{}).add(Attributes.INLINE, 0x0F, 1);
11261124
final DebugData func = new DebugData(Tags.SUBPROGRAM, 1, funcAttr.attributeInfo(), funcAttr.attributeValues(), new DebugData[0]);
11271125

11281126
final TestObjectFactory factory = new TestObjectFactory();
1129-
final DebugParserContext context = parseCompilationUnit(factory, lineMap, s, func);
1127+
final DebugParserContext context = parseCompilationUnit(factory, lineMap, p, func);
11301128

11311129
Assert.assertEquals(0, context.functions().size());
11321130
}
11331131

11341132
@Test
11351133
public void testFunctionMissingFrameBase() {
1136-
final DebugLineMap lineMap = new DebugLineMap(Path.of(""));
1134+
final Path p = Path.of("");
1135+
final DebugLineMap lineMap = new DebugLineMap(p);
11371136
lineMap.add(0, 1);
11381137
lineMap.add(10, 2);
1139-
final Source s = Source.newBuilder(WasmLanguage.ID, "", "test").internal(true).build();
11401138
final AttributeBuilder funcAttr = AttributeBuilder.create().add(Attributes.DECL_FILE, 0x0F, 0).add(Attributes.NAME, 0x08, "func").add(Attributes.LOW_PC, 0x0F, 0).add(Attributes.HIGH_PC, 0x0F,
11411139
10);
11421140
final DebugData func = new DebugData(Tags.SUBPROGRAM, 1, funcAttr.attributeInfo(), funcAttr.attributeValues(), new DebugData[0]);
11431141

11441142
final TestObjectFactory factory = new TestObjectFactory();
1145-
final DebugParserContext context = parseCompilationUnit(factory, lineMap, s, func);
1143+
final DebugParserContext context = parseCompilationUnit(factory, lineMap, p, func);
11461144

11471145
Assert.assertEquals(0, context.functions().size());
11481146
}
11491147

11501148
@Test
11511149
public void testFunctionWithVariables() {
1152-
final DebugLineMap lineMap = new DebugLineMap(Path.of(""));
1150+
final Path p = Path.of("");
1151+
final DebugLineMap lineMap = new DebugLineMap(p);
11531152
lineMap.add(0, 1);
11541153
lineMap.add(10, 2);
1155-
final Source s = Source.newBuilder(WasmLanguage.ID, "", "test").internal(true).build();
11561154

11571155
final AttributeBuilder baseAttr = AttributeBuilder.create().add(Attributes.NAME, 0x08, "int").add(Attributes.ENCODING, 0x0F, AttributeEncodings.SIGNED).add(Attributes.BYTE_SIZE, 0x0F, 4);
11581156
final DebugData baseType = new DebugData(Tags.BASE_TYPE, 1, baseAttr.attributeInfo(), baseAttr.attributeValues(), new DebugData[0]);
@@ -1172,7 +1170,7 @@ public void testFunctionWithVariables() {
11721170
final DebugData func = new DebugData(Tags.SUBPROGRAM, 3, funcAttr.attributeInfo(), funcAttr.attributeValues(), new DebugData[]{var1, var2, var3});
11731171

11741172
final TestObjectFactory factory = new TestObjectFactory();
1175-
final DebugParserContext context = parseCompilationUnit(factory, lineMap, s, baseType, func);
1173+
final DebugParserContext context = parseCompilationUnit(factory, lineMap, p, baseType, func);
11761174

11771175
Assert.assertEquals(1, context.functions().size());
11781176
final DebugFunction function = context.functions().get(0);
@@ -1181,10 +1179,10 @@ public void testFunctionWithVariables() {
11811179

11821180
@Test
11831181
public void testFunctionWithVariableMissingTypeAttribute() {
1184-
final DebugLineMap lineMap = new DebugLineMap(Path.of(""));
1182+
final Path p = Path.of("");
1183+
final DebugLineMap lineMap = new DebugLineMap(p);
11851184
lineMap.add(0, 1);
11861185
lineMap.add(10, 2);
1187-
final Source s = Source.newBuilder(WasmLanguage.ID, "", "test").internal(true).build();
11881186

11891187
final AttributeBuilder baseAttr = AttributeBuilder.create().add(Attributes.NAME, 0x08, "int").add(Attributes.ENCODING, 0x0F, AttributeEncodings.SIGNED).add(Attributes.BYTE_SIZE, 0x0F, 4);
11901188
final DebugData baseType = new DebugData(Tags.BASE_TYPE, 1, baseAttr.attributeInfo(), baseAttr.attributeValues(), new DebugData[0]);
@@ -1198,7 +1196,7 @@ public void testFunctionWithVariableMissingTypeAttribute() {
11981196
final DebugData func = new DebugData(Tags.SUBPROGRAM, 3, funcAttr.attributeInfo(), funcAttr.attributeValues(), new DebugData[]{var});
11991197

12001198
final TestObjectFactory factory = new TestObjectFactory();
1201-
final DebugParserContext context = parseCompilationUnit(factory, lineMap, s, baseType, func);
1199+
final DebugParserContext context = parseCompilationUnit(factory, lineMap, p, baseType, func);
12021200

12031201
Assert.assertEquals(1, context.functions().size());
12041202
final DebugFunction function = context.functions().get(0);
@@ -1207,10 +1205,10 @@ public void testFunctionWithVariableMissingTypeAttribute() {
12071205

12081206
@Test
12091207
public void testFunctionWithVariableMissingType() {
1210-
final DebugLineMap lineMap = new DebugLineMap(Path.of(""));
1208+
final Path p = Path.of("");
1209+
final DebugLineMap lineMap = new DebugLineMap(p);
12111210
lineMap.add(0, 1);
12121211
lineMap.add(10, 2);
1213-
final Source s = Source.newBuilder(WasmLanguage.ID, "", "test").internal(true).build();
12141212

12151213
final AttributeBuilder varAttr = AttributeBuilder.create().add(Attributes.NAME, 0x08, "c").add(Attributes.TYPE, 0x0F, 1).add(Attributes.LOCATION, 0x09, new byte[0]).add(Attributes.DECL_FILE,
12161214
0x0F, 0).add(Attributes.DECL_LINE, 0x0F, 1);
@@ -1221,7 +1219,7 @@ public void testFunctionWithVariableMissingType() {
12211219
final DebugData func = new DebugData(Tags.SUBPROGRAM, 3, funcAttr.attributeInfo(), funcAttr.attributeValues(), new DebugData[]{var});
12221220

12231221
final TestObjectFactory factory = new TestObjectFactory();
1224-
final DebugParserContext context = parseCompilationUnit(factory, lineMap, s, func);
1222+
final DebugParserContext context = parseCompilationUnit(factory, lineMap, p, func);
12251223

12261224
Assert.assertEquals(1, context.functions().size());
12271225
final DebugFunction function = context.functions().get(0);

wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/BinaryParser.java

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -576,18 +576,19 @@ private static byte[] encapsulateResultType(int type) {
576576
}
577577

578578
private CodeEntry readFunction(int functionIndex, byte[] locals, byte[] resultTypes, int sourceCodeEndOffset, boolean hasNextFunction, RuntimeBytecodeGen bytecode,
579-
int codeEntryIndex, EconomicMap<Integer, Integer> sourceLocationToLineMap) {
579+
int codeEntryIndex, EconomicMap<Integer, Integer> offsetToLineIndexMap) {
580580
final ParserState state = new ParserState(bytecode);
581581
final ArrayList<CallNode> callNodes = new ArrayList<>();
582582
final int bytecodeStartOffset = bytecode.location();
583583
state.enterFunction(resultTypes);
584584

585585
int opcode;
586586
end: while (offset < sourceCodeEndOffset) {
587-
// Insert a debug instruction if a line mapping exists.
588-
if (sourceLocationToLineMap != null) {
589-
if (sourceLocationToLineMap.containsKey(offset)) {
590-
bytecode.addNotify(sourceLocationToLineMap.get(offset), offset);
587+
// Insert a debug instruction if a line mapping (line index) exists.
588+
if (offsetToLineIndexMap != null) {
589+
final Integer lineIndex = offsetToLineIndexMap.get(offset);
590+
if (lineIndex != null) {
591+
bytecode.addNotify(lineIndex, offset);
591592
}
592593
}
593594

@@ -713,6 +714,10 @@ private CodeEntry readFunction(int functionIndex, byte[] locals, byte[] resultTy
713714
break;
714715
}
715716
case Instructions.RETURN: {
717+
if (offsetToLineIndexMap != null) {
718+
// Make sure we exit the current statement before leaving the function
719+
bytecode.addNotify(-1, -1);
720+
}
716721
state.addReturn(multiValue);
717722

718723
// This instruction is stack-polymorphic
@@ -1019,22 +1024,27 @@ private CodeEntry readFunction(int functionIndex, byte[] locals, byte[] resultTy
10191024
}
10201025
}
10211026
final int bytecodeEndOffset = bytecode.location();
1022-
bytecode.addCodeEntry(functionIndex, state.maxStackSize(), bytecodeEndOffset - bytecodeStartOffset, locals.length, resultTypes.length);
1023-
for (byte local : locals) {
1024-
bytecode.addByte(local);
1025-
}
1026-
if (locals.length != 0) {
1027-
bytecode.addByte((byte) 0);
1028-
}
1029-
for (byte result : resultTypes) {
1030-
bytecode.addByte(result);
1031-
}
1032-
if (resultTypes.length != 0) {
1033-
bytecode.addByte((byte) 0);
1034-
}
1035-
if (sourceLocationToLineMap == null) {
1027+
1028+
if (offsetToLineIndexMap == null) {
1029+
bytecode.addCodeEntry(functionIndex, state.maxStackSize(), bytecodeEndOffset - bytecodeStartOffset, locals.length, resultTypes.length);
1030+
for (byte local : locals) {
1031+
bytecode.addByte(local);
1032+
}
1033+
if (locals.length != 0) {
1034+
bytecode.addByte((byte) 0);
1035+
}
1036+
for (byte result : resultTypes) {
1037+
bytecode.addByte(result);
1038+
}
1039+
if (resultTypes.length != 0) {
1040+
bytecode.addByte((byte) 0);
1041+
}
1042+
10361043
// Do not override the code entry offset when rereading the function.
10371044
module.setCodeEntryOffset(codeEntryIndex, bytecodeEndOffset);
1045+
} else {
1046+
// Make sure we notify a statement exit before leaving the function
1047+
bytecode.addNotify(-1, -1);
10381048
}
10391049
return new CodeEntry(functionIndex, state.maxStackSize(), locals, resultTypes, callNodes, bytecodeStartOffset, bytecodeEndOffset, state.usesMemoryZero());
10401050
}
@@ -3248,17 +3258,17 @@ private Vector128 readUnsignedInt128() {
32483258
* Creates a runtime bytecode of a function with added debug opcodes.
32493259
*
32503260
* @param functionIndex the function index
3251-
* @param sourceLocationToLineMap a mapping from source code locations to source code lines
3252-
* numbers (extracted from debugging information)
3261+
* @param offsetToLineIndexMap a mapping from source code locations to line indices in the line
3262+
* index map
32533263
*/
32543264
@TruffleBoundary
3255-
public byte[] createFunctionDebugBytecode(int functionIndex, EconomicMap<Integer, Integer> sourceLocationToLineMap) {
3265+
public byte[] createFunctionDebugBytecode(int functionIndex, EconomicMap<Integer, Integer> offsetToLineIndexMap) {
32563266
final RuntimeBytecodeGen bytecode = new RuntimeBytecodeGen();
32573267
final int codeEntryIndex = functionIndex - module.numImportedFunctions();
32583268
final CodeEntry codeEntry = BytecodeParser.readCodeEntry(module, module.bytecode(), codeEntryIndex);
32593269
offset = module.functionSourceCodeInstructionOffset(functionIndex);
32603270
final int endOffset = module.functionSourceCodeEndOffset(functionIndex);
3261-
readFunction(functionIndex, codeEntry.localTypes(), codeEntry.resultTypes(), endOffset, true, bytecode, codeEntryIndex, sourceLocationToLineMap);
3271+
readFunction(functionIndex, codeEntry.localTypes(), codeEntry.resultTypes(), endOffset, true, bytecode, codeEntryIndex, offsetToLineIndexMap);
32623272
return bytecode.toArray();
32633273
}
32643274
}

0 commit comments

Comments
 (0)