Skip to content

Commit

Permalink
add wasm-slice tool
Browse files Browse the repository at this point in the history
doehyunbaek committed Sep 3, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent c779add commit 260e876
Showing 5 changed files with 168 additions and 119 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -15,3 +15,5 @@ data/
tests/*output.txt
out/
.vscode
*.shrunken.wasm
*.shrunken.wat
21 changes: 15 additions & 6 deletions evaluation/interesting.py
Original file line number Diff line number Diff line change
@@ -48,13 +48,22 @@ def run_command(engine, test_input):

result = run_command(ENGINE, WASM)

# Check if the output contains the target bug's panic message
def is_runnable(result):
return result.stdout.find('no main export from module') == -1
def crash_on_wizard():
result = run_command(ENGINE, WASM)
return result.returncode != 0 and result.stdout.find('no main export from module') == -1

if result.returncode != 0 and is_runnable(result):
print("Return code is not 0, interesting!")
def ok_on_wasmtime():
command = ['timeout', '1', 'wasmtime', '--invoke', 'main', WASM]
result = subprocess.run(
command,
capture_output=True,
text=True,
)
return result.returncode == 0

if crash_on_wizard() and ok_on_wasmtime():
print("Interesting!")
sys.exit(0)
else:
print("Return code is 0, not interesting")
print("Not interesting")
sys.exit(1)
4 changes: 2 additions & 2 deletions evaluation/run_reduction_tool.py
Original file line number Diff line number Diff line change
@@ -32,9 +32,9 @@
}

tool_to_command = {
"wasm-reduce": "wasm-reduce -b $BINARYEN_ROOT/bin '--command=./evaluation/interesting.py test.wasm' -t test.wasm -w work.wasm",
"wasm-reduce": "wasm-reduce -b $BINARYEN_ROOT/bin '--command=./evaluation/interesting.py test.shrunken.wasm' -t test.shrunken.wasm -w work.shrunken.wasm",
"wasm-shrink": "wasm-tools shrink ./evaluation/interesting.py",
"slicedice": "",
"wasm-slice": "wasm-slice ./evaluation/interesting.py",
}

def run_command(tool, test_input):
111 changes: 0 additions & 111 deletions evaluation/run_slicedice.py

This file was deleted.

149 changes: 149 additions & 0 deletions wasm-slice
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/usr/bin/env python3

import sys, subprocess, concurrent.futures, re, os, re, subprocess, csv

# this takes upto 150GB of memory.
# TODO: why?
WASMR3_PATH = os.getenv("WASMR3_PATH", "/home/wasm-r3")
PARALLEL = os.getenv
TIMEOUT = 120

# The Wasm file is given as the first and only argument to the script.
if len(sys.argv) != 3:
print("Usage: wasm-slice <WASM_FILE>")
sys.exit(1)

interesting_script = sys.argv[1]
test_input = sys.argv[2]
test_name = os.path.splitext(os.path.basename(test_input))[0]

# We prioritize single function replay of heuristic > dynamic > all

def get_heuristic_fidx() -> list:
# Hardcode for now
# TODO: implement heuristics
testname_to_heuristic = {
"commanderkeen": [490],
"funky-kart": [1529],
"guiicons": [213],
"hydro": [1290],
"jsc": [4771],
"mandelbrot": [7],
"rfxgen": [271],
"rguilayout": [216],
"rguistyler": [260],
"riconpacker": [244],
"rtexviewer": [303],
"sqlgui": [917],
}
return testname_to_heuristic[test_name]

def get_dynamic_fidx():
command = ["wizeng.x86-64-linux", "-no-names", "-csv", "--monitors=icount", test_input]
try:
return extract_dynamic_fidx(subprocess.check_output(command, text=True))
except subprocess.CalledProcessError as e:
print(f"Command failed: {' '.join(command)}\nError: {e.output}")
raise

def extract_dynamic_fidx(csv_output: str):
dynamic_fidx = []
reader = csv.reader(csv_output.splitlines())
next(reader) # Skip header
for row in reader:
if len(row) >= 3:
function_index = int(row[0].lstrip('#'))
dynamic_count = int(row[2])
if dynamic_count > 0 and function_index != 0:
dynamic_fidx.append(function_index)
return sorted(dynamic_fidx)

def get_all_fidx():
command = f"wasm-tools objdump {test_input}"
output = subprocess.check_output(command, shell=True, text=True)
count = extract_function_count(output)
return list(range(count))

def extract_function_count(objdump_output):
for line in objdump_output.split('\n'):
if 'functions' in line:
match = re.search(r'(\d+) count', line)
if match:
return int(match.group(1))
return None


test_get_function_count = """
types | 0xb - 0x198 | 397 bytes | 55 count
functions | 0x19b - 0x396 | 507 bytes | 505 count
tables | 0x398 - 0x39f | 7 bytes | 1 count
memories | 0x3a1 - 0x3a7 | 6 bytes | 1 count
globals | 0x3aa - 0x930 | 1414 bytes | 282 count
exports | 0x933 - 0x100c | 1753 bytes | 301 count
elements | 0x100f - 0x1193 | 388 bytes | 1 count
data count | 0x1195 - 0x1197 | 2 bytes | 1 count
code | 0x119b - 0x46e2b | 285840 bytes | 505 count
data | 0x46e2f - 0x5c006 | 86487 bytes | 1422 count
"""

assert extract_function_count(test_get_function_count) == 505

def get_fidx():
heuristic_fidx = get_heuristic_fidx()
dynamic_fidx = get_dynamic_fidx()
all_fidx = get_all_fidx()

print("Heuristic function indices:", heuristic_fidx)
dynamic_filtered = [idx for idx in dynamic_fidx if idx not in heuristic_fidx]
print("Dynamic function indices (except heuristic):", dynamic_filtered)

# Filter out both heuristic and dynamic indices from all
all_filtered = [idx for idx in all_fidx if idx not in heuristic_fidx and idx not in dynamic_fidx]
print("All function indices (except heuristic and dynamic):", all_filtered)

# Create a set to keep track of added indices
added = set()
combined_fidx = []

# Add heuristic_fidx first
for idx in heuristic_fidx:
if idx not in added:
combined_fidx.append(idx)
added.add(idx)

# Add dynamic_fidx second
for idx in dynamic_fidx:
if idx not in added:
combined_fidx.append(idx)
added.add(idx)

# Add all_fidx last
for idx in all_fidx:
if idx not in added:
combined_fidx.append(idx)
added.add(idx)

return combined_fidx


def run_slicedice(testname, fidx):
try:
# fidxargs = " ".join([f"-i {f}" for f in fidx.split("-")])
command = f"timeout {TIMEOUT}s npm test slicedice -- -t {testname} -i {fidx}"
output = subprocess.check_output(command, shell=True, text=True)
# TODO: make this configurable
replay_wasm_path = f'{WASMR3_PATH}/benchmarks/{test_name}/out/{fidx}/benchmarks/bin_1/replay.wasm'
interestingness_command = [interesting_script, replay_wasm_path]
result = subprocess.run(interestingness_command, check=False)
test_input_size = os.path.getsize(test_input)
replay_wasm_size = os.path.getsize(replay_wasm_path)
print("Test input file size:", test_input_size)
print("Sliced wasm file size:", replay_wasm_size)
sys.exit(result.returncode)
except Exception as e:
print(f"Failed to run {testname} - {fidx}")
print(e)
return [testname, fidx, "fail"]

for fidx in get_fidx():
run_slicedice(test_name, fidx)

0 comments on commit 260e876

Please sign in to comment.