From 5f5a1e96448f9f79ae4d8d0212c99bbd43ee8989 Mon Sep 17 00:00:00 2001 From: Jake Lang Date: Wed, 28 Mar 2018 18:41:09 -0400 Subject: [PATCH 1/3] Runtime evm_option for debugging --- src/hera.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/hera.cpp b/src/hera.cpp index 3bd5a58c1..697b2d158 100644 --- a/src/hera.cpp +++ b/src/hera.cpp @@ -50,7 +50,9 @@ struct hera_instance : evm_instance { #if HERA_EVM2WASM bool use_evm2wasm_js = false; #endif - +#if HERA_DEBUGGING + bool debug = false; +#endif hera_instance() : evm_instance({EVM_ABI_VERSION, nullptr, nullptr, nullptr}) {} }; @@ -263,6 +265,9 @@ evm_result evm_execute( evm_result ret; memset(&ret, 0, sizeof(evm_result)); + // Declare hera_instance here so that VM options are accessible + hera_instance* hera = static_cast(instance); + try { heraAssert(msg->gas >= 0, "Negative startgas?"); @@ -273,7 +278,6 @@ evm_result evm_execute( // ensure we can only handle WebAssembly version 1 if (!hasWasmPreamble(_code)) { - hera_instance* hera = static_cast(instance); #if HERA_EVM2WASM // Translate EVM bytecode to WASM if (hera->use_evm2wasm_js) @@ -356,6 +360,11 @@ int evm_set_option( hera->use_evm2wasm_js = strcmp(value, "true") == 0; return 1; } +#endif +#if HERA_DEBUGGING + if (strcmp(name, "debug") == 0) { + hera->debug = strcmp(value, "true") == 0; + } #endif return 0; } From f21171283f80448f459d7e9476c87ad1e6f36143 Mon Sep 17 00:00:00 2001 From: Jake Lang Date: Thu, 29 Mar 2018 18:28:20 -0400 Subject: [PATCH 2/3] New switchable output stream for debug messages --- src/hera.cpp | 22 ++++++++++++++-------- src/hera.h | 10 ++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/hera.cpp b/src/hera.cpp index 697b2d158..38e88c504 100644 --- a/src/hera.cpp +++ b/src/hera.cpp @@ -110,7 +110,7 @@ vector callSystemContract( vector sentinel(evm_context* context, vector const& input) { #if HERA_DEBUGGING - cerr << "Metering (input " << input.size() << " bytes)..." << endl; + hera_debug << "Metering (input " << input.size() << " bytes)..." << endl; #endif #if HERA_METERING_CONTRACT @@ -124,7 +124,7 @@ vector sentinel(evm_context* context, vector const& input) ); #if HERA_DEBUGGING - cerr << "Metering done (output " << ret.size() << " bytes, used " << (startgas - gas) << " gas)" << endl; + hera_debug << "Metering done (output " << ret.size() << " bytes, used " << (startgas - gas) << " gas)" << endl; #endif return ret; @@ -180,7 +180,7 @@ vector evm2wasm_js(vector const& input) { vector evm2wasm(evm_context* context, vector const& input) { #if HERA_DEBUGGING - cerr << "Calling evm2wasm (input " << input.size() << " bytes)..." << endl; + hera_debug << "Calling evm2wasm (input " << input.size() << " bytes)..." << endl; #endif int64_t startgas = numeric_limits::max(); // do not charge for metering yet (give unlimited gas) @@ -193,7 +193,7 @@ vector evm2wasm(evm_context* context, vector const& input) { ); #if HERA_DEBUGGING - cerr << "evm2wasm done (output " << ret.size() << " bytes, used " << (startgas - gas) << " gas)" << endl; + hera_debug << "evm2wasm done (output " << ret.size() << " bytes, used " << (startgas - gas) << " gas)" << endl; #endif return ret; @@ -207,7 +207,7 @@ void execute( ExecutionResult & result ) { #if HERA_DEBUGGING - cerr << "Executing..." << endl; + hera_debug << "Executing..." << endl; #endif Module module; @@ -267,6 +267,12 @@ evm_result evm_execute( // Declare hera_instance here so that VM options are accessible hera_instance* hera = static_cast(instance); + + // Set fail bit on debug stream if debug messages are disabled + #if HERA_DEBUGGING + if (!hera->debug) + hera_debug.setstate(std::ios_base::failbit); + #endif try { heraAssert(msg->gas >= 0, "Negative startgas?"); @@ -328,17 +334,17 @@ evm_result evm_execute( } catch (InternalErrorException &e) { ret.status_code = EVM_INTERNAL_ERROR; #if HERA_DEBUGGING - cerr << "InternalError: " << e.what() << endl; + hera_debug << "InternalError: " << e.what() << endl; #endif } catch (exception &e) { ret.status_code = EVM_INTERNAL_ERROR; #if HERA_DEBUGGING - cerr << "Unknown exception: " << e.what() << endl; + hera_debug << "Unknown exception: " << e.what() << endl; #endif } catch (...) { ret.status_code = EVM_INTERNAL_ERROR; #if HERA_DEBUGGING - cerr << "Totally unknown exception" << endl; + hera_debug << "Totally unknown exception" << endl; #endif } diff --git a/src/hera.h b/src/hera.h index 5fa830859..f4aab45c4 100644 --- a/src/hera.h +++ b/src/hera.h @@ -36,6 +36,8 @@ # define HERA_IMPORT #endif +#include + #if __cplusplus extern "C" { #endif @@ -45,6 +47,14 @@ struct evm_instance; HERA_EXPORT struct evm_instance* hera_create(void); +/* + * Debug output stream + * At runtime this can be disabled according to evm_options + */ +#if HERA_DEBUGGING +std::ostream& hera_debug(std::cerr); +#endif + #if __cplusplus } #endif From 31ec22326b60f96c68a2c2f8e491678147134657 Mon Sep 17 00:00:00 2001 From: Jake Lang Date: Thu, 29 Mar 2018 18:49:23 -0400 Subject: [PATCH 3/3] Make EEI use new debug stream --- src/eei.cpp | 246 +++++++++++++++++++++++++-------------------------- src/hera.cpp | 24 ++--- src/hera.h | 57 ++++++++++-- 3 files changed, 185 insertions(+), 142 deletions(-) diff --git a/src/eei.cpp b/src/eei.cpp index d801b21e9..f55b53650 100644 --- a/src/eei.cpp +++ b/src/eei.cpp @@ -27,19 +27,8 @@ using namespace std; using namespace wasm; - #if HERA_DEBUGGING - -#define HERA_DEBUG cerr - -#else - -struct NullStream { - template NullStream& operator<<(const T&) { return *this; } -}; - -#define HERA_DEBUG NullStream() - +using namespace HeraDebugging; #endif namespace HeraVM { @@ -59,7 +48,9 @@ string toHex(evm_uint256be const& value) { void EthereumInterface::importGlobals(std::map& globals, Module& wasm) { (void)globals; (void)wasm; - HERA_DEBUG << "importGlobals\n"; +#if HERA_DEBUGGING + HeraDebug() << "importGlobals\n"; +#endif } #if HERA_DEBUGGING @@ -69,7 +60,7 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("print32")) { uint32_t value = arguments[0].geti32(); - cerr << "DEBUG print32: " << value << " " << hex << "0x" << value << dec << endl; + HeraDebug() << "DEBUG print32: " << value << " " << hex << "0x" << value << dec << endl; return Literal(); } @@ -77,7 +68,7 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("print64")) { uint64_t value = arguments[0].geti64(); - cerr << "DEBUG print64: " << value << " " << hex << "0x" << value << dec << endl; + HeraDebug() << "DEBUG print64: " << value << " " << hex << "0x" << value << dec << endl; return Literal(); } @@ -91,22 +82,22 @@ string toHex(evm_uint256be const& value) { bool useHex = import->base == Name("printMemHex"); - cerr << "DEBUG printMem" << (useHex ? "Hex(" : "(") << hex << "0x" << offset << ":0x" << length << "): " << dec; + HeraDebug() << "DEBUG printMem" << (useHex ? "Hex(" : "(") << hex << "0x" << offset << ":0x" << length << "): " << dec; if (useHex) { - cerr << hex; + HeraDebug() << hex; for (uint32_t i = offset; i < (offset + length); i++) { - cerr << static_cast(memory.get(i)) << " "; + HeraDebug() << static_cast(memory.get(i)) << " "; } - cerr << dec; + HeraDebug() << dec; } else { for (uint32_t i = offset; i < (offset + length); i++) { - cerr << memory.get(i) << " "; + HeraDebug() << memory.get(i) << " "; } } - cerr << endl; + HeraDebug() << endl; return Literal(); } @@ -118,30 +109,30 @@ string toHex(evm_uint256be const& value) { bool useHex = import->base == Name("printStorageHex"); - HERA_DEBUG << "DEBUG printStorage" << (useHex ? "Hex" : "") << "(0x" << hex; + HeraDebug() << "DEBUG printStorage" << (useHex ? "Hex" : "") << "(0x" << hex; // Print out the path for (uint8_t b: path.bytes) - cerr << static_cast(b); + HeraDebug() << static_cast(b); - HERA_DEBUG << "): " << dec; + HeraDebug() << "): " << dec; evm_uint256be result; context->fn_table->get_storage(&result, context, &msg.destination, &path); if (useHex) { - cerr << hex; + HeraDebug() << hex; for (uint8_t b: result.bytes) - cerr << static_cast(b) << " "; - cerr << dec; + HeraDebug() << static_cast(b) << " "; + HeraDebug() << dec; } else { for (uint8_t b: result.bytes) - cerr << b << " "; + HeraDebug() << b << " "; } - cerr << endl; + HeraDebug() << endl; return Literal(); } @@ -152,7 +143,7 @@ string toHex(evm_uint256be const& value) { uint32_t cost = static_cast(arguments[2].geti32()); int32_t sp = arguments[3].geti32(); - HERA_DEBUG << "evmTrace\n"; + HeraDebug() << "evmTrace\n"; static constexpr int stackItemSize = sizeof(evm_uint256be); heraAssert(sp <= (1024 * stackItemSize), "EVM stack pointer out of bounds."); @@ -190,17 +181,18 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("useGas")) { uint64_t gas = arguments[0].geti64(); - - HERA_DEBUG << "useGas " << gas << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "useGas " << gas << "\n"; +#endif takeGas(gas); return Literal(); } if (import->base == Name("getGasLeft")) { - HERA_DEBUG << "getGasLeft\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getGasLeft\n"; +#endif static_assert(is_same::value, "uint64_t type expected"); takeGas(GasSchedule::base); @@ -210,9 +202,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getAddress")) { uint32_t resultOffset = arguments[0].geti32(); - - HERA_DEBUG << "getAddress " << hex << resultOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getAddress " << hex << resultOffset << dec << "\n"; +#endif storeUint160(msg.destination, resultOffset); takeGas(GasSchedule::base); @@ -223,9 +215,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getBalance")) { uint32_t addressOffset = arguments[0].geti32(); uint32_t resultOffset = arguments[1].geti32(); - - HERA_DEBUG << "getBalance " << hex << addressOffset << " " << resultOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getBalance " << hex << addressOffset << " " << resultOffset << dec << "\n"; +#endif evm_address address = loadUint160(addressOffset); evm_uint256be result; @@ -239,9 +231,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getBlockHash")) { int64_t number = arguments[0].geti64(); uint32_t resultOffset = arguments[1].geti32(); - - HERA_DEBUG << "getBlockHash " << hex << number << " " << resultOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getBlockHash " << hex << number << " " << resultOffset << dec << "\n"; +#endif evm_uint256be blockhash; takeGas(GasSchedule::blockhash); @@ -252,8 +244,9 @@ string toHex(evm_uint256be const& value) { } if (import->base == Name("getCallDataSize")) { - HERA_DEBUG << "callDataSize\n"; - +#if HERA_DEBUGGING + HeraDebug() << "callDataSize\n"; +#endif takeGas(GasSchedule::base); return Literal(static_cast(msg.input_size)); @@ -263,9 +256,9 @@ string toHex(evm_uint256be const& value) { uint32_t resultOffset = arguments[0].geti32(); uint32_t dataOffset = arguments[1].geti32(); uint32_t length = arguments[2].geti32(); - - HERA_DEBUG << "callDataCopy " << hex << resultOffset << " " << dataOffset << " " << length << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "callDataCopy " << hex << resultOffset << " " << dataOffset << " " << length << dec << "\n"; +#endif heraAssert(ffs(GasSchedule::copy) + (ffs(length) - 5) <= 64, "Gas charge overflow"); heraAssert( numeric_limits::max() - GasSchedule::verylow >= GasSchedule::copy * ((uint64_t(length) + 31) / 32), @@ -281,9 +274,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getCaller")) { uint32_t resultOffset = arguments[0].geti32(); - - HERA_DEBUG << "getCaller " << hex << resultOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getCaller " << hex << resultOffset << dec << "\n"; +#endif takeGas(GasSchedule::base); storeUint160(msg.sender, resultOffset); @@ -292,9 +285,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getCallValue")) { uint32_t resultOffset = arguments[0].geti32(); - - HERA_DEBUG << "getCallValue " << hex << resultOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getCallValue " << hex << resultOffset << dec << "\n"; +#endif takeGas(GasSchedule::base); storeUint128(msg.value, resultOffset); @@ -305,9 +298,9 @@ string toHex(evm_uint256be const& value) { uint32_t resultOffset = arguments[0].geti32(); uint32_t codeOffset = arguments[1].geti32(); uint32_t length = arguments[2].geti32(); - - HERA_DEBUG << "codeCopy " << hex << resultOffset << " " << codeOffset << " " << length << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "codeCopy " << hex << resultOffset << " " << codeOffset << " " << length << dec << "\n"; +#endif heraAssert(ffs(GasSchedule::copy) + (ffs(length) - 5) <= 64, "Gas charge overflow"); heraAssert( numeric_limits::max() - GasSchedule::verylow >= GasSchedule::copy * ((uint64_t(length) + 31) / 32), @@ -320,8 +313,9 @@ string toHex(evm_uint256be const& value) { } if (import->base == Name("getCodeSize")) { - HERA_DEBUG << "getCodeSize\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getCodeSize\n"; +#endif takeGas(GasSchedule::base); return Literal(static_cast(code.size())); @@ -332,9 +326,9 @@ string toHex(evm_uint256be const& value) { uint32_t resultOffset = arguments[1].geti32(); uint32_t codeOffset = arguments[2].geti32(); uint32_t length = arguments[3].geti32(); - - HERA_DEBUG << "externalCodeCopy " << hex << addressOffset << " " << resultOffset << " " << codeOffset << " " << length << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "externalCodeCopy " << hex << addressOffset << " " << resultOffset << " " << codeOffset << " " << length << dec << "\n"; +#endif evm_address address = loadUint160(addressOffset); const uint8_t *code; size_t code_size = context->fn_table->get_code(&code, context, &address); @@ -351,9 +345,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getExternalCodeSize")) { uint32_t addressOffset = arguments[0].geti32(); - - HERA_DEBUG << "getExternalCodeSize " << hex << addressOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getExternalCodeSize " << hex << addressOffset << dec << "\n"; +#endif evm_address address = loadUint160(addressOffset); takeGas(GasSchedule::extcode); size_t code_size = context->fn_table->get_code(NULL, context, &address); @@ -363,9 +357,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getBlockCoinbase")) { uint32_t resultOffset = arguments[0].geti32(); - - HERA_DEBUG << "getBlockCoinbase " << hex << resultOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getBlockCoinbase " << hex << resultOffset << dec << "\n"; +#endif evm_tx_context tx_context; takeGas(GasSchedule::base); @@ -377,9 +371,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getBlockDifficulty")) { uint32_t offset = arguments[0].geti32(); - - HERA_DEBUG << "getBlockDifficulty " << hex << offset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getBlockDifficulty " << hex << offset << dec << "\n"; +#endif evm_tx_context tx_context; takeGas(GasSchedule::base); @@ -390,8 +384,9 @@ string toHex(evm_uint256be const& value) { } if (import->base == Name("getBlockGasLimit")) { - HERA_DEBUG << "getBlockGasLimit\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getBlockGasLimit\n"; +#endif evm_tx_context tx_context; takeGas(GasSchedule::base); @@ -404,9 +399,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getTxGasPrice")) { uint32_t valueOffset = arguments[0].geti32(); - - HERA_DEBUG << "getTxGasPrice " << hex << valueOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getTxGasPrice " << hex << valueOffset << dec << "\n"; +#endif evm_tx_context tx_context; takeGas(GasSchedule::base); @@ -420,9 +415,9 @@ string toHex(evm_uint256be const& value) { uint32_t dataOffset = arguments[0].geti32(); uint32_t length = arguments[1].geti32(); uint32_t numberOfTopics = arguments[2].geti32(); - - HERA_DEBUG << "log " << hex << dataOffset << " " << length << " " << numberOfTopics << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "log " << hex << dataOffset << " " << length << " " << numberOfTopics << dec << "\n"; +#endif heraAssert(!(msg.flags & EVM_STATIC), "\"log\" attempted in static mode"); heraAssert(numberOfTopics <= 4, "Too many topics specified"); @@ -447,8 +442,9 @@ string toHex(evm_uint256be const& value) { } if (import->base == Name("getBlockNumber")) { - HERA_DEBUG << "getBlockNumber\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getBlockNumber\n"; +#endif evm_tx_context tx_context; takeGas(GasSchedule::base); @@ -460,8 +456,9 @@ string toHex(evm_uint256be const& value) { } if (import->base == Name("getBlockTimestamp")) { - HERA_DEBUG << "getBlockTimestamp\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getBlockTimestamp\n"; +#endif evm_tx_context tx_context; takeGas(GasSchedule::base); @@ -474,9 +471,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("getTxOrigin")) { uint32_t resultOffset = arguments[0].geti32(); - - HERA_DEBUG << "getTxOrigin " << hex << resultOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getTxOrigin " << hex << resultOffset << dec << "\n"; +#endif evm_tx_context tx_context; takeGas(GasSchedule::base); @@ -489,9 +486,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("storageStore")) { uint32_t pathOffset = arguments[0].geti32(); uint32_t valueOffset = arguments[1].geti32(); - - HERA_DEBUG << "storageStore " << hex << pathOffset << " " << valueOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "storageStore " << hex << pathOffset << " " << valueOffset << dec << "\n"; +#endif heraAssert(!(msg.flags & EVM_STATIC), "\"storageStore\" attempted in static mode"); evm_uint256be path = loadUint256(pathOffset); @@ -515,9 +512,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("storageLoad")) { uint32_t pathOffset = arguments[0].geti32(); uint32_t resultOffset = arguments[1].geti32(); - - HERA_DEBUG << "storageLoad " << hex << pathOffset << " " << resultOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "storageLoad " << hex << pathOffset << " " << resultOffset << dec << "\n"; +#endif evm_uint256be path = loadUint256(pathOffset); evm_uint256be result; @@ -532,9 +529,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("return") || import->base == Name("revert")) { uint32_t offset = arguments[0].geti32(); uint32_t size = arguments[1].geti32(); - - HERA_DEBUG << (import->base == Name("revert") ? "revert " : "return ") << hex << offset << " " << size << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << (import->base == Name("revert") ? "revert " : "return ") << hex << offset << " " << size << dec << "\n"; +#endif result.returnValue = vector(size); loadMemory(offset, result.returnValue, size); @@ -544,8 +541,9 @@ string toHex(evm_uint256be const& value) { } if (import->base == Name("getReturnDataSize")) { - HERA_DEBUG << "getReturnDataSize\n"; - +#if HERA_DEBUGGING + HeraDebug() << "getReturnDataSize\n"; +#endif takeGas(GasSchedule::base); return Literal(static_cast(lastReturnData.size())); @@ -555,9 +553,9 @@ string toHex(evm_uint256be const& value) { uint32_t dataOffset = arguments[0].geti32(); uint32_t offset = arguments[1].geti32(); uint32_t size = arguments[2].geti32(); - - HERA_DEBUG << "returnDataCopy " << hex << dataOffset << " " << offset << " " << size << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "returnDataCopy " << hex << dataOffset << " " << offset << " " << size << dec << "\n"; +#endif takeGas(GasSchedule::verylow); storeMemory(lastReturnData, offset, dataOffset, size); @@ -614,15 +612,15 @@ string toHex(evm_uint256be const& value) { call_message.flags |= EVM_STATIC; } } - - HERA_DEBUG << +#if HERA_DEBUGGING + HeraDebug() << import->base << " " << hex << gas << " " << addressOffset << " " << valueOffset << " " << dataOffset << " " << dataLength << dec << "\n"; - +#endif if (dataLength) { vector input_data(dataLength); loadMemory(dataOffset, input_data, dataLength); @@ -670,9 +668,9 @@ string toHex(evm_uint256be const& value) { uint32_t dataOffset = arguments[1].geti32(); uint32_t length = arguments[2].geti32(); uint32_t resultOffset = arguments[3].geti32(); - - HERA_DEBUG << "create " << hex << valueOffset << " " << dataOffset << " " << length << dec << " " << resultOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "create " << hex << valueOffset << " " << dataOffset << " " << length << dec << " " << resultOffset << dec << "\n"; +#endif heraAssert(!(msg.flags & EVM_STATIC), "\"create\" attempted in static mode"); evm_message create_message; @@ -729,9 +727,9 @@ string toHex(evm_uint256be const& value) { if (import->base == Name("selfDestruct")) { uint32_t addressOffset = arguments[0].geti32(); - - HERA_DEBUG << "selfDestruct " << hex << addressOffset << dec << "\n"; - +#if HERA_DEBUGGING + HeraDebug() << "selfDestruct " << hex << addressOffset << dec << "\n"; +#endif heraAssert(!(msg.flags & EVM_STATIC), "\"selfDestruct\" attempted in static mode"); evm_address address = loadUint160(addressOffset); @@ -750,7 +748,9 @@ string toHex(evm_uint256be const& value) { void EthereumInterface::takeGas(uint64_t gas) { if (gas > result.gasLeft) { - HERA_DEBUG << "Out of gas :(\n"; +#if HERA_DEBUGGING + HeraDebug() << "Out of gas :(\n"; +#endif throw OutOfGasException(); } @@ -764,10 +764,10 @@ string toHex(evm_uint256be const& value) { void EthereumInterface::loadMemory(uint32_t srcOffset, uint8_t *dst, size_t length) { heraAssert((srcOffset + length) > srcOffset, "Out of bounds (source) memory copy."); - +#if HERA_DEBUGGING if (!length) - HERA_DEBUG << "Zero-length memory load from offset 0x" << hex << srcOffset << dec << "\n"; - + HeraDebug() << "Zero-length memory load from offset 0x" << hex << srcOffset << dec << "\n"; +#endif for (uint32_t i = 0; i < length; ++i) { dst[length - (i + 1)] = memory.get(srcOffset + i); } @@ -777,10 +777,10 @@ string toHex(evm_uint256be const& value) { { heraAssert((srcOffset + length) >= srcOffset, "Out of bounds (source) memory copy."); heraAssert(dst.size() >= length, "Out of bounds (destination) memory copy."); - +#if HERA_DEBUGGING if (!length) - HERA_DEBUG << "Zero-length memory load from offset 0x" << hex << srcOffset << dec <<"\n"; - + HeraDebug() << "Zero-length memory load from offset 0x" << hex << srcOffset << dec <<"\n"; +#endif for (uint32_t i = 0; i < length; ++i) { dst[i] = memory.get(srcOffset + i); } @@ -790,10 +790,10 @@ string toHex(evm_uint256be const& value) { { heraAssert((dstOffset + length) >= dstOffset, "Out of bounds (destination) memory copy."); heraAssert(memory.size() >= (dstOffset + length), "Out of bounds (destination) memory copy."); - +#if HERA_DEBUGGING if (!length) - HERA_DEBUG << "Zero-length memory store to offset 0x" << hex << dstOffset << dec << "\n"; - + HeraDebug() << "Zero-length memory store to offset 0x" << hex << dstOffset << dec << "\n"; +#endif for (uint32_t i = 0; i < length; ++i) { memory.set(dstOffset + length - (i + 1), src[i]); } @@ -805,10 +805,10 @@ string toHex(evm_uint256be const& value) { heraAssert(src.size() >= (srcOffset + length), "Out of bounds (source) memory copy."); heraAssert((dstOffset + length) >= dstOffset, "Out of bounds (destination) memory copy."); heraAssert(memory.size() >= (dstOffset + length), "Out of bounds (destination) memory copy."); - +#if HERA_DEBUGGING if (!length) - HERA_DEBUG << "Zero-length memory store to offset 0x" << hex << dstOffset << dec << "\n"; - + HeraDebug() << "Zero-length memory store to offset 0x" << hex << dstOffset << dec << "\n"; +#endif for (uint32_t i = 0; i < length; i++) { memory.set(dstOffset + i, src[srcOffset + i]); } diff --git a/src/hera.cpp b/src/hera.cpp index 38e88c504..5211ea442 100644 --- a/src/hera.cpp +++ b/src/hera.cpp @@ -44,6 +44,9 @@ using namespace std; using namespace wasm; using namespace HeraVM; +#if HERA_DEBUGGING +using namespace HeraDebugging; +#endif struct hera_instance : evm_instance { bool fallback = false; @@ -110,7 +113,7 @@ vector callSystemContract( vector sentinel(evm_context* context, vector const& input) { #if HERA_DEBUGGING - hera_debug << "Metering (input " << input.size() << " bytes)..." << endl; + HeraDebug() << "Metering (input " << input.size() << " bytes)..." << endl; #endif #if HERA_METERING_CONTRACT @@ -124,7 +127,7 @@ vector sentinel(evm_context* context, vector const& input) ); #if HERA_DEBUGGING - hera_debug << "Metering done (output " << ret.size() << " bytes, used " << (startgas - gas) << " gas)" << endl; + HeraDebug() << "Metering done (output " << ret.size() << " bytes, used " << (startgas - gas) << " gas)" << endl; #endif return ret; @@ -180,7 +183,7 @@ vector evm2wasm_js(vector const& input) { vector evm2wasm(evm_context* context, vector const& input) { #if HERA_DEBUGGING - hera_debug << "Calling evm2wasm (input " << input.size() << " bytes)..." << endl; + HeraDebug() << "Calling evm2wasm (input " << input.size() << " bytes)..." << endl; #endif int64_t startgas = numeric_limits::max(); // do not charge for metering yet (give unlimited gas) @@ -193,7 +196,7 @@ vector evm2wasm(evm_context* context, vector const& input) { ); #if HERA_DEBUGGING - hera_debug << "evm2wasm done (output " << ret.size() << " bytes, used " << (startgas - gas) << " gas)" << endl; + HeraDebug() << "evm2wasm done (output " << ret.size() << " bytes, used " << (startgas - gas) << " gas)" << endl; #endif return ret; @@ -207,7 +210,7 @@ void execute( ExecutionResult & result ) { #if HERA_DEBUGGING - hera_debug << "Executing..." << endl; + HeraDebug() << "Executing..." << endl; #endif Module module; @@ -268,10 +271,9 @@ evm_result evm_execute( // Declare hera_instance here so that VM options are accessible hera_instance* hera = static_cast(instance); - // Set fail bit on debug stream if debug messages are disabled #if HERA_DEBUGGING - if (!hera->debug) - hera_debug.setstate(std::ios_base::failbit); + hera_debug = DebugStream(); + hera_debug.setDebug(hera->debug); #endif try { @@ -334,17 +336,17 @@ evm_result evm_execute( } catch (InternalErrorException &e) { ret.status_code = EVM_INTERNAL_ERROR; #if HERA_DEBUGGING - hera_debug << "InternalError: " << e.what() << endl; + HeraDebug() << "InternalError: " << e.what() << endl; #endif } catch (exception &e) { ret.status_code = EVM_INTERNAL_ERROR; #if HERA_DEBUGGING - hera_debug << "Unknown exception: " << e.what() << endl; + HeraDebug() << "Unknown exception: " << e.what() << endl; #endif } catch (...) { ret.status_code = EVM_INTERNAL_ERROR; #if HERA_DEBUGGING - hera_debug << "Totally unknown exception" << endl; + HeraDebug() << "Totally unknown exception" << endl; #endif } diff --git a/src/hera.h b/src/hera.h index f4aab45c4..82a653ad5 100644 --- a/src/hera.h +++ b/src/hera.h @@ -38,6 +38,55 @@ #include +/* + * Debug output stream + * At runtime this is silenced if vm options disable debug messages + */ +#if HERA_DEBUGGING +namespace HeraDebugging { + class NullBuf: public std::streambuf + { + public: + virtual int overflow(int c) { return c; } + }; + + class NullStream : public std::ostream + { + public: + NullStream(): std::ostream(&m_sb) { } + private: + NullBuf m_sb; + }; + + class DebugStream + { + public: + DebugStream() + { nullstream = NullStream(); } + + bool isDebug() { return debugmode; } + + void setDebug(bool _debug) + { + debugmode = _debug; + } + + std::ostream& getStream() + { + return (debugmode) ? std::cerr : static_cast(nullstream); + } + + private: + NullStream nullstream; + bool debugmode; + }; + + DebugStream hera_debug; + +} +#define HeraDebug() hera_debug.getStream() +#endif + #if __cplusplus extern "C" { #endif @@ -47,14 +96,6 @@ struct evm_instance; HERA_EXPORT struct evm_instance* hera_create(void); -/* - * Debug output stream - * At runtime this can be disabled according to evm_options - */ -#if HERA_DEBUGGING -std::ostream& hera_debug(std::cerr); -#endif - #if __cplusplus } #endif