Add more info to log messages (e.g., timestamp, thread, and round)

This commit is contained in:
Tate, Hongliang Tian 2020-03-24 14:58:24 +00:00
parent dddcb89f7e
commit 6d7cf7b9f6
7 changed files with 208 additions and 72 deletions

@ -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

@ -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);
}