diff options
Diffstat (limited to 'components/shared/profile/mem.rs')
-rw-r--r-- | components/shared/profile/mem.rs | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/components/shared/profile/mem.rs b/components/shared/profile/mem.rs new file mode 100644 index 00000000000..87b6f26597c --- /dev/null +++ b/components/shared/profile/mem.rs @@ -0,0 +1,211 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +//! APIs for memory profiling. + +#![deny(missing_docs)] + +use std::marker::Send; + +use crossbeam_channel::Sender; +use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::router::ROUTER; +use log::warn; +use serde::{Deserialize, Serialize}; + +/// A trait to abstract away the various kinds of message senders we use. +pub trait OpaqueSender<T> { + /// Send a message. + fn send(&self, message: T); +} + +impl<T> OpaqueSender<T> for Sender<T> { + fn send(&self, message: T) { + if let Err(e) = Sender::send(self, message) { + warn!( + "Error communicating with the target thread from the profiler: {:?}", + e + ); + } + } +} + +impl<T> OpaqueSender<T> for IpcSender<T> +where + T: serde::Serialize, +{ + fn send(&self, message: T) { + if let Err(e) = IpcSender::send(self, message) { + warn!( + "Error communicating with the target thread from the profiler: {}", + e + ); + } + } +} + +/// Front-end representation of the profiler used to communicate with the +/// profiler. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ProfilerChan(pub IpcSender<ProfilerMsg>); + +impl ProfilerChan { + /// Send `msg` on this `IpcSender`. + /// + /// Warns if the send fails. + pub fn send(&self, msg: ProfilerMsg) { + if let Err(e) = self.0.send(msg) { + warn!("Error communicating with the memory profiler thread: {}", e); + } + } + + /// Runs `f()` with memory profiling. + pub fn run_with_memory_reporting<F, M, T, C>( + &self, + f: F, + reporter_name: String, + channel_for_reporter: C, + msg: M, + ) where + F: FnOnce(), + M: Fn(ReportsChan) -> T + Send + 'static, + T: Send + 'static, + C: OpaqueSender<T> + Send + 'static, + { + // Register the memory reporter. + let (reporter_sender, reporter_receiver) = ipc::channel().unwrap(); + ROUTER.add_route( + reporter_receiver.to_opaque(), + Box::new(move |message| { + // Just injects an appropriate event into the paint thread's queue. + let request: ReporterRequest = message.to().unwrap(); + channel_for_reporter.send(msg(request.reports_channel)); + }), + ); + self.send(ProfilerMsg::RegisterReporter( + reporter_name.clone(), + Reporter(reporter_sender), + )); + + f(); + + self.send(ProfilerMsg::UnregisterReporter(reporter_name)); + } +} + +/// 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(Debug, 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 `MallocSizeOf` 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(Debug, 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, +} + +/// A channel through which memory reports can be sent. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct ReportsChan(pub IpcSender<Vec<Report>>); + +impl ReportsChan { + /// Send `report` on this `IpcSender`. + /// + /// Panics if the send fails. + pub fn send(&self, report: Vec<Report>) { + self.0.send(report).unwrap(); + } +} + +/// The protocol used to send reporter requests. +#[derive(Debug, Deserialize, Serialize)] +pub struct ReporterRequest { + /// The channel on which reports are to be sent. + pub reports_channel: ReportsChan, +} + +/// A memory reporter is capable of measuring some data structure of interest. It's structured as +/// an IPC sender that a `ReporterRequest` in transmitted over. `ReporterRequest` objects in turn +/// encapsulate the channel on which the memory profiling information is to be sent. +/// +/// In many cases, clients construct `Reporter` objects by creating an IPC sender/receiver pair and +/// registering the receiving end with the router so that messages from the memory profiler end up +/// injected into the client's event loop. +#[derive(Debug, Deserialize, Serialize)] +pub struct Reporter(pub IpcSender<ReporterRequest>); + +impl Reporter { + /// Collect one or more memory reports. Returns true on success, and false on failure. + pub fn collect_reports(&self, reports_chan: ReportsChan) { + self.0 + .send(ReporterRequest { + reports_channel: reports_chan, + }) + .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() ),*] + }} +} + +/// Messages that can be sent to the memory profiler thread. +#[derive(Debug, Deserialize, Serialize)] +pub enum ProfilerMsg { + /// Register a Reporter 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. + RegisterReporter(String, Reporter), + + /// Unregister a Reporter 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. + UnregisterReporter(String), + + /// Triggers printing of the memory profiling metrics. + Print, + + /// Tells the memory profiler to shut down. + Exit, +} |