From 0120f091ab9a03589f7e9b8094e4047781257a38 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 16 Nov 2024 12:40:02 +0200 Subject: [PATCH 1/6] stash: try out usdt tracing Linux perf does not understand the probes laid down by the usdt crate. This mostly makes sense, as usdt is made by oxidecomputer folks and they mainly target illumos and linux distributions with DTrace available. My assumption is that we end up with the no-action register_probes() call and hence we get no probe information. Not sure why that is but so it is. --- nova_cli/src/main.rs | 3 +++ nova_vm/Cargo.toml | 2 ++ nova_vm/build.rs | 2 ++ nova_vm/provider.d | 4 ++++ .../ecmascript/builtins/ecmascript_function.rs | 15 +++++++++++++++ nova_vm/src/lib.rs | 7 +++++++ 6 files changed, 33 insertions(+) create mode 100644 nova_vm/provider.d diff --git a/nova_cli/src/main.rs b/nova_cli/src/main.rs index 5ff76d7aa..4905e5712 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 b7c2ea921..a40c0e036 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/build.rs b/nova_vm/build.rs index 1193dcd3f..f8ef204e3 100644 --- a/nova_vm/build.rs +++ b/nova_vm/build.rs @@ -98,4 +98,6 @@ fn main() { let dest_path = Path::new(&out_dir).join("builtin_strings.rs"); let builtin_strings_data = gen_builtin_strings().unwrap(); fs::write(dest_path, builtin_strings_data).unwrap(); + + usdt::Builder::new("provider.d").build().unwrap(); } diff --git a/nova_vm/provider.d b/nova_vm/provider.d new file mode 100644 index 000000000..99574cf57 --- /dev/null +++ b/nova_vm/provider.d @@ -0,0 +1,4 @@ +provider nova { + probe start_function_call(); + probe stop_function_call(); +}; diff --git a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs index 3e901e822..68f91dd0b 100644 --- a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs +++ b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs @@ -43,6 +43,7 @@ use crate::{ WorkQueues, }, }; +use crate::{engine::context::GcScope, tracing}; use super::{ ordinary::{ordinary_create_from_constructor, ordinary_object_create_with_intrinsics}, @@ -421,6 +422,13 @@ impl InternalMethods for ECMAScriptFunction { this_argument: Value, arguments_list: ArgumentsList<'_>, ) -> JsResult { + let orig_name = agent[self].name; + let name = if let Some(name) = &orig_name { + name.as_str(agent) + } else { + &"anonymous" + }; + tracing::nova::start_function_call!(|| ()); // 1. Let callerContext be the running execution context. let _ = agent.running_execution_context(); // 2. Let calleeContext be PrepareForOrdinaryCall(F, undefined). @@ -462,6 +470,13 @@ 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(); + let orig_name = agent[self].name; + let name = if let Some(name) = &orig_name { + name.as_str(agent) + } else { + &"anonymous" + }; + tracing::nova::stop_function_call!(|| ()); // 8. If result is a return completion, return result.[[Value]]. // 9. ReturnIfAbrupt(result). // 10. Return undefined. diff --git a/nova_vm/src/lib.rs b/nova_vm/src/lib.rs index 99489c0b7..c42d08cb6 100644 --- a/nova_vm/src/lib.rs +++ b/nova_vm/src/lib.rs @@ -10,3 +10,10 @@ pub mod heap; pub use engine::small_integer::SmallInteger; use heap::Heap; pub use small_string::SmallString; + +pub(crate) mod tracing { + // Include the Rust implementation generated by the build script. + include!(concat!(env!("OUT_DIR"), "/provider.rs")); +} + +pub use usdt::register_probes; From ce304d02456535c30447d610277b17f4711f0d86 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 1 Dec 2024 15:24:55 +0200 Subject: [PATCH 2/6] MAKE IT WORK! OH YEAH BABY! --- Cargo.toml | 2 + nova_vm/build.rs | 2 - nova_vm/provider.d | 4 -- .../ecmascript/builtins/builtin_function.rs | 25 ++++++++++- .../builtins/ecmascript_function.rs | 44 ++++++++++--------- .../object_objects/object_constructor.rs | 5 +++ nova_vm/src/lib.rs | 7 +-- 7 files changed, 56 insertions(+), 33 deletions(-) delete mode 100644 nova_vm/provider.d diff --git a/Cargo.toml b/Cargo.toml index cb8a3296f..721b5bd21 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_vm/build.rs b/nova_vm/build.rs index f8ef204e3..1193dcd3f 100644 --- a/nova_vm/build.rs +++ b/nova_vm/build.rs @@ -98,6 +98,4 @@ fn main() { let dest_path = Path::new(&out_dir).join("builtin_strings.rs"); let builtin_strings_data = gen_builtin_strings().unwrap(); fs::write(dest_path, builtin_strings_data).unwrap(); - - usdt::Builder::new("provider.d").build().unwrap(); } diff --git a/nova_vm/provider.d b/nova_vm/provider.d deleted file mode 100644 index 99574cf57..000000000 --- a/nova_vm/provider.d +++ /dev/null @@ -1,4 +0,0 @@ -provider nova { - probe start_function_call(); - probe stop_function_call(); -}; diff --git a/nova_vm/src/ecmascript/builtins/builtin_function.rs b/nova_vm/src/ecmascript/builtins/builtin_function.rs index 0f0a8e9a6..658818fe6 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_function.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_function.rs @@ -313,7 +313,30 @@ impl InternalMethods for BuiltinFunction { arguments_list: ArgumentsList, ) -> JsResult { // 1. Return ? BuiltinCallOrConstruct(F, thisArgument, argumentsList, undefined). - builtin_call_or_construct(agent, gc, self, Some(this_argument), arguments_list, None) + #[usdt::provider] + mod nova { + fn start_builtin_call(name: &str) {} + // fn stop_builtin_call(name: &str) {} + } + nova::start_builtin_call!(|| { + println!("First"); + agent[self] + .initial_name + .as_ref() + .map_or("anonymous", |name| name.as_str(agent)) + }); + println!("Second"); + let result = + builtin_call_or_construct(agent, gc, self, Some(this_argument), arguments_list, None); + // nova::stop_builtin_call!(|| { + // println!("Third"); + // agent[self] + // .initial_name + // .as_ref() + // .map_or("anonymous", |name| name.as_str(agent)) + // }); + // println!("Fourth"); + result } /// ### [10.3.2 \[\[Construct\]\] ( argumentsList, newTarget )](https://tc39.es/ecma262/#sec-built-in-function-objects-construct-argumentslist-newtarget) diff --git a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs index 68f91dd0b..2bd86e382 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,12 @@ use crate::{ BUILTIN_STRING_MEMORY, }, }, + engine::context::{GcScope, NoGcScope}, heap::{ indexes::ECMAScriptFunctionIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, }, }; -use crate::{engine::context::GcScope, tracing}; - -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); @@ -341,6 +339,12 @@ impl FunctionInternalProperties for ECMAScriptFunction { } } +#[usdt::provider] +mod nova { + fn start_function_call(name: &str) {} + fn stop_function_call(name: &str) {} +} + impl InternalMethods for ECMAScriptFunction { fn internal_get_own_property( self, @@ -422,13 +426,12 @@ impl InternalMethods for ECMAScriptFunction { this_argument: Value, arguments_list: ArgumentsList<'_>, ) -> JsResult { - let orig_name = agent[self].name; - let name = if let Some(name) = &orig_name { - name.as_str(agent) - } else { - &"anonymous" - }; - tracing::nova::start_function_call!(|| ()); + 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). @@ -470,13 +473,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(); - let orig_name = agent[self].name; - let name = if let Some(name) = &orig_name { - name.as_str(agent) - } else { - &"anonymous" - }; - tracing::nova::stop_function_call!(|| ()); + 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. 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 9cb3f2763..364fa1aed 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/lib.rs b/nova_vm/src/lib.rs index c42d08cb6..63584e44b 100644 --- a/nova_vm/src/lib.rs +++ b/nova_vm/src/lib.rs @@ -11,9 +11,6 @@ pub use engine::small_integer::SmallInteger; use heap::Heap; pub use small_string::SmallString; -pub(crate) mod tracing { - // Include the Rust implementation generated by the build script. - include!(concat!(env!("OUT_DIR"), "/provider.rs")); -} - +// 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; From 298366954c076f59c2010574c48a3afa97491747 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 1 Dec 2024 15:26:53 +0200 Subject: [PATCH 3/6] nit --- nova_vm/src/ecmascript/builtins/builtin_function.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nova_vm/src/ecmascript/builtins/builtin_function.rs b/nova_vm/src/ecmascript/builtins/builtin_function.rs index 658818fe6..ac9c021ef 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_function.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_function.rs @@ -319,23 +319,19 @@ impl InternalMethods for BuiltinFunction { // fn stop_builtin_call(name: &str) {} } nova::start_builtin_call!(|| { - println!("First"); agent[self] .initial_name .as_ref() .map_or("anonymous", |name| name.as_str(agent)) }); - println!("Second"); let result = builtin_call_or_construct(agent, gc, self, Some(this_argument), arguments_list, None); // nova::stop_builtin_call!(|| { - // println!("Third"); // agent[self] // .initial_name // .as_ref() // .map_or("anonymous", |name| name.as_str(agent)) // }); - // println!("Fourth"); result } From d26f048e110889a55fc2dadb28d4dfb58fb6a37f Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 1 Dec 2024 15:27:53 +0200 Subject: [PATCH 4/6] More --- .../src/ecmascript/builtins/builtin_function.rs | 14 +++++++------- .../src/ecmascript/builtins/ecmascript_function.rs | 11 +++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/nova_vm/src/ecmascript/builtins/builtin_function.rs b/nova_vm/src/ecmascript/builtins/builtin_function.rs index ac9c021ef..6fb9f81c5 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_function.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_function.rs @@ -316,7 +316,7 @@ impl InternalMethods for BuiltinFunction { #[usdt::provider] mod nova { fn start_builtin_call(name: &str) {} - // fn stop_builtin_call(name: &str) {} + fn stop_builtin_call(name: &str) {} } nova::start_builtin_call!(|| { agent[self] @@ -326,12 +326,12 @@ impl InternalMethods for BuiltinFunction { }); 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)) - // }); + nova::stop_builtin_call!(|| { + 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 2bd86e382..bb0ab932e 100644 --- a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs +++ b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs @@ -339,12 +339,6 @@ impl FunctionInternalProperties for ECMAScriptFunction { } } -#[usdt::provider] -mod nova { - fn start_function_call(name: &str) {} - fn stop_function_call(name: &str) {} -} - impl InternalMethods for ECMAScriptFunction { fn internal_get_own_property( self, @@ -426,6 +420,11 @@ 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 From a120d73b7d4f46e1b86a1267a23d0eb0ae6faccb Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 1 Dec 2024 15:42:20 +0200 Subject: [PATCH 5/6] Trace builtin and ECMAScript functions and constructors --- .../ecmascript/builtins/builtin_function.rs | 25 ++++++++++++++++--- .../builtins/ecmascript_function.rs | 22 ++++++++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/nova_vm/src/ecmascript/builtins/builtin_function.rs b/nova_vm/src/ecmascript/builtins/builtin_function.rs index 6fb9f81c5..620d67484 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_function.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_function.rs @@ -312,12 +312,12 @@ impl InternalMethods for BuiltinFunction { this_argument: Value, arguments_list: ArgumentsList, ) -> JsResult { - // 1. Return ? BuiltinCallOrConstruct(F, thisArgument, argumentsList, undefined). #[usdt::provider] mod nova { fn start_builtin_call(name: &str) {} fn stop_builtin_call(name: &str) {} } + // 1. Return ? BuiltinCallOrConstruct(F, thisArgument, argumentsList, undefined). nova::start_builtin_call!(|| { agent[self] .initial_name @@ -348,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 bb0ab932e..b879f81c9 100644 --- a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs +++ b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs @@ -491,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 @@ -552,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. @@ -578,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 } } From 5df37407d32d6bc820a4bccfd0bd8a4efaa73503 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 1 Dec 2024 15:49:10 +0200 Subject: [PATCH 6/6] Trace heap GC --- nova_vm/src/ecmascript/builtins/builtin_function.rs | 2 +- nova_vm/src/heap/heap_gc.rs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/nova_vm/src/ecmascript/builtins/builtin_function.rs b/nova_vm/src/ecmascript/builtins/builtin_function.rs index 620d67484..9f0f133a1 100644 --- a/nova_vm/src/ecmascript/builtins/builtin_function.rs +++ b/nova_vm/src/ecmascript/builtins/builtin_function.rs @@ -317,13 +317,13 @@ impl InternalMethods for BuiltinFunction { fn start_builtin_call(name: &str) {} fn stop_builtin_call(name: &str) {} } - // 1. Return ? BuiltinCallOrConstruct(F, thisArgument, argumentsList, undefined). 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). let result = builtin_call_or_construct(agent, gc, self, Some(this_argument), arguments_list, None); nova::stop_builtin_call!(|| { diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index b4e448693..2ba10a0b4 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(