Misc proc code cleanup

This commit is contained in:
jD91mZM2
2020-06-16 10:07:41 +02:00
parent 4effb97c04
commit 12f632837a
3 changed files with 100 additions and 120 deletions

View File

@@ -48,7 +48,7 @@ use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard};
// |____/ \___||___/___/_|\___/|_| |_|___/
#[derive(Debug)]
struct SessionData {
pub struct SessionData {
breakpoint: Option<Breakpoint>,
events: VecDeque<PtraceEvent>,
file_id: usize,
@@ -63,13 +63,53 @@ impl SessionData {
proc_trigger_event(self.file_id, EVENT_READ);
}
}
/// Override the breakpoint for the specified tracee. Pass `None` to clear
/// breakpoint.
pub fn set_breakpoint(&mut self, flags: Option<PtraceFlags>) {
self.breakpoint = flags.map(|flags| Breakpoint {
reached: false,
flags
});
}
/// Used for getting the flags in fevent
pub fn session_fevent_flags(&self) -> EventFlags {
let mut flags = EventFlags::empty();
if !self.events.is_empty() {
flags |= EVENT_READ;
}
flags
}
/// Poll events, return the amount read. This drains events from the queue.
pub fn recv_events(&mut self, out: &mut [PtraceEvent]) -> usize {
let len = cmp::min(out.len(), self.events.len());
for (dst, src) in out.iter_mut().zip(self.events.drain(..len)) {
*dst = src;
}
len
}
}
#[derive(Debug)]
struct Session {
data: Mutex<SessionData>,
tracee: WaitCondition,
tracer: WaitCondition,
pub struct Session {
pub data: Mutex<SessionData>,
pub tracee: WaitCondition,
pub tracer: WaitCondition,
}
impl Session {
pub fn with_session<F, T>(pid: ContextId, callback: F) -> Result<T>
where
F: FnOnce(&Session) -> Result<T>,
{
let sessions = sessions();
let session = sessions.get(&pid).ok_or(Error::new(ENODEV))?;
callback(session)
}
}
type SessionMap = BTreeMap<ContextId, Arc<Session>>;
@@ -108,33 +148,23 @@ pub fn try_new_session(pid: ContextId, file_id: usize) -> bool {
}
}
/// Returns true if a session is attached to this process
pub fn is_traced(pid: ContextId) -> bool {
sessions().contains_key(&pid)
}
/// Used for getting the flags in fevent
pub fn session_fevent_flags(pid: ContextId) -> Option<EventFlags> {
let sessions = sessions();
let session = sessions.get(&pid)?;
let data = session.data.lock();
let mut flags = EventFlags::empty();
if !data.events.is_empty() {
flags |= EVENT_READ;
}
Some(flags)
}
/// Remove the session from the list of open sessions and notify any
/// waiting processes
pub fn close_session(pid: ContextId) {
if let Some(session) = sessions_mut().remove(&pid) {
session.tracer.notify();
session.tracee.notify();
let data = session.data.lock();
proc_trigger_event(data.file_id, EVENT_READ);
}
}
/// Returns true if a session is attached to this process
pub fn is_traced(pid: ContextId) -> bool {
sessions().contains_key(&pid)
}
/// Trigger a notification to the event: scheme
fn proc_trigger_event(file_id: usize, flags: EventFlags) {
event::trigger(proc::PROC_SCHEME_ID.load(Ordering::SeqCst), file_id, flags);
@@ -144,12 +174,15 @@ fn proc_trigger_event(file_id: usize, flags: EventFlags) {
/// the tracer to wake up and poll for events. Returns Some(()) if an
/// event was sent.
pub fn send_event(event: PtraceEvent) -> Option<()> {
let contexts = context::contexts();
let context = contexts.current()?;
let context = context.read();
let id = {
let contexts = context::contexts();
let context = contexts.current()?;
let context = context.read();
context.id
};
let sessions = sessions();
let session = sessions.get(&context.id)?;
let session = sessions.get(&id)?;
let mut data = session.data.lock();
let breakpoint = data.breakpoint.as_ref()?;
@@ -165,19 +198,6 @@ pub fn send_event(event: PtraceEvent) -> Option<()> {
Some(())
}
/// Poll events, return the amount read
pub fn recv_events(pid: ContextId, out: &mut [PtraceEvent]) -> Option<usize> {
let mut sessions = sessions_mut();
let session = sessions.get_mut(&pid)?;
let mut data = session.data.lock();
let len = cmp::min(out.len(), data.events.len());
for (dst, src) in out.iter_mut().zip(data.events.drain(..len)) {
*dst = src;
}
Some(len)
}
// ____ _ _ _
// | __ ) _ __ ___ __ _| | ___ __ ___ (_)_ __ | |_ ___
// | _ \| '__/ _ \/ _` | |/ / '_ \ / _ \| | '_ \| __/ __|
@@ -191,49 +211,11 @@ struct Breakpoint {
flags: PtraceFlags
}
/// Clear any breakpoints for the process with the specified ID
pub fn clear_breakpoint(pid: ContextId) {
let sessions = sessions();
let session = match sessions.get(&pid) {
Some(session) => session,
None => return
};
let mut data = session.data.lock();
// Remove the breakpoint to make sure any yet unreached but
// obsolete breakpoints don't stop the program.
data.breakpoint = None;
}
/// Notify the tracee of the current session. Returns None if session does not exist
pub fn notify_tracee(pid: ContextId) {
let sessions = sessions();
let session = match sessions.get(&pid) {
Some(session) => session,
None => return
};
session.tracee.notify();
}
/// Create a new breakpoint for the specified tracee, optionally with
/// a sysemu flag. Panics if the session is invalid.
pub fn set_breakpoint(pid: ContextId, flags: PtraceFlags) {
let sessions = sessions_mut();
let session = sessions.get(&pid).expect("proc (set_breakpoint): invalid session");
let mut data = session.data.lock();
data.breakpoint = Some(Breakpoint {
reached: false,
flags
});
}
/// Wait for the tracee to stop. If an event occurs, it returns a copy
/// of that. It will still be available for read using recv_event.
/// Wait for the tracee to stop, or return immediately if there's an unread
/// event.
///
/// Note: Don't call while holding any locks or allocated data, this
/// will switch contexts and may in fact just never terminate.
/// Note: Don't call while holding any locks or allocated data, this will
/// switch contexts and may in fact just never terminate.
pub fn wait(pid: ContextId) -> Result<()> {
loop {
let session = {
@@ -272,7 +254,9 @@ pub fn wait(pid: ContextId) -> Result<()> {
Ok(())
}
/// Notify the tracer and await green flag to continue.
/// Notify the tracer and await green flag to continue. If the breakpoint was
/// set and reached, return the flags which the user waited for. Otherwise,
/// None.
///
/// Note: Don't call while holding any locks or allocated data, this
/// will switch contexts and may in fact just never terminate.
@@ -317,8 +301,6 @@ pub fn breakpoint_callback(match_flags: PtraceFlags, event: Option<PtraceEvent>)
/// Obtain the next breakpoint flags for the current process. This is used for
/// detecting whether or not the tracer decided to use sysemu mode.
// TODO: Check if this is actually safe from race conditions, maybe it
// shouldn't just drop the locks like this...
pub fn next_breakpoint() -> Option<PtraceFlags> {
let contexts = context::contexts();
let context = contexts.current()?;
@@ -332,21 +314,6 @@ pub fn next_breakpoint() -> Option<PtraceFlags> {
Some(breakpoint.flags)
}
/// Call when a context is closed to alert any tracers
pub fn close_tracee(pid: ContextId) -> Option<()> {
let sessions = sessions();
let session = sessions.get(&pid)?;
let mut data = session.data.lock();
// Cause tracers to wake up. Any following action from the tracer will
// return ESRCH which can be used to detect exit.
data.breakpoint = None;
proc_trigger_event(data.file_id, EVENT_READ);
session.tracer.notify();
Some(())
}
// ____ _ _
// | _ \ ___ __ _(_)___| |_ ___ _ __ ___
// | |_) / _ \/ _` | / __| __/ _ \ '__/ __|

View File

@@ -26,7 +26,8 @@ use core::{
use spin::RwLock;
fn with_context<F, T>(pid: ContextId, callback: F) -> Result<T>
where F: FnOnce(&Context) -> Result<T>
where
F: FnOnce(&Context) -> Result<T>,
{
let contexts = context::contexts();
let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
@@ -37,7 +38,8 @@ fn with_context<F, T>(pid: ContextId, callback: F) -> Result<T>
callback(&context)
}
fn with_context_mut<F, T>(pid: ContextId, callback: F) -> Result<T>
where F: FnOnce(&mut Context) -> Result<T>
where
F: FnOnce(&mut Context) -> Result<T>,
{
let contexts = context::contexts();
let context = contexts.get(pid).ok_or(Error::new(ESRCH))?;
@@ -369,7 +371,9 @@ impl Scheme for ProcScheme {
buf.len() / mem::size_of::<PtraceEvent>()
)
};
let read = ptrace::recv_events(info.pid, slice).unwrap_or(0);
let read = ptrace::Session::with_session(info.pid, |session| {
Ok(session.data.lock().recv_events(slice))
})?;
// Won't context switch, don't worry about the locks
let mut handles = self.handles.write();
@@ -468,12 +472,15 @@ impl Scheme for ProcScheme {
let should_continue = !op.contains(PTRACE_FLAG_WAIT) || op.intersects(PTRACE_STOP_MASK);
// Set next breakpoint, or clear it if no stop condition was set and we should continue
if op.intersects(PTRACE_STOP_MASK) {
ptrace::set_breakpoint(info.pid, op);
} else if should_continue {
ptrace::clear_breakpoint(info.pid);
}
// Set next breakpoint
ptrace::Session::with_session(info.pid, |session| {
if op.intersects(PTRACE_STOP_MASK) {
session.data.lock().set_breakpoint(Some(op));
} else if should_continue {
session.data.lock().set_breakpoint(None);
}
Ok(())
})?;
if op.contains(PTRACE_STOP_SINGLESTEP) {
try_stop_context(info.pid, |context| {
@@ -490,6 +497,7 @@ impl Scheme for ProcScheme {
})?;
}
// Continue execution, if requested
if should_continue {
// disable the ptrace_stop flag, which is used in some cases
with_context_mut(info.pid, |context| {
@@ -498,10 +506,13 @@ impl Scheme for ProcScheme {
})?;
// and notify the tracee's WaitCondition, which is used in other cases
ptrace::notify_tracee(info.pid);
ptrace::Session::with_session(info.pid, |session| {
session.tracee.notify();
Ok(())
})?;
}
// And await the tracee, if requested to
// And await the tracee, if requested
if op.contains(PTRACE_FLAG_WAIT) || info.flags & O_NONBLOCK != O_NONBLOCK {
ptrace::wait(info.pid)?;
}
@@ -526,7 +537,9 @@ impl Scheme for ProcScheme {
let handles = self.handles.read();
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
Ok(ptrace::session_fevent_flags(handle.info.pid).expect("proc (fevent): invalid session"))
ptrace::Session::with_session(handle.info.pid, |session| {
Ok(session.data.lock().session_fevent_flags())
})
}
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
@@ -555,12 +568,12 @@ impl Scheme for ProcScheme {
if handle.info.flags & O_EXCL == O_EXCL {
syscall::kill(handle.info.pid, SIGKILL)?;
} else {
let contexts = context::contexts();
if let Some(context) = contexts.get(handle.info.pid) {
let mut context = context.write();
context.ptrace_stop = false;
}
}
let contexts = context::contexts();
if let Some(context) = contexts.get(handle.info.pid) {
let mut context = context.write();
context.ptrace_stop = false;
}
}
Ok(0)

View File

@@ -1151,8 +1151,8 @@ pub fn exit(status: usize) -> ! {
}
}
// Alert any tracers waiting for process (important: AFTER sending waitpid event)
ptrace::close_tracee(pid);
// Alert any tracers waiting of this process
ptrace::close_session(pid);
if pid == ContextId::from(1) {
println!("Main kernel thread exited with status {:X}", status);