diff --git a/Makefile.am b/Makefile.am index cfef0009..883eb8da 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,7 +14,7 @@ noinst_HEADERS = src/common.h src/cpu.h src/disk.h src/glue.h src/vm.h \ src/timing.h src/uthash.h src/video/video.h src/zlib-helpers.h \ \ src/x86/glue-prologue.h \ - src/meta/debug.h src/meta/trace.h \ + src/meta/debug.h src/meta/log.h src/meta/trace.h \ \ src/audio/alhelpers.h src/audio/AY8910.h src/audio/mockingboard.h \ src/audio/peripherals.h src/audio/soundcore.h src/audio/speaker.h \ @@ -66,6 +66,7 @@ apple2ix_SOURCES = \ src/meta/debugger.c \ src/meta/opcodes.c \ src/meta/lintrace.c \ + src/meta/log.c \ src/misc.c \ src/prefs.c \ src/rom.c \ diff --git a/src/audio/playqueue.c b/src/audio/playqueue.c index da3f9e82..94e4f49b 100644 --- a/src/audio/playqueue.c +++ b/src/audio/playqueue.c @@ -277,7 +277,6 @@ PlayQueue_s *playq_createPlayQueue(const long *nodeIdPtr, unsigned long numBuffe #define SELF_TEST 0 #if SELF_TEST bool do_logging = true; -FILE *error_log = NULL; static void _test_creation(void) { LOG("begin test"); @@ -789,7 +788,6 @@ static void _test_remove_middle_of_queue(void) { int main(int argc, char **argv) { #warning use Valgrind to check proper memory management - error_log = stdout; LOG("beginning tests"); _test_creation(); _test_internal_list_creation_integrity(); diff --git a/src/common.h b/src/common.h index 39124dcb..abf7c2fe 100644 --- a/src/common.h +++ b/src/common.h @@ -89,21 +89,10 @@ #endif #if VIDEO_OPENGL -#include "video_util/glUtil.h" -// 2015/04/01 ... early calls to glGetError()--before a context exists--causes segfaults on MacOS X -extern bool safe_to_do_opengl_logging; -static inline GLenum safeGLGetError(void) { - if (safe_to_do_opengl_logging && video_isRenderThread()) { - return glGetError(); - } - return (GLenum)0; -} -#else -#define GLenum int -#define safeGLGetError() 0 -#define glGetError() 0 +# include "video_util/glUtil.h" #endif +#include "meta/log.h" #include "meta/debug.h" #include "audio/soundcore.h" @@ -157,136 +146,6 @@ static inline GLenum safeGLGetError(void) { _rc; }) -extern bool do_logging; - -#ifdef ANDROID -static const char *log_end = ""; -# include -# define QUIT_FUNCTION(x) exit(x) -# define _LOG_CMD(str) __android_log_print(ANDROID_LOG_ERROR, "apple2ix", "%s", str) -#else -extern FILE *error_log; -static const char *log_end = "\n"; -# define QUIT_FUNCTION(x) exit(x) -# define _LOG_CMD(str) \ - do { \ - if (UNLIKELY(!error_log)) { \ - error_log = stderr; \ - } \ - fprintf(error_log, "%s", str); \ - } while (0); -#endif - -#define _MYFILE_ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) - -#define _LOG(...) \ - do { \ - int _err = errno; \ - errno = 0; \ - \ - char *syserr_str = NULL; \ - char *glerr_str = NULL; \ - if (_err) { \ - asprintf(&syserr_str, " (syserr:%s)", strerror(_err)); \ - } \ - if (_glerr) { \ - asprintf(&glerr_str, " (glerr:%04X)", _glerr); \ - } \ - \ - char *buf0 = NULL; \ - asprintf(&buf0, __VA_ARGS__); \ - \ - char *buf = NULL; \ - asprintf(&buf, "%s:%d (%s) -%s%s %s%s", _MYFILE_, __LINE__, __func__, syserr_str ? : "", glerr_str ? : "", buf0, log_end); \ - \ - _LOG_CMD(buf); \ - \ - free(buf0); \ - free(buf); \ - if (syserr_str) { \ - free(syserr_str); \ - } \ - if (glerr_str) { \ - free(glerr_str); \ - } \ - } while (0) - -#if !defined(NDEBUG) || (defined(NDEBUG) && defined(ANDROID)) - -#ifdef ANDROID -// Apparently some non-conformant Android devices (ahem, Spamsung, ahem) do not actually let me see what the assert -// actually was before aborting/segfaulting ... -# undef assert -# define assert(e) \ - do { \ - if ((e)) { \ - /* ... */ \ - } else { \ - LOG( "!!! ASSERT !!! : " #e ); \ - sleep(1); \ - __assert2(_MYFILE_, __LINE__, __func__, #e); \ - } \ - } while (0) -#endif - -#define LOG(...) \ - if (do_logging) { \ - GLenum _glerr = safeGLGetError(); \ - _LOG(__VA_ARGS__); \ - while ( (_glerr = safeGLGetError()) ) { \ - _LOG(__VA_ARGS__); \ - } \ - } // - -// GL_MAYBELOG() only logs if an OpenGL error occurred -#define GL_MAYBELOG(...) \ - if (do_logging) { \ - GLenum _glerr = 0; \ - while ( (_glerr = safeGLGetError()) ) { \ - _LOG(__VA_ARGS__); \ - } \ - } // - -#define ERRQUIT(...) \ - do { \ - GLenum _glerr = safeGLGetError(); \ - _LOG(__VA_ARGS__); \ - while ( (_glerr = safeGLGetError()) ) { \ - _LOG(__VA_ARGS__); \ - } \ - QUIT_FUNCTION(1); \ - } while (0) - -// GL_ERRQUIT() only logs/quits if an OpenGL error occurred -#define GL_ERRQUIT(...) \ - do { \ - GLenum _glerr = 0; \ - GLenum _last_glerr = 0; \ - while ( (_glerr = safeGLGetError()) ) { \ - _last_glerr = _glerr; \ - _LOG(__VA_ARGS__); \ - } \ - if (_last_glerr) { \ - QUIT_FUNCTION(_last_glerr); \ - } \ - } while (0) - -#else // NDEBUG - -#define LOG(...) \ - do { } while (0) - -#define GL_MAYBELOG(...) \ - do { } while (0) - -#define ERRQUIT(...) \ - do { } while (0) - -#define GL_ERRQUIT(...) \ - do { } while (0) - -#endif // NDEBUG - // memory management #include "memmngt.h" diff --git a/src/meta/log.c b/src/meta/log.c new file mode 100644 index 00000000..692bf9a1 --- /dev/null +++ b/src/meta/log.c @@ -0,0 +1,140 @@ +/* + * Apple // emulator for *ix + * + * This software package is subject to the GNU General Public License + * version 3 or later (your choice) as published by the Free Software + * Foundation. + * + * Copyright 2017 Aaron Culliney + * + */ + +#include "common.h" + +// log.c : +// - simple and not-particularly-performant logging functions +// - do not call in a tight loop! + +#if defined(__ANDROID__) +# include +#endif + +#define LOG_PATH_TEMPLATE "%s%sapple2ix_log.%u.txt" + +bool do_logging = true; +bool do_std_logging = true; + +static int logFd = -1; +static int logSiz = 0; +static const unsigned int logRotateSize = 1024 * 1024; +static const unsigned int logRotateCount = 4; + +static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; + +// ---------------------------------------------------------------------------- + +static void _log_stopLogging(void) { + if (logFd >= 0) { + TEMP_FAILURE_RETRY(fsync(logFd)); + TEMP_FAILURE_RETRY(close(logFd)); + logFd = -1; + logSiz = 0; + } +} + +static void _log_rotate(bool performRotation) { + + _log_stopLogging(); + + int xflag = 0; + if (LIKELY(performRotation)) { + xflag = O_TRUNC; + for (unsigned int i = logRotateCount; i>0; i--) { + char *logPath0 = NULL; + char *logPath1 = NULL; + + ASPRINTF(&logPath0, LOG_PATH_TEMPLATE, data_dir, PATH_SEPARATOR, (i-1)); + ASPRINTF(&logPath1, LOG_PATH_TEMPLATE, data_dir, PATH_SEPARATOR, i); + assert(logPath0); + assert(logPath1); + + int ret = -1; + TEMP_FAILURE_RETRY(ret = rename(logPath0, logPath1)); + + FREE(logPath0); + FREE(logPath1); + } + } + + char *logPath = NULL; + ASPRINTF(&logPath, LOG_PATH_TEMPLATE, data_dir, PATH_SEPARATOR, 0); + assert(logPath); + + TEMP_FAILURE_RETRY(logFd = open(logPath, O_WRONLY|O_CREAT|xflag, S_IRUSR|S_IWUSR)); + + logSiz = lseek(logFd, 0L, SEEK_END); + + log_outputString("-------------------------------------------------------------------------------"); + + FREE(logPath); +} + +void log_init(void) { + _log_rotate(/*performRotation:*/false); +} + +void log_outputString(const char * const str) { + if (UNLIKELY(!str)) { + return; + } + + if (UNLIKELY(!do_logging)) { + return; + } + + if (do_std_logging) { +#if defined(__ANDROID__) && !defined(NDEBUG) + __android_log_print(ANDROID_LOG_ERROR, "apple2ix", "%s", str); +#else + fprintf(stderr, "%s\n", str); +#endif + } + + if (UNLIKELY(logFd < 0)) { + return; + } + + pthread_mutex_lock(&log_mutex); + + int expected_bytescount = strlen(str); + int bytescount = 0; + + do { + ssize_t byteswritten = 0; + TEMP_FAILURE_RETRY(byteswritten = write(logFd, str+bytescount, expected_bytescount-bytescount)); + if (UNLIKELY(byteswritten <= 0)) { + break; // OOPS ! + } + + bytescount += byteswritten; + if (bytescount >= expected_bytescount) { + bytescount = expected_bytescount; + TEMP_FAILURE_RETRY(write(logFd, "\n", 1)); + TEMP_FAILURE_RETRY(fsync(logFd)); + logSiz += bytescount + 1; + if (UNLIKELY(logSiz >= logRotateSize)) { + _log_rotate(/*performRotation:*/true); + } + + break; // OKAY + } + } while (1); + + if (UNLIKELY(bytescount != expected_bytescount)) { + // logging is b0rked, shut it down ... + _log_stopLogging(); + } + + pthread_mutex_unlock(&log_mutex); +} + diff --git a/src/meta/log.h b/src/meta/log.h new file mode 100644 index 00000000..0e2c85e7 --- /dev/null +++ b/src/meta/log.h @@ -0,0 +1,141 @@ +/* + * Apple // emulator for *ix + * + * This software package is subject to the GNU General Public License + * version 3 or later (your choice) as published by the Free Software + * Foundation. + * + * Copyright 2017 Aaron Culliney + * + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + +#if VIDEO_OPENGL +# include "video_util/glUtil.h" + +// 2015/04/01 ... early calls to glGetError()--before a context exists--causes segfaults on MacOS X +extern bool safe_to_do_opengl_logging; +static inline GLenum safeGLGetError(void) { + if (safe_to_do_opengl_logging && video_isRenderThread()) { + return glGetError(); + } + return (GLenum)0; +} + +#else +# define GLenum int +# define safeGLGetError() 0 +# define glGetError() 0 +#endif + +// global logging kill switch +extern bool do_logging; + +// log to the standard log facility (e.g., stderr) +extern bool do_std_logging; + +// initialize logging facility +void log_init(void); + +// print a string to the log file. Terminating '\n' is added. +void log_outputString(const char * const str); + +#define _MYFILE_ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) + +#define _LOG(...) \ + do { \ + int _err = errno; \ + errno = 0; \ + \ + char *syserr_str = NULL; \ + char *glerr_str = NULL; \ + if (_err) { \ + asprintf(&syserr_str, " (syserr:%s)", strerror(_err)); \ + } \ + if (_glerr) { \ + asprintf(&glerr_str, " (glerr:%04X)", _glerr); \ + } \ + \ + char *buf0 = NULL; \ + asprintf(&buf0, __VA_ARGS__); \ + \ + char *buf = NULL; \ + asprintf(&buf, "%s:%d (%s) -%s%s %s", _MYFILE_, __LINE__, __func__, (syserr_str ? : ""), (glerr_str ? : ""), buf0); \ + \ + log_outputString(buf); \ + \ + free(buf0); \ + free(buf); \ + if (syserr_str) { \ + free(syserr_str); \ + } \ + if (glerr_str) { \ + free(glerr_str); \ + } \ + } while (0) + + +#ifdef ANDROID +// Apparently some non-conformant Android devices (ahem, Spamsung, ahem) do not actually let me see what the assert +// actually was before aborting/segfaulting ... +# undef assert +# define assert(e) \ + do { \ + if ((e)) { \ + /* ... */ \ + } else { \ + LOG( "!!! ASSERT !!! : " #e ); \ + sleep(1); \ + __assert2(_MYFILE_, __LINE__, __func__, #e); \ + } \ + } while (0) +#endif + +#define LOG(...) \ + if (LIKELY(do_logging)) { \ + GLenum _glerr = safeGLGetError(); \ + _LOG(__VA_ARGS__); \ + while ( (_glerr = safeGLGetError()) ) { \ + _LOG(__VA_ARGS__); \ + } \ + } // + +// GL_MAYBELOG() only logs if an OpenGL error occurred +#define GL_MAYBELOG(...) \ + if (LIKELY(do_logging)) { \ + GLenum _glerr = 0; \ + while ( (_glerr = safeGLGetError()) ) { \ + _LOG(__VA_ARGS__); \ + } \ + } // + +#define QUIT_FUNCTION(x) exit(x) + +#define ERRQUIT(...) \ + do { \ + GLenum _glerr = safeGLGetError(); \ + _LOG(__VA_ARGS__); \ + while ( (_glerr = safeGLGetError()) ) { \ + _LOG(__VA_ARGS__); \ + } \ + QUIT_FUNCTION(1); \ + } while (0) + +// GL_ERRQUIT() only logs/quits if an OpenGL error occurred +#define GL_ERRQUIT(...) \ + do { \ + GLenum _glerr = 0; \ + GLenum _last_glerr = 0; \ + while ( (_glerr = safeGLGetError()) ) { \ + _last_glerr = _glerr; \ + _LOG(__VA_ARGS__); \ + } \ + if (_last_glerr) { \ + QUIT_FUNCTION(_last_glerr); \ + } \ + } while (0) + + +#endif // whole file diff --git a/src/misc.c b/src/misc.c index 6976163f..b9b5fa13 100644 --- a/src/misc.c +++ b/src/misc.c @@ -29,8 +29,6 @@ typedef struct module_ctor_node_s { static module_ctor_node_s *head = NULL; static bool emulatorShuttingDown = false; -bool do_logging = true; // also controlled by NDEBUG -FILE *error_log = NULL; const char *data_dir = NULL; char **argv = NULL; int argc = 0; @@ -38,8 +36,9 @@ CrashHandler_s *crashHandler = NULL; #if defined(CONFIG_DATADIR) static void _init_common(void) { - LOG("Initializing common..."); data_dir = STRDUP(CONFIG_DATADIR PATH_SEPARATOR PACKAGE_NAME); + log_init(); + LOG("Initializing common..."); } static __attribute__((constructor)) void __init_common(void) { diff --git a/src/test/testcommon.c b/src/test/testcommon.c index fd9b065c..36c73d8f 100644 --- a/src/test/testcommon.c +++ b/src/test/testcommon.c @@ -45,7 +45,7 @@ void test_common_init(void) { #if __ANDROID__ // tags help us wade through log soup #else - do_logging = false;// silence regular emulator logging + do_std_logging = false;// silence regular emulator logging #endif extern void emulator_ctors(void);