Semi-transparent logging facility with user msgbox capability; initial version

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2328 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2006-04-27 05:48:01 +00:00
parent 00f4d80d7e
commit b6232dae83
3 changed files with 405 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "log/uqmlog.h"
+333
View File
@@ -0,0 +1,333 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "uqmlog.h"
#include <string.h>
#include <stdio.h>
#include "libs/threadlib.h"
#ifndef MAX_LOG_ENTRY_SIZE
# define MAX_LOG_ENTRY_SIZE 256
#endif
#ifndef MAX_LOG_ENTRIES
# define MAX_LOG_ENTRIES 128
#endif
typedef char log_Entry[MAX_LOG_ENTRY_SIZE];
// static buffers in case we run out of memory
static log_Entry queue[MAX_LOG_ENTRIES];
static log_Entry msgNoThread;
static char msgBuf[16384];
static int maxLevel = log_Debug;
static int maxStreamLevel = log_Debug;
static int maxDisp = 10;
static int qtotal = 0;
static int qhead = 0;
static int qtail = 0;
static volatile bool noThreadReady = 0;
static bool forceBox = 0;
static FILE *streamOut;
static volatile int qlock = 0;
static Mutex qmutex;
static void displayLog (bool isError);
static void displayBox (const char *title, bool isError, const char *msg);
static void
lockQueue (void)
{
if (!qlock)
return;
LockMutex (qmutex);
}
static void
unlockQueue (void)
{
if (!qlock)
return;
UnlockMutex (qmutex);
}
static void
removeExcess (int room)
{
room = maxDisp - room;
if (room < 0)
room = 0;
for ( ; qtotal > room; --qtotal, ++qtail)
;
qtail %= MAX_LOG_ENTRIES;
}
static int
acquireSlot (void)
{
int slot;
lockQueue ();
removeExcess (1);
slot = qhead;
qhead = (qhead + 1) % MAX_LOG_ENTRIES;
++qtotal;
unlockQueue ();
return slot;
}
// queues the non-threaded message when present
static void
queueNonThreaded (void)
{
int slot;
// This is not perfect. A race condition still exists
// between buffering the no-thread message and setting
// the noThreadReady flag. Neither does this prevent
// the fully or partially overwritten message (by
// another competing thread). But it is 'good enough'
if (!noThreadReady)
return;
noThreadReady = false;
slot = acquireSlot ();
memcpy (queue[slot], msgNoThread, sizeof (msgNoThread));
}
void
log_init (int max_lines)
{
int i;
maxDisp = max_lines;
streamOut = stderr;
// pre-term queue strings
for (i = 0; i < MAX_LOG_ENTRIES; ++i)
queue[i][MAX_LOG_ENTRY_SIZE - 1] = '\0';
msgBuf[sizeof (msgBuf) - 1] = '\0';
msgNoThread[sizeof (msgNoThread) - 1] = '\0';
}
void
log_initThreads (void)
{
qmutex = CreateMutex ("Logging Lock", SYNC_CLASS_RESOURCE);
qlock = 1;
}
int
log_exit (int code)
{
if (code != EXIT_SUCCESS)
displayLog (true);
else if (forceBox)
displayLog (false);
if (qlock)
{
qlock = 0;
DestroyMutex (qmutex);
}
return code;
}
void
logged_abort (void)
{
log_exit (3);
abort ();
}
void
log_setLevel (int level)
{
maxLevel = level;
//maxStreamLevel = level;
}
FILE *
log_setOutput (FILE *out)
{
FILE *old = streamOut;
streamOut = out;
return old;
}
void
log_addV (log_Level level, const char *fmt, va_list list)
{
if ((int)level <= maxStreamLevel)
{
vfprintf (streamOut, fmt, list);
fputc ('\n', streamOut);
}
if ((int)level <= maxLevel)
{
int slot;
queueNonThreaded ();
slot = acquireSlot ();
vsnprintf (queue[slot], sizeof (queue[0]) - 1, fmt, list);
}
}
void
log_add (log_Level level, const char *fmt, ...)
{
va_list list;
va_start (list, fmt);
log_addV (level, fmt, list);
va_end (list);
}
// non-threaded version of 'add'
// uses single-instance static storage with entry into the
// queue delayed until the next threaded 'add' or 'exit'
void
log_add_nothreadV (log_Level level, const char *fmt, va_list list)
{
if ((int)level <= maxStreamLevel)
{
vfprintf (streamOut, fmt, list);
fputc ('\n', streamOut);
}
if ((int)level <= maxLevel)
{
vsnprintf (msgNoThread, sizeof (msgNoThread) - 1, fmt, list);
noThreadReady = true;
}
}
void
log_add_nothread (log_Level level, const char *fmt, ...)
{
va_list list;
va_start (list, fmt);
log_add_nothreadV (level, fmt, list);
va_end (list);
}
void
log_forceBox (bool force)
{
forceBox = force;
}
// sets the maximum log lines captured for the final
// display to the user on failure exit
void
log_captureLines (int num)
{
if (num > MAX_LOG_ENTRIES)
num = MAX_LOG_ENTRIES;
if (num < 1)
num = 1;
maxDisp = num;
// remove any extra lines already on queue
lockQueue ();
removeExcess (0);
unlockQueue ();
}
static void
displayLog (bool isError)
{
char *p = msgBuf;
int left = sizeof (msgBuf) - 1;
int len;
queueNonThreaded ();
if (isError)
{
strcpy (p, "The Ur-Quan Masters encountered a fatal error.\n"
"Part of the log follows:\n\n");
len = strlen (p);
p += len;
left -= len;
}
// glue the log entries together
lockQueue ();
while (qtail != qhead && left > 0)
{
len = strlen (queue[qtail]) + 1;
if (len > left)
len = left;
memcpy (p, queue[qtail], len);
p[len - 1] = '\n';
p += len;
left -= len;
qtail = (qtail + 1) % MAX_LOG_ENTRIES;
}
unlockQueue ();
displayBox ("The Ur-Quan Masters", isError, msgBuf);
}
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
extern int __stdcall MessageBoxA (void *hWnd, const char* lpText,
const char* lpCaption, uint32 uType);
#define MB_ICONEXCLAMATION 0x00000030L
#define MB_ICONASTERISK 0x00000040L
static void
displayBox (const char *title, bool isError, const char *msg)
{
MessageBoxA (0, msg, title,
isError ? MB_ICONEXCLAMATION : MB_ICONASTERISK);
}
#elif defined(__APPLE__)
static void
displayBox (const char *title, bool isError, const char *msg)
{
// do something here
}
#else
static void
displayBox (const char *title, bool isError, const char *msg)
{
// do nothing here
}
#endif /* OS disjunction */
+55
View File
@@ -0,0 +1,55 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _UQMLOG_H
#define _UQMLOG_H
#include "port.h"
#include "types.h"
#include <stdio.h>
#include <stdarg.h>
extern void log_init (int max_lines);
extern void log_initThreads (void);
extern int log_exit (int code);
inline void logged_exit (int code) { exit (log_exit (code)); }
extern void logged_abort (void);
extern FILE * log_setOutput (FILE *out);
// sets the new output stream and returns the previous one
extern void log_setLevel (int level);
extern void log_forceBox (bool force);
extern void log_captureLines (int num);
#define LOG_CAPTURE_ALL 1000000 // unreasonably big number
typedef enum
{
log_Never = -1,
log_Always = 0,
log_Warning,
log_Info,
log_Debug,
} log_Level;
extern void log_add (log_Level, const char *fmt, ...);
extern void log_addV (log_Level, const char *fmt, va_list);
extern void log_add_nothread (log_Level, const char *fmt, ...);
extern void log_add_nothreadV (log_Level, const char *fmt, va_list);
#endif /* _UQMLOG_H */