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