aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <metajack+bors@gmail.com>2015-03-24 03:15:49 -0600
committerbors-servo <metajack+bors@gmail.com>2015-03-24 03:15:49 -0600
commitd1268ec9c6633684270015e7b2619181aeb47b8b (patch)
tree800918f117f1c696d53f694d7bf7a2c72b0ba554
parent2f85c5bb502c2582d34772db979e27c741ee76e3 (diff)
parent52447ccd9b1889b21d5a5c8057a72e98a5ee96ea (diff)
downloadservo-d1268ec9c6633684270015e7b2619181aeb47b8b.tar.gz
servo-d1268ec9c6633684270015e7b2619181aeb47b8b.zip
auto merge of #5335 : nnethercote/servo/profile-crate, r=glennw
- Most of util::memory has been moved into profile::mem, though the `SizeOf` trait and related things remain in util::memory. The `SystemMemoryReporter` code is now in a submodule profile::mem::system_reporter. - util::time has been moved entirely into profile::time.
-rw-r--r--components/compositing/Cargo.toml5
-rw-r--r--components/compositing/compositor.rs19
-rw-r--r--components/compositing/compositor_task.rs8
-rw-r--r--components/compositing/constellation.rs4
-rw-r--r--components/compositing/headless.rs10
-rw-r--r--components/compositing/lib.rs1
-rw-r--r--components/compositing/pipeline.rs6
-rw-r--r--components/gfx/Cargo.toml3
-rw-r--r--components/gfx/lib.rs1
-rw-r--r--components/gfx/paint_task.rs10
-rw-r--r--components/layout/Cargo.toml3
-rw-r--r--components/layout/layout_task.rs6
-rw-r--r--components/layout/lib.rs2
-rw-r--r--components/layout/parallel.rs6
-rw-r--r--components/layout_traits/Cargo.toml3
-rw-r--r--components/layout_traits/lib.rs7
-rw-r--r--components/net/Cargo.toml3
-rw-r--r--components/net/image_cache_task.rs10
-rw-r--r--components/net/lib.rs1
-rw-r--r--components/profile/Cargo.toml20
-rw-r--r--components/profile/lib.rs27
-rw-r--r--components/profile/mem.rs658
-rw-r--r--components/profile/time.rs (renamed from components/util/time.rs)2
-rw-r--r--components/script/Cargo.toml3
-rw-r--r--components/script/layout_interface.rs6
-rw-r--r--components/script/lib.rs1
-rw-r--r--components/servo/Cargo.lock22
-rw-r--r--components/servo/Cargo.toml3
-rw-r--r--components/servo/lib.rs5
-rw-r--r--components/util/Cargo.toml5
-rw-r--r--components/util/lib.rs12
-rw-r--r--components/util/memory.rs652
-rw-r--r--ports/cef/Cargo.lock22
-rw-r--r--ports/gonk/Cargo.lock22
-rw-r--r--ports/gonk/Cargo.toml3
-rw-r--r--ports/gonk/src/lib.rs5
36 files changed, 850 insertions, 726 deletions
diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml
index dbbc9f4f7f8..a24069ab946 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 e865c61be49..8a80947f1cf 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<Window: WindowMethods> {
@@ -869,7 +870,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
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<Window> CompositorEventListener for IOCompositor<Window> 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 3c615175c32..a9b4fb68475 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 f822a9e19a5..228211e3ef8 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 18cf76961a7..3f38099d83b 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 77c389d8607..6ab2e313437 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 52771c75bcf..0d8e6261d25 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 ee9d4990da9..bb213624240 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 a2cb03d33a6..0be282fb8f6 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 db281e1acac..8548161d165 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 733985cefde..2eee5eb3d4a 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 08658f05d03..2c15e643b9c 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 2ff8502f7f1..a0498e2464f 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 5562db5e10d..e0effe6e6fc 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 4922db6639b..c099a8b3541 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 66d0ccc624c..f8b9c3f263f 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 440bed882e6..8f8c43b4139 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 ecb6b4ae396..43a0d085507 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<resource_task::ProgressMsg>) { }
diff --git a/components/net/lib.rs b/components/net/lib.rs
index 410e8ab1556..4933d64f0e6 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 00000000000..de9fb13a597
--- /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 00000000000..c8ffdc72d17
--- /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 00000000000..163a255659d
--- /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<MemoryProfilerMsg>);
+
+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<String>,
+
+ /// The size, in bytes.
+ pub size: u64,
+}
+
+/// A channel through which memory reports can be sent.
+#[derive(Clone)]
+pub struct MemoryReportsChan(pub Sender<Vec<MemoryReport>>);
+
+impl MemoryReportsChan {
+ pub fn send(&self, report: Vec<MemoryReport>) {
+ 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<MemoryReporter + Send>),
+
+ /// 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<MemoryProfilerMsg>,
+
+ /// Registered memory reporters.
+ reporters: HashMap<String, Box<MemoryReporter + Send>>,
+}
+
+impl MemoryProfiler {
+ pub fn create(period: Option<f64>) -> 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<MemoryProfilerMsg>) -> 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<ReportsTree>,
+}
+
+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<usize> {
+ 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<String, ReportsTree>,
+}
+
+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<u64> {
+ 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<u64> {
+ 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<u64> {
+ // 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::<u64>() 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::<size_t>() 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<u64> {
+ 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::<u64>().ok());
+ Some(npages * (::std::env::page_size() as u64))
+ }
+
+ #[cfg(target_os="linux")]
+ fn get_vsize() -> Option<u64> {
+ get_proc_self_statm_field(0)
+ }
+
+ #[cfg(target_os="linux")]
+ fn get_resident() -> Option<u64> {
+ get_proc_self_statm_field(1)
+ }
+
+ #[cfg(target_os="macos")]
+ fn get_vsize() -> Option<u64> {
+ virtual_size()
+ }
+
+ #[cfg(target_os="macos")]
+ fn get_resident() -> Option<u64> {
+ resident_size()
+ }
+
+ #[cfg(not(any(target_os="linux", target_os = "macos")))]
+ fn get_vsize() -> Option<u64> {
+ None
+ }
+
+ #[cfg(not(any(target_os="linux", target_os = "macos")))]
+ fn get_resident() -> Option<u64> {
+ 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/<pid>/smaps looks just like an entry
+ // in /proc/<pid>/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<String, u64> = 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::<u64>().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/<pid>/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
index 1cae21719d4..fcd83d8093a 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 d7f1b2916b5..ac910ecb32a 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 52b5aed1b43..b24adf944f6 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 5895127b552..44ac6f1856c 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 3e289cf5f2a..bc520d62f3e 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)",
@@ -698,6 +704,18 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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 5dfbbe8696c..64e7f060773 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 0312fca761e..bc86b6a4215 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 2070c9bb158..1fc0ac21f61 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 58b29b66b49..62068621467 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 88379bcebfd..4cefce3d0e7 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<T> Drop for LinkedList2<T> {
fn drop(&mut self) {}
}
-//---------------------------------------------------------------------------
-
-#[derive(Clone)]
-pub struct MemoryProfilerChan(pub Sender<MemoryProfilerMsg>);
-
-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<String>,
-
- /// The size, in bytes.
- pub size: u64,
-}
-
-/// A channel through which memory reports can be sent.
-#[derive(Clone)]
-pub struct MemoryReportsChan(pub Sender<Vec<MemoryReport>>);
-
-impl MemoryReportsChan {
- pub fn send(&self, report: Vec<MemoryReport>) {
- 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<MemoryReporter + Send>),
-
- /// 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<MemoryProfilerMsg>,
-
- /// Registered memory reporters.
- reporters: HashMap<String, Box<MemoryReporter + Send>>,
-}
-
-impl MemoryProfiler {
- pub fn create(period: Option<f64>) -> 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<MemoryProfilerMsg>) -> 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<ReportsTree>,
-}
-
-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<usize> {
- 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<String, ReportsTree>,
-}
-
-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<u64> {
- 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<u64> {
- 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<u64> {
- // 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::<u64>() 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::<size_t>() 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<u64> {
- 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::<u64>().ok());
- Some(npages * (::std::env::page_size() as u64))
-}
-
-#[cfg(target_os="linux")]
-fn get_vsize() -> Option<u64> {
- get_proc_self_statm_field(0)
-}
-
-#[cfg(target_os="linux")]
-fn get_resident() -> Option<u64> {
- get_proc_self_statm_field(1)
-}
-
-#[cfg(target_os="macos")]
-fn get_vsize() -> Option<u64> {
- virtual_size()
-}
-
-#[cfg(target_os="macos")]
-fn get_resident() -> Option<u64> {
- resident_size()
-}
-
-#[cfg(not(any(target_os="linux", target_os = "macos")))]
-fn get_vsize() -> Option<u64> {
- None
-}
-
-#[cfg(not(any(target_os="linux", target_os = "macos")))]
-fn get_resident() -> Option<u64> {
- 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/<pid>/smaps looks just like an entry
- // in /proc/<pid>/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<String, u64> = 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::<u64>().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/<pid>/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 555abedf97c..c7152667053 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)",
@@ -704,6 +709,18 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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 47ec887aa88..7dcc0a6d7b9 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)",
@@ -628,6 +633,18 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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 5889da21f27..4ab7d59b377 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 4e7152ea6b8..431f340d697 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))]