diff --git a/Cargo.lock b/Cargo.lock index 3c5a0d8..0f441c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1098,6 +1098,7 @@ version = "0.1.0" dependencies = [ "lru", "oxide_bytecode", + "oxide_log", ] [[package]] diff --git a/crates/oxide_code_cache/Cargo.toml b/crates/oxide_code_cache/Cargo.toml index eeb4d05..bd29776 100644 --- a/crates/oxide_code_cache/Cargo.toml +++ b/crates/oxide_code_cache/Cargo.toml @@ -8,4 +8,5 @@ description = "OxideJS - Compiler-independent compiled module cache" [dependencies] oxide_bytecode = { path = "../oxide_bytecode" } +oxide_log = { path = "../oxide_log" } lru.workspace = true diff --git a/crates/oxide_code_cache/src/code_cache_log.rs b/crates/oxide_code_cache/src/code_cache_log.rs new file mode 100644 index 0000000..ed7c657 --- /dev/null +++ b/crates/oxide_code_cache/src/code_cache_log.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! code_cache_debug { + ($($arg:tt)*) => { + oxide_log::__log_event!("oxide::kernel", oxide_log::tracing::Level::DEBUG, $($arg)*) + }; +} diff --git a/crates/oxide_code_cache/src/lib.rs b/crates/oxide_code_cache/src/lib.rs index 288094a..28ff47c 100644 --- a/crates/oxide_code_cache/src/lib.rs +++ b/crates/oxide_code_cache/src/lib.rs @@ -6,6 +6,8 @@ use std::sync::{Arc, Mutex}; use lru::LruCache; use oxide_bytecode::CompiledModule; +mod code_cache_log; + /// Shared compiled-module cache keyed by a caller-provided safe module hash. /// /// The cache intentionally does not know how to parse or compile JavaScript. @@ -26,12 +28,27 @@ impl CodeForge { } pub fn get(&self, hash: u64) -> Option> { - self.map.lock().unwrap().get(&hash).map(Arc::clone) + let result = self.map.lock().unwrap().get(&hash).map(Arc::clone); + if result.is_some() { + code_cache_debug!("CodeForge hit: hash={:#x}", hash); + } + result } pub fn insert(&self, hash: u64, module: CompiledModule) -> Arc { let module = Arc::new(module); - self.map.lock().unwrap().put(hash, Arc::clone(&module)); + let mut cache = self.map.lock().unwrap(); + let evicted_hash = if cache.len() == cache.cap().get() && !cache.contains(&hash) { + cache.peek_lru().map(|(k, _)| *k) + } else { + None + }; + cache.put(hash, Arc::clone(&module)); + drop(cache); + match evicted_hash { + Some(evicted_hash) => code_cache_debug!("CodeForge evict: hash={:#x}", evicted_hash), + None => code_cache_debug!("CodeForge insert: hash={:#x}", hash), + } module } @@ -44,6 +61,7 @@ impl CodeForge { if let Some(module) = cache.get(&hash) { let module = Arc::clone(module); drop(cache); + code_cache_debug!("CodeForge hit: hash={:#x}", hash); #[cfg(debug_assertions)] { let fresh = compile()?; @@ -57,6 +75,7 @@ impl CodeForge { } let module = Arc::new(compile()?); self.map.lock().unwrap().put(hash, Arc::clone(&module)); + code_cache_debug!("CodeForge miss: hash={:#x}", hash); Ok(module) } diff --git a/crates/oxide_kernel/src/builtin.rs b/crates/oxide_kernel/src/builtin.rs index 5bab2b8..be24cf1 100644 --- a/crates/oxide_kernel/src/builtin.rs +++ b/crates/oxide_kernel/src/builtin.rs @@ -3,6 +3,7 @@ use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes}; use oxide_types::value::JsValue; use crate::kernel::{BuiltinDirtySet, BuiltinId}; +use crate::kernel_info; use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID}; use crate::string_forge::PermInterner; @@ -643,6 +644,7 @@ impl BuiltinWorld { stub_objects, }; wire_builtin_world_links(&world); + kernel_info!("BuiltinWorld initialized"); world } diff --git a/crates/oxide_kernel/src/kernel.rs b/crates/oxide_kernel/src/kernel.rs index 040a580..467eeef 100644 --- a/crates/oxide_kernel/src/kernel.rs +++ b/crates/oxide_kernel/src/kernel.rs @@ -9,6 +9,7 @@ use oxide_types::value::JsValue; use crate::builtin::BuiltinWorld; use crate::code_forge::CodeForge; +use crate::kernel_info; use crate::prop_forge::PropForge; use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID}; use crate::string_forge::PermInterner; @@ -124,13 +125,17 @@ impl KernelCore { NonZeroUsize::new(config.max_cached_modules).expect("max_cached_modules must be greater than zero"), )); let prop_forge = Arc::new(PropForge::new()); - Arc::new(Self { + let max_cached = config.max_cached_modules; + let min_pool = config.min_pool_size; + let core = Arc::new(Self { config, perm_interner, shape_forge, code_forge, prop_forge, - }) + }); + kernel_info!("KernelCore initialized: max_cached_modules={}, min_pool={}", max_cached, min_pool); + core } pub fn perm_interner(&self) -> &Arc { diff --git a/crates/oxide_kernel/src/shape_forge.rs b/crates/oxide_kernel/src/shape_forge.rs index 119d57d..5756c5c 100644 --- a/crates/oxide_kernel/src/shape_forge.rs +++ b/crates/oxide_kernel/src/shape_forge.rs @@ -4,7 +4,7 @@ use std::sync::{Arc, RwLock}; use dashmap::DashMap; -use crate::kernel_debug; +use crate::{kernel_debug, kernel_trace}; pub type ShapeId = u32; pub type StringIndex = u32; @@ -83,6 +83,8 @@ impl ShapeForge { pub fn make_shape(&self, parent_id: ShapeId, prop_name: StringIndex) -> ShapeId { let key = Self::pack_key(parent_id, prop_name); + kernel_trace!("ShapeForge lookup: parent={} prop={}", parent_id, prop_name); + if let Some(entry) = self.transitions.get(&key) { kernel_debug!("ShapeForge transition cached parent={} prop={} -> id={}", parent_id, prop_name, *entry); return *entry; diff --git a/crates/oxide_kernel/src/string_forge.rs b/crates/oxide_kernel/src/string_forge.rs index dd45c4a..dd15afe 100644 --- a/crates/oxide_kernel/src/string_forge.rs +++ b/crates/oxide_kernel/src/string_forge.rs @@ -82,9 +82,13 @@ impl PermInterner { let id = entries.len() as u32; let leaked: &'static str = Box::leak(s.to_string().into_boxed_str()); entries.push(PermEntry { data: leaked, hash }); + let entry_count = entries.len(); drop(entries); self.hash_map.entry(hash).or_default().push(id); kernel_debug!("PermInterner intern new id={} len={}", id, s.len()); + if entry_count % 1000 == 0 { + kernel_debug!("PermInterner stats: {} strings", entry_count); + } (id, hash) }