Refactor performance profiler
1. Add the thread profiler to Thread structure 2. Fix bugs introduced by previous commits
This commit is contained in:
parent
86d11e9d44
commit
ec970f0e76
@ -39,25 +39,11 @@ pub fn exec(libos_tid: pid_t, host_tid: pid_t) -> Result<i32> {
|
||||
// Enable current::get() from now on
|
||||
current::set(this_thread.clone());
|
||||
|
||||
#[cfg(feature = "syscall_timing")]
|
||||
GLOBAL_PROFILER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.thread_enter()
|
||||
.expect("unexpected error from profiler to enter thread");
|
||||
|
||||
unsafe {
|
||||
// task may only be modified by this function; so no lock is needed
|
||||
do_exec_task(this_thread.task() as *const Task as *mut Task);
|
||||
}
|
||||
|
||||
#[cfg(feature = "syscall_timing")]
|
||||
GLOBAL_PROFILER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.thread_exit()
|
||||
.expect("unexpected error from profiler to exit thread");
|
||||
|
||||
let term_status = this_thread.inner().term_status().unwrap();
|
||||
match term_status {
|
||||
TermStatus::Exited(status) => {
|
||||
|
@ -5,6 +5,7 @@ use super::{
|
||||
SigQueues, SigSet, Task, Thread, ThreadId, ThreadInner, ThreadRef,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::time::ThreadProfiler;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ThreadBuilder {
|
||||
@ -102,6 +103,11 @@ impl ThreadBuilder {
|
||||
let sig_mask = SgxRwLock::new(SigSet::new_empty());
|
||||
let sig_tmp_mask = SgxRwLock::new(SigSet::new_empty());
|
||||
let sig_stack = SgxMutex::new(None);
|
||||
let profiler = if cfg!(feature = "syscall_timing") {
|
||||
SgxMutex::new(Some(ThreadProfiler::new()))
|
||||
} else {
|
||||
SgxMutex::new(None)
|
||||
};
|
||||
|
||||
let new_thread = Arc::new(Thread {
|
||||
task,
|
||||
@ -118,6 +124,7 @@ impl ThreadBuilder {
|
||||
sig_mask,
|
||||
sig_tmp_mask,
|
||||
sig_stack,
|
||||
profiler,
|
||||
});
|
||||
|
||||
let mut inner = new_thread.process().inner();
|
||||
|
@ -8,6 +8,7 @@ use super::{
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::signal::{SigQueues, SigSet, SigStack};
|
||||
use crate::time::ThreadProfiler;
|
||||
|
||||
pub use self::builder::ThreadBuilder;
|
||||
pub use self::id::ThreadId;
|
||||
@ -36,6 +37,8 @@ pub struct Thread {
|
||||
sig_mask: SgxRwLock<SigSet>,
|
||||
sig_tmp_mask: SgxRwLock<SigSet>,
|
||||
sig_stack: SgxMutex<Option<SigStack>>,
|
||||
// System call timing
|
||||
profiler: SgxMutex<Option<ThreadProfiler>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
@ -97,6 +100,11 @@ impl Thread {
|
||||
&self.sig_stack
|
||||
}
|
||||
|
||||
/// Get the alternate thread performance profiler
|
||||
pub fn profiler(&self) -> &SgxMutex<Option<ThreadProfiler>> {
|
||||
&self.profiler
|
||||
}
|
||||
|
||||
/// Get a file from the file table.
|
||||
pub fn file(&self, fd: FileDesc) -> Result<FileRef> {
|
||||
self.files().lock().unwrap().get(fd)
|
||||
@ -126,9 +134,27 @@ impl Thread {
|
||||
pub(super) fn start(&self, host_tid: pid_t) {
|
||||
self.sched().lock().unwrap().attach(host_tid);
|
||||
self.inner().start();
|
||||
|
||||
#[cfg(feature = "syscall_timing")]
|
||||
self.profiler()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.start()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub(super) fn exit(&self, term_status: TermStatus) -> usize {
|
||||
#[cfg(feature = "syscall_timing")]
|
||||
self.profiler()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.stop()
|
||||
.unwrap();
|
||||
|
||||
self.sched().lock().unwrap().detach();
|
||||
|
||||
// Remove this thread from its owner process
|
||||
@ -171,6 +197,7 @@ impl fmt::Debug for Thread {
|
||||
.field("vm", self.vm())
|
||||
.field("fs", self.fs())
|
||||
.field("files", self.files())
|
||||
.field("profiler", self.profiler())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use std::convert::TryFrom;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::ptr;
|
||||
use time::{clockid_t, timespec_t, timeval_t, GLOBAL_PROFILER};
|
||||
use time::{clockid_t, timespec_t, timeval_t};
|
||||
use util::log::{self, LevelFilter};
|
||||
use util::mem_util::from_user::*;
|
||||
|
||||
@ -609,40 +609,48 @@ fn do_syscall(user_context: &mut CpuContext) {
|
||||
let arg4 = user_context.r8 as isize;
|
||||
let arg5 = user_context.r9 as isize;
|
||||
|
||||
// TODO: the profiler will trigger panic for syscall simulation
|
||||
#[cfg(feature = "syscall_timing")]
|
||||
GLOBAL_PROFILER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.syscall_enter(syscall_num)
|
||||
.expect("unexpected error from profiler to enter syscall");
|
||||
|
||||
let ret = Syscall::new(num, arg0, arg1, arg2, arg3, arg4, arg5).and_then(|mut syscall| {
|
||||
log::set_round_desc(Some(syscall.num.as_str()));
|
||||
trace!("{:?}", &syscall);
|
||||
let syscall_num = syscall.num;
|
||||
|
||||
// Pass user_context as an extra argument to two special syscalls that
|
||||
// need to modify it
|
||||
if syscall.num == SyscallNum::RtSigreturn {
|
||||
if syscall_num == SyscallNum::RtSigreturn {
|
||||
syscall.args[0] = user_context as *mut _ as isize;
|
||||
} else if syscall.num == SyscallNum::HandleException {
|
||||
} else if syscall_num == SyscallNum::HandleException {
|
||||
// syscall.args[0] == info
|
||||
syscall.args[1] = user_context as *mut _ as isize;
|
||||
} else if syscall.num == SyscallNum::Sigaltstack {
|
||||
} else if syscall_num == SyscallNum::Sigaltstack {
|
||||
// syscall.args[0] == new_ss
|
||||
// syscall.args[1] == old_ss
|
||||
syscall.args[2] = user_context as *const _ as isize;
|
||||
}
|
||||
|
||||
dispatch_syscall(syscall)
|
||||
});
|
||||
#[cfg(feature = "syscall_timing")]
|
||||
current!()
|
||||
.profiler()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.syscall_enter(syscall_num)
|
||||
.expect("unexpected error from profiler to enter syscall");
|
||||
|
||||
#[cfg(feature = "syscall_timing")]
|
||||
GLOBAL_PROFILER
|
||||
.lock()
|
||||
.unwrap()
|
||||
.syscall_exit(syscall_num, ret.is_err())
|
||||
.expect("unexpected error from profiler to exit syscall");
|
||||
let ret = dispatch_syscall(syscall);
|
||||
|
||||
#[cfg(feature = "syscall_timing")]
|
||||
current!()
|
||||
.profiler()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.syscall_exit(syscall_num, ret.is_err())
|
||||
.expect("unexpected error from profiler to exit syscall");
|
||||
|
||||
ret
|
||||
});
|
||||
|
||||
let retval = match ret {
|
||||
Ok(retval) => retval as isize,
|
||||
|
@ -10,7 +10,7 @@ use syscall::SyscallNum;
|
||||
mod profiler;
|
||||
pub mod up_time;
|
||||
|
||||
pub use profiler::GLOBAL_PROFILER;
|
||||
pub use profiler::ThreadProfiler;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type time_t = i64;
|
||||
|
@ -1,66 +1,8 @@
|
||||
use super::*;
|
||||
use std::fmt::Write;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref GLOBAL_PROFILER: SgxMutex<GlobalProfiler> = SgxMutex::new(GlobalProfiler::new());
|
||||
}
|
||||
|
||||
/// A profiler that can be used across threads.
|
||||
// TODO: Use light-weight thread_local storage other than mutex
|
||||
pub struct GlobalProfiler {
|
||||
inner: HashMap<pid_t, ThreadProfiler>,
|
||||
}
|
||||
|
||||
impl GlobalProfiler {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn thread_enter(&mut self) -> Result<()> {
|
||||
let tid = current!().tid();
|
||||
if self.inner.insert(tid, ThreadProfiler::new()).is_some() {
|
||||
return_errno!(
|
||||
EINVAL,
|
||||
"global profiler should exit the thread before entering"
|
||||
);
|
||||
}
|
||||
self.inner.get_mut(&tid).unwrap().start()
|
||||
}
|
||||
|
||||
pub fn thread_exit(&mut self) -> Result<()> {
|
||||
// A thread exits by invoking exit syscall which
|
||||
// will never return
|
||||
self.syscall_exit(SyscallNum::Exit, false);
|
||||
|
||||
let tid = current!().tid();
|
||||
|
||||
let mut exiting_profiler = self.inner.remove(&tid).ok_or_else(|| {
|
||||
errno!(
|
||||
EINVAL,
|
||||
"global profiler should enter a thread before exit one"
|
||||
)
|
||||
})?;
|
||||
exiting_profiler.stop()?;
|
||||
exiting_profiler.display()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn syscall_enter(&mut self, syscall_num: SyscallNum) -> Result<()> {
|
||||
let tid = current!().tid();
|
||||
let mut prof = self.inner.get_mut(&tid).unwrap();
|
||||
prof.syscall_enter(syscall_num)
|
||||
}
|
||||
|
||||
pub fn syscall_exit(&mut self, syscall_num: SyscallNum, is_err: bool) -> Result<()> {
|
||||
let tid = current!().tid();
|
||||
let mut prof = self.inner.get_mut(&tid).unwrap();
|
||||
prof.syscall_exit(syscall_num, is_err)
|
||||
}
|
||||
}
|
||||
/// A profiler used inside a thread.
|
||||
// TODO: add support for exception
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ThreadProfiler {
|
||||
syscall_data: HashMap<SyscallNum, PerfEntry>,
|
||||
start_time: ProfileTime,
|
||||
@ -83,7 +25,7 @@ impl ThreadProfiler {
|
||||
}
|
||||
}
|
||||
|
||||
fn start(&mut self) -> Result<()> {
|
||||
pub fn start(&mut self) -> Result<()> {
|
||||
match self.status {
|
||||
Status::Stopped(..) => {
|
||||
self.status = Status::Running;
|
||||
@ -96,21 +38,26 @@ impl ThreadProfiler {
|
||||
}
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> Result<()> {
|
||||
if self.status != Status::Running {
|
||||
return_errno!(EINVAL, "fail to stop thread profiler");
|
||||
}
|
||||
let real = time::do_gettimeofday().as_duration() - self.start_time.get_realtime().unwrap();
|
||||
|
||||
pub fn stop(&mut self) -> Result<()> {
|
||||
let total_cputime =
|
||||
time::do_thread_getcpuclock()?.as_duration() - self.start_time.get_cputime().unwrap();
|
||||
|
||||
let real = time::do_gettimeofday().as_duration() - self.start_time.get_realtime().unwrap();
|
||||
let sys = self.get_syscall_total_time()?;
|
||||
let usr = total_cputime - sys;
|
||||
|
||||
self.status = Status::Stopped(TimeSummary::new(real, usr, sys));
|
||||
|
||||
self.display()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn syscall_enter(&mut self, syscall_num: SyscallNum) -> Result<()> {
|
||||
pub fn syscall_enter(&mut self, syscall_num: SyscallNum) -> Result<()> {
|
||||
if Self::is_not_traced(syscall_num) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match self.status {
|
||||
Status::Running => {
|
||||
let mut cur_time = ProfileTime::CpuTime(Default::default());
|
||||
@ -124,17 +71,28 @@ impl ThreadProfiler {
|
||||
.or_insert(PerfEntry::new());
|
||||
Ok(())
|
||||
}
|
||||
_ => return_errno!(
|
||||
EINVAL,
|
||||
"threa profiler should be started before entering syscall"
|
||||
),
|
||||
_ => {
|
||||
eprintln!(
|
||||
"The wrong status is {:?} the input syscall is {:?}",
|
||||
self.status, syscall_num
|
||||
);
|
||||
return_errno!(
|
||||
EINVAL,
|
||||
"thread profiler should be started before entering syscall"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn syscall_exit(&mut self, syscall_num: SyscallNum, is_err: bool) -> Result<()> {
|
||||
pub fn syscall_exit(&mut self, syscall_num: SyscallNum, is_err: bool) -> Result<()> {
|
||||
if Self::is_not_traced(syscall_num) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match self.status {
|
||||
Status::InSyscall { start_cpu, num } => {
|
||||
if syscall_num != num {
|
||||
eprintln!("current {:?} exit: {:?}", num, syscall_num);
|
||||
return_errno!(EINVAL, "syscall number mismatches");
|
||||
}
|
||||
self.status = Status::Running;
|
||||
@ -154,6 +112,19 @@ impl ThreadProfiler {
|
||||
}
|
||||
}
|
||||
|
||||
// Do not trace special system calls
|
||||
// HandleException: one exception can be invoked by another exception,
|
||||
// resulting in nested calling
|
||||
// Exit and ExitGroup: stop would be called inside these two system calls,
|
||||
// where only entering is traced
|
||||
// TODO: add support for the above system calls
|
||||
fn is_not_traced(syscall_num: SyscallNum) -> bool {
|
||||
match syscall_num {
|
||||
SyscallNum::HandleException | SyscallNum::Exit | SyscallNum::ExitGroup => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_syscall_total_time(&self) -> Result<Duration> {
|
||||
Ok(self.get_syscall_total()?.0)
|
||||
}
|
||||
@ -176,10 +147,13 @@ impl ThreadProfiler {
|
||||
fn display(&self) -> Result<()> {
|
||||
match self.status {
|
||||
Status::Stopped(report) => {
|
||||
// Pretty-print the Debug formatting report of the profiled thread
|
||||
println!("{:#?}", report);
|
||||
// Print the syscall statistics of the profiled thread
|
||||
self.display_syscall_stat()
|
||||
let mut s = String::new();
|
||||
write!(&mut s, "Thread {}: \n", current!().tid());
|
||||
write!(&mut s, "{:#?}\n", report);
|
||||
// Print all the statitics in one function to prevent
|
||||
// overlap of information from different threads
|
||||
eprintln!("{}", s + &self.format_syscall_statistics()?);
|
||||
Ok(())
|
||||
}
|
||||
_ => return_errno!(EINVAL, "thread profiler can report only in stopped status"),
|
||||
}
|
||||
@ -197,13 +171,17 @@ impl ThreadProfiler {
|
||||
/// SysWritev 0.40 0.000131 26 5 0 [12, 47]
|
||||
/// SysMprotect 0.03 0.000009 4 2 0 [4, 4]
|
||||
/// ------------------- ------ ----------- ----------- --------- --------- -----------
|
||||
fn display_syscall_stat(&self) -> Result<()> {
|
||||
println!(
|
||||
"{:<19} {:>6} {:>11} {:>11} {:>9} {:>9} {}",
|
||||
fn format_syscall_statistics(&self) -> Result<String> {
|
||||
let mut s = String::new();
|
||||
|
||||
write!(
|
||||
&mut s,
|
||||
"{:<19} {:>6} {:>11} {:>11} {:>9} {:>9} {}\n",
|
||||
"syscall", "% time", "seconds", "us/call", "calls", "errors", "range(us)",
|
||||
);
|
||||
println!(
|
||||
"{:-<19} {:-<6} {:-<11} {:-<11} {:-<9} {:-<9} {:-<11}",
|
||||
write!(
|
||||
s,
|
||||
"{:-<19} {:-<6} {:-<11} {:-<11} {:-<9} {:-<9} {:-<11}\n",
|
||||
"", "", "", "", "", "", ""
|
||||
);
|
||||
|
||||
@ -217,28 +195,31 @@ impl ThreadProfiler {
|
||||
for (syscall_num, entry) in syscall_data_ref {
|
||||
let time_percentage =
|
||||
entry.get_total_time().as_secs_f64() / total_time.as_secs_f64() * 100_f64;
|
||||
println!(
|
||||
"{:<19} {:>6.2} {:?}",
|
||||
write!(
|
||||
&mut s,
|
||||
"{:<19} {:>6.2} {:?}\n",
|
||||
format!("{:?}", syscall_num),
|
||||
time_percentage,
|
||||
entry,
|
||||
);
|
||||
}
|
||||
|
||||
println!(
|
||||
"{:-<19} {:-<6} {:-<11} {:-<11} {:-<9} {:-<9} {:-<11}",
|
||||
write!(
|
||||
&mut s,
|
||||
"{:-<19} {:-<6} {:-<11} {:-<11} {:-<9} {:-<9} {:-<11}\n",
|
||||
"", "", "", "", "", "", ""
|
||||
);
|
||||
|
||||
println!(
|
||||
"{} {:>20} {:>11.6} {:>21} {:>9}",
|
||||
write!(
|
||||
&mut s,
|
||||
"{} {:>20} {:>11.6} {:>21} {:>9}\n",
|
||||
"total",
|
||||
"100",
|
||||
total_time.as_secs_f64(),
|
||||
total_calls,
|
||||
total_errors,
|
||||
);
|
||||
Ok(())
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user