abi: change set/get_thread/process_option()

This commit is contained in:
Mark Poliakov 2025-01-12 14:42:52 +02:00
parent 26d8b9b3bd
commit 9fa940f011
28 changed files with 458 additions and 435 deletions

View File

@ -24,7 +24,11 @@ use libk_util::{
}; };
use yggdrasil_abi::{ use yggdrasil_abi::{
error::Error, error::Error,
process::{ExitCode, ProcessGroupId, ProcessId, Signal, ThreadSpawnOptions, WaitFlags}, option::OptionValue,
process::{
options::ProcessOptionVariant, ExitCode, ProcessGroupId, ProcessId, Signal,
ThreadSpawnOptions, WaitFlags,
},
}; };
use crate::{ use crate::{
@ -281,6 +285,43 @@ impl Process {
// unreachable!() // unreachable!()
} }
pub fn get_option(
&self,
option: ProcessOptionVariant,
buffer: &mut [u8],
) -> Result<usize, Error> {
use yggdrasil_abi::process::options;
match option {
ProcessOptionVariant::Name => options::Name::store(&self.name.as_str(), buffer),
ProcessOptionVariant::Directory => {
let io = self.io.lock();
let path = io.ioctx().cwd_path();
options::Directory::store(&path.as_str(), buffer)
}
ProcessOptionVariant::SignalEntry => {
options::SignalEntry::store(&self.signal_entry(), buffer)
}
}
}
pub fn set_option(&self, option: ProcessOptionVariant, buffer: &[u8]) -> Result<(), Error> {
use yggdrasil_abi::process::options;
match option {
ProcessOptionVariant::Name => Err(Error::ReadOnly),
ProcessOptionVariant::Directory => {
let value = options::Directory::load(buffer)?;
let mut io = self.io.lock();
io.ioctx_mut().set_cwd(value)
}
ProcessOptionVariant::SignalEntry => {
let value = options::SignalEntry::load(buffer)?;
self.set_signal_entry(value);
Ok(())
}
}
}
/// Returns the address space of the process /// Returns the address space of the process
pub fn space(&self) -> Arc<ProcessAddressSpace> { pub fn space(&self) -> Arc<ProcessAddressSpace> {
self.inner.read().space.clone().unwrap() self.inner.read().space.clone().unwrap()

View File

@ -20,7 +20,11 @@ use yggdrasil_abi::{
arch::SavedFrame, arch::SavedFrame,
debug::DebugFrame, debug::DebugFrame,
error::Error, error::Error,
process::{ExitCode, ProcessId, Signal, SignalEntryData}, option::OptionValue,
process::{
thread::{ThreadOptionVariant, ThreadSignalStack},
ExitCode, ProcessId, Signal, SignalEntryData,
},
}; };
use crate::task::{ use crate::task::{
@ -54,14 +58,8 @@ pub struct ThreadDebuggingInfo {
pub breakpoints: BTreeMap<usize, BreakpointType>, pub breakpoints: BTreeMap<usize, BreakpointType>,
} }
#[derive(Default, Clone, Copy, Debug)]
pub struct SignalStack {
pub base: usize,
pub size: usize,
}
pub struct ThreadInfo { pub struct ThreadInfo {
pub signal_stack: SignalStack, pub signal_stack: ThreadSignalStack,
} }
/// Describes a single thread within the system /// Describes a single thread within the system
@ -136,7 +134,7 @@ impl Thread {
space, space,
info: IrqSafeRwLock::new(ThreadInfo { info: IrqSafeRwLock::new(ThreadInfo {
signal_stack: SignalStack::default(), signal_stack: ThreadSignalStack::default(),
}), }),
signal_queue: SegQueue::new(), signal_queue: SegQueue::new(),
exit: Arc::new(BoolEvent::new()), exit: Arc::new(BoolEvent::new()),
@ -177,6 +175,51 @@ impl Thread {
) )
} }
pub fn get_option(
&self,
option: ThreadOptionVariant,
buffer: &mut [u8],
) -> Result<usize, Error> {
use yggdrasil_abi::process::thread as options;
match option {
ThreadOptionVariant::Name => {
let name = self.name.read();
options::Name::store(&name.as_str(), buffer)
}
ThreadOptionVariant::SignalStack => {
options::SignalStack::store(&self.signal_stack(), buffer)
}
// There're better ways to do this, don't ask the kernel
ThreadOptionVariant::ThreadPointer => Err(Error::InvalidOperation),
}
}
pub fn set_option(&self, option: ThreadOptionVariant, buffer: &[u8]) -> Result<(), Error> {
use yggdrasil_abi::process::thread as options;
match option {
ThreadOptionVariant::Name => {
let value = options::Name::load(buffer)?;
self.set_name(value);
Ok(())
}
ThreadOptionVariant::SignalStack => {
let value = options::SignalStack::load(buffer)?;
if value.base.checked_add(value.size).is_none() {
return Err(Error::InvalidArgument);
}
self.set_signal_stack(value);
Ok(())
}
ThreadOptionVariant::ThreadPointer => {
let value = options::ThreadPointer::load(buffer)?;
self.set_thread_pointer(value);
Ok(())
}
}
}
/// Sets up arguments on the task's stack, if needed by the ABI. /// Sets up arguments on the task's stack, if needed by the ABI.
/// ///
/// # Safety /// # Safety
@ -230,13 +273,13 @@ impl Thread {
} }
/// Updates the thread signal stack information /// Updates the thread signal stack information
pub fn set_signal_stack(&self, stack: SignalStack) -> SignalStack { pub fn set_signal_stack(&self, stack: ThreadSignalStack) -> ThreadSignalStack {
let mut info = self.info.write(); let mut info = self.info.write();
core::mem::replace(&mut info.signal_stack, stack) core::mem::replace(&mut info.signal_stack, stack)
} }
/// Returns the currently set signal stack /// Returns the currently set signal stack
pub fn signal_stack(&self) -> SignalStack { pub fn signal_stack(&self) -> ThreadSignalStack {
self.info.read().signal_stack self.info.read().signal_stack
} }

