wasm: add stats generator

This commit is contained in:
Alexey Andreev 2023-08-19 16:59:49 +02:00
parent 9d3ac1a603
commit 0e7d6b65b4
6 changed files with 205 additions and 21 deletions

View File

@ -103,7 +103,9 @@ import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
import org.teavm.backend.wasm.optimization.UnusedFunctionElimination;
import org.teavm.backend.wasm.render.ReportingWasmBinaryStatsCollector;
import org.teavm.backend.wasm.render.WasmBinaryRenderer;
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.backend.wasm.render.WasmCRenderer;
@ -205,6 +207,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
private Set<MethodReference> asyncMethods;
private boolean hasThreads;
private WasmRuntimeType runtimeType = WasmRuntimeType.TEAVM;
private ReportingWasmBinaryStatsCollector statsCollector;
@Override
public void setController(TeaVMTargetController controller) {
@ -438,6 +441,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
@Override
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName)
throws IOException {
prepareStats();
var statsCollector = this.statsCollector != null ? this.statsCollector : WasmBinaryStatsCollector.EMPTY;
WasmModule module = new WasmModule();
WasmFunction initFunction = new WasmFunction("__start__");
@ -457,7 +463,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
: null;
var classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(),
vtableProvider, tagRegistry, binaryWriter, names, metadataRequirements,
controller.getClassInitializerInfo(), characteristics, dwarfClassGen);
controller.getClassInitializerInfo(), characteristics, dwarfClassGen, statsCollector);
Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false);
var stringPool = classGenerator.getStringPool();
@ -550,22 +556,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
new IndirectCallTraceTransformation(module).apply();
}
var writer = new WasmBinaryWriter();
var debugBuilder = debugging ? new DebugInfoBuilder() : null;
if (debugBuilder != null) {
classGenerator.writeDebug(debugBuilder.classLayout());
}
var renderer = new WasmBinaryRenderer(
writer, version, obfuscated, dwarfGenerator, dwarfClassGen,
debugBuilder != null ? debugBuilder.lines() : null,
debugBuilder != null ? debugBuilder.variables() : null
);
renderer.render(module, buildDebug(dwarfGenerator, dwarfClassGen, debugBuilder));
try (OutputStream output = buildTarget.createResource(outputName)) {
output.write(writer.getData());
output.flush();
}
writeBinaryWasm(buildTarget, outputName, module, classGenerator, dwarfGenerator, dwarfClassGen,
statsCollector);
if (wastEmitted) {
emitWast(module, buildTarget, getBaseName(outputName) + ".wast");
@ -573,11 +565,52 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
if (cEmitted) {
emitC(module, buildTarget, getBaseName(outputName) + ".wasm.c");
}
if (statsCollector != null) {
writeStats(buildTarget, outputName);
}
if (runtimeType == WasmRuntimeType.TEAVM) {
emitRuntime(buildTarget, getBaseName(outputName) + ".wasm-runtime.js");
}
}
private void prepareStats() {
var statsProp = controller.getProperties().getProperty("teavm.wasm.stats");
var stats = Boolean.parseBoolean(statsProp);
if (stats) {
statsCollector = new ReportingWasmBinaryStatsCollector();
}
}
private void writeStats(BuildTarget buildTarget, String outputName) throws IOException {
try (var writer = new OutputStreamWriter(buildTarget.createResource(outputName + ".stats.txt"))) {
statsCollector.write(writer);
}
}
private void writeBinaryWasm(BuildTarget buildTarget, String outputName,
WasmModule module, WasmClassGenerator classGenerator, DwarfGenerator dwarfGenerator,
DwarfClassGenerator dwarfClassGen, WasmBinaryStatsCollector statsCollector) throws IOException {
var writer = new WasmBinaryWriter();
var debugBuilder = debugging ? new DebugInfoBuilder() : null;
if (debugBuilder != null) {
classGenerator.writeDebug(debugBuilder.classLayout());
}
var renderer = new WasmBinaryRenderer(
writer, version, obfuscated, dwarfGenerator, dwarfClassGen,
debugBuilder != null ? debugBuilder.lines() : null,
debugBuilder != null ? debugBuilder.variables() : null,
statsCollector
);
renderer.render(module, buildDebug(dwarfGenerator, dwarfClassGen, debugBuilder));
try (var output = buildTarget.createResource(outputName)) {
output.write(writer.getData());
output.flush();
}
}
private Supplier<Collection<? extends WasmCustomSection>> buildDebug(DwarfGenerator generator,
DwarfClassGenerator classGen, DebugInfoBuilder debugBuilder) {

View File

@ -33,6 +33,7 @@ import org.teavm.backend.wasm.binary.DataType;
import org.teavm.backend.wasm.binary.DataValue;
import org.teavm.backend.wasm.debug.DebugClassLayout;
import org.teavm.backend.wasm.debug.info.FieldType;
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector;
import org.teavm.common.IntegerArray;
import org.teavm.interop.Address;
import org.teavm.interop.Function;
@ -100,6 +101,7 @@ public class WasmClassGenerator {
private ClassMetadataRequirements metadataRequirements;
private ClassInitializerInfo classInitializerInfo;
private DwarfClassGenerator dwarfClassGenerator;
private WasmBinaryStatsCollector statsCollector;
private static final int CLASS_SIZE = 1;
private static final int CLASS_FLAGS = 2;
@ -121,18 +123,19 @@ public class WasmClassGenerator {
VirtualTableProvider vtableProvider, TagRegistry tagRegistry, BinaryWriter binaryWriter,
NameProvider names, ClassMetadataRequirements metadataRequirements,
ClassInitializerInfo classInitializerInfo, Characteristics characteristics,
DwarfClassGenerator dwarfClassGenerator) {
DwarfClassGenerator dwarfClassGenerator, WasmBinaryStatsCollector statsCollector) {
this.processedClassSource = processedClassSource;
this.classSource = classSource;
this.vtableProvider = vtableProvider;
this.tagRegistry = tagRegistry;
this.binaryWriter = binaryWriter;
this.stringPool = new WasmStringPool(this, binaryWriter);
this.stringPool = new WasmStringPool(this, binaryWriter, statsCollector);
this.names = names;
this.metadataRequirements = metadataRequirements;
this.classInitializerInfo = classInitializerInfo;
this.characteristics = characteristics;
this.dwarfClassGenerator = dwarfClassGenerator;
this.statsCollector = statsCollector;
}
public WasmStringPool getStringPool() {
@ -193,6 +196,8 @@ public class WasmClassGenerator {
calculateLayout(cls, binaryData, dwarfClass);
if (binaryData.start >= 0) {
binaryData.start = binaryWriter.append(createStructure(binaryData));
var size = binaryWriter.getAddress() - binaryData.start;
statsCollector.addClassMetadataSize(className, size);
}
if (dwarfClass != null) {
dwarfClass.setSize(binaryData.size);

View File

@ -22,6 +22,7 @@ import org.teavm.backend.wasm.binary.DataArray;
import org.teavm.backend.wasm.binary.DataPrimitives;
import org.teavm.backend.wasm.binary.DataStructure;
import org.teavm.backend.wasm.binary.DataValue;
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector;
import org.teavm.model.ValueType;
import org.teavm.runtime.RuntimeObject;
@ -38,10 +39,13 @@ public class WasmStringPool {
DataPrimitives.ADDRESS, /* monitor */
DataPrimitives.ADDRESS, /* characters */
DataPrimitives.INT /* hash code */);
private WasmBinaryStatsCollector statsCollector;
public WasmStringPool(WasmClassGenerator classGenerator, BinaryWriter binaryWriter) {
public WasmStringPool(WasmClassGenerator classGenerator, BinaryWriter binaryWriter,
WasmBinaryStatsCollector statsCollector) {
this.classGenerator = classGenerator;
this.binaryWriter = binaryWriter;
this.statsCollector = statsCollector;
}
public int getStringPointer(String value) {
@ -54,6 +58,8 @@ public class WasmStringPool {
}
private int generateStringPointer(String value) {
var start = binaryWriter.getAddress();
DataArray charactersType = new DataArray(DataPrimitives.SHORT, value.length());
DataStructure wrapperType = new DataStructure((byte) 0, arrayHeaderType, charactersType);
DataValue wrapper = wrapperType.createValue();
@ -73,6 +79,9 @@ public class WasmStringPool {
stringObject.setInt(0, (classPointer >>> 3) | RuntimeObject.GC_MARKED);
stringObject.setAddress(2, binaryWriter.append(wrapper));
var size = binaryWriter.getAddress() - start;
statsCollector.addStringsSize(size);
return stringPointer;
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2023 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.render;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.LinkedHashMap;
import java.util.Map;
public class ReportingWasmBinaryStatsCollector implements WasmBinaryStatsCollector {
private Map<String, ClassStats> statsByClass = new LinkedHashMap<>();
private ObjectIntMap<String> sectionSizes = new ObjectIntHashMap<>();
private int stringsSize;
@Override
public void addClassCodeSize(String className, int bytes) {
getStats(className).codeSize += bytes;
}
@Override
public void addClassMetadataSize(String className, int bytes) {
getStats(className).metadataSize += bytes;
}
private ClassStats getStats(String className) {
return statsByClass.computeIfAbsent(className, k -> new ClassStats());
}
@Override
public void addSectionSize(String name, int bytes) {
sectionSizes.put(name, sectionSizes.get(name) + bytes);
}
@Override
public void addStringsSize(int bytes) {
stringsSize += bytes;
}
public void write(Writer writer) throws IOException {
var pw = new PrintWriter(writer);
pw.println("[classes]");
for (var entry : statsByClass.entrySet()) {
pw.append(entry.getKey()).append(" ");
pw.print(entry.getValue().codeSize);
pw.append(" ");
pw.print(entry.getValue().metadataSize);
pw.println();
}
pw.println();
pw.println("[strings]");
pw.println(stringsSize);
pw.println();
pw.println("[sections]");
for (var entry : sectionSizes) {
pw.append(entry.key).append(" ");
pw.print(entry.value);
pw.println();
}
}
private static class ClassStats {
int codeSize;
int metadataSize;
}
}

View File

@ -61,10 +61,11 @@ public class WasmBinaryRenderer {
private DwarfFunctionGenerator dwarfFunctionGen;
private DebugLines debugLines;
private DebugVariables debugVariables;
private WasmBinaryStatsCollector statsCollector;
public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version, boolean obfuscated,
DwarfGenerator dwarfGenerator, DwarfClassGenerator dwarfClassGen, DebugLines debugLines,
DebugVariables debugVariables) {
DebugVariables debugVariables, WasmBinaryStatsCollector statsCollector) {
this.output = output;
this.version = version;
this.obfuscated = obfuscated;
@ -72,6 +73,7 @@ public class WasmBinaryRenderer {
dwarfFunctionGen = dwarfClassGen != null ? new DwarfFunctionGenerator(dwarfClassGen, dwarfGenerator) : null;
this.debugLines = debugLines;
this.debugVariables = debugVariables;
this.statsCollector = statsCollector;
}
public void render(WasmModule module) {
@ -279,8 +281,13 @@ public class WasmBinaryRenderer {
section.writeLEB(functions.size());
for (var function : functions) {
var body = renderFunction(function, section.getPosition() + 4);
var startPos = section.getPosition();
section.writeLEB4(body.length);
section.writeBytes(body);
var size = section.getPosition() - startPos;
if (function.getJavaMethod() != null) {
statsCollector.addClassCodeSize(function.getJavaMethod().getClassName(), size);
}
}
if (dwarfGenerator != null) {
@ -441,6 +448,7 @@ public class WasmBinaryRenderer {
}
private void writeSection(int id, String name, byte[] data) {
var start = output.getPosition();
output.writeByte(id);
int length = data.length;
if (id == 0) {
@ -452,5 +460,7 @@ public class WasmBinaryRenderer {
}
output.writeBytes(data);
statsCollector.addSectionSize(name, output.getPosition() - start);
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2023 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.render;
public interface WasmBinaryStatsCollector {
void addClassCodeSize(String className, int bytes);
void addClassMetadataSize(String className, int bytes);
void addStringsSize(int bytes);
void addSectionSize(String name, int bytes);
WasmBinaryStatsCollector EMPTY = new WasmBinaryStatsCollector() {
@Override
public void addClassCodeSize(String className, int bytes) {
}
@Override
public void addClassMetadataSize(String className, int bytes) {
}
@Override
public void addStringsSize(int bytes) {
}
@Override
public void addSectionSize(String name, int bytes) {
}
};
}