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,
|
||||
int flags
|
||||
) 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 {
|
||||
INIT_ONCE.call_once(|| {
|
||||
// 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
|
||||
util::mpx_util::mpx_enable();
|
||||
// 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) {
|
||||
Ok(path_and_args) => path_and_args,
|
||||
Err(e) => {
|
||||
error!("invalid arguments for LibOS: {}", e.backtrace());
|
||||
eprintln!("invalid arguments for LibOS: {}", e.backtrace());
|
||||
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) {
|
||||
Ok(pid_t) => pid_t as i32,
|
||||
Err(e) => {
|
||||
error!("failed to boot up LibOS: {}", e.backtrace());
|
||||
eprintln!("failed to boot up LibOS: {}", e.backtrace());
|
||||
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) {
|
||||
Ok(exit_status) => exit_status,
|
||||
Err(e) => {
|
||||
error!("failed to execute a process: {}", e.backtrace());
|
||||
eprintln!("failed to execute a process: {}", e.backtrace());
|
||||
EXIT_STATUS_INTERNAL_ERROR
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ pub use self::process::{Status, IDLE_PROCESS};
|
||||
pub use self::process_table::get;
|
||||
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::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::wait::{WaitQueue, Waiter};
|
||||
|
||||
|
@ -137,11 +137,11 @@ thread_local! {
|
||||
Cell::new(0 as *const SgxMutex<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 {
|
||||
_PID.with(|p| p.get())
|
||||
pub fn get_current_tid() -> pid_t {
|
||||
_TID.with(|tid_cell| tid_cell.get())
|
||||
}
|
||||
|
||||
pub fn get_current() -> ProcessRef {
|
||||
@ -155,8 +155,8 @@ pub fn get_current() -> ProcessRef {
|
||||
}
|
||||
|
||||
fn set_current(process: &ProcessRef) {
|
||||
let pid = process.lock().unwrap().get_pid();
|
||||
_PID.with(|p| p.set(pid));
|
||||
let tid = process.lock().unwrap().get_tid();
|
||||
_TID.with(|tid_cell| tid_cell.set(tid));
|
||||
|
||||
let process_ref_clone = process.clone();
|
||||
let process_ptr = Arc::into_raw(process_ref_clone);
|
||||
@ -167,7 +167,7 @@ fn set_current(process: &ProcessRef) {
|
||||
}
|
||||
|
||||
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>));
|
||||
|
||||
// Prevent memory leakage
|
||||
|
@ -27,6 +27,7 @@ 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 util::log::{self, LevelFilter};
|
||||
use util::mem_util::from_user::*;
|
||||
use vm::{MMapFlags, VMPerms};
|
||||
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 {
|
||||
type Error = error::Error;
|
||||
|
||||
@ -429,7 +442,6 @@ macro_rules! impl_syscall_nums {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct SyscallNumError {
|
||||
invalid_num: u32,
|
||||
@ -565,7 +577,10 @@ pub extern "C" fn occlum_syscall(
|
||||
arg4: isize,
|
||||
arg5: 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")]
|
||||
GLOBAL_PROFILER
|
||||
@ -574,12 +589,12 @@ pub extern "C" fn occlum_syscall(
|
||||
.syscall_enter(syscall_num)
|
||||
.expect("unexpected error from profiler to enter syscall");
|
||||
|
||||
let ret = {
|
||||
let syscall = Syscall::new(num, arg0, arg1, arg2, arg3, arg4, arg5).unwrap();
|
||||
info!("{:?}", &syscall);
|
||||
let ret = Syscall::new(num, arg0, arg1, arg2, arg3, arg4, arg5).and_then(|syscall| {
|
||||
log::set_round_desc(Some(syscall.num.as_str()));
|
||||
trace!("{:?}", &syscall);
|
||||
|
||||
dispatch_syscall(syscall)
|
||||
};
|
||||
});
|
||||
|
||||
#[cfg(feature = "syscall_timing")]
|
||||
GLOBAL_PROFILER
|
||||
@ -588,18 +603,38 @@ pub extern "C" fn occlum_syscall(
|
||||
.syscall_exit(syscall_num, ret.is_err())
|
||||
.expect("unexpected error from profiler to exit syscall");
|
||||
|
||||
info!("tid: {} => {:?} ", pid, ret);
|
||||
|
||||
match ret {
|
||||
let retval = match ret {
|
||||
Ok(retval) => retval as isize,
|
||||
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);
|
||||
debug_assert!(retval != 0);
|
||||
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 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;
|
||||
log::set_logger(&LOGGER).expect("logger cannot be set twice");
|
||||
log::set_max_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
|
||||
log::set_max_level(level);
|
||||
}
|
||||
|
||||
/// Notify the logger that a new round starts.
|
||||
///
|
||||
/// Log messages generated in a thread are organized in _rounds_. Each round
|
||||
/// 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;
|
||||
|
||||
impl Log for SimpleLogger {
|
||||
@ -21,51 +71,36 @@ impl Log for SimpleLogger {
|
||||
}
|
||||
fn log(&self, record: &Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
let color = Color::from(record.level());
|
||||
// TODO: add process info
|
||||
println!(
|
||||
//"\u{1B}[{}m[{:>5}][{}] {}\u{1B}[0m",
|
||||
"\u{1B}[{}m[{:>5}] {}\u{1B}[0m",
|
||||
color as u8,
|
||||
record.level(),
|
||||
//crate::process::current_pid(),
|
||||
record.args()
|
||||
);
|
||||
// Parts of message
|
||||
let level = record.level();
|
||||
let tid = process::get_current_tid();
|
||||
let rounds = round_count();
|
||||
let desc = round_desc();
|
||||
// Message (null-terminated)
|
||||
let message = if let Some(desc) = desc {
|
||||
format!(
|
||||
"[{:>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) {}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
fn flush(&self) {
|
||||
//unsafe { occlum_ocall_flush_log(); }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
#[repr(u8)]
|
||||
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,
|
||||
extern "C" {
|
||||
fn occlum_ocall_print_log(level: u32, msg: *const u8);
|
||||
fn occlum_ocall_flush_log();
|
||||
}
|
||||
|
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