View File

@ -116,7 +116,7 @@ pub fn global_control(option: u32, buffer: &mut [u8], len: usize) -> Result<usiz
} }
}?; }?;
return Ok(0); Ok(0)
} }
} }
} }

View File

@ -10,11 +10,12 @@ pub(crate) use abi::{
mem::{MappingFlags, MappingSource}, mem::{MappingFlags, MappingSource},
net::{SocketShutdown, SocketType}, net::{SocketShutdown, SocketType},
process::{Signal, SignalEntryData, SpawnOptions, WaitFlags}, process::{Signal, SignalEntryData, SpawnOptions, WaitFlags},
system::SystemInfo,
}; };
use abi::{ use abi::{
option::OptionValue,
path::Path, path::Path,
process::{ExecveOptions, ProcessId}, process::{ExecveOptions, ProcessId},
system::{self, SystemInfoVariant},
time::{ClockType, SystemTime}, time::{ClockType, SystemTime},
}; };
use libk::{ use libk::{
@ -65,12 +66,10 @@ pub(crate) fn get_clock(ty: ClockType, value: &mut MaybeUninit<SystemTime>) -> R
Ok(()) Ok(())
} }
pub(crate) fn get_system_info(element: &mut SystemInfo) -> Result<(), Error> { pub(crate) fn get_system_info(option: u32, buffer: &mut [u8]) -> Result<usize, Error> {
match element { let option = SystemInfoVariant::try_from(option)?;
SystemInfo::MemoryStats(stats) => { match option {
*stats = phys::stats(); SystemInfoVariant::MemoryUsage => system::MemoryUsage::store(&phys::stats(), buffer),
Ok(())
}
} }
} }

View File

@ -1,25 +1,19 @@
use core::{ use core::{mem::MaybeUninit, num::NonZeroUsize, sync::atomic::AtomicU32, time::Duration};
mem::MaybeUninit, num::NonZeroUsize, str::FromStr, sync::atomic::AtomicU32, time::Duration,
};
use abi::{ use abi::{
error::Error, error::Error,
mem::{MappingFlags, MappingSource}, mem::{MappingFlags, MappingSource},
process::{ process::{
ExitCode, MutexOperation, ProcessGroupId, ProcessId, ProcessOption, ProcessWait, Signal, options::ProcessOptionVariant, thread::ThreadOptionVariant, ExitCode, MutexOperation,
SpawnFlags, SpawnOption, SpawnOptions, ThreadOption, ThreadSpawnOptions, WaitFlags, ProcessGroupId, ProcessId, ProcessWait, Signal, SpawnFlags, SpawnOption, SpawnOptions,
ThreadSpawnOptions, WaitFlags,
}, },
util::FixedString,
}; };
use alloc::sync::Arc; use alloc::sync::Arc;
use libk::{ use libk::{
block, block,
task::{ task::{
binary::LoadOptions, binary::LoadOptions, debug::ThreadDebugger, process::Process, runtime, thread::Thread,
debug::ThreadDebugger,
process::Process,
runtime,
thread::{SignalStack, Thread},
ThreadId, ThreadId,
}, },
time::monotonic_time, time::monotonic_time,
@ -330,78 +324,28 @@ pub(crate) fn wait_thread(id: u32) -> Result<(), Error> {
block!(process.wait_for_thread(tid).await)? block!(process.wait_for_thread(tid).await)?
} }
pub(crate) fn get_thread_option(option: &mut ThreadOption) -> Result<(), Error> { pub(crate) fn get_thread_option(option: u32, buffer: &mut [u8]) -> Result<usize, Error> {
let option = ThreadOptionVariant::try_from(option)?;
let thread = Thread::current(); let thread = Thread::current();
match option { thread.get_option(option, buffer)
// There're better ways to do this, don't ask the kernel
ThreadOption::ThreadPointer(_) => Err(Error::InvalidOperation),
ThreadOption::SignalStack(base, size) => {
let stack = thread.signal_stack();
*base = stack.base;
*size = stack.size;
Ok(())
}
ThreadOption::Name(_) => todo!(),
}
} }
pub(crate) fn set_thread_option(option: &mut ThreadOption) -> Result<(), Error> { pub(crate) fn set_thread_option(option: u32, buffer: &[u8]) -> Result<(), Error> {
let option = ThreadOptionVariant::try_from(option)?;
let thread = Thread::current(); let thread = Thread::current();
match option { thread.set_option(option, buffer)
ThreadOption::ThreadPointer(tp) => {
log::debug!("{:?}: set thread pointer: {:#x}", thread.id, tp);
thread.set_thread_pointer(*tp);
Ok(())
}
ThreadOption::SignalStack(base, size) => {
if base.checked_add(*size).is_none() {
return Err(Error::InvalidArgument);
}
let old = thread.set_signal_stack(SignalStack {
base: *base,
size: *size,
});
*base = old.base;
*size = old.size;
Ok(())
}
ThreadOption::Name(name) => {
// Make a kernel-owned string
log::debug!("{:?}: set thread name: {name:?}", thread.id);
thread.set_name(*name);
Ok(())
}
}
} }
pub(crate) fn get_process_option(option: &mut ProcessOption) -> Result<(), Error> { pub(crate) fn get_process_option(option: u32, buffer: &mut [u8]) -> Result<usize, Error> {
let option = ProcessOptionVariant::try_from(option)?;
let thread = Thread::current(); let thread = Thread::current();
let process = thread.process(); let process = thread.process();
match option { process.get_option(option, buffer)
ProcessOption::SignalEntry(entry) => {
*entry = process.signal_entry();
Ok(())
}
ProcessOption::Directory(dst) => run_with_io(&process, |io| {
let path = io.ioctx().cwd_path();
*dst = FixedString::from_str(path.as_str())?;
Ok(())
}),
}
} }
pub(crate) fn set_process_option(option: &mut ProcessOption) -> Result<(), Error> { pub(crate) fn set_process_option(option: u32, buffer: &[u8]) -> Result<(), Error> {
let option = ProcessOptionVariant::try_from(option)?;
let thread = Thread::current(); let thread = Thread::current();
let process = thread.process(); let process = thread.process();
match option { process.set_option(option, buffer)
ProcessOption::SignalEntry(entry) => {
*entry = process.set_signal_entry(*entry);
Ok(())
}
ProcessOption::Directory(path) => run_with_io(&process, |mut io| {
let path = path.as_ref();
io.ioctx_mut().set_cwd(path)?;
Ok(())
}),
}
} }

View File

@ -29,10 +29,12 @@ impl_primitive_serde!(
i16: [read_i16, write_i16], i16: [read_i16, write_i16],
i32: [read_i32, write_i32], i32: [read_i32, write_i32],
i64: [read_i64, write_i64], i64: [read_i64, write_i64],
isize: [read_isize, write_isize],
u8: [read_u8, write_u8], u8: [read_u8, write_u8],
u16: [read_u16, write_u16], u16: [read_u16, write_u16],
u32: [read_u32, write_u32], u32: [read_u32, write_u32],
u64: [read_u64, write_u64] u64: [read_u64, write_u64],
usize: [read_usize, write_usize]
); );
impl Serialize for () { impl Serialize for () {
@ -49,6 +51,39 @@ impl<'de> Deserialize<'de> for () {
} }
} }
impl Serialize for &str {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_str(self)
}
}
impl<'de> Deserialize<'de> for &'de str {
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
deserializer.read_str()
}
}
impl<const N: usize> Serialize for [u8; N]
where
[u8; N]: Sized,
{
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_bytes(self)
}
}
impl<'de, const N: usize> Deserialize<'de> for [u8; N]
where
[u8; N]: Sized,
{
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
deserializer
.read_bytes()?
.try_into()
.map_err(|_| D::Error::INVALID_ARRAY_LEN)
}
}
impl<T: Serialize> Serialize for Option<T> { impl<T: Serialize> Serialize for Option<T> {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> { fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
match self { match self {

View File

@ -70,52 +70,11 @@ impl Serialize for SocketAddrV6 {
} }
} }
impl<'de> Deserialize<'de> for IpAddr { crate::impl_enum_serde!(IpAddr: [
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> { V4 => 4,
match deserializer.read_enum_variant()? { V6 => 6
4 => Ipv4Addr::deserialize(deserializer).map(Self::V4), ]);
6 => Ipv6Addr::deserialize(deserializer).map(Self::V6), crate::impl_enum_serde!(SocketAddr: [
_ => Err(D::Error::INVALID_ENUM_VARIANT), V4 => 4,
} V6 => 6
} ]);
}
impl<'de> Deserialize<'de> for SocketAddr {
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
match deserializer.read_enum_variant()? {
4 => SocketAddrV4::deserialize(deserializer).map(Self::V4),
6 => SocketAddrV6::deserialize(deserializer).map(Self::V6),
_ => Err(D::Error::INVALID_ENUM_VARIANT),
}
}
}
impl Serialize for IpAddr {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
match self {
Self::V4(v4) => {
serializer.write_enum_variant(4)?;
v4.serialize(serializer)
}
Self::V6(v6) => {
serializer.write_enum_variant(6)?;
v6.serialize(serializer)
}
}
}
}
impl Serialize for SocketAddr {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
match self {
Self::V4(v4) => {
serializer.write_enum_variant(4)?;
v4.serialize(serializer)
}
Self::V6(v6) => {
serializer.write_enum_variant(6)?;
v6.serialize(serializer)
}
}
}
}

