Skip to content

Commit

Permalink
wasm gc: support writing debug info, support it in disassembler
Browse files Browse the repository at this point in the history
  • Loading branch information
konsoletyper committed Oct 4, 2024
1 parent 7aec076 commit c2eb11e
Show file tree
Hide file tree
Showing 9 changed files with 368 additions and 10 deletions.
22 changes: 22 additions & 0 deletions core/src/main/java/org/teavm/backend/wasm/WasmDebugInfoLevel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm;

public enum WasmDebugInfoLevel {
NONE,
DEOBFUSCATION,
FULL
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm;

public enum WasmDebugInfoLocation {
EMBEDDED,
EXTERNAL
}
39 changes: 35 additions & 4 deletions core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.teavm.backend.wasm.debug.DebugLines;
import org.teavm.backend.wasm.debug.ExternalDebugFile;
import org.teavm.backend.wasm.debug.GCDebugInfoBuilder;
import org.teavm.backend.wasm.gc.TeaVMWasmGCHost;
import org.teavm.backend.wasm.gc.WasmGCDependencies;
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
Expand Down Expand Up @@ -63,6 +66,8 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
private boolean strict;
private boolean obfuscated;
private WasmDebugInfoLocation debugLocation;
private WasmDebugInfoLevel debugLevel;
private List<WasmGCIntrinsicFactory> intrinsicFactories = new ArrayList<>();
private Map<MethodReference, WasmGCIntrinsic> customIntrinsics = new HashMap<>();
private List<WasmGCCustomTypeMapperFactory> customTypeMapperFactories = new ArrayList<>();
Expand All @@ -77,6 +82,14 @@ public void setStrict(boolean strict) {
this.strict = strict;
}

public void setDebugLevel(WasmDebugInfoLevel debugLevel) {
this.debugLevel = debugLevel;
}

public void setDebugLocation(WasmDebugInfoLocation debugLocation) {
this.debugLocation = debugLocation;
}

@Override
public void addIntrinsicFactory(WasmGCIntrinsicFactory intrinsicFactory) {
intrinsicFactories.add(intrinsicFactory);
Expand Down Expand Up @@ -172,6 +185,7 @@ public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, Str
customGeneratorFactories, customCustomGenerators,
controller.getProperties());
var intrinsics = new WasmGCIntrinsics(classes, controller.getServices(), intrinsicFactories, customIntrinsics);
var debugInfoBuilder = new GCDebugInfoBuilder();
var declarationsGenerator = new WasmGCDeclarationsGenerator(
module,
classes,
Expand Down Expand Up @@ -231,7 +245,7 @@ public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, Str
customGenerators.contributeToModule(module);
adjustModuleMemory(module);

emitWasmFile(module, buildTarget, outputName);
emitWasmFile(module, buildTarget, outputName, debugInfoBuilder);
}

private void adjustModuleMemory(WasmModule module) {
Expand All @@ -248,20 +262,37 @@ private void adjustModuleMemory(WasmModule module) {
module.setMaxMemorySize(pages);
}

private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException {
private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName,
GCDebugInfoBuilder debugInfoBuilder) throws IOException {
var binaryWriter = new WasmBinaryWriter();
DebugLines debugLines = null;
if (debugLevel != WasmDebugInfoLevel.NONE) {
debugLines = debugInfoBuilder.lines();
}
var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated,
null, null, null, null, WasmBinaryStatsCollector.EMPTY);
null, null, debugLines, null, WasmBinaryStatsCollector.EMPTY);
optimizeIndexes(module);
module.prepareForRendering();
binaryRenderer.render(module);
if (debugLocation == WasmDebugInfoLocation.EMBEDDED) {
binaryRenderer.render(module, debugInfoBuilder::build);
} else {
binaryRenderer.render(module);
}
var data = binaryWriter.getData();
if (!outputName.endsWith(".wasm")) {
outputName += ".wasm";
}
try (var output = buildTarget.createResource(outputName)) {
output.write(data);
}
if (debugLocation == WasmDebugInfoLocation.EXTERNAL) {
var debugInfoData = ExternalDebugFile.write(debugInfoBuilder.build());
if (debugInfoData != null) {
try (var output = buildTarget.createResource(outputName + ".tdbg")) {
output.write(debugInfoData);
}
}
}
}

private void optimizeIndexes(WasmModule module) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.debug;

import java.util.List;
import org.teavm.backend.wasm.model.WasmCustomSection;
import org.teavm.backend.wasm.render.WasmBinaryWriter;

