profile: Add instrumentation to startup related functions (#44456)

Follow-up to #44443.
This helps investigating the cold-start timeline, and could be used
by tooling to A/B compare branches affecting the cold-start time.

Additionally also change the `handle_request::select` span, so that we
can see the blocked time (which was probably what was intended), since
the actual time spent on recv after select is insignificant.

Testing: Tracing output is not covered by automatic tests.

---------

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
This commit is contained in:
Jonathan Schwender
2026-04-23 13:47:27 +02:00
committed by GitHub
parent ab5deb4030
commit 0ea42bc774
13 changed files with 25 additions and 3 deletions

1
Cargo.lock generated
View File

@@ -8687,6 +8687,7 @@ dependencies = [
"servo-script-traits", "servo-script-traits",
"servo-storage-traits", "servo-storage-traits",
"servo-timers", "servo-timers",
"servo-tracing",
"servo-url", "servo-url",
"servo-wakelock", "servo-wakelock",
"servo-webgpu-traits", "servo-webgpu-traits",

View File

@@ -1272,10 +1272,12 @@ where
self.process_manager.register(&mut sel); self.process_manager.register(&mut sel);
let request = { let request = {
let oper = sel.select(); let oper = {
let _span = profile_traits::trace_span!("handle_request::select").entered();
sel.select()
};
let index = oper.index(); let index = oper.index();
let _span = profile_traits::trace_span!("handle_request::select").entered();
match index { match index {
0 => oper 0 => oper
.recv(&self.namespace_receiver) .recv(&self.namespace_receiver)
@@ -1349,6 +1351,7 @@ where
} }
} }
#[servo_tracing::instrument(skip_all)]
fn handle_request_from_swmanager(&mut self, message: SWManagerMsg) { fn handle_request_from_swmanager(&mut self, message: SWManagerMsg) {
match message { match message {
SWManagerMsg::PostMessageToClient => { SWManagerMsg::PostMessageToClient => {

View File

@@ -65,6 +65,7 @@ impl ProcessManager {
receiver receiver
} }
#[servo_tracing::instrument(skip_all)]
pub fn remove(&mut self, index: usize) { pub fn remove(&mut self, index: usize) {
let (mut process, _) = self.processes.swap_remove(index); let (mut process, _) = self.processes.swap_remove(index);
debug!("Removing process pid={}", process.pid()); debug!("Removing process pid={}", process.pid());

View File

@@ -139,6 +139,7 @@ servo-config = { workspace = true }
servo-constellation-traits = { workspace = true } servo-constellation-traits = { workspace = true }
servo-geometry = { workspace = true } servo-geometry = { workspace = true }
servo-media = { workspace = true } servo-media = { workspace = true }
servo-tracing = { workspace = true }
servo-url = { workspace = true } servo-url = { workspace = true }
servo-wakelock = { workspace = true } servo-wakelock = { workspace = true }
servo_arc = { workspace = true } servo_arc = { workspace = true }

View File

@@ -160,6 +160,7 @@ fn jit_forbidden() -> bool {
} }
#[expect(unsafe_code)] #[expect(unsafe_code)]
#[servo_tracing::instrument(name = "script::init")]
pub fn init() -> JSEngineSetup { pub fn init() -> JSEngineSetup {
if pref!(js_disable_jit) || jit_forbidden() { if pref!(js_disable_jit) || jit_forbidden() {
let reason = if pref!(js_disable_jit) { let reason = if pref!(js_disable_jit) {

View File

@@ -470,6 +470,10 @@ impl ScriptThreadFactory for ScriptThread {
.name(format!("Script#{script_thread_id}")) .name(format!("Script#{script_thread_id}"))
.stack_size(8 * 1024 * 1024) // 8 MiB stack to be consistent with other browsers. .stack_size(8 * 1024 * 1024) // 8 MiB stack to be consistent with other browsers.
.spawn(move || { .spawn(move || {
profile_traits::debug_event!(
"ScriptThread::spawned",
script_thread_id = script_thread_id.to_string()
);
thread_state::initialize(ThreadState::SCRIPT); thread_state::initialize(ThreadState::SCRIPT);
PipelineNamespace::install(state.pipeline_namespace_id); PipelineNamespace::install(state.pipeline_namespace_id);
ScriptEventLoopId::install(state.id); ScriptEventLoopId::install(state.id);
@@ -863,6 +867,7 @@ impl ScriptThread {
} }
/// Creates a new script thread. /// Creates a new script thread.
#[servo_tracing::instrument(name = "ScripThread::new", level = "debug", skip_all)]
pub(crate) fn new( pub(crate) fn new(
state: InitialScriptState, state: InitialScriptState,
layout_factory: Arc<dyn LayoutFactory>, layout_factory: Arc<dyn LayoutFactory>,
@@ -3811,6 +3816,7 @@ impl ScriptThread {
/// Instructs the constellation to fetch the document that will be loaded. Stores the InProgressLoad /// Instructs the constellation to fetch the document that will be loaded. Stores the InProgressLoad
/// argument until a notification is received that the fetch is complete. /// argument until a notification is received that the fetch is complete.
#[servo_tracing::instrument(skip_all)]
fn pre_page_load(&self, cx: &mut js::context::JSContext, mut incomplete: InProgressLoad) { fn pre_page_load(&self, cx: &mut js::context::JSContext, mut incomplete: InProgressLoad) {
let url_str = incomplete.load_data.url.as_str(); let url_str = incomplete.load_data.url.as_str();
if url_str == "about:blank" || incomplete.load_data.js_eval_result.is_some() { if url_str == "about:blank" || incomplete.load_data.js_eval_result.is_some() {

View File

@@ -63,6 +63,8 @@ pub use servo_config::{opts, pref, prefs};
pub use servo_geometry::{ pub use servo_geometry::{
DeviceIndependentIntRect, DeviceIndependentPixel, convert_rect_to_css_pixel, DeviceIndependentIntRect, DeviceIndependentPixel, convert_rect_to_css_pixel,
}; };
#[doc(hidden)]
pub use servo_tracing;
pub use servo_url::ServoUrl; pub use servo_url::ServoUrl;
pub use style::Zero; pub use style::Zero;
pub use style_traits::CSSPixel; pub use style_traits::CSSPixel;

View File

@@ -192,6 +192,7 @@ impl ServoInner {
.and_then(WebView::from_weak_handle) .and_then(WebView::from_weak_handle)
} }
#[servo_tracing::instrument(level = "debug", skip_all)]
fn spin_event_loop(&self) -> bool { fn spin_event_loop(&self) -> bool {
if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown { if self.shutdown_state.get() == ShutdownState::FinishedShuttingDown {
return false; return false;
@@ -263,6 +264,7 @@ impl ServoInner {
true true
} }
#[servo_tracing::instrument(level = "debug", skip_all)]
fn receive_one_message(&self) -> Option<Message> { fn receive_one_message(&self) -> Option<Message> {
let mut select = crossbeam_channel::Select::new(); let mut select = crossbeam_channel::Select::new();
let embedder_receiver_index = select.recv(&self.embedder_receiver); let embedder_receiver_index = select.recv(&self.embedder_receiver);
@@ -832,7 +834,7 @@ impl Drop for ServoInner {
pub struct Servo(Rc<ServoInner>); pub struct Servo(Rc<ServoInner>);
impl Servo { impl Servo {
#[servo_tracing::instrument(skip(builder))] #[servo_tracing::instrument(name = "Servo::new", skip(builder))]
fn new(builder: ServoBuilder) -> Self { fn new(builder: ServoBuilder) -> Self {
// Global configuration options, parsed from the command line. // Global configuration options, parsed from the command line.
let opts = builder.opts.map(|opts| *opts); let opts = builder.opts.map(|opts| *opts);

View File

@@ -129,6 +129,7 @@ impl App {
self.state = AppState::Running(running_state); self.state = AppState::Running(running_state);
} }
#[servo::servo_tracing::instrument(level = "debug", skip_all)]
fn create_platform_window( fn create_platform_window(
&self, &self,
url: Url, url: Url,

View File

@@ -105,6 +105,7 @@ pub struct HeadedWindow {
} }
impl HeadedWindow { impl HeadedWindow {
#[servo::servo_tracing::instrument(level = "debug", name = "HeadedWindow::new", skip_all)]
pub(crate) fn new( pub(crate) fn new(
servoshell_preferences: &ServoShellPreferences, servoshell_preferences: &ServoShellPreferences,
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,

View File

@@ -35,6 +35,7 @@ pub struct HeadlessWindow {
} }
impl HeadlessWindow { impl HeadlessWindow {
#[servo::servo_tracing::instrument(level = "debug", name = "HeadlessWindow::new", skip_all)]
pub fn new(servoshell_preferences: &ServoShellPreferences) -> Rc<Self> { pub fn new(servoshell_preferences: &ServoShellPreferences) -> Rc<Self> {
let size = servoshell_preferences.initial_window_size; let size = servoshell_preferences.initial_window_size;

View File

@@ -277,6 +277,7 @@ pub struct App {
#[expect(unused)] #[expect(unused)]
impl App { impl App {
#[servo::servo_tracing::instrument(skip_all, name = "App::new", level = "info")]
pub(super) fn new(init: AppInitOptions) -> Rc<Self> { pub(super) fn new(init: AppInitOptions) -> Rc<Self> {
let mut servo_builder = ServoBuilder::default() let mut servo_builder = ServoBuilder::default()
.opts(init.opts) .opts(init.opts)

View File

@@ -86,6 +86,7 @@ impl ServoShellWindow {
} }
/// Must be called *after* `self` is in `state.windows`, otherwise it will panic. /// Must be called *after* `self` is in `state.windows`, otherwise it will panic.
#[servo::servo_tracing::instrument(skip(self, state))]
pub(crate) fn create_toplevel_webview(&self, state: Rc<RunningAppState>, url: Url) -> WebView { pub(crate) fn create_toplevel_webview(&self, state: Rc<RunningAppState>, url: Url) -> WebView {
let mut webview_builder = let mut webview_builder =
WebViewBuilder::new(state.servo(), self.platform_window.rendering_context()) WebViewBuilder::new(state.servo(), self.platform_window.rendering_context())