View File

@ -10,3 +10,86 @@ pub mod wire;
pub use des::Deserialize; pub use des::Deserialize;
pub use ser::Serialize; pub use ser::Serialize;
#[macro_export]
macro_rules! impl_enum_serde {
(
$name:ident $(<$lifetime:lifetime>)? : [
$(
$variant:ident => $discriminant:literal
),* $(,)?
]
) => {
impl $(<$lifetime>)? $crate::Serialize for $name $(<$lifetime>)? {
fn serialize<S: $crate::ser::Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
match self {
$(
Self::$variant(value) => {
serializer.write_enum_variant($discriminant)?;
$crate::Serialize::serialize(value, serializer)
}
)*
}
}
}
impl<'de> $crate::Deserialize<'de> for $name $(<$lifetime>)? {
fn deserialize<D: $crate::des::Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
let variant = deserializer.read_enum_variant()?;
match variant {
$(
$discriminant => $crate::Deserialize::deserialize(deserializer).map(Self::$variant),
)*
_ => Err(<D::Error as $crate::des::DeserializeError>::INVALID_ENUM_VARIANT)
}
}
}
};
}
#[macro_export]
macro_rules! impl_struct_serde {
(
$name:ident $(<$lifetime:lifetime>)? : [$($field:ident),* $(,)?]
) => {
impl $(<$lifetime>)? $crate::Serialize for $name $(<$lifetime>)? {
fn serialize<S: $crate::ser::Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
$(
$crate::Serialize::serialize(&self.$field, serializer)?;
)*
Ok(())
}
}
impl<'de> $crate::Deserialize<'de> for $name $(<$lifetime:lifetime>)? {
fn deserialize<D: $crate::des::Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
$(
let $field = $crate::Deserialize::deserialize(deserializer)?;
)*
Ok(Self {$($field),*})
}
}
}
}
#[macro_export]
macro_rules! impl_newtype_serde {
($name:ident) => {
impl $crate::Serialize for $name {
fn serialize<S: $crate::ser::Serializer>(
&self,
serializer: &mut S,
) -> Result<(), S::Error> {
$crate::Serialize::serialize(&self.0, serializer)
}
}
impl<'de> $crate::Deserialize<'de> for $name {
fn deserialize<D: $crate::des::Deserializer<'de>>(
deserializer: &mut D,
) -> Result<Self, D::Error> {
$crate::Deserialize::deserialize(deserializer).map(Self)
}
}
};
}

