diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index dbbc9f4f7f8c..a24069ab946d 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -22,6 +22,9 @@ path = "../msg" [dependencies.net] path = "../net" +[dependencies.profile] +path = "../profile" + [dependencies.util] path = "../util" @@ -52,4 +55,4 @@ git = "https://github.com/servo/gleam" [dependencies] url = "0.2.16" time = "0.1.17" -libc = "*" \ No newline at end of file +libc = "*" diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index e865c61be49a..8a80947f1cfa 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -11,8 +11,6 @@ use scrolling::ScrollingTimerProxy; use windowing; use windowing::{MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg}; -use std::cmp; -use std::mem; use geom::point::{Point2D, TypedPoint2D}; use geom::rect::{Rect, TypedRect}; use geom::size::{Size2D, TypedSize2D}; @@ -35,20 +33,23 @@ use msg::constellation_msg::{ConstellationChan, NavigationDirection}; use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData}; use msg::constellation_msg::{PipelineId, WindowSizeData}; -use util::geometry::{PagePx, ScreenPx, ViewportPx}; -use util::memory::MemoryProfilerChan; -use util::opts; -use util::time::{TimeProfilerCategory, profile, TimeProfilerChan}; -use util::{memory, time}; +use profile::mem; +use profile::mem::{MemoryProfilerChan}; +use profile::time; +use profile::time::{TimeProfilerCategory, profile, TimeProfilerChan}; +use std::cmp; use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::old_path::Path; +use std::mem::replace; use std::num::Float; use std::rc::Rc; use std::slice::bytes::copy_memory; use std::sync::mpsc::Sender; use time::{precise_time_ns, precise_time_s}; use url::Url; +use util::geometry::{PagePx, ScreenPx, ViewportPx}; +use util::opts; /// NB: Never block on the constellation, because sometimes the constellation blocks on us. pub struct IOCompositor { @@ -869,7 +870,7 @@ impl IOCompositor { fn process_pending_scroll_events(&mut self) { let had_scroll_events = self.pending_scroll_events.len() > 0; - for scroll_event in mem::replace(&mut self.pending_scroll_events, Vec::new()).into_iter() { + for scroll_event in replace(&mut self.pending_scroll_events, Vec::new()).into_iter() { let delta = scroll_event.delta / self.scene.scale; let cursor = scroll_event.cursor.as_f32() / self.scene.scale; @@ -1356,7 +1357,7 @@ impl CompositorEventListener for IOCompositor where Window: Wind // Tell the profiler, memory profiler, and scrolling timer to shut down. self.time_profiler_chan.send(time::TimeProfilerMsg::Exit); - self.memory_profiler_chan.send(memory::MemoryProfilerMsg::Exit); + self.memory_profiler_chan.send(mem::MemoryProfilerMsg::Exit); self.scrolling_timer.shutdown(); } diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index 3c615175c328..a9b4fb68475d 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -21,13 +21,13 @@ use msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState}; use msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy}; use msg::constellation_msg::{ConstellationChan, PipelineId}; use msg::constellation_msg::{Key, KeyState, KeyModifiers}; -use url::Url; -use util::cursor::Cursor; -use util::memory::MemoryProfilerChan; -use util::time::TimeProfilerChan; +use profile::mem::MemoryProfilerChan; +use profile::time::TimeProfilerChan; use std::sync::mpsc::{channel, Sender, Receiver}; use std::fmt::{Error, Formatter, Debug}; use std::rc::Rc; +use url::Url; +use util::cursor::Cursor; /// Sends messages to the compositor. This is a trait supplied by the port because the method used /// to communicate with the compositor may have to kick OS event loops awake, communicate cross- diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 86efe99ab8df..ab1537d53f2a 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -25,12 +25,12 @@ use msg::constellation_msg::Msg as ConstellationMsg; use net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use net::resource_task::{self, ResourceTask}; use net::storage_task::{StorageTask, StorageTaskMsg}; +use profile::mem::MemoryProfilerChan; +use profile::time::TimeProfilerChan; use util::cursor::Cursor; use util::geometry::PagePx; -use util::memory::MemoryProfilerChan; use util::opts; use util::task::spawn_named; -use util::time::TimeProfilerChan; use std::borrow::ToOwned; use std::collections::HashMap; use std::old_io as io; diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index 18cf76961a72..3f38099d83b5 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -9,10 +9,10 @@ use geom::scale_factor::ScaleFactor; use geom::size::TypedSize2D; use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::{ConstellationChan, WindowSizeData}; -use util::memory::MemoryProfilerChan; -use util::memory; -use util::time::TimeProfilerChan; -use util::time; +use profile::mem; +use profile::mem::MemoryProfilerChan; +use profile::time; +use profile::time::TimeProfilerChan; /// Starts the compositor, which listens for messages on the specified port. /// @@ -121,7 +121,7 @@ impl CompositorEventListener for NullCompositor { while self.port.try_recv_compositor_msg().is_some() {} self.time_profiler_chan.send(time::TimeProfilerMsg::Exit); - self.memory_profiler_chan.send(memory::MemoryProfilerMsg::Exit); + self.memory_profiler_chan.send(mem::MemoryProfilerMsg::Exit); } fn pinch_zoom_level(&self) -> f32 { diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index 77c389d8607c..6ab2e3134375 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -23,6 +23,7 @@ extern crate png; extern crate script_traits; extern crate msg; extern crate net; +extern crate profile; #[macro_use] extern crate util; extern crate gleam; diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index 52771c75bcfb..0d8e6261d25d 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -19,11 +19,11 @@ use msg::constellation_msg::{LoadData, WindowSizeData, PipelineExitType}; use net::image_cache_task::ImageCacheTask; use net::resource_task::ResourceTask; use net::storage_task::StorageTask; +use profile::mem::MemoryProfilerChan; +use profile::time::TimeProfilerChan; +use std::sync::mpsc::{Receiver, channel}; use url::Url; use util::geometry::{PagePx, ViewportPx}; -use util::memory::MemoryProfilerChan; -use util::time::TimeProfilerChan; -use std::sync::mpsc::{Receiver, channel}; /// A uniquely-identifiable pipeline of script task, layout task, and paint task. pub struct Pipeline { diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml index ee9d4990da96..bb2136242405 100644 --- a/components/gfx/Cargo.toml +++ b/components/gfx/Cargo.toml @@ -20,6 +20,9 @@ path = "../util" [dependencies.msg] path = "../msg" +[dependencies.profile] +path = "../profile" + [dependencies.style] path = "../style" diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs index a2cb03d33a6e..0be282fb8f60 100644 --- a/components/gfx/lib.rs +++ b/components/gfx/lib.rs @@ -27,6 +27,7 @@ extern crate layers; extern crate libc; extern crate stb_image; extern crate png; +extern crate profile; extern crate script_traits; extern crate "rustc-serialize" as rustc_serialize; extern crate unicode; diff --git a/components/gfx/paint_task.rs b/components/gfx/paint_task.rs index db281e1acac2..8548161d165e 100644 --- a/components/gfx/paint_task.rs +++ b/components/gfx/paint_task.rs @@ -25,17 +25,17 @@ use msg::compositor_msg::{LayerMetadata, PaintListener, ScrollPolicy}; use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::{ConstellationChan, Failure, PipelineId}; use msg::constellation_msg::PipelineExitType; +use profile::time::{TimeProfilerChan, TimeProfilerCategory, profile}; use skia::SkiaGrGLNativeContextRef; +use std::mem; +use std::thread::Builder; +use std::sync::Arc; +use std::sync::mpsc::{Receiver, Sender, channel}; use util::geometry::{Au, ZERO_POINT}; use util::opts; use util::smallvec::SmallVec; use util::task::spawn_named_with_send_on_failure; use util::task_state; -use util::time::{TimeProfilerChan, TimeProfilerCategory, profile}; -use std::mem; -use std::thread::Builder; -use std::sync::Arc; -use std::sync::mpsc::{Receiver, Sender, channel}; /// Information about a hardware graphics layer that layout sends to the painting task. #[derive(Clone)] diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index 733985cefde9..2eee5eb3d4ad 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -37,6 +37,9 @@ path = "../plugins" [dependencies.net] path = "../net" +[dependencies.profile] +path = "../profile" + [dependencies.util] path = "../util" diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 08658f05d037..2c15e643b9c9 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -43,6 +43,9 @@ use msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType, Pipel use net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use net::local_image_cache::{ImageResponder, LocalImageCache}; use net::resource_task::{ResourceTask, load_bytes_iter}; +use profile::mem::{MemoryProfilerChan, MemoryProfilerMsg, MemoryReport, MemoryReportsChan}; +use profile::time::{TimeProfilerCategory, ProfilerMetadata, TimeProfilerChan}; +use profile::time::{TimerMetadataFrameType, TimerMetadataReflowType, profile}; use script::dom::bindings::js::LayoutJS; use script::dom::element::ElementTypeId; use script::dom::htmlelement::HTMLElementTypeId; @@ -70,14 +73,11 @@ use url::Url; use util::cursor::Cursor; use util::geometry::Au; use util::logical_geometry::LogicalPoint; -use util::memory::{MemoryProfilerChan, MemoryProfilerMsg, MemoryReport, MemoryReportsChan}; use util::memory::{SizeOf}; use util::opts; use util::smallvec::{SmallVec, SmallVec1, VecLike}; use util::task::spawn_named_with_send_on_failure; use util::task_state; -use util::time::{TimeProfilerCategory, ProfilerMetadata, TimeProfilerChan}; -use util::time::{TimerMetadataFrameType, TimerMetadataReflowType, profile}; use util::workqueue::WorkQueue; /// Mutable data belonging to the LayoutTask. diff --git a/components/layout/lib.rs b/components/layout/lib.rs index 2ff8502f7f11..a0498e2464f2 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -43,6 +43,8 @@ extern crate style; extern crate "plugins" as servo_plugins; extern crate net; extern crate msg; +#[macro_use] +extern crate profile; extern crate selectors; #[macro_use] extern crate util; diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index 5562db5e10db..e0effe6e6fc2 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -20,12 +20,12 @@ use wrapper::{layout_node_to_unsafe_layout_node, layout_node_from_unsafe_layout_ use wrapper::{PostorderNodeMutTraversal, UnsafeLayoutNode}; use wrapper::{PreorderDomTraversal, PostorderDomTraversal}; -use util::opts; -use util::time::{TimeProfilerCategory, ProfilerMetadata, TimeProfilerChan, profile}; -use util::workqueue::{WorkQueue, WorkUnit, WorkerProxy}; +use profile::time::{TimeProfilerCategory, ProfilerMetadata, TimeProfilerChan, profile}; use std::mem; use std::ptr; use std::sync::atomic::{AtomicIsize, Ordering}; +use util::opts; +use util::workqueue::{WorkQueue, WorkUnit, WorkerProxy}; #[allow(dead_code)] fn static_assertion(node: UnsafeLayoutNode) { diff --git a/components/layout_traits/Cargo.toml b/components/layout_traits/Cargo.toml index 4922db6639b2..c099a8b35411 100644 --- a/components/layout_traits/Cargo.toml +++ b/components/layout_traits/Cargo.toml @@ -19,6 +19,9 @@ path = "../msg" [dependencies.net] path = "../net" +[dependencies.profile] +path = "../profile" + [dependencies.util] path = "../util" diff --git a/components/layout_traits/lib.rs b/components/layout_traits/lib.rs index 66d0ccc624ce..f8b9c3f263f7 100644 --- a/components/layout_traits/lib.rs +++ b/components/layout_traits/lib.rs @@ -6,6 +6,7 @@ extern crate gfx; extern crate script_traits; extern crate msg; extern crate net; +extern crate profile; extern crate url; extern crate util; @@ -19,11 +20,11 @@ use gfx::paint_task::PaintChan; use msg::constellation_msg::{ConstellationChan, Failure, PipelineId, PipelineExitType}; use net::image_cache_task::ImageCacheTask; use net::resource_task::ResourceTask; -use url::Url; -use util::memory::MemoryProfilerChan; -use util::time::TimeProfilerChan; +use profile::mem::MemoryProfilerChan; +use profile::time::TimeProfilerChan; use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel}; use std::sync::mpsc::{Sender, Receiver}; +use url::Url; /// Messages sent to the layout task from the constellation pub enum LayoutControlMsg { diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml index 440bed882e67..8f8c43b41393 100644 --- a/components/net/Cargo.toml +++ b/components/net/Cargo.toml @@ -7,6 +7,9 @@ authors = ["The Servo Project Developers"] name = "net" path = "lib.rs" +[dependencies.profile] +path = "../profile" + [dependencies.util] path = "../util" diff --git a/components/net/image_cache_task.rs b/components/net/image_cache_task.rs index ecb6b4ae3965..43a0d0855072 100644 --- a/components/net/image_cache_task.rs +++ b/components/net/image_cache_task.rs @@ -7,9 +7,7 @@ use resource_task; use resource_task::{LoadData, ResourceTask}; use resource_task::ProgressMsg::{Payload, Done}; -use util::task::spawn_named; -use util::taskpool::TaskPool; -use util::time::{self, profile, TimeProfilerChan}; +use profile::time::{self, profile, TimeProfilerChan}; use std::borrow::ToOwned; use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -17,6 +15,8 @@ use std::mem::replace; use std::sync::{Arc, Mutex}; use std::sync::mpsc::{channel, Receiver, Sender}; use url::Url; +use util::task::spawn_named; +use util::taskpool::TaskPool; pub enum Msg { /// Tell the cache that we may need a particular image soon. Must be posted @@ -495,10 +495,10 @@ mod tests { use resource_task::ProgressMsg::{Payload, Done}; use sniffer_task; use image::base::test_image_bin; - use util::taskpool::TaskPool; - use util::time::{TimeProfiler, TimeProfilerChan}; + use profile::time::{TimeProfiler, TimeProfilerChan}; use std::sync::mpsc::{Sender, channel, Receiver}; use url::Url; + use util::taskpool::TaskPool; trait Closure { fn invoke(&self, _response: Sender) { } diff --git a/components/net/lib.rs b/components/net/lib.rs index 410e8ab1556e..4933d64f0e65 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -26,6 +26,7 @@ extern crate png; #[macro_use] extern crate log; extern crate openssl; +extern crate profile; extern crate "rustc-serialize" as rustc_serialize; extern crate util; extern crate stb_image; diff --git a/components/profile/Cargo.toml b/components/profile/Cargo.toml new file mode 100644 index 000000000000..de9fb13a5975 --- /dev/null +++ b/components/profile/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "profile" +version = "0.0.1" +authors = ["The Servo Project Developers"] + +[lib] +name = "profile" +path = "lib.rs" + +[dependencies.task_info] +path = "../../support/rust-task_info" + +[dependencies.util] +path = "../util" + +[dependencies] +libc = "*" +regex = "0.1.14" +time = "0.1.12" +url = "0.2.16" diff --git a/components/profile/lib.rs b/components/profile/lib.rs new file mode 100644 index 000000000000..c8ffdc72d17d --- /dev/null +++ b/components/profile/lib.rs @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#![feature(collections)] +#![feature(core)] +#![cfg_attr(target_os="linux", feature(io))] +#![feature(old_io)] +#![cfg_attr(target_os="linux", feature(page_size))] +#![feature(rustc_private)] +#![feature(std_misc)] +#![cfg_attr(target_os="linux", feature(str_words))] + +#[macro_use] extern crate log; + +extern crate collections; +extern crate libc; +#[cfg(target_os="linux")] +extern crate regex; +#[cfg(target_os="macos")] +extern crate task_info; +extern crate "time" as std_time; +extern crate util; +extern crate url; + +pub mod mem; +pub mod time; diff --git a/components/profile/mem.rs b/components/profile/mem.rs new file mode 100644 index 000000000000..163a255659d7 --- /dev/null +++ b/components/profile/mem.rs @@ -0,0 +1,658 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Memory profiling functions. + +use self::system_reporter::SystemMemoryReporter; +use std::borrow::ToOwned; +use std::cmp::Ordering; +use std::collections::HashMap; +use std::old_io::timer::sleep; +use std::sync::mpsc::{Sender, channel, Receiver}; +use std::time::duration::Duration; +use util::task::spawn_named; + +#[derive(Clone)] +pub struct MemoryProfilerChan(pub Sender); + +impl MemoryProfilerChan { + pub fn send(&self, msg: MemoryProfilerMsg) { + let MemoryProfilerChan(ref c) = *self; + c.send(msg).unwrap(); + } +} + +/// An easy way to build a path for a report. +#[macro_export] +macro_rules! path { + ($($x:expr),*) => {{ + use std::borrow::ToOwned; + vec![$( $x.to_owned() ),*] + }} +} + +pub struct MemoryReport { + /// The identifying path for this report. + pub path: Vec, + + /// The size, in bytes. + pub size: u64, +} + +/// A channel through which memory reports can be sent. +#[derive(Clone)] +pub struct MemoryReportsChan(pub Sender>); + +impl MemoryReportsChan { + pub fn send(&self, report: Vec) { + let MemoryReportsChan(ref c) = *self; + c.send(report).unwrap(); + } +} + +/// A memory reporter is capable of measuring some data structure of interest. Because it needs +/// to be passed to and registered with the MemoryProfiler, it's typically a "small" (i.e. easily +/// cloneable) value that provides access to a "large" data structure, e.g. a channel that can +/// inject a request for measurements into the event queue associated with the "large" data +/// structure. +pub trait MemoryReporter { + /// Collect one or more memory reports. Returns true on success, and false on failure. + fn collect_reports(&self, reports_chan: MemoryReportsChan) -> bool; +} + +/// Messages that can be sent to the memory profiler thread. +pub enum MemoryProfilerMsg { + /// Register a MemoryReporter with the memory profiler. The String is only used to identify the + /// reporter so it can be unregistered later. The String must be distinct from that used by any + /// other registered reporter otherwise a panic will occur. + RegisterMemoryReporter(String, Box), + + /// Unregister a MemoryReporter with the memory profiler. The String must match the name given + /// when the reporter was registered. If the String does not match the name of a registered + /// reporter a panic will occur. + UnregisterMemoryReporter(String), + + /// Triggers printing of the memory profiling metrics. + Print, + + /// Tells the memory profiler to shut down. + Exit, +} + +pub struct MemoryProfiler { + /// The port through which messages are received. + pub port: Receiver, + + /// Registered memory reporters. + reporters: HashMap>, +} + +impl MemoryProfiler { + pub fn create(period: Option) -> MemoryProfilerChan { + let (chan, port) = channel(); + + // Create the timer thread if a period was provided. + if let Some(period) = period { + let period_ms = Duration::milliseconds((period * 1000f64) as i64); + let chan = chan.clone(); + spawn_named("Memory profiler timer".to_owned(), move || { + loop { + sleep(period_ms); + if chan.send(MemoryProfilerMsg::Print).is_err() { + break; + } + } + }); + } + + // Always spawn the memory profiler. If there is no timer thread it won't receive regular + // `Print` events, but it will still receive the other events. + spawn_named("Memory profiler".to_owned(), move || { + let mut memory_profiler = MemoryProfiler::new(port); + memory_profiler.start(); + }); + + let memory_profiler_chan = MemoryProfilerChan(chan); + + // Register the system memory reporter, which will run on the memory profiler's own thread. + // It never needs to be unregistered, because as long as the memory profiler is running the + // system memory reporter can make measurements. + let system_reporter = Box::new(SystemMemoryReporter); + memory_profiler_chan.send(MemoryProfilerMsg::RegisterMemoryReporter("system".to_owned(), + system_reporter)); + + memory_profiler_chan + } + + pub fn new(port: Receiver) -> MemoryProfiler { + MemoryProfiler { + port: port, + reporters: HashMap::new(), + } + } + + pub fn start(&mut self) { + loop { + match self.port.recv() { + Ok(msg) => { + if !self.handle_msg(msg) { + break + } + } + _ => break + } + } + } + + fn handle_msg(&mut self, msg: MemoryProfilerMsg) -> bool { + match msg { + MemoryProfilerMsg::RegisterMemoryReporter(name, reporter) => { + // Panic if it has already been registered. + let name_clone = name.clone(); + match self.reporters.insert(name, reporter) { + None => true, + Some(_) => + panic!(format!("RegisterMemoryReporter: '{}' name is already in use", + name_clone)), + } + }, + + MemoryProfilerMsg::UnregisterMemoryReporter(name) => { + // Panic if it hasn't previously been registered. + match self.reporters.remove(&name) { + Some(_) => true, + None => + panic!(format!("UnregisterMemoryReporter: '{}' name is unknown", &name)), + } + }, + + MemoryProfilerMsg::Print => { + self.handle_print_msg(); + true + }, + + MemoryProfilerMsg::Exit => false + } + } + + fn handle_print_msg(&self) { + println!("Begin memory reports"); + println!("|"); + + // Collect reports from memory reporters. + // + // This serializes the report-gathering. It might be worth creating a new scoped thread for + // each reporter once we have enough of them. + // + // If anything goes wrong with a reporter, we just skip it. + let mut forest = ReportsForest::new(); + for reporter in self.reporters.values() { + let (chan, port) = channel(); + if reporter.collect_reports(MemoryReportsChan(chan)) { + if let Ok(reports) = port.recv() { + for report in reports.iter() { + forest.insert(&report.path, report.size); + } + } + } + } + forest.print(); + + println!("|"); + println!("End memory reports"); + println!(""); + } +} + +/// A collection of one or more reports with the same initial path segment. A ReportsTree +/// containing a single node is described as "degenerate". +struct ReportsTree { + /// For leaf nodes, this is the sum of the sizes of all reports that mapped to this location. + /// For interior nodes, this is the sum of the sizes of all its child nodes. + size: u64, + + /// For leaf nodes, this is the count of all reports that mapped to this location. + /// For interor nodes, this is always zero. + count: u32, + + /// The segment from the report path that maps to this node. + path_seg: String, + + /// Child nodes. + children: Vec, +} + +impl ReportsTree { + fn new(path_seg: String) -> ReportsTree { + ReportsTree { + size: 0, + count: 0, + path_seg: path_seg, + children: vec![] + } + } + + // Searches the tree's children for a path_seg match, and returns the index if there is a + // match. + fn find_child(&self, path_seg: &String) -> Option { + for (i, child) in self.children.iter().enumerate() { + if child.path_seg == *path_seg { + return Some(i); + } + } + None + } + + // Insert the path and size into the tree, adding any nodes as necessary. + fn insert(&mut self, path: &[String], size: u64) { + let mut t: &mut ReportsTree = self; + for path_seg in path.iter() { + let i = match t.find_child(&path_seg) { + Some(i) => i, + None => { + let new_t = ReportsTree::new(path_seg.clone()); + t.children.push(new_t); + t.children.len() - 1 + }, + }; + let tmp = t; // this temporary is needed to satisfy the borrow checker + t = &mut tmp.children[i]; + } + + t.size += size; + t.count += 1; + } + + // Fill in sizes for interior nodes. Should only be done once all the reports have been + // inserted. + fn compute_interior_node_sizes(&mut self) -> u64 { + if !self.children.is_empty() { + // Interior node. Derive its size from its children. + if self.size != 0 { + // This will occur if e.g. we have paths ["a", "b"] and ["a", "b", "c"]. + panic!("one report's path is a sub-path of another report's path"); + } + for child in self.children.iter_mut() { + self.size += child.compute_interior_node_sizes(); + } + } + self.size + } + + fn print(&self, depth: i32) { + if !self.children.is_empty() { + assert_eq!(self.count, 0); + } + + let mut indent_str = String::new(); + for _ in range(0, depth) { + indent_str.push_str(" "); + } + + let mebi = 1024f64 * 1024f64; + let count_str = if self.count > 1 { format!(" {}", self.count) } else { "".to_owned() }; + println!("|{}{:8.2} MiB -- {}{}", + indent_str, (self.size as f64) / mebi, self.path_seg, count_str); + + for child in self.children.iter() { + child.print(depth + 1); + } + } +} + +/// A collection of ReportsTrees. It represents the data from multiple memory reports in a form +/// that's good to print. +struct ReportsForest { + trees: HashMap, +} + +impl ReportsForest { + fn new() -> ReportsForest { + ReportsForest { + trees: HashMap::new(), + } + } + + // Insert the path and size into the forest, adding any trees and nodes as necessary. + fn insert(&mut self, path: &[String], size: u64) { + // Get the right tree, creating it if necessary. + if !self.trees.contains_key(&path[0]) { + self.trees.insert(path[0].clone(), ReportsTree::new(path[0].clone())); + } + let t = self.trees.get_mut(&path[0]).unwrap(); + + // Use tail() because the 0th path segment was used to find the right tree in the forest. + t.insert(path.tail(), size); + } + + fn print(&mut self) { + // Fill in sizes of interior nodes. + for (_, tree) in self.trees.iter_mut() { + tree.compute_interior_node_sizes(); + } + + // Put the trees into a sorted vector. Primary sort: degenerate trees (those containing a + // single node) come after non-degenerate trees. Secondary sort: alphabetical order of the + // root node's path_seg. + let mut v = vec![]; + for (_, tree) in self.trees.iter() { + v.push(tree); + } + v.sort_by(|a, b| { + if a.children.is_empty() && !b.children.is_empty() { + Ordering::Greater + } else if !a.children.is_empty() && b.children.is_empty() { + Ordering::Less + } else { + a.path_seg.cmp(&b.path_seg) + } + }); + + // Print the forest. + for tree in v.iter() { + tree.print(0); + // Print a blank line after non-degenerate trees. + if !tree.children.is_empty() { + println!("|"); + } + } + } +} + +//--------------------------------------------------------------------------- + +mod system_reporter { + use libc::{c_char, c_int, c_void, size_t}; + use std::borrow::ToOwned; + use std::ffi::CString; + use std::mem::size_of; + use std::ptr::null_mut; + use super::{MemoryReport, MemoryReporter, MemoryReportsChan}; + #[cfg(target_os="macos")] + use task_info::task_basic_info::{virtual_size, resident_size}; + + /// Collects global measurements from the OS and heap allocators. + pub struct SystemMemoryReporter; + + impl MemoryReporter for SystemMemoryReporter { + fn collect_reports(&self, reports_chan: MemoryReportsChan) -> bool { + let mut reports = vec![]; + { + let mut report = |path, size| { + if let Some(size) = size { + reports.push(MemoryReport { path: path, size: size }); + } + }; + + // Virtual and physical memory usage, as reported by the OS. + report(path!["vsize"], get_vsize()); + report(path!["resident"], get_resident()); + + // Memory segments, as reported by the OS. + for seg in get_resident_segments().iter() { + report(path!["resident-according-to-smaps", seg.0], Some(seg.1)); + } + + // Total number of bytes allocated by the application on the system + // heap. + report(path!["system-heap-allocated"], get_system_heap_allocated()); + + // The descriptions of the following jemalloc measurements are taken + // directly from the jemalloc documentation. + + // "Total number of bytes allocated by the application." + report(path!["jemalloc-heap-allocated"], get_jemalloc_stat("stats.allocated")); + + // "Total number of bytes in active pages allocated by the application. + // This is a multiple of the page size, and greater than or equal to + // |stats.allocated|." + report(path!["jemalloc-heap-active"], get_jemalloc_stat("stats.active")); + + // "Total number of bytes in chunks mapped on behalf of the application. + // This is a multiple of the chunk size, and is at least as large as + // |stats.active|. This does not include inactive chunks." + report(path!["jemalloc-heap-mapped"], get_jemalloc_stat("stats.mapped")); + } + reports_chan.send(reports); + + true + } + } + + + #[cfg(target_os="linux")] + extern { + fn mallinfo() -> struct_mallinfo; + } + + #[cfg(target_os="linux")] + #[repr(C)] + pub struct struct_mallinfo { + arena: c_int, + ordblks: c_int, + smblks: c_int, + hblks: c_int, + hblkhd: c_int, + usmblks: c_int, + fsmblks: c_int, + uordblks: c_int, + fordblks: c_int, + keepcost: c_int, + } + + #[cfg(target_os="linux")] + fn get_system_heap_allocated() -> Option { + let mut info: struct_mallinfo; + unsafe { + info = mallinfo(); + } + // The documentation in the glibc man page makes it sound like |uordblks| + // would suffice, but that only gets the small allocations that are put in + // the brk heap. We need |hblkhd| as well to get the larger allocations + // that are mmapped. + Some((info.hblkhd + info.uordblks) as u64) + } + + #[cfg(not(target_os="linux"))] + fn get_system_heap_allocated() -> Option { + None + } + + extern { + fn je_mallctl(name: *const c_char, oldp: *mut c_void, oldlenp: *mut size_t, + newp: *mut c_void, newlen: size_t) -> c_int; + } + + fn get_jemalloc_stat(value_name: &str) -> Option { + // Before we request the measurement of interest, we first send an "epoch" + // request. Without that jemalloc gives cached statistics(!) which can be + // highly inaccurate. + let epoch_name = "epoch"; + let epoch_c_name = CString::new(epoch_name).unwrap(); + let mut epoch: u64 = 0; + let epoch_ptr = &mut epoch as *mut _ as *mut c_void; + let mut epoch_len = size_of::() as size_t; + + let value_c_name = CString::new(value_name).unwrap(); + let mut value: size_t = 0; + let value_ptr = &mut value as *mut _ as *mut c_void; + let mut value_len = size_of::() as size_t; + + // Using the same values for the `old` and `new` parameters is enough + // to get the statistics updated. + let rv = unsafe { + je_mallctl(epoch_c_name.as_ptr(), epoch_ptr, &mut epoch_len, epoch_ptr, + epoch_len) + }; + if rv != 0 { + return None; + } + + let rv = unsafe { + je_mallctl(value_c_name.as_ptr(), value_ptr, &mut value_len, null_mut(), 0) + }; + if rv != 0 { + return None; + } + + Some(value as u64) + } + + // Like std::macros::try!, but for Option<>. + macro_rules! option_try( + ($e:expr) => (match $e { Some(e) => e, None => return None }) + ); + + #[cfg(target_os="linux")] + fn get_proc_self_statm_field(field: usize) -> Option { + use std::fs::File; + use std::io::Read; + + let mut f = option_try!(File::open("/proc/self/statm").ok()); + let mut contents = String::new(); + option_try!(f.read_to_string(&mut contents).ok()); + let s = option_try!(contents.words().nth(field)); + let npages = option_try!(s.parse::().ok()); + Some(npages * (::std::env::page_size() as u64)) + } + + #[cfg(target_os="linux")] + fn get_vsize() -> Option { + get_proc_self_statm_field(0) + } + + #[cfg(target_os="linux")] + fn get_resident() -> Option { + get_proc_self_statm_field(1) + } + + #[cfg(target_os="macos")] + fn get_vsize() -> Option { + virtual_size() + } + + #[cfg(target_os="macos")] + fn get_resident() -> Option { + resident_size() + } + + #[cfg(not(any(target_os="linux", target_os = "macos")))] + fn get_vsize() -> Option { + None + } + + #[cfg(not(any(target_os="linux", target_os = "macos")))] + fn get_resident() -> Option { + None + } + + #[cfg(target_os="linux")] + fn get_resident_segments() -> Vec<(String, u64)> { + use regex::Regex; + use std::collections::HashMap; + use std::collections::hash_map::Entry; + use std::fs::File; + use std::io::{BufReader, BufReadExt}; + + // The first line of an entry in /proc//smaps looks just like an entry + // in /proc//maps: + // + // address perms offset dev inode pathname + // 02366000-025d8000 rw-p 00000000 00:00 0 [heap] + // + // Each of the following lines contains a key and a value, separated + // by ": ", where the key does not contain either of those characters. + // For example: + // + // Rss: 132 kB + + let f = match File::open("/proc/self/smaps") { + Ok(f) => BufReader::new(f), + Err(_) => return vec![], + }; + + let seg_re = Regex::new( + r"^[:xdigit:]+-[:xdigit:]+ (....) [:xdigit:]+ [:xdigit:]+:[:xdigit:]+ \d+ +(.*)").unwrap(); + let rss_re = Regex::new(r"^Rss: +(\d+) kB").unwrap(); + + // We record each segment's resident size. + let mut seg_map: HashMap = HashMap::new(); + + #[derive(PartialEq)] + enum LookingFor { Segment, Rss } + let mut looking_for = LookingFor::Segment; + + let mut curr_seg_name = String::new(); + + // Parse the file. + for line in f.lines() { + let line = match line { + Ok(line) => line, + Err(_) => continue, + }; + if looking_for == LookingFor::Segment { + // Look for a segment info line. + let cap = match seg_re.captures(line.as_slice()) { + Some(cap) => cap, + None => continue, + }; + let perms = cap.at(1).unwrap(); + let pathname = cap.at(2).unwrap(); + + // Construct the segment name from its pathname and permissions. + curr_seg_name.clear(); + if pathname == "" || pathname.starts_with("[stack:") { + // Anonymous memory. Entries marked with "[stack:nnn]" + // look like thread stacks but they may include other + // anonymous mappings, so we can't trust them and just + // treat them as entirely anonymous. + curr_seg_name.push_str("anonymous"); + } else { + curr_seg_name.push_str(pathname); + } + curr_seg_name.push_str(" ("); + curr_seg_name.push_str(perms); + curr_seg_name.push_str(")"); + + looking_for = LookingFor::Rss; + } else { + // Look for an "Rss:" line. + let cap = match rss_re.captures(line.as_slice()) { + Some(cap) => cap, + None => continue, + }; + let rss = cap.at(1).unwrap().parse::().unwrap() * 1024; + + if rss > 0 { + // Aggregate small segments into "other". + let seg_name = if rss < 512 * 1024 { + "other".to_owned() + } else { + curr_seg_name.clone() + }; + match seg_map.entry(seg_name) { + Entry::Vacant(entry) => { entry.insert(rss); }, + Entry::Occupied(mut entry) => *entry.get_mut() += rss, + } + } + + looking_for = LookingFor::Segment; + } + } + + let mut segs: Vec<(String, u64)> = seg_map.into_iter().collect(); + + // Note that the sum of all these segments' RSS values differs from the "resident" measurement + // obtained via /proc//statm in get_resident(). It's unclear why this difference occurs; + // for some processes the measurements match, but for Servo they do not. + segs.sort_by(|&(_, rss1), &(_, rss2)| rss2.cmp(&rss1)); + + segs + } + + #[cfg(not(target_os="linux"))] + fn get_resident_segments() -> Vec<(String, u64)> { + vec![] + } +} diff --git a/components/util/time.rs b/components/profile/time.rs similarity index 99% rename from components/util/time.rs rename to components/profile/time.rs index 1cae21719d4f..fcd83d8093ae 100644 --- a/components/util/time.rs +++ b/components/profile/time.rs @@ -14,8 +14,8 @@ use std::num::Float; use std::sync::mpsc::{Sender, channel, Receiver}; use std::time::duration::Duration; use std_time::precise_time_ns; -use task::{spawn_named}; use url::Url; +use util::task::spawn_named; // front-end representation of the profiler used to communicate with the profiler #[derive(Clone)] diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index d7f1b2916b55..ac910ecb32ab 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -24,6 +24,9 @@ path = "../msg" [dependencies.net] path = "../net" +[dependencies.profile] +path = "../profile" + [dependencies.script_traits] path = "../script_traits" diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 52b5aed1b43b..b24adf944f6f 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -10,16 +10,16 @@ use dom::node::LayoutData; use geom::point::Point2D; use geom::rect::Rect; -use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress}; use msg::constellation_msg::{PipelineExitType, WindowSizeData}; -use util::geometry::Au; -use util::memory::{MemoryReporter, MemoryReportsChan}; +use profile::mem::{MemoryReporter, MemoryReportsChan}; +use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress}; use std::any::Any; use std::sync::mpsc::{channel, Receiver, Sender}; use std::boxed::BoxAny; use style::stylesheets::Stylesheet; use style::media_queries::MediaQueryList; use url::Url; +use util::geometry::Au; pub use dom::node::TrustedNodeAddress; diff --git a/components/script/lib.rs b/components/script/lib.rs index 5895127b552e..44ac6f1856cb 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -43,6 +43,7 @@ extern crate net; extern crate "rustc-serialize" as rustc_serialize; extern crate time; extern crate canvas; +extern crate profile; extern crate script_traits; extern crate selectors; extern crate util; diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index 3e289cf5f2a5..bc520d62f3ef 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -12,6 +12,7 @@ dependencies = [ "msg 0.0.1", "net 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "script 0.0.1", "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -93,6 +94,7 @@ dependencies = [ "msg 0.0.1", "net 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "script_traits 0.0.1", "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -308,6 +310,7 @@ dependencies = [ "net 0.0.1", "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "skia 0.0.20130412 (git+https://github.com/servo/skia?branch=upstream-2014-06-16)", @@ -510,6 +513,7 @@ dependencies = [ "net 0.0.1", "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "script 0.0.1", "script_traits 0.0.1", @@ -528,6 +532,7 @@ dependencies = [ "gfx 0.0.1", "msg 0.0.1", "net 0.0.1", + "profile 0.0.1", "script_traits 0.0.1", "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", @@ -609,6 +614,7 @@ dependencies = [ "hyper 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "regex 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "regex_macros 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -697,6 +703,18 @@ name = "png-sys" version = "1.6.16" source = "git+https://github.com/servo/rust-png#1d9c59c97598014860077f372443ae98b35ff4d9" +[[package]] +name = "profile" +version = "0.0.1" +dependencies = [ + "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "task_info 0.0.1", + "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", + "util 0.0.1", +] + [[package]] name = "rand" version = "0.1.4" @@ -742,6 +760,7 @@ dependencies = [ "msg 0.0.1", "net 0.0.1", "plugins 0.0.1", + "profile 0.0.1", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", @@ -887,15 +906,12 @@ dependencies = [ "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "plugins 0.0.1", "rand 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache_plugin 0.0.0 (git+https://github.com/servo/string-cache)", - "task_info 0.0.1", "text_writer 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index 5dfbbe8696c8..64e7f0607739 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -51,6 +51,9 @@ path = "../net" [dependencies.msg] path = "../msg" +[dependencies.profile] +path = "../profile" + [dependencies.util] path = "../util" diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 0312fca761ef..bc86b6a42152 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -11,6 +11,7 @@ extern crate compositing; extern crate devtools; extern crate net; extern crate msg; +extern crate profile; #[macro_use] extern crate util; extern crate script; @@ -40,9 +41,9 @@ use net::storage_task::{StorageTaskFactory, StorageTask}; #[cfg(not(test))] use gfx::font_cache_task::FontCacheTask; #[cfg(not(test))] -use util::time::TimeProfiler; +use profile::mem::MemoryProfiler; #[cfg(not(test))] -use util::memory::MemoryProfiler; +use profile::time::TimeProfiler; #[cfg(not(test))] use util::opts; #[cfg(not(test))] diff --git a/components/util/Cargo.toml b/components/util/Cargo.toml index 2070c9bb158f..1fc0ac21f610 100644 --- a/components/util/Cargo.toml +++ b/components/util/Cargo.toml @@ -30,9 +30,6 @@ git = "https://github.com/servo/rust-geom" [dependencies.layers] git = "https://github.com/servo/rust-layers" -[dependencies.task_info] -path = "../../support/rust-task_info" - [dependencies.string_cache] git = "https://github.com/servo/string-cache" @@ -46,8 +43,6 @@ git = "https://github.com/Kimundi/lazy-static.rs" bitflags = "*" libc = "*" rand = "*" -regex = "0.1.14" rustc-serialize = "0.3" text_writer = "0.1.1" time = "0.1.12" -url = "0.2.16" diff --git a/components/util/lib.rs b/components/util/lib.rs index 58b29b66b496..620686214679 100644 --- a/components/util/lib.rs +++ b/components/util/lib.rs @@ -12,7 +12,6 @@ #![feature(io)] #![feature(old_io)] #![feature(optin_builtin_traits)] -#![cfg_attr(target_os = "linux", feature(page_size, str_words))] #![feature(path)] #![feature(path_ext)] #![feature(plugin)] @@ -27,7 +26,6 @@ extern crate alloc; #[macro_use] extern crate bitflags; -extern crate collections; extern crate cssparser; extern crate geom; extern crate getopts; @@ -35,19 +33,10 @@ extern crate layers; extern crate libc; #[no_link] #[macro_use] extern crate cssparser; extern crate rand; -#[cfg(target_os="linux")] -extern crate regex; extern crate "rustc-serialize" as rustc_serialize; -#[cfg(target_os="macos")] -extern crate task_info; -extern crate "time" as std_time; extern crate text_writer; extern crate selectors; extern crate string_cache; -extern crate unicode; -extern crate url; - -extern crate lazy_static; pub use selectors::smallvec; @@ -70,7 +59,6 @@ pub mod resource_files; pub mod str; pub mod task; pub mod tid; -pub mod time; pub mod taskpool; pub mod task_state; pub mod vec; diff --git a/components/util/memory.rs b/components/util/memory.rs index 88379bcebfd6..4cefce3d0e70 100644 --- a/components/util/memory.rs +++ b/components/util/memory.rs @@ -2,23 +2,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -//! Memory profiling functions. +//! Data structure measurement. -use libc::{c_char,c_int,c_void,size_t}; -use std::borrow::ToOwned; -use std::cmp::Ordering; -use std::collections::HashMap; +use libc::{c_void, size_t}; use std::collections::LinkedList; -use std::ffi::CString; -use std::old_io::timer::sleep; -use std::mem::{size_of, transmute}; -use std::ptr::null_mut; +use std::mem::transmute; use std::sync::Arc; -use std::sync::mpsc::{Sender, channel, Receiver}; -use std::time::duration::Duration; -use task::spawn_named; -#[cfg(target_os="macos")] -use task_info::task_basic_info::{virtual_size,resident_size}; extern { // Get the size of a heap block. @@ -170,638 +159,3 @@ impl Drop for LinkedList2 { fn drop(&mut self) {} } -//--------------------------------------------------------------------------- - -#[derive(Clone)] -pub struct MemoryProfilerChan(pub Sender); - -impl MemoryProfilerChan { - pub fn send(&self, msg: MemoryProfilerMsg) { - let MemoryProfilerChan(ref c) = *self; - c.send(msg).unwrap(); - } -} - -/// An easy way to build a path for a report. -#[macro_export] -macro_rules! path { - ($($x:expr),*) => {{ vec![$( $x.to_owned() ),*] }} -} - -pub struct MemoryReport { - /// The identifying path for this report. - pub path: Vec, - - /// The size, in bytes. - pub size: u64, -} - -/// A channel through which memory reports can be sent. -#[derive(Clone)] -pub struct MemoryReportsChan(pub Sender>); - -impl MemoryReportsChan { - pub fn send(&self, report: Vec) { - let MemoryReportsChan(ref c) = *self; - c.send(report).unwrap(); - } -} - -/// A memory reporter is capable of measuring some data structure of interest. Because it needs -/// to be passed to and registered with the MemoryProfiler, it's typically a "small" (i.e. easily -/// cloneable) value that provides access to a "large" data structure, e.g. a channel that can -/// inject a request for measurements into the event queue associated with the "large" data -/// structure. -pub trait MemoryReporter { - /// Collect one or more memory reports. Returns true on success, and false on failure. - fn collect_reports(&self, reports_chan: MemoryReportsChan) -> bool; -} - -/// Messages that can be sent to the memory profiler thread. -pub enum MemoryProfilerMsg { - /// Register a MemoryReporter with the memory profiler. The String is only used to identify the - /// reporter so it can be unregistered later. The String must be distinct from that used by any - /// other registered reporter otherwise a panic will occur. - RegisterMemoryReporter(String, Box), - - /// Unregister a MemoryReporter with the memory profiler. The String must match the name given - /// when the reporter was registered. If the String does not match the name of a registered - /// reporter a panic will occur. - UnregisterMemoryReporter(String), - - /// Triggers printing of the memory profiling metrics. - Print, - - /// Tells the memory profiler to shut down. - Exit, -} - -pub struct MemoryProfiler { - /// The port through which messages are received. - pub port: Receiver, - - /// Registered memory reporters. - reporters: HashMap>, -} - -impl MemoryProfiler { - pub fn create(period: Option) -> MemoryProfilerChan { - let (chan, port) = channel(); - - // Create the timer thread if a period was provided. - if let Some(period) = period { - let period_ms = Duration::milliseconds((period * 1000f64) as i64); - let chan = chan.clone(); - spawn_named("Memory profiler timer".to_owned(), move || { - loop { - sleep(period_ms); - if chan.send(MemoryProfilerMsg::Print).is_err() { - break; - } - } - }); - } - - // Always spawn the memory profiler. If there is no timer thread it won't receive regular - // `Print` events, but it will still receive the other events. - spawn_named("Memory profiler".to_owned(), move || { - let mut memory_profiler = MemoryProfiler::new(port); - memory_profiler.start(); - }); - - let memory_profiler_chan = MemoryProfilerChan(chan); - - // Register the system memory reporter, which will run on the memory profiler's own thread. - // It never needs to be unregistered, because as long as the memory profiler is running the - // system memory reporter can make measurements. - let system_reporter = Box::new(SystemMemoryReporter); - memory_profiler_chan.send(MemoryProfilerMsg::RegisterMemoryReporter("system".to_owned(), - system_reporter)); - - memory_profiler_chan - } - - pub fn new(port: Receiver) -> MemoryProfiler { - MemoryProfiler { - port: port, - reporters: HashMap::new(), - } - } - - pub fn start(&mut self) { - loop { - match self.port.recv() { - Ok(msg) => { - if !self.handle_msg(msg) { - break - } - } - _ => break - } - } - } - - fn handle_msg(&mut self, msg: MemoryProfilerMsg) -> bool { - match msg { - MemoryProfilerMsg::RegisterMemoryReporter(name, reporter) => { - // Panic if it has already been registered. - let name_clone = name.clone(); - match self.reporters.insert(name, reporter) { - None => true, - Some(_) => - panic!(format!("RegisterMemoryReporter: '{}' name is already in use", - name_clone)), - } - }, - - MemoryProfilerMsg::UnregisterMemoryReporter(name) => { - // Panic if it hasn't previously been registered. - match self.reporters.remove(&name) { - Some(_) => true, - None => - panic!(format!("UnregisterMemoryReporter: '{}' name is unknown", &name)), - } - }, - - MemoryProfilerMsg::Print => { - self.handle_print_msg(); - true - }, - - MemoryProfilerMsg::Exit => false - } - } - - fn handle_print_msg(&self) { - println!("Begin memory reports"); - println!("|"); - - // Collect reports from memory reporters. - // - // This serializes the report-gathering. It might be worth creating a new scoped thread for - // each reporter once we have enough of them. - // - // If anything goes wrong with a reporter, we just skip it. - let mut forest = ReportsForest::new(); - for reporter in self.reporters.values() { - let (chan, port) = channel(); - if reporter.collect_reports(MemoryReportsChan(chan)) { - if let Ok(reports) = port.recv() { - for report in reports.iter() { - forest.insert(&report.path, report.size); - } - } - } - } - forest.print(); - - println!("|"); - println!("End memory reports"); - println!(""); - } -} - -/// A collection of one or more reports with the same initial path segment. A ReportsTree -/// containing a single node is described as "degenerate". -struct ReportsTree { - /// For leaf nodes, this is the sum of the sizes of all reports that mapped to this location. - /// For interior nodes, this is the sum of the sizes of all its child nodes. - size: u64, - - /// For leaf nodes, this is the count of all reports that mapped to this location. - /// For interor nodes, this is always zero. - count: u32, - - /// The segment from the report path that maps to this node. - path_seg: String, - - /// Child nodes. - children: Vec, -} - -impl ReportsTree { - fn new(path_seg: String) -> ReportsTree { - ReportsTree { - size: 0, - count: 0, - path_seg: path_seg, - children: vec![] - } - } - - // Searches the tree's children for a path_seg match, and returns the index if there is a - // match. - fn find_child(&self, path_seg: &String) -> Option { - for (i, child) in self.children.iter().enumerate() { - if child.path_seg == *path_seg { - return Some(i); - } - } - None - } - - // Insert the path and size into the tree, adding any nodes as necessary. - fn insert(&mut self, path: &[String], size: u64) { - let mut t: &mut ReportsTree = self; - for path_seg in path.iter() { - let i = match t.find_child(&path_seg) { - Some(i) => i, - None => { - let new_t = ReportsTree::new(path_seg.clone()); - t.children.push(new_t); - t.children.len() - 1 - }, - }; - let tmp = t; // this temporary is needed to satisfy the borrow checker - t = &mut tmp.children[i]; - } - - t.size += size; - t.count += 1; - } - - // Fill in sizes for interior nodes. Should only be done once all the reports have been - // inserted. - fn compute_interior_node_sizes(&mut self) -> u64 { - if !self.children.is_empty() { - // Interior node. Derive its size from its children. - if self.size != 0 { - // This will occur if e.g. we have paths ["a", "b"] and ["a", "b", "c"]. - panic!("one report's path is a sub-path of another report's path"); - } - for child in self.children.iter_mut() { - self.size += child.compute_interior_node_sizes(); - } - } - self.size - } - - fn print(&self, depth: i32) { - if !self.children.is_empty() { - assert_eq!(self.count, 0); - } - - let mut indent_str = String::new(); - for _ in range(0, depth) { - indent_str.push_str(" "); - } - - let mebi = 1024f64 * 1024f64; - let count_str = if self.count > 1 { format!(" {}", self.count) } else { "".to_owned() }; - println!("|{}{:8.2} MiB -- {}{}", - indent_str, (self.size as f64) / mebi, self.path_seg, count_str); - - for child in self.children.iter() { - child.print(depth + 1); - } - } -} - -/// A collection of ReportsTrees. It represents the data from multiple memory reports in a form -/// that's good to print. -struct ReportsForest { - trees: HashMap, -} - -impl ReportsForest { - fn new() -> ReportsForest { - ReportsForest { - trees: HashMap::new(), - } - } - - // Insert the path and size into the forest, adding any trees and nodes as necessary. - fn insert(&mut self, path: &[String], size: u64) { - // Get the right tree, creating it if necessary. - if !self.trees.contains_key(&path[0]) { - self.trees.insert(path[0].clone(), ReportsTree::new(path[0].clone())); - } - let t = self.trees.get_mut(&path[0]).unwrap(); - - // Use tail() because the 0th path segment was used to find the right tree in the forest. - t.insert(path.tail(), size); - } - - fn print(&mut self) { - // Fill in sizes of interior nodes. - for (_, tree) in self.trees.iter_mut() { - tree.compute_interior_node_sizes(); - } - - // Put the trees into a sorted vector. Primary sort: degenerate trees (those containing a - // single node) come after non-degenerate trees. Secondary sort: alphabetical order of the - // root node's path_seg. - let mut v = vec![]; - for (_, tree) in self.trees.iter() { - v.push(tree); - } - v.sort_by(|a, b| { - if a.children.is_empty() && !b.children.is_empty() { - Ordering::Greater - } else if !a.children.is_empty() && b.children.is_empty() { - Ordering::Less - } else { - a.path_seg.cmp(&b.path_seg) - } - }); - - // Print the forest. - for tree in v.iter() { - tree.print(0); - // Print a blank line after non-degenerate trees. - if !tree.children.is_empty() { - println!("|"); - } - } - } -} - -//--------------------------------------------------------------------------- - -/// Collects global measurements from the OS and heap allocators. -struct SystemMemoryReporter; - -impl MemoryReporter for SystemMemoryReporter { - fn collect_reports(&self, reports_chan: MemoryReportsChan) -> bool { - let mut reports = vec![]; - { - let mut report = |path, size| { - if let Some(size) = size { - reports.push(MemoryReport { path: path, size: size }); - } - }; - - // Virtual and physical memory usage, as reported by the OS. - report(path!["vsize"], get_vsize()); - report(path!["resident"], get_resident()); - - // Memory segments, as reported by the OS. - for seg in get_resident_segments().iter() { - report(path!["resident-according-to-smaps".to_owned(), seg.0.to_owned()], - Some(seg.1)); - } - - // Total number of bytes allocated by the application on the system - // heap. - report(path!["system-heap-allocated".to_owned()], get_system_heap_allocated()); - - // The descriptions of the following jemalloc measurements are taken - // directly from the jemalloc documentation. - - // "Total number of bytes allocated by the application." - report(path!["jemalloc-heap-allocated".to_owned()], - get_jemalloc_stat("stats.allocated")); - - // "Total number of bytes in active pages allocated by the application. - // This is a multiple of the page size, and greater than or equal to - // |stats.allocated|." - report(path!["jemalloc-heap-active"], get_jemalloc_stat("stats.active")); - - // "Total number of bytes in chunks mapped on behalf of the application. - // This is a multiple of the chunk size, and is at least as large as - // |stats.active|. This does not include inactive chunks." - report(path!["jemalloc-heap-mapped"], get_jemalloc_stat("stats.mapped")); - } - reports_chan.send(reports); - - true - } -} - - -#[cfg(target_os="linux")] -extern { - fn mallinfo() -> struct_mallinfo; -} - -#[cfg(target_os="linux")] -#[repr(C)] -pub struct struct_mallinfo { - arena: c_int, - ordblks: c_int, - smblks: c_int, - hblks: c_int, - hblkhd: c_int, - usmblks: c_int, - fsmblks: c_int, - uordblks: c_int, - fordblks: c_int, - keepcost: c_int, -} - -#[cfg(target_os="linux")] -fn get_system_heap_allocated() -> Option { - let mut info: struct_mallinfo; - unsafe { - info = mallinfo(); - } - // The documentation in the glibc man page makes it sound like |uordblks| - // would suffice, but that only gets the small allocations that are put in - // the brk heap. We need |hblkhd| as well to get the larger allocations - // that are mmapped. - Some((info.hblkhd + info.uordblks) as u64) -} - -#[cfg(not(target_os="linux"))] -fn get_system_heap_allocated() -> Option { - None -} - -extern { - fn je_mallctl(name: *const c_char, oldp: *mut c_void, oldlenp: *mut size_t, - newp: *mut c_void, newlen: size_t) -> c_int; -} - -fn get_jemalloc_stat(value_name: &str) -> Option { - // Before we request the measurement of interest, we first send an "epoch" - // request. Without that jemalloc gives cached statistics(!) which can be - // highly inaccurate. - let epoch_name = "epoch"; - let epoch_c_name = CString::new(epoch_name).unwrap(); - let mut epoch: u64 = 0; - let epoch_ptr = &mut epoch as *mut _ as *mut c_void; - let mut epoch_len = size_of::() as size_t; - - let value_c_name = CString::new(value_name).unwrap(); - let mut value: size_t = 0; - let value_ptr = &mut value as *mut _ as *mut c_void; - let mut value_len = size_of::() as size_t; - - // Using the same values for the `old` and `new` parameters is enough - // to get the statistics updated. - let rv = unsafe { - je_mallctl(epoch_c_name.as_ptr(), epoch_ptr, &mut epoch_len, epoch_ptr, - epoch_len) - }; - if rv != 0 { - return None; - } - - let rv = unsafe { - je_mallctl(value_c_name.as_ptr(), value_ptr, &mut value_len, - null_mut(), 0) - }; - if rv != 0 { - return None; - } - - Some(value as u64) -} - -// Like std::macros::try!, but for Option<>. -macro_rules! option_try( - ($e:expr) => (match $e { Some(e) => e, None => return None }) -); - -#[cfg(target_os="linux")] -fn get_proc_self_statm_field(field: usize) -> Option { - use std::fs::File; - use std::io::Read; - - let mut f = option_try!(File::open("/proc/self/statm").ok()); - let mut contents = String::new(); - option_try!(f.read_to_string(&mut contents).ok()); - let s = option_try!(contents.words().nth(field)); - let npages = option_try!(s.parse::().ok()); - Some(npages * (::std::env::page_size() as u64)) -} - -#[cfg(target_os="linux")] -fn get_vsize() -> Option { - get_proc_self_statm_field(0) -} - -#[cfg(target_os="linux")] -fn get_resident() -> Option { - get_proc_self_statm_field(1) -} - -#[cfg(target_os="macos")] -fn get_vsize() -> Option { - virtual_size() -} - -#[cfg(target_os="macos")] -fn get_resident() -> Option { - resident_size() -} - -#[cfg(not(any(target_os="linux", target_os = "macos")))] -fn get_vsize() -> Option { - None -} - -#[cfg(not(any(target_os="linux", target_os = "macos")))] -fn get_resident() -> Option { - None -} - -#[cfg(target_os="linux")] -fn get_resident_segments() -> Vec<(String, u64)> { - use regex::Regex; - use std::collections::HashMap; - use std::collections::hash_map::Entry; - use std::fs::File; - use std::io::{BufReader, BufReadExt}; - - // The first line of an entry in /proc//smaps looks just like an entry - // in /proc//maps: - // - // address perms offset dev inode pathname - // 02366000-025d8000 rw-p 00000000 00:00 0 [heap] - // - // Each of the following lines contains a key and a value, separated - // by ": ", where the key does not contain either of those characters. - // For example: - // - // Rss: 132 kB - - let f = match File::open("/proc/self/smaps") { - Ok(f) => BufReader::new(f), - Err(_) => return vec![], - }; - - let seg_re = Regex::new( - r"^[:xdigit:]+-[:xdigit:]+ (....) [:xdigit:]+ [:xdigit:]+:[:xdigit:]+ \d+ +(.*)").unwrap(); - let rss_re = Regex::new(r"^Rss: +(\d+) kB").unwrap(); - - // We record each segment's resident size. - let mut seg_map: HashMap = HashMap::new(); - - #[derive(PartialEq)] - enum LookingFor { Segment, Rss } - let mut looking_for = LookingFor::Segment; - - let mut curr_seg_name = String::new(); - - // Parse the file. - for line in f.lines() { - let line = match line { - Ok(line) => line, - Err(_) => continue, - }; - if looking_for == LookingFor::Segment { - // Look for a segment info line. - let cap = match seg_re.captures(line.as_slice()) { - Some(cap) => cap, - None => continue, - }; - let perms = cap.at(1).unwrap(); - let pathname = cap.at(2).unwrap(); - - // Construct the segment name from its pathname and permissions. - curr_seg_name.clear(); - if pathname == "" || pathname.starts_with("[stack:") { - // Anonymous memory. Entries marked with "[stack:nnn]" - // look like thread stacks but they may include other - // anonymous mappings, so we can't trust them and just - // treat them as entirely anonymous. - curr_seg_name.push_str("anonymous"); - } else { - curr_seg_name.push_str(pathname); - } - curr_seg_name.push_str(" ("); - curr_seg_name.push_str(perms); - curr_seg_name.push_str(")"); - - looking_for = LookingFor::Rss; - } else { - // Look for an "Rss:" line. - let cap = match rss_re.captures(line.as_slice()) { - Some(cap) => cap, - None => continue, - }; - let rss = cap.at(1).unwrap().parse::().unwrap() * 1024; - - if rss > 0 { - // Aggregate small segments into "other". - let seg_name = if rss < 512 * 1024 { - "other".to_owned() - } else { - curr_seg_name.clone() - }; - match seg_map.entry(seg_name) { - Entry::Vacant(entry) => { entry.insert(rss); }, - Entry::Occupied(mut entry) => *entry.get_mut() += rss, - } - } - - looking_for = LookingFor::Segment; - } - } - - let mut segs: Vec<(String, u64)> = seg_map.into_iter().collect(); - - // Note that the sum of all these segments' RSS values differs from the "resident" measurement - // obtained via /proc//statm in get_resident(). It's unclear why this difference occurs; - // for some processes the measurements match, but for Servo they do not. - segs.sort_by(|&(_, rss1), &(_, rss2)| rss2.cmp(&rss1)); - - segs -} - -#[cfg(not(target_os="linux"))] -fn get_resident_segments() -> Vec<(String, u64)> { - vec![] -} - diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 555abedf97cf..c71526670539 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -99,6 +99,7 @@ dependencies = [ "msg 0.0.1", "net 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "script_traits 0.0.1", "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -314,6 +315,7 @@ dependencies = [ "net 0.0.1", "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "skia 0.0.20130412 (git+https://github.com/servo/skia?branch=upstream-2014-06-16)", @@ -516,6 +518,7 @@ dependencies = [ "net 0.0.1", "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "script 0.0.1", "script_traits 0.0.1", @@ -534,6 +537,7 @@ dependencies = [ "gfx 0.0.1", "msg 0.0.1", "net 0.0.1", + "profile 0.0.1", "script_traits 0.0.1", "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", @@ -615,6 +619,7 @@ dependencies = [ "hyper 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "regex 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "regex_macros 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -703,6 +708,18 @@ name = "png-sys" version = "1.6.16" source = "git+https://github.com/servo/rust-png#1d9c59c97598014860077f372443ae98b35ff4d9" +[[package]] +name = "profile" +version = "0.0.1" +dependencies = [ + "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "task_info 0.0.1", + "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", + "util 0.0.1", +] + [[package]] name = "rand" version = "0.1.4" @@ -748,6 +765,7 @@ dependencies = [ "msg 0.0.1", "net 0.0.1", "plugins 0.0.1", + "profile 0.0.1", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", @@ -798,6 +816,7 @@ dependencies = [ "msg 0.0.1", "net 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "script 0.0.1", "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -912,15 +931,12 @@ dependencies = [ "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "plugins 0.0.1", "rand 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache_plugin 0.0.0 (git+https://github.com/servo/string-cache)", - "task_info 0.0.1", "text_writer 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index 47ec887aa888..7dcc0a6d7b94 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -77,6 +77,7 @@ dependencies = [ "msg 0.0.1", "net 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "script_traits 0.0.1", "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -284,6 +285,7 @@ dependencies = [ "net 0.0.1", "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "skia 0.0.20130412 (git+https://github.com/servo/skia?branch=upstream-2014-06-16)", @@ -440,6 +442,7 @@ dependencies = [ "net 0.0.1", "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "script 0.0.1", "script_traits 0.0.1", @@ -458,6 +461,7 @@ dependencies = [ "gfx 0.0.1", "msg 0.0.1", "net 0.0.1", + "profile 0.0.1", "script_traits 0.0.1", "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", @@ -539,6 +543,7 @@ dependencies = [ "hyper 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "regex 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "regex_macros 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -627,6 +632,18 @@ name = "png-sys" version = "1.6.16" source = "git+https://github.com/servo/rust-png#1d9c59c97598014860077f372443ae98b35ff4d9" +[[package]] +name = "profile" +version = "0.0.1" +dependencies = [ + "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "task_info 0.0.1", + "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", + "util 0.0.1", +] + [[package]] name = "rand" version = "0.1.4" @@ -672,6 +689,7 @@ dependencies = [ "msg 0.0.1", "net 0.0.1", "plugins 0.0.1", + "profile 0.0.1", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", @@ -721,6 +739,7 @@ dependencies = [ "msg 0.0.1", "net 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "profile 0.0.1", "script 0.0.1", "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -827,15 +846,12 @@ dependencies = [ "libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "plugins 0.0.1", "rand 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", "string_cache 0.0.0 (git+https://github.com/servo/string-cache)", "string_cache_plugin 0.0.0 (git+https://github.com/servo/string-cache)", - "task_info 0.0.1", "text_writer 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "url 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/ports/gonk/Cargo.toml b/ports/gonk/Cargo.toml index 5889da21f272..4ab7d59b3772 100644 --- a/ports/gonk/Cargo.toml +++ b/ports/gonk/Cargo.toml @@ -36,6 +36,9 @@ path = "../../components/devtools" path = "../../components/servo" default-features = false +[dependencies.profile] +path = "../../components/profile" + [dependencies.util] path = "../../components/util" diff --git a/ports/gonk/src/lib.rs b/ports/gonk/src/lib.rs index 4e7152ea6b8f..431f340d697a 100644 --- a/ports/gonk/src/lib.rs +++ b/ports/gonk/src/lib.rs @@ -22,6 +22,7 @@ extern crate script; extern crate layout; extern crate gfx; extern crate libc; +extern crate profile; extern crate url; use compositing::CompositorEventListener; @@ -45,9 +46,9 @@ use net::resource_task::new_resource_task; #[cfg(not(test))] use gfx::font_cache_task::FontCacheTask; #[cfg(not(test))] -use util::time::TimeProfiler; +use profile::mem::MemoryProfiler; #[cfg(not(test))] -use util::memory::MemoryProfiler; +use profile::time::TimeProfiler; #[cfg(not(test))] use util::opts; #[cfg(not(test))]