From 19a1e57c9019d363a5391c5a93722b4206c95440 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Tue, 3 Nov 2015 11:16:25 -0500 Subject: [PATCH] Add debugging information when running tests that timeout on build machines. --- components/compositing/compositor.rs | 75 ++++++++++++++++++------- components/compositing/constellation.rs | 31 +++++++--- components/util/opts.rs | 13 +++++ 3 files changed, 93 insertions(+), 26 deletions(-) diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 3482d730be4..c59dd622adc 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -54,6 +54,24 @@ use util::opts; use util::print_tree::PrintTree; use windowing::{self, MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg}; +#[derive(Debug)] +enum UnableToComposite { + NoContext, + WindowUnprepared, + NotReadyToPaintImage(NotReadyToPaint), +} + +#[derive(Debug)] +enum NotReadyToPaint { + LayerHasOutstandingPaintMessages, + MissingRoot, + PendingSubpages(usize), + AnimationsRunning, + AnimationCallbacksRunning, + JustNotifiedConstellation, + WaitingOnConstellation, +} + const BUFFER_MAP_SIZE: usize = 10000000; // Default viewport constraints @@ -206,7 +224,7 @@ pub struct ScrollEvent { cursor: TypedPoint2D, } -#[derive(PartialEq)] +#[derive(PartialEq, Debug)] enum CompositionRequest { NoCompositingNecessary, CompositeOnScrollTimeout(u64), @@ -570,8 +588,14 @@ impl IOCompositor { assert!(self.ready_to_save_state == ReadyState::WaitingForConstellationReply); if is_ready { self.ready_to_save_state = ReadyState::ReadyToSaveImage; + if opts::get().is_running_problem_test { + println!("ready to save image!"); + } } else { self.ready_to_save_state = ReadyState::Unknown; + if opts::get().is_running_problem_test { + println!("resetting ready_to_save_state!"); + } } self.composite_if_necessary(CompositingReason::Headless); } @@ -1612,7 +1636,7 @@ impl IOCompositor { /// Query the constellation to see if the current compositor /// output matches the current frame tree output, and if the /// associated script tasks are idle. - fn is_ready_to_paint_image_output(&mut self) -> bool { + fn is_ready_to_paint_image_output(&mut self) -> Result<(), NotReadyToPaint> { match self.ready_to_save_state { ReadyState::Unknown => { // Unsure if the output image is stable. @@ -1623,17 +1647,17 @@ impl IOCompositor { match self.scene.root { Some(ref root_layer) => { if self.does_layer_have_outstanding_paint_messages(root_layer) { - return false; + return Err(NotReadyToPaint::LayerHasOutstandingPaintMessages); } } None => { - return false; + return Err(NotReadyToPaint::MissingRoot); } } // Check if there are any pending frames. If so, the image is not stable yet. if self.pending_subpages.len() > 0 { - return false + return Err(NotReadyToPaint::PendingSubpages(self.pending_subpages.len())); } // Collect the currently painted epoch of each pipeline that is @@ -1644,8 +1668,11 @@ impl IOCompositor { for (id, details) in &self.pipeline_details { // If animations are currently running, then don't bother checking // with the constellation if the output image is stable. - if details.animations_running || details.animation_callbacks_running { - return false; + if details.animations_running { + return Err(NotReadyToPaint::AnimationsRunning); + } + if details.animation_callbacks_running { + return Err(NotReadyToPaint::AnimationCallbacksRunning); } pipeline_epochs.insert(*id, details.current_epoch); @@ -1656,12 +1683,12 @@ impl IOCompositor { let ConstellationChan(ref chan) = self.constellation_chan; chan.send(ConstellationMsg::IsReadyToSaveImage(pipeline_epochs)).unwrap(); self.ready_to_save_state = ReadyState::WaitingForConstellationReply; - false + Err(NotReadyToPaint::JustNotifiedConstellation) } ReadyState::WaitingForConstellationReply => { // If waiting on a reply from the constellation to the last // query if the image is stable, then assume not ready yet. - false + Err(NotReadyToPaint::WaitingOnConstellation) } ReadyState::ReadyToSaveImage => { // Constellation has replied at some point in the past @@ -1669,8 +1696,9 @@ impl IOCompositor { // for saving. // Reset the flag so that we check again in the future // TODO: only reset this if we load a new document? + println!("was ready to save, resetting ready_to_save_state"); self.ready_to_save_state = ReadyState::Unknown; - true + Ok(()) } } } @@ -1680,8 +1708,10 @@ impl IOCompositor { let composited = self.composite_specific_target(target); if composited.is_ok() && (opts::get().output_file.is_some() || opts::get().exit_after_load) { - debug!("Shutting down the Constellation after generating an output file or exit flag specified"); + println!("Shutting down the Constellation after generating an output file or exit flag specified"); self.start_shutting_down(); + } else if composited.is_err() && opts::get().is_running_problem_test { + println!("not ready to composite: {:?}", composited.err().unwrap()); } } @@ -1690,26 +1720,28 @@ impl IOCompositor { /// for some reason. If CompositeTarget is Window or Png no image data is returned; /// in the latter case the image is written directly to a file. If CompositeTarget /// is WindowAndPng Ok(Some(png::Image)) is returned. - pub fn composite_specific_target(&mut self, target: CompositeTarget) -> Result, ()> { + pub fn composite_specific_target(&mut self, target: CompositeTarget) -> Result, UnableToComposite> { if !self.context.is_some() { - return Err(()) + return Err(UnableToComposite::NoContext) } let (width, height) = (self.window_size.width.get() as usize, self.window_size.height.get() as usize); if !self.window.prepare_for_composite(width, height) { - return Err(()) + return Err(UnableToComposite::WindowUnprepared) } match target { CompositeTarget::WindowAndPng | CompositeTarget::PngFile => { - if !self.is_ready_to_paint_image_output() { - return Err(()) + if let Err(result) = self.is_ready_to_paint_image_output() { + return Err(UnableToComposite::NotReadyToPaintImage(result)) } } CompositeTarget::Window => { - if opts::get().exit_after_load && !self.is_ready_to_paint_image_output() { - return Err(()) + if opts::get().exit_after_load { + if let Err(result) = self.is_ready_to_paint_image_output() { + return Err(UnableToComposite::NotReadyToPaintImage(result)) + } } } } @@ -1826,7 +1858,12 @@ impl IOCompositor { fn composite_if_necessary(&mut self, reason: CompositingReason) { if self.composition_request == CompositionRequest::NoCompositingNecessary { + if opts::get().is_running_problem_test { + println!("updating composition_request ({:?})", reason); + } self.composition_request = CompositionRequest::CompositeNow(reason) + } else if opts::get().is_running_problem_test { + println!("composition_request is already {:?}", self.composition_request); } } @@ -2079,7 +2116,7 @@ impl CompositorEventListener for IOCompositor where Window: Wind } /// Why we performed a composite. This is used for debugging. -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum CompositingReason { /// We hit the scroll timeout and are therefore drawing unrendered content. HitScrollTimeout, diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index d578fac7832..adfb9fb5b21 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -57,6 +57,16 @@ use util::geometry::PagePx; use util::task::spawn_named; use util::{opts, prefs}; +#[derive(Debug, PartialEq)] +enum ReadyToSave { + NoRootFrame, + WebFontNotLoaded, + DocumentLoading, + EpochMismatch, + PipelineUnknown, + Ready, +} + /// Maintains the pipelines and navigation context and grants permission to composite. /// /// It is parameterized over a `LayoutTaskFactory` and a @@ -516,7 +526,14 @@ impl Constellation { } ConstellationMsg::IsReadyToSaveImage(pipeline_states) => { let is_ready = self.handle_is_ready_to_save_image(pipeline_states); + if opts::get().is_running_problem_test { + println!("got ready to save image query, result is {:?}", is_ready); + } + let is_ready = is_ready == ReadyToSave::Ready; self.compositor_proxy.send(CompositorMsg::IsReadyToSaveImageReply(is_ready)); + if opts::get().is_running_problem_test { + println!("sent response"); + } } ConstellationMsg::RemoveIFrame(pipeline_id) => { debug!("constellation got remove iframe message"); @@ -1188,11 +1205,11 @@ impl Constellation { /// to check if the output image is "stable" and can be written as a screenshot /// for reftests. fn handle_is_ready_to_save_image(&mut self, - pipeline_states: HashMap) -> bool { + pipeline_states: HashMap) -> ReadyToSave { // If there is no root frame yet, the initial page has // not loaded, so there is nothing to save yet. if self.root_frame_id.is_none() { - return false; + return ReadyToSave::NoRootFrame; } // Step through the current frame tree, checking that the script @@ -1215,7 +1232,7 @@ impl Constellation { let msg = LayoutControlMsg::GetWebFontLoadState(sender); pipeline.layout_chan.0.send(msg).unwrap(); if receiver.recv().unwrap() { - return false; + return ReadyToSave::WebFontNotLoaded; } // Synchronously query the script task for this pipeline @@ -1225,7 +1242,7 @@ impl Constellation { pipeline.script_chan.send(msg).unwrap(); let result = receiver.recv().unwrap(); if result == ScriptState::DocumentLoading { - return false; + return ReadyToSave::DocumentLoading; } // Check the visible rectangle for this pipeline. If the constellation has received a @@ -1253,20 +1270,20 @@ impl Constellation { layout_chan.send(LayoutControlMsg::GetCurrentEpoch(sender)).unwrap(); let layout_task_epoch = receiver.recv().unwrap(); if layout_task_epoch != *compositor_epoch { - return false; + return ReadyToSave::EpochMismatch; } } None => { // The compositor doesn't know about this pipeline yet. // Assume it hasn't rendered yet. - return false; + return ReadyToSave::PipelineUnknown; } } } } // All script tasks are idle and layout epochs match compositor, so output image! - true + ReadyToSave::Ready } // Close a frame (and all children) diff --git a/components/util/opts.rs b/components/util/opts.rs index 9f68ffb432b..40617aa28c3 100644 --- a/components/util/opts.rs +++ b/components/util/opts.rs @@ -23,6 +23,8 @@ use url::{self, Url}; /// Global flags for Servo, currently set on the command line. #[derive(Clone)] pub struct Opts { + pub is_running_problem_test: bool, + /// The initial URL to load. pub url: Option, @@ -403,6 +405,7 @@ const DEFAULT_USER_AGENT: UserAgent = UserAgent::Desktop; pub fn default_opts() -> Opts { Opts { + is_running_problem_test: false, url: Some(Url::parse("about:blank").unwrap()), paint_threads: 1, gpu_painting: false, @@ -524,6 +527,15 @@ pub fn from_cmdline_args(args: &[String]) { } else { homepage_pref.as_string() }; + let is_running_problem_test = + url_opt + .as_ref() + .map(|url| + url.starts_with("http://web-platform.test:8000/2dcontext/drawing-images-to-the-canvas/") || + url.starts_with("http://web-platform.test:8000/_mozilla/mozilla/canvas/") || + url.starts_with("http://web-platform.test:8000/_mozilla/css/canvas_over_area.html")) + .unwrap_or(false); + let url = match url_opt { Some(url_string) => { parse_url_or_filename(&cwd, url_string) @@ -611,6 +623,7 @@ pub fn from_cmdline_args(args: &[String]) { }).collect(); let opts = Opts { + is_running_problem_test: is_running_problem_test, url: Some(url), paint_threads: paint_threads, gpu_painting: gpu_painting,