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