Skip to content

Commit 7e764b5

Browse files
committed
rebased Profiler
1 parent 4beadfc commit 7e764b5

8 files changed

Lines changed: 209 additions & 15 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,9 @@ yarn-error.log
1717

1818
# tests/js/test.js is used for testing changes locally
1919
tests/js/test.js
20+
21+
# Profiling
22+
*.string_data
23+
*.string_index
24+
*.events
25+
chrome_profiler.json

Cargo.lock

Lines changed: 89 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

boa/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ num-bigint = { version = "0.2.6", features = ["serde"] }
2222
# Optional Dependencies
2323
serde = { version = "1.0.110", features = ["derive"], optional = true }
2424
bitflags = "1.2.1"
25+
measureme = { version = "0.7.1" }
26+
once_cell = "1.4.0"
2527

2628
[dev-dependencies]
2729
criterion = "0.3.2"

boa/src/lib.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@
3636
pub mod builtins;
3737
pub mod environment;
3838
pub mod exec;
39+
pub mod profiler;
3940
pub mod realm;
4041
pub mod syntax;
4142

4243
use crate::{builtins::value::ResultValue, syntax::ast::node::StatementList};
4344
pub use crate::{
4445
exec::{Executable, Interpreter},
46+
profiler::BoaProfiler,
4547
realm::Realm,
4648
syntax::{lexer::Lexer, parser::Parser},
4749
};
@@ -72,14 +74,22 @@ pub fn forward(engine: &mut Interpreter, src: &str) -> String {
7274
/// Similar to `forward`, except the current value is returned instad of the string
7375
/// If the interpreter fails parsing an error value is returned instead (error object)
7476
pub fn forward_val(engine: &mut Interpreter, src: &str) -> ResultValue {
77+
BoaProfiler::init();
78+
let main_timer = BoaProfiler::global().start_event("Main", "Main");
7579
// Setup executor
76-
match parser_expr(src) {
80+
let result = match parser_expr(src) {
7781
Ok(expr) => expr.run(engine),
7882
Err(e) => {
7983
eprintln!("{}", e);
8084
std::process::exit(1);
8185
}
82-
}
86+
};
87+
88+
// The main_timer needs to be dropped before the BoaProfiler is.
89+
drop(main_timer);
90+
BoaProfiler::global().drop();
91+
92+
result
8393
}
8494

8595
/// Create a clean Interpreter and execute the code

boa/src/profiler.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#![allow(missing_copy_implementations, missing_debug_implementations)]
2+
3+
use measureme::{EventId, Profiler, TimingGuard};
4+
use once_cell::sync::OnceCell;
5+
use std::fmt::{self, Debug};
6+
use std::{
7+
path::Path,
8+
thread::{current, ThreadId},
9+
};
10+
11+
/// MmapSerializatioSink is faster on macOS and Linux
12+
/// but FileSerializationSink is faster on Windows
13+
#[cfg(not(windows))]
14+
type SerializationSink = measureme::MmapSerializationSink;
15+
#[cfg(windows)]
16+
type SerializationSink = measureme::FileSerializationSink;
17+
18+
pub struct BoaProfiler {
19+
profiler: Profiler<SerializationSink>,
20+
}
21+
22+
pub static mut INSTANCE: OnceCell<BoaProfiler> = OnceCell::new();
23+
24+
impl BoaProfiler {
25+
pub fn start_event(&self, label: &str, category: &str) -> TimingGuard<'_, SerializationSink> {
26+
let kind = self.profiler.alloc_string(category);
27+
let id = EventId::from_label(self.profiler.alloc_string(label));
28+
let thread_id = Self::thread_id_to_u32(current().id());
29+
self.profiler
30+
.start_recording_interval_event(kind, id, thread_id)
31+
}
32+
33+
pub fn default() -> BoaProfiler {
34+
let profiler = Profiler::new(Path::new("./my_trace")).unwrap();
35+
BoaProfiler { profiler }
36+
}
37+
38+
// init creates a global instance of BoaProfiler which can be used across the whole application
39+
pub fn init() {
40+
let profiler = Self::default();
41+
unsafe {
42+
INSTANCE
43+
.set(profiler)
44+
.expect("Failed to set BoaProfiler globally");
45+
}
46+
}
47+
48+
pub fn global() -> &'static BoaProfiler {
49+
unsafe { INSTANCE.get().expect("Profiler is not initialized") }
50+
}
51+
52+
pub fn drop(&self) {
53+
// 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
54+
// mutating statics is unsafe, so we need to wrap it as so.
55+
// This is actually safe though because init and drop are only called at the beginning and end of the application
56+
unsafe {
57+
INSTANCE
58+
.take()
59+
.expect("Could not take back profiler instance");
60+
}
61+
}
62+
63+
// Sadly we need to use the unsafe method until this is resolved:
64+
// https://github.com/rust-lang/rust/issues/67939
65+
// Once `as_64()` is in stable we can do this:
66+
// https://github.com/rust-lang/rust/pull/68531/commits/ea42b1c5b85f649728e3a3b334489bac6dce890a
67+
// Until then our options are: use rust-nightly or use unsafe {}
68+
fn thread_id_to_u32(tid: ThreadId) -> u32 {
69+
unsafe { std::mem::transmute::<ThreadId, u64>(tid) as u32 }
70+
}
71+
}
72+
73+
impl Debug for BoaProfiler {
74+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75+
Debug::fmt("no debug implemented", f)
76+
}
77+
}

