VM trace JSON serialization bugfix.

This commit is contained in:
eugene-shevchenko 2015-04-09 22:42:52 +03:00
parent 429d1025a2
commit a23617afda
5 changed files with 101 additions and 83 deletions

View File

@ -240,14 +240,16 @@ public class TransactionExecutor {
this.receipt = receipt;
return;
} finally {
String traceAsJson = "{}";
if (program != null) {
ProgramTrace trace = program.getProgramTrace();
trace.setResult(result.getHReturn());
trace.setError(result.getException());
traceAsJson = trace.getJsonString();
if (CONFIG.vmTrace()) {
String traceAsJson = "{}";
if (program != null) {
ProgramTrace trace = program.getProgramTrace();
trace.setResult(result.getHReturn());
trace.setError(result.getException());
traceAsJson = trace.asJsonString();
}
listener.onVMTraceCreated(txHash, traceAsJson);
}
listener.onVMTraceCreated(txHash, traceAsJson);
}
trackTx.commit();
} else {

View File

@ -8,35 +8,21 @@ import org.ethereum.vm.MessageCall.MsgType;
import org.ethereum.vm.PrecompiledContracts.PrecompiledContract;
import org.ethereum.vmtrace.Op;
import org.ethereum.vmtrace.ProgramTrace;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.*;
import static org.ethereum.config.SystemProperties.CONFIG;
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
import static org.springframework.util.StringUtils.isEmpty;
/**
* @author Roman Mandeleil
@ -840,7 +826,7 @@ public class Program {
public void saveProgramTraceToFile(String fileName) {
if (!CONFIG.vmTrace()) return;
if (!CONFIG.vmTrace() || isEmpty(CONFIG.vmTraceDir())) return;
String dir = CONFIG.databaseDir() + "/" + CONFIG.vmTraceDir() + "/";
@ -849,21 +835,12 @@ public class Program {
BufferedWriter bw = null;
try {
dumpFile.getParentFile().mkdirs();
dumpFile.createNewFile();
fw = new FileWriter(dumpFile.getAbsoluteFile());
bw = new BufferedWriter(fw);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
String originalJson = programTrace.getJsonString();
JsonNode tree = objectMapper.readTree(originalJson);
String formattedJson = objectMapper.writeValueAsString(tree);
bw.write(formattedJson);
bw.write(programTrace.asJsonString());
} catch (IOException e) {
logger.error(e.getMessage(), e);
} finally {

View File

@ -1,23 +1,13 @@
package org.ethereum.vmtrace;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.ethereum.vm.DataWord;
import org.ethereum.vm.OpCode;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.spongycastle.util.encoders.Hex;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import static org.ethereum.vmtrace.Serializers.*;
/**
* Data to for one program step to save.
@ -35,16 +25,18 @@ import java.util.Stack;
* @author Roman Mandeleil
* @since 28.10.2014
*/
public class Op {
@JsonSerialize(using = OpCodeSerializer.class)
private byte op;
private int deep;
private int pc;
@JsonSerialize(using = DataWordSerializer.class)
private DataWord gas;
private Map<String, String> storage;
@JsonSerialize(using = ByteArraySerializer.class)
private byte[] memory;
private List<String> stack;
private Map<String, String> storage;
public void setOp(byte op) {
this.op = op;
@ -89,19 +81,10 @@ public class Op {
}
public String toString() {
Map<Object, Object> jsonData = new LinkedHashMap<>();
jsonData.put("op", OpCode.code(op).name());
jsonData.put("deep", Long.toString(deep));
jsonData.put("pc", Long.toString(pc));
jsonData.put("gas", gas.value().toString());
jsonData.put("stack", stack);
jsonData.put("memory", memory == null ? "" : Hex.toHexString(memory));
jsonData.put("storage", new JSONObject(storage));
return JSONValue.toJSONString(jsonData);
return asJsonString();
}
private String asJsonString() {
return serializeFieldsOnly(this, false);
}
}

View File

@ -1,22 +1,14 @@
package org.ethereum.vmtrace;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static java.lang.String.format;
import static org.ethereum.vmtrace.Serializers.serializeFieldsOnly;
/**
* @author Roman Mandeleil
@ -24,8 +16,6 @@ import static java.lang.String.format;
*/
public class ProgramTrace {
private static final Logger LOGGER = LoggerFactory.getLogger("vmtrace");
@JsonIgnore
private byte[] txHash;
private List<Op> ops = new ArrayList<>();
@ -41,7 +31,7 @@ public class ProgramTrace {
}
public void setError(Exception error) {
this.error = (error == null) ? "" : format("%s: %s", error.getClass(), error.getMessage());;
this.error = (error == null) ? "" : format("%s: %s", error.getClass(), error.getMessage());
}
public void addOp(Op op) {
@ -56,14 +46,7 @@ public class ProgramTrace {
this.ops.addAll(programTrace.ops);
}
public String getJsonString() {
try {
return new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT)
.writeValueAsString(this);
} catch (JsonProcessingException e) {
LOGGER.error("JSON serialization error: ", e);
return "{}";
}
public String asJsonString() {
return serializeFieldsOnly(this, true);
}
}

View File

@ -0,0 +1,73 @@
package org.ethereum.vmtrace;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
import org.ethereum.vm.DataWord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.util.encoders.Hex;
import java.io.IOException;
final class Serializers {
private static final Logger LOGGER = LoggerFactory.getLogger("vmtrace");
public static class DataWordSerializer extends JsonSerializer<DataWord> {
@Override
public void serialize(DataWord gas, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(gas.value().toString());
}
}
public static class ByteArraySerializer extends JsonSerializer<byte[]> {
@Override
public void serialize(byte[] memory, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(Hex.toHexString(memory));
}
}
public static class OpCodeSerializer extends JsonSerializer<Byte> {
@Override
public void serialize(Byte op, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeString(org.ethereum.vm.OpCode.code(op).name());
}
}
public static String serializeFieldsOnly(Object value, boolean pretty) {
try {
ObjectMapper mapper = createMapper(pretty);
mapper.setVisibilityChecker(fieldsOnlyVisibilityChecker(mapper));
return mapper.writeValueAsString(value);
} catch (Exception e) {
LOGGER.error("JSON serialization error: ", e);
return "{}";
}
}
private static VisibilityChecker<?> fieldsOnlyVisibilityChecker(ObjectMapper mapper) {
return mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withIsGetterVisibility(JsonAutoDetect.Visibility.NONE);
}
public static ObjectMapper createMapper(boolean pretty) {
ObjectMapper mapper = new ObjectMapper();
if (pretty) {
mapper.enable(SerializationFeature.INDENT_OUTPUT);
}
return mapper;
}
}