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