#![forbid(clippy::as_ptr_cast_mut)]
#![forbid(clippy::cast_ptr_alignment)]
#![deny(missing_docs)]
#![deny(clippy::arithmetic_side_effects)]
#![deny(clippy::as_underscore)]
#![deny(clippy::assertions_on_result_states)]
#![deny(clippy::borrow_as_ptr)]
#![deny(clippy::branches_sharing_code)]
#![deny(clippy::case_sensitive_file_extension_comparisons)]
#![deny(clippy::cast_lossless)]
#![deny(clippy::cast_possible_truncation)]
#![deny(clippy::cast_possible_wrap)]
#![deny(clippy::cast_precision_loss)]
#![deny(clippy::cast_sign_loss)]
#![deny(clippy::checked_conversions)]
#![deny(clippy::clear_with_drain)]
#![deny(clippy::clone_on_ref_ptr)]
#![deny(clippy::cloned_instead_of_copied)]
#![deny(clippy::cognitive_complexity)]
#![deny(clippy::collection_is_never_read)]
#![deny(clippy::copy_iterator)]
#![deny(clippy::create_dir)]
#![deny(clippy::dbg_macro)]
#![deny(clippy::debug_assert_with_mut_call)]
#![deny(clippy::decimal_literal_representation)]
#![deny(clippy::default_trait_access)]
#![deny(clippy::default_union_representation)]
#![deny(clippy::derive_partial_eq_without_eq)]
#![deny(clippy::doc_link_with_quotes)]
#![deny(clippy::explicit_into_iter_loop)]
#![deny(clippy::explicit_iter_loop)]
#![deny(clippy::fallible_impl_from)]
#![deny(clippy::missing_safety_doc)]
#![deny(clippy::undocumented_unsafe_blocks)]
pub mod api;
#[cfg(feature = "asm")]
pub mod asm;
pub mod bins;
pub mod cgroup;
pub mod cookie;
pub(crate) mod mask;
pub mod mount;
#[macro_use]
pub(crate) mod kernel;
pub(crate) mod workers;
pub(crate) mod cache;
#[expect(missing_docs)]
pub mod compat;
pub mod config;
pub mod confine;
pub mod dns;
pub mod elf;
pub mod err;
pub mod fd;
pub mod filemap;
pub mod fs;
pub mod hash;
pub mod hook;
pub mod io;
pub mod ioctl;
pub mod ip;
pub mod landlock_policy;
pub mod log;
pub mod lookup;
pub mod magic;
pub mod namespace;
pub mod ofd;
pub mod parsers;
pub mod path;
pub mod port;
pub mod proc;
pub mod ptrace;
pub mod pty;
pub mod req;
pub mod retry;
pub mod rng;
pub mod sandbox;
pub mod seal;
#[expect(clippy::disallowed_types)]
pub mod sealbox;
pub mod sigset;
pub mod spec;
pub mod sysinfo;
pub mod syslog;
pub mod timer;
pub mod unix;
pub mod uts;
#[expect(clippy::arithmetic_side_effects)]
pub mod wildmatch;
#[cfg(not(target_os = "android"))]
pub mod wordexp;
pub mod xattr;
#[expect(missing_docs)]
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::undocumented_unsafe_blocks)]
pub mod caps;
#[expect(missing_docs)]
#[expect(unused_imports)]
#[expect(clippy::as_underscore)]
#[expect(clippy::borrow_as_ptr)]
#[expect(clippy::cast_lossless)]
#[expect(clippy::cast_possible_truncation)]
#[expect(clippy::decimal_literal_representation)]
#[expect(clippy::default_trait_access)]
#[expect(clippy::disallowed_methods)]
#[expect(clippy::init_numbered_fields)]
#[expect(clippy::undocumented_unsafe_blocks)]
pub mod landlock;
pub(crate) mod pool;
pub mod unshare;
#[cfg(feature = "kcov")]
#[allow(clippy::arithmetic_side_effects)]
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
#[allow(clippy::cast_sign_loss)]
#[allow(clippy::disallowed_methods)]
pub(crate) mod kcov;
#[cfg(not(feature = "kcov"))]
#[allow(missing_docs)]
#[macro_export]
macro_rules! kcov_edge {
() => {{}};
($site:expr) => {{}};
}
#[cfg(not(feature = "kcov"))]
#[allow(missing_docs)]
#[macro_export]
macro_rules! kcov_edge_site {
($s:literal) => {{}};
}
#[cfg(not(feature = "kcov"))]
#[allow(missing_docs)]
#[macro_export]
macro_rules! kcov_cmp {
($sz:expr, $isconst:expr, $a:expr, $b:expr) => {{}};
($sz:expr, $isconst:expr, $a:expr, $b:expr, $site:expr) => {{}};
}
#[cfg(not(feature = "kcov"))]
#[allow(missing_docs)]
#[macro_export]
macro_rules! kcov_cmp_site {
($sz:expr, $isconst:expr, $a:expr, $b:expr, $s:literal) => {{}};
}
use std::{env, ffi::OsStr};
use lexis::ToName;
use libseccomp::ScmpVersion;
use nix::{
errno::Errno,
sched::CloneFlags,
sys::{
resource::{getrlimit, Resource},
signal::{sigaction, signal, SaFlags, SigAction, SigHandler, SigSet, Signal},
utsname::uname,
},
};
use serde::{Serialize, Serializer};
use zeroize::Zeroizing;
use crate::{
compat::lsm_list_modules,
confine::{
apparmor_enabled, check_cross_memory_attach, check_unix_diag, is_coredump, lock_enabled,
ns_enabled, seccomp_arch_native_name, selinux_enabled, selinux_enforced, vdso_list_calls,
SydArch, SydPersona, SCMP_ARCH,
},
err::err2no,
hash::{aes_ctr_info, check_setsockopt_serial_support, hmac_sha256_info, key_ring_validate},
landlock::ABI,
path::{XPath, XPathBuf},
proc::{
proc_fs_file_max, proc_fs_nr_open, proc_kernel_randomize_va_space, proc_kernel_taint,
proc_net_bpf_jit_enable, proc_yama_ptrace_scope,
},
sealbox::check_mseal_support,
spec::{speculation_get, SpeculationFeature},
};
#[macro_export]
macro_rules! main {
{ $name:ident => $($body:tt)* } => {
#[doc = concat!("Binary entry point: `", stringify!($name), "`.")]
pub fn $name() -> std::process::ExitCode {
$crate::filemap::FileMap::set_alloc_hardened();
match (|| -> $crate::err::SydResult<std::process::ExitCode> { $($body)* })() {
Ok(code) => code,
Err(err) => {
u8::try_from(
err.errno()
.map(|e| e as i32)
.unwrap_or(128)
)
.map(std::process::ExitCode::from)
.unwrap_or(std::process::ExitCode::FAILURE)
}
}
}
};
{ $($body:tt)* } => {
fn main() -> std::process::ExitCode {
$crate::filemap::FileMap::set_alloc_hardened();
match (|| -> $crate::err::SydResult<std::process::ExitCode> { $($body)* })() {
Ok(code) => code,
Err(err) => {
use std::io::Write;
let desc = format!("Error: {err}\n");
let _ = std::io::stderr().write_all(desc.as_bytes());
u8::try_from(
err.errno()
.map(|e| e as i32)
.unwrap_or(128)
)
.map(std::process::ExitCode::from)
.unwrap_or(std::process::ExitCode::FAILURE)
}
}
}
};
}
#[expect(clippy::cognitive_complexity)]
pub fn syd_info(verbose: bool) -> Result<(), Errno> {
use crate::config::*;
printfln!("syd {} ({})", *crate::config::VERSION, syd_code_name())?;
printfln!("Rock solid application kernel")?;
printfln!("Author: Ali Polatel <alip@chesswob.org>")?;
printfln!("License: GPL-3.0-only")?;
let feat = [
#[cfg(debug_assertions)]
"+debug",
#[cfg(not(debug_assertions))]
"-debug",
#[cfg(feature = "log")]
"+log",
#[cfg(not(feature = "log"))]
"-log",
#[cfg(feature = "oci")]
"+oci",
#[cfg(not(feature = "oci"))]
"-oci",
#[cfg(feature = "prof")]
"+prof",
#[cfg(not(feature = "prof"))]
"-prof",
#[cfg(feature = "trusted")]
"+trusted",
#[cfg(not(feature = "trusted"))]
"-trusted",
];
printfln!("Features: {}", feat.join(", "))?;
if !verbose {
return Ok(());
}
let alloc = if cfg!(all(
not(feature = "prof"),
target_page_size_4k,
target_pointer_width = "64"
)) {
"GrapheneOS"
} else if cfg!(feature = "prof") {
"TCMalloc"
} else {
"Libc"
};
printfln!("Allocator: {alloc}")?;
let libapi = libseccomp::get_api();
match ScmpVersion::current() {
Ok(libver) => {
printfln!(
"LibSeccomp: v{}.{}.{} api:{}",
libver.major,
libver.minor,
libver.micro,
libapi
)?;
}
Err(error) => {
printfln!("LibSeccomp: ? (error: {error})")?;
}
}
match proc_kernel_taint() {
Ok(tflags) => printfln!("{tflags}"),
Err(errno) => printfln!("Kernel may be tainted (error: {errno})."),
}?;
let aslr = match proc_kernel_randomize_va_space() {
Ok(0) => "disabled".to_string(),
Ok(1) => "enabled (stack, mmap, VDSO; PIE text randomized)".to_string(),
Ok(2) => "enabled (heap + stack, mmap, VDSO; PIE text randomized)".to_string(),
Ok(n) => format!("{n} (error: {})", Errno::EINVAL),
Err(errno) => format!("? (error: {errno})"),
};
printfln!("ASLR is {aslr}.")?;
let bpf_jit = match proc_net_bpf_jit_enable() {
Ok(0) => "disabled".to_string(),
Ok(1) => "enabled".to_string(),
Ok(2) => "enabled in debug mode".to_string(),
Ok(n) => format!("{n} (error: {})", Errno::EINVAL),
Err(errno) => format!("? (error: {errno})"),
};
printfln!("BPF JIT compiler is {bpf_jit}.")?;
let ptrace_scope = match proc_yama_ptrace_scope() {
Ok(0) => "0 - classic ptrace permissions".to_string(),
Ok(1) => "1 - restricted ptrace".to_string(),
Ok(2) => "2 - admin-only attach".to_string(),
Ok(3) => "3 - no attach".to_string(),
Ok(n) => format!("{n} (error: {})", Errno::EINVAL),
Err(errno) => format!("? (error: {errno})"),
};
printfln!("YAMA ptrace(2) scope is {ptrace_scope}.")?;
let abi = ABI::new_current();
if abi == ABI::Unsupported {
printfln!("Landlock is not supported.")?;
} else {
let state = lock_enabled(abi);
let state_verb = match state {
0 => "fully enforced",
1 => "partially enforced",
2 => "not enforced",
_ => "unsupported",
};
printfln!("Landlock ABI {} is {state_verb}.", abi as i32)?;
}
printfln!(
"User namespaces are {}supported.",
if ns_enabled(CloneFlags::CLONE_NEWUSER).unwrap_or(false) {
""
} else {
"not "
}
)?;
let cfg_cma = check_cross_memory_attach();
printfln!(
"Cross memory attach is {}supported{}",
if cfg_cma { "" } else { "not " },
if cfg_cma {
"."
} else {
" (\x1b[91minsecure\x1b[0m)."
},
)?;
printfln!(
"Memory sealing is {}supported.",
if check_mseal_support() { "" } else { "not " }
)?;
let unix_diag = match check_unix_diag() {
Ok(true) => "supported".to_string(),
Ok(false) => "not supported".to_string(),
Err(errno) => format!("unknown (error: {errno})"),
};
printfln!("UNIX socket diagnostics are {unix_diag}.")?;
printfln!(
"Algorithm sockets {} keyrings(7) support.",
if check_setsockopt_serial_support() {
"have"
} else {
"doesn't have"
}
)?;
match key_ring_validate() {
Ok(()) => {
printfln!("Session keyring is attached to the user keyring.")?;
}
Err(errno) => {
printfln!("Session keyring isn't attached to the user keyring: {errno}!")?;
}
}
printfln!("{}", aes_ctr_info())?;
printfln!("{}", hmac_sha256_info())?;
let lsms = match lsm_list_modules() {
Ok(lsms) => lsms
.into_iter()
.map(|s| s.to_string())
.collect::<Vec<String>>()
.join(", "),
Err(Errno::ENOENT) => "none loaded".to_string(),
Err(errno) => format!("? (error: {errno})"),
};
printfln!("LSMs: {lsms}.")?;
let selinux = match selinux_enabled() {
Ok(true) => {
let enforce = if selinux_enforced().unwrap_or(false) {
"Enforcing"
} else {
"Permissive"
};
format!("enabled ({enforce})")
}
Ok(false) => "disabled".to_string(),
Err(errno) => format!("? (error: {errno})"),
};
let apparmor = match apparmor_enabled() {
Ok(true) => "enabled".to_string(),
Ok(false) => "disabled".to_string(),
Err(errno) => format!("? (error: {errno})"),
};
printfln!("SELinux is {selinux}.")?;
printfln!("AppArmor is {apparmor}.")?;
match vdso_list_calls() {
Ok(names) if names.is_empty() => printfln!("No vDSO calls found.")?,
Ok(names) => {
let names = names
.iter()
.map(|s| s.to_string_lossy())
.collect::<Vec<_>>()
.join(", ");
printfln!("List of vDSO calls: {names}.")?;
}
Err(error) => printfln!("List of vDSO calls: ? (error: {error}).")?,
}
let (nofile_soft, nofile_hard) = getrlimit(Resource::RLIMIT_NOFILE).unwrap_or((0, 0));
printf!("Open file limits: {nofile_soft} soft, {nofile_hard} hard, ")?;
let file_max = proc_fs_file_max().unwrap_or(0);
let nr_open = proc_fs_nr_open().unwrap_or(0);
printfln!("{nr_open} nr_open, {file_max} file-max")?;
let uname = match uname() {
Ok(info) => OsStr::to_str(info.release()).unwrap_or("?").to_string(),
Err(_) => "?".to_string(),
};
printfln!("Host (build): {}", env!("SYD_BUILDHOST"))?;
printfln!(
"Host (target): {uname} {}",
seccomp_arch_native_name().unwrap_or("?")
)?;
printf!("Host Linux: {}.{} with", KERNEL_VERSION.0, KERNEL_VERSION.1)?;
printf!(" mmap_min_addr={}", *MMAP_MIN_ADDR)?;
printf!(", page_size={}", *PAGE_SIZE)?;
printf!(
", {}at_execve_check",
if *HAVE_AT_EXECVE_CHECK { "+" } else { "-" }
)?;
printf!(
", {}landlock_scoped_signals",
if *HAVE_LANDLOCK_SCOPED_SIGNALS {
"+"
} else {
"-"
}
)?;
printf!(
", {}madv_guard_install",
if *HAVE_MADV_GUARD { "+" } else { "-" }
)?;
printf!(
", {}namespaced_pid_max",
if *HAVE_NAMESPACED_PID_MAX { "+" } else { "-" }
)?;
printf!(
", {}pidfd_thread",
if *HAVE_PIDFD_THREAD { "+" } else { "-" }
)?;
printf!(
", {}pidfd_get_info",
if *HAVE_PIDFD_GET_INFO { "+" } else { "-" }
)?;
printf!(
", {}procmap_query",
if *HAVE_PROCMAP_QUERY { "+" } else { "-" }
)?;
printf!(
", {}proc_pid_fd_stat_size",
if *HAVE_PROC_PID_FD_STAT_SIZE {
"+"
} else {
"-"
}
)?;
printf!(
", {}pwritev2_rwf_noappend",
if *HAVE_RWF_NOAPPEND { "+" } else { "-" }
)?;
printf!(
", {}seccomp_user_notif_fd_sync_wake_up",
if *HAVE_SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP {
"+"
} else {
"-"
}
)?;
printfln!(
", {}statx_mnt_id_unique",
if *HAVE_STATX_MNT_ID_UNIQUE { "+" } else { "-" }
)?;
let pers = match SydPersona::get() {
Ok(pers) => pers.to_string(),
Err(errno) => format!("? (error: {errno})"),
};
printfln!(
"Environment: {}-{pers}-{}",
env!("SYD_TARGET_ENV"),
env!("SYD_TARGET_POINTER_WIDTH")
)?;
let arch = SCMP_ARCH
.iter()
.map(SydArch::from)
.map(|arch| arch.to_string())
.collect::<Vec<_>>()
.join(", ");
printfln!("Architectures: {arch}")?;
let mut has_ipc = Vec::new();
let mut has_socketcall = Vec::new();
for arch in SCMP_ARCH.iter().map(SydArch::from) {
if arch.has_ipc() {
has_ipc.push(arch.to_string());
}
if arch.has_socketcall() {
has_socketcall.push(arch.to_string());
}
}
if !has_ipc.is_empty() {
let plurals = if has_ipc.len() > 1 { "s" } else { "" };
let verbone = if has_ipc.len() == 1 { "s" } else { "" };
let has_ipc = has_ipc.join(", ");
printfln!("Architecture{plurals} {has_ipc} support{verbone} ipc(2) multiplexer.")?;
}
if !has_socketcall.is_empty() {
let plurals = if has_socketcall.len() > 1 { "s" } else { "" };
let verbone = if has_socketcall.len() == 1 { "s" } else { "" };
let has_socketcall = has_socketcall.join(", ");
printfln!(
"Architecture{plurals} {has_socketcall} support{verbone} socketcall(2) multiplexer."
)?;
}
printfln!(
"CPU: {} ({} cores), {}-endian",
num_cpus::get(),
num_cpus::get_physical(),
env!("SYD_TARGET_ENDIAN")
)?;
printfln!("CPUFLAGS: {}", env!("SYD_TARGET_FEATURE"))?;
for spec_feat in [
SpeculationFeature::StoreBypass,
SpeculationFeature::IndirectBranch,
SpeculationFeature::L1DFlush,
] {
printfln!(
"{}",
match speculation_get(spec_feat) {
Ok(status) => status.to_string(),
Err(errno) => format!("{spec_feat} status: ? (error: {errno})"),
}
)?;
}
Ok(())
}
pub fn syd_code_name() -> String {
#[expect(clippy::disallowed_methods)]
let major = env!("CARGO_PKG_VERSION_MAJOR")
.parse::<u64>()
.expect("CARGO_PKG_VERSION_MAJOR");
#[expect(clippy::disallowed_methods)]
let minor = env!("CARGO_PKG_VERSION_MINOR")
.parse::<u64>()
.expect("CARGO_PKG_VERSION_MINOR");
#[expect(clippy::disallowed_methods)]
let patch = env!("CARGO_PKG_VERSION_PATCH")
.parse::<u64>()
.expect("CARGO_PKG_VERSION_PATCH");
let hex_version = (major << 16) | (minor << 8) | patch;
hex_version
.to_name()
.split('_')
.map(|word| {
let mut c = word.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
}
})
.collect::<Vec<String>>()
.join(" ")
}
pub fn ignore_signal(signal: Signal) -> Result<(), Errno> {
let sig_action = SigAction::new(
SigHandler::SigIgn, SaFlags::empty(),
SigSet::empty(),
);
unsafe { sigaction(signal, &sig_action) }.map(drop)
}
pub fn reset_signal(signal: Signal) -> Result<(), Errno> {
let sig_action = SigAction::new(
SigHandler::SigDfl, SaFlags::empty(),
SigSet::empty(),
);
unsafe { sigaction(signal, &sig_action) }.map(drop)
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct IgnoreSignalOpts: u8 {
const SkipIgnoreAlarm = 1 << 0;
const SkipIgnoreCoreDump = 1 << 1;
}
}
impl Serialize for IgnoreSignalOpts {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut opts: Vec<&str> = vec![];
if self.is_empty() {
return serializer.collect_seq(opts);
}
if self.contains(Self::SkipIgnoreAlarm) {
opts.push("skip_ignore_alarm");
}
if self.contains(Self::SkipIgnoreCoreDump) {
opts.push("skip_ignore_core_dump");
}
opts.sort();
serializer.collect_seq(opts)
}
}
pub fn ignore_signals(opts: IgnoreSignalOpts) -> Result<(), Errno> {
for signal in Signal::iterator() {
match signal {
Signal::SIGCHLD | Signal::SIGKILL | Signal::SIGSTOP => {}
#[cfg(feature = "prof")]
Signal::SIGPROF => {}
Signal::SIGALRM if opts.contains(IgnoreSignalOpts::SkipIgnoreAlarm) => {}
signal
if opts.contains(IgnoreSignalOpts::SkipIgnoreCoreDump)
&& is_coredump(signal as i32) => {}
signal => ignore_signal(signal)?,
}
}
for signum in libc::SIGRTMIN()..libc::SIGRTMAX() {
Errno::result(unsafe { libc::signal(signum, libc::SIG_IGN as libc::sighandler_t) })?;
}
Ok(())
}
pub fn reset_signals() -> Result<(), Errno> {
for signal in Signal::iterator() {
if !matches!(signal, Signal::SIGKILL | Signal::SIGSTOP) {
reset_signal(signal)?;
}
}
for signum in libc::SIGRTMIN()..libc::SIGRTMAX() {
Errno::result(unsafe { libc::signal(signum, libc::SIG_DFL as libc::sighandler_t) })?;
}
Ok(())
}
const IOPRIO_CLASS_IDLE: i32 = 3;
const IOPRIO_WHO_PROCESS: i32 = 1;
pub(crate) fn set_io_priority_idle() -> Result<(), Errno> {
let ioprio = IOPRIO_CLASS_IDLE << 13;
Errno::result(unsafe { libc::syscall(libc::SYS_ioprio_set, IOPRIO_WHO_PROCESS, 0, ioprio) })
.map(drop)
}
pub(crate) fn set_cpu_priority_idle() -> Result<(), Errno> {
let param: libc::sched_param = unsafe { std::mem::zeroed() };
Errno::result(unsafe {
libc::sched_setscheduler(0, libc::SCHED_IDLE, std::ptr::addr_of!(param))
})
.map(drop)
}
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_precision_loss)]
pub fn human_size(bytes: usize) -> String {
const SIZES: &[char] = &['B', 'K', 'M', 'G', 'T', 'P', 'E'];
let factor = 1024usize;
let mut size = bytes as f64;
let mut i = 0;
while size > factor as f64 && i < SIZES.len() - 1 {
size /= factor as f64;
i += 1;
}
format!("{:.2}{}", size, SIZES[i])
}
pub fn set_sigpipe_dfl() -> Result<(), Errno> {
unsafe { signal(Signal::SIGPIPE, SigHandler::SigDfl) }.map(drop)
}
pub fn try_to_vec(data: &[u8]) -> Result<Vec<u8>, Errno> {
let mut vec = Vec::new();
vec.try_reserve(data.len()).or(Err(Errno::ENOMEM))?;
vec.extend_from_slice(data);
Ok(vec)
}
pub fn try_to_vec_zeroed(data: &[u8]) -> Result<Zeroizing<Vec<u8>>, Errno> {
let mut vec = Zeroizing::new(Vec::new());
vec.try_reserve(data.len()).or(Err(Errno::ENOMEM))?;
vec.extend_from_slice(data);
Ok(vec)
}
#[inline]
#[cold]
fn cold() {}
#[inline]
pub(crate) fn likely(b: bool) -> bool {
if !b {
cold()
}
b
}
#[inline]
pub(crate) fn unlikely(b: bool) -> bool {
if b {
cold()
}
b
}
pub fn t(msg: &str) {
let buf = msg.as_bytes();
let len = buf.len() as libc::size_t;
unsafe { libc::syscall(libc::SYS_write, -31415, buf.as_ptr(), len) };
}
#[macro_export]
macro_rules! t {
($($arg:tt)*) => {{
syd::t(&format!($($arg)*));
}}
}
#[macro_export]
macro_rules! T {
($($arg:tt)*) => {{
$crate::t(&format!($($arg)*));
}}
}
#[cfg(feature = "prof")]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn start_cpu_profile(name: &str) {
gperftools::profiler::PROFILER
.lock()
.expect("lock profiler")
.start(format!("./syd-cpu-{name}.pprof"))
.expect("start profiler");
}
#[cfg(not(feature = "prof"))]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn start_cpu_profile(_name: &str) {}
#[cfg(feature = "prof")]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn stop_cpu_profile() {
gperftools::profiler::PROFILER
.lock()
.expect("lock profiler")
.stop()
.expect("stop profiler");
}
#[cfg(not(feature = "prof"))]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn stop_cpu_profile() {}
#[cfg(feature = "prof")]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn start_mem_profile(name: &str) {
gperftools::heap_profiler::HEAP_PROFILER
.lock()
.expect("lock profiler")
.start(format!("./syd-mem-{name}"))
.expect("start profiler");
}
#[cfg(not(feature = "prof"))]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn start_mem_profile(_name: &str) {}
#[cfg(feature = "prof")]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn dump_mem_profile(name: &str) {
gperftools::heap_profiler::HEAP_PROFILER
.lock()
.expect("lock profiler")
.dump(format!("./syd-mem-{name}"))
.expect("dump profiler");
}
#[cfg(not(feature = "prof"))]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn dump_mem_profile(_name: &str) {}
#[cfg(feature = "prof")]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn stop_mem_profile() {
gperftools::heap_profiler::HEAP_PROFILER
.lock()
.expect("lock profiler")
.stop()
.expect("stop profiler");
}
#[cfg(not(feature = "prof"))]
#[inline(always)]
#[expect(dead_code)]
pub(crate) fn stop_mem_profile() {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_human_size_0() {
assert_eq!(human_size(0), "0.00B");
}
#[test]
fn test_human_size_1() {
assert_eq!(human_size(512), "512.00B");
}
#[test]
fn test_human_size_2() {
assert_eq!(human_size(1024), "1024.00B");
}
#[test]
fn test_human_size_3() {
assert_eq!(human_size(1025), "1.00K");
}
#[test]
fn test_human_size_4() {
assert_eq!(human_size(1048576), "1024.00K");
}
#[test]
fn test_human_size_5() {
assert_eq!(human_size(1048577), "1.00M");
}
#[test]
fn test_human_size_6() {
assert_eq!(human_size(1073741824), "1024.00M");
}
#[test]
fn test_human_size_7() {
assert_eq!(human_size(1073741825), "1.00G");
}
#[test]
fn test_likely_0() {
assert!(likely(true));
}
#[test]
fn test_likely_1() {
assert!(!likely(false));
}
#[test]
fn test_unlikely_0() {
assert!(unlikely(true));
}
#[test]
fn test_unlikely_1() {
assert!(!unlikely(false));
}
#[test]
fn test_syd_code_name_0() {
let name = syd_code_name();
assert!(!name.is_empty());
}
#[test]
fn test_syd_code_name_1() {
let name = syd_code_name();
assert!(name.chars().next().unwrap().is_uppercase());
}
#[test]
fn test_ignore_signal_opts_0() {
let opts = IgnoreSignalOpts::empty();
assert!(!opts.contains(IgnoreSignalOpts::SkipIgnoreAlarm));
assert!(!opts.contains(IgnoreSignalOpts::SkipIgnoreCoreDump));
}
#[test]
fn test_ignore_signal_opts_1() {
let opts = IgnoreSignalOpts::SkipIgnoreAlarm | IgnoreSignalOpts::SkipIgnoreCoreDump;
assert!(opts.contains(IgnoreSignalOpts::SkipIgnoreAlarm));
assert!(opts.contains(IgnoreSignalOpts::SkipIgnoreCoreDump));
}
}