logvisor/lib/logvisor.cpp

614 lines
17 KiB
C++
Raw Normal View History

2015-07-03 22:08:41 +00:00
#if _WIN32
2015-08-31 03:31:31 +00:00
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
2015-09-27 04:33:36 +00:00
#ifndef NOMINMAX
#define NOMINMAX
#endif
2015-07-04 05:20:02 +00:00
#include <windows.h>
2015-11-05 00:02:40 +00:00
#include <io.h>
#else
#include <sys/ioctl.h>
#include <unistd.h>
#include <dlfcn.h>
#include <cxxabi.h>
#include <string.h>
2015-07-03 22:08:41 +00:00
#endif
#include <fcntl.h>
2015-07-03 22:08:41 +00:00
#include <chrono>
#include <mutex>
#include <thread>
2016-09-19 01:32:20 +00:00
#include <string>
2015-07-03 22:08:41 +00:00
#include <unordered_map>
#include <stdio.h>
2015-08-31 19:45:52 +00:00
#include <inttypes.h>
#include <signal.h>
2016-03-04 20:11:37 +00:00
#include "logvisor/logvisor.hpp"
2015-07-03 22:08:41 +00:00
/* ANSI sequences */
2015-07-04 06:41:44 +00:00
#define RED "\x1b[1;31m"
#define YELLOW "\x1b[1;33m"
#define GREEN "\x1b[1;32m"
#define MAGENTA "\x1b[1;35m"
#define CYAN "\x1b[1;36m"
2015-07-03 22:08:41 +00:00
#define BOLD "\x1b[1m"
#define NORMAL "\x1b[0m"
2015-07-22 19:06:24 +00:00
#if _WIN32
#define FOREGROUND_WHITE FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE
#endif
2016-03-04 23:01:18 +00:00
void logvisorBp() {}
2015-11-26 07:32:50 +00:00
2016-03-04 20:28:28 +00:00
namespace logvisor
2015-07-03 22:08:41 +00:00
{
static Module Log("logvisor");
2015-07-03 22:08:41 +00:00
static std::unordered_map<std::thread::id, const char*> ThreadMap;
2017-01-17 01:30:32 +00:00
static void AddThreadToMap(const char* name)
2015-07-03 22:08:41 +00:00
{
auto lk = LockLog();
2015-07-03 22:08:41 +00:00
ThreadMap[std::this_thread::get_id()] = name;
2017-01-17 01:30:32 +00:00
}
void RegisterThreadName(const char* name)
{
AddThreadToMap(name);
2015-07-03 22:08:41 +00:00
#if __APPLE__
pthread_setname_np(name);
#elif __linux__
pthread_setname_np(pthread_self(), name);
2015-07-04 05:20:02 +00:00
#elif _MSC_VER
2015-07-03 22:08:41 +00:00
struct
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
2015-07-04 05:20:02 +00:00
} info = {0x1000, name, (DWORD)-1, 0};
2015-07-03 22:08:41 +00:00
__try
{
RaiseException(0x406D1388, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
#endif
}
#if _WIN32
#include <DbgHelp.h>
#pragma comment(lib, "Dbghelp.lib")
2016-09-11 01:19:55 +00:00
void logvisorAbort()
{
unsigned int i;
void* stack[100];
unsigned short frames;
SYMBOL_INFO* symbol;
HANDLE process;
process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
frames = CaptureStackBackTrace(0, 100, stack, NULL);
symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
symbol->MaxNameLen = 255;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
for (i = 0; i < frames; i++)
{
SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
2016-09-11 01:19:55 +00:00
fprintf(stderr, "%i: %s - 0x%0llX", frames - i - 1, symbol->Name, symbol->Address);
DWORD dwDisplacement;
IMAGEHLP_LINE64 line;
SymSetOptions(SYMOPT_LOAD_LINES);
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, (DWORD64)(stack[i]), &dwDisplacement, &line))
{
// SymGetLineFromAddr64 returned success
2016-09-11 01:19:55 +00:00
fprintf(stderr, " LINE %d\n", line.LineNumber);
}
else
{
2016-09-11 01:19:55 +00:00
fprintf(stderr, "\n");
}
}
2016-09-11 01:19:55 +00:00
fflush(stderr);
free(symbol);
// If you caught one of the above signals, it is likely you just
// want to quit your program right now.
2016-09-11 01:19:55 +00:00
system("PAUSE");
exit(1);
}
#else
#include <execinfo.h>
void logvisorAbort()
{
void* array[128];
size_t size = backtrace(array, 128);
constexpr size_t exeBufSize = 1024 + 1;
char exeNameBuffer[exeBufSize] = {};
#if __linux__
readlink("/proc/self/exe", exeNameBuffer, exeBufSize);
#endif
char cmdLine[1024];
#if __APPLE__
2016-09-30 03:31:24 +00:00
snprintf(cmdLine, 1024, "atos -p %d", getpid());
#else
snprintf(cmdLine, 1024, "addr2line -C -f -e \"%s\"", exeNameBuffer);
#endif
std::string cmdLineStr = cmdLine;
for (size_t i = 0; i < size; i++)
{
snprintf(cmdLine, 128, " %p", array[i]);
cmdLineStr += cmdLine;
}
FILE* fp = popen(cmdLineStr.c_str(), "r");
if (fp)
{
char readBuf[256];
size_t readSz;
while ((readSz = fread(readBuf, 1, 256, fp)))
fwrite(readBuf, 1, readSz, stderr);
pclose(fp);
}
else
{
for (size_t i = 0; i < size; i++)
{
fprintf(stderr, "- ");
Dl_info dlip;
if (dladdr(array[i], &dlip))
{
int status;
char* demangledName = abi::__cxa_demangle(dlip.dli_sname, nullptr, nullptr, &status);
fprintf(stderr, "%p(%s+%p)\n", dlip.dli_saddr, demangledName ? demangledName : dlip.dli_sname,
(void*)((uint8_t*)array[i] - (uint8_t*)dlip.dli_fbase));
free(demangledName);
}
else
{
fprintf(stderr, "%p\n", array[i]);
}
}
}
2016-09-09 04:18:15 +00:00
fflush(stderr);
2017-01-17 01:59:00 +00:00
fflush(stdout);
2017-01-19 09:01:09 +00:00
signal(SIGABRT, SIG_DFL);
abort();
}
#endif
2017-01-18 20:54:00 +00:00
LogMutex _LogMutex;
static void AbortHandler(int signum)
{
2017-01-18 20:54:00 +00:00
_LogMutex.enabled = false;
switch (signum)
{
case SIGSEGV:
Log.report(logvisor::Fatal, "Segmentation Fault");
case SIGILL:
Log.report(logvisor::Fatal, "Bad Execution");
case SIGFPE:
Log.report(logvisor::Fatal, "Floating Point Exception");
2017-01-17 01:59:00 +00:00
case SIGABRT:
Log.report(logvisor::Fatal, "Abort Signal");
default:
Log.report(logvisor::Fatal, "unknown signal %d", signum);
}
}
2015-07-03 22:08:41 +00:00
std::vector<std::unique_ptr<ILogger>> MainLoggers;
std::atomic_size_t ErrorCount(0);
2015-07-03 22:08:41 +00:00
static std::chrono::steady_clock MonoClock;
static std::chrono::steady_clock::time_point GlobalStart = MonoClock.now();
static inline std::chrono::steady_clock::duration CurrentUptime()
{return MonoClock.now() - GlobalStart;}
2015-07-26 20:55:17 +00:00
std::atomic_uint_fast64_t FrameIndex(0);
2016-03-05 03:21:18 +00:00
static inline int ConsoleWidth()
{
int retval = 80;
#if _WIN32
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);
2015-09-10 06:19:32 +00:00
retval = info.dwSize.X - 1;
#else
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1)
retval = w.ws_col;
#endif
if (retval < 10)
return 10;
return retval;
}
2015-07-03 22:08:41 +00:00
#if _WIN32
static HANDLE Term = 0;
#else
static const char* Term = nullptr;
#endif
2015-10-07 23:18:37 +00:00
bool XtermColor = false;
2015-07-03 22:08:41 +00:00
struct ConsoleLogger : public ILogger
{
ConsoleLogger()
{
#if _WIN32
const char* conemuANSI = getenv("ConEmuANSI");
if (conemuANSI && !strcmp(conemuANSI, "ON"))
XtermColor = true;
2015-07-03 22:08:41 +00:00
if (!Term)
Term = GetStdHandle(STD_ERROR_HANDLE);
#else
if (!Term)
{
Term = getenv("TERM");
2015-07-06 06:50:28 +00:00
if (Term && !strncmp(Term, "xterm", 5))
{
2015-07-03 22:08:41 +00:00
XtermColor = true;
putenv((char*)"TERM=xterm-16color");
}
2015-07-03 22:08:41 +00:00
}
#endif
}
2015-07-04 06:41:44 +00:00
static void _reportHead(const char* modName, const char* sourceInfo, Level severity)
2015-07-03 22:08:41 +00:00
{
/* Clear current line out */
int width = ConsoleWidth();
fprintf(stderr, "\r");
for (int w=0 ; w<width ; ++w)
fprintf(stderr, " ");
fprintf(stderr, "\r");
2016-03-05 03:21:18 +00:00
2015-07-03 22:08:41 +00:00
std::chrono::steady_clock::duration tm = CurrentUptime();
2015-07-04 06:01:39 +00:00
double tmd = tm.count() *
std::chrono::steady_clock::duration::period::num /
2015-07-03 22:08:41 +00:00
(double)std::chrono::steady_clock::duration::period::den;
std::thread::id thrId = std::this_thread::get_id();
const char* thrName = nullptr;
if (ThreadMap.find(thrId) != ThreadMap.end())
thrName = ThreadMap[thrId];
if (XtermColor)
{
fprintf(stderr, BOLD "[");
2015-07-04 06:41:44 +00:00
fprintf(stderr, GREEN "%5.4f ", tmd);
2015-07-26 20:55:17 +00:00
uint_fast64_t fIdx = FrameIndex.load();
if (fIdx)
fprintf(stderr, "(%" PRIu64 ") ", fIdx);
switch (severity)
{
case Info:
fprintf(stderr, BOLD CYAN "INFO");
break;
case Warning:
fprintf(stderr, BOLD YELLOW "WARNING");
break;
case Error:
fprintf(stderr, RED BOLD "ERROR");
break;
2016-03-04 20:28:28 +00:00
case Fatal:
fprintf(stderr, BOLD RED "FATAL ERROR");
break;
default:
break;
};
fprintf(stderr, NORMAL BOLD " %s", modName);
if (sourceInfo)
fprintf(stderr, BOLD YELLOW " {%s}", sourceInfo);
if (thrName)
fprintf(stderr, BOLD MAGENTA " (%s)", thrName);
fprintf(stderr, NORMAL BOLD "] " NORMAL);
}
else
{
#if _WIN32
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
fprintf(stderr, "[");
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
fprintf(stderr, "%5.4f ", tmd);
uint64_t fi = FrameIndex.load();
if (fi)
fprintf(stderr, "(%" PRIu64 ") ", fi);
2015-07-03 22:08:41 +00:00
switch (severity)
{
2015-07-04 06:01:39 +00:00
case Info:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
fprintf(stderr, "INFO");
2015-07-03 22:08:41 +00:00
break;
2015-07-04 06:01:39 +00:00
case Warning:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
fprintf(stderr, "WARNING");
2015-07-03 22:08:41 +00:00
break;
2015-07-04 06:01:39 +00:00
case Error:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
fprintf(stderr, "ERROR");
2015-07-03 22:08:41 +00:00
break;
2016-03-05 03:21:18 +00:00
case Fatal:
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED);
fprintf(stderr, "FATAL ERROR");
2015-07-03 22:08:41 +00:00
break;
default:
break;
};
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
fprintf(stderr, " %s", modName);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);
2015-07-04 06:41:44 +00:00
if (sourceInfo)
fprintf(stderr, " {%s}", sourceInfo);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);
2015-07-03 22:08:41 +00:00
if (thrName)
fprintf(stderr, " (%s)", thrName);
SetConsoleTextAttribute(Term, FOREGROUND_INTENSITY | FOREGROUND_WHITE);
fprintf(stderr, "] ");
SetConsoleTextAttribute(Term, FOREGROUND_WHITE);
#else
2015-07-03 22:08:41 +00:00
fprintf(stderr, "[");
2015-07-04 06:41:44 +00:00
fprintf(stderr, "%5.4f ", tmd);
2015-07-26 20:55:17 +00:00
uint_fast64_t fIdx = FrameIndex.load();
if (fIdx)
fprintf(stderr, "(%" PRIu64 ") ", fIdx);
2015-07-03 22:08:41 +00:00
switch (severity)
{
2015-07-04 06:01:39 +00:00
case Info:
2015-07-03 22:08:41 +00:00
fprintf(stderr, "INFO");
break;
2015-07-04 06:01:39 +00:00
case Warning:
2015-07-03 22:08:41 +00:00
fprintf(stderr, "WARNING");
break;
2015-07-04 06:01:39 +00:00
case Error:
2015-07-03 22:08:41 +00:00
fprintf(stderr, "ERROR");
break;
2016-03-04 20:28:28 +00:00
case Fatal:
2015-07-03 22:08:41 +00:00
fprintf(stderr, "FATAL ERROR");
break;
default:
break;
};
fprintf(stderr, " %s", modName);
2015-07-04 06:41:44 +00:00
if (sourceInfo)
fprintf(stderr, " {%s}", sourceInfo);
2015-07-03 22:08:41 +00:00
if (thrName)
fprintf(stderr, " (%s)", thrName);
fprintf(stderr, "] ");
#endif
}
2015-07-03 22:08:41 +00:00
}
void report(const char* modName, Level severity,
const char* format, va_list ap)
{
2015-07-04 06:41:44 +00:00
_reportHead(modName, nullptr, severity);
2015-07-03 22:08:41 +00:00
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
}
void report(const char* modName, Level severity,
const wchar_t* format, va_list ap)
{
2015-07-04 06:41:44 +00:00
_reportHead(modName, nullptr, severity);
vfwprintf(stderr, format, ap);
fprintf(stderr, "\n");
}
void reportSource(const char* modName, Level severity,
const char* file, unsigned linenum,
const char* format, va_list ap)
{
char sourceInfo[128];
snprintf(sourceInfo, 128, "%s:%u", file, linenum);
_reportHead(modName, sourceInfo, severity);
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
}
void reportSource(const char* modName, Level severity,
const char* file, unsigned linenum,
const wchar_t* format, va_list ap)
{
char sourceInfo[128];
snprintf(sourceInfo, 128, "%s:%u", file, linenum);
_reportHead(modName, sourceInfo, severity);
2015-07-03 22:08:41 +00:00
vfwprintf(stderr, format, ap);
fprintf(stderr, "\n");
}
};
void RegisterConsoleLogger()
{
/* Determine if console logger already added */
for (auto& logger : MainLoggers)
{
if (typeid(logger.get()) == typeid(ConsoleLogger))
return;
}
/* Otherwise construct new console logger */
MainLoggers.emplace_back(new ConsoleLogger);
}
2015-11-05 00:02:40 +00:00
#if _WIN32
void CreateWin32Console()
{
/* Debug console */
AllocConsole();
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
}
#endif
void RegisterStandardExceptions()
{
2017-01-17 01:59:00 +00:00
signal(SIGABRT, AbortHandler);
signal(SIGSEGV, AbortHandler);
signal(SIGILL, AbortHandler);
signal(SIGFPE, AbortHandler);
}
2015-07-03 22:08:41 +00:00
struct FileLogger : public ILogger
{
FILE* fp;
virtual void openFile()=0;
virtual void closeFile() {fclose(fp);}
2015-07-04 06:41:44 +00:00
void _reportHead(const char* modName, const char* sourceInfo, Level severity)
2015-07-03 22:08:41 +00:00
{
std::chrono::steady_clock::duration tm = CurrentUptime();
2015-07-04 06:01:39 +00:00
double tmd = tm.count() *
std::chrono::steady_clock::duration::period::num /
2015-07-03 22:08:41 +00:00
(double)std::chrono::steady_clock::duration::period::den;
std::thread::id thrId = std::this_thread::get_id();
const char* thrName = nullptr;
if (ThreadMap.find(thrId) != ThreadMap.end())
thrName = ThreadMap[thrId];
fprintf(fp, "[");
fprintf(fp, "%5.4f ", tmd);
uint_fast64_t fIdx = FrameIndex.load();
if (fIdx)
fprintf(fp, "(%" PRIu64 ") ", fIdx);
2015-07-03 22:08:41 +00:00
switch (severity)
{
2015-07-04 05:20:02 +00:00
case Info:
2015-07-03 22:08:41 +00:00
fprintf(fp, "INFO");
break;
2015-07-04 05:20:02 +00:00
case Warning:
2015-07-03 22:08:41 +00:00
fprintf(fp, "WARNING");
break;
2015-07-04 05:20:02 +00:00
case Error:
2015-07-03 22:08:41 +00:00
fprintf(fp, "ERROR");
break;
2016-03-04 20:28:28 +00:00
case Fatal:
2015-07-03 22:08:41 +00:00
fprintf(fp, "FATAL ERROR");
break;
default:
break;
};
fprintf(fp, " %s", modName);
2015-07-04 06:41:44 +00:00
if (sourceInfo)
fprintf(fp, " {%s}", sourceInfo);
2015-07-03 22:08:41 +00:00
if (thrName)
fprintf(fp, " (%s)", thrName);
fprintf(fp, "] ");
}
void report(const char* modName, Level severity,
const char* format, va_list ap)
{
openFile();
2015-07-04 06:41:44 +00:00
_reportHead(modName, nullptr, severity);
2015-07-03 22:08:41 +00:00
vfprintf(fp, format, ap);
fprintf(fp, "\n");
closeFile();
}
void report(const char* modName, Level severity,
const wchar_t* format, va_list ap)
{
openFile();
2015-07-04 06:41:44 +00:00
_reportHead(modName, nullptr, severity);
vfwprintf(fp, format, ap);
fprintf(fp, "\n");
closeFile();
}
void reportSource(const char* modName, Level severity,
const char* file, unsigned linenum,
const char* format, va_list ap)
{
openFile();
char sourceInfo[128];
snprintf(sourceInfo, 128, "%s:%u", file, linenum);
_reportHead(modName, sourceInfo, severity);
vfprintf(fp, format, ap);
fprintf(fp, "\n");
closeFile();
}
void reportSource(const char* modName, Level severity,
const char* file, unsigned linenum,
const wchar_t* format, va_list ap)
{
openFile();
char sourceInfo[128];
snprintf(sourceInfo, 128, "%s:%u", file, linenum);
_reportHead(modName, sourceInfo, severity);
2015-07-03 22:08:41 +00:00
vfwprintf(fp, format, ap);
fprintf(fp, "\n");
closeFile();
}
};
struct FileLogger8 : public FileLogger
{
const char* m_filepath;
FileLogger8(const char* filepath) : m_filepath(filepath) {}
void openFile() {fp = fopen(m_filepath, "a");}
};
void RegisterFileLogger(const char* filepath)
{
/* Determine if file logger already added */
for (auto& logger : MainLoggers)
{
FileLogger8* filelogger = dynamic_cast<FileLogger8*>(logger.get());
if (filelogger)
{
if (!strcmp(filepath, filelogger->m_filepath))
return;
}
}
/* Otherwise construct new file logger */
MainLoggers.emplace_back(new FileLogger8(filepath));
}
#if LOG_UCS2
struct FileLogger16 : public FileLogger
{
const wchar_t* m_filepath;
FileLogger16(const wchar_t* filepath) : m_filepath(filepath) {}
void openFile() {fp = _wfopen(m_filepath, L"a");}
};
void RegisterFileLogger(const wchar_t* filepath)
{
/* Determine if file logger already added */
for (auto& logger : MainLoggers)
{
FileLogger16* filelogger = dynamic_cast<FileLogger16*>(logger.get());
if (filelogger)
{
if (!wcscmp(filepath, filelogger->m_filepath))
return;
}
}
/* Otherwise construct new file logger */
MainLoggers.emplace_back(new FileLogger16(filepath));
}
#endif
}