nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: Filename : OVR_Log.cpp nuclear@0: Content : Logging support nuclear@0: Created : September 19, 2012 nuclear@0: Notes : nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #include "OVR_Log.h" nuclear@0: #include "OVR_Std.h" nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "../Kernel/OVR_System.h" nuclear@0: #include "../Kernel/OVR_DebugHelp.h" nuclear@0: #include "../Util/Util_SystemGUI.h" nuclear@0: nuclear@0: #if defined(OVR_OS_MS) && !defined(OVR_OS_MS_MOBILE) nuclear@0: #define WIN32_LEAN_AND_MEAN nuclear@0: #include nuclear@0: #elif defined(OVR_OS_ANDROID) nuclear@0: #include nuclear@0: #elif defined(OVR_OS_LINUX) || defined(OVR_OS_MAC) || defined(OVR_OS_UNIX) nuclear@0: #include nuclear@0: #endif nuclear@0: nuclear@0: nuclear@0: class LogSubject : public OVR::SystemSingletonBase nuclear@0: { nuclear@0: static bool isShuttingDown; nuclear@0: nuclear@0: public: nuclear@0: nuclear@0: LogSubject(){ nuclear@0: isShuttingDown = false; nuclear@0: // Must be at end of function nuclear@0: PushDestroyCallbacks(); nuclear@0: } nuclear@0: nuclear@0: virtual ~LogSubject(){} // Required because we use delete this below. nuclear@0: nuclear@0: virtual void OnThreadDestroy() nuclear@0: { nuclear@0: isShuttingDown = true; nuclear@0: } nuclear@0: nuclear@0: virtual void OnSystemDestroy() nuclear@0: { nuclear@0: delete this; nuclear@0: } nuclear@0: nuclear@0: static bool IsValid() { nuclear@0: return isShuttingDown == false; nuclear@0: } nuclear@0: nuclear@0: OVR::Lock logSubjectLock; nuclear@0: OVR::ObserverScope logSubject; nuclear@0: }; nuclear@0: nuclear@0: bool LogSubject::isShuttingDown; nuclear@0: nuclear@0: OVR_DEFINE_SINGLETON(LogSubject); nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: // Global Log pointer. nuclear@0: Log* volatile OVR_GlobalLog = 0; nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** Log Implementation nuclear@0: nuclear@0: Log::Log(unsigned logMask) : nuclear@0: LoggingMask(logMask) nuclear@0: { nuclear@0: #ifdef OVR_OS_WIN32 nuclear@0: hEventSource = RegisterEventSourceA(NULL, "OculusVR"); nuclear@0: OVR_ASSERT(hEventSource != NULL); nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: Log::~Log() nuclear@0: { nuclear@0: #ifdef OVR_OS_WIN32 nuclear@0: if (hEventSource) nuclear@0: { nuclear@0: DeregisterEventSource(hEventSource); nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: // Clear out global log nuclear@0: if (this == OVR_GlobalLog) nuclear@0: { nuclear@0: // TBD: perhaps we should ASSERT if this happens before system shutdown? nuclear@0: OVR_GlobalLog = 0; nuclear@0: } nuclear@0: } nuclear@0: void Log::AddLogObserver(ObserverScope *logObserver) nuclear@0: { nuclear@0: if (OVR::System::IsInitialized() && LogSubject::GetInstance()->IsValid()) nuclear@0: { nuclear@0: Lock::Locker locker(&LogSubject::GetInstance()->logSubjectLock); nuclear@0: logObserver->GetPtr()->Observe(LogSubject::GetInstance()->logSubject); nuclear@0: } nuclear@0: } nuclear@0: void Log::LogMessageVargInt(LogMessageType messageType, const char* fmt, va_list argList) nuclear@0: { nuclear@0: if (OVR::System::IsInitialized() && LogSubject::GetInstance()->IsValid()) nuclear@0: { nuclear@0: // Invoke subject nuclear@0: char buffer[MaxLogBufferMessageSize]; nuclear@0: char* pBuffer = buffer; nuclear@0: char* pAllocated = NULL; nuclear@0: nuclear@0: #if !defined(OVR_CC_MSVC) // Non-Microsoft compilers require you to save a copy of the va_list. nuclear@0: va_list argListSaved; nuclear@0: va_copy(argListSaved, argList); nuclear@0: #endif nuclear@0: nuclear@0: int result = FormatLog(pBuffer, MaxLogBufferMessageSize, Log_Text, fmt, argList); nuclear@0: nuclear@0: if(result >= MaxLogBufferMessageSize) // If there was insufficient capacity... nuclear@0: { nuclear@0: pAllocated = (char*)OVR_ALLOC(result + 1); // We assume C++ exceptions are disabled. FormatLog will handle the case that pAllocated is NULL. nuclear@0: pBuffer = pAllocated; nuclear@0: nuclear@0: #if !defined(OVR_CC_MSVC) nuclear@0: va_end(argList); // The caller owns argList and will call va_end on it. nuclear@0: va_copy(argList, argListSaved); nuclear@0: #endif nuclear@0: nuclear@0: FormatLog(pBuffer, (size_t)result + 1, Log_Text, fmt, argList); nuclear@0: } nuclear@0: nuclear@0: Lock::Locker locker(&LogSubject::GetInstance()->logSubjectLock); nuclear@0: LogSubject::GetInstance()->logSubject.GetPtr()->Call(pBuffer, messageType); nuclear@0: nuclear@0: delete[] pAllocated; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void Log::LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList) nuclear@0: { nuclear@0: if ((messageType & LoggingMask) == 0) nuclear@0: return; nuclear@0: #ifndef OVR_BUILD_DEBUG nuclear@0: if (IsDebugMessage(messageType)) nuclear@0: return; nuclear@0: #endif nuclear@0: nuclear@0: char buffer[MaxLogBufferMessageSize]; nuclear@0: char* pBuffer = buffer; nuclear@0: char* pAllocated = NULL; nuclear@0: nuclear@0: #if !defined(OVR_CC_MSVC) // Non-Microsoft compilers require you to save a copy of the va_list. nuclear@0: va_list argListSaved; nuclear@0: va_copy(argListSaved, argList); nuclear@0: #endif nuclear@0: nuclear@0: int result = FormatLog(pBuffer, MaxLogBufferMessageSize, messageType, fmt, argList); nuclear@0: nuclear@0: if(result >= MaxLogBufferMessageSize) // If there was insufficient capacity... nuclear@0: { nuclear@0: pAllocated = (char*)OVR_ALLOC(result + 1); // We assume C++ exceptions are disabled. FormatLog will handle the case that pAllocated is NULL. nuclear@0: pBuffer = pAllocated; nuclear@0: nuclear@0: #if !defined(OVR_CC_MSVC) nuclear@0: va_end(argList); // The caller owns argList and will call va_end on it. nuclear@0: va_copy(argList, argListSaved); nuclear@0: #endif nuclear@0: nuclear@0: FormatLog(pBuffer, (size_t)result + 1, messageType, fmt, argList); nuclear@0: } nuclear@0: nuclear@0: DefaultLogOutput(pBuffer, messageType, result); nuclear@0: delete[] pAllocated; nuclear@0: } nuclear@0: nuclear@0: void OVR::Log::LogMessage(LogMessageType messageType, const char* pfmt, ...) nuclear@0: { nuclear@0: va_list argList; nuclear@0: va_start(argList, pfmt); nuclear@0: LogMessageVarg(messageType, pfmt, argList); nuclear@0: va_end(argList); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // Return behavior is the same as ISO C vsnprintf: returns the required strlen of buffer (which will nuclear@0: // be >= bufferSize if bufferSize is insufficient) or returns a negative value because the input was bad. nuclear@0: int Log::FormatLog(char* buffer, size_t bufferSize, LogMessageType messageType, nuclear@0: const char* fmt, va_list argList) nuclear@0: { nuclear@0: OVR_ASSERT(buffer && (bufferSize >= 10)); // Need to be able to at least print "Assert: \n" to it. nuclear@0: if(!buffer || (bufferSize < 10)) nuclear@0: return -1; nuclear@0: nuclear@0: int addNewline = 1; nuclear@0: int prefixLength = 0; nuclear@0: nuclear@0: switch(messageType) nuclear@0: { nuclear@0: case Log_Error: OVR_strcpy(buffer, bufferSize, "Error: "); prefixLength = 7; break; nuclear@0: case Log_Debug: OVR_strcpy(buffer, bufferSize, "Debug: "); prefixLength = 7; break; nuclear@0: case Log_Assert: OVR_strcpy(buffer, bufferSize, "Assert: "); prefixLength = 8; break; nuclear@0: case Log_Text: buffer[0] = 0; addNewline = 0; break; nuclear@0: case Log_DebugText: buffer[0] = 0; addNewline = 0; break; nuclear@0: default: buffer[0] = 0; addNewline = 0; break; nuclear@0: } nuclear@0: nuclear@0: char* buffer2 = buffer + prefixLength; nuclear@0: size_t size2 = bufferSize - (size_t)prefixLength; nuclear@0: int messageLength = OVR_vsnprintf(buffer2, size2, fmt, argList); nuclear@0: nuclear@0: if (addNewline) nuclear@0: { nuclear@0: if (messageLength < 0) // If there was a format error... nuclear@0: { nuclear@0: // To consider: append to the buffer here. nuclear@0: buffer2[0] = '\n'; // We are guaranteed to have capacity for this. nuclear@0: buffer2[1] = '\0'; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // If the printed string used all of the capacity or required more than the capacity, nuclear@0: // Chop the output by one character so we can append the \n safely. nuclear@0: int messageEnd = (messageLength >= (int)(size2 - 1)) ? (int)(size2 - 2) : messageLength; nuclear@0: buffer2[messageEnd + 0] = '\n'; nuclear@0: buffer2[messageEnd + 1] = '\0'; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (messageLength >= 0) // If the format was OK... nuclear@0: return prefixLength + messageLength + addNewline; // Return the required strlen of buffer. nuclear@0: nuclear@0: return messageLength; // Else we cannot know what the required strlen is and return the error to the caller. nuclear@0: } nuclear@0: nuclear@0: void Log::DefaultLogOutput(const char* formattedText, LogMessageType messageType, int bufferSize) nuclear@0: { nuclear@0: bool debug = IsDebugMessage(messageType); nuclear@0: OVR_UNUSED(bufferSize); nuclear@0: nuclear@0: #if defined(OVR_OS_WIN32) nuclear@0: // Under Win32, output regular messages to console if it exists; debug window otherwise. nuclear@0: static DWORD dummyMode; nuclear@0: static bool hasConsole = (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) && nuclear@0: (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummyMode)); nuclear@0: nuclear@0: if (!hasConsole || debug) nuclear@0: { nuclear@0: ::OutputDebugStringA(formattedText); nuclear@0: } nuclear@0: nuclear@0: fputs(formattedText, stdout); nuclear@0: nuclear@0: #elif defined(OVR_OS_MS) // Any other Microsoft OSs nuclear@0: nuclear@0: ::OutputDebugStringA(formattedText); nuclear@0: nuclear@0: #elif defined(OVR_OS_ANDROID) nuclear@0: // To do: use bufferSize to deal with the case that Android has a limited output length. nuclear@0: __android_log_write(ANDROID_LOG_INFO, "OVR", formattedText); nuclear@0: nuclear@0: #else nuclear@0: fputs(formattedText, stdout); nuclear@0: nuclear@0: #endif nuclear@0: nuclear@0: if (messageType == Log_Error) nuclear@0: { nuclear@0: #if defined(OVR_OS_WIN32) nuclear@0: if (!ReportEventA(hEventSource, EVENTLOG_ERROR_TYPE, 0, 0, NULL, 1, 0, &formattedText, NULL)) nuclear@0: { nuclear@0: OVR_ASSERT(false); nuclear@0: } nuclear@0: #elif defined(OVR_OS_MS) // Any other Microsoft OSs nuclear@0: // TBD nuclear@0: #elif defined(OVR_OS_ANDROID) nuclear@0: // TBD nuclear@0: #elif defined(OVR_OS_MAC) || defined(OVR_OS_LINUX) nuclear@0: syslog(LOG_ERR, "%s", formattedText); nuclear@0: #else nuclear@0: // TBD nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: // Just in case. nuclear@0: OVR_UNUSED2(formattedText, debug); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //static nuclear@0: void Log::SetGlobalLog(Log *log) nuclear@0: { nuclear@0: OVR_GlobalLog = log; nuclear@0: } nuclear@0: //static nuclear@0: Log* Log::GetGlobalLog() nuclear@0: { nuclear@0: // No global log by default? nuclear@0: // if (!OVR_GlobalLog) nuclear@0: // OVR_GlobalLog = GetDefaultLog(); nuclear@0: return OVR_GlobalLog; nuclear@0: } nuclear@0: nuclear@0: //static nuclear@0: Log* Log::GetDefaultLog() nuclear@0: { nuclear@0: // Create default log pointer statically so that it can be used nuclear@0: // even during startup. nuclear@0: static Log defaultLog; nuclear@0: return &defaultLog; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** Global Logging functions nuclear@0: nuclear@0: #if !defined(OVR_CC_MSVC) nuclear@0: // The reason for va_copy is because you can't use va_start twice on Linux nuclear@0: #define OVR_LOG_FUNCTION_IMPL(Name) \ nuclear@0: void Log##Name(const char* fmt, ...) \ nuclear@0: { \ nuclear@0: if (OVR_GlobalLog) \ nuclear@0: { \ nuclear@0: va_list argList1; \ nuclear@0: va_start(argList1, fmt); \ nuclear@0: va_list argList2; \ nuclear@0: va_copy(argList2, argList1); \ nuclear@0: OVR_GlobalLog->LogMessageVargInt(Log_##Name, fmt, argList2); \ nuclear@0: va_end(argList2); \ nuclear@0: OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList1); \ nuclear@0: va_end(argList1); \ nuclear@0: } \ nuclear@0: } nuclear@0: #else nuclear@0: #define OVR_LOG_FUNCTION_IMPL(Name) \ nuclear@0: void Log##Name(const char* fmt, ...) \ nuclear@0: { \ nuclear@0: if (OVR_GlobalLog) \ nuclear@0: { \ nuclear@0: va_list argList1; \ nuclear@0: va_start(argList1, fmt); \ nuclear@0: OVR_GlobalLog->LogMessageVargInt(Log_##Name, fmt, argList1); \ nuclear@0: OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList1); \ nuclear@0: va_end(argList1); \ nuclear@0: } \ nuclear@0: } nuclear@0: #endif // #if !defined(OVR_OS_WIN32) nuclear@0: nuclear@0: OVR_LOG_FUNCTION_IMPL(Text) nuclear@0: OVR_LOG_FUNCTION_IMPL(Error) nuclear@0: nuclear@0: #ifdef OVR_BUILD_DEBUG nuclear@0: OVR_LOG_FUNCTION_IMPL(DebugText) nuclear@0: OVR_LOG_FUNCTION_IMPL(Debug) nuclear@0: OVR_LOG_FUNCTION_IMPL(Assert) nuclear@0: #endif nuclear@0: nuclear@0: nuclear@0: nuclear@0: // Assertion handler support nuclear@0: // To consider: Move this to an OVR_Types.cpp or OVR_Assert.cpp source file. nuclear@0: nuclear@0: static OVRAssertionHandler sOVRAssertionHandler = OVR::DefaultAssertionHandler; nuclear@0: static intptr_t sOVRAssertionHandlerUserParameter = 0; nuclear@0: nuclear@0: OVRAssertionHandler GetAssertionHandler(intptr_t* userParameter) nuclear@0: { nuclear@0: if(userParameter) nuclear@0: *userParameter = sOVRAssertionHandlerUserParameter; nuclear@0: return sOVRAssertionHandler; nuclear@0: } nuclear@0: nuclear@0: void SetAssertionHandler(OVRAssertionHandler assertionHandler, intptr_t userParameter) nuclear@0: { nuclear@0: sOVRAssertionHandler = assertionHandler; nuclear@0: sOVRAssertionHandlerUserParameter = userParameter; nuclear@0: } nuclear@0: nuclear@0: intptr_t DefaultAssertionHandler(intptr_t /*userParameter*/, const char* title, const char* message) nuclear@0: { nuclear@0: if(OVRIsDebuggerPresent()) nuclear@0: { nuclear@0: OVR_DEBUG_BREAK; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: #if defined(OVR_BUILD_DEBUG) nuclear@0: // Print a stack trace of all threads. nuclear@0: OVR::String s; nuclear@0: OVR::String threadListOutput; nuclear@0: static OVR::SymbolLookup symbolLookup; nuclear@0: nuclear@0: s = "Failure: "; nuclear@0: s += message; nuclear@0: nuclear@0: if(symbolLookup.Initialize() && symbolLookup.ReportThreadCallstack(threadListOutput, 4)) // This '4' is there to skip our internal handling and retrieve starting at the assertion location (our caller) only. nuclear@0: { nuclear@0: s += "\r\n\r\n"; nuclear@0: s += threadListOutput; nuclear@0: } nuclear@0: nuclear@0: OVR::Util::DisplayMessageBox(title, s.ToCStr()); nuclear@0: #else nuclear@0: OVR::Util::DisplayMessageBox(title, message); nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: } // OVR