aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/compositing/compositor.rs12
-rw-r--r--components/layout/layout_task.rs13
-rw-r--r--components/profile/mem.rs79
-rw-r--r--components/profile_traits/mem.rs36
-rw-r--r--components/script/script_task.rs46
5 files changed, 161 insertions, 25 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index 468301317b8..e0caae3f98d 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -39,7 +39,7 @@ use msg::constellation_msg::{ConstellationChan, NavigationDirection};
use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData};
use msg::constellation_msg::{PipelineId, WindowSizeData};
use png;
-use profile_traits::mem::{self, Reporter, ReporterRequest};
+use profile_traits::mem::{self, Reporter, ReporterRequest, ReportKind};
use profile_traits::time::{self, ProfilerCategory, profile};
use script_traits::{ConstellationControlMsg, LayoutControlMsg, ScriptControlChan};
use std::collections::HashMap;
@@ -501,11 +501,17 @@ impl<Window: WindowMethods> IOCompositor<Window> {
(Msg::CollectMemoryReports(reports_chan), ShutdownState::NotShuttingDown) => {
let mut reports = vec![];
let name = "compositor-task";
+ // These are both `ExplicitUnknownLocationSize` because the memory might be in the
+ // GPU or on the heap.
reports.push(mem::Report {
- path: path![name, "surface-map"], size: self.surface_map.mem(),
+ path: path![name, "surface-map"],
+ kind: ReportKind::ExplicitUnknownLocationSize,
+ size: self.surface_map.mem(),
});
reports.push(mem::Report {
- path: path![name, "layer-tree"], size: self.scene.get_memory_usage(),
+ path: path![name, "layer-tree"],
+ kind: ReportKind::ExplicitUnknownLocationSize,
+ size: self.scene.get_memory_usage(),
});
reports_chan.send(reports);
}
diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs
index 363e35cc72e..803b8fb1a0a 100644
--- a/components/layout/layout_task.rs
+++ b/components/layout/layout_task.rs
@@ -46,7 +46,7 @@ use log;
use msg::compositor_msg::{Epoch, ScrollPolicy, LayerId};
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType, PipelineId};
-use profile_traits::mem::{self, Report, Reporter, ReporterRequest, ReportsChan};
+use profile_traits::mem::{self, Report, Reporter, ReporterRequest, ReportKind, ReportsChan};
use profile_traits::time::{self, ProfilerMetadata, profile};
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
use net_traits::{load_bytes_iter, PendingAsyncLoad};
@@ -609,13 +609,15 @@ impl LayoutTask {
let rw_data = self.lock_rw_data(possibly_locked_rw_data);
let stacking_context = rw_data.stacking_context.as_ref();
reports.push(Report {
- path: path!["pages", format!("url({})", self.url), "layout-task", "display-list"],
+ path: path![format!("url({})", self.url), "layout-task", "display-list"],
+ kind: ReportKind::ExplicitJemallocHeapSize,
size: stacking_context.map_or(0, |sc| sc.heap_size_of_children()),
});
// The LayoutTask has a context in TLS...
reports.push(Report {
- path: path!["pages", format!("url({})", self.url), "layout-task", "local-context"],
+ path: path![format!("url({})", self.url), "layout-task", "local-context"],
+ kind: ReportKind::ExplicitJemallocHeapSize,
size: heap_size_of_local_context(),
});
@@ -624,9 +626,10 @@ impl LayoutTask {
let sizes = traversal.heap_size_of_tls(heap_size_of_local_context);
for (i, size) in sizes.iter().enumerate() {
reports.push(Report {
- path: path!["pages", format!("url({})", self.url),
+ path: path![format!("url({})", self.url),
format!("layout-worker-{}-local-context", i)],
- size: *size
+ kind: ReportKind::ExplicitJemallocHeapSize,
+ size: *size,
});
}
}
diff --git a/components/profile/mem.rs b/components/profile/mem.rs
index 9c4a5ac710f..ba0d9e168e6 100644
--- a/components/profile/mem.rs
+++ b/components/profile/mem.rs
@@ -6,7 +6,8 @@
use ipc_channel::ipc::{self, IpcReceiver};
use ipc_channel::router::ROUTER;
-use profile_traits::mem::{ProfilerChan, ProfilerMsg, Reporter, ReporterRequest, ReportsChan};
+use profile_traits::mem::{ProfilerChan, ProfilerMsg, Reporter, ReporterRequest, ReportKind};
+use profile_traits::mem::ReportsChan;
use std::borrow::ToOwned;
use std::cmp::Ordering;
use std::collections::HashMap;
@@ -21,6 +22,9 @@ pub struct Profiler {
reporters: HashMap<String, Reporter>,
}
+const JEMALLOC_HEAP_ALLOCATED_STR: &'static str = "jemalloc-heap-allocated";
+const SYSTEM_HEAP_ALLOCATED_STR: &'static str = "system-heap-allocated";
+
impl Profiler {
pub fn create(period: Option<f64>) -> ProfilerChan {
let (chan, port) = ipc::channel().unwrap();
@@ -122,16 +126,72 @@ impl Profiler {
// each reporter once we have enough of them.
//
// If anything goes wrong with a reporter, we just skip it.
+ //
+ // We also track the total memory reported on the jemalloc heap and the system heap, and
+ // use that to compute the special "jemalloc-heap-unclassified" and
+ // "system-heap-unclassified" values.
+
let mut forest = ReportsForest::new();
+
+ let mut jemalloc_heap_reported_size = 0;
+ let mut system_heap_reported_size = 0;
+
+ let mut jemalloc_heap_allocated_size: Option<usize> = None;
+ let mut system_heap_allocated_size: Option<usize> = None;
+
for reporter in self.reporters.values() {
let (chan, port) = ipc::channel().unwrap();
reporter.collect_reports(ReportsChan(chan));
- if let Ok(reports) = port.recv() {
- for report in reports.iter() {
+ if let Ok(mut reports) = port.recv() {
+
+ for report in reports.iter_mut() {
+
+ // Add "explicit" to the start of the path, when appropriate.
+ match report.kind {
+ ReportKind::ExplicitJemallocHeapSize |
+ ReportKind::ExplicitSystemHeapSize |
+ ReportKind::ExplicitNonHeapSize |
+ ReportKind::ExplicitUnknownLocationSize =>
+ report.path.insert(0, String::from("explicit")),
+ ReportKind::NonExplicitSize => {},
+ }
+
+ // Update the reported fractions of the heaps, when appropriate.
+ match report.kind {
+ ReportKind::ExplicitJemallocHeapSize =>
+ jemalloc_heap_reported_size += report.size,
+ ReportKind::ExplicitSystemHeapSize =>
+ system_heap_reported_size += report.size,
+ _ => {},
+ }
+
+ // Record total size of the heaps, when we see them.
+ if report.path.len() == 1 {
+ if report.path[0] == JEMALLOC_HEAP_ALLOCATED_STR {
+ assert!(jemalloc_heap_allocated_size.is_none());
+ jemalloc_heap_allocated_size = Some(report.size);
+ } else if report.path[0] == SYSTEM_HEAP_ALLOCATED_STR {
+ assert!(system_heap_allocated_size.is_none());
+ system_heap_allocated_size = Some(report.size);
+ }
+ }
+
+ // Insert the report.
forest.insert(&report.path, report.size);
}
}
}
+
+ // Compute and insert the heap-unclassified values.
+ if let Some(jemalloc_heap_allocated_size) = jemalloc_heap_allocated_size {
+ forest.insert(&path!["explicit", "jemalloc-heap-unclassified"],
+ jemalloc_heap_allocated_size - jemalloc_heap_reported_size);
+ }
+ if let Some(system_heap_allocated_size) = system_heap_allocated_size {
+ forest.insert(&path!["explicit", "system-heap-unclassified"],
+ system_heap_allocated_size - system_heap_reported_size);
+ }
+
forest.print();
println!("|");
@@ -301,11 +361,12 @@ impl ReportsForest {
mod system_reporter {
use libc::{c_char, c_int, c_void, size_t};
- use profile_traits::mem::{Report, ReporterRequest};
+ use profile_traits::mem::{Report, ReporterRequest, ReportKind};
use std::borrow::ToOwned;
use std::ffi::CString;
use std::mem::size_of;
use std::ptr::null_mut;
+ use super::{JEMALLOC_HEAP_ALLOCATED_STR, SYSTEM_HEAP_ALLOCATED_STR};
#[cfg(target_os="macos")]
use task_info::task_basic_info::{virtual_size, resident_size};
@@ -315,7 +376,11 @@ mod system_reporter {
{
let mut report = |path, size| {
if let Some(size) = size {
- reports.push(Report { path: path, size: size });
+ reports.push(Report {
+ path: path,
+ kind: ReportKind::NonExplicitSize,
+ size: size,
+ });
}
};
@@ -330,13 +395,13 @@ mod system_reporter {
// Total number of bytes allocated by the application on the system
// heap.
- report(path!["system-heap-allocated"], get_system_heap_allocated());
+ report(path![SYSTEM_HEAP_ALLOCATED_STR], 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"));
+ report(path![JEMALLOC_HEAP_ALLOCATED_STR], 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
diff --git a/components/profile_traits/mem.rs b/components/profile_traits/mem.rs
index 0269e8f29d7..19470576aad 100644
--- a/components/profile_traits/mem.rs
+++ b/components/profile_traits/mem.rs
@@ -23,12 +23,48 @@ impl ProfilerChan {
}
}
+/// The various kinds of memory measurement.
+///
+/// Here "explicit" means explicit memory allocations done by the application. It includes
+/// allocations made at the OS level (via functions such as VirtualAlloc, vm_allocate, and mmap),
+/// allocations made at the heap allocation level (via functions such as malloc, calloc, realloc,
+/// memalign, operator new, and operator new[]) and where possible, the overhead of the heap
+/// allocator itself. It excludes memory that is mapped implicitly such as code and data segments,
+/// and thread stacks. "explicit" is not guaranteed to cover every explicit allocation, but it does
+/// cover most (including the entire heap), and therefore it is the single best number to focus on
+/// when trying to reduce memory usage.
+#[derive(Deserialize, Serialize)]
+pub enum ReportKind {
+ /// A size measurement for an explicit allocation on the jemalloc heap. This should be used
+ /// for any measurements done via the `HeapSizeOf` trait.
+ ExplicitJemallocHeapSize,
+
+ /// A size measurement for an explicit allocation on the system heap. Only likely to be used
+ /// for external C or C++ libraries that don't use jemalloc.
+ ExplicitSystemHeapSize,
+
+ /// A size measurement for an explicit allocation not on the heap, e.g. via mmap().
+ ExplicitNonHeapSize,
+
+ /// A size measurement for an explicit allocation whose location is unknown or uncertain.
+ ExplicitUnknownLocationSize,
+
+ /// A size measurement for a non-explicit allocation. This kind is used for global
+ /// measurements such as "resident" and "vsize", and also for measurements that cross-cut the
+ /// measurements grouped under "explicit", e.g. by grouping those measurements in a way that's
+ /// different to how they are grouped under "explicit".
+ NonExplicitSize,
+}
+
/// A single memory-related measurement.
#[derive(Deserialize, Serialize)]
pub struct Report {
/// The identifying path for this report.
pub path: Vec<String>,
+ /// The report kind.
+ pub kind: ReportKind,
+
/// The size, in bytes.
pub size: usize,
}
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index d9560e40911..e34e93008d2 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -70,7 +70,7 @@ use net_traits::{ResourceTask, LoadConsumer, ControlMsg, Metadata};
use net_traits::LoadData as NetLoadData;
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageCacheResult};
use net_traits::storage_task::StorageTask;
-use profile_traits::mem::{self, Report, Reporter, ReporterRequest, ReportsChan};
+use profile_traits::mem::{self, Report, Reporter, ReporterRequest, ReportKind, ReportsChan};
use string_cache::Atom;
use util::str::DOMString;
use util::task::spawn_named_with_send_on_failure;
@@ -1042,18 +1042,44 @@ impl ScriptTask {
let rt = JS_GetRuntime(cx);
let mut stats = ::std::mem::zeroed();
if CollectServoSizes(rt, &mut stats) {
- let mut report = |mut path_suffix, size| {
- let mut path = path!["pages", path_seg, "js"];
+ let mut report = |mut path_suffix, kind, size| {
+ let mut path = path![path_seg, "js"];
path.append(&mut path_suffix);
- reports.push(Report { path: path, size: size as usize })
+ reports.push(Report {
+ path: path,
+ kind: kind,
+ size: size as usize,
+ })
};
- report(path!["gc-heap", "used"], stats.gcHeapUsed);
- report(path!["gc-heap", "unused"], stats.gcHeapUnused);
- report(path!["gc-heap", "admin"], stats.gcHeapAdmin);
- report(path!["gc-heap", "decommitted"], stats.gcHeapDecommitted);
- report(path!["malloc-heap"], stats.mallocHeap);
- report(path!["non-heap"], stats.nonHeap);
+ // A note about possibly confusing terminology: the JS GC "heap" is allocated via
+ // mmap/VirtualAlloc, which means it's not on the malloc "heap", so we use
+ // `ExplicitNonHeapSize` as its kind.
+
+ report(path!["gc-heap", "used"],
+ ReportKind::ExplicitNonHeapSize,
+ stats.gcHeapUsed);
+
+ report(path!["gc-heap", "unused"],
+ ReportKind::ExplicitNonHeapSize,
+ stats.gcHeapUnused);
+
+ report(path!["gc-heap", "admin"],
+ ReportKind::ExplicitNonHeapSize,
+ stats.gcHeapAdmin);
+
+ report(path!["gc-heap", "decommitted"],
+ ReportKind::ExplicitNonHeapSize,
+ stats.gcHeapDecommitted);
+
+ // SpiderMonkey uses the system heap, not jemalloc.
+ report(path!["malloc-heap"],
+ ReportKind::ExplicitSystemHeapSize,
+ stats.mallocHeap);
+
+ report(path!["non-heap"],
+ ReportKind::ExplicitNonHeapSize,
+ stats.nonHeap);
}
}
reports