kernel: Linux-compatible /proc/[pid]/stat format
Implements proper /proc/[pid]/stat output matching Linux fs/proc/array.c format: - utime/stime: separate user/kernel CPU time accounting in context switch - pgrp/session/start_time: process group, session, boot-time start - proc_rss(): Resident Set Size from page table grant walker - num_threads: thread-group counting via owner_proc_id filter - nice: derived from priority (prio/2 - 20) - proc_stat_line(): proper 52-field /proc/[pid]/stat format - proc_status_text(): VmSize/VmRSS in kB Infrastructure: - require_zero_offset(): EINVAL for non-zero read/write offsets - validate_kfmap_flags(): KFMAP flag validation (MAP_SHARED/PRIVATE) - ContextHandle::Ctx: atomic FullContextRegs save/restore - kwriteoff: offset parameter for regs/ctx/attr/start writes - try_stop_context: preserve HardBlocked::NotYetStarted status on restore
This commit is contained in:
@@ -100,6 +100,10 @@ pub struct Context {
|
||||
pub switch_time: u128,
|
||||
/// Amount of CPU time used
|
||||
pub cpu_time: u128,
|
||||
/// User-mode CPU time used
|
||||
pub utime: u128,
|
||||
/// Kernel-mode CPU time used
|
||||
pub stime: u128,
|
||||
/// Scheduler CPU affinity. If set, [`cpu_id`] can except [`None`] never be anything else than
|
||||
/// this value.
|
||||
pub sched_affinity: LogicalCpuSet,
|
||||
@@ -140,6 +144,12 @@ pub struct Context {
|
||||
pub fmap_ret: Option<Frame>,
|
||||
/// Priority
|
||||
pub prio: usize,
|
||||
/// Process group ID.
|
||||
pub pgrp: usize,
|
||||
/// Session ID.
|
||||
pub session: usize,
|
||||
/// Context start time in nanoseconds since boot.
|
||||
pub start_time: u128,
|
||||
|
||||
// TODO: id can reappear after wraparound?
|
||||
pub owner_proc_id: Option<NonZeroUsize>,
|
||||
@@ -186,6 +196,8 @@ impl Context {
|
||||
cpu_id: None,
|
||||
switch_time: 0,
|
||||
cpu_time: 0,
|
||||
utime: 0,
|
||||
stime: 0,
|
||||
sched_affinity: LogicalCpuSet::all(),
|
||||
inside_syscall: false,
|
||||
syscall_head: SyscallFrame::Free(RaiiFrame::allocate()?),
|
||||
@@ -207,6 +219,9 @@ impl Context {
|
||||
egid: 0,
|
||||
pid: 0,
|
||||
groups: Vec::new(),
|
||||
pgrp: 0,
|
||||
session: 0,
|
||||
start_time: 0,
|
||||
|
||||
#[cfg(feature = "syscall_debug")]
|
||||
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
|
||||
|
||||
@@ -131,6 +131,7 @@ pub fn init(token: &mut CleanLockToken) {
|
||||
context.status = Status::Runnable;
|
||||
context.running = true;
|
||||
context.cpu_id = Some(crate::cpu_id());
|
||||
context.start_time = crate::time::monotonic(token);
|
||||
|
||||
let context_lock = Arc::new(ContextLock::new(context));
|
||||
|
||||
@@ -223,6 +224,9 @@ pub fn spawn(
|
||||
let stack = Kstack::new()?;
|
||||
|
||||
let mut context = Context::new(owner_proc_id)?;
|
||||
context.start_time = crate::time::monotonic(token);
|
||||
context.pgrp = context.pid;
|
||||
context.session = context.pid;
|
||||
|
||||
let _ = context.set_addr_space(Some(AddrSpaceWrapper::new()?), token.downgrade());
|
||||
context
|
||||
|
||||
@@ -221,7 +221,13 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
||||
|
||||
// Update times
|
||||
if !was_idle {
|
||||
prev_context.cpu_time += switch_time.saturating_sub(prev_context.switch_time);
|
||||
let delta = switch_time.saturating_sub(prev_context.switch_time);
|
||||
prev_context.cpu_time += delta;
|
||||
if prev_context.userspace {
|
||||
prev_context.utime += delta;
|
||||
} else {
|
||||
prev_context.stime += delta;
|
||||
}
|
||||
}
|
||||
next_context.switch_time = switch_time;
|
||||
if next_context.userspace {
|
||||
|
||||
+217
-18
@@ -89,7 +89,9 @@ fn try_stop_context<T>(
|
||||
let (context, token) = context.token_split();
|
||||
let ret = callback(context, token);
|
||||
|
||||
context.status = prev_status;
|
||||
if matches!(context.status, Status::HardBlocked { reason: HardBlockedReason::NotYetStarted }) {
|
||||
context.status = prev_status;
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
@@ -112,7 +114,9 @@ enum ContextHandle {
|
||||
privileged: bool,
|
||||
}, // can write ContextVerb
|
||||
|
||||
/// Deprecated compatibility handles; prefer `ctx` for atomic save/restore.
|
||||
Regs(RegsKind),
|
||||
Ctx,
|
||||
Sighandler,
|
||||
Start,
|
||||
NewFiletable {
|
||||
@@ -237,6 +241,54 @@ fn proc_vsize(addr_space: &Option<Arc<AddrSpaceWrapper>>, token: &mut CleanLockT
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn proc_rss(addr_space: &Option<Arc<AddrSpaceWrapper>>, token: &mut CleanLockToken) -> usize {
|
||||
addr_space
|
||||
.as_ref()
|
||||
.map(|addrspace| {
|
||||
let guard = addrspace.acquire_read(token.downgrade());
|
||||
guard
|
||||
.grants
|
||||
.iter()
|
||||
.map(|(_, info)| {
|
||||
use crate::context::memory::Provider;
|
||||
|
||||
match info.provider {
|
||||
Provider::Allocated { .. }
|
||||
| Provider::AllocatedShared { .. }
|
||||
| Provider::PhysBorrowed { .. }
|
||||
| Provider::External { .. }
|
||||
| Provider::FmapBorrowed { .. } => info.page_count() * PAGE_SIZE,
|
||||
}
|
||||
})
|
||||
.sum()
|
||||
})
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn require_zero_offset(offset: u64) -> Result<()> {
|
||||
if offset == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_kfmap_flags(flags: MapFlags, consume: bool) -> Result<()> {
|
||||
let flags = MapFlags::from_bits(flags.bits()).ok_or(Error::new(EINVAL))?;
|
||||
let shared = flags.contains(MapFlags::MAP_SHARED);
|
||||
let private = flags.contains(MapFlags::MAP_PRIVATE);
|
||||
if shared == private {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
if flags.contains(MapFlags::MAP_FIXED) && flags.contains(MapFlags::MAP_FIXED_NOREPLACE) {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
if consume && !shared {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn proc_context(pid: usize, token: &mut CleanLockToken) -> Option<Arc<ContextLock>> {
|
||||
let mut contexts = context::contexts(token.downgrade());
|
||||
let (contexts, mut token2) = contexts.token_split();
|
||||
@@ -254,7 +306,14 @@ fn proc_stat_line(
|
||||
ppid: usize,
|
||||
priority: usize,
|
||||
utime: u128,
|
||||
stime: u128,
|
||||
pgrp: usize,
|
||||
session: usize,
|
||||
nice: i32,
|
||||
num_threads: usize,
|
||||
starttime: u128,
|
||||
vsize: usize,
|
||||
rss: usize,
|
||||
) -> String {
|
||||
format!(
|
||||
"{pid} ({comm}) {state} {ppid} {pgrp} {session} {tty_nr} {tpgid} {flags} {minflt} {cminflt} {majflt} {cmajflt} {utime} {stime} {cutime} {cstime} {priority} {nice} {num_threads} {itrealvalue} {starttime} {vsize} {rss} {rsslim}\n",
|
||||
@@ -262,8 +321,8 @@ fn proc_stat_line(
|
||||
comm = comm,
|
||||
state = state,
|
||||
ppid = ppid,
|
||||
pgrp = ppid,
|
||||
session = ppid,
|
||||
pgrp = pgrp,
|
||||
session = session,
|
||||
tty_nr = 0,
|
||||
tpgid = 0,
|
||||
flags = 0,
|
||||
@@ -272,32 +331,41 @@ fn proc_stat_line(
|
||||
majflt = 0,
|
||||
cmajflt = 0,
|
||||
utime = utime,
|
||||
stime = 0,
|
||||
stime = stime,
|
||||
cutime = 0,
|
||||
cstime = 0,
|
||||
priority = priority,
|
||||
nice = 0,
|
||||
num_threads = 1,
|
||||
nice = nice,
|
||||
num_threads = num_threads,
|
||||
itrealvalue = 0,
|
||||
starttime = 0,
|
||||
starttime = starttime,
|
||||
vsize = vsize,
|
||||
rss = 0,
|
||||
rss = rss / PAGE_SIZE,
|
||||
rsslim = 0,
|
||||
)
|
||||
}
|
||||
|
||||
fn proc_status_text(context: &Context) -> String {
|
||||
fn proc_status_text(context: &Context, num_threads: usize, vsize: usize, rss: usize) -> String {
|
||||
let comm = context.name.as_str();
|
||||
let state = proc_status_char(&context.status);
|
||||
let ppid = context.owner_proc_id.map_or(0, NonZeroUsize::get);
|
||||
format!(
|
||||
"Name:\t{}\nState:\t{}\nPid:\t{}\nPPid:\t{}\nUid:\t{}\nGid:\t{}\nThreads:\t1\nCpus_allowed_list:\t{}\n",
|
||||
"Name:\t{}\nState:\t{}\nPid:\t{}\nPPid:\t{}\nUid:\t{}\t{}\t{}\t{}\nGid:\t{}\t{}\t{}\t{}\nVmSize:\t{} kB\nVmRSS:\t{} kB\nThreads:\t{}\nCpus_allowed_list:\t{}\n",
|
||||
comm,
|
||||
state,
|
||||
context.pid,
|
||||
ppid,
|
||||
context.euid,
|
||||
context.euid,
|
||||
context.euid,
|
||||
context.euid,
|
||||
context.egid,
|
||||
context.egid,
|
||||
context.egid,
|
||||
context.egid,
|
||||
vsize / 1024,
|
||||
rss / 1024,
|
||||
num_threads,
|
||||
context.sched_affinity
|
||||
)
|
||||
}
|
||||
@@ -361,6 +429,8 @@ impl ProcScheme {
|
||||
),
|
||||
"current-addrspace" => (ContextHandle::CurrentAddrSpace, false),
|
||||
"current-filetable" => (ContextHandle::CurrentFiletable, false),
|
||||
"ctx" => (ContextHandle::Ctx, false),
|
||||
// Deprecated compatibility handles; prefer `ctx` for atomic save/restore.
|
||||
"regs/float" => (ContextHandle::Regs(RegsKind::Float), false),
|
||||
"regs/int" => (ContextHandle::Regs(RegsKind::Int), false),
|
||||
"regs/env" => (ContextHandle::Regs(RegsKind::Env), false),
|
||||
@@ -667,7 +737,7 @@ impl KernelScheme for ProcScheme {
|
||||
|
||||
let mut notify_files = Vec::new();
|
||||
|
||||
// TODO: Validate flags
|
||||
validate_kfmap_flags(map.flags, consume)?;
|
||||
let result_base = if consume {
|
||||
dst_addr_space.r#move(
|
||||
Some((addrspace, &mut *src_addr_space)),
|
||||
@@ -802,7 +872,7 @@ impl KernelScheme for ProcScheme {
|
||||
&self,
|
||||
id: usize,
|
||||
buf: UserSliceRo,
|
||||
_offset: u64,
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_stored_flags: u32,
|
||||
token: &mut CleanLockToken,
|
||||
@@ -817,7 +887,7 @@ impl KernelScheme for ProcScheme {
|
||||
};
|
||||
|
||||
let Handle { context, kind } = handle;
|
||||
kind.kwriteoff(id, context, buf, token)
|
||||
kind.kwriteoff(id, context, buf, offset, token)
|
||||
}
|
||||
|
||||
fn kfpath(&self, _id: usize, buf: UserSliceWo, _token: &mut CleanLockToken) -> Result<usize> {
|
||||
@@ -1091,12 +1161,21 @@ impl ContextHandle {
|
||||
id: usize,
|
||||
context: Arc<ContextLock>,
|
||||
buf: UserSliceRo,
|
||||
offset: u64,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<usize> {
|
||||
match self {
|
||||
Self::AddrSpace { addrspace } => {
|
||||
let mut chunks = buf.usizes();
|
||||
let mut words_read = 0;
|
||||
let word_size = size_of::<usize>() as u64;
|
||||
if offset % word_size != 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let offset_words = usize::try_from(offset / word_size).map_err(|_| Error::new(EINVAL))?;
|
||||
for _ in 0..offset_words {
|
||||
chunks.next().ok_or(Error::new(EINVAL))??;
|
||||
}
|
||||
let mut next = || {
|
||||
words_read += 1;
|
||||
chunks.next().ok_or(Error::new(EINVAL))
|
||||
@@ -1164,8 +1243,24 @@ impl ContextHandle {
|
||||
}
|
||||
Ok(words_read * size_of::<usize>())
|
||||
}
|
||||
ContextHandle::Ctx => {
|
||||
require_zero_offset(offset)?;
|
||||
let regs = unsafe { buf.read_exact::<syscall::FullContextRegs>()? };
|
||||
try_stop_context(context, token, |context, _| {
|
||||
context.set_fx_regs(regs.float);
|
||||
match context.regs_mut() {
|
||||
None => Err(Error::new(ENOTRECOVERABLE)),
|
||||
Some(stack) => {
|
||||
stack.load(®s.int);
|
||||
context.write_env_regs(regs.env)
|
||||
}
|
||||
}
|
||||
})?;
|
||||
Ok(size_of::<syscall::FullContextRegs>())
|
||||
}
|
||||
ContextHandle::Regs(kind) => match kind {
|
||||
RegsKind::Float => {
|
||||
require_zero_offset(offset)?;
|
||||
let regs = unsafe { buf.read_exact::<FloatRegisters>()? };
|
||||
|
||||
try_stop_context(context, token, |context, _| {
|
||||
@@ -1179,6 +1274,7 @@ impl ContextHandle {
|
||||
})
|
||||
}
|
||||
RegsKind::Int => {
|
||||
require_zero_offset(offset)?;
|
||||
let regs = unsafe { buf.read_exact::<IntRegisters>()? };
|
||||
|
||||
try_stop_context(context, token, |context, _| match context.regs_mut() {
|
||||
@@ -1198,12 +1294,14 @@ impl ContextHandle {
|
||||
})
|
||||
}
|
||||
RegsKind::Env => {
|
||||
require_zero_offset(offset)?;
|
||||
let regs = unsafe { buf.read_exact::<EnvRegisters>()? };
|
||||
write_env_regs(context, regs, token)?;
|
||||
Ok(size_of::<EnvRegisters>())
|
||||
}
|
||||
},
|
||||
ContextHandle::Sighandler => {
|
||||
require_zero_offset(offset)?;
|
||||
let data = unsafe { buf.read_exact::<SetSighandlerData>()? };
|
||||
|
||||
if data.user_handler >= crate::USER_END_OFFSET
|
||||
@@ -1259,6 +1357,7 @@ impl ContextHandle {
|
||||
Ok(size_of::<SetSighandlerData>())
|
||||
}
|
||||
ContextHandle::Start => match context.write(token.token()).status {
|
||||
_ if offset != 0 => Err(Error::new(EINVAL)),
|
||||
ref mut status @ Status::HardBlocked {
|
||||
reason: HardBlockedReason::NotYetStarted,
|
||||
} => {
|
||||
@@ -1272,6 +1371,7 @@ impl ContextHandle {
|
||||
}
|
||||
|
||||
ContextHandle::CurrentFiletable => {
|
||||
require_zero_offset(offset)?;
|
||||
let filetable_fd = buf.read_usize()?;
|
||||
let (hopefully_this_scheme, number) = extract_scheme_number(filetable_fd, token)?;
|
||||
verify_scheme(hopefully_this_scheme)?;
|
||||
@@ -1317,6 +1417,7 @@ impl ContextHandle {
|
||||
Ok(size_of::<usize>())
|
||||
}
|
||||
ContextHandle::CurrentAddrSpace => {
|
||||
require_zero_offset(offset)?;
|
||||
let mut iter = buf.usizes();
|
||||
let addrspace_fd = iter.next().ok_or(Error::new(EINVAL))??;
|
||||
let sp = iter.next().ok_or(Error::new(EINVAL))??;
|
||||
@@ -1354,6 +1455,7 @@ impl ContextHandle {
|
||||
Ok(written)
|
||||
}
|
||||
Self::MmapMinAddr(ref addrspace) => {
|
||||
require_zero_offset(offset)?;
|
||||
let val = buf.read_usize()?;
|
||||
if val % PAGE_SIZE != 0 || val > crate::USER_END_OFFSET {
|
||||
return Err(Error::new(EINVAL));
|
||||
@@ -1363,6 +1465,7 @@ impl ContextHandle {
|
||||
Ok(size_of::<usize>())
|
||||
}
|
||||
Self::SchedAffinity => {
|
||||
require_zero_offset(offset)?;
|
||||
let mask = unsafe { buf.read_exact::<crate::cpu_set::RawMask>()? };
|
||||
|
||||
context
|
||||
@@ -1398,7 +1501,16 @@ impl ContextHandle {
|
||||
guard.status = Status::HardBlocked {
|
||||
reason: HardBlockedReason::Stopped,
|
||||
};
|
||||
// TODO: wait for context to be switched away from, and/or IPI?
|
||||
drop(guard);
|
||||
|
||||
let mut spins = 10_000usize;
|
||||
while context.read(token.token()).running {
|
||||
if spins == 0 {
|
||||
return Err(Error::new(EAGAIN));
|
||||
}
|
||||
spins -= 1;
|
||||
context::switch(token);
|
||||
}
|
||||
Ok(size_of::<usize>())
|
||||
}
|
||||
ContextVerb::Unstop => {
|
||||
@@ -1458,6 +1570,7 @@ impl ContextHandle {
|
||||
}
|
||||
}
|
||||
ContextHandle::Attr => {
|
||||
require_zero_offset(offset)?;
|
||||
let info = unsafe { buf.read_exact::<ProcSchemeAttrs>()? };
|
||||
let mut guard = context.write(token.token());
|
||||
|
||||
@@ -1476,9 +1589,16 @@ impl ContextHandle {
|
||||
guard.euid = info.euid;
|
||||
guard.egid = info.egid;
|
||||
guard.prio = (info.prio as usize).min(39);
|
||||
if guard.pgrp == 0 {
|
||||
guard.pgrp = guard.pid;
|
||||
}
|
||||
if guard.session == 0 {
|
||||
guard.session = guard.pid;
|
||||
}
|
||||
Ok(size_of::<ProcSchemeAttrs>())
|
||||
}
|
||||
Self::Groups => {
|
||||
require_zero_offset(offset)?;
|
||||
const NGROUPS_MAX: usize = 65536;
|
||||
if buf.len() % size_of::<u32>() != 0 {
|
||||
return Err(Error::new(EINVAL));
|
||||
@@ -1512,6 +1632,7 @@ impl ContextHandle {
|
||||
Ok(count * size_of::<u32>())
|
||||
}
|
||||
ContextHandle::OpenViaDup => {
|
||||
require_zero_offset(offset)?;
|
||||
let mut args = buf.usizes();
|
||||
|
||||
let user_data = args.next().ok_or(Error::new(EINVAL))??;
|
||||
@@ -1566,6 +1687,35 @@ impl ContextHandle {
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<usize> {
|
||||
match self {
|
||||
ContextHandle::Ctx => {
|
||||
require_zero_offset(offset)?;
|
||||
let mut regs = syscall::FullContextRegs::default();
|
||||
try_stop_context(context, token, |context, _| {
|
||||
match context.regs() {
|
||||
None => {
|
||||
assert!(!context.running, "try_stop_context is broken, clearly");
|
||||
println!(
|
||||
"{}:{}: Couldn't read registers from stopped process",
|
||||
file!(),
|
||||
line!()
|
||||
);
|
||||
Err(Error::new(ENOTRECOVERABLE))
|
||||
}
|
||||
Some(stack) => {
|
||||
stack.save(&mut regs.int);
|
||||
regs.float = context.get_fx_regs();
|
||||
regs.env = context.read_env_regs()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})?;
|
||||
buf.copy_common_bytes_from_slice(unsafe {
|
||||
slice::from_raw_parts(
|
||||
®s as *const syscall::FullContextRegs as *const u8,
|
||||
size_of::<syscall::FullContextRegs>(),
|
||||
)
|
||||
})
|
||||
}
|
||||
ContextHandle::Regs(kind) => {
|
||||
union Output {
|
||||
float: FloatRegisters,
|
||||
@@ -1658,7 +1808,7 @@ impl ContextHandle {
|
||||
ContextHandle::Filetable { data, .. } => read_from(buf, data, offset),
|
||||
ContextHandle::ProcStat { pid } => {
|
||||
let context = proc_context(*pid, token).ok_or(Error::new(ESRCH))?;
|
||||
let (pid, comm, state, ppid, priority, utime, addr_space) = {
|
||||
let (pid, comm, state, ppid, priority, utime, stime, pgrp, session, starttime, owner_proc_id, addr_space) = {
|
||||
let context = context.read(token.token());
|
||||
(
|
||||
context.pid,
|
||||
@@ -1666,14 +1816,45 @@ impl ContextHandle {
|
||||
proc_status_char(&context.status),
|
||||
context.owner_proc_id.map_or(0, NonZeroUsize::get),
|
||||
context.prio,
|
||||
context.cpu_time / crate::time::NANOS_PER_SEC,
|
||||
context.utime / crate::time::NANOS_PER_SEC,
|
||||
context.stime / crate::time::NANOS_PER_SEC,
|
||||
context.pgrp,
|
||||
context.session,
|
||||
context.start_time / (crate::time::NANOS_PER_SEC / 100),
|
||||
context.owner_proc_id,
|
||||
context.addr_space.clone(),
|
||||
)
|
||||
};
|
||||
let num_threads = {
|
||||
let mut contexts = context::contexts(token.downgrade());
|
||||
let (contexts, mut token2) = contexts.token_split();
|
||||
contexts
|
||||
.iter()
|
||||
.filter(|ctx| ctx.read(token2.token()).owner_proc_id == owner_proc_id)
|
||||
.count()
|
||||
};
|
||||
let vsize = proc_vsize(&addr_space, token);
|
||||
let rss = proc_rss(&addr_space, token);
|
||||
let nice = (priority as i32 / 2) - 20;
|
||||
read_from(
|
||||
buf,
|
||||
proc_stat_line(pid, &comm, state, ppid, priority, utime, vsize).as_bytes(),
|
||||
proc_stat_line(
|
||||
pid,
|
||||
&comm,
|
||||
state,
|
||||
ppid,
|
||||
priority,
|
||||
utime,
|
||||
stime,
|
||||
pgrp,
|
||||
session,
|
||||
nice,
|
||||
num_threads,
|
||||
starttime,
|
||||
vsize,
|
||||
rss,
|
||||
)
|
||||
.as_bytes(),
|
||||
offset,
|
||||
)
|
||||
}
|
||||
@@ -1691,8 +1872,26 @@ impl ContextHandle {
|
||||
}
|
||||
ContextHandle::ProcStatus { pid } => {
|
||||
let context = proc_context(*pid, token).ok_or(Error::new(ESRCH))?;
|
||||
let owner_proc_id = context.read(token.token()).owner_proc_id;
|
||||
let num_threads = {
|
||||
let mut contexts = context::contexts(token.downgrade());
|
||||
let (contexts, mut token2) = contexts.token_split();
|
||||
contexts
|
||||
.iter()
|
||||
.filter(|ctx| ctx.read(token2.token()).owner_proc_id == owner_proc_id)
|
||||
.count()
|
||||
};
|
||||
let (vsize, rss) = {
|
||||
let addr_space = {
|
||||
let context = context.read(token.token());
|
||||
context.addr_space.clone()
|
||||
};
|
||||
let vsize = proc_vsize(&addr_space, token);
|
||||
let rss = proc_rss(&addr_space, token);
|
||||
(vsize, rss)
|
||||
};
|
||||
let context = context.read(token.token());
|
||||
read_from(buf, proc_status_text(&context).as_bytes(), offset)
|
||||
read_from(buf, proc_status_text(&context, num_threads, vsize, rss).as_bytes(), offset)
|
||||
}
|
||||
ContextHandle::ProcRoot | ContextHandle::ProcDir { .. } => Err(Error::new(EBADF)),
|
||||
ContextHandle::MmapMinAddr(addrspace) => {
|
||||
|
||||
Reference in New Issue
Block a user