Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.0.4] bump eos-vm submodule to fix spurious access violations in performance_test_basic_read_only test #1102

Merged
merged 9 commits into from
Jan 22, 2025
5 changes: 5 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/trx_finality_status_forked_test.py ${
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/plugin_http_api_test.py ${CMAKE_CURRENT_BINARY_DIR}/plugin_http_api_test.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/plugin_http_api_test_savanna.py ${CMAKE_CURRENT_BINARY_DIR}/plugin_http_api_test_savanna.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_contrl_c_test.py ${CMAKE_CURRENT_BINARY_DIR}/nodeos_contrl_c_test.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/read_only_trx_overlap_timeout.py ${CMAKE_CURRENT_BINARY_DIR}/read_only_trx_overlap_timeout.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/read_only_trx_test.py ${CMAKE_CURRENT_BINARY_DIR}/read_only_trx_test.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resource_monitor_plugin_test.py ${CMAKE_CURRENT_BINARY_DIR}/resource_monitor_plugin_test.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/light_validation_sync_test.py ${CMAKE_CURRENT_BINARY_DIR}/light_validation_sync_test.py COPYONLY)
Expand Down Expand Up @@ -204,6 +205,10 @@ add_test(NAME nodeos_protocol_feature_test COMMAND tests/nodeos_protocol_feature
set_property(TEST nodeos_protocol_feature_test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME compute_transaction_test COMMAND tests/compute_transaction_test.py -v -p 2 -n 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST compute_transaction_test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME read-only-trx-overlap-timeout-test COMMAND tests/read_only_trx_overlap_timeout.py ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST read-only-trx-overlap-timeout-test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME read-only-trx-overlap-timeout-no-oc-test COMMAND tests/read_only_trx_overlap_timeout.py --eos-vm-oc-enable none ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST read-only-trx-overlap-timeout-no-oc-test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME read-only-trx-basic-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 1 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST read-only-trx-basic-test PROPERTY LABELS nonparallelizable_tests)
add_test(NAME read-only-trx-parallel-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 16 --num-test-runs 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
Expand Down
161 changes: 161 additions & 0 deletions tests/read_only_trx_overlap_timeout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#!/usr/bin/env python3

import random
import time
import signal
import threading
import os
import platform
import traceback

from TestHarness import Account, Cluster, ReturnType, TestHelper, Utils, WalletMgr
from TestHarness.TestHelper import AppArgs

###############################################################
# read_only_trx_overlap_timeout tests that a ROtrx which hits the deadline timer on one thread doesn't
# disturb ROtrx on other threads
###############################################################

Print=Utils.Print
errorExit=Utils.errorExit

appArgs=AppArgs()
appArgs.add(flag="--test-length-seconds", type=int, help="number of seconds to search for a failure", default=10)
appArgs.add(flag="--eos-vm-oc-enable", type=str, help="specify eos-vm-oc-enable option", default="auto")
appArgs.add(flag="--wasm-runtime", type=str, help="if set to eos-vm-oc, must compile with EOSIO_EOS_VM_OC_DEVELOPER", default="eos-vm-jit")

args=TestHelper.parse_args({"-d","-s","--nodes-file","--seed"
,"--activate-if","--dump-error-details","-v","--leave-running"
,"--keep-logs","--unshared"}, applicationSpecificArgs=appArgs)

pnodes=1
topo=args.s
delay=args.d
total_nodes=2
debug=args.v
nodesFile=args.nodes_file
dontLaunch=nodesFile is not None
seed=args.seed
activateIF=args.activate_if
dumpErrorDetails=args.dump_error_details
testLengthSeconds=args.test_length_seconds

Utils.Debug=debug
testSuccessful=False
stopThread=False

random.seed(seed) # Use a fixed seed for repeatability.
cluster=Cluster(loggingLevel="all", unshared=args.unshared, keepRunning=True if nodesFile is not None else args.leave_running, keepLogs=args.keep_logs)

walletMgr=WalletMgr(True)
EOSIO_ACCT_PRIVATE_DEFAULT_KEY = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"
EOSIO_ACCT_PUBLIC_DEFAULT_KEY = "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"

producerNode = None
apiNode = None
tightloopAccountName = "tightloop"

def startCluster():
global total_nodes
global producerNode
global apiNode

TestHelper.printSystemInfo("BEGIN")
cluster.setWalletMgr(walletMgr)

if dontLaunch: # run test against remote cluster
jsonStr=None
with open(nodesFile, "r") as f:
jsonStr=f.read()
if not cluster.initializeNodesFromJson(jsonStr):
errorExit("Failed to initilize nodes from Json string.")
total_nodes=len(cluster.getNodes())

print("Stand up walletd")
if walletMgr.launch() is False:
errorExit("Failed to stand up keosd.")

Print("producing nodes: %d, non-producing nodes: %d, topology: %s, delay between nodes launch(seconds): %d" % (pnodes, total_nodes-pnodes, topo, delay))

Print("Stand up cluster")
# set up read-only options for API node
specificExtraNodeosArgs={}
# producer nodes will be mapped to 0 through pnodes-1, so the number pnodes is the no-producing API node
specificExtraNodeosArgs[pnodes]="--read-only-threads "
specificExtraNodeosArgs[pnodes]+=" 2 "
specificExtraNodeosArgs[pnodes]+=" --max-transaction-time "
specificExtraNodeosArgs[pnodes]+=" 10 "
if args.eos_vm_oc_enable:
if platform.system() != "Linux":
Print("OC not run on Linux. Skip the test")
exit(True) # Do not fail the test
specificExtraNodeosArgs[pnodes]+=" --eos-vm-oc-enable "
specificExtraNodeosArgs[pnodes]+=args.eos_vm_oc_enable
if args.wasm_runtime:
specificExtraNodeosArgs[pnodes]+=" --wasm-runtime "
specificExtraNodeosArgs[pnodes]+=args.wasm_runtime

if cluster.launch(pnodes=pnodes, totalNodes=total_nodes, topo=topo, delay=delay, activateIF=activateIF, specificExtraNodeosArgs=specificExtraNodeosArgs, extraNodeosArgs="--resource-monitor-not-shutdown-on-threshold-exceeded" ) is False:
errorExit("Failed to stand up eos cluster.")

Print ("Wait for Cluster stabilization")
# wait for cluster to start producing blocks
if not cluster.waitOnClusterBlockNumSync(3):
errorExit("Cluster never stabilized")

producerNode = cluster.getNode()
apiNode = cluster.nodes[-1]


def deployTestContracts():
Utils.Print("create test accounts")
tightloopAccount = Account(tightloopAccountName)
tightloopAccount.ownerPublicKey = EOSIO_ACCT_PUBLIC_DEFAULT_KEY
tightloopAccount.activePublicKey = EOSIO_ACCT_PUBLIC_DEFAULT_KEY
cluster.createAccountAndVerify(tightloopAccount, cluster.eosioAccount, buyRAM=1000000)

tightloopContractDir="unittests/test-contracts/tightloop"
tightloopWasmFile="tightloop.wasm"
tightloopAbiFile="tightloop.abi"
producerNode.publishContract(tightloopAccount, tightloopContractDir, tightloopWasmFile, tightloopAbiFile, waitForTransBlock=True)


def sendTransaction(account, action, data, auth=[], opts=None):
trx = {
"actions": [{
"account": account,
"name": action,
"authorization": auth,
"data": data
}]
}
return apiNode.pushTransaction(trx, opts)

def longROtrxThread():
Print("start longROtrxThread")

while stopThread==False:
sendTransaction(tightloopAccountName, 'doit', {"count": 50000000}, opts='--read') #50 million is a good number to always take >10ms

try:
startCluster()
deployTestContracts()

# start a background thread that constatly runs a ROtrx that is never expected to complete in max-transaction-time
thr = threading.Thread(target = longROtrxThread)
thr.start()

endTime = time.time() + testLengthSeconds
# and then run some other ROtrx that should complete successfully
while time.time() < endTime:
results = sendTransaction(tightloopAccountName, 'doit', {"count": 1000000}, opts='--read') #1 million is a good number to always take <10ms
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I am a little worried about the sensitivity across runs of this time requirement

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and yup this value is no good in CI but great locally. hmmm I will have to think some on what to do. The test is more accurate the larger this number is (without "going over"). So maybe it needs to try and sniff out a good value before starting the test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed up some changes to the test I was hoping would get it working in CI but no... still just way too sensitive to the timing. Not sure where to go from here on it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made at least one more change, cec196e, to try and improve the failures, but no luck. I think the test needs to be run with dedicated resources (really I think all the NP/LR tests should run with dedicated resources)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One counter-point to NP/PR tests running in a more dedicated environment is that the chaos-monkey of the current approach has shaken out bugs we would never see in a more stable environment.

assert(results[0])

testSuccessful = True
finally:
stopThread = True;
thr.join()
TestHelper.shutdown(cluster, walletMgr, testSuccessful, dumpErrorDetails)

errorCode = 0 if testSuccessful else 1
exit(errorCode)
1 change: 1 addition & 0 deletions unittests/test-contracts/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ add_subdirectory( test_api )
add_subdirectory( test_api_db )
add_subdirectory( test_api_multi_index )
add_subdirectory( test_ram_limit )
add_subdirectory( tightloop )
add_subdirectory( action_results )
add_subdirectory( wasm_config_bios )
add_subdirectory( params_test )
Expand Down
9 changes: 9 additions & 0 deletions unittests/test-contracts/tightloop/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
if(EOSIO_COMPILE_TEST_CONTRACTS)
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm"
COMMAND "${CDT_ROOT}/bin/eosio-wast2wasm" "${CMAKE_CURRENT_SOURCE_DIR}/tightloop.wast" -o "${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/tightloop.wast")
add_custom_target(gen_tightloop_wasm ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm")
else()
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/tightloop.wasm ${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm COPYONLY )
endif()
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/tightloop.abi ${CMAKE_CURRENT_BINARY_DIR}/tightloop.abi COPYONLY )
21 changes: 21 additions & 0 deletions unittests/test-contracts/tightloop/tightloop.abi
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": "eosio::abi/1.0",
"types": [],
"structs": [{
"name": "doit",
"base": "",
"fields": [{"name":"count","type":"uint64"}]
}],
"actions": [{
"name": "doit",
"type": "doit",
"ricardian_contract": ""
}
],
"tables": [],
"ricardian_clauses": [],
"error_messages": [],
"abi_extensions": [],
"variants": [],
"action_results": []
}
Binary file added unittests/test-contracts/tightloop/tightloop.wasm
Binary file not shown.
17 changes: 17 additions & 0 deletions unittests/test-contracts/tightloop/tightloop.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
(module
(import "env" "read_action_data" (func $read_action_data (param i32 i32) (result i32)))
(memory 1)
(export "apply" (func $apply))

(func $apply (param $receiver i64) (param $account i64) (param $action_name i64) (local $end i64)
(drop (call $read_action_data (i32.const 4) (i32.const 8)))
(set_local $end (i64.load (i32.const 4)))

(loop
(set_global $i (i64.add (i64.const 1) (get_global $i)))
(br_if 0 (i64.ne (get_local $end) (get_global $i)))
)
)

(global $i (mut i64) (i64.const 0))
)
Loading