Add more info to log messages (e.g., timestamp, thread, and round)
This commit is contained in:
parent
dddcb89f7e
commit
6d7cf7b9f6
@ -117,5 +117,8 @@ enclave {
|
|||||||
unsigned int initval,
|
unsigned int initval,
|
||||||
int flags
|
int flags
|
||||||
) propagate_errno;
|
) propagate_errno;
|
||||||
|
|
||||||
|
void occlum_ocall_print_log(uint32_t level, [in, string] const char* msg);
|
||||||
|
void occlum_ocall_flush_log(void);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -21,7 +21,16 @@ pub extern "C" fn occlum_ecall_new_process(
|
|||||||
) -> i32 {
|
) -> i32 {
|
||||||
INIT_ONCE.call_once(|| {
|
INIT_ONCE.call_once(|| {
|
||||||
// Init the log infrastructure first so that log messages will be printed afterwards
|
// Init the log infrastructure first so that log messages will be printed afterwards
|
||||||
util::log::init();
|
use util::log::LevelFilter;
|
||||||
|
let log_level = match option_env!("LIBOS_LOG") {
|
||||||
|
Some("error") => LevelFilter::Error,
|
||||||
|
Some("warn") => LevelFilter::Warn,
|
||||||
|
Some("info") => LevelFilter::Info,
|
||||||
|
Some("debug") => LevelFilter::Debug,
|
||||||
|
Some("trace") => LevelFilter::Trace,
|
||||||
|
_ => LevelFilter::Error, // errors are printed be default
|
||||||
|
};
|
||||||
|
util::log::init(log_level);
|
||||||
// Init MPX for SFI
|
// Init MPX for SFI
|
||||||
util::mpx_util::mpx_enable();
|
util::mpx_util::mpx_enable();
|
||||||
// Register exception handlers (support cpuid & rdtsc for now)
|
// Register exception handlers (support cpuid & rdtsc for now)
|
||||||
@ -33,7 +42,7 @@ pub extern "C" fn occlum_ecall_new_process(
|
|||||||
let (path, args) = match parse_arguments(path_buf, argv) {
|
let (path, args) = match parse_arguments(path_buf, argv) {
|
||||||
Ok(path_and_args) => path_and_args,
|
Ok(path_and_args) => path_and_args,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("invalid arguments for LibOS: {}", e.backtrace());
|
eprintln!("invalid arguments for LibOS: {}", e.backtrace());
|
||||||
return EXIT_STATUS_INTERNAL_ERROR;
|
return EXIT_STATUS_INTERNAL_ERROR;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -42,7 +51,7 @@ pub extern "C" fn occlum_ecall_new_process(
|
|||||||
backtrace::__rust_begin_short_backtrace(|| match do_new_process(&path, &args) {
|
backtrace::__rust_begin_short_backtrace(|| match do_new_process(&path, &args) {
|
||||||
Ok(pid_t) => pid_t as i32,
|
Ok(pid_t) => pid_t as i32,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("failed to boot up LibOS: {}", e.backtrace());
|
eprintln!("failed to boot up LibOS: {}", e.backtrace());
|
||||||
EXIT_STATUS_INTERNAL_ERROR
|
EXIT_STATUS_INTERNAL_ERROR
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -62,7 +71,7 @@ pub extern "C" fn occlum_ecall_exec_thread(libos_pid: i32, host_tid: i32) -> i32
|
|||||||
match do_exec_thread(libos_pid as pid_t, host_tid as pid_t) {
|
match do_exec_thread(libos_pid as pid_t, host_tid as pid_t) {
|
||||||
Ok(exit_status) => exit_status,
|
Ok(exit_status) => exit_status,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("failed to execute a process: {}", e.backtrace());
|
eprintln!("failed to execute a process: {}", e.backtrace());
|
||||||
EXIT_STATUS_INTERNAL_ERROR
|
EXIT_STATUS_INTERNAL_ERROR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ pub use self::process::{Status, IDLE_PROCESS};
|
|||||||
pub use self::process_table::get;
|
pub use self::process_table::get;
|
||||||
pub use self::sched::{do_sched_getaffinity, do_sched_setaffinity, do_sched_yield, CpuSet};
|
pub use self::sched::{do_sched_getaffinity, do_sched_setaffinity, do_sched_yield, CpuSet};
|
||||||
pub use self::spawn::{do_spawn, do_spawn_without_exec, ElfFile, FileAction, ProgramHeaderExt};
|
pub use self::spawn::{do_spawn, do_spawn_without_exec, ElfFile, FileAction, ProgramHeaderExt};
|
||||||
pub use self::task::{current_pid, get_current, run_task, Task};
|
pub use self::task::{get_current, get_current_tid, run_task, Task};
|
||||||
pub use self::thread::{do_clone, do_set_tid_address, CloneFlags, ThreadGroup};
|
pub use self::thread::{do_clone, do_set_tid_address, CloneFlags, ThreadGroup};
|
||||||
pub use self::wait::{WaitQueue, Waiter};
|
pub use self::wait::{WaitQueue, Waiter};
|
||||||
|
|
||||||
|
@ -137,11 +137,11 @@ thread_local! {
|
|||||||
Cell::new(0 as *const SgxMutex<Process>)
|
Cell::new(0 as *const SgxMutex<Process>)
|
||||||
};
|
};
|
||||||
// for log getting pid without locking process
|
// for log getting pid without locking process
|
||||||
static _PID: Cell<pid_t> = Cell::new(0);
|
static _TID: Cell<pid_t> = Cell::new(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_pid() -> pid_t {
|
pub fn get_current_tid() -> pid_t {
|
||||||
_PID.with(|p| p.get())
|
_TID.with(|tid_cell| tid_cell.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current() -> ProcessRef {
|
pub fn get_current() -> ProcessRef {
|
||||||
@ -155,8 +155,8 @@ pub fn get_current() -> ProcessRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_current(process: &ProcessRef) {
|
fn set_current(process: &ProcessRef) {
|
||||||
let pid = process.lock().unwrap().get_pid();
|
let tid = process.lock().unwrap().get_tid();
|
||||||
_PID.with(|p| p.set(pid));
|
_TID.with(|tid_cell| tid_cell.set(tid));
|
||||||
|
|
||||||
let process_ref_clone = process.clone();
|
let process_ref_clone = process.clone();
|
||||||
let process_ptr = Arc::into_raw(process_ref_clone);
|
let process_ptr = Arc::into_raw(process_ref_clone);
|
||||||
@ -167,7 +167,7 @@ fn set_current(process: &ProcessRef) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn reset_current() {
|
fn reset_current() {
|
||||||
_PID.with(|p| p.set(0));
|
_TID.with(|tid_cell| tid_cell.set(0));
|
||||||
let mut process_ptr = _CURRENT_PROCESS_PTR.with(|cp| cp.replace(0 as *const SgxMutex<Process>));
|
let mut process_ptr = _CURRENT_PROCESS_PTR.with(|cp| cp.replace(0 as *const SgxMutex<Process>));
|
||||||
|
|
||||||
// Prevent memory leakage
|
// Prevent memory leakage
|
||||||
|
@ -27,6 +27,7 @@ use std::ffi::{CStr, CString};
|
|||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use time::{clockid_t, timespec_t, timeval_t, GLOBAL_PROFILER};
|
use time::{clockid_t, timespec_t, timeval_t, GLOBAL_PROFILER};
|
||||||
|
use util::log::{self, LevelFilter};
|
||||||
use util::mem_util::from_user::*;
|
use util::mem_util::from_user::*;
|
||||||
use vm::{MMapFlags, VMPerms};
|
use vm::{MMapFlags, VMPerms};
|
||||||
use {fs, process, std, vm};
|
use {fs, process, std, vm};
|
||||||
@ -416,6 +417,18 @@ macro_rules! impl_syscall_nums {
|
|||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SyscallNum {
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
use SyscallNum::*;
|
||||||
|
match *self {
|
||||||
|
#![deny(unreachable_patterns)]
|
||||||
|
$(
|
||||||
|
$name => stringify!($name),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<u32> for SyscallNum {
|
impl TryFrom<u32> for SyscallNum {
|
||||||
type Error = error::Error;
|
type Error = error::Error;
|
||||||
|
|
||||||
@ -429,7 +442,6 @@ macro_rules! impl_syscall_nums {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct SyscallNumError {
|
pub struct SyscallNumError {
|
||||||
invalid_num: u32,
|
invalid_num: u32,
|
||||||
@ -565,7 +577,10 @@ pub extern "C" fn occlum_syscall(
|
|||||||
arg4: isize,
|
arg4: isize,
|
||||||
arg5: isize,
|
arg5: isize,
|
||||||
) -> isize {
|
) -> isize {
|
||||||
let pid = process::do_gettid();
|
// Start a new round of log messages for this system call. But we do not
|
||||||
|
// set the description of this round, yet. We will do so after checking the
|
||||||
|
// given system call number is a valid.
|
||||||
|
log::next_round(None);
|
||||||
|
|
||||||
#[cfg(feature = "syscall_timing")]
|
#[cfg(feature = "syscall_timing")]
|
||||||
GLOBAL_PROFILER
|
GLOBAL_PROFILER
|
||||||
@ -574,12 +589,12 @@ pub extern "C" fn occlum_syscall(
|
|||||||
.syscall_enter(syscall_num)
|
.syscall_enter(syscall_num)
|
||||||
.expect("unexpected error from profiler to enter syscall");
|
.expect("unexpected error from profiler to enter syscall");
|
||||||
|
|
||||||
let ret = {
|
let ret = Syscall::new(num, arg0, arg1, arg2, arg3, arg4, arg5).and_then(|syscall| {
|
||||||
let syscall = Syscall::new(num, arg0, arg1, arg2, arg3, arg4, arg5).unwrap();
|
log::set_round_desc(Some(syscall.num.as_str()));
|
||||||
info!("{:?}", &syscall);
|
trace!("{:?}", &syscall);
|
||||||
|
|
||||||
dispatch_syscall(syscall)
|
dispatch_syscall(syscall)
|
||||||
};
|
});
|
||||||
|
|
||||||
#[cfg(feature = "syscall_timing")]
|
#[cfg(feature = "syscall_timing")]
|
||||||
GLOBAL_PROFILER
|
GLOBAL_PROFILER
|
||||||
@ -588,18 +603,38 @@ pub extern "C" fn occlum_syscall(
|
|||||||
.syscall_exit(syscall_num, ret.is_err())
|
.syscall_exit(syscall_num, ret.is_err())
|
||||||
.expect("unexpected error from profiler to exit syscall");
|
.expect("unexpected error from profiler to exit syscall");
|
||||||
|
|
||||||
info!("tid: {} => {:?} ", pid, ret);
|
let retval = match ret {
|
||||||
|
|
||||||
match ret {
|
|
||||||
Ok(retval) => retval as isize,
|
Ok(retval) => retval as isize,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("{}", e.backtrace());
|
let should_log_err = |errno| {
|
||||||
|
// If the log level requires every detail, don't ignore any error
|
||||||
|
if log::max_level() == LevelFilter::Trace {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other log levels require errors to be outputed. But
|
||||||
|
// some errnos are usually benign and may occur in a very high
|
||||||
|
// frequency. So we want to ignore them to keep noises at a
|
||||||
|
// minimum level in the log.
|
||||||
|
//
|
||||||
|
// TODO: use a smarter, frequency-based strategy to decide whether
|
||||||
|
// to suppress error messages.
|
||||||
|
match errno {
|
||||||
|
EAGAIN | ETIMEDOUT => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if should_log_err(e.errno()) {
|
||||||
|
error!("Error = {}", e.backtrace());
|
||||||
|
}
|
||||||
|
|
||||||
let retval = -(e.errno() as isize);
|
let retval = -(e.errno() as isize);
|
||||||
debug_assert!(retval != 0);
|
debug_assert!(retval != 0);
|
||||||
retval
|
retval
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
trace!("Retval = {:?}", retval);
|
||||||
|
retval
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,18 +1,68 @@
|
|||||||
|
use super::process;
|
||||||
|
/// Log infrastructure.
|
||||||
|
///
|
||||||
|
/// There are five APIs for producing log messages:
|
||||||
|
/// 1. `error!`
|
||||||
|
/// 2. `warn!`
|
||||||
|
/// 3. `info!`
|
||||||
|
/// 4. `debug!`
|
||||||
|
/// 5. `trace!`
|
||||||
|
/// which corresponds to five different log levels.
|
||||||
|
///
|
||||||
|
/// Safety. Sensitive, internal info may be leaked though log messages. To prevent
|
||||||
|
/// this from happening, the current solution is to turn off the log entirely
|
||||||
|
/// when initializing the log infrastructure, if the enclave is in release mode.
|
||||||
|
///
|
||||||
|
/// Note. Do not use log as a way to display critical info to users as log may be
|
||||||
|
/// turned off (even the error messages). For such messages, use `println!` or
|
||||||
|
/// `eprintln!` directly.
|
||||||
use log::*;
|
use log::*;
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
pub fn init() {
|
pub use log::{max_level, LevelFilter};
|
||||||
|
|
||||||
|
/// Initialize the log infrastructure with the given log level.
|
||||||
|
pub fn init(level: LevelFilter) {
|
||||||
static LOGGER: SimpleLogger = SimpleLogger;
|
static LOGGER: SimpleLogger = SimpleLogger;
|
||||||
log::set_logger(&LOGGER).expect("logger cannot be set twice");
|
log::set_logger(&LOGGER).expect("logger cannot be set twice");
|
||||||
log::set_max_level(match option_env!("LIBOS_LOG") {
|
log::set_max_level(level);
|
||||||
Some("error") => LevelFilter::Error,
|
}
|
||||||
Some("warn") => LevelFilter::Warn,
|
|
||||||
Some("info") => LevelFilter::Info,
|
/// Notify the logger that a new round starts.
|
||||||
Some("debug") => LevelFilter::Debug,
|
///
|
||||||
Some("trace") => LevelFilter::Trace,
|
/// Log messages generated in a thread are organized in _rounds_. Each round
|
||||||
_ => LevelFilter::Error, // errors are printed be default
|
/// is a group of related log messages. For examples, all log messages generated
|
||||||
|
/// during the execution of a single system call may belong to the same round.
|
||||||
|
pub fn next_round(desc: Option<&'static str>) {
|
||||||
|
ROUND_COUNT.with(|cell| {
|
||||||
|
cell.set(cell.get() + 1);
|
||||||
|
});
|
||||||
|
ROUND_DESC.with(|cell| {
|
||||||
|
cell.set(desc);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the description of the current round
|
||||||
|
pub fn set_round_desc(desc: Option<&'static str>) {
|
||||||
|
ROUND_DESC.with(|cell| {
|
||||||
|
cell.set(desc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn round_count() -> u64 {
|
||||||
|
ROUND_COUNT.with(|cell| cell.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn round_desc() -> Option<&'static str> {
|
||||||
|
ROUND_DESC.with(|cell| cell.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static ROUND_COUNT : Cell<u64> = Default::default();
|
||||||
|
static ROUND_DESC : Cell<Option<&'static str>> = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple logger that adds thread and round info to log messages.
|
||||||
struct SimpleLogger;
|
struct SimpleLogger;
|
||||||
|
|
||||||
impl Log for SimpleLogger {
|
impl Log for SimpleLogger {
|
||||||
@ -21,51 +71,36 @@ impl Log for SimpleLogger {
|
|||||||
}
|
}
|
||||||
fn log(&self, record: &Record) {
|
fn log(&self, record: &Record) {
|
||||||
if self.enabled(record.metadata()) {
|
if self.enabled(record.metadata()) {
|
||||||
let color = Color::from(record.level());
|
// Parts of message
|
||||||
// TODO: add process info
|
let level = record.level();
|
||||||
println!(
|
let tid = process::get_current_tid();
|
||||||
//"\u{1B}[{}m[{:>5}][{}] {}\u{1B}[0m",
|
let rounds = round_count();
|
||||||
"\u{1B}[{}m[{:>5}] {}\u{1B}[0m",
|
let desc = round_desc();
|
||||||
color as u8,
|
// Message (null-terminated)
|
||||||
record.level(),
|
let message = if let Some(desc) = desc {
|
||||||
//crate::process::current_pid(),
|
format!(
|
||||||
record.args()
|
"[{:>5}][T{}][#{}][{:·>8}] {}\0",
|
||||||
);
|
level,
|
||||||
|
tid,
|
||||||
|
rounds,
|
||||||
|
desc,
|
||||||
|
record.args()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!("[{:>5}][T{}][#{}] {}\0", level, tid, rounds, record.args())
|
||||||
|
};
|
||||||
|
// Print the message
|
||||||
|
unsafe {
|
||||||
|
occlum_ocall_print_log(level as u32, message.as_ptr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn flush(&self) {}
|
fn flush(&self) {
|
||||||
}
|
//unsafe { occlum_ocall_flush_log(); }
|
||||||
|
|
||||||
impl From<Level> for Color {
|
|
||||||
fn from(level: Level) -> Self {
|
|
||||||
match level {
|
|
||||||
Level::Error => Color::Red,
|
|
||||||
Level::Warn => Color::Yellow,
|
|
||||||
Level::Info => Color::Blue,
|
|
||||||
Level::Debug => Color::Green,
|
|
||||||
Level::Trace => Color::BrightBlack,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
extern "C" {
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
fn occlum_ocall_print_log(level: u32, msg: *const u8);
|
||||||
#[repr(u8)]
|
fn occlum_ocall_flush_log();
|
||||||
pub enum Color {
|
|
||||||
Black = 30,
|
|
||||||
Red = 31,
|
|
||||||
Green = 32,
|
|
||||||
Yellow = 33,
|
|
||||||
Blue = 34,
|
|
||||||
Magenta = 35,
|
|
||||||
Cyan = 36,
|
|
||||||
White = 37,
|
|
||||||
BrightBlack = 90,
|
|
||||||
BrightRed = 91,
|
|
||||||
BrightGreen = 92,
|
|
||||||
BrightYellow = 93,
|
|
||||||
BrightBlue = 94,
|
|
||||||
BrightMagenta = 95,
|
|
||||||
BrightCyan = 96,
|
|
||||||
BrightWhite = 97,
|
|
||||||
}
|
}
|
||||||
|
54
src/pal/src/ocalls/log.c
Normal file
54
src/pal/src/ocalls/log.c
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "ocalls.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LEVEL_OFF = 0,
|
||||||
|
LEVEL_ERROR = 1,
|
||||||
|
LEVEL_WARN = 2,
|
||||||
|
LEVEL_INFO = 3,
|
||||||
|
LEVEL_DEBUG = 4,
|
||||||
|
LEVEL_TRACE = 5
|
||||||
|
} level_t;
|
||||||
|
|
||||||
|
#define COLOR_NORMAL "\x1B[0m"
|
||||||
|
#define COLOR_RED "\x1B[31m"
|
||||||
|
#define COLOR_YELLOW "\x1B[33m"
|
||||||
|
#define COLOR_GREEN "\x1B[32m"
|
||||||
|
|
||||||
|
static level_t new_level(unsigned int level) {
|
||||||
|
if (level >= 5) level = 5;
|
||||||
|
return (level_t) level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void occlum_ocall_print_log(unsigned int _level, const char* msg) {
|
||||||
|
level_t level = new_level(_level);
|
||||||
|
if (level == LEVEL_OFF) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* color;
|
||||||
|
switch(level) {
|
||||||
|
case LEVEL_ERROR:
|
||||||
|
color = COLOR_RED;
|
||||||
|
break;
|
||||||
|
case LEVEL_WARN:
|
||||||
|
color = COLOR_YELLOW;
|
||||||
|
break;
|
||||||
|
case LEVEL_INFO:
|
||||||
|
color = COLOR_GREEN;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
color = COLOR_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval now_tv;
|
||||||
|
gettimeofday(&now_tv, NULL);
|
||||||
|
char day_and_sec[20];
|
||||||
|
strftime(day_and_sec, 20, "%Y-%m-%dT%H:%M:%S", gmtime(&now_tv.tv_sec));
|
||||||
|
int ms = now_tv.tv_usec / 1000;
|
||||||
|
fprintf(stderr, "%s[%s.%03dZ]%s%s\n", color, day_and_sec, ms, msg, COLOR_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void occlum_ocall_flush_log() {
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user