diff --git a/Cargo.toml b/Cargo.toml index cb8a3296..721b5bd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,5 +28,7 @@ rand = "0.8.5" ryu-js = "1.0.1" sonic-rs = "0.3.16" unicode-normalization = "0.1.24" +usdt = { git = "https://github.com/aapoalas/usdt", branch = "feat/linux-stap-support" } +# usdt = { path = "../usdt/usdt" } wtf8 = "0.1" fast_float = "0.2.0" diff --git a/nova_cli/src/main.rs b/nova_cli/src/main.rs index 5ff76d7a..4905e571 100644 --- a/nova_cli/src/main.rs +++ b/nova_cli/src/main.rs @@ -19,6 +19,7 @@ use nova_vm::{ types::{Object, String as JsString}, }, engine::context::GcScope, + register_probes, }; use oxc_parser::Parser; use oxc_semantic::{SemanticBuilder, SemanticBuilderReturn}; @@ -90,6 +91,8 @@ impl HostHooks for CliHostHooks { fn main() -> Result<(), Box> { let args = Cli::parse(); + register_probes().unwrap(); + match args.command { Command::Parse { path } => { let file = std::fs::read_to_string(&path)?; diff --git a/nova_vm/Cargo.toml b/nova_vm/Cargo.toml index b7c2ea92..a40c0e03 100644 --- a/nova_vm/Cargo.toml +++ b/nova_vm/Cargo.toml @@ -22,6 +22,7 @@ ryu-js = { workspace = true } small_string = { path = "../small_string" } sonic-rs = { workspace = true, optional = true } unicode-normalization = { workspace = true } +usdt = { workspace = true } wtf8 = { workspace = true } [features] @@ -48,3 +49,4 @@ typescript = [] [build-dependencies] small_string = { path = "../small_string" } +usdt = { workspace = true } diff --git a/nova_vm/src/ecmascript/builtins/builtin_function.rs b/nova_vm/src/ecmascript/builtins/builtin_function.rs index 0f0a8e9a..9f0f133a 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_function.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_function.rs @@ -312,8 +312,27 @@ impl InternalMethods for BuiltinFunction { this_argument: Value, arguments_list: ArgumentsList, ) -> JsResult { + #[usdt::provider] + mod nova { + fn start_builtin_call(name: &str) {} + fn stop_builtin_call(name: &str) {} + } + nova::start_builtin_call!(|| { + agent[self] + .initial_name + .as_ref() + .map_or("anonymous", |name| name.as_str(agent)) + }); // 1. Return ? BuiltinCallOrConstruct(F, thisArgument, argumentsList, undefined). - builtin_call_or_construct(agent, gc, self, Some(this_argument), arguments_list, None) + let result = + builtin_call_or_construct(agent, gc, self, Some(this_argument), arguments_list, None); + nova::stop_builtin_call!(|| { + agent[self] + .initial_name + .as_ref() + .map_or("anonymous", |name| name.as_str(agent)) + }); + result } /// ### [10.3.2 \[\[Construct\]\] ( argumentsList, newTarget )](https://tc39.es/ecma262/#sec-built-in-function-objects-construct-argumentslist-newtarget) @@ -329,9 +348,28 @@ impl InternalMethods for BuiltinFunction { arguments_list: ArgumentsList, new_target: Function, ) -> JsResult { + #[usdt::provider] + mod nova { + fn start_builtin_constructor(name: &str) {} + fn stop_builtin_constructor(name: &str) {} + } + nova::start_builtin_constructor!(|| { + agent[self] + .initial_name + .as_ref() + .map_or("anonymous", |name| name.as_str(agent)) + }); // 1. Return ? BuiltinCallOrConstruct(F, uninitialized, argumentsList, newTarget). - builtin_call_or_construct(agent, gc, self, None, arguments_list, Some(new_target)) - .map(|result| result.try_into().unwrap()) + let result = + builtin_call_or_construct(agent, gc, self, None, arguments_list, Some(new_target)) + .map(|result| result.try_into().unwrap()); + nova::stop_builtin_constructor!(|| { + agent[self] + .initial_name + .as_ref() + .map_or("anonymous", |name| name.as_str(agent)) + }); + result } } diff --git a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs index 3e901e82..b879f81c 100644 --- a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs +++ b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs @@ -11,10 +11,13 @@ use oxc_ast::ast::{FormalParameters, FunctionBody}; use oxc_ecmascript::IsSimpleParameterList; use oxc_span::Span; -use crate::engine::context::{GcScope, NoGcScope}; use crate::{ ecmascript::{ abstract_operations::type_conversion::to_object, + builtins::{ + ordinary::{ordinary_create_from_constructor, ordinary_object_create_with_intrinsics}, + ArgumentsList, + }, execution::{ agent::{ get_active_script_or_module, @@ -38,17 +41,13 @@ use crate::{ BUILTIN_STRING_MEMORY, }, }, + engine::context::{GcScope, NoGcScope}, heap::{ indexes::ECMAScriptFunctionIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, }, }; -use super::{ - ordinary::{ordinary_create_from_constructor, ordinary_object_create_with_intrinsics}, - ArgumentsList, -}; - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct ECMAScriptFunction(ECMAScriptFunctionIndex); @@ -421,6 +420,17 @@ impl InternalMethods for ECMAScriptFunction { this_argument: Value, arguments_list: ArgumentsList<'_>, ) -> JsResult { + #[usdt::provider] + mod nova { + fn start_function_call(name: &str) {} + fn stop_function_call(name: &str) {} + } + nova::start_function_call!(|| { + agent[self] + .name + .as_ref() + .map_or("anonymous", |name| name.as_str(agent)) + }); // 1. Let callerContext be the running execution context. let _ = agent.running_execution_context(); // 2. Let calleeContext be PrepareForOrdinaryCall(F, undefined). @@ -462,6 +472,12 @@ impl InternalMethods for ECMAScriptFunction { // 7. Remove calleeContext from the execution context stack and restore callerContext as the running execution context. // NOTE: calleeContext must not be destroyed if it is suspended and retained for later resumption by an accessible Generator. agent.execution_context_stack.pop(); + nova::stop_function_call!(|| { + agent[self] + .name + .as_ref() + .map_or("anonymous", |name| name.as_str(agent)) + }); // 8. If result is a return completion, return result.[[Value]]. // 9. ReturnIfAbrupt(result). // 10. Return undefined. @@ -475,6 +491,17 @@ impl InternalMethods for ECMAScriptFunction { arguments_list: ArgumentsList, new_target: Function, ) -> JsResult { + #[usdt::provider] + mod nova { + fn start_ecmascript_constructor(name: &str) {} + fn stop_ecmascript_constructor(name: &str) {} + } + nova::start_ecmascript_constructor!(|| { + agent[self] + .name + .as_ref() + .map_or("anonymous", |name| name.as_str(agent)) + }); // 2. Let kind be F.[[ConstructorKind]]. let is_base = !agent[self] .ecmascript_function @@ -536,7 +563,7 @@ impl InternalMethods for ECMAScriptFunction { let value = result?; // 10. If result is a return completion, then // a. If result.[[Value]] is an Object, return result.[[Value]]. - if let Ok(value) = Object::try_from(value) { + let result = if let Ok(value) = Object::try_from(value) { Ok(value) } else // b. If kind is base, return thisArgument. @@ -562,7 +589,14 @@ impl InternalMethods for ECMAScriptFunction { // 14. Return thisBinding. Ok(this_binding) - } + }; + nova::stop_ecmascript_constructor!(|| { + agent[self] + .name + .as_ref() + .map_or("anonymous", |name| name.as_str(agent)) + }); + result } } diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs index 9cb3f276..364fa1ae 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs @@ -680,6 +680,11 @@ impl ObjectConstructor { _this_value: Value, arguments: ArgumentsList, ) -> JsResult { + #[usdt::provider] + mod nova { + fn get_prototype_of() {} + } + // nova::get_prototype_of!(|| ()); let obj = to_object(agent, gc.nogc(), arguments.get(0))?; obj.internal_get_prototype_of(agent, gc) .map(|proto| proto.map_or(Value::Null, |proto| proto.into_value())) diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index b4e44869..2ba10a0b 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -73,6 +73,12 @@ pub fn heap_gc( gc: GcScope<'_, '_>, root_realms: &mut [Option], ) { + #[usdt::provider] + mod nova { + fn start_heap_gc() {} + fn stop_heap_gc() {} + } + nova::start_heap_gc!(|| ()); let Agent { heap, execution_context_stack, @@ -995,6 +1001,7 @@ pub fn heap_gc( } sweep(agent, gc, &bits, root_realms); + nova::stop_heap_gc!(|| ()); } fn sweep( diff --git a/nova_vm/src/lib.rs b/nova_vm/src/lib.rs index 99489c0b..63584e44 100644 --- a/nova_vm/src/lib.rs +++ b/nova_vm/src/lib.rs @@ -10,3 +10,7 @@ pub mod heap; pub use engine::small_integer::SmallInteger; use heap::Heap; pub use small_string::SmallString; + +// Expose the USDT probe registering function. In Linux this is a no-op but it +// is required on illumos and OS X for DTrace to work automatically. +pub use usdt::register_probes;