diff options
-rw-r--r-- | TODO.txt | 6 | ||||
-rw-r--r-- | include/log.h | 31 | ||||
-rw-r--r-- | init/log.c | 123 |
3 files changed, 135 insertions, 25 deletions
@@ -22,3 +22,9 @@ TODO: - command registration API +- Panic + - Print tons of debug info + - Levels: + - Oops (fake panic, no die) + - Respawn (respawns subsystem in case of nonfatal) + - Real panic (fatal error, kills all subsystems and destroys all) diff --git a/include/log.h b/include/log.h index 397ce2b..85f192d 100644 --- a/include/log.h +++ b/include/log.h @@ -1,4 +1,3 @@ - #define LOG_SOH "\001" #define LOG_SOH_ASCII '\001' @@ -14,19 +13,33 @@ #define DEFAULT_LOGLEVEL NOTICE_LOGLEVEL #define CONSOLE_LOGLEVEL DEBUG_LOGLEVEL -#define LOG_EMERG LOG_SOH "0" -#define LOG_ALERT LOG_SOH "1" -#define LOG_CRIT LOG_SOH "2" -#define LOG_ERR LOG_SOH "3" -#define LOG_WARNING LOG_SOH "4" -#define LOG_NOTICE LOG_SOH "5" -#define LOG_INFO LOG_SOH "6" -#define LOG_DEBUG LOG_SOH "7" +#define LOG_EMERG LOG_SOH "\1" "0" +#define LOG_ALERT LOG_SOH "\1" "1" +#define LOG_CRIT LOG_SOH "\1" "2" +#define LOG_ERR LOG_SOH "\1" "3" +#define LOG_WARNING LOG_SOH "\1" "4" +#define LOG_NOTICE LOG_SOH "\1" "5" +#define LOG_INFO LOG_SOH "\1" "6" +#define LOG_DEBUG LOG_SOH "\1" "7" #define LOG_DEFAULT "" int print(const char *fmt, ...); +#define PANICMODE_DEBUGONLY 'o' +#define PANICMODE_RESPAWN 'r' +#define PANICMODE_DIE 'd' + +#define PANIC_OOPS LOG_SOH #PANICMODE_DEBUGONLY +#define PANIC_RESPAWN LOG_SOH #PANICMODE_RESPAWN +#define PANIC_PANIC LOG_SOH #PANICMODE_DIE + +#define PANIC_DEFAULT PANIC_PANIC + +void _panic(const char *fileorigin, const int lineorigin, const char *fmt, ...); +#define panic(...) _panic(__FILE__, __LINE__, __VA_ARGS__) +#define oops(...) _panic(__FILE__, __LINE__, PANIC_OOPS __VA_ARGS__) + #define ANSI_CSI "\x1b[" #define ANSI_BOLD ANSI_CSI "1m" @@ -1,13 +1,21 @@ +#include <execinfo.h> +#include <signal.h> #include <stdarg.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <sys/file.h> +#include <sys/syscall.h> #include <sys/time.h> #include <unistd.h> #include <log.h> #include <util.h> +extern int subsystem_change_mode(int pid, char mode); +extern char *subsystem_get_name(int pid); +extern int mainpid; + static const char *colors[] = { [EMERG_LOGLEVEL] = ANSI_BLINK ANSI_REVERSE ANSI_BOLD ANSI_RED, [ALERT_LOGLEVEL] = ANSI_REVERSE ANSI_BOLD ANSI_RED, @@ -19,14 +27,27 @@ static const char *colors[] = { [DEBUG_LOGLEVEL] = ANSI_ITALIC ANSI_BRIGHT_BLUE, }; +static const char *mode_to_string[] = { + [PANICMODE_DEBUGONLY] = "subsystem OOPS", + [PANICMODE_RESPAWN] = "subsystem failure", + [PANICMODE_DIE] = "catastrophic failure", +}; + int console_lock = 0; -int print(const char *fmt, ...) +static int vaprint(const char *fmt, va_list ap) { int loglevel = DEFAULT_LOGLEVEL; + int dolocks = 1; + int parsecolon = 1; if(fmt[0] == LOG_SOH_ASCII) { - loglevel = (fmt[1] - 0x30) % 10; - fmt += 2; + loglevel = (fmt[2] - 0x30) % 10; + char flags = fmt[1]; + if(flags & 1 << 1) + dolocks = 0; + if(flags & 1 << 2) + parsecolon = 0; + fmt += 3; } /* not going to be printed? dont bother! */ @@ -37,16 +58,15 @@ int print(const char *fmt, ...) later we will parse it and print out ANSI colors and what not */ char buf[512]; - va_list ap; - va_start(ap, fmt); vsnprintf(buf, 512, fmt, ap); - va_end(ap); buf[512 - 1] = '\0'; size_t colon = 0; - for(; colon < strlen(buf); ++colon) { - if(buf[colon] == ':') - break; + if(parsecolon) { + for(; colon < strlen(buf); ++colon) { + if(buf[colon] == ':') + break; + } } char tsbuf[64] = "\0"; @@ -60,18 +80,20 @@ int print(const char *fmt, ...) at the same time. I considered simply writing all at once, but ended up just not caring enough to the point where spinlocks prevail. */ - __asm__(".spin_lock:"); - __asm__("mov rax, 1"); - __asm__("xchg rax, [console_lock]"); - __asm__("test rax, rax"); - __asm__("jnz .spin_lock"); + if(dolocks) { + __asm__(".spin_lock:"); + __asm__("mov rax, 1"); + __asm__("xchg rax, [console_lock]"); + __asm__("test rax, rax"); + __asm__("jnz .spin_lock"); + } /* we want to support stuff without colons, but frankly I havent tested this at time of writing. will find out later */ writeputs(ANSI_RESET ANSI_GREEN); writeputs(tsbuf); writeputs(ANSI_RESET); - if(buf[colon] == ':') { + if(parsecolon && buf[colon] == ':') { writeputs(colors[loglevel]); writeputs(ANSI_YELLOW); write(STDOUT_FILENO, buf, colon); @@ -85,7 +107,76 @@ int print(const char *fmt, ...) } writeputs(ANSI_RESET); write(STDOUT_FILENO, "\n", 1); + if(dolocks) + console_lock = 0; + return 0; +} + +void _panic(const char *fileorigin, const int lineorigin, const char *fmt, ...) +{ + char mode = PANICMODE_DIE; + int pid = getpid(); + if(fmt[0] == LOG_SOH_ASCII) { + mode = fmt[1]; + /* cannot respawn main thread */ + if(pid == mainpid && mode == PANICMODE_RESPAWN) + mode = PANICMODE_DIE; + fmt += 2; + } + +#define NOLOCK(loglevel) LOG_SOH "\3" loglevel + va_list ap; + va_start(ap, fmt); + char *_fmt = malloc(strlen(fmt) + 4 * sizeof(char)); + sprintf(_fmt, NOLOCK("1") "%s", fmt); + + void **backtrace_addresses = malloc(sizeof(void*) * 32); + int backtrace_count = backtrace(backtrace_addresses, 32); + char **backtrace_symbolnames = backtrace_symbols(backtrace_addresses, backtrace_count); + + __asm__("_panic.spin_lock:"); + __asm__("mov rax, 1"); + __asm__("xchg rax, [console_lock]"); + __asm__("test rax, rax"); + __asm__("jnz .spin_lock"); + + print(NOLOCK("5") "------------[ cut here ]------------"); + print(LOG_SOH "\7""0" "%s at %s:%d", mode_to_string[(int)mode], fileorigin, lineorigin); + vaprint(_fmt, ap); + print(LOG_SOH "\7""7" "Call Trace:"); + for(int i = 0; i < backtrace_count; ++i) { + print(NOLOCK("7") " [0x%016x] %s", backtrace_addresses[i], backtrace_symbolnames[i]); + } + if(mainpid == pid){ + print(NOLOCK("7") " <start of main thread>"); + } else { + print(NOLOCK("7") " <start of %s[%d]>", subsystem_get_name(pid), pid); + } + + /* if we are going to die, we dont really need to clean up */ + if(mode == PANICMODE_DIE) + kill(-getpgid(pid), SIGINT); console_lock = 0; - return 0; + free(_fmt); + va_end(ap); + + if(mode == PANICMODE_DEBUGONLY) + return; + + if(pid != mainpid && mode == PANICMODE_RESPAWN) { + /* we want to let the main process handle the rest */ + subsystem_change_mode(pid, mode); + syscall(SYS_exit, 1); + } +} + +int print(const char *fmt, ...) +{ + int ret = 0; + va_list ap; + va_start(ap, fmt); + ret = vaprint(fmt, ap); + va_end(ap); + return ret; } |