#include <kernel/kernel.h> #include <phantasmal/log.h> #include <phantasmal/channel.h> #include <phantasmal/lpc_names.h> #include <trace.h> inherit COMMON_AUTO; private object log; private string comp_err; private int reset_comp_err; private string last_rt_err; private string last_st; private int in_atomic_error; private int in_init_sequence; static void create(varargs int clone) { log = find_object(LOGD); reset_comp_err = 0; in_atomic_error = 0; in_init_sequence = 1; } void runtime_error(string error, int caught, mixed** trace) { int size, i, len; string line, progname, function, str, objname, err_str; if(!SYSTEM() && !KERNEL()) return; if(reset_comp_err) { log->write_syslog("Clearing comp_err in runtime_error!", LOG_VERBOSE); comp_err = nil; reset_comp_err = 0; } else { reset_comp_err = 1; } str = error; if(caught != 0) { str += " [caught]"; } last_rt_err = str; err_str = str + "\n"; str = ""; if(sscanf(err_str, "Failed to compile%*s") > 0) { return; } size = sizeof(trace) - 1; for(i = 0; i < size; i++) { progname = trace[i][TRACE_PROGNAME]; len = trace[i][TRACE_LINE]; if(len == 0) { line = " "; } else { line = " " + (string)len; line = line[strlen(line) - 4 ..]; } function = trace[i][TRACE_FUNCTION]; len = strlen(function); if(progname == AUTO && i != size - 1 && len > 3) { switch(function[..2]) { case "bad": case "_F_": case "_Q_": continue; } } if(len < 17) { function += " "[len..]; } objname = trace[i][TRACE_OBJNAME]; if(progname != objname) { len = strlen(progname); if(len < strlen(objname) && progname == objname[.. len - 1] && objname[len] == '#') { objname = objname[len..]; } str += line + " " + function + " " + progname + " (" + objname + ")\n"; } else { str += line + " " + function + " " + progname + "\n"; } } last_st = str; str = err_str + str; /* If the first character is a $, this is a silent error. Store it as * the last error, but don't log it. */ if (error[0] != '$') { if(caught) { log->write_syslog("Runtime error: " + str, LOG_WARNING); } else { log->write_syslog("Runtime error: " + str, LOG_ERROR); } send_message("Runtime error: " + str); if(in_init_sequence) { DRIVER->message("Runtime error: " + str); } if(caught == 0 && this_user()) { this_user()->message(str); } if(!caught && find_object(CHANNELD)) { CHANNELD->string_to_channel(CHANNEL_ERR, str); } } } void atomic_error(string error, int atom, mixed** trace) { int size, i, len; string line, progname, function, str, objname, err_str; object obj; if(!SYSTEM() && !KERNEL()) return; /* prevents recursion into atomic_error(). Without this the driver * will crash if an error occures within atomic_error(). * * I discovered this from experience :(. * (kdunwoody) * * N.B. I chose to write a driver message, but no log message since you * can't write to file from within an atomic, and this seems to count as * an atomic. */ if (in_atomic_error) { DRIVER->message("Re-entering atomic_error()! Bailing...\n"); return; } in_atomic_error = 1; /* if the first character of the error message is '$', this is a silent * error -- do not record it */ if (error[0] == '$') { return; } str = error; str += " [atomic]\n"; err_str = str + "\n"; str = ""; size = sizeof(trace) - 1; for (i = atom; i < size; i++) { progname = trace[i][TRACE_PROGNAME]; len = trace[i][TRACE_LINE]; if (len == 0) { line = " "; } else { line = " " + len; line = line[strlen(line) - 4 ..]; } function = trace[i][TRACE_FUNCTION]; len = strlen(function); if (progname == AUTO && i != size - 1 && len > 3) { switch (function[.. 2]) { case "bad": case "_F_": case "_Q_": continue; } } if (len < 17) { function += " "[len ..]; } objname = trace[i][TRACE_OBJNAME]; if (progname != objname) { len = strlen(progname); if (len < strlen(objname) && progname == objname[.. len - 1] && objname[len] == '#') { objname = objname[len ..]; } str += line + " " + function + " " + progname + " (" + objname + ")\n"; } else { str += line + " " + function + " " + progname + "\n"; } } /* * Don't set last_st -- it will just be rolled back almost as * soon as this function exits last_st = str; */ str = err_str + str; /* * Re-throws the error with the full stack trace. This was recommended * by Par Winzell of Skotos as being a good way to pass the stack trace * up to runtime_error(). (kdunwoody) */ error(str); in_atomic_error = 0; } void compile_error(string file, int line, string error) { if(!SYSTEM() && !KERNEL()) return; if(reset_comp_err) { log->write_syslog("Clearing comp_err in compile_error!", LOG_VERBOSE); reset_comp_err = 0; comp_err = ""; } if(!comp_err) comp_err = ""; comp_err += file + ":" + line + " " + error + "\n"; log->write_syslog("Compile error: " + file + ":" + (string)line + ": " + error); send_message("Compile error!"); if(in_init_sequence) { DRIVER->message("Compile error: " + file + ": " + (string)line + ": " + error + "\n"); } } string last_compile_errors(void) { if(SYSTEM()) return comp_err; return nil; } string last_runtime_error(void) { if(SYSTEM()) return last_rt_err; return nil; } string last_stack_trace(void) { if(SYSTEM()) return last_st; return nil; } void clear_errors(void) { if(!SYSTEM()) { error("Only System callers can clear errord's errors!"); } comp_err = last_rt_err = last_st = nil; } void done_with_init(void) { if(previous_program() == INITD) in_init_sequence = 0; }