diff --git a/etc/uce/settings.cfg b/etc/uce/settings.cfg index ef8c67f..059d038 100644 --- a/etc/uce/settings.cfg +++ b/etc/uce/settings.cfg @@ -38,6 +38,7 @@ WASM_COMPILE_SCRIPT=scripts/compile_wasm_unit # WASM RUNTIME SETTINGS. Unit execution is always routed through wasm. WASM_BACKEND_VERBOSE=0 +WASM_PROFILE_HOSTCALL_CPU=1 WASM_CORE_PATH=bin/wasm/core.wasm WASM_MEMORY_LIMIT_BYTES=536870912 WASM_EPOCH_DEADLINE_TICKS=200 diff --git a/site/doc/pages/request_perf.txt b/site/doc/pages/request_perf.txt index abb31eb..29b1c43 100644 --- a/site/doc/pages/request_perf.txt +++ b/site/doc/pages/request_perf.txt @@ -11,7 +11,7 @@ return value : performance snapshot for the active request/workspace :content Returns a DValue with timing and process metadata such as worker pid, parent pid, request count, request start times, native dispatch, workspace setup and birth, context application, guest execution, and hostcall timing. `accept_us` is divided into `transport_params_us` (FastCGI begin through the end of parameters), `transport_input_us` (parameters through the end of input), and `handler_queue_us` (input close through handler entry). This distinguishes upstream request delivery from work inside the UCE handler. -Hostcall totals include component resolution; `hostcall_cpu_us` separates thread CPU from hostcall wall time, and each bounded `hostcall_operations` item includes the same `cpu_us` attribution. MySQL, memcache, and component resolution also expose their own count and microsecond fields. Workspace setup, birth, and context application expose matching wall and thread-CPU microseconds. `execution_cpu_us` is the remaining workspace thread CPU through the snapshot after those three phases. `unit_module_operations` is a source-root-relative list of up to 32 unit loads. Each item attributes module lookup/read/parse/build/classification plus allocation, import construction, symbol resolution, instantiation, and initialization; no caller-supplied paths are exposed. `mysql_operations` is an ordered, query-text-free list of up to 64 logical MySQL operations and their microsecond durations. A connect operation also identifies its source as `new`, cross-request `worker`, or same-request `request`; the corresponding `mysql_connection_open_count`, `mysql_connection_reuse_count`, and `mysql_request_pool_hit_count` fields provide totals. `mysql_operations_dropped` reports any overflow. The profiling hostcall itself is excluded from those totals so repeated snapshots remain comparable. +Hostcall totals include component resolution; `hostcall_cpu_us` separates thread CPU from hostcall wall time, and each bounded `hostcall_operations` item includes the same `cpu_us` attribution. `WASM_PROFILE_HOSTCALL_CPU=0` disables those per-hostcall thread-CPU samples and leaves their counters at zero when profiling cost matters more than that split. MySQL, memcache, and component resolution also expose their own count and microsecond fields. Workspace setup, birth, and context application expose matching wall and thread-CPU microseconds. `execution_cpu_us` is the remaining workspace thread CPU through the snapshot after those three phases. `unit_module_operations` is a source-root-relative list of up to 32 unit loads. Each item attributes module lookup/read/parse/build/classification plus allocation, import construction, symbol resolution, instantiation, and initialization; no caller-supplied paths are exposed. `mysql_operations` is an ordered, query-text-free list of up to 64 logical MySQL operations and their microsecond durations. A connect operation also identifies its source as `new`, cross-request `worker`, or same-request `request`; the corresponding `mysql_connection_open_count`, `mysql_connection_reuse_count`, and `mysql_request_pool_hit_count` fields provide totals. `mysql_operations_dropped` reports any overflow. The profiling hostcall itself is excluded from those totals so repeated snapshots remain comparable. Component resolution is divided into `component_path_us`, `component_artifact_us`, `component_load_us`, and `component_link_us`. These aggregate path resolution, artifact readiness/freshness, Wasmtime side-module loading, and exported-handler lookup/table placement without exposing source paths. diff --git a/src/lib/sys.cpp b/src/lib/sys.cpp index 1481461..00a7d8e 100644 --- a/src/lib/sys.cpp +++ b/src/lib/sys.cpp @@ -1676,6 +1676,7 @@ StringMap make_server_settings() cfg["BIN_DIRECTORY"] = "/tmp/uce/work"; cfg["WASM_COMPILE_SCRIPT"] = "scripts/compile_wasm_unit"; cfg["WASM_BACKEND_VERBOSE"] = "0"; + cfg["WASM_PROFILE_HOSTCALL_CPU"] = "1"; cfg["WASM_CORE_PATH"] = ""; cfg["WASM_MEMORY_LIMIT_BYTES"] = std::to_string(512ull * 1024 * 1024); cfg["WASM_EPOCH_DEADLINE_TICKS"] = "200"; diff --git a/src/wasm/backend.cpp b/src/wasm/backend.cpp index 3ef33eb..497c50c 100644 --- a/src/wasm/backend.cpp +++ b/src/wasm/backend.cpp @@ -53,6 +53,7 @@ static String wasm_backend_ensure_started(Request* context) wc.memory_limit = (int64_t)to_u64(cfg["WASM_MEMORY_LIMIT_BYTES"], 512ull * 1024 * 1024); wc.epoch_deadline_ticks = to_u64(cfg["WASM_EPOCH_DEADLINE_TICKS"], 200); wc.mysql_persistent_pool_size = std::min(to_u64(cfg["MYSQL_PERSISTENT_POOL_SIZE"], 8), 64); + wc.profile_hostcall_cpu = to_bool(cfg["WASM_PROFILE_HOSTCALL_CPU"], true); wc.verbose = to_bool(cfg["WASM_BACKEND_VERBOSE"], false); // UCE_HOSTCALL_BLOCKLIST: comma-separated uce_host_* names (with or without // the "uce_host_" prefix) the sysadmin disables; each blocked call traps into diff --git a/src/wasm/worker.cpp b/src/wasm/worker.cpp index 2ebb651..a18c23d 100644 --- a/src/wasm/worker.cpp +++ b/src/wasm/worker.cpp @@ -125,6 +125,7 @@ struct WasmWorkerConfig u32 table_headroom = 4096; u64 epoch_deadline_ticks = 200; // ticker period × ticks = CPU budget u64 mysql_persistent_pool_size = 8; + bool profile_hostcall_cpu = true; bool verbose = false; // uce_host_* names (bare, without the "uce_host_" prefix) the sysadmin has // disabled via UCE_HOSTCALL_BLOCKLIST. A blocked hostcall resolves to a trap @@ -2238,14 +2239,14 @@ private: bool profile_memcache = name == "uce_host_memcache_command"; auto profiled = [self, callback, profile_index, profile_enabled, profile_mysql, profile_memcache](Caller caller, Span args, Span results) mutable -> Result { auto started = std::chrono::steady_clock::now(); - f64 cpu_started = profile_enabled ? wasm_thread_cpu_time() : 0; + f64 cpu_started = profile_enabled && self->worker.cfg.profile_hostcall_cpu ? wasm_thread_cpu_time() : 0; auto result = callback(caller, args, results); // Epoch interruption limits guest CPU, not time spent in native I/O, // process management, hashing, or other host work. Re-arm at the one // membrane every hostcall crosses so newly added blocking imports // cannot silently consume the next guest segment's budget. caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); - f64 cpu_finished = profile_enabled ? wasm_thread_cpu_time() : 0; + f64 cpu_finished = profile_enabled && self->worker.cfg.profile_hostcall_cpu ? wasm_thread_cpu_time() : 0; if(profile_enabled) { u64 elapsed = (u64)std::chrono::duration_cast(std::chrono::steady_clock::now() - started).count();