Move *_traits and other shared types to shared

This is the start of the organization of types that are in their own
crates in order to break dependency cycles between other crates. The
idea here is that putting these packages into their own directory is the
first step toward cleaning them up. They have grown organically and it
is difficult to explain to new folks where to put new shared types. Many
of these crates contain more than traits or don't contain traits at all.

Notably, `script_traits` isn't touched because it is vendored from
Gecko. Eventually this will move to `third_party`.
This commit is contained in:
Martin Robinson 2023-10-05 19:47:39 +02:00
parent 863529d962
commit f4d3af296c
89 changed files with 244 additions and 226 deletions

View file

@ -0,0 +1,20 @@
[package]
name = "profile_traits"
version = "0.0.1"
authors = ["The Servo Project Developers"]
license = "MPL-2.0"
edition = "2018"
publish = false
[lib]
name = "profile_traits"
path = "lib.rs"
[dependencies]
crossbeam-channel = { workspace = true }
ipc-channel = { workspace = true }
log = { workspace = true }
serde = { workspace = true }
servo_config = { path = "../../config" }
signpost = { git = "https://github.com/pcwalton/signpost.git" }
time = { workspace = true }

View file

@ -0,0 +1,82 @@
/* 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/. */
use std::io::Error;
use ipc_channel::ipc;
use serde::{Deserialize, Serialize};
use crate::time;
use crate::time::{ProfilerCategory, ProfilerChan};
pub struct IpcReceiver<T>
where
T: for<'de> Deserialize<'de> + Serialize,
{
ipc_receiver: ipc::IpcReceiver<T>,
time_profile_chan: ProfilerChan,
}
impl<T> IpcReceiver<T>
where
T: for<'de> Deserialize<'de> + Serialize,
{
pub fn recv(&self) -> Result<T, ipc::IpcError> {
time::profile(
ProfilerCategory::IpcReceiver,
None,
self.time_profile_chan.clone(),
move || self.ipc_receiver.recv(),
)
}
pub fn try_recv(&self) -> Result<T, ipc::TryRecvError> {
self.ipc_receiver.try_recv()
}
pub fn to_opaque(self) -> ipc::OpaqueIpcReceiver {
self.ipc_receiver.to_opaque()
}
}
pub fn channel<T>(
time_profile_chan: ProfilerChan,
) -> Result<(ipc::IpcSender<T>, IpcReceiver<T>), Error>
where
T: for<'de> Deserialize<'de> + Serialize,
{
let (ipc_sender, ipc_receiver) = ipc::channel()?;
let profiled_ipc_receiver = IpcReceiver {
ipc_receiver,
time_profile_chan,
};
Ok((ipc_sender, profiled_ipc_receiver))
}
pub struct IpcBytesReceiver {
ipc_bytes_receiver: ipc::IpcBytesReceiver,
time_profile_chan: ProfilerChan,
}
impl IpcBytesReceiver {
pub fn recv(&self) -> Result<Vec<u8>, ipc::IpcError> {
time::profile(
ProfilerCategory::IpcBytesReceiver,
None,
self.time_profile_chan.clone(),
move || self.ipc_bytes_receiver.recv(),
)
}
}
pub fn bytes_channel(
time_profile_chan: ProfilerChan,
) -> Result<(ipc::IpcBytesSender, IpcBytesReceiver), Error> {
let (ipc_bytes_sender, ipc_bytes_receiver) = ipc::bytes_channel()?;
let profiled_ipc_bytes_receiver = IpcBytesReceiver {
ipc_bytes_receiver,
time_profile_chan,
};
Ok((ipc_bytes_sender, profiled_ipc_bytes_receiver))
}

View file

@ -0,0 +1,13 @@
/* 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/. */
//! This module contains APIs for the `profile` crate used generically in the
//! rest of Servo. These APIs are here instead of in `profile` so that these
//! modules won't have to depend on `profile`.
#![deny(unsafe_code)]
pub mod ipc;
pub mod mem;
pub mod time;

View file

@ -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,
}

View file