View File

@ -11,14 +11,10 @@ extern {
// TODO "tagged union" enums are not yet implemented // TODO "tagged union" enums are not yet implemented
type MappingSource; type MappingSource;
type SystemInfo = yggdrasil_abi::system::SystemInfo;
type SignalEntryData = yggdrasil_abi::process::SignalEntryData; type SignalEntryData = yggdrasil_abi::process::SignalEntryData;
type MutexOperation = yggdrasil_abi::process::MutexOperation; type MutexOperation = yggdrasil_abi::process::MutexOperation;
#[thin] #[thin]
type ExitCode = yggdrasil_abi::process::ExitCode; type ExitCode = yggdrasil_abi::process::ExitCode;
type ThreadOption = yggdrasil_abi::process::ThreadOption;
type ProcessOption = yggdrasil_abi::process::ProcessOption;
type ProcessWait = yggdrasil_abi::process::ProcessWait; type ProcessWait = yggdrasil_abi::process::ProcessWait;
type SocketAddr = core::net::SocketAddr; type SocketAddr = core::net::SocketAddr;
@ -72,12 +68,14 @@ bitfield MappingFlags(u32) {
syscall get_random(buffer: &mut [u8]); syscall get_random(buffer: &mut [u8]);
syscall get_clock(clock: ClockType, out: &mut MaybeUninit<SystemTime>) -> Result<()>; syscall get_clock(clock: ClockType, out: &mut MaybeUninit<SystemTime>) -> Result<()>;
syscall get_system_info(info: &mut SystemInfo) -> Result<()>;
syscall mount(opts: &MountOptions<'_>) -> Result<()>; syscall mount(opts: &MountOptions<'_>) -> Result<()>;
syscall unmount(opts: &UnmountOptions) -> Result<()>; syscall unmount(opts: &UnmountOptions) -> Result<()>;
syscall load_module(path: &str) -> Result<()>; syscall load_module(path: &str) -> Result<()>;
syscall filesystem_control(fd: Option<RawFd>, option: u32, value: &mut [u8], len: usize) -> Result<usize>; syscall filesystem_control(fd: Option<RawFd>, option: u32, value: &mut [u8], len: usize) -> Result<usize>;
syscall get_system_info(option: u32, value: &mut [u8]) -> Result<usize>;
// Memory management // Memory management
syscall map_memory( syscall map_memory(
hint: Option<NonZeroUsize>, hint: Option<NonZeroUsize>,
@ -101,10 +99,10 @@ syscall get_tid() -> u32;
syscall spawn_thread(options: &ThreadSpawnOptions) -> Result<u32>; syscall spawn_thread(options: &ThreadSpawnOptions) -> Result<u32>;
syscall exit_thread() -> !; syscall exit_thread() -> !;
syscall wait_thread(tid: u32) -> Result<()>; syscall wait_thread(tid: u32) -> Result<()>;
syscall get_thread_option(option: &mut ThreadOption<'_>) -> Result<()>; syscall get_thread_option(option: u32, value: &mut [u8]) -> Result<usize>;
syscall set_thread_option(option: &mut ThreadOption<'_>) -> Result<()>; syscall set_thread_option(option: u32, value: &[u8]) -> Result<()>;
syscall get_process_option(option: &mut ProcessOption) -> Result<()>; syscall get_process_option(option: u32, value: &mut [u8]) -> Result<usize>;
syscall set_process_option(option: &mut ProcessOption) -> Result<()>; syscall set_process_option(option: u32, value: &[u8]) -> Result<()>;
syscall nanosleep(duration: &Duration, remaining: &mut MaybeUninit<Duration>) -> Result<()>; syscall nanosleep(duration: &Duration, remaining: &mut MaybeUninit<Duration>) -> Result<()>;

View File

@ -1,5 +1,4 @@
//! Device data structures and options //! Device data structures and options
use abi_serde::{des::Deserializer, ser::Serializer, Deserialize, Serialize};
use crate::{io::terminal, process::ProcessGroupId}; use crate::{io::terminal, process::ProcessGroupId};
@ -56,27 +55,6 @@ request_group!(
} }
); );
impl<'de> Deserialize<'de> for Framebuffer { abi_serde::impl_struct_serde!(Framebuffer: [
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> { width, height, stride, size
let width = deserializer.read_u32()?; ]);
let height = deserializer.read_u32()?;
let stride = deserializer.read_usize()?;
let size = deserializer.read_usize()?;
Ok(Self {
width,
height,
stride,
size,
})
}
}
impl Serialize for Framebuffer {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_u32(self.width)?;
serializer.write_u32(self.height)?;
serializer.write_usize(self.stride)?;
serializer.write_usize(self.size)?;
Ok(())
}
}

View File

@ -1,5 +1,3 @@
use abi_serde::{des::Deserializer, ser::Serializer, Deserialize, Serialize};
pub use crate::generated::{ pub use crate::generated::{
TerminalControlCharacters, TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalControlCharacters, TerminalInputOptions, TerminalLineOptions, TerminalOptions,
TerminalOutputOptions, TerminalSize, TerminalOutputOptions, TerminalSize,
@ -65,107 +63,15 @@ impl Default for TerminalOptions {
} }
} }
impl<'de> Deserialize<'de> for TerminalLineOptions { abi_serde::impl_newtype_serde!(TerminalLineOptions);
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> { abi_serde::impl_newtype_serde!(TerminalInputOptions);
Ok(unsafe { Self::from_raw(deserializer.read_u32()?) }) abi_serde::impl_newtype_serde!(TerminalOutputOptions);
} abi_serde::impl_struct_serde!(TerminalControlCharacters: [
} eof, kill, erase, werase, interrupt
]);
impl Serialize for TerminalLineOptions { abi_serde::impl_struct_serde!(TerminalOptions: [
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> { line, input, output, chars
serializer.write_u32(self.bits()) ]);
} abi_serde::impl_struct_serde!(TerminalSize: [
} rows, columns
]);
impl<'de> Deserialize<'de> for TerminalInputOptions {
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
Ok(unsafe { Self::from_raw(deserializer.read_u32()?) })
}
}
impl Serialize for TerminalInputOptions {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_u32(self.bits())
}
}
impl<'de> Deserialize<'de> for TerminalOutputOptions {
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
Ok(unsafe { Self::from_raw(deserializer.read_u32()?) })
}
}
impl Serialize for TerminalOutputOptions {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_u32(self.bits())
}
}
impl<'de> Deserialize<'de> for TerminalControlCharacters {
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
let eof = deserializer.read_u8()?;
let interrupt = deserializer.read_u8()?;
let erase = deserializer.read_u8()?;
let werase = deserializer.read_u8()?;
let kill = deserializer.read_u8()?;
Ok(Self {
eof,
interrupt,
erase,
werase,
kill,
})
}
}
impl Serialize for TerminalControlCharacters {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_u8(self.eof)?;
serializer.write_u8(self.interrupt)?;
serializer.write_u8(self.erase)?;
serializer.write_u8(self.werase)?;
serializer.write_u8(self.kill)?;
Ok(())
}
}
impl<'de> Deserialize<'de> for TerminalOptions {
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
let line = TerminalLineOptions::deserialize(deserializer)?;
let input = TerminalInputOptions::deserialize(deserializer)?;
let output = TerminalOutputOptions::deserialize(deserializer)?;
let chars = TerminalControlCharacters::deserialize(deserializer)?;
Ok(Self {
line,
input,
output,
chars,
})
}
}
impl Serialize for TerminalOptions {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
self.line.serialize(serializer)?;
self.input.serialize(serializer)?;
self.output.serialize(serializer)?;
self.chars.serialize(serializer)?;
Ok(())
}
}
impl<'de> Deserialize<'de> for TerminalSize {
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
let rows = deserializer.read_usize()?;
let columns = deserializer.read_usize()?;
Ok(Self { rows, columns })
}
}
impl Serialize for TerminalSize {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_usize(self.rows)?;
serializer.write_usize(self.columns)?;
Ok(())
}
}

