6#include "../HashFunctions.h"
7#include "../Platform/Atomic.h"
15#elif !defined( EMSCRIPTEN )
27 Cogs::Atomic<Cogs::Logging::Category> minimumCategory(Cogs::Logging::Category::Trace);
30 LPTOP_LEVEL_EXCEPTION_FILTER previousFilter =
nullptr;
33 Cogs::Mutex& getConsumersMutex() {
34 static Cogs::Mutex mutex;
38 std::set<Cogs::Logging::Consumer*>& getConsumers() {
39 static std::set<Cogs::Logging::Consumer*> consumers;
49 void consumeMessage(
const char* source,
Category category, uint32_t errorNumber,
const char* filename,
int lineNumber, _Printf_format_string_
const char* fmt, va_list argptr) {
50 if (category >= minimumCategory.load()) {
51 LockGuard lock(getConsumersMutex());
54 static char buffer[16000]{};
60 int rv = std::vsnprintf(buffer,
sizeof(buffer), fmt, argptr);
63 for (
Consumer* consumer : getConsumers()) {
64 if (category >= consumer->getMinimumCategory()) {
65 consumer->consumeMessage(source, category, errorNumber, buffer, filename, lineNumber);
71 std::snprintf(buffer,
sizeof(buffer),
"Invalid log format: %s", fmt);
72 for (
Consumer* consumer : getConsumers()) {
73 if (category >= consumer->getMinimumCategory()) {
75 consumer->consumeMessage(source, category, errorNumber, buffer, filename, lineNumber);
83 LONG WINAPI exceptionHandler(EXCEPTION_POINTERS* ep) {
85 int noofaddresses = CaptureStackBackTrace(0, 199, addresses,
nullptr);
88 LOG_FATAL(logger,
"Caught unhandled exception.");
90 for(
int idx = 0; idx < noofaddresses; ++idx) {
91 char local[
sizeof(IMAGEHLP_SYMBOL64) + 992];
92 IMAGEHLP_SYMBOL64* symbol =
reinterpret_cast<IMAGEHLP_SYMBOL64*
>(local);
93 DWORD64 realaddr =
reinterpret_cast<DWORD64
>(addresses[idx]) - 1;
96 memset(local, 0,
sizeof(local));
98 symbol->SizeOfStruct =
sizeof(IMAGEHLP_SYMBOL64);
99 symbol->MaxNameLength =
sizeof(local) -
sizeof(IMAGEHLP_SYMBOL64);
101 if(SymGetSymFromAddr64(GetCurrentProcess(), realaddr, &symdisp, symbol) || (!GetLastError() && symbol->Name[0])) {
104 IMAGEHLP_LINE64 line;
106 UnDecorateSymbolName(symbol->Name, symname,
sizeof(symname), UNDNAME_COMPLETE);
108 memset(&line, 0,
sizeof(line));
109 line.SizeOfStruct =
sizeof(line);
111 if(SymGetLineFromAddr64(GetCurrentProcess(), realaddr, &linedisp, &line)) {
112 LOG_FATAL(logger,
"%p - %s (%s:%d)",
reinterpret_cast<void*
>(realaddr), symname, line.FileName, line.LineNumber);
115 LOG_FATAL(logger,
"%p - %s",
reinterpret_cast<void*
>(realaddr), symname);
119 LOG_FATAL(logger,
"%p - (Unknown)",
reinterpret_cast<void*
>(realaddr));
122 return previousFilter ? previousFilter(ep) : EXCEPTION_CONTINUE_SEARCH;
124#elif !defined( EMSCRIPTEN )
130 _Unwind_Reason_Code unwindCallback(
struct _Unwind_Context* context,
void* arg)
133 uintptr_t pc = _Unwind_GetIP(context);
136 if (state->current == state->end) {
137 return _URC_END_OF_STACK;
139 *state->current++ =
reinterpret_cast<void*
>(pc);
141 return _URC_NO_REASON;
144 void printFrame(
void* address) {
146 char hexAddr[20] = {
'0',
'x',
'0',
'0',
'0',
'0',
'0',
'0',
'0',
'0',
'0',
'0',
'0',
'0',
'0',
'0',
'0',
'0',
':',
' ' };
147 uint64_t addrValue =
reinterpret_cast<uint64_t
>(address);
148 int file = fileno(stderr);
150 for (
int idx = 18; addrValue; addrValue >>= 4) {
151 hexAddr[--idx] =
"0123456789ABCDEF"[addrValue & 0x0F];
154 write(file, hexAddr, 20);
156 if (dladdr(address, &info)) {
157 if (info.dli_sname) {
159 char* demangled = abi::__cxa_demangle(info.dli_sname,
nullptr,
nullptr, &status);
163 write(file, demangled, strlen(demangled));
168 write(file, info.dli_sname, strlen(info.dli_sname));
171 else if (info.dli_fname) {
172 write(file, info.dli_fname, strlen(info.dli_fname));
175 write(file,
"\n", 1);
178 void signalHandler(
int sig) {
179 static volatile sig_atomic_t inProgress = 0;
180 void* addresses[200];
181 BacktraceState state = { addresses, addresses + 200 };
182 struct sigaction sigact = {};
187 sigact.sa_handler = SIG_DFL;
188 sigaction(sig, &sigact,
nullptr);
189 sigaction(SIGABRT, &sigact,
nullptr);
191 _Unwind_Backtrace(&unwindCallback, &state);
194 case SIGILL: write(fileno(stderr),
"Caught SIGILL.\n", 15);
break;
195 case SIGABRT: write(fileno(stderr),
"Caught SIGABRT.\n", 16);
break;
196 case SIGFPE: write(fileno(stderr),
"Caught SIGFPE.\n", 15);
break;
197 case SIGSEGV: write(fileno(stderr),
"Caught SIGSEGV.\n", 16);
break;
198 case SIGBUS: write(fileno(stderr),
"Caught SIGBUS.\n", 15);
break;
201 for (
int idx = 0, addrCount =
static_cast<int>(state.current - addresses); idx < addrCount; ++idx) {
202 printFrame(addresses[idx]);
204 fsync(fileno(stderr));
216 Consumer::setDefaultMinimumCategory(category);
222 for (
Consumer* consumer : getConsumers()) {
223 consumer->setMinimumCategory(category);
233 LockGuard lock(getConsumersMutex());
234 Category category = Category::Fatal;
236 for (
Consumer* consumer : getConsumers()) {
237 category = std::min(category, consumer->getMinimumCategory());
239 minimumCategory = category;
247 SymSetOptions(SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_ALLOW_ABSOLUTE_SYMBOLS | SYMOPT_INCLUDE_32BIT_MODULES);
248 SymInitialize(GetCurrentProcess(),
nullptr, 1);
249 SetUnhandledExceptionFilter(&exceptionHandler);
250#elif !defined( EMSCRIPTEN )
251 struct sigaction sigact = {};
253 sigact.sa_handler = &signalHandler;
254 sigemptyset(&sigact.sa_mask);
256 sigaddset(&sigact.sa_mask, SIGILL);
257 sigaddset(&sigact.sa_mask, SIGABRT);
258 sigaddset(&sigact.sa_mask, SIGFPE);
259 sigaddset(&sigact.sa_mask, SIGSEGV);
260 sigaddset(&sigact.sa_mask, SIGBUS);
262 sigaction(SIGILL, &sigact,
nullptr);
263 sigaction(SIGABRT, &sigact,
nullptr);
264 sigaction(SIGFPE, &sigact,
nullptr);
265 sigaction(SIGSEGV, &sigact,
nullptr);
266 sigaction(SIGBUS, &sigact,
nullptr);
272 LockGuard lock(getConsumersMutex());
274 getConsumers().insert(consumer);
278 LockGuard lock(getConsumersMutex());
279 std::set<Consumer*>& consumers = getConsumers();
280 auto i = consumers.find(consumer);
282 if (i != consumers.end()) {
292 case Cogs::hash(
"trace"):
return Logging::Category::Trace;
293 case Cogs::hash(
"debug"):
return Logging::Category::Debug;
294 case Cogs::hash(
"info"):
return Logging::Category::Info;
295 case Cogs::hash(
"warning"):
return Logging::Category::Warning;
296 case Cogs::hash(
"error"):
return Logging::Category::Error;
297 case Cogs::hash(
"fatal"):
return Logging::Category::Fatal;
298 default:
return Logging::Category::Debug;
303 return (errorNumber & group) == group;
307 consumeMessage(source, category, errorNumber,
"", 0, message,
nullptr);
313 va_start(argptr, fmt);
314 consumeMessage(source, category, errorNumber,
"", 0, fmt, argptr);
319 consumeMessage(source, category, errorNumber,
"", 0, fmt, argptr);
322void Cogs::Logging::logFileLine(
const char* filename,
const int lineNumber,
const char * source,
const Category category, uint32_t errorNumber, _Printf_format_string_
const char * fmt, ...) {
325 va_start(argptr, fmt);
326 consumeMessage(source, category, errorNumber, filename, lineNumber, fmt, argptr);
331 consumeMessage(source, category, errorNumber, filename, lineNumber, fmt, argptr);
Consumer is the base class for objects that want to consume log messages through the LogManager.
Log implementation class.
Provides a weakly referenced view over the contents of a string.
Contains logging functionality for the Cogs native library.
void COGSFOUNDATION_API enableUnhandledExceptionLogging()
Enable catching and logging of hardware exceptions (crashes) before exiting. (Windows only - for now....
void COGSFOUNDATION_API registerConsumer(Consumer *consumer)
Registers the specified consumer with the LogManager.
void COGSFOUNDATION_API logFileLineArgs(const char *file, const int line, const char *source, const Category category, uint32_t errorNumber, const char *fmt, va_list args)
Log the given formatted string with argument list and file/line information.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Category COGSFOUNDATION_API parseCategoryString(const StringView category)
Utility function that takes a loglevel name as a string and returns the corresponding log level enum ...
void COGSFOUNDATION_API logFileLine(const char *file, const int line, const char *source, const Category category, uint32_t errorNumber, _Printf_format_string_ const char *fmt,...) VALIDATE_ARGS(6)
Logs the given formatted string (using printf formatting rules) with file/line information and source...
bool COGSFOUNDATION_API isErrorGroup(uint32_t errorNumber, ErrorGroup group)
Tests whether the specified error number is from the given group.
void COGSFOUNDATION_API updateLoggerCategory(Category category)
void COGSFOUNDATION_API setLoggerCategory(Category category)
Sets the default category level for loggers created after this call.
ErrorGroup
ErrorGroup values define the top 16-bits of module specific error numbers.
void consumeMessage(const char *source, Category category, uint32_t errorNumber, const char *filename, int lineNumber, _Printf_format_string_ const char *fmt, va_list argptr)
Forwards the incoming log message to all interested consumers.
void COGSFOUNDATION_API logArgs(const char *source, const Category category, uint32_t errorNumber, const char *fmt, va_list args)
Log the given formatted string with argument list.
Category
Logging categories used to filter log messages.
void COGSFOUNDATION_API log(const char *message, const char *source, const Category category, uint32_t errorNumber)
Logs the given message with source and category.
void updateMinimumCategory()
Internal.
void COGSFOUNDATION_API unregisterConsumer(Consumer *consumer)
Removes the specified consumer from the LogManager.
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
COGSFOUNDATION_API size_t hashLowercase(std::string_view str, size_t hashValue=Cogs::hash()) noexcept
Get the hash code of the string converted to lowercase.