boa/src/realm.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::{
1616
lexical_environment::LexicalEnvironment,
1717
object_environment_record::ObjectEnvironmentRecord,
1818
},
19+
BoaProfiler,
1920
};
2021
use gc::{Gc, GcCell};
2122
use rustc_hash::{FxHashMap, FxHashSet};
@@ -32,6 +33,7 @@ pub struct Realm {
3233

3334
impl Realm {
3435
pub fn create() -> Self {
36+
let _timer = BoaProfiler::global().start_event("Realm::create", "Realm");
3537
// Create brand new global object
3638
// Global has no prototype to pass None to new_obj
3739
let global = Value::new_object(None);

boa/src/syntax/lexer/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
mod tests;
88

99
use crate::syntax::ast::bigint::BigInt;
10-
use crate::syntax::ast::{
11-
token::{NumericLiteral, Token, TokenKind},
12-
Position, Punctuator, Span,
10+
use crate::{
11+
syntax::ast::{
12+
token::{NumericLiteral, Token, TokenKind},
13+
Position, Punctuator, Span,
14+
},
15+
BoaProfiler,
1316
};
1417
use std::{
1518
char::{decode_utf16, from_u32},
@@ -486,6 +489,7 @@ impl<'a> Lexer<'a> {
486489
/// }
487490
/// ```
488491
pub fn lex(&mut self) -> Result<(), LexerError> {
492+
let _timer = BoaProfiler::global().start_event("lex", "lexing");
489493
loop {
490494
// Check if we've reached the end
491495
if self.preview_next().is_none() {

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@
88
//! [spec]: https://tc39.es/ecma262/#sec-arrow-function-definitions
99
1010
use super::AssignmentExpression;
11-
use crate::syntax::{
12-
ast::{
13-
node::{ArrowFunctionDecl, FormalParameter, Node, StatementList},
14-
Punctuator, TokenKind,
15-
},
16-
parser::{
17-
error::{ErrorContext, ParseError, ParseResult},
18-
function::{FormalParameters, FunctionBody},
19-
statement::BindingIdentifier,
20-
AllowAwait, AllowIn, AllowYield, Cursor, TokenParser,
11+
use crate::{
12+
syntax::{
13+
ast::{
14+
node::{ArrowFunctionDecl, FormalParameter, Node, StatementList},
15+
Punctuator, TokenKind,
16+
},
17+
parser::{
18+
error::{ErrorContext, ParseError, ParseResult},
19+
function::{FormalParameters, FunctionBody},
20+
statement::BindingIdentifier,
21+
AllowAwait, AllowIn, AllowYield, Cursor, TokenParser,
22+
},
2123
},
24+
BoaProfiler,
2225
};
2326

2427
/// Arrow function parsing.
@@ -60,6 +63,7 @@ impl TokenParser for ArrowFunction {
6063
type Output = ArrowFunctionDecl;
6164

6265
fn parse(self, cursor: &mut Cursor<'_>) -> Result<Self::Output, ParseError> {
66+
let _timer = BoaProfiler::global().start_event("ArrowFunction", "Parsing");
6367
let next_token = cursor.peek(0).ok_or(ParseError::AbruptEnd)?;
6468
let params = if let TokenKind::Punctuator(Punctuator::OpenParen) = &next_token.kind {
6569
// CoverParenthesizedExpressionAndArrowParameterList

0 commit comments

Comments
 (0)