|
|
1.1 ! root 1: /* ! 2: * Hatari - log.c ! 3: * ! 4: * This file is distributed under the GNU Public License, version 2 or at ! 5: * your option any later version. Read the file gpl.txt for details. ! 6: * ! 7: * Logger functions. ! 8: * ! 9: * When Hatari runs, it can output information, debug, warning and error texts ! 10: * to the error log file and/or displays them in alert dialog boxes. ! 11: * ! 12: * It can also dynamically output trace messages, based on the content ! 13: * of LogTraceFlags. Multiple trace levels can be set at once, by setting ! 14: * the corresponding bits in LogTraceFlags. ! 15: */ ! 16: const char Log_fileid[] = "Hatari log.c : " __DATE__ " " __TIME__; ! 17: ! 18: #include <stdio.h> ! 19: #include <stdarg.h> ! 20: #include <stdlib.h> ! 21: #include <string.h> ! 22: #include <ctype.h> ! 23: ! 24: #include "main.h" ! 25: #include "configuration.h" ! 26: #include "dialog.h" ! 27: #include "log.h" ! 28: #include "screen.h" ! 29: #include "file.h" ! 30: ! 31: ! 32: static struct { ! 33: Uint32 Level; ! 34: const char *Name; ! 35: } ! 36: TraceOptions[] = { ! 37: { TRACE_NONE , "none" }, ! 38: ! 39: { TRACE_VIDEO_SYNC , "video_sync" } , ! 40: { TRACE_VIDEO_RES , "video_res" } , ! 41: { TRACE_VIDEO_COLOR , "video_color" } , ! 42: { TRACE_VIDEO_BORDER_V , "video_border_v" } , ! 43: { TRACE_VIDEO_BORDER_H , "video_border_h" } , ! 44: { TRACE_VIDEO_ADDR , "video_addr" } , ! 45: { TRACE_VIDEO_HBL , "video_hbl" } , ! 46: { TRACE_VIDEO_VBL , "video_vbl" } , ! 47: { TRACE_VIDEO_STE , "video_ste" } , ! 48: { TRACE_VIDEO_ALL , "video_all" } , ! 49: ! 50: { TRACE_MFP_EXCEPTION , "mfp_exception" } , ! 51: { TRACE_MFP_START , "mfp_start" } , ! 52: { TRACE_MFP_READ , "mfp_read" } , ! 53: { TRACE_MFP_WRITE , "mfp_write" } , ! 54: { TRACE_MFP_ALL , "mfp_all" } , ! 55: ! 56: { TRACE_PSG_READ , "psg_read" } , ! 57: { TRACE_PSG_WRITE , "psg_write" } , ! 58: { TRACE_PSG_ALL , "psg_all" } , ! 59: ! 60: { TRACE_CPU_PAIRING , "cpu_pairing" } , ! 61: { TRACE_CPU_DISASM , "cpu_disasm" } , ! 62: { TRACE_CPU_EXCEPTION , "cpu_exception" } , ! 63: { TRACE_CPU_ALL , "cpu_all" } , ! 64: ! 65: { TRACE_INT , "int" } , ! 66: ! 67: { TRACE_FDC , "fdc" } , ! 68: ! 69: { TRACE_IKBD_CMDS , "ikbd_cmds" } , ! 70: { TRACE_IKBD_ACIA , "ikbd_acia" } , ! 71: { TRACE_IKBD_EXEC , "ikbd_exec" } , ! 72: { TRACE_IKBD_ALL , "ikbd_all" } , ! 73: ! 74: { TRACE_BLITTER , "blitter" } , ! 75: ! 76: { TRACE_OS_BIOS , "bios" }, ! 77: { TRACE_OS_XBIOS , "xbios" }, ! 78: { TRACE_OS_GEMDOS , "gemdos" }, ! 79: { TRACE_OS_VDI , "vdi" }, ! 80: { TRACE_OS_ALL , "os_all" } , ! 81: ! 82: { TRACE_IOMEM_RD , "io_read" } , ! 83: { TRACE_IOMEM_WR , "io_write" } , ! 84: { TRACE_IOMEM_ALL , "io_all" } , ! 85: ! 86: { TRACE_DMASND , "dmasound" } , ! 87: ! 88: { TRACE_CROSSBAR , "crossbar" } , ! 89: ! 90: { TRACE_ALL , "all" } ! 91: }; ! 92: ! 93: ! 94: Uint32 LogTraceFlags = TRACE_NONE; ! 95: FILE *TraceFile = NULL; ! 96: ! 97: static FILE *hLogFile = NULL; ! 98: static LOGTYPE TextLogLevel; ! 99: static LOGTYPE AlertDlgLogLevel; ! 100: ! 101: /*-----------------------------------------------------------------------*/ ! 102: /** ! 103: * Initialize the logging and tracing functionality (open the log files etc.). ! 104: * ! 105: * Return zero if that fails. ! 106: */ ! 107: int Log_Init(void) ! 108: { ! 109: TextLogLevel = ConfigureParams.Log.nTextLogLevel; ! 110: AlertDlgLogLevel = ConfigureParams.Log.nAlertDlgLogLevel; ! 111: ! 112: hLogFile = File_Open(ConfigureParams.Log.sLogFileName, "w"); ! 113: TraceFile = File_Open(ConfigureParams.Log.sTraceFileName, "w"); ! 114: ! 115: return (hLogFile && TraceFile); ! 116: } ! 117: ! 118: /** ! 119: * Set Alert log level temporarily without config change. ! 120: * ! 121: * Return old level for restoring the original level with this. ! 122: */ ! 123: int Log_SetAlertLevel(int level) ! 124: { ! 125: int old = AlertDlgLogLevel; ! 126: AlertDlgLogLevel = level; ! 127: return old; ! 128: } ! 129: ! 130: ! 131: /*-----------------------------------------------------------------------*/ ! 132: /** ! 133: * Un-Initialize - close log files etc. ! 134: */ ! 135: void Log_UnInit(void) ! 136: { ! 137: hLogFile = File_Close(hLogFile); ! 138: TraceFile = File_Close(TraceFile); ! 139: } ! 140: ! 141: ! 142: /*-----------------------------------------------------------------------*/ ! 143: /** ! 144: * Output string to log file ! 145: */ ! 146: void Log_Printf(LOGTYPE nType, const char *psFormat, ...) ! 147: { ! 148: va_list argptr; ! 149: ! 150: if (hLogFile && nType <= TextLogLevel) ! 151: { ! 152: va_start(argptr, psFormat); ! 153: vfprintf(hLogFile, psFormat, argptr); ! 154: va_end(argptr); ! 155: /* Add a new-line if necessary: */ ! 156: if (psFormat[strlen(psFormat)-1] != '\n') ! 157: fputs("\n", hLogFile); ! 158: } ! 159: } ! 160: ! 161: ! 162: /*-----------------------------------------------------------------------*/ ! 163: /** ! 164: * Show logging alert dialog box and output string to log file ! 165: */ ! 166: void Log_AlertDlg(LOGTYPE nType, const char *psFormat, ...) ! 167: { ! 168: va_list argptr; ! 169: ! 170: /* Output to log file: */ ! 171: if (hLogFile && nType <= TextLogLevel) ! 172: { ! 173: va_start(argptr, psFormat); ! 174: vfprintf(hLogFile, psFormat, argptr); ! 175: va_end(argptr); ! 176: /* Add a new-line if necessary: */ ! 177: if (psFormat[strlen(psFormat)-1] != '\n') ! 178: fputs("\n", hLogFile); ! 179: } ! 180: ! 181: /* Show alert dialog box: */ ! 182: if (sdlscrn && nType <= AlertDlgLogLevel) ! 183: { ! 184: char *psTmpBuf; ! 185: psTmpBuf = malloc(2048); ! 186: if (!psTmpBuf) ! 187: { ! 188: perror("Log_AlertDlg"); ! 189: return; ! 190: } ! 191: va_start(argptr, psFormat); ! 192: vsnprintf(psTmpBuf, 2048, psFormat, argptr); ! 193: va_end(argptr); ! 194: DlgAlert_Notice(psTmpBuf); ! 195: free(psTmpBuf); ! 196: } ! 197: } ! 198: ! 199: ! 200: /*-----------------------------------------------------------------------*/ ! 201: /** ! 202: * parse what log level should be used and return it ! 203: */ ! 204: LOGTYPE Log_ParseOptions(const char *arg) ! 205: { ! 206: const char *levels[] = { ! 207: "fatal", "error", "warn", "info", "todo", "debug", NULL ! 208: }; ! 209: LOGTYPE level = LOG_FATAL; ! 210: const char **level_str; ! 211: char *input, *str; ! 212: ! 213: input = strdup(arg); ! 214: str = input; ! 215: while (*str) ! 216: { ! 217: *str++ = tolower(*arg++); ! 218: } ! 219: for (level_str = levels; *level_str; level_str++, level++) ! 220: { ! 221: if (strcmp(input, *level_str) == 0) ! 222: { ! 223: free(input); ! 224: return level; ! 225: } ! 226: } ! 227: free(input); ! 228: return level; ! 229: } ! 230: ! 231: ! 232: #if ENABLE_TRACING ! 233: ! 234: /*-----------------------------------------------------------------------*/ ! 235: /** ! 236: * Parse a list of comma separated strings. ! 237: * If the string is prefixed with an optional '+', ! 238: * corresponding trace flag is turned on. ! 239: * If the string is prefixed with a '-', ! 240: * corresponding trace flag is turned off. ! 241: * Result is stored in LogTraceFlags. ! 242: * Return error string (""=silent 'error') or NULL for success. ! 243: */ ! 244: const char* Log_SetTraceOptions (const char *OptionsStr) ! 245: { ! 246: char *OptionsCopy; ! 247: char *cur, *sep; ! 248: int i; ! 249: int Mode; /* 0=add, 1=del */ ! 250: int MaxOptions; ! 251: ! 252: MaxOptions = ARRAYSIZE(TraceOptions); ! 253: ! 254: /* special case for "help" : display the list of possible trace levels */ ! 255: if (strcmp (OptionsStr, "help") == 0) ! 256: { ! 257: fprintf(stderr, "\nList of available trace levels :\n"); ! 258: ! 259: for (i = 0; i < MaxOptions; i++) ! 260: fprintf(stderr, " %s\n", TraceOptions[i].Name); ! 261: ! 262: fprintf(stderr, "Multiple trace levels can be separated by ','\n"); ! 263: fprintf(stderr, "Levels can be prefixed by '+' or '-' to be mixed.\n"); ! 264: fprintf(stderr, "Giving just trace level 'none' disables all traces.\n\n"); ! 265: return ""; ! 266: } ! 267: ! 268: LogTraceFlags = TRACE_NONE; ! 269: if (strcmp (OptionsStr, "none") == 0) ! 270: { ! 271: return NULL; ! 272: } ! 273: ! 274: OptionsCopy = strdup(OptionsStr); ! 275: if (!OptionsCopy) ! 276: { ! 277: return "strdup error in ParseTraceOptions"; ! 278: } ! 279: ! 280: cur = OptionsCopy; ! 281: while (cur) ! 282: { ! 283: sep = strchr(cur, ','); ! 284: if (sep) /* end of next options */ ! 285: *sep++ = '\0'; ! 286: ! 287: Mode = 0; /* default is 'add' */ ! 288: if (*cur == '+') ! 289: { Mode = 0; cur++; } ! 290: else if (*cur == '-') ! 291: { Mode = 1; cur++; } ! 292: ! 293: for (i = 0; i < MaxOptions; i++) ! 294: { ! 295: if (strcmp(cur, TraceOptions[i].Name) == 0) ! 296: break; ! 297: } ! 298: ! 299: if (i < MaxOptions) /* option found */ ! 300: { ! 301: if (Mode == 0) ! 302: LogTraceFlags |= TraceOptions[i].Level; ! 303: else ! 304: LogTraceFlags &= (~TraceOptions[i].Level); ! 305: } ! 306: else ! 307: { ! 308: fprintf(stderr, "Unknown trace type '%s'\n", cur); ! 309: free(OptionsCopy); ! 310: return "Unknown trace type."; ! 311: } ! 312: ! 313: cur = sep; ! 314: } ! 315: ! 316: //fprintf(stderr, "trace parse <%x>\n", LogTraceFlags); ! 317: ! 318: free (OptionsCopy); ! 319: return NULL; ! 320: } ! 321: ! 322: ! 323: /** ! 324: * Readline match callback for trace type name completion. ! 325: * STATE = 0 -> different text from previous one. ! 326: * Return next match or NULL if no matches. ! 327: */ ! 328: char *Log_MatchTrace(const char *text, int state) ! 329: { ! 330: static int i, len; ! 331: const char *name; ! 332: ! 333: if (!state) { ! 334: /* first match */ ! 335: len = strlen(text); ! 336: i = 0; ! 337: } ! 338: /* next match */ ! 339: while (i < ARRAYSIZE(TraceOptions)) { ! 340: name = TraceOptions[i++].Name; ! 341: if (strncasecmp(name, text, len) == 0) ! 342: return (strdup(name)); ! 343: } ! 344: return NULL; ! 345: } ! 346: ! 347: #else /* !ENABLE_TRACING */ ! 348: ! 349: /** dummy */ ! 350: const char* Log_SetTraceOptions (const char *OptionsStr) ! 351: { ! 352: return "Hatari has been compiled without ENABLE_TRACING!"; ! 353: } ! 354: ! 355: /** dummy */ ! 356: char *Log_MatchTrace(const char *text, int state) ! 357: { ! 358: return NULL; ! 359: } ! 360: ! 361: #endif /* !ENABLE_TRACING */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.