public final class ExternalDebugFile {
private ExternalDebugFile() {
}

public static byte[] write(List<WasmCustomSection> sections) {
if (sections.isEmpty()) {
return null;
}
var writer = new WasmBinaryWriter();
writer.writeInt32(0x67626474);
writer.writeInt32(1);
for (var section : sections) {
var data = section.getData();
writer.writeAsciiString(section.getName());
writer.writeLEB(data.length);
writer.writeBytes(data);
}
return writer.getData();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2024 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.debug;

import java.util.ArrayList;
import java.util.List;
import org.teavm.backend.wasm.model.WasmCustomSection;

public class GCDebugInfoBuilder {
private DebugStringsBuilder strings;
private DebugFilesBuilder files;
private DebugPackagesBuilder packages;
private DebugClassesBuilder classes;
private DebugMethodsBuilder methods;
private DebugLinesBuilder lines;

public GCDebugInfoBuilder() {
strings = new DebugStringsBuilder();
files = new DebugFilesBuilder(strings);
packages = new DebugPackagesBuilder(strings);
classes = new DebugClassesBuilder(packages, strings);
methods = new DebugMethodsBuilder(classes, strings);
lines = new DebugLinesBuilder(files, methods);
}

public DebugStrings strings() {
return strings;
}

public DebugFiles files() {
return files;
}

public DebugPackages packages() {
return packages;
}

public DebugClasses classes() {
return classes;
}

public DebugMethods methods() {
return methods;
}

public DebugLines lines() {
return lines;
}

public List<WasmCustomSection> build() {
var result = new ArrayList<WasmCustomSection>();
addSection(result, strings);
addSection(result, files);
addSection(result, packages);
addSection(result, classes);
addSection(result, methods);
addSection(result, lines);
return result;
}

private void addSection(List<WasmCustomSection> sections, DebugSectionBuilder builder) {
if (builder.isEmpty()) {
return;
}
sections.add(new WasmCustomSection(builder.name(), builder.build()));
}
}
44 changes: 41 additions & 3 deletions core/src/main/java/org/teavm/backend/wasm/disasm/Disassembler.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.teavm.backend.wasm.debug.info.LineInfo;
import org.teavm.backend.wasm.debug.parser.DebugClassParser;
import org.teavm.backend.wasm.debug.parser.DebugFileParser;
import org.teavm.backend.wasm.debug.parser.DebugLinesParser;
import org.teavm.backend.wasm.debug.parser.DebugMethodParser;
import org.teavm.backend.wasm.debug.parser.DebugPackageParser;
import org.teavm.backend.wasm.debug.parser.DebugSectionParser;
import org.teavm.backend.wasm.debug.parser.DebugStringParser;
import org.teavm.backend.wasm.parser.AddressListener;
import org.teavm.backend.wasm.parser.CodeSectionParser;
import org.teavm.backend.wasm.parser.FunctionSectionListener;
Expand All @@ -41,9 +51,27 @@ public final class Disassembler {
private WasmHollowFunctionType[] functionTypes;
private int[] functionTypeRefs;
private int importFunctionCount;
private Map<String, DebugSectionParser> debugSectionParsers = new HashMap<>();
private DebugLinesParser debugLines;
private LineInfo lineInfo;

public Disassembler(DisassemblyWriter writer) {
this.writer = writer;
installDebugParsers();
}

private void installDebugParsers() {
var strings = addDebugSection(new DebugStringParser());
var files = addDebugSection(new DebugFileParser(strings));
var packages = addDebugSection(new DebugPackageParser(strings));
var classes = addDebugSection(new DebugClassParser(strings, packages));
var methods = addDebugSection(new DebugMethodParser(strings, classes));
debugLines = addDebugSection(new DebugLinesParser(files, methods));
}

private <T extends DebugSectionParser> T addDebugSection(T section) {
debugSectionParsers.put(section.name(), section);
return section;
}

public void startModule() {
Expand All @@ -65,18 +93,25 @@ public void disassemble(byte[] bytes) {
public void read(byte[] bytes) {
var nameAccumulator = new NameAccumulatingSectionListener();
var input = new ByteArrayAsyncInputStream(bytes);
var nameParser = createNameParser(input, nameAccumulator);
input.readFully(nameParser::parse);
var preparationParser = createPreparationParser(input, nameAccumulator);
input.readFully(preparationParser::parse);
lineInfo = debugLines.getLineInfo();

input = new ByteArrayAsyncInputStream(bytes);
var parser = createParser(input, nameAccumulator.buildProvider());
input.readFully(parser::parse);
}

public ModuleParser createNameParser(AsyncInputStream input, NameSectionListener listener) {
public ModuleParser createPreparationParser(AsyncInputStream input, NameSectionListener listener) {
return new ModuleParser(input) {
@Override
protected Consumer<byte[]> getSectionConsumer(int code, int pos, String name) {
if (code == 0) {
var debugSection = debugSectionParsers.get(name);
if (debugSection != null) {
return debugSection::parse;
}
}
return Disassembler.this.getNameSectionConsumer(code, name, listener);
}
};
Expand Down Expand Up @@ -139,11 +174,14 @@ public void function(int index, int typeIndex) {
disassembler.setFunctionTypes(functionTypes);
disassembler.setFunctionTypeRefs(functionTypeRefs);
writer.setAddressOffset(pos);
writer.setDebugLines(lineInfo);
writer.startSection();
writer.write("(; code section size: " + bytes.length + " ;)").eol();
var sectionParser = new CodeSectionParser(disassembler);
sectionParser.setFunctionIndexOffset(importFunctionCount);
sectionParser.parse(writer.addressListener, bytes);
writer.flush();
writer.setDebugLines(null);
};
} else {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ public DisassemblyHTMLWriter(PrintWriter out) {

@Override
public DisassemblyWriter prologue() {
return writeExact("<html><body><pre>");
writeExact("<html>\n<head>");
writeExact("<style>\n");
writeExact("em { color: gray; }\n");
writeExact("</style></head>\n");
return writeExact("<body><pre>");
}

@Override
Expand Down Expand Up @@ -82,4 +86,14 @@ public DisassemblyWriter write(String s) {
writeExact(s);
return this;
}

@Override
protected void startAnnotation() {
writeExact("<em>");
}

@Override
protected void endAnnotation() {
writeExact("</em>");
}
}
Loading

0 comments on commit c2eb11e

Please sign in to comment.