Browse Source

Profiler using measureme (#317)

Profiler
pull/450/head
Jason Williams 5 years ago committed by GitHub
parent
commit
32b0741cc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      .gitignore
  2. 18
      .vscode/tasks.json
  3. 89
      Cargo.lock
  4. 4
      README.md
  5. 7
      boa/Cargo.toml
  6. 2
      boa/src/builtins/array/mod.rs
  7. 2
      boa/src/builtins/bigint/mod.rs
  8. 2
      boa/src/builtins/boolean/mod.rs
  9. 2
      boa/src/builtins/console/mod.rs
  10. 2
      boa/src/builtins/error/mod.rs
  11. 2
      boa/src/builtins/error/range.rs
  12. 9
      boa/src/builtins/function/mod.rs
  13. 3
      boa/src/builtins/json/mod.rs
  14. 3
      boa/src/builtins/math/mod.rs
  15. 2
      boa/src/builtins/number/mod.rs
  16. 13
      boa/src/builtins/object/internal_methods_trait.rs
  17. 5
      boa/src/builtins/object/mod.rs
  18. 2
      boa/src/builtins/regexp/mod.rs
  19. 2
      boa/src/builtins/string/mod.rs
  20. 2
      boa/src/builtins/symbol/mod.rs
  21. 2
      boa/src/builtins/value/conversions.rs
  22. 11
      boa/src/builtins/value/mod.rs
  23. 3
      boa/src/environment/lexical_environment.rs
  24. 2
      boa/src/exec/array/mod.rs
  25. 2
      boa/src/exec/block/mod.rs
  26. 2
      boa/src/exec/declaration/mod.rs
  27. 2
      boa/src/exec/expression/mod.rs
  28. 2
      boa/src/exec/iteration/mod.rs
  29. 2
      boa/src/exec/mod.rs
  30. 2
      boa/src/exec/operator/mod.rs
  31. 2
      boa/src/exec/statement_list.rs
  32. 2
      boa/src/exec/try_node/mod.rs
  33. 15
      boa/src/lib.rs
  34. 95
      boa/src/profiler.rs
  35. 3
      boa/src/realm.rs
  36. 10
      boa/src/syntax/lexer/mod.rs
  37. 24
      boa/src/syntax/parser/expression/assignment/arrow_function.rs
  38. 14
      boa/src/syntax/parser/expression/assignment/conditional.rs
  39. 22
      boa/src/syntax/parser/expression/assignment/exponentiation.rs
  40. 14
      boa/src/syntax/parser/expression/assignment/mod.rs
  41. 13
      boa/src/syntax/parser/expression/left_hand_side/arguments.rs
  42. 20
      boa/src/syntax/parser/expression/left_hand_side/call.rs
  43. 20
      boa/src/syntax/parser/expression/left_hand_side/member.rs
  44. 10
      boa/src/syntax/parser/expression/left_hand_side/mod.rs
  45. 10
      boa/src/syntax/parser/expression/mod.rs
  46. 19
      boa/src/syntax/parser/expression/primary/array_initializer/mod.rs
  47. 16
      boa/src/syntax/parser/expression/primary/function_expression.rs
  48. 24
      boa/src/syntax/parser/expression/primary/object_initializer/mod.rs
  49. 10
      boa/src/syntax/parser/statement/block/mod.rs
  50. 10
      boa/src/syntax/parser/statement/break_stm/mod.rs
  51. 10
      boa/src/syntax/parser/statement/continue_stm/mod.rs
  52. 14
      boa/src/syntax/parser/statement/declaration/hoistable.rs
  53. 20
      boa/src/syntax/parser/statement/declaration/lexical.rs
  54. 10
      boa/src/syntax/parser/statement/declaration/mod.rs
  55. 14
      boa/src/syntax/parser/statement/if_stm/mod.rs
  56. 14
      boa/src/syntax/parser/statement/iteration/do_while_statement.rs
  57. 24
      boa/src/syntax/parser/statement/iteration/for_statement.rs
  58. 14
      boa/src/syntax/parser/statement/iteration/while_statement.rs
  59. 10
      boa/src/syntax/parser/statement/mod.rs
  60. 12
      boa/src/syntax/parser/statement/return_stm/mod.rs
  61. 14
      boa/src/syntax/parser/statement/switch/mod.rs
  62. 12
      boa/src/syntax/parser/statement/throw/mod.rs
  63. 20
      boa/src/syntax/parser/statement/try_stm/catch.rs
  64. 14
      boa/src/syntax/parser/statement/try_stm/finally.rs
  65. 10
      boa/src/syntax/parser/statement/try_stm/mod.rs
  66. 20
      boa/src/syntax/parser/statement/variable.rs
  67. BIN
      docs/img/profiler.png
  68. 30
      docs/profiling.md

6
.gitignore vendored

@ -17,3 +17,9 @@ yarn-error.log
# tests/js/test.js is used for testing changes locally
tests/js/test.js
# Profiling
*.string_data
*.string_index
*.events
chrome_profiler.json

18
.vscode/tasks.json vendored

@ -20,6 +20,24 @@
"clear": true
}
},
{
"type": "process",
"label": "Cargo Run (Profiler)",
"command": "cargo",
"args": ["run", "--features", "Boa/profiler", "../tests/js/test.js"],
"problemMatcher": ["$rustc"],
"group": {
"kind": "build",
"isDefault": true
},
"options": {
"env": { "RUST_BACKTRACE": "full" },
"cwd": "${workspaceFolder}/boa_cli"
},
"presentation": {
"clear": true
}
},
{
"type": "process",
"label": "Get Tokens",

89
Cargo.lock generated

@ -8,8 +8,10 @@ dependencies = [
"criterion",
"gc",
"jemallocator",
"measureme",
"num-bigint",
"num-traits",
"once_cell",
"rand",
"regex",
"rustc-hash",
@ -136,6 +138,15 @@ dependencies = [
"vec_map",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
]
[[package]]
name = "criterion"
version = "0.3.2"
@ -359,6 +370,15 @@ version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
[[package]]
name = "lock_api"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.8"
@ -374,12 +394,34 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "measureme"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fef709d3257013bba7cff14fc504e07e80631d3fe0f6d38ce63b8f6510ccb932"
dependencies = [
"byteorder",
"memmap",
"parking_lot",
"rustc-hash",
]
[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "memmap"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "memoffset"
version = "0.5.4"
@ -430,12 +472,44 @@ dependencies = [
"libc",
]
[[package]]
name = "once_cell"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"
[[package]]
name = "oorandom"
version = "11.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94af325bc33c7f60191be4e2c984d48aaa21e2854f473b85398344b60c9b6358"
[[package]]
name = "parking_lot"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
dependencies = [
"lock_api",
"parking_lot_core",
"rustc_version",
]
[[package]]
name = "parking_lot_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
dependencies = [
"cfg-if",
"cloudabi",
"libc",
"redox_syscall",
"rustc_version",
"smallvec",
"winapi",
]
[[package]]
name = "plotters"
version = "0.2.14"
@ -563,6 +637,12 @@ dependencies = [
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
[[package]]
name = "regex"
version = "1.3.7"
@ -672,6 +752,15 @@ dependencies = [
"serde",
]
[[package]]
name = "smallvec"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
dependencies = [
"maybe-uninit",
]
[[package]]
name = "strsim"
version = "0.8.0"

4
README.md

@ -71,6 +71,10 @@ See [CHANGELOG.md](./CHANGELOG.md).
- Run with `cargo run -- test.js` where `test.js` is an existing JS file.
- If any JS doesn't work then it's a bug. Please raise an issue!
## Profiling
See [Profiling](./docs/profiling.md)
## Command-line Options
```

7
boa/Cargo.toml

@ -10,6 +10,9 @@ license = "Unlicense/MIT"
exclude = ["../.vscode/*", "../Dockerfile", "../Makefile", "../.editorConfig"]
edition = "2018"
[features]
profiler = ["measureme", "once_cell"]
[dependencies]
gc = { version = "0.3.5", features = ["derive"] }
serde_json = "1.0.53"
@ -18,10 +21,12 @@ num-traits = "0.2.11"
regex = "1.3.7"
rustc-hash = "1.1.0"
num-bigint = { version = "0.2.6", features = ["serde"] }
bitflags = "1.2.1"
# Optional Dependencies
serde = { version = "1.0.110", features = ["derive"], optional = true }
bitflags = "1.2.1"
measureme = { version = "0.7.1", optional = true }
once_cell = { version = "1.4.0", optional = true }
[dev-dependencies]
criterion = "0.3.2"

2
boa/src/builtins/array/mod.rs

@ -20,6 +20,7 @@ use crate::{
value::{same_value_zero, ResultValue, Value, ValueData},
},
exec::Interpreter,
BoaProfiler,
};
use std::{
borrow::Borrow,
@ -1041,6 +1042,7 @@ impl Array {
/// Initialise the `Array` object on the global object.
#[inline]
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("array", "init");
global.set_field("Array", Self::create(global));
}
}

2
boa/src/builtins/bigint/mod.rs

@ -19,6 +19,7 @@ use crate::{
},
exec::Interpreter,
syntax::ast::bigint::BigInt as AstBigInt,
BoaProfiler,
};
#[cfg(test)]
@ -138,6 +139,7 @@ impl BigInt {
/// Initialise the `BigInt` object on the global object.
#[inline]
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("bigint", "init");
global.set_field("BigInt", Self::create(global));
}
}

2
boa/src/builtins/boolean/mod.rs

@ -19,6 +19,7 @@ use crate::{
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
BoaProfiler,
};
use std::{borrow::Borrow, ops::Deref};
@ -128,6 +129,7 @@ impl Boolean {
/// Initialise the `Boolean` object on the global object.
#[inline]
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("boolean", "init");
global.set_field("Boolean", Self::create(global));
}
}

2
boa/src/builtins/console/mod.rs

@ -23,6 +23,7 @@ use crate::{
value::{display_obj, ResultValue, Value},
},
exec::Interpreter,
BoaProfiler,
};
use rustc_hash::FxHashMap;
use std::time::SystemTime;
@ -554,5 +555,6 @@ pub fn create(global: &Value) -> Value {
/// Initialise the `console` object on the global object.
#[inline]
pub fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("console", "init");
global.set_field("console", create(global));
}

2
boa/src/builtins/error/mod.rs

@ -17,6 +17,7 @@ use crate::{
value::{ResultValue, Value},
},
exec::Interpreter,
profiler::BoaProfiler,
};
// mod eval;
@ -81,6 +82,7 @@ impl Error {
/// Initialise the global object with the `Error` object.
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("error", "init");
global.set_field("Error", Self::create(global));
}
}

2
boa/src/builtins/error/range.rs

@ -17,6 +17,7 @@ use crate::{
value::{ResultValue, Value},
},
exec::Interpreter,
profiler::BoaProfiler,
};
/// JavaScript `RangeError` impleentation.
@ -71,6 +72,7 @@ impl RangeError {
/// Initialise the global object with the `RangeError` object.
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("rangeerror", "init");
global.set_field("RangeError", Self::create(global));
}
}

9
boa/src/builtins/function/mod.rs

@ -22,6 +22,7 @@ use crate::{
environment::lexical_environment::{new_function_environment, Environment},
exec::{Executable, Interpreter},
syntax::ast::node::{FormalParameter, StatementList},
BoaProfiler,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
use std::fmt::{self, Debug};
@ -142,6 +143,7 @@ impl Function {
where
P: Into<Box<[FormalParameter]>>,
{
let _timer = BoaProfiler::global().start_event("function::builtin", "function");
Self::new(
parameter_list.into(),
None,
@ -163,6 +165,7 @@ impl Function {
interpreter: &mut Interpreter,
this_obj: &mut Value,
) -> ResultValue {
let _timer = BoaProfiler::global().start_event("function::call", "function");
if self.callable {
match self.body {
FunctionBody::BuiltIn(func) => func(this_obj, args_list, interpreter),
@ -433,6 +436,9 @@ pub fn make_builtin_fn<N>(function: NativeFunctionData, name: N, parent: &Value,
where
N: Into<String>,
{
let name_copy: String = name.into();
let label = format!("{}{}", String::from("make_builtin_fn: "), &name_copy);
let _timer = BoaProfiler::global().start_event(&label, "init");
let func = Function::builtin(Vec::new(), function);
let mut new_func = Object::function();
@ -441,11 +447,12 @@ where
let new_func_obj = Value::from(new_func);
new_func_obj.set_field("length", length);
parent.set_field(name.into(), new_func_obj);
parent.set_field(Value::from(name_copy), new_func_obj);
}
/// Initialise the `Function` object on the global object.
#[inline]
pub fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("function", "init");
global.set_field("Function", create(global));
}

3
boa/src/builtins/json/mod.rs

@ -19,7 +19,7 @@ use crate::builtins::{
property::Property,
value::{ResultValue, Value},
};
use crate::exec::Interpreter;
use crate::{exec::Interpreter, BoaProfiler};
use serde_json::{self, Value as JSONValue};
#[cfg(test)]
@ -174,5 +174,6 @@ pub fn create(global: &Value) -> Value {
/// Initialise the `JSON` object on the global object.
#[inline]
pub fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("json", "init");
global.set_field("JSON", create(global));
}

3
boa/src/builtins/math/mod.rs

@ -17,6 +17,7 @@ use crate::{
value::{ResultValue, Value},
},
exec::Interpreter,
BoaProfiler,
};
use rand::random;
use std::f64;
@ -507,6 +508,7 @@ pub fn trunc(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// Create a new `Math` object
pub fn create(global: &Value) -> Value {
let _timer = BoaProfiler::global().start_event("math:create", "init");
let math = Value::new_object(Some(global));
math.set_field("E", Value::from(f64::consts::E));
@ -553,5 +555,6 @@ pub fn create(global: &Value) -> Value {
/// Initialise the `Math` object on the global object.
#[inline]
pub fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("math", "init");
global.set_field("Math", create(global));
}

2
boa/src/builtins/number/mod.rs

@ -26,6 +26,7 @@ use crate::{
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
BoaProfiler,
};
use num_traits::float::FloatCore;
use std::{borrow::Borrow, ops::Deref};
@ -435,6 +436,7 @@ impl Number {
/// Initialise the `Number` object on the global object.
#[inline]
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("number", "init");
global.set_field("Number", Self::create(global));
}

13
boa/src/builtins/object/internal_methods_trait.rs

@ -5,10 +5,13 @@
//!
//! [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
use crate::builtins::{
object::{Object, INSTANCE_PROTOTYPE},
property::Property,
value::{same_value, Value, ValueData},
use crate::{
builtins::{
object::{Object, INSTANCE_PROTOTYPE},
property::Property,
value::{same_value, Value, ValueData},
},
BoaProfiler,
};
use std::borrow::Borrow;
use std::ops::Deref;
@ -131,6 +134,7 @@ pub trait ObjectInternalMethods {
/// [[Set]]
/// <https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver>
fn set(&mut self, field: Value, val: Value) -> bool {
let _timer = BoaProfiler::global().start_event("Object::set", "object");
// [1]
debug_assert!(Property::is_property_key(&field));
@ -168,6 +172,7 @@ pub trait ObjectInternalMethods {
}
fn define_own_property(&mut self, property_key: String, desc: Property) -> bool {
let _timer = BoaProfiler::global().start_event("Object::define_own_property", "object");
let mut current = self.get_own_property(&Value::from(property_key.to_string()));
let extensible = self.is_extensible();

5
boa/src/builtins/object/mod.rs

@ -20,6 +20,7 @@ use crate::{
value::{same_value, ResultValue, Value, ValueData},
},
exec::Interpreter,
BoaProfiler,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
use rustc_hash::FxHashMap;
@ -131,6 +132,7 @@ impl ObjectInternalMethods for Object {
/// Helper function to get an immutable internal slot or Null
fn get_internal_slot(&self, name: &str) -> Value {
let _timer = BoaProfiler::global().start_event("Object::get_internal_slot", "object");
match self.internal_slots.get(name) {
Some(v) => v.clone(),
None => Value::null(),
@ -146,6 +148,7 @@ impl ObjectInternalMethods for Object {
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p
fn get_own_property(&self, prop: &Value) -> Property {
let _timer = BoaProfiler::global().start_event("Object::get_own_property", "object");
debug_assert!(Property::is_property_key(prop));
// Prop could either be a String or Symbol
match *(*prop) {
@ -352,6 +355,7 @@ impl Object {
/// Return a new ObjectData struct, with `kind` set to Ordinary
pub fn function() -> Self {
let _timer = BoaProfiler::global().start_event("Object::Function", "object");
let mut object = Self {
kind: ObjectKind::Function,
internal_slots: FxHashMap::default(),
@ -640,5 +644,6 @@ pub fn create(global: &Value) -> Value {
/// Initialise the `Object` object on the global object.
#[inline]
pub fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("object", "init");
global.set_field("Object", create(global));
}

2
boa/src/builtins/regexp/mod.rs

@ -21,6 +21,7 @@ use crate::{
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
BoaProfiler,
};
#[cfg(test)]
@ -491,6 +492,7 @@ impl RegExp {
/// Initialise the `RegExp` object on the global object.
#[inline]
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("regexp", "init");
global.set_field("RegExp", Self::create(global));
}
}

2
boa/src/builtins/string/mod.rs

@ -21,6 +21,7 @@ use crate::{
RegExp,
},
exec::Interpreter,
BoaProfiler,
};
use regex::Regex;
use std::string::String as StdString;
@ -1077,6 +1078,7 @@ impl String {
/// Initialise the `String` object on the global object.
#[inline]
pub(crate) fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("string", "init");
global.set_field("String", Self::create(global));
}
}

2
boa/src/builtins/symbol/mod.rs

@ -28,6 +28,7 @@ use crate::{
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
BoaProfiler,
};
use gc::{Gc, GcCell};
use rand::random;
@ -100,5 +101,6 @@ pub fn create(global: &Value) -> Value {
/// Initialise the `Symbol` object on the global object.
#[inline]
pub fn init(global: &Value) {
let _timer = BoaProfiler::global().start_event("symbol", "init");
global.set_field("Symbol", create(global));
}

2
boa/src/builtins/value/conversions.rs

@ -9,6 +9,7 @@ impl From<&Value> for Value {
impl From<String> for Value {
fn from(value: String) -> Self {
let _timer = BoaProfiler::global().start_event("From<String>", "value");
Self::string(value)
}
}
@ -164,6 +165,7 @@ where
impl From<Object> for Value {
fn from(object: Object) -> Self {
let _timer = BoaProfiler::global().start_event("From<Object>", "value");
Value::object(object)
}
}

11
boa/src/builtins/value/mod.rs

@ -13,7 +13,7 @@ use crate::builtins::{
},
property::Property,
};
use crate::syntax::ast::bigint::BigInt;
use crate::{syntax::ast::bigint::BigInt, BoaProfiler};
use gc::{Finalize, Gc, GcCell, GcCellRef, Trace};
use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue};
use std::{
@ -124,6 +124,7 @@ impl Value {
/// Returns a new empty object
pub fn new_object(global: Option<&Value>) -> Self {
let _timer = BoaProfiler::global().start_event("new_object", "value");
if let Some(global) = global {
let object_prototype = global.get_field("Object").get_field(PROTOTYPE);
@ -393,6 +394,7 @@ impl ValueData {
///
/// A copy of the Property is returned.
pub fn get_property(&self, field: &str) -> Option<Property> {
let _timer = BoaProfiler::global().start_event("Value::get_property", "value");
// Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154
// This is only for primitive strings, String() objects have their lengths calculated in string.rs
if self.is_string() && field == "length" {
@ -439,6 +441,7 @@ impl ValueData {
writable: Option<bool>,
configurable: Option<bool>,
) {
let _timer = BoaProfiler::global().start_event("Value::update_property", "value");
let obj: Option<Object> = match self {
Self::Object(ref obj) => Some(obj.borrow_mut().deref_mut().clone()),
_ => None,
@ -459,6 +462,7 @@ impl ValueData {
///
/// Returns a copy of the Property.
pub fn get_internal_slot(&self, field: &str) -> Value {
let _timer = BoaProfiler::global().start_event("Value::get_internal_slot", "value");
let obj: Object = match *self {
Self::Object(ref obj) => {
let hash = obj.clone();
@ -484,6 +488,7 @@ impl ValueData {
where
F: Into<Value>,
{
let _timer = BoaProfiler::global().start_event("Value::get_field", "value");
match *field.into() {
// Our field will either be a String or a Symbol
Self::String(ref s) => {
@ -582,6 +587,7 @@ impl ValueData {
/// Check to see if the Value has the field, mainly used by environment records
pub fn has_field(&self, field: &str) -> bool {
let _timer = BoaProfiler::global().start_event("Value::has_field", "value");
self.get_property(field).is_some()
}
@ -592,6 +598,7 @@ impl ValueData {
F: Into<Value>,
V: Into<Value>,
{
let _timer = BoaProfiler::global().start_event("Value::set_field", "value");
let field = field.into();
let val = val.into();
@ -621,6 +628,7 @@ impl ValueData {
/// Set the private field in the value
pub fn set_internal_slot(&self, field: &str, val: Value) -> Value {
let _timer = BoaProfiler::global().start_event("Value::set_internal_slot", "exec");
if let Self::Object(ref obj) = *self {
obj.borrow_mut()
.internal_slots
@ -766,6 +774,7 @@ impl ValueData {
///
/// https://tc39.es/ecma262/#sec-typeof-operator
pub fn get_type(&self) -> &'static str {
let _timer = BoaProfiler::global().start_event("Value::get_type", "value");
match *self {
Self::Rational(_) | Self::Integer(_) => "number",
Self::String(_) => "string",

3
boa/src/environment/lexical_environment.rs

@ -14,6 +14,7 @@ use crate::{
global_environment_record::GlobalEnvironmentRecord,
object_environment_record::ObjectEnvironmentRecord,
},
BoaProfiler,
};
use gc::{Gc, GcCell};
use rustc_hash::{FxHashMap, FxHashSet};
@ -79,6 +80,7 @@ impl error::Error for EnvironmentError {
impl LexicalEnvironment {
pub fn new(global: Value) -> Self {
let _timer = BoaProfiler::global().start_event("LexicalEnvironment::new", "env");
let global_env = new_global_environment(global.clone(), global);
let mut lexical_env = Self {
environment_stack: VecDeque::new(),
@ -219,6 +221,7 @@ impl LexicalEnvironment {
}
pub fn new_declarative_environment(env: Option<Environment>) -> Environment {
let _timer = BoaProfiler::global().start_event("new_declarative_environment", "env");
let boxed_env = Box::new(DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(),
outer_env: env,

2
boa/src/exec/array/mod.rs

@ -4,10 +4,12 @@ use super::{Executable, Interpreter};
use crate::{
builtins::{Array, ResultValue},
syntax::ast::node::{ArrayDecl, Node},
BoaProfiler,
};
impl Executable for ArrayDecl {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let _timer = BoaProfiler::global().start_event("ArrayDecl", "exec");
let array = Array::new_array(interpreter)?;
let mut elements = Vec::new();
for elem in self.as_ref() {

2
boa/src/exec/block/mod.rs

@ -5,10 +5,12 @@ use crate::{
builtins::value::{ResultValue, Value},
environment::lexical_environment::new_declarative_environment,
syntax::ast::node::Block,
BoaProfiler,
};
impl Executable for Block {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let _timer = BoaProfiler::global().start_event("Block", "exec");
{
let env = &mut interpreter.realm_mut().environment;
env.push(new_declarative_environment(Some(

2
boa/src/exec/declaration/mod.rs

@ -10,10 +10,12 @@ use crate::{
syntax::ast::node::{
ArrowFunctionDecl, ConstDeclList, FunctionDecl, FunctionExpr, LetDeclList, VarDeclList,
},
BoaProfiler,
};
impl Executable for FunctionDecl {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let _timer = BoaProfiler::global().start_event("FunctionDecl", "exec");
let val = interpreter.create_function(
self.parameters().to_vec(),
self.body().to_vec(),

2
boa/src/exec/expression/mod.rs

@ -7,10 +7,12 @@ use crate::{
value::{ResultValue, Value, ValueData},
},
syntax::ast::node::{Call, New, Node},
BoaProfiler,
};
impl Executable for Call {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let _timer = BoaProfiler::global().start_event("Call", "exec");
let (mut this, func) = match self.expr() {
Node::GetConstField(ref obj, ref field) => {
let mut obj = obj.run(interpreter)?;

2
boa/src/exec/iteration/mod.rs

@ -5,11 +5,13 @@ use crate::{
builtins::value::{ResultValue, Value},
environment::lexical_environment::new_declarative_environment,
syntax::ast::node::ForLoop,
BoaProfiler,
};
impl Executable for ForLoop {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
// Create the block environment.
let _timer = BoaProfiler::global().start_event("ForLoop", "exec");
{
let env = &mut interpreter.realm_mut().environment;
env.push(new_declarative_environment(Some(

2
boa/src/exec/mod.rs

@ -29,6 +29,7 @@ use crate::{
constant::Const,
node::{FormalParameter, MethodDefinitionKind, Node, PropertyDefinition, StatementList},
},
BoaProfiler,
};
use std::{borrow::Borrow, ops::Deref};
@ -362,6 +363,7 @@ impl Interpreter {
impl Executable for Node {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let _timer = BoaProfiler::global().start_event("Executable", "exec");
match *self {
Node::Const(Const::Null) => Ok(Value::null()),
Node::Const(Const::Undefined) => Ok(Value::undefined()),

2
boa/src/exec/operator/mod.rs

@ -8,11 +8,13 @@ use crate::{
node::{Assign, BinOp, Node, UnaryOp},
op::{self, AssignOp, BitOp, CompOp, LogOp, NumOp},
},
BoaProfiler,
};
use std::borrow::BorrowMut;
impl Executable for Assign {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let _timer = BoaProfiler::global().start_event("Assign", "exec");
let val = self.rhs().run(interpreter)?;
match self.lhs() {
Node::Identifier(ref name) => {

2
boa/src/exec/statement_list.rs

@ -4,10 +4,12 @@ use super::{Executable, Interpreter};
use crate::{
builtins::value::{ResultValue, Value},
syntax::ast::node::StatementList,
BoaProfiler,
};
impl Executable for StatementList {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let _timer = BoaProfiler::global().start_event("StatementList", "exec");
let mut obj = Value::null();
for (i, item) in self.statements().iter().enumerate() {
let val = item.run(interpreter)?;

2
boa/src/exec/try_node/mod.rs

@ -5,6 +5,7 @@ use crate::{
builtins::value::ResultValue,
environment::lexical_environment::{new_declarative_environment, VariableScope},
syntax::ast::node::Try,
BoaProfiler,
};
#[cfg(test)]
@ -12,6 +13,7 @@ mod tests;
impl Executable for Try {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let _timer = BoaProfiler::global().start_event("Try", "exec");
let res = self.block().run(interpreter).map_or_else(
|err| {
if let Some(catch) = self.catch() {

15
boa/src/lib.rs

@ -30,18 +30,21 @@
clippy::must_use_candidate,
clippy::missing_errors_doc,
clippy::as_conversions,
clippy::let_unit_value,
missing_doc_code_examples
)]
pub mod builtins;
pub mod environment;
pub mod exec;
pub mod profiler;
pub mod realm;
pub mod syntax;
use crate::{builtins::value::ResultValue, syntax::ast::node::StatementList};
pub use crate::{
exec::{Executable, Interpreter},
profiler::BoaProfiler,
realm::Realm,
syntax::{lexer::Lexer, parser::Parser},
};
@ -71,15 +74,23 @@ pub fn forward(engine: &mut Interpreter, src: &str) -> String {
/// The str is consumed and the state of the Interpreter is changed
/// Similar to `forward`, except the current value is returned instad of the string
/// If the interpreter fails parsing an error value is returned instead (error object)
#[allow(clippy::unit_arg, clippy::drop_copy)]
pub fn forward_val(engine: &mut Interpreter, src: &str) -> ResultValue {
let main_timer = BoaProfiler::global().start_event("Main", "Main");
// Setup executor
match parser_expr(src) {
let result = match parser_expr(src) {
Ok(expr) => expr.run(engine),
Err(e) => {
eprintln!("{}", e);
std::process::exit(1);
}
}
};
// The main_timer needs to be dropped before the BoaProfiler is.
drop(main_timer);
BoaProfiler::global().drop();
result
}
/// Create a clean Interpreter and execute the code

95
boa/src/profiler.rs

@ -0,0 +1,95 @@
#![allow(missing_copy_implementations, missing_debug_implementations)]
#[cfg(feature = "profiler")]
use measureme::{EventId, Profiler, TimingGuard};
#[cfg(feature = "profiler")]
use once_cell::sync::OnceCell;
use std::fmt::{self, Debug};
#[cfg(feature = "profiler")]
use std::{
path::Path,
thread::{current, ThreadId},
};
/// MmapSerializatioSink is faster on macOS and Linux
/// but FileSerializationSink is faster on Windows
#[cfg(not(windows))]
#[cfg(feature = "profiler")]
type SerializationSink = measureme::MmapSerializationSink;
#[cfg(windows)]
#[cfg(feature = "profiler")]
type SerializationSink = measureme::FileSerializationSink;
#[cfg(feature = "profiler")]
pub struct BoaProfiler {
profiler: Profiler<SerializationSink>,
}
/// This static instance should never be public, and its only access should be done through the `global()` and `drop()` methods
/// This is because `get_or_init` manages synchronisation and the case of an empty value
#[cfg(feature = "profiler")]
static mut INSTANCE: OnceCell<BoaProfiler> = OnceCell::new();
#[cfg(feature = "profiler")]
impl BoaProfiler {
pub fn start_event(&self, label: &str, category: &str) -> TimingGuard<'_, SerializationSink> {
let kind = self.profiler.alloc_string(category);
let id = EventId::from_label(self.profiler.alloc_string(label));
let thread_id = Self::thread_id_to_u32(current().id());
self.profiler
.start_recording_interval_event(kind, id, thread_id)
}
pub fn default() -> BoaProfiler {
let profiler = Profiler::new(Path::new("./my_trace")).unwrap();
BoaProfiler { profiler }
}
pub fn global() -> &'static BoaProfiler {
unsafe { INSTANCE.get_or_init(Self::default) }
}
pub fn drop(&self) {
// In order to drop the INSTANCE we need to get ownership of it, which isn't possible on a static unless you make it a mutable static
// mutating statics is unsafe, so we need to wrap it as so.
// This is actually safe though because init and drop are only called at the beginning and end of the application
unsafe {
INSTANCE
.take()
.expect("Could not take back profiler instance");
}
}
// Sadly we need to use the unsafe method until this is resolved:
// https://github.com/rust-lang/rust/issues/67939
// Once `as_64()` is in stable we can do this:
// https://github.com/rust-lang/rust/pull/68531/commits/ea42b1c5b85f649728e3a3b334489bac6dce890a
// Until then our options are: use rust-nightly or use unsafe {}
fn thread_id_to_u32(tid: ThreadId) -> u32 {
unsafe { std::mem::transmute::<ThreadId, u64>(tid) as u32 }
}
}
impl Debug for BoaProfiler {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt("no debug implemented", f)
}
}
#[cfg(not(feature = "profiler"))]
pub struct BoaProfiler;
#[allow(clippy::unused_unit)]
#[cfg(not(feature = "profiler"))]
impl BoaProfiler {
pub fn start_event(&self, _label: &str, _category: &str) -> () {
()
}
pub fn drop(&self) {
()
}
pub fn global() -> BoaProfiler {
BoaProfiler
}
}

3
boa/src/realm.rs

@ -16,6 +16,7 @@ use crate::{
lexical_environment::LexicalEnvironment,
object_environment_record::ObjectEnvironmentRecord,
},
BoaProfiler,
};
use gc::{Gc, GcCell};
use rustc_hash::{FxHashMap, FxHashSet};
@ -32,6 +33,7 @@ pub struct Realm {
impl Realm {
pub fn create() -> Self {
let _timer = BoaProfiler::global().start_event("Realm::create", "realm");
// Create brand new global object
// Global has no prototype to pass None to new_obj
let global = Value::new_object(None);
@ -53,6 +55,7 @@ impl Realm {
// Sets up the default global objects within Global
fn create_instrinsics(&self) {
let _timer = BoaProfiler::global().start_event("create_instrinsics", "realm");
let global = &self.global_obj;
// Create intrinsics, add global objects here
builtins::init(global);

10
boa/src/syntax/lexer/mod.rs

@ -7,9 +7,12 @@
mod tests;
use crate::syntax::ast::bigint::BigInt;
use crate::syntax::ast::{
token::{NumericLiteral, Token, TokenKind},
Position, Punctuator, Span,
use crate::{
syntax::ast::{
token::{NumericLiteral, Token, TokenKind},
Position, Punctuator, Span,
},
BoaProfiler,
};
use std::{
char::{decode_utf16, from_u32},
@ -486,6 +489,7 @@ impl<'a> Lexer<'a> {
/// }
/// ```
pub fn lex(&mut self) -> Result<(), LexerError> {
let _timer = BoaProfiler::global().start_event("lex", "lexing");
loop {
// Check if we've reached the end
if self.preview_next().is_none() {

24
boa/src/syntax/parser/expression/assignment/arrow_function.rs

@ -8,17 +8,20 @@
//! [spec]: https://tc39.es/ecma262/#sec-arrow-function-definitions
use super::AssignmentExpression;
use crate::syntax::{
ast::{
node::{ArrowFunctionDecl, FormalParameter, Node, StatementList},
Punctuator, TokenKind,
},
parser::{
error::{ErrorContext, ParseError, ParseResult},
function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
AllowAwait, AllowIn, AllowYield, Cursor, TokenParser,
use crate::{
syntax::{
ast::{
node::{ArrowFunctionDecl, FormalParameter, Node, StatementList},
Punctuator, TokenKind,
},
parser::{
error::{ErrorContext, ParseError, ParseResult},
function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
AllowAwait, AllowIn, AllowYield, Cursor, TokenParser,
},
},
BoaProfiler,
};
/// Arrow function parsing.
@ -60,6 +63,7 @@ impl TokenParser for ArrowFunction {
type Output = ArrowFunctionDecl;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("ArrowFunction", "Parsing");
let next_token = cursor.peek(0).ok_or(ParseError::AbruptEnd)?;
let params = if let TokenKind::Punctuator(Punctuator::OpenParen) = &next_token.kind {
// CoverParenthesizedExpressionAndArrowParameterList

14
boa/src/syntax/parser/expression/assignment/conditional.rs

@ -7,12 +7,15 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator
//! [spec]: https://tc39.es/ecma262/#sec-conditional-operator
use crate::syntax::{
ast::{Node, Punctuator, TokenKind},
parser::{
expression::{AssignmentExpression, LogicalORExpression},
AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser,
use crate::{
syntax::{
ast::{Node, Punctuator, TokenKind},
parser::{
expression::{AssignmentExpression, LogicalORExpression},
AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// Conditional expression parsing.
@ -54,6 +57,7 @@ impl TokenParser for ConditionalExpression {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("Conditional", "Parsing");
// TODO: coalesce expression
let lhs = LogicalORExpression::new(self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor)?;

22
boa/src/syntax/parser/expression/assignment/exponentiation.rs

@ -7,16 +7,19 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Exponentiation
//! [spec]: https://tc39.es/ecma262/#sec-exp-operator
use crate::syntax::{
ast::{
node::{BinOp, Node},
op::NumOp,
Keyword, Punctuator, TokenKind,
},
parser::{
expression::{unary::UnaryExpression, update::UpdateExpression},
AllowAwait, AllowYield, Cursor, ParseResult, TokenParser,
use crate::{
syntax::{
ast::{
node::{BinOp, Node},
op::NumOp,
Keyword, Punctuator, TokenKind,
},
parser::{
expression::{unary::UnaryExpression, update::UpdateExpression},
AllowAwait, AllowYield, Cursor, ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// Parses an exponentiation expression.
@ -71,6 +74,7 @@ impl TokenParser for ExponentiationExpression {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("ExponentiationExpression", "Parsing");
if Self::is_unary_expression(cursor) {
return UnaryExpression::new(self.allow_yield, self.allow_await).parse(cursor);
}

14
boa/src/syntax/parser/expression/assignment/mod.rs

@ -12,12 +12,15 @@ mod conditional;
mod exponentiation;
use self::{arrow_function::ArrowFunction, conditional::ConditionalExpression};
use crate::syntax::{
ast::{
node::{Assign, BinOp, Node},
Keyword, Punctuator, TokenKind,
use crate::{
syntax::{
ast::{
node::{Assign, BinOp, Node},
Keyword, Punctuator, TokenKind,
},
parser::{AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
},
parser::{AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
BoaProfiler,
};
pub(super) use exponentiation::ExponentiationExpression;
@ -70,6 +73,7 @@ impl TokenParser for AssignmentExpression {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("AssignmentExpression", "Parsing");
// Arrow function
let next_token = cursor.peek(0).ok_or(ParseError::AbruptEnd)?;
match next_token.kind {

13
boa/src/syntax/parser/expression/left_hand_side/arguments.rs

@ -7,11 +7,15 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Argument
//! [spec]: https://tc39.es/ecma262/#prod-Arguments
use crate::syntax::{
ast::{Node, Punctuator, TokenKind},
parser::{
expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, TokenParser,
use crate::{
syntax::{
ast::{Node, Punctuator, TokenKind},
parser::{
expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError,
TokenParser,
},
},
BoaProfiler,
};
/// Parses a list of arguments.
@ -46,6 +50,7 @@ impl TokenParser for Arguments {
type Output = Box<[Node]>;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("Arguments", "Parsing");
cursor.expect(Punctuator::OpenParen, "arguments")?;
let mut args = Vec::new();
loop {

20
boa/src/syntax/parser/expression/left_hand_side/call.rs

@ -8,15 +8,18 @@
//! [spec]: https://tc39.es/ecma262/#prod-CallExpression
use super::arguments::Arguments;
use crate::syntax::{
ast::{
node::{Call, Node},
Punctuator, TokenKind,
},
parser::{
expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult,
TokenParser,
use crate::{
syntax::{
ast::{
node::{Call, Node},
Punctuator, TokenKind,
},
parser::{
expression::Expression, AllowAwait, AllowYield, Cursor, ParseError, ParseResult,
TokenParser,
},
},
BoaProfiler,
};
/// Parses a call expression.
@ -51,6 +54,7 @@ impl TokenParser for CallExpression {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("CallExpression", "Parsing");
let mut lhs = match cursor.peek(0) {
Some(tk) if tk.kind == TokenKind::Punctuator(Punctuator::OpenParen) => {
let args = Arguments::new(self.allow_yield, self.allow_await).parse(cursor)?;

20
boa/src/syntax/parser/expression/left_hand_side/member.rs

@ -6,15 +6,18 @@
//! [spec]: https://tc39.es/ecma262/#prod-MemberExpression
use super::arguments::Arguments;
use crate::syntax::{
ast::{
node::{Call, New, Node},
Keyword, Punctuator, TokenKind,
},
parser::{
expression::{primary::PrimaryExpression, Expression},
AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
use crate::{
syntax::{
ast::{
node::{Call, New, Node},
Keyword, Punctuator, TokenKind,
},
parser::{
expression::{primary::PrimaryExpression, Expression},
AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// Parses a member expression.
@ -47,6 +50,7 @@ impl TokenParser for MemberExpression {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("MemberExpression", "Parsing");
let mut lhs = if cursor.peek(0).ok_or(ParseError::AbruptEnd)?.kind
== TokenKind::Keyword(Keyword::New)
{

10
boa/src/syntax/parser/expression/left_hand_side/mod.rs

@ -12,9 +12,12 @@ mod call;
mod member;
use self::{call::CallExpression, member::MemberExpression};
use crate::syntax::{
ast::{Node, Punctuator, TokenKind},
parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser},
use crate::{
syntax::{
ast::{Node, Punctuator, TokenKind},
parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser},
},
BoaProfiler,
};
/// Parses a left hand side expression.
@ -49,6 +52,7 @@ impl TokenParser for LeftHandSideExpression {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("LeftHandSIdeExpression", "Parsing");
// TODO: Implement NewExpression: new MemberExpression
let lhs = MemberExpression::new(self.allow_yield, self.allow_await).parse(cursor)?;
match cursor.peek(0) {

10
boa/src/syntax/parser/expression/mod.rs

@ -18,9 +18,12 @@ mod update;
use self::assignment::ExponentiationExpression;
pub(super) use self::{assignment::AssignmentExpression, primary::Initializer};
use super::{AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser};
use crate::syntax::ast::{
node::{BinOp, Node},
Keyword, Punctuator, TokenKind,
use crate::{
profiler::BoaProfiler,
syntax::ast::{
node::{BinOp, Node},
Keyword, Punctuator, TokenKind,
},
};
// For use in the expression! macro to allow for both Punctuator and Keyword parameters.
@ -51,6 +54,7 @@ macro_rules! expression { ($name:ident, $lower:ident, [$( $op:path ),*], [$( $lo
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("Expression", "Parsing");
let mut lhs = $lower::new($( self.$low_param ),*).parse(cursor)?;
while let Some(tok) = cursor.peek(0) {
match tok.kind {

19
boa/src/syntax/parser/expression/primary/array_initializer/mod.rs

@ -10,14 +10,18 @@
#[cfg(test)]
mod tests;
use crate::syntax::{
ast::{
node::{ArrayDecl, Node},
Const, Punctuator,
},
parser::{
expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError, TokenParser,
use crate::{
syntax::{
ast::{
node::{ArrayDecl, Node},
Const, Punctuator,
},
parser::{
expression::AssignmentExpression, AllowAwait, AllowYield, Cursor, ParseError,
TokenParser,
},
},
BoaProfiler,
};
/// Parses an array literal.
@ -52,6 +56,7 @@ impl TokenParser for ArrayLiteral {
type Output = ArrayDecl;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("ArrayLiteral", "Parsing");
let mut elements = Vec::new();
loop {

16
boa/src/syntax/parser/expression/primary/function_expression.rs

@ -7,13 +7,16 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
//! [spec]: https://tc39.es/ecma262/#prod-FunctionExpression
use crate::syntax::{
ast::{node::FunctionExpr, Punctuator},
parser::{
function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
Cursor, ParseError, TokenParser,
use crate::{
syntax::{
ast::{node::FunctionExpr, Punctuator},
parser::{
function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
Cursor, ParseError, TokenParser,
},
},
BoaProfiler,
};
/// Function expression parsing.
@ -31,6 +34,7 @@ impl TokenParser for FunctionExpression {
type Output = FunctionExpr;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("FunctionExpression", "Parsing");
let name = BindingIdentifier::new(false, false).try_parse(cursor);
cursor.expect(Punctuator::OpenParen, "function expression")?;

24
boa/src/syntax/parser/expression/primary/object_initializer/mod.rs

@ -10,17 +10,20 @@
#[cfg(test)]
mod tests;
use crate::syntax::{
ast::{
node::{self, FunctionExpr, MethodDefinitionKind, Node},
token::{Token, TokenKind},
Punctuator,
},
parser::{
expression::AssignmentExpression,
function::{FormalParameters, FunctionBody},
AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
use crate::{
syntax::{
ast::{
node::{self, FunctionExpr, MethodDefinitionKind, Node},
token::{Token, TokenKind},
Punctuator,
},
parser::{
expression::AssignmentExpression,
function::{FormalParameters, FunctionBody},
AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// Parses an object literal.
@ -55,6 +58,7 @@ impl TokenParser for ObjectLiteral {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("ObjectLiteral", "Parsing");
let mut elements = Vec::new();
loop {

10
boa/src/syntax/parser/statement/block/mod.rs

@ -11,9 +11,12 @@
mod tests;
use super::StatementList;
use crate::syntax::{
ast::{node, Punctuator, TokenKind},
parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser},
use crate::{
profiler::BoaProfiler,
syntax::{
ast::{node, Punctuator, TokenKind},
parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser},
},
};
/// A `BlockStatement` is equivalent to a `Block`.
@ -59,6 +62,7 @@ impl TokenParser for Block {
type Output = node::Block;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("Block", "Parsing");
cursor.expect(Punctuator::OpenBlock, "block")?;
if let Some(tk) = cursor.peek(0) {
if tk.kind == TokenKind::Punctuator(Punctuator::CloseBlock) {

10
boa/src/syntax/parser/statement/break_stm/mod.rs

@ -11,9 +11,12 @@
mod tests;
use super::LabelIdentifier;
use crate::syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser},
use crate::{
syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser},
},
BoaProfiler,
};
/// Break statement parsing
@ -48,6 +51,7 @@ impl TokenParser for BreakStatement {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("BreakStatement", "Parsing");
cursor.expect(Keyword::Break, "break statement")?;
let label = if let (true, tok) = cursor.peek_semicolon(false) {

10
boa/src/syntax/parser/statement/continue_stm/mod.rs

@ -11,9 +11,12 @@
mod tests;
use super::LabelIdentifier;
use crate::syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser},
use crate::{
syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{AllowAwait, AllowYield, Cursor, ParseResult, TokenParser},
},
BoaProfiler,
};
/// For statement parsing
@ -48,6 +51,7 @@ impl TokenParser for ContinueStatement {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("ContinueStatement", "Parsing");
cursor.expect(Keyword::Continue, "continue statement")?;
let label = if let (true, tok) = cursor.peek_semicolon(false) {

14
boa/src/syntax/parser/statement/declaration/hoistable.rs

@ -5,12 +5,15 @@
//!
//! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration
use crate::syntax::{
ast::{node::FunctionDecl, Keyword, Node, Punctuator},
parser::{
function::FormalParameters, function::FunctionBody, statement::BindingIdentifier,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
use crate::{
syntax::{
ast::{node::FunctionDecl, Keyword, Node, Punctuator},
parser::{
function::FormalParameters, function::FunctionBody, statement::BindingIdentifier,
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// Hoistable declaration parsing.
@ -46,6 +49,7 @@ impl TokenParser for HoistableDeclaration {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("HoistableDeclaration", "Parsing");
// TODO: check for generators and async functions + generators
FunctionDeclaration::new(self.allow_yield, self.allow_await, self.is_default)
.parse(cursor)

20
boa/src/syntax/parser/statement/declaration/lexical.rs

@ -7,15 +7,18 @@
//!
//! [spec]: https://tc39.es/ecma262/#sec-let-and-const-declarations
use crate::syntax::{
ast::{
node::{ConstDecl, ConstDeclList, LetDecl, LetDeclList, Node},
Keyword, Punctuator, TokenKind,
},
parser::{
expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield,
Cursor, ParseError, ParseResult, TokenParser,
use crate::{
syntax::{
ast::{
node::{ConstDecl, ConstDeclList, LetDecl, LetDeclList, Node},
Keyword, Punctuator, TokenKind,
},
parser::{
expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield,
Cursor, ParseError, ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// Parses a lexical declaration.
@ -51,6 +54,7 @@ impl TokenParser for LexicalDeclaration {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("LexicalDeclaration", "Parsing");
let tok = cursor.next().ok_or(ParseError::AbruptEnd)?;
match tok.kind {

10
boa/src/syntax/parser/statement/declaration/mod.rs

@ -13,9 +13,12 @@ mod lexical;
mod tests;
use self::{hoistable::HoistableDeclaration, lexical::LexicalDeclaration};
use crate::syntax::{
ast::{Keyword, Node, TokenKind},
parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
use crate::{
syntax::{
ast::{Keyword, Node, TokenKind},
parser::{AllowAwait, AllowYield, Cursor, ParseError, ParseResult, TokenParser},
},
BoaProfiler,
};
/// Parses a declaration.
@ -47,6 +50,7 @@ impl TokenParser for Declaration {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("Declaration", "Parsing");
let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?;
match tok.kind {

14
boa/src/syntax/parser/statement/if_stm/mod.rs

@ -2,12 +2,15 @@
mod tests;
use super::Statement;
use crate::syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{
expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult,
TokenParser,
use crate::{
syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{
expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseResult,
TokenParser,
},
},
BoaProfiler,
};
/// If statement parsing.
@ -47,6 +50,7 @@ impl TokenParser for IfStatement {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("IfStatement", "Parsing");
cursor.expect(Keyword::If, "if statement")?;
cursor.expect(Punctuator::OpenParen, "if statement")?;

14
boa/src/syntax/parser/statement/iteration/do_while_statement.rs

@ -7,12 +7,15 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
//! [spec]: https://tc39.es/ecma262/#sec-do-while-statement
use crate::syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{
expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor,
ParseError, ParseResult, TokenParser,
use crate::{
syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{
expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield,
Cursor, ParseError, ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// Do...while statement parsing
@ -54,6 +57,7 @@ impl TokenParser for DoWhileStatement {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("DoWhileStatement", "Parsing");
cursor.expect(Keyword::Do, "do while statement")?;
let body =

24
boa/src/syntax/parser/statement/iteration/for_statement.rs

@ -7,17 +7,20 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
//! [spec]: https://tc39.es/ecma262/#sec-for-statement
use crate::syntax::{
ast::{
node::{ForLoop, Node},
Const, Keyword, Punctuator, TokenKind,
},
parser::{
expression::Expression,
statement::declaration::Declaration,
statement::{variable::VariableDeclarationList, Statement},
AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser,
use crate::{
syntax::{
ast::{
node::{ForLoop, Node},
Const, Keyword, Punctuator, TokenKind,
},
parser::{
expression::Expression,
statement::declaration::Declaration,
statement::{variable::VariableDeclarationList, Statement},
AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser,
},
},
BoaProfiler,
};
/// For statement parsing
@ -59,6 +62,7 @@ impl TokenParser for ForStatement {
type Output = ForLoop;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<ForLoop, ParseError> {
let _timer = BoaProfiler::global().start_event("ForStatement", "Parsing");
cursor.expect(Keyword::For, "for statement")?;
cursor.expect(Punctuator::OpenParen, "for statement")?;

14
boa/src/syntax/parser/statement/iteration/while_statement.rs

@ -1,9 +1,12 @@
use crate::syntax::{
ast::{Keyword, Node, Punctuator},
parser::{
expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield, Cursor,
ParseResult, TokenParser,
use crate::{
syntax::{
ast::{Keyword, Node, Punctuator},
parser::{
expression::Expression, statement::Statement, AllowAwait, AllowReturn, AllowYield,
Cursor, ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// While statement parsing
@ -45,6 +48,7 @@ impl TokenParser for WhileStatement {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("WhileStatement", "Parsing");
cursor.expect(Keyword::While, "while statement")?;
cursor.expect(Punctuator::OpenParen, "while statement")?;

10
boa/src/syntax/parser/statement/mod.rs

@ -36,7 +36,10 @@ use super::{
expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, ParseResult,
TokenParser,
};
use crate::syntax::ast::{node, Keyword, Node, Punctuator, TokenKind};
use crate::{
syntax::ast::{node, Keyword, Node, Punctuator, TokenKind},
BoaProfiler,
};
/// Statement parsing.
///
@ -90,6 +93,7 @@ impl TokenParser for Statement {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("Statement", "Parsing");
// TODO: add BreakableStatement and divide Whiles, fors and so on to another place.
let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?;
@ -197,6 +201,7 @@ impl TokenParser for StatementList {
type Output = node::StatementList;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("StatementList", "Parsing");
let mut items = Vec::new();
loop {
@ -270,6 +275,7 @@ impl TokenParser for StatementListItem {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("StatementListItem", "Parsing");
let tok = cursor.peek(0).ok_or(ParseError::AbruptEnd)?;
match tok.kind {
@ -315,6 +321,7 @@ impl TokenParser for ExpressionStatement {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("ExpressionStatement", "Parsing");
// TODO: lookahead
let expr = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?;
@ -364,6 +371,7 @@ impl TokenParser for BindingIdentifier {
type Output = Box<str>;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("BindingIdentifier", "Parsing");
// TODO: strict mode.
let next_token = cursor.next().ok_or(ParseError::AbruptEnd)?;

12
boa/src/syntax/parser/statement/return_stm/mod.rs

@ -1,9 +1,14 @@
#[cfg(test)]
mod tests;
use crate::syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser},
use crate::{
syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{
expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// Return statement parsing
@ -38,6 +43,7 @@ impl TokenParser for ReturnStatement {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("ReturnStatement", "Parsing");
cursor.expect(Keyword::Return, "return statement")?;
if let (true, tok) = cursor.peek_semicolon(false) {

14
boa/src/syntax/parser/statement/switch/mod.rs

@ -1,12 +1,15 @@
#[cfg(test)]
mod tests;
use crate::syntax::{
ast::{Keyword, Node, Punctuator},
parser::{
expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError,
ParseResult, TokenParser,
use crate::{
syntax::{
ast::{Keyword, Node, Punctuator},
parser::{
expression::Expression, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError,
ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// Switch statement parsing.
@ -44,6 +47,7 @@ impl TokenParser for SwitchStatement {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("SwitchStatement", "Parsing");
cursor.expect(Keyword::Switch, "switch statement")?;
cursor.expect(Punctuator::OpenParen, "switch statement")?;

12
boa/src/syntax/parser/statement/throw/mod.rs

@ -1,9 +1,14 @@
#[cfg(test)]
mod tests;
use crate::syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser},
use crate::{
syntax::{
ast::{Keyword, Node, Punctuator, TokenKind},
parser::{
expression::Expression, AllowAwait, AllowYield, Cursor, ParseResult, TokenParser,
},
},
BoaProfiler,
};
/// For statement parsing
@ -38,6 +43,7 @@ impl TokenParser for ThrowStatement {
type Output = Node;
fn parse(self, cursor: &mut Cursor<'_>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("ThrowStatement", "Parsing");
cursor.expect(Keyword::Throw, "throw statement")?;
cursor.peek_expect_no_lineterminator(0)?;

20
boa/src/syntax/parser/statement/try_stm/catch.rs

@ -1,12 +1,15 @@
use crate::syntax::{
ast::{
node::{self, Identifier},
Keyword, Punctuator,
},
parser::{
statement::{block::Block, BindingIdentifier},
AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser,
use crate::{
syntax::{
ast::{
node::{self, Identifier},
Keyword, Punctuator,
},
parser::{
statement::{block::Block, BindingIdentifier},
AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser,
},
},
BoaProfiler,
};
/// Catch parsing
@ -44,6 +47,7 @@ impl TokenParser for Catch {
type Output = node::Catch;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("Catch", "Parsing");
cursor.expect(Keyword::Catch, "try statement")?;
let catch_param = if cursor.next_if(Punctuator::OpenParen).is_some() {
let catch_param =

14
boa/src/syntax/parser/statement/try_stm/finally.rs

@ -1,9 +1,12 @@
use crate::syntax::{
ast::{node, Keyword},
parser::{
statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError,
TokenParser,
use crate::{
syntax::{
ast::{node, Keyword},
parser::{
statement::block::Block, AllowAwait, AllowReturn, AllowYield, Cursor, ParseError,
TokenParser,
},
},
BoaProfiler,
};
/// Finally parsing
@ -41,6 +44,7 @@ impl TokenParser for Finally {
type Output = node::Finally;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("Finally", "Parsing");
cursor.expect(Keyword::Finally, "try statement")?;
Ok(
Block::new(self.allow_yield, self.allow_await, self.allow_return)

10
boa/src/syntax/parser/statement/try_stm/mod.rs

@ -7,9 +7,12 @@ mod tests;
use self::catch::Catch;
use self::finally::Finally;
use super::block::Block;
use crate::syntax::{
ast::{node::Try, Keyword, TokenKind},
parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser},
use crate::{
syntax::{
ast::{node::Try, Keyword, TokenKind},
parser::{AllowAwait, AllowReturn, AllowYield, Cursor, ParseError, TokenParser},
},
BoaProfiler,
};
/// Try...catch statement parsing
@ -47,6 +50,7 @@ impl TokenParser for TryStatement {
type Output = Try;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Try, ParseError> {
let _timer = BoaProfiler::global().start_event("TryStatement", "Parsing");
// TRY
cursor.expect(Keyword::Try, "try statement")?;

20
boa/src/syntax/parser/statement/variable.rs

@ -1,13 +1,16 @@
// use super::lexical_declaration_continuation;
use crate::syntax::{
ast::{
node::{VarDecl, VarDeclList},
Keyword, Punctuator, TokenKind,
},
parser::{
expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield,
Cursor, ParseError, TokenParser,
use crate::{
syntax::{
ast::{
node::{VarDecl, VarDeclList},
Keyword, Punctuator, TokenKind,
},
parser::{
expression::Initializer, statement::BindingIdentifier, AllowAwait, AllowIn, AllowYield,
Cursor, ParseError, TokenParser,
},
},
BoaProfiler,
};
/// Variable statement parsing.
@ -44,6 +47,7 @@ impl TokenParser for VariableStatement {
type Output = VarDeclList;
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("VariableStatement", "Parsing");
cursor.expect(Keyword::Var, "variable statement")?;
let decl_list =

BIN
docs/img/profiler.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

30
docs/profiling.md

@ -0,0 +1,30 @@
# Profiling
![Example](img/profiler.png)
It's possible to get a full profile of Boa in action.
Sometimes this is needed to figure out where it is spending most of it's time.
We use a crate called [measureme](https://github.com/rust-lang/measureme), which helps us keep track of timing functions during runtime.
When the "profiler" flag is enabled, you compile with the profiler and it is called throughout the interpreter.
when the feature flag is not enabled, you have an empty dummy implementation that is just no ops. rustc should completely optimize that away. So there should be no performance downgrade from these changes
## Prerequesites
- [Crox](https://github.com/rust-lang/measureme/blob/master/crox/Readme.md)
## How To Use
You can run boa using the "profiler" feature flag to enable profiling. Seeing as you'll most likely be using boa_cli you can pass this through, like so:
`cargo run --features Boa/profiler ../tests/js/test.js`
Once finished you should see some trace files left in the directory (boa_cli in this case).
In the same directory as the `.events, string_data, string_index` files run `crox my_trace` or whatever the name of the files are. This will generate a chrome_profiler.json file, you can load this into Chrome Dev tools.
## More Info
- https://blog.rust-lang.org/inside-rust/2020/02/25/intro-rustc-self-profile.html
- https://github.com/rust-lang/measureme
- https://github.com/rust-lang/measureme/blob/master/crox/Readme.md
Loading…
Cancel
Save