View File

@ -1,11 +1,5 @@
//! Defines data types for network operations //! Defines data types for network operations
use abi_serde::{
des::{DeserializeError, Deserializer},
ser::Serializer,
Deserialize, Serialize,
};
#[cfg(any(feature = "alloc", feature = "rustc_std_alloc"))] #[cfg(any(feature = "alloc", feature = "rustc_std_alloc"))]
pub mod dns; pub mod dns;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
@ -46,30 +40,10 @@ impl From<u32> for SocketInterfaceQuery<'_> {
} }
} }
impl<'de> Deserialize<'de> for SocketInterfaceQuery<'de> { abi_serde::impl_enum_serde!(SocketInterfaceQuery<'de>: [
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> { ById => 1,
match deserializer.read_enum_variant()? { ByName => 2
1 => deserializer.read_u32().map(Self::ById), ]);
2 => deserializer.read_str().map(Self::ByName),
_ => Err(D::Error::INVALID_ENUM_VARIANT),
}
}
}
impl Serialize for SocketInterfaceQuery<'_> {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
match *self {
Self::ById(id) => {
serializer.write_enum_variant(1)?;
serializer.write_u32(id)
}
Self::ByName(name) => {
serializer.write_enum_variant(2)?;
serializer.write_str(name)
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -2,12 +2,6 @@
use core::fmt; use core::fmt;
use abi_serde::{
des::{DeserializeError, Deserializer},
ser::Serializer,
Deserialize, Serialize,
};
pub mod ip_addr; pub mod ip_addr;
pub mod net_value; pub mod net_value;
pub mod socket_addr; pub mod socket_addr;
@ -54,18 +48,4 @@ impl fmt::Display for MacAddress {
} }
} }
impl<'de> Deserialize<'de> for MacAddress { abi_serde::impl_newtype_serde!(MacAddress);
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> {
deserializer
.read_bytes()?
.try_into()
.map_err(|_| D::Error::INVALID_ARRAY_LEN)
.map(Self)
}
}
impl Serialize for MacAddress {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_bytes(&self.0)
}
}

View File

@ -2,21 +2,17 @@
use core::{ffi::CStr, fmt, marker::PhantomData, time::Duration}; use core::{ffi::CStr, fmt, marker::PhantomData, time::Duration};
use crate::{ use crate::io::{FileMode, RawFd};
io::{FileMode, RawFd},
util::FixedString,
};
mod exit; mod exit;
pub mod options;
pub mod thread; pub mod thread;
pub use crate::generated::{ pub use crate::generated::{
ExecveOptions, ProcessGroupId, ProcessId, Signal, SignalEntryData, SpawnFlags, SpawnOptions, ExecveOptions, ProcessGroupId, ProcessId, Signal, SignalEntryData, SpawnFlags, SpawnOptions,
ThreadId, ThreadSpawnOptions, WaitFlags, ThreadId, ThreadSpawnOptions, WaitFlags,
}; };
use abi_serde::{des::Deserializer, ser::Serializer, Deserialize, Serialize};
pub use exit::ExitCode; pub use exit::ExitCode;
pub use thread::ThreadOption;
// TODO this is ugly // TODO this is ugly
pub mod auxv { pub mod auxv {
@ -47,16 +43,6 @@ pub enum ProcessWait {
AnyChild, AnyChild,
} }
/// Represents a process option being set/retrieved
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum ProcessOption {
/// Signal entry point address
SignalEntry(usize),
/// Current working directory
Directory(FixedString<4096>),
}
/// Defines an optional argument for controlling process creation /// Defines an optional argument for controlling process creation
#[derive(Debug)] #[derive(Debug)]
pub enum SpawnOption { pub enum SpawnOption {
@ -229,14 +215,5 @@ impl Signal {
} }
} }
impl<'de> Deserialize<'de> for ProcessGroupId { abi_serde::impl_newtype_serde!(ProcessGroupId);
fn deserialize<D: Deserializer<'de>>(deserializer: &mut D) -> Result<Self, D::Error> { abi_serde::impl_newtype_serde!(ProcessId);
deserializer.read_u32().map(Self)
}
}
impl Serialize for ProcessGroupId {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.write_u32(self.0)
}
}