@ -0,0 +1,175 @@
/* 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/. */
use std::time::{SystemTime, UNIX_EPOCH};
use ipc_channel::ipc::IpcSender;
use log::warn;
use serde::{Deserialize, Serialize};
use servo_config::opts;
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub struct TimerMetadata {
pub url: String,
pub iframe: TimerMetadataFrameType,
pub incremental: TimerMetadataReflowType,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ProfilerChan(pub IpcSender<ProfilerMsg>);
impl ProfilerChan {
pub fn send(&self, msg: ProfilerMsg) {
if let Err(e) = self.0.send(msg) {
warn!("Error communicating with the time profiler thread: {}", e);
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum ProfilerData {
NoRecords,
Record(Vec<f64>),
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum ProfilerMsg {
/// Normal message used for reporting time
Time((ProfilerCategory, Option<TimerMetadata>), (u64, u64)),
/// Message used to get time spend entries for a particular ProfilerBuckets (in nanoseconds)
Get(
(ProfilerCategory, Option<TimerMetadata>),
IpcSender<ProfilerData>,
),
/// Message used to force print the profiling metrics
Print,
/// Report a layout query that could not be processed immediately for a particular URL.
BlockedLayoutQuery(String),
/// Tells the profiler to shut down.
Exit(IpcSender<()>),
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub enum ProfilerCategory {
Compositing = 0x00,
LayoutPerform = 0x10,
LayoutStyleRecalc = 0x11,
LayoutTextShaping = 0x12,
LayoutRestyleDamagePropagation = 0x13,
LayoutNonIncrementalReset = 0x14,
LayoutSelectorMatch = 0x15,
LayoutTreeBuilder = 0x16,
LayoutDamagePropagate = 0x17,
LayoutGeneratedContent = 0x18,
LayoutDisplayListSorting = 0x19,
LayoutFloatPlacementSpeculation = 0x1a,
LayoutMain = 0x1b,
LayoutStoreOverflow = 0x1c,
LayoutParallelWarmup = 0x1d,
LayoutDispListBuild = 0x1e,
NetHTTPRequestResponse = 0x30,
PaintingPerTile = 0x41,
PaintingPrepBuff = 0x42,
Painting = 0x43,
ImageDecoding = 0x50,
ImageSaving = 0x51,
ScriptAttachLayout = 0x60,
ScriptConstellationMsg = 0x61,
ScriptDevtoolsMsg = 0x62,
ScriptDocumentEvent = 0x63,
ScriptDomEvent = 0x64,
ScriptEvaluate = 0x65,
ScriptEvent = 0x66,
ScriptFileRead = 0x67,
ScriptImageCacheMsg = 0x68,
ScriptInputEvent = 0x69,
ScriptNetworkEvent = 0x6a,
ScriptParseHTML = 0x6b,
ScriptPlannedNavigation = 0x6c,
ScriptResize = 0x6d,
ScriptSetScrollState = 0x6e,
ScriptSetViewport = 0x6f,
ScriptTimerEvent = 0x70,
ScriptStylesheetLoad = 0x71,
ScriptUpdateReplacedElement = 0x72,
ScriptWebSocketEvent = 0x73,
ScriptWorkerEvent = 0x74,
ScriptServiceWorkerEvent = 0x75,
ScriptParseXML = 0x76,
ScriptEnterFullscreen = 0x77,
ScriptExitFullscreen = 0x78,
ScriptWebVREvent = 0x79,
ScriptWorkletEvent = 0x7a,
ScriptPerformanceEvent = 0x7b,
ScriptHistoryEvent = 0x7c,
ScriptPortMessage = 0x7d,
ScriptWebGPUMsg = 0x7e,
TimeToFirstPaint = 0x80,
TimeToFirstContentfulPaint = 0x81,
TimeToInteractive = 0x82,
IpcReceiver = 0x83,
IpcBytesReceiver = 0x84,
}
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum TimerMetadataFrameType {
RootWindow,
IFrame,
}
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum TimerMetadataReflowType {
Incremental,
FirstReflow,
}
pub fn profile<T, F>(
category: ProfilerCategory,
meta: Option<TimerMetadata>,
profiler_chan: ProfilerChan,
callback: F,
) -> T
where
F: FnOnce() -> T,
{
if opts::get().debug.signpost {
signpost::start(category as u32, &[0, 0, 0, (category as usize) >> 4]);
}
let start_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let val = callback();
let end_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
if opts::get().debug.signpost {
signpost::end(category as u32, &[0, 0, 0, (category as usize) >> 4]);
}
send_profile_data(
category,
meta,
&profiler_chan,
start_time as u64,
end_time as u64,
);
val
}
pub fn send_profile_data(
category: ProfilerCategory,
meta: Option<TimerMetadata>,
profiler_chan: &ProfilerChan,
start_time: u64,
end_time: u64,
) {
profiler_chan.send(ProfilerMsg::Time((category, meta), (start_time, end_time)));
}