1 /*
2 * Hatari - log.c
3 *
4 * This file is distributed under the GNU General Public License, version 2
5 * or at 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 #include <assert.h>
24
25 #include "main.h"
26 #include "configuration.h"
27 #include "dialog.h"
28 #include "log.h"
29 #include "screen.h"
30 #include "file.h"
31 #include "vdi.h"
32 #include "options.h"
33
34 int ExceptionDebugMask;
35
36 typedef struct {
37 Uint64 flag;
38 const char *name;
39 } flagname_t;
40
41 static flagname_t ExceptionFlags[] = {
42 { EXCEPT_NONE, "none" },
43
44 { EXCEPT_BUS, "bus" },
45 { EXCEPT_ADDRESS, "address" },
46 { EXCEPT_ILLEGAL, "illegal" },
47 { EXCEPT_ZERODIV, "zerodiv" },
48 { EXCEPT_CHK, "chk" },
49 { EXCEPT_TRAPV, "trapv" },
50 { EXCEPT_PRIVILEGE, "privilege" },
51 { EXCEPT_TRACE, "trace" },
52 { EXCEPT_NOHANDLER, "nohandler" },
53
54 { EXCEPT_DSP, "dsp" },
55
56 { EXCEPT_AUTOSTART, "autostart" },
57
58 { EXCEPT_ALL, "all" }
59 };
60
61 #if ENABLE_TRACING
62 static flagname_t TraceFlags[] = {
63 { TRACE_NONE , "none" },
64
65 { TRACE_VIDEO_SYNC , "video_sync" } ,
66 { TRACE_VIDEO_RES , "video_res" } ,
67 { TRACE_VIDEO_COLOR , "video_color" } ,
68 { TRACE_VIDEO_BORDER_V , "video_border_v" } ,
69 { TRACE_VIDEO_BORDER_H , "video_border_h" } ,
70 { TRACE_VIDEO_ADDR , "video_addr" } ,
71 { TRACE_VIDEO_HBL , "video_hbl" } ,
72 { TRACE_VIDEO_VBL , "video_vbl" } ,
73 { TRACE_VIDEO_STE , "video_ste" } ,
74 { TRACE_VIDEO_ALL , "video_all" } ,
75
76 { TRACE_MFP_EXCEPTION , "mfp_exception" } ,
77 { TRACE_MFP_START , "mfp_start" } ,
78 { TRACE_MFP_READ , "mfp_read" } ,
79 { TRACE_MFP_WRITE , "mfp_write" } ,
80 { TRACE_MFP_ALL , "mfp_all" } ,
81
82 { TRACE_PSG_READ , "psg_read" } ,
83 { TRACE_PSG_WRITE , "psg_write" } ,
84 { TRACE_PSG_ALL , "psg_all" } ,
85
86 { TRACE_CPU_PAIRING , "cpu_pairing" } ,
87 { TRACE_CPU_DISASM , "cpu_disasm" } ,
88 { TRACE_CPU_EXCEPTION , "cpu_exception" } ,
89 { TRACE_CPU_REGS , "cpu_regs" } ,
90 { TRACE_CPU_ALL , "cpu_all" } ,
91
92 { TRACE_INT , "int" } ,
93
94 { TRACE_FDC , "fdc" } ,
95
96 { TRACE_ACIA , "acia" } ,
97
98 { TRACE_IKBD_CMDS , "ikbd_cmds" } ,
99 { TRACE_IKBD_ACIA , "ikbd_acia" } ,
100 { TRACE_IKBD_EXEC , "ikbd_exec" } ,
101 { TRACE_IKBD_ALL , "ikbd_all" } ,
102
103 { TRACE_BLITTER , "blitter" } ,
104
105 { TRACE_OS_BIOS , "bios" },
106 { TRACE_OS_XBIOS , "xbios" },
107 { TRACE_OS_GEMDOS , "gemdos" },
108 { TRACE_OS_VDI , "vdi" },
109 { TRACE_OS_AES , "aes" },
110 { TRACE_OS_ALL , "os_all" } ,
111
112 { TRACE_IOMEM_RD , "io_read" } ,
113 { TRACE_IOMEM_WR , "io_write" } ,
114 { TRACE_IOMEM_ALL , "io_all" } ,
115
116 { TRACE_DMASND , "dmasound" } ,
117
118 { TRACE_CROSSBAR , "crossbar" } ,
119
120 { TRACE_VIDEL , "videl" } ,
121
122 { TRACE_DSP_HOST_INTERFACE, "dsp_host_interface" },
123 { TRACE_DSP_HOST_COMMAND , "dsp_host_command" },
124 { TRACE_DSP_HOST_SSI , "dsp_host_ssi" },
125 { TRACE_DSP_INTERRUPT , "dsp_interrupt" },
126 { TRACE_DSP_DISASM , "dsp_disasm" },
127 { TRACE_DSP_DISASM_REG , "dsp_disasm_reg" },
128 { TRACE_DSP_DISASM_MEM , "dsp_disasm_mem" },
129 { TRACE_DSP_STATE , "dsp_state" },
130 { TRACE_DSP_ALL , "dsp_all" },
131
132 { TRACE_DSP_SYMBOLS , "dsp_symbols" },
133 { TRACE_CPU_SYMBOLS , "cpu_symbols" },
134
135 { TRACE_NVRAM , "nvram" } ,
136
137 { TRACE_SCSI_CMD , "scsi_cmd" } ,
138
139 { TRACE_NATFEATS , "natfeats" } ,
140
141 { TRACE_KEYMAP , "keymap" } ,
142
143 { TRACE_MIDI , "midi" } ,
144
145 { TRACE_IDE , "ide" } ,
146
147 { TRACE_OS_BASE , "os_base" } ,
148
149 { TRACE_SCSIDRV , "scsidrv" } ,
150
151 { TRACE_MEM , "mem" } ,
152
153 { TRACE_ALL , "all" }
154 };
155 #endif /* ENABLE_TRACING */
156
157
158 Uint64 LogTraceFlags = TRACE_NONE;
159 FILE *TraceFile = NULL;
160
161 static FILE *hLogFile = NULL;
162
163 /* local settings, to be able change them temporarily */
164 static LOGTYPE TextLogLevel;
165 static LOGTYPE AlertDlgLogLevel;
166
167 /*-----------------------------------------------------------------------*/
168 /**
169 * Set default files to stderr (used at the very start, before parsing options)
170 */
Log_Default(void)171 void Log_Default(void)
172 {
173 hLogFile = stderr;
174 TraceFile = stderr;
175 TextLogLevel = LOG_INFO;
176 }
177
178 /**
179 * Set local log levels from configuration values
180 */
Log_SetLevels(void)181 void Log_SetLevels(void)
182 {
183 TextLogLevel = ConfigureParams.Log.nTextLogLevel;
184 AlertDlgLogLevel = ConfigureParams.Log.nAlertDlgLogLevel;
185 }
186
187 /*-----------------------------------------------------------------------*/
188 /**
189 * Initialize the logging and tracing functionality (open the log files etc.).
190 *
191 * Return zero if that fails.
192 */
Log_Init(void)193 int Log_Init(void)
194 {
195 Log_SetLevels();
196
197 hLogFile = File_Open(ConfigureParams.Log.sLogFileName, "w");
198 TraceFile = File_Open(ConfigureParams.Log.sTraceFileName, "w");
199
200 return (hLogFile && TraceFile);
201 }
202
203 /**
204 * Set Alert log level temporarily without config change.
205 *
206 * Return old level for restoring the original level with this.
207 */
Log_SetAlertLevel(int level)208 int Log_SetAlertLevel(int level)
209 {
210 int old = AlertDlgLogLevel;
211 AlertDlgLogLevel = level;
212 return old;
213 }
214
215
216 /*-----------------------------------------------------------------------*/
217 /**
218 * Un-Initialize - close log files etc.
219 */
Log_UnInit(void)220 void Log_UnInit(void)
221 {
222 hLogFile = File_Close(hLogFile);
223 TraceFile = File_Close(TraceFile);
224 }
225
226
227 /*-----------------------------------------------------------------------*/
228 /**
229 * Print log prefix when needed
230 */
Log_PrintPrefix(FILE * fp,LOGTYPE idx)231 static void Log_PrintPrefix(FILE *fp, LOGTYPE idx)
232 {
233 static const char* prefix[] = LOG_NAMES;
234
235 assert(idx >= 0 && idx < ARRAY_SIZE(prefix));
236 if (prefix[idx])
237 fprintf(fp, "%s: ", prefix[idx]);
238 }
239
240
241 /*-----------------------------------------------------------------------*/
242 /**
243 * Output string to log file
244 */
Log_Printf(LOGTYPE nType,const char * psFormat,...)245 void Log_Printf(LOGTYPE nType, const char *psFormat, ...)
246 {
247 va_list argptr;
248
249 if (hLogFile && nType <= TextLogLevel)
250 {
251 Log_PrintPrefix(hLogFile, nType);
252 va_start(argptr, psFormat);
253 vfprintf(hLogFile, psFormat, argptr);
254 va_end(argptr);
255 /* Add a new-line if necessary: */
256 if (psFormat[strlen(psFormat)-1] != '\n')
257 fputs("\n", hLogFile);
258 }
259 }
260
261
262 /*-----------------------------------------------------------------------*/
263 /**
264 * Show logging alert dialog box and output string to log file
265 */
Log_AlertDlg(LOGTYPE nType,const char * psFormat,...)266 void Log_AlertDlg(LOGTYPE nType, const char *psFormat, ...)
267 {
268 va_list argptr;
269
270 /* Output to log file: */
271 if (hLogFile && nType <= TextLogLevel)
272 {
273 Log_PrintPrefix(hLogFile, nType);
274 va_start(argptr, psFormat);
275 vfprintf(hLogFile, psFormat, argptr);
276 va_end(argptr);
277 /* Add a new-line if necessary: */
278 if (psFormat[strlen(psFormat)-1] != '\n')
279 fputs("\n", hLogFile);
280 }
281
282 /* Show alert dialog box: */
283 if (sdlscrn && nType <= AlertDlgLogLevel)
284 {
285 char *psTmpBuf;
286 psTmpBuf = malloc(2048);
287 if (!psTmpBuf)
288 {
289 perror("Log_AlertDlg");
290 return;
291 }
292 va_start(argptr, psFormat);
293 vsnprintf(psTmpBuf, 2048, psFormat, argptr);
294 va_end(argptr);
295 DlgAlert_Notice(psTmpBuf);
296 free(psTmpBuf);
297 }
298 }
299
300
301 /*-----------------------------------------------------------------------*/
302 /**
303 * parse what log level should be used and return it
304 */
Log_ParseOptions(const char * arg)305 LOGTYPE Log_ParseOptions(const char *arg)
306 {
307 const char *levels[] = {
308 "fatal", "error", "warn", "info", "todo", "debug", NULL
309 };
310 LOGTYPE level = LOG_FATAL;
311 const char **level_str;
312 char *input, *str;
313
314 input = strdup(arg);
315 str = input;
316 while (*str)
317 {
318 *str++ = tolower((unsigned char)*arg++);
319 }
320 for (level_str = levels; *level_str; level_str++, level++)
321 {
322 if (strcmp(input, *level_str) == 0)
323 {
324 free(input);
325 return level;
326 }
327 }
328 free(input);
329 return level;
330 }
331
332
333 /*-----------------------------------------------------------------------*/
334 /**
335 * Parse a list of comma separated strings.
336 * If the string is prefixed with an optional '+',
337 * corresponding mask flag is turned on.
338 * If the string is prefixed with a '-',
339 * corresponding mask flag is turned off.
340 * Return error string (""=silent 'error') or NULL for success.
341 */
342 static const char*
Log_ParseOptionFlags(const char * FlagsStr,flagname_t * Flags,int MaxFlags,Uint64 * Mask)343 Log_ParseOptionFlags (const char *FlagsStr, flagname_t *Flags, int MaxFlags, Uint64 *Mask)
344 {
345 char *FlagsCopy;
346 char *cur, *sep;
347 int i;
348 int Mode; /* 0=add, 1=del */
349
350 /* special case for "help" : display the list of possible settings */
351 if (strcmp (FlagsStr, "help") == 0)
352 {
353 fprintf(stderr, "\nList of available option flags :\n");
354
355 for (i = 0; i < MaxFlags; i++)
356 fprintf(stderr, " %s\n", Flags[i].name);
357
358 fprintf(stderr, "Multiple flags can be separated by ','.\n");
359 fprintf(stderr, "They can be prefixed by '+' or '-' to be mixed.\n");
360 fprintf(stderr, "Giving just 'none' flag disables all of them.\n\n");
361 return "";
362 }
363
364 if (strcmp (FlagsStr, "none") == 0)
365 {
366 return NULL;
367 }
368
369 FlagsCopy = strdup(FlagsStr);
370 if (!FlagsCopy)
371 {
372 return "strdup error in Log_OptionFlags";
373 }
374
375 cur = FlagsCopy;
376 while (cur)
377 {
378 sep = strchr(cur, ',');
379 if (sep) /* end of next options */
380 *sep++ = '\0';
381
382 Mode = 0; /* default is 'add' */
383 if (*cur == '+')
384 { Mode = 0; cur++; }
385 else if (*cur == '-')
386 { Mode = 1; cur++; }
387
388 for (i = 0; i < MaxFlags; i++)
389 {
390 if (strcmp(cur, Flags[i].name) == 0)
391 break;
392 }
393
394 if (i < MaxFlags) /* option found */
395 {
396 if (Mode == 0)
397 *Mask |= Flags[i].flag;
398 else
399 *Mask &= (~Flags[i].flag);
400 }
401 else
402 {
403 fprintf(stderr, "Unknown flag type '%s'\n", cur);
404 free(FlagsCopy);
405 return "Unknown flag type.";
406 }
407
408 cur = sep;
409 }
410
411 //fprintf(stderr, "flags parse <%x>\n", Mask);
412
413 free (FlagsCopy);
414 return NULL;
415 }
416
417 /**
418 * Parse exception flags and store results in ExceptionDebugMask.
419 * Return error string or NULL for success.
420 *
421 * See Log_ParseOptionFlags() for details.
422 */
Log_SetExceptionDebugMask(const char * FlagsStr)423 const char* Log_SetExceptionDebugMask (const char *FlagsStr)
424 {
425 const char *errstr;
426
427 Uint64 mask = EXCEPT_NONE;
428 errstr = Log_ParseOptionFlags(FlagsStr, ExceptionFlags, ARRAY_SIZE(ExceptionFlags), &mask);
429 ConfigureParams.Debugger.nExceptionDebugMask = mask;
430 return errstr;
431 }
432
433
434 #if ENABLE_TRACING
435
436 /**
437 * Parse trace flags and store results in LogTraceFlags.
438 * Return error string or NULL for success.
439 *
440 * See Log_ParseOptionFlags() for details.
441 */
Log_SetTraceOptions(const char * FlagsStr)442 const char* Log_SetTraceOptions (const char *FlagsStr)
443 {
444 const char *errstr;
445
446 LogTraceFlags = TRACE_NONE;
447 errstr = Log_ParseOptionFlags(FlagsStr, TraceFlags, ARRAY_SIZE(TraceFlags), &LogTraceFlags);
448
449 /* Enable Hatari flags needed for tracing selected items */
450 if (LogTraceFlags & (TRACE_OS_AES|TRACE_OS_VDI))
451 bVdiAesIntercept = true;
452
453 if ((LogTraceFlags & TRACE_OS_BASE) && ConOutDevice == CONOUT_DEVICE_NONE)
454 ConOutDevice = 2;
455
456 return errstr;
457 }
458
459 /**
460 * Readline match callback for trace type name completion.
461 * STATE = 0 -> different text from previous one.
462 * Return next match or NULL if no matches.
463 */
Log_MatchTrace(const char * text,int state)464 char *Log_MatchTrace(const char *text, int state)
465 {
466 static int i, len;
467 const char *name;
468
469 if (!state) {
470 /* first match */
471 len = strlen(text);
472 i = 0;
473 }
474 /* next match */
475 while (i < ARRAY_SIZE(TraceFlags)) {
476 name = TraceFlags[i++].name;
477 if (strncasecmp(name, text, len) == 0)
478 return (strdup(name));
479 }
480 return NULL;
481 }
482
483 #else /* !ENABLE_TRACING */
484
485 /** dummy */
Log_SetTraceOptions(const char * FlagsStr)486 const char* Log_SetTraceOptions (const char *FlagsStr)
487 {
488 return "Hatari has been compiled without ENABLE_TRACING!";
489 }
490
491 /** dummy */
Log_MatchTrace(const char * text,int state)492 char *Log_MatchTrace(const char *text, int state)
493 {
494 return NULL;
495 }
496
497 #endif /* !ENABLE_TRACING */
498