View File

@ -0,0 +1,13 @@
//! Process option definitions
option_group!(
#[doc = "Process options"]
pub enum ProcessOptionVariant<'a> {
#[doc = "Process signal entry point address"]
0x1000: SignalEntry # 8 (usize),
#[doc = "Current working directory"]
0x1001: Directory(&'a str),
#[doc = "Process name"]
0x1002: Name(&'a str)
}
);

View File

@ -1,12 +1,24 @@
//! Thread data structures //! Thread option definitions
/// Represents a thread option being set/retrieved /// Thread signal stack
#[derive(Debug)] #[derive(Clone, Copy, Debug, Default)]
pub enum ThreadOption<'a> { pub struct ThreadSignalStack {
/// Thread-local storage base pointer /// Base address
ThreadPointer(usize), pub base: usize,
/// Thread signal entry stack base and size /// Size
SignalStack(usize, usize), pub size: usize,
/// Thread display name
Name(&'a str),
} }
option_group!(
#[doc = "Thread options"]
pub enum ThreadOptionVariant<'a> {
#[doc = "Thread-local storage pointer"]
0x1000: ThreadPointer # 8(usize),
#[doc = "Signal stack information"]
0x1001: SignalStack # 16(ThreadSignalStack),
#[doc = "Thread display name"]
0x1002: Name(&'a str),
}
);
abi_serde::impl_struct_serde!(ThreadSignalStack: [base, size]);

View File

@ -13,9 +13,14 @@ pub struct SystemMemoryStats {
pub page_size: usize, pub page_size: usize,
} }
/// Describes a piece of system information option_group!(
#[derive(Clone, Debug)] #[doc = "System information requests"]
pub enum SystemInfo { pub enum SystemInfoVariant<'de> {
/// Memory usage stats #[doc = "Memory usage information"]
MemoryStats(SystemMemoryStats), 0x1000: MemoryUsage # 64 (SystemMemoryStats),
} }
);
abi_serde::impl_struct_serde!(
SystemMemoryStats: [total_usable_pages, allocated_pages, free_pages, page_size]
);

View File

