diff --git a/build.cm b/build.cm index 70fcf93b..a29b0e7a 100644 --- a/build.cm +++ b/build.cm @@ -971,7 +971,7 @@ Build.compile_native = function(src_path, target, buildtype, pkg) { log.error('Linking native dylib failed for: ' + src_path); disrupt } - log.console('Built native: ' + fd.basename(dylib_path)) + log.shop('compiled native: ' + src_path) return dylib_path } @@ -1043,7 +1043,7 @@ Build.compile_native_ir = function(optimized, src_path, opts) { log.error('Linking native dylib failed for: ' + src_path); disrupt } - log.console('Built native: ' + fd.basename(dylib_path)) + log.shop('compiled native: ' + src_path) return dylib_path } diff --git a/internal/engine.cm b/internal/engine.cm index 604afdc5..788c3976 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -1558,6 +1558,7 @@ $_.clock(_ => { } env.use = function(path) { + if (path == 'prosperon/compositor') log.shop('DEBUG engine env.use: pkg=' + (pkg || '(null)')) var ck = 'core/' + path var _use_core_result = null var _use_core_ok = false diff --git a/internal/shop.cm b/internal/shop.cm index 9396e05a..c4749376 100644 --- a/internal/shop.cm +++ b/internal/shop.cm @@ -788,7 +788,7 @@ function resolve_mod_fn(path, pkg) { if (dylib_path) { handle = os.dylib_open(dylib_path) if (handle) { - sym = pkg && _stem ? Shop.c_symbol_for_file(pkg, _stem) : null + sym = pkg ? Shop.c_symbol_for_file(pkg, _stem || fd.basename(path)) : null return {_native: true, _handle: handle, _sym: sym} } } @@ -1280,6 +1280,7 @@ Shop.is_loaded = function is_loaded(path, package_context) { // Create a use function bound to a specific package context function make_use_fn(pkg, force_native) { return function(path) { + if (path == 'prosperon/compositor') log.shop('DEBUG make_use_fn: pkg=' + (is_text(pkg) ? pkg : '(non-text)') + ' force_native=' + (force_native ? 'true' : 'false')) var _native = null if (force_native && !native_mode) { _native = function() { @@ -1323,6 +1324,7 @@ function execute_module(info) env = stone(env) used = os.native_module_load_named( mod_resolve.symbol._handle, mod_resolve.symbol._sym, env) + log.shop('loaded ' + info.cache_key + ' [native]') } else { // Build env with runtime fns, capabilities, and use function file_info = Shop.file_info(mod_resolve.path) @@ -1334,6 +1336,7 @@ function execute_module(info) // Load compiled bytecode with env used = mach_load(mod_resolve.symbol, env) + log.shop('loaded ' + info.cache_key + ' [bytecode]') } } else if (c_resolve.scope < 900) { // C only @@ -1373,6 +1376,11 @@ Shop.use = function use(path, _pkg_ctx) { log.error("use() expects a text module path, but received a non-text value") disrupt } + if (path == 'prosperon/compositor') { + if (is_text(_pkg_ctx)) log.shop('DEBUG use(): _pkg_ctx=' + _pkg_ctx) + else if (_pkg_ctx == null) log.shop('DEBUG use(): _pkg_ctx=NULL') + else log.shop('DEBUG use(): _pkg_ctx is non-text non-null') + } var package_context = is_core_dir(_pkg_ctx) ? 'core' : safe_canonicalize(_pkg_ctx) // Check for embedded module (static builds) var embed_key = 'embedded:' + path diff --git a/source/cell.c b/source/cell.c index 9e51b866..58276e20 100644 --- a/source/cell.c +++ b/source/cell.c @@ -789,9 +789,32 @@ double cell_random() { return (double)buf / 9007199254740992.0; } +static cell_hook g_cell_trace_hook = NULL; + void cell_trace_sethook(cell_hook hook) { - (void)hook; + g_cell_trace_hook = hook; +} + +cell_hook cell_trace_gethook(void) +{ + return g_cell_trace_hook; +} + +void cell_rt_set_trace_hook(JSContext *ctx, cell_hook hook) +{ + struct cell_rt *rt = JS_GetContextOpaque(ctx); + if (rt) rt->trace_hook = hook; +} + +const char *cell_rt_name(struct cell_rt *rt) +{ + return rt->name; +} + +JSContext *cell_rt_context(struct cell_rt *rt) +{ + return rt->context; } int uncaught_exception(JSContext *js, JSValue v) diff --git a/source/cell.h b/source/cell.h index 1f0692ae..cb76bfac 100644 --- a/source/cell.h +++ b/source/cell.h @@ -37,8 +37,15 @@ JSValue js_core_json_use(JSContext *js); #define CELL_HOOK_ENTER 1 #define CELL_HOOK_EXIT 2 -typedef void (*cell_hook)(const char *name, int type); +struct cell_rt; +typedef void (*cell_hook)(struct cell_rt *rt, int type); void cell_trace_sethook(cell_hook); +cell_hook cell_trace_gethook(void); +void cell_rt_set_trace_hook(JSContext *ctx, cell_hook hook); +size_t cell_ctx_heap_used(JSContext *ctx); +const char *cell_rt_name(struct cell_rt *rt); +JSContext *cell_rt_context(struct cell_rt *rt); +void js_debug_gethook(JSContext *ctx, js_hook *hook, int *type, void **user); // Macros to help with creating scripts #define MIST_CFUNC_DEF(name, length, func1, props) { name, props, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } } diff --git a/source/mach.c b/source/mach.c index 4dc27394..bb3a96de 100644 --- a/source/mach.c +++ b/source/mach.c @@ -1345,6 +1345,20 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, pc = code->entry_point; result = JS_NULL; + + /* Fire trace hook for top-level register function entry */ + if (unlikely(ctx->trace_hook) && (ctx->trace_type & JS_HOOK_CALL)) { + js_debug dbg = {0}; + if (code->name_cstr) + snprintf(dbg.name, sizeof(dbg.name), "%s", code->name_cstr); + if (code->filename_cstr) + snprintf(dbg.filename, sizeof(dbg.filename), "%s", code->filename_cstr); + if (code->line_table) + dbg.line = code->line_table[code->entry_point].line; + dbg.param_n = code->arity; + dbg.unique = (int)(uintptr_t)code; + ctx->trace_hook(ctx, JS_HOOK_CALL, &dbg, ctx->trace_data); + } } /* end normal path block */ vm_dispatch: @@ -2132,6 +2146,8 @@ vm_dispatch: stone_mutable_text(frame->slots[a]); result = frame->slots[a]; if (!JS_IsPtr(frame->caller)) goto done; + if (unlikely(ctx->trace_hook) && (ctx->trace_type & JS_HOOK_RET)) + ctx->trace_hook(ctx, JS_HOOK_RET, NULL, ctx->trace_data); { #ifdef VALIDATE_GC const char *callee_name = "?"; @@ -2177,6 +2193,8 @@ vm_dispatch: VM_CASE(MACH_RETNIL): result = JS_NULL; if (!JS_IsPtr(frame->caller)) goto done; + if (unlikely(ctx->trace_hook) && (ctx->trace_type & JS_HOOK_RET)) + ctx->trace_hook(ctx, JS_HOOK_RET, NULL, ctx->trace_data); { JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller); frame->caller = JS_NULL; @@ -2745,6 +2763,19 @@ vm_dispatch: if (fn->kind == JS_FUNC_KIND_REGISTER) { /* Register function: FRAME already allocated nr_slots — just switch */ JSCodeRegister *fn_code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code; + /* Fire trace hook for register-to-register call */ + if (unlikely(ctx->trace_hook) && (ctx->trace_type & JS_HOOK_CALL)) { + js_debug dbg = {0}; + if (fn_code->name_cstr) + snprintf(dbg.name, sizeof(dbg.name), "%s", fn_code->name_cstr); + if (fn_code->filename_cstr) + snprintf(dbg.filename, sizeof(dbg.filename), "%s", fn_code->filename_cstr); + if (fn_code->line_table) + dbg.line = fn_code->line_table[fn_code->entry_point].line; + dbg.param_n = fn_code->arity; + dbg.unique = (int)(uintptr_t)fn_code; + ctx->trace_hook(ctx, JS_HOOK_CALL, &dbg, ctx->trace_data); + } /* Save return info */ frame->address = JS_NewInt32(ctx, (pc << 16) | b); fr->caller = JS_MKPTR(frame); @@ -2800,6 +2831,23 @@ vm_dispatch: if (fn->kind == JS_FUNC_KIND_REGISTER) { JSCodeRegister *fn_code = JS_VALUE_GET_CODE(FN_READ_CODE(fn))->u.reg.code; + /* Tail call: fire RET for current, CALL for new */ + if (unlikely(ctx->trace_hook)) { + if (ctx->trace_type & JS_HOOK_RET) + ctx->trace_hook(ctx, JS_HOOK_RET, NULL, ctx->trace_data); + if (ctx->trace_type & JS_HOOK_CALL) { + js_debug dbg = {0}; + if (fn_code->name_cstr) + snprintf(dbg.name, sizeof(dbg.name), "%s", fn_code->name_cstr); + if (fn_code->filename_cstr) + snprintf(dbg.filename, sizeof(dbg.filename), "%s", fn_code->filename_cstr); + if (fn_code->line_table) + dbg.line = fn_code->line_table[fn_code->entry_point].line; + dbg.param_n = fn_code->arity; + dbg.unique = (int)(uintptr_t)fn_code; + ctx->trace_hook(ctx, JS_HOOK_CALL, &dbg, ctx->trace_data); + } + } int current_slots = (int)objhdr_cap56(frame->header); if (fn_code->nr_slots <= current_slots) { @@ -3003,6 +3051,9 @@ suspend: return result; done: + /* Fire trace hook for top-level register function return */ + if (unlikely(ctx->trace_hook) && (ctx->trace_type & JS_HOOK_RET)) + ctx->trace_hook(ctx, JS_HOOK_RET, NULL, ctx->trace_data); #ifdef HAVE_ASAN __asan_js_ctx = NULL; #endif diff --git a/source/runtime.c b/source/runtime.c index 94df3904..afdbe636 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -1935,6 +1935,10 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) { } #endif + /* Fire GC hook if registered */ + if (ctx->trace_hook && (ctx->trace_type & JS_HOOK_GC)) + ctx->trace_hook(ctx, JS_HOOK_GC, NULL, ctx->trace_data); + /* Check memory limit — kill actor if heap exceeds cap */ if (ctx->heap_memory_limit > 0 && ctx->current_block_size > ctx->heap_memory_limit) { #ifdef ACTOR_TRACE @@ -11780,6 +11784,15 @@ void js_debug_sethook (JSContext *ctx, js_hook hook, int type, void *user) { ctx->trace_data = user; } +void js_debug_gethook (JSContext *ctx, js_hook *hook, int *type, void **user) { + if (hook) *hook = ctx->trace_hook; + if (type) *type = ctx->trace_type; + if (user) *user = ctx->trace_data; +} + +size_t cell_ctx_heap_used (JSContext *ctx) { + return (size_t)(ctx->heap_free - ctx->heap_base); +} /* Public API: get stack trace as JS array of {fn, file, line, col} objects. Does NOT clear reg_current_frame — caller is responsible if needed. diff --git a/source/scheduler.c b/source/scheduler.c index e5a2ad78..f138164f 100644 --- a/source/scheduler.c +++ b/source/scheduler.c @@ -710,8 +710,13 @@ void actor_turn(cell_rt *actor) actor->name ? actor->name : actor->id, prev_state); #endif + if (!actor->trace_hook) { + cell_hook gh = cell_trace_gethook(); + if (gh) actor->trace_hook = gh; + } + if (actor->trace_hook) - actor->trace_hook(actor->name, CELL_HOOK_ENTER); + actor->trace_hook(actor, CELL_HOOK_ENTER); JSValue result; @@ -826,7 +831,7 @@ ENDTURN: actor->state = ACTOR_IDLE; if (actor->trace_hook) - actor->trace_hook(actor->name, CELL_HOOK_EXIT); + actor->trace_hook(actor, CELL_HOOK_EXIT); if (actor->disrupt) { #ifdef SCHEDULER_DEBUG @@ -853,7 +858,7 @@ ENDTURN_SLOW: actor->name ? actor->name : actor->id); #endif if (actor->trace_hook) - actor->trace_hook(actor->name, CELL_HOOK_EXIT); + actor->trace_hook(actor, CELL_HOOK_EXIT); enqueue_actor_priority(actor); pthread_mutex_unlock(actor->mutex); }