Skip to content

Commit

Permalink
add vm trace to trace stack, memory, storage info for each step
Browse files Browse the repository at this point in the history
  • Loading branch information
taihaofu committed Aug 16, 2018
1 parent e88da4e commit 54ba471
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 212 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ src/main/resources/META-INF/
output*
nodeId.properties
Wallet

# vm_trace
/vm_trace/
23 changes: 20 additions & 3 deletions src/main/java/org/tron/common/runtime/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import static com.google.common.primitives.Longs.min;
import static org.apache.commons.lang3.ArrayUtils.isEmpty;
import static org.tron.common.runtime.utils.MUtil.transfer;
import static org.tron.common.runtime.vm.VMUtils.saveProgramTraceFile;
import static org.tron.common.runtime.vm.VMUtils.zipAndEncode;
import static org.tron.common.runtime.vm.program.InternalTransaction.ExecutorType.ET_CONSTANT_TYPE;
import static org.tron.common.runtime.vm.program.InternalTransaction.ExecutorType.ET_NORMAL_TYPE;
import static org.tron.common.runtime.vm.program.InternalTransaction.ExecutorType.ET_PRE_TYPE;
Expand All @@ -21,6 +23,7 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.spongycastle.util.encoders.Hex;
import org.tron.common.runtime.config.SystemProperties;
import org.tron.common.runtime.vm.PrecompiledContracts;
import org.tron.common.runtime.vm.VM;
Expand Down Expand Up @@ -67,7 +70,7 @@
public class Runtime {


SystemProperties config;
SystemProperties config = SystemProperties.getInstance();

private Transaction trx;
private Block block = null;
Expand Down Expand Up @@ -671,8 +674,22 @@ private boolean isCallConstant(byte[] address) {
return false;
}

public RuntimeSummary finalization() {
return null;
public void finalization() {
if (config.vmTrace() && program != null && result != null) {
String trace = program.getTrace()
.result(result.getHReturn())
.error(result.getException())
.toString();


if (config.vmTraceCompressed()) {
trace = zipAndEncode(trace);
}

String txHash = Hex.toHexString(new InternalTransaction(trx).getHash());
saveProgramTraceFile(config,txHash, trace);
}

}

public ProgramResult getResult() {
Expand Down
227 changes: 25 additions & 202 deletions src/main/java/org/tron/common/runtime/config/SystemProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigRenderOptions;
import org.apache.commons.lang3.ObjectUtils.Null;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
Expand All @@ -33,218 +34,40 @@
import java.net.URL;
import java.util.*;

//import org.ethereum.config.blockchain.OlympicConfig;
//import org.ethereum.config.net.*;

/**
* For developer only
*/
public class SystemProperties {
private static Logger logger = LoggerFactory.getLogger("general");

private static SystemProperties CONFIG;
private static boolean useOnlySpringConfig = true; //false;
/**
* Returns the static config instance. If the config is passed
* as a Spring bean by the application this instance shouldn't
* be used
* This method is mainly used for testing purposes
* (Autowired fields are initialized with this static instance
* but when running within Spring context they replaced with the
* bean config instance)
*/
public static SystemProperties getDefault() {
return useOnlySpringConfig ? null : getSpringDefault();
}

static SystemProperties getSpringDefault() {
if (CONFIG == null) {
CONFIG = new SystemProperties();
}
return CONFIG;
}

/**
* Used mostly for testing purposes to ensure the application
* refers only to the config passed as a Spring bean.
* If this property is set to true {@link #getDefault()} returns null
*/
public static void setUseOnlySpringConfig(boolean useOnlySpringConfig) {
SystemProperties.useOnlySpringConfig = useOnlySpringConfig;
}

static boolean isUseOnlySpringConfig() {
return useOnlySpringConfig;
}

/**
* Marks config accessor methods which need to be called (for value validation)
* upon config creation or modification
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
private @interface ValidateMe {};


private Config config;

private final ClassLoader classLoader;

public SystemProperties() {
this(ConfigFactory.empty());
}

public SystemProperties(File configFile) {
this(ConfigFactory.parseFile(configFile));
}

public SystemProperties(String configResource) {
this(ConfigFactory.parseResources(configResource));
}

public SystemProperties(Config apiConfig) {
this(apiConfig, SystemProperties.class.getClassLoader());
}

public SystemProperties(Config apiConfig, ClassLoader classLoader) {
try {
this.classLoader = classLoader;

Config javaSystemProperties = ConfigFactory.load("no-such-resource-only-system-props");
Config referenceConfig = ConfigFactory.parseResources("ethereumj.conf");
logger.info("Config (" + (referenceConfig.entrySet().size() > 0 ? " yes " : " no ") + "): default properties from resource 'ethereumj.conf'");
String res = System.getProperty("ethereumj.conf.res");
Config cmdLineConfigRes = res != null ? ConfigFactory.parseResources(res) : ConfigFactory.empty();
logger.info("Config (" + (cmdLineConfigRes.entrySet().size() > 0 ? " yes " : " no ") + "): user properties from -Dethereumj.conf.res resource '" + res + "'");
Config userConfig = ConfigFactory.parseResources("user.conf");
logger.info("Config (" + (userConfig.entrySet().size() > 0 ? " yes " : " no ") + "): user properties from resource 'user.conf'");
File userDirFile = new File(System.getProperty("user.dir"), "/config/ethereumj.conf");
Config userDirConfig = ConfigFactory.parseFile(userDirFile);
logger.info("Config (" + (userDirConfig.entrySet().size() > 0 ? " yes " : " no ") + "): user properties from file '" + userDirFile + "'");
Config testConfig = ConfigFactory.parseResources("test-ethereumj.conf");
logger.info("Config (" + (testConfig.entrySet().size() > 0 ? " yes " : " no ") + "): test properties from resource 'test-ethereumj.conf'");
Config testUserConfig = ConfigFactory.parseResources("test-user.conf");
logger.info("Config (" + (testUserConfig.entrySet().size() > 0 ? " yes " : " no ") + "): test properties from resource 'test-user.conf'");
String file = System.getProperty("ethereumj.conf.file");
Config cmdLineConfigFile = file != null ? ConfigFactory.parseFile(new File(file)) : ConfigFactory.empty();
logger.info("Config (" + (cmdLineConfigFile.entrySet().size() > 0 ? " yes " : " no ") + "): user properties from -Dethereumj.conf.file file '" + file + "'");
logger.info("Config (" + (apiConfig.entrySet().size() > 0 ? " yes " : " no ") + "): config passed via constructor");
config = apiConfig
.withFallback(cmdLineConfigFile)
.withFallback(testUserConfig)
.withFallback(testConfig)
.withFallback(userDirConfig)
.withFallback(userConfig)
.withFallback(cmdLineConfigRes)
.withFallback(referenceConfig);

logger.debug("Config trace: " + config.root().render(ConfigRenderOptions.defaults().
setComments(false).setJson(false)));

config = javaSystemProperties.withFallback(config)
.resolve(); // substitute variables in config if any
validateConfig();

// There could be several files with the same name from other packages,
// "version.properties" is a very common name
List<InputStream> iStreams = loadResources("version.properties", this.getClass().getClassLoader());
for (InputStream is : iStreams) {
Properties props = new Properties();
props.load(is);
if (props.getProperty("versionNumber") == null || props.getProperty("databaseVersion") == null) {
continue;
}
break;
}
} catch (Exception e) {
logger.error("Can't read config.", e);
throw new RuntimeException(e);
}
}

/**
* Loads resources using given ClassLoader assuming, there could be several resources
* with the same name
*/
public static List<InputStream> loadResources(
final String name, final ClassLoader classLoader) throws IOException {
final List<InputStream> list = new ArrayList<InputStream>();
final Enumeration<URL> systemResources =
(classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader)
.getResources(name);
while (systemResources.hasMoreElements()) {
list.add(systemResources.nextElement().openStream());
}
return list;
}
private static Logger logger = LoggerFactory.getLogger("general");

public Config getConfig() {
return config;
}
private boolean vmTraceCompressed = false;
private boolean vmOn = true;
private boolean vmTrace = true;

public boolean vmOn() {
return true;
}
private SystemProperties() {
}

public boolean vmTrace() {
return false;
}
private static class SystemPropertiesInstance {

/**
* Puts a new config atop of existing stack making the options
* in the supplied config overriding existing options
* Once put this config can't be removed
*
* @param overrideOptions - atop config
*/
public void overrideParams(Config overrideOptions) {
config = overrideOptions.withFallback(config);
validateConfig();
}
private static final SystemProperties INSTANCE = new SystemProperties();
}

/**
* Puts a new config atop of existing stack making the options
* in the supplied config overriding existing options
* Once put this config can't be removed
*
* @param keyValuePairs [name] [value] [name] [value] ...
*/
public void overrideParams(String ... keyValuePairs) {
if (keyValuePairs.length % 2 != 0) throw new RuntimeException("Odd argument number");
Map<String, String> map = new HashMap<>();
for (int i = 0; i < keyValuePairs.length; i += 2) {
map.put(keyValuePairs[i], keyValuePairs[i + 1]);
}
overrideParams(map);
}
public static SystemProperties getInstance() {
return SystemPropertiesInstance.INSTANCE;
}

/**
* Puts a new config atop of existing stack making the options
* in the supplied config overriding existing options
* Once put this config can't be removed
*
* @param cliOptions - command line options to take presidency
*/
public void overrideParams(Map<String, ?> cliOptions) {
Config cliConf = ConfigFactory.parseMap(cliOptions);
overrideParams(cliConf);
}
public boolean vmOn() {
return vmOn;
}

private void validateConfig() {
for (Method method : getClass().getMethods()) {
try {
if (method.isAnnotationPresent(ValidateMe.class)) {
method.invoke(this);
}
} catch (Exception e) {
throw new RuntimeException("Error validating config method: " + method, e);
}
}
}
public boolean vmTrace() {
return vmTrace;
}

public <T> T getProperty(String propName, T defaultValue) {
if (!config.hasPath(propName)) return defaultValue;
String string = config.getString(propName);
if (string.trim().isEmpty()) return defaultValue;
return (T) config.getAnyRef(propName);
}
public boolean vmTraceCompressed() {
return vmTraceCompressed;
}


}
5 changes: 3 additions & 2 deletions src/main/java/org/tron/common/runtime/vm/VM.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.tron.common.runtime.config.SystemProperties;
import org.tron.common.runtime.vm.program.Program;
import org.tron.common.runtime.vm.program.Stack;
import org.tron.core.config.args.Args;
import org.tron.core.exception.ContractValidateException;

@Slf4j(topic = "VM")
Expand All @@ -41,7 +42,7 @@ public class VM {
private final SystemProperties config;

public VM() {
this(SystemProperties.getDefault());
config = SystemProperties.getInstance();
}

@Autowired
Expand Down Expand Up @@ -93,7 +94,7 @@ private long calcMemGas(GasCost gasCosts, long oldMemSize, BigInteger newMemSize

public void step(Program program)
throws ContractValidateException {
if (vmTrace) {
if (config.vmTrace()) {
program.saveOpTrace();
}

Expand Down
9 changes: 5 additions & 4 deletions src/main/java/org/tron/common/runtime/vm/program/Program.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public Program(byte[] ops, ProgramInvoke programInvoke) {
}

public Program(byte[] ops, ProgramInvoke programInvoke, InternalTransaction transaction) {
this(ops, programInvoke, transaction, SystemProperties.getDefault());
this(ops, programInvoke, transaction, SystemProperties.getInstance());
}

public Program(byte[] ops, ProgramInvoke programInvoke, InternalTransaction transaction,
Expand All @@ -142,11 +142,11 @@ public Program(byte[] codeHash, byte[] ops, ProgramInvoke programInvoke,
//this.codeHash = codeHash;
this.ops = nullToEmpty(ops);

//traceListener = new ProgramTraceListener(config.vmTrace());
traceListener = new ProgramTraceListener(config.vmTrace());
this.memory = setupProgramListener(new Memory());
this.stack = setupProgramListener(new Stack());
this.contractState = setupProgramListener(new ContractState(programInvoke));
//this.trace = new ProgramTrace(config, programInvoke);
this.trace = new ProgramTrace(config, programInvoke);

this.transactionHash = transaction.getHash();
}
Expand Down Expand Up @@ -195,7 +195,7 @@ private InternalTransaction addInternalTx(DataWord gasLimit, byte[] senderAddres

private <T extends ProgramListenerAware> T setupProgramListener(T programListenerAware) {
if (programListener.isEmpty()) {
//programListener.addListener(traceListener);
programListener.addListener(traceListener);
programListener.addListener(storageDiffListener);
}

Expand Down Expand Up @@ -512,6 +512,7 @@ this, new DataWord(newAddress), getOwnerAddress(), value,
vm.play(program);
result = program.getResult();

getTrace().merge(program.getTrace());
getResult().merge(result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@

import java.util.ArrayList;
import java.util.List;
import org.tron.core.config.args.Args;

import static java.lang.String.format;
import static org.tron.common.runtime.utils.MUtil.convertToTronAddress;
import static org.tron.common.runtime.vm.trace.Serializers.serializeFieldsOnly;
import static org.tron.common.utils.ByteUtil.toHexString;

Expand All @@ -43,7 +45,7 @@ public ProgramTrace() {

public ProgramTrace(SystemProperties config, ProgramInvoke programInvoke) {
if (programInvoke != null && config.vmTrace()) {
contractAddress = Hex.toHexString(programInvoke.getOwnerAddress().getLast20Bytes());
contractAddress = Hex.toHexString(convertToTronAddress(programInvoke.getOwnerAddress().getLast20Bytes()));
}
}

Expand Down
Loading

0 comments on commit 54ba471

Please sign in to comment.