@ -1,8 +1,8 @@
use core::str::FromStr; use abi::error::Error;
use abi::{error::Error, process::ProcessOption, util::FixedString};
use alloc::string::String; use alloc::string::String;
use crate::process;
pub fn current_exe<T, F: FnOnce(&str) -> T>(_mapper: F) -> Result<T, Error> { pub fn current_exe<T, F: FnOnce(&str) -> T>(_mapper: F) -> Result<T, Error> {
todo!() todo!()
} }
@ -12,13 +12,9 @@ pub fn home_directory<T, F: FnOnce(&str) -> T>(_mapper: F) -> Result<T, Error> {
} }
pub fn current_directory<T, F: FnOnce(&str) -> T>(mapper: F) -> Result<T, Error> { pub fn current_directory<T, F: FnOnce(&str) -> T>(mapper: F) -> Result<T, Error> {
let mut option = ProcessOption::Directory(FixedString::empty()); let mut buffer = [0; 512];
unsafe { crate::sys::get_process_option(&mut option) }?; let path = process::get_process_option::<process::options::Directory>(&mut buffer)?;
let ProcessOption::Directory(path) = &option else { Ok(mapper(path))
unreachable!()
};
Ok(mapper(path.as_ref()))
} }
pub fn current_directory_string() -> Result<String, Error> { pub fn current_directory_string() -> Result<String, Error> {
@ -26,9 +22,8 @@ pub fn current_directory_string() -> Result<String, Error> {
} }
pub fn set_current_directory(path: &str) -> Result<(), Error> { pub fn set_current_directory(path: &str) -> Result<(), Error> {
let mut option = ProcessOption::Directory(FixedString::from_str(path)?); let mut buffer = [0; 512];
unsafe { crate::sys::set_process_option(&mut option) }?; process::set_process_option_with::<process::options::Directory>(&mut buffer, &path)
Ok(())
} }
pub fn make_temp_directory(_template: &mut [u8]) -> Result<(), Error> { pub fn make_temp_directory(_template: &mut [u8]) -> Result<(), Error> {

View File

@ -30,7 +30,7 @@ pub fn get_terminal_size(fd: RawFd) -> Result<TerminalSize, Error> {
} }
pub fn set_terminal_size(fd: RawFd, size: &TerminalSize) -> Result<(), Error> { pub fn set_terminal_size(fd: RawFd, size: &TerminalSize) -> Result<(), Error> {
let mut buffer = [0; 32]; let mut buffer = [0; 32];
device_request::<device::SetTerminalSize>(fd, &mut buffer, &size) device_request::<device::SetTerminalSize>(fd, &mut buffer, size)
} }
pub fn is_terminal(fd: RawFd) -> bool { pub fn is_terminal(fd: RawFd) -> bool {

View File

@ -32,10 +32,5 @@ pub mod net;
pub mod process; pub mod process;
pub mod sync; pub mod sync;
pub mod sys; pub mod sys;
pub mod system;
pub mod time; pub mod time;
pub mod system {
//! System-related data structures
pub use abi::system::*;
}

View File

@ -2,12 +2,15 @@
use core::{mem::MaybeUninit, time::Duration}; use core::{mem::MaybeUninit, time::Duration};
use abi::error::Error;
pub use abi::process::{ pub use abi::process::{
auxv, AuxValue, AuxValueIter, ExecveOptions, ExitCode, MutexOperation, ProcessGroupId, auxv, AuxValue, AuxValueIter, ExecveOptions, ExitCode, MutexOperation, ProcessGroupId,
ProcessId, ProcessInfoElement, ProcessWait, ProgramArgumentInner, Signal, SignalEntryData, ProcessId, ProcessInfoElement, ProcessWait, ProgramArgumentInner, Signal, SignalEntryData,
SpawnFlags, SpawnOption, SpawnOptions, StringArgIter, ThreadId, ThreadSpawnOptions, WaitFlags, SpawnFlags, SpawnOption, SpawnOptions, StringArgIter, ThreadId, ThreadSpawnOptions, WaitFlags,
}; };
use abi::{
error::Error,
option::{OptionSizeHint, OptionValue},
};
use crate::sys; use crate::sys;
@ -15,6 +18,8 @@ pub mod signal;
pub mod thread; pub mod thread;
pub mod thread_local; pub mod thread_local;
pub use abi::process::options;
/// Makes the current thread wait until *at least* the amount of time specified in `duration` /// Makes the current thread wait until *at least* the amount of time specified in `duration`
/// passes. /// passes.
/// ///
@ -39,3 +44,71 @@ pub fn uninterruptible_sleep(mut duration: Duration) {
} }
} }
} }
/// Helper macro for [get_process_option].
pub macro get_process_option($variant_ty:ty) {{
let mut buffer = [0; <$variant_ty as $crate::io::OptionSizeHint>::SIZE_HINT];
$crate::process::get_process_option::<$variant_ty>(&mut buffer)
}}
/// Retrieves a process option value.
pub fn get_process_option<'de, T: OptionValue<'de>>(
buffer: &'de mut [u8],
) -> Result<T::Value, Error> {
let len = unsafe { crate::sys::get_process_option(T::VARIANT.into(), buffer) }?;
T::load(&buffer[..len])
}
/// Helper macro for [get_thread_option].
pub macro get_thread_option($variant_ty:ty) {{
let mut buffer = [0; <$variant_ty as $crate::io::OptionSizeHint>::SIZE_HINT];
$crate::process::get_thread_option::<$variant_ty>(&mut buffer)
}}
/// Retrieves a thread option value.
pub fn get_thread_option<'de, T: OptionValue<'de>>(
buffer: &'de mut [u8],
) -> Result<T::Value, Error> {
let len = unsafe { crate::sys::get_thread_option(T::VARIANT.into(), buffer) }?;
T::load(&buffer[..len])
}
/// Update a process option value. Requires `T`: [OptionSizeHint].
pub fn set_process_option<'de, T: OptionValue<'de> + OptionSizeHint>(
value: &T::Value,
) -> Result<(), Error>
where
[u8; T::SIZE_HINT]: Sized,
{
let mut buffer = [0; T::SIZE_HINT];
set_process_option_with::<T>(&mut buffer, value)
}
/// Update a process option value, using provided buffer for serialization.
pub fn set_process_option_with<'de, T: OptionValue<'de>>(
buffer: &mut [u8],
value: &T::Value,
) -> Result<(), Error> {
let len = T::store(value, buffer)?;
unsafe { crate::sys::set_process_option(T::VARIANT.into(), &buffer[..len]) }
}
/// Update a thread option value. Requires `T`: [OptionSizeHint].
pub fn set_thread_option<'de, T: OptionValue<'de> + OptionSizeHint>(
value: &T::Value,
) -> Result<(), Error>
where
[u8; T::SIZE_HINT]: Sized,
{
let mut buffer = [0; T::SIZE_HINT];
set_thread_option_with::<T>(&mut buffer, value)
}
/// Update a thread option value, using provided buffer for serialization.
pub fn set_thread_option_with<'de, T: OptionValue<'de>>(
buffer: &mut [u8],
value: &T::Value,
) -> Result<(), Error> {
let len = T::store(value, buffer)?;
unsafe { crate::sys::set_thread_option(T::VARIANT.into(), &buffer[..len]) }
}

View File

View File

