Files
uce/src/wasm/w3_driver.cpp
T
2026-06-13 02:07:38 +00:00

185 lines
6.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// W3 CLI driver — the exit gate for WASM-PROPOSAL §9.1 W3.
//
// Serves real requests through the production workspace runtime
// (src/wasm/worker.cpp): UCEB1 context in → core + lazily loaded real
// generated units → body/response-meta out. Each --repeat gets a fresh
// workspace, proving birth/drop. An epoch ticker thread enforces the CPU
// budget; the store limiter enforces memory; traps come back as collapsed
// wasm_trace summaries.
//
// Amalgamation TU (project style, like uce_lib.cpp): native types + DValue
// codec first, then the worker.
// same order as uce_lib.cpp, minus the native compiler/connector block
#include "../lib/types.cpp"
#include "../lib/dvalue.cpp"
#include "../lib/functionlib.cpp"
#include "../lib/hash.cpp"
#include "../lib/sys.cpp"
#include "../lib/uri.cpp"
#include "../lib/cli.cpp"
#include "../lib/wasm_trace.h"
#include "worker.cpp"
#include <atomic>
#include <thread>
static String arg_value(int argc, char** argv, int& i, const char* flag)
{
if(i + 1 >= argc)
{
fprintf(stderr, "missing value for %s\n", flag);
exit(2);
}
return(String(argv[++i]));
}
static String absolute_path(String path)
{
if(path.rfind("/", 0) == 0)
return(path);
char buf[4096];
if(!getcwd(buf, sizeof(buf)))
return(path);
return(String(buf) + "/" + path);
}
int main(int argc, char** argv)
{
WasmWorkerConfig cfg;
cfg.site_root = absolute_path("site");
String page;
String route;
StringMap params;
StringMap get_params;
std::vector<String> expects;
String expect_status;
int repeat = 1;
u64 epoch_period_ms = 50;
bool show_body = true;
for(int i = 1; i < argc; i++)
{
String arg = argv[i];
if(arg == "--core") cfg.core_wasm_path = arg_value(argc, argv, i, "--core");
else if(arg == "--site") cfg.site_root = absolute_path(arg_value(argc, argv, i, "--site"));
else if(arg == "--cache") cfg.cache_root = arg_value(argc, argv, i, "--cache");
else if(arg == "--page") page = arg_value(argc, argv, i, "--page");
else if(arg == "--route") route = arg_value(argc, argv, i, "--route");
else if(arg == "--repeat") repeat = atoi(arg_value(argc, argv, i, "--repeat").c_str());
else if(arg == "--expect") expects.push_back(arg_value(argc, argv, i, "--expect"));
else if(arg == "--expect-status") expect_status = arg_value(argc, argv, i, "--expect-status");
else if(arg == "--epoch-ticks") cfg.epoch_deadline_ticks = strtoull(arg_value(argc, argv, i, "--epoch-ticks").c_str(), 0, 10);
else if(arg == "--epoch-ms") epoch_period_ms = strtoull(arg_value(argc, argv, i, "--epoch-ms").c_str(), 0, 10);
else if(arg == "--mem-limit") cfg.memory_limit = strtoll(arg_value(argc, argv, i, "--mem-limit").c_str(), 0, 10);
else if(arg == "--table-headroom") cfg.table_headroom = (u32)atoi(arg_value(argc, argv, i, "--table-headroom").c_str());
else if(arg == "--quiet") show_body = false;
else if(arg == "--verbose") cfg.verbose = true;
else if(arg == "--param" || arg == "--get")
{
String pair = arg_value(argc, argv, i, arg.c_str());
auto eq = pair.find("=");
if(eq == String::npos)
{
fprintf(stderr, "%s expects K=V\n", arg.c_str());
return(2);
}
(arg == "--param" ? params : get_params)[pair.substr(0, eq)] = pair.substr(eq + 1);
}
else
{
fprintf(stderr, "unknown argument: %s\n", arg.c_str());
return(2);
}
}
if(page == "")
{
fprintf(stderr, "usage: w3_driver --page /demo/hello.uce [--site site] [--cache /tmp/uce/work]\n"
" [--core bin/wasm/core.wasm] [--param K=V ...] [--get K=V ...] [--route path]\n"
" [--repeat N] [--expect STR ...] [--expect-status STR] [--quiet] [--verbose]\n"
" [--epoch-ticks N] [--epoch-ms N] [--mem-limit BYTES]\n");
return(2);
}
String entry_unit = cfg.site_root + page;
WasmWorker worker(cfg);
String init_error = worker.init();
if(init_error != "")
{
fprintf(stderr, "FAIL: %s\n", init_error.c_str());
return(1);
}
// the epoch only advances through this ticker; period × deadline-ticks
// is the per-request CPU budget
std::atomic<bool> running(true);
std::thread ticker([&] {
while(running.load())
{
std::this_thread::sleep_for(std::chrono::milliseconds(epoch_period_ms));
worker.engine.increment_epoch();
}
});
DValue context_tree;
for(auto& entry : params)
context_tree["params"][entry.first] = entry.second;
if(context_tree.key("params") == 0 || !params.count("HTTP_HOST"))
context_tree["params"]["HTTP_HOST"] = "w3.test";
context_tree["params"]["SCRIPT_URL"] = page;
context_tree["params"]["REQUEST_METHOD"] = "GET";
for(auto& entry : get_params)
context_tree["get"][entry.first] = entry.second;
if(route != "")
{
context_tree["route"]["l_path"] = route;
context_tree["params"]["ROUTE_PATH"] = route;
}
context_tree["entry_unit"] = entry_unit;
bool all_ok = true;
WasmResponse last;
for(int request = 0; request < repeat && all_ok; request++)
{
f64 started = (f64)clock() / CLOCKS_PER_SEC;
last = wasm_worker_serve(worker, context_tree, entry_unit);
f64 elapsed_ms = ((f64)clock() / CLOCKS_PER_SEC - started) * 1000.0;
printf("==== request %d/%d (%.1f ms cpu) ====\n", request + 1, repeat, elapsed_ms);
if(!last.ok)
{
printf("ERROR:\n%s\n", last.error.c_str());
all_ok = false;
break;
}
printf("status: %s\n", last.meta["status"].to_string().c_str());
if(last.meta.key("headers"))
last.meta["headers"].each([](const DValue& value, String key) {
printf("header: %s: %s\n", key.c_str(), value.to_string().c_str());
});
if(show_body)
printf("---- body (%zu bytes) ----\n%.*s\n----\n",
last.body.size(), (int)last.body.size(), last.body.data());
else
printf("body: %zu bytes\n", last.body.size());
}
running.store(false);
ticker.join();
if(all_ok && expect_status != "" && last.meta["status"].to_string().find(expect_status) == String::npos)
{
printf("FAIL: status %s does not contain %s\n", last.meta["status"].to_string().c_str(), expect_status.c_str());
all_ok = false;
}
for(auto& expect : expects)
if(all_ok && last.body.find(expect) == String::npos)
{
printf("FAIL: body does not contain %s\n", expect.c_str());
all_ok = false;
}
printf(all_ok ? "W3 RESULT: PASS\n" : "W3 RESULT: FAIL\n");
return(all_ok ? 0 : 1);
}