/* * Logging routines */ #include #include #include #define __LOG__ #include "v9t9_common.h" #include "system.h" #include "v9t9.h" #include "command.h" #include "debugger.h" #include "dsr.h" #include "log.h" // Mask for currently logged sources (see log.h) //static u32 logsrc = 0; // Verbosity level (0=minimum, ...) static int loglevel[32]; // Verbosity echoed to screen //static int consoleverbosity[32]; // Error buffer static char strbuf[1024]; static int stridx; static void lstart(void) { stridx = 0; } static void lend(u32 srcflags) { strbuf[stridx] = 0; system_log(srcflags, strbuf); } static void vlprintf(const char *format, va_list va) { int len; len = vsnprintf(strbuf + stridx, sizeof(strbuf) - stridx, format, va); // old usage if (len < 0) len = sizeof(strbuf) - stridx - 1; else if (len > sizeof(strbuf) - stridx) len = sizeof(strbuf) - stridx - 1; stridx += len; } static void lprintf(const char *format, ...) { va_list va; va_start(va, format); vlprintf(format, va); va_end(va); } /////////////////////////////////// static u32 lookup_system_name(char *name) { int idx; if (strcasecmp(name, "all") == 0) return ~0; for (idx = 0; idx < sizeof(log_src) / sizeof(char *); idx++) { if (strcasecmp(name, log_src[idx]) == 0) return 1 << idx; } return 0; } static DECL_SYMBOL_ACTION(command_toggle_logging) { int level; char *name; u32 mask, bit; int idx; if (task == csa_READ) { if (iter >= sizeof(log_src) / sizeof(log_src[0])) return 0; command_arg_set_string(SYM_ARG_1st, log_src[iter]); command_arg_set_num(SYM_ARG_2nd, loglevel[iter]); return 1; } command_arg_get_string(SYM_ARG_1st, &name); command_arg_get_num(SYM_ARG_2nd, &level); if ((mask = lookup_system_name(name)) == 0) { logger(LOG_ERROR | LOG_USER, "Unknown subsystem '%s'\n", name); return 0; } for (idx = 0, bit = 1; idx < LOG_NUM_SRC; idx ++, bit += bit) { if (mask & bit) { loglevel[idx] = level; } } return 1; } static DECL_SYMBOL_ACTION(command_list_log_systems) { int idx; logger(LOG_USER, "Supported logging subsystems:"); for (idx = 0; idx < sizeof(log_src) / sizeof(char *); idx++) { logger(LOG_USER, "\t%20s\t(level=%d)", log_src[idx], loglevel[idx]); } logger(LOG_USER, "\n"); return 1; } /* static DECL_SYMBOL_ACTION(command_log_verbosity) { int delta; if (task == csa_READ) { if (!iter) { command_arg_set_num(sym->args, logverbosity); return 1; } return 0; } command_arg_get_num(sym->args, &delta); log_verbose(delta); return 1; } static DECL_SYMBOL_ACTION(command_log_visible_verbosity) { int delta; if (task == csa_READ) { if (!iter) { command_arg_set_num(sym->args, logvisibleverbosity); return 1; } return 0; } command_arg_get_num(sym->args, &delta); log_visible_verbose(delta); return 1; } */ void log_add_commands(void) { command_symbol_table *logs = command_symbol_table_new("Logging Commands", "These options control sources and verbosity of logging", command_symbol_new ("Log", "Toggle logging for given subsystem", c_DYNAMIC, command_toggle_logging /* action */ , RET_FIRST_ARG /* ret */ , command_arg_new_string ("system", "one of the subsystems from ListLogSystems", NULL, NEW_ARG_STR(10), command_arg_new_num ("log level", "level of verbosity (0=off...9=max); " "a negative value sends output only to file, else output " "is copied to console or log window", NULL, NEW_ARG_NUM(int), NULL /* next */)), command_symbol_new ("ListLogSystems", "List targets for logging", c_DONT_SAVE, command_list_log_systems /* action */ , NULL /* ret */ , NULL, #if 0 command_symbol_new ("LogVerbosity", "Change logging verbosity for log file", c_DYNAMIC, command_log_verbosity /* action */ , RET_FIRST_ARG /* ret */ , command_arg_new_num ("delta", "delta>0 increases verbosity, delta<0 decreases verbosity", NULL, NEW_ARG_NUM(u8), NULL /* next */ ), command_symbol_new ("ConsoleLogVerbosity", "Change logging verbosity for messages echoed to console; " "note: LogVerbosity is treated as being at least ConsoleLogVerbosity, " "so messages will always be logged to disk", c_DYNAMIC, command_log_visible_verbosity /* action */ , RET_FIRST_ARG /* ret */ , command_arg_new_num ("delta", "delta>0 increases verbosity, delta<0 decreases verbosity", NULL, NEW_ARG_NUM (u8), NULL /* next */ ), #endif NULL /* next */ )), NULL /* sub */ , NULL /* next */ ); command_symbol_table_add_subtable(universe, logs); } // Initialize log void initlog(void) { memset(loglevel, 0, sizeof(loglevel)); // system_initlog(); } // Terminate log void termlog(void) { // system_termlog(); } static int log_2(u32 mask) { int idx = 0; u32 bit = 1; while (idx < LOG_NUM_SRC && !(mask & bit)) { idx++; bit <<=1; } return idx; } #define log_abs(x) ((x) < 0 ? -(x) : (x)) int log_level(int src) { return log_abs(loglevel[src]); } const char * log_name(int src) { if (src >= LOG_GENERAL && src < LOG_NUM_SRC) if (*log_src[src]) return log_src[src]; else return "General"; else return 0L; } bool log_enabled(int srcflags) { int verb = (srcflags & LOG_VERBOSE_MASK) >> LOG_VERBOSE_SHIFT; int srcidx = (srcflags & LOG_SRC_MASK); // LOG_USER or LOG_FATAL forces output if (!LOG_IS_VISIBLE(srcflags)) { // verbosity? if (verb > log_abs(loglevel[srcidx])) return false; } return true; } // Return source flags coerced to add LOG_USER if necessary int log_coerced_to_user(int srcflags) { int verb = (srcflags & LOG_VERBOSE_MASK) >> LOG_VERBOSE_SHIFT; int srcidx = (srcflags & LOG_SRC_MASK); return srcflags | (verb < loglevel[srcidx] ? LOG_USER : 0); } // Log something. srcflags=LOG_xxx void vlogger(u32 srcflags, const char *format, va_list va) { static u32 lastsrc = 0; if (!log_enabled(srcflags)) return; // start new buffer lstart(); // start new line? if (lastsrc != (srcflags & (LOG_SRC_MASK|LOG_TYPE_MASK)) && (srcflags & LOG_SRC_MASK)) { lastsrc = srcflags & (LOG_SRC_MASK|LOG_TYPE_MASK); #if 0 lprintf("%-7s ", log_src[srcflags & LOG_SRC_MASK]); if ((srcflags & LOG_TYPE_MASK) == LOG_WARN) lprintf("warn : "); else if ((srcflags & LOG_TYPE_MASK) == LOG_ERROR) lprintf("error: "); else if ((srcflags & LOG_TYPE_MASK) == LOG_FATAL) lprintf("fatal: "); else if ((srcflags & LOG_TYPE_MASK) == LOG_DEBUG) #ifdef DEBUG lprintf("debug: "); #else return; #endif else lprintf(": "); #endif #ifndef DEBUG if ((srcflags & LOG_TYPE_MASK) == LOG_DEBUG) return; #endif } // emit formatted text vlprintf(format, va); // dump text // if loglevel is positive, LOG_USER will always be set (else // we would have exited above); if loglevel is negative, // LOG_USER will never be set. lend(log_coerced_to_user(srcflags)); // die if it was fatal if ((srcflags & LOG_TYPE_MASK) == LOG_FATAL) { v9t9_term(1); abort(); } } void logger(u32 flags, const char *format, ...) { va_list va; va_start(va, format); vlogger(flags, format, va); va_end(va); } void OSerror(int err, char *format, ...) { va_list ap; va_start(ap, format); lstart(); lprintf("\nOS error:\t"); vlprintf(format, ap); lprintf("%s (error %d)\n", OS_GetErrText(err), err); lend(LOG_USER); va_end(ap); } // Print a status item for frontend void report_status(status_item item, ...) { va_list va; va_start(va, item); system_report_status(item, va); va_end(va); } // Format status item into text, if verbosity allows it void report_status_text(status_item item, va_list va, char *buffer, int bufsz) { *buffer = 0; if (bufsz < 32) return; switch (item) { case STATUS_CYCLES_SECOND: case STATUS_FRAMES_SECOND: break; case STATUS_DISK_ACCESS: { int disk, onoff; disk = va_arg(va, int); onoff = va_arg(va, int); if (log_enabled((dsr_is_emu_disk(disk) ? LOG_EMUDISK : LOG_REALDISK) | L_1)) sprintf(buffer, "Disk %d %s\n", disk, onoff ? "on" : "off"); break; } case STATUS_RS232_ACCESS: { if (log_enabled(LOG_RS232 | L_1)) sprintf(buffer, "RS232/%d %s\n", va_arg(va, int), va_arg(va, int) ? "on" : "off"); break; } case STATUS_DEBUG_REFRESH: break; case STATUS_CPU_PC: if (log_enabled(LOG_CPU | L_3)) sprintf(buffer, "[PC=>%04X] ", va_arg(va, int)); break; case STATUS_CPU_STATUS: if (log_enabled(LOG_CPU | L_3)) sprintf(buffer, "[ST=>%04X] ", va_arg(va, int)); break; case STATUS_CPU_WP: if (log_enabled(LOG_CPU | L_2)) sprintf(buffer, "[WP=>%04X] ", va_arg(va, int)); break; case STATUS_CPU_REGISTER_VIEW: { if (log_enabled(LOG_CPU | L_1)) { int reg; char *ptr = buffer; u16 wp = va_arg(va, int); u16 *regs = va_arg(va, u16 *); ptr += sprintf(ptr, "WP=>%04X\n", wp); for (reg = 0; reg < 16; reg++) { if (ptr + 12 > buffer + bufsz) break; ptr += sprintf(ptr, "R%1X=%04X ", reg, regs[reg]); if ((reg & 7) == 7) { ptr += sprintf(ptr, "\n"); } } *ptr = 0; } break; } case STATUS_CPU_REGISTER_READ: { if (log_enabled(LOG_CPU | L_2)) { int reg, val; reg = va_arg(va, int); val = va_arg(va, int); sprintf(buffer, "[Read R%d=>%04X] ", reg, val); } break; } case STATUS_CPU_REGISTER_WRITE: { if (log_enabled(LOG_CPU | L_2)) { int reg, val; reg = va_arg(va, int); val = va_arg(va, int); sprintf(buffer, "[Wrote R%d=>%04X] ", reg, val); } break; } case STATUS_CPU_INSTRUCTION: { if (log_enabled(LOG_CPU | L_1)) { Instruction *inst; char *hex, *disasm, *op1, *op2; inst = va_arg(va, Instruction *); hex = va_arg(va, char *); disasm = va_arg(va, char *); op1 = va_arg(va, char *); op2 = va_arg(va, char *); snprintf(buffer, bufsz, "%-10s %-5s %-22s %s%s%s%s%s%s\n", hex, inst->name, disasm, op1 ? "[" : "", op1 ? op1 : "", op1 ? "]" : "", op2 ? " [" : "", op2 ? op2 : "", op2 ? "]" : ""); } break; } case STATUS_CPU_INSTRUCTION_LAST: { if (log_enabled(LOG_CPU | L_1)) { Instruction *inst; char tmp[32]; char *op1, *op2; inst = va_arg(va, Instruction *); op1 = va_arg(va, char *); op2 = va_arg(va, char *); if (inst) { sprintf(tmp, "%s%s%s%s%s%s", op1 ? "{" : "", op1 ? op1 : "", op1 ? "}" : "", op2 ? " {" : "", op2 ? op2 : "", op2 ? "}" : ""); sprintf(buffer, "%-16s", tmp); } else { sprintf(buffer, "%-16s", ""); } } break; } case STATUS_MEMORY_VIEW: { if (log_enabled(LOG_MEMORY | L_1)) { Memory *s = va_arg(va, Memory *); char *start, *end, *astart, *aend; debugger_hex_dump_line(s, 0, debugger_memory_view_size[s->which], '=', ' ', ' ', '\n', buffer, bufsz, &start, &end, &astart, &aend); if (start) *(start-1) = '|'; if (end) *end = '|'; } break; } case STATUS_MEMORY_READ: { if (log_enabled(LOG_MEMORY | L_2)) { Memory *s = va_arg(va, Memory *); sprintf(buffer, "[Read %d bytes at >%04X] ", s->len, s->addr); } break; } case STATUS_MEMORY_WRITE: { if (log_enabled(LOG_MEMORY | L_2)) { Memory *s = va_arg(va, Memory *); sprintf(buffer, "[Wrote %d bytes at >%04X] ", s->len, s->addr); } break; } } } void my_assert_func(char *file, int line, char *message) { logger(LOG_FATAL | LOG_INTERNAL, "assertion failed: %s:%d: %s\n", file, line, message); }