@ -4,10 +4,13 @@ use core::ffi::c_int;
use abi::{ use abi::{
error::Error, error::Error,
mem::{MappingFlags, MappingSource}, mem::{MappingFlags, MappingSource},
process::{ExitCode, ProcessOption, Signal, SignalEntryData, ThreadOption}, process::{ExitCode, Signal, SignalEntryData},
}; };
use crate::sync::rwlock::RwLock; use crate::{
process::{self, thread},
sync::rwlock::RwLock,
};
/// Describes how a signal should be handled /// Describes how a signal should be handled
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -76,23 +79,18 @@ pub unsafe fn set_handler(signal: Signal, handler: SignalHandler) -> SignalHandl
/// below it. It is up to the caller to make sure `sp` points to a proper stack's top. /// below it. It is up to the caller to make sure `sp` points to a proper stack's top.
/// ///
/// TLDR: just use [setup_signal_stack]. /// TLDR: just use [setup_signal_stack].
pub unsafe fn set_signal_stack(base: usize, size: usize) -> (usize, usize) { pub unsafe fn set_signal_stack(base: usize, size: usize) {
let mut option = ThreadOption::SignalStack(base, size); process::set_thread_option::<thread::options::SignalStack>(
crate::sys::set_thread_option(&mut option).expect("set_signal_stack() failed"); &thread::options::ThreadSignalStack { base, size },
let ThreadOption::SignalStack(base, size) = option else { )
unreachable!() .expect("set_signal_stack() failed")
};
(base, size)
} }
/// Returns the currently set signal stack base:size. /// Returns the currently set signal stack base:size.
pub fn get_signal_stack() -> (usize, usize) { pub fn get_signal_stack() -> (usize, usize) {
let mut option = ThreadOption::SignalStack(0, 0); let stack = process::get_thread_option!(thread::options::SignalStack)
unsafe { crate::sys::get_thread_option(&mut option) }.expect("get_signal_stack() failed"); .expect("get_signal_stack() failed");
let ThreadOption::SignalStack(base, size) = option else { (stack.base, stack.size)
unreachable!()
};
(base, size)
} }
/// Sets the program's signal entry function. /// Sets the program's signal entry function.
@ -103,15 +101,9 @@ pub fn get_signal_stack() -> (usize, usize) {
/// ///
/// 1. It must not return, it must call `exit_thread` instead. /// 1. It must not return, it must call `exit_thread` instead.
/// 2. It must conform to the kernel's signal entry ABI. /// 2. It must conform to the kernel's signal entry ABI.
pub unsafe fn set_signal_entry(entry: usize) -> usize { pub unsafe fn set_signal_entry(entry: usize) {
let mut option = ProcessOption::SignalEntry(entry); process::set_process_option::<process::options::SignalEntry>(&entry)
crate::sys::set_process_option(&mut option).expect("set_signal_entry() failed"); .expect("set_signal_entry() failed");
#[allow(irrefutable_let_patterns)]
let ProcessOption::SignalEntry(entry) = option
else {
unreachable!()
};
entry
} }
fn allocate_signal_stack(mut size: usize) -> Result<(usize, usize), Error> { fn allocate_signal_stack(mut size: usize) -> Result<(usize, usize), Error> {

View File

@ -8,7 +8,7 @@ use core::{
use abi::{ use abi::{
error::Error, error::Error,
mem::{MappingFlags, MappingSource}, mem::{MappingFlags, MappingSource},
process::{ThreadOption, ThreadSpawnOptions}, process::ThreadSpawnOptions,
}; };
use alloc::{boxed::Box, sync::Arc}; use alloc::{boxed::Box, sync::Arc};
@ -16,6 +16,11 @@ use crate::{process::thread_local, sync::rwlock::RwLock};
use super::{signal, thread_local::TlsImage}; use super::{signal, thread_local::TlsImage};
pub mod options {
//! Thread option definitions
pub use abi::process::thread::*;
}
/// Describes a runtime thread. /// Describes a runtime thread.
/// ///
/// `R` generic parameter denotes the thread's return type. /// `R` generic parameter denotes the thread's return type.
@ -135,7 +140,8 @@ impl<R> Thread<R> {
/// Sets the current thread name. /// Sets the current thread name.
pub fn set_name(name: &str) { pub fn set_name(name: &str) {
unsafe { crate::sys::set_thread_option(&mut ThreadOption::Name(name)).ok() }; let mut buffer = [0; 256];
crate::process::set_thread_option_with::<options::Name>(&mut buffer, &name).ok();
} }
/// # Safety /// # Safety

View File

@ -1,6 +1,8 @@
#![allow(missing_docs)] #![allow(missing_docs)]
use abi::{error::Error, process::ThreadOption}; use abi::error::Error;
use crate::process::{self, thread};
pub fn get_thread_pointer() -> usize { pub fn get_thread_pointer() -> usize {
let tp: usize; let tp: usize;
@ -17,7 +19,7 @@ pub fn get_thread_pointer() -> usize {
/// `value` must hold an address to a structure, first element of which is a pointer to itself. /// `value` must hold an address to a structure, first element of which is a pointer to itself.
/// Usual pointer safety requirements apply. /// Usual pointer safety requirements apply.
pub unsafe fn set_thread_pointer(value: usize) -> Result<(), Error> { pub unsafe fn set_thread_pointer(value: usize) -> Result<(), Error> {
crate::sys::set_thread_option(&mut ThreadOption::ThreadPointer(value)) process::set_process_option::<thread::options::ThreadPointer>(&value)
} }
// ___tls_get_addr, TLS_index structure address gets passed in the %eax register // ___tls_get_addr, TLS_index structure address gets passed in the %eax register

View File

@ -1,6 +1,6 @@
#![allow(missing_docs)] #![allow(missing_docs)]
use abi::{error::Error, process::ThreadOption}; use abi::{error::Error, process::thread};
pub fn get_thread_pointer() -> usize { pub fn get_thread_pointer() -> usize {
let tp: usize; let tp: usize;
@ -17,5 +17,5 @@ pub fn get_thread_pointer() -> usize {
/// `value` must hold an address to a structure, first element of which is a pointer to itself. /// `value` must hold an address to a structure, first element of which is a pointer to itself.
/// Usual pointer safety requirements apply. /// Usual pointer safety requirements apply.
pub unsafe fn set_thread_pointer(value: usize) -> Result<(), Error> { pub unsafe fn set_thread_pointer(value: usize) -> Result<(), Error> {
crate::sys::set_thread_option(&mut ThreadOption::ThreadPointer(value)) crate::process::set_thread_option::<thread::ThreadPointer>(&value)
} }

16
lib/runtime/src/system.rs Normal file
View File

@ -0,0 +1,16 @@
//! System-related parameters
pub use abi::system::*;
use abi::{error::Error, option::OptionValue};
/// Helper macro for [get_system_info]
pub macro get_system_info($variant_ty:ty) {{
let mut buffer = [0; <$variant_ty as $crate::io::OptionSizeHint>::SIZE_HINT];
$crate::system::get_system_info::<$variant_ty>(&mut buffer)
}}
/// Retrieves a system information object
pub fn get_system_info<'de, T: OptionValue<'de>>(buffer: &'de mut [u8]) -> Result<T::Value, Error> {
let len = unsafe { crate::sys::get_system_info(T::VARIANT.into(), buffer) }?;
T::load(&buffer[..len])
}

View File

@ -2,18 +2,15 @@
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
fmt::Write, fmt::Write
os::yggdrasil::{get_system_info, SystemInfo, SystemMemoryStats},
}; };
use humansize::FormatSize; use humansize::FormatSize;
use libterm::{Clear, Term}; use libterm::{Clear, Term};
use yggdrasil_rt::system;
fn get_memory_stats() -> SystemMemoryStats { fn get_memory_stats() -> system::SystemMemoryStats {
let mut info = SystemInfo::MemoryStats(SystemMemoryStats::default()); system::get_system_info!(system::MemoryUsage).unwrap()
get_system_info(&mut info).unwrap();
let SystemInfo::MemoryStats(stats) = info;
stats
} }
fn main() { fn main() {