ovr_sdk

annotate LibOVR/Src/Kernel/OVR_Log.cpp @ 0:1b39a1b46319

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
rev   line source
nuclear@0 1 /************************************************************************************
nuclear@0 2
nuclear@0 3 Filename : OVR_Log.cpp
nuclear@0 4 Content : Logging support
nuclear@0 5 Created : September 19, 2012
nuclear@0 6 Notes :
nuclear@0 7
nuclear@0 8 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
nuclear@0 9
nuclear@0 10 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
nuclear@0 11 you may not use the Oculus VR Rift SDK except in compliance with the License,
nuclear@0 12 which is provided at the time of installation or download, or which
nuclear@0 13 otherwise accompanies this software in either electronic or hard copy form.
nuclear@0 14
nuclear@0 15 You may obtain a copy of the License at
nuclear@0 16
nuclear@0 17 http://www.oculusvr.com/licenses/LICENSE-3.2
nuclear@0 18
nuclear@0 19 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
nuclear@0 20 distributed under the License is distributed on an "AS IS" BASIS,
nuclear@0 21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
nuclear@0 22 See the License for the specific language governing permissions and
nuclear@0 23 limitations under the License.
nuclear@0 24
nuclear@0 25 ************************************************************************************/
nuclear@0 26
nuclear@0 27 #include "OVR_Log.h"
nuclear@0 28 #include "OVR_Std.h"
nuclear@0 29 #include <stdarg.h>
nuclear@0 30 #include <stdio.h>
nuclear@0 31 #include <time.h>
nuclear@0 32 #include "../Kernel/OVR_System.h"
nuclear@0 33 #include "../Kernel/OVR_DebugHelp.h"
nuclear@0 34 #include "../Util/Util_SystemGUI.h"
nuclear@0 35
nuclear@0 36 #if defined(OVR_OS_MS) && !defined(OVR_OS_MS_MOBILE)
nuclear@0 37 #define WIN32_LEAN_AND_MEAN
nuclear@0 38 #include <windows.h>
nuclear@0 39 #elif defined(OVR_OS_ANDROID)
nuclear@0 40 #include <android/log.h>
nuclear@0 41 #elif defined(OVR_OS_LINUX) || defined(OVR_OS_MAC) || defined(OVR_OS_UNIX)
nuclear@0 42 #include <syslog.h>
nuclear@0 43 #endif
nuclear@0 44
nuclear@0 45
nuclear@0 46 class LogSubject : public OVR::SystemSingletonBase<LogSubject>
nuclear@0 47 {
nuclear@0 48 static bool isShuttingDown;
nuclear@0 49
nuclear@0 50 public:
nuclear@0 51
nuclear@0 52 LogSubject(){
nuclear@0 53 isShuttingDown = false;
nuclear@0 54 // Must be at end of function
nuclear@0 55 PushDestroyCallbacks();
nuclear@0 56 }
nuclear@0 57
nuclear@0 58 virtual ~LogSubject(){} // Required because we use delete this below.
nuclear@0 59
nuclear@0 60 virtual void OnThreadDestroy()
nuclear@0 61 {
nuclear@0 62 isShuttingDown = true;
nuclear@0 63 }
nuclear@0 64
nuclear@0 65 virtual void OnSystemDestroy()
nuclear@0 66 {
nuclear@0 67 delete this;
nuclear@0 68 }
nuclear@0 69
nuclear@0 70 static bool IsValid() {
nuclear@0 71 return isShuttingDown == false;
nuclear@0 72 }
nuclear@0 73
nuclear@0 74 OVR::Lock logSubjectLock;
nuclear@0 75 OVR::ObserverScope<OVR::Log::LogHandler> logSubject;
nuclear@0 76 };
nuclear@0 77
nuclear@0 78 bool LogSubject::isShuttingDown;
nuclear@0 79
nuclear@0 80 OVR_DEFINE_SINGLETON(LogSubject);
nuclear@0 81
nuclear@0 82 namespace OVR {
nuclear@0 83
nuclear@0 84 // Global Log pointer.
nuclear@0 85 Log* volatile OVR_GlobalLog = 0;
nuclear@0 86
nuclear@0 87 //-----------------------------------------------------------------------------------
nuclear@0 88 // ***** Log Implementation
nuclear@0 89
nuclear@0 90 Log::Log(unsigned logMask) :
nuclear@0 91 LoggingMask(logMask)
nuclear@0 92 {
nuclear@0 93 #ifdef OVR_OS_WIN32
nuclear@0 94 hEventSource = RegisterEventSourceA(NULL, "OculusVR");
nuclear@0 95 OVR_ASSERT(hEventSource != NULL);
nuclear@0 96 #endif
nuclear@0 97 }
nuclear@0 98
nuclear@0 99 Log::~Log()
nuclear@0 100 {
nuclear@0 101 #ifdef OVR_OS_WIN32
nuclear@0 102 if (hEventSource)
nuclear@0 103 {
nuclear@0 104 DeregisterEventSource(hEventSource);
nuclear@0 105 }
nuclear@0 106 #endif
nuclear@0 107
nuclear@0 108 // Clear out global log
nuclear@0 109 if (this == OVR_GlobalLog)
nuclear@0 110 {
nuclear@0 111 // TBD: perhaps we should ASSERT if this happens before system shutdown?
nuclear@0 112 OVR_GlobalLog = 0;
nuclear@0 113 }
nuclear@0 114 }
nuclear@0 115 void Log::AddLogObserver(ObserverScope<LogHandler> *logObserver)
nuclear@0 116 {
nuclear@0 117 if (OVR::System::IsInitialized() && LogSubject::GetInstance()->IsValid())
nuclear@0 118 {
nuclear@0 119 Lock::Locker locker(&LogSubject::GetInstance()->logSubjectLock);
nuclear@0 120 logObserver->GetPtr()->Observe(LogSubject::GetInstance()->logSubject);
nuclear@0 121 }
nuclear@0 122 }
nuclear@0 123 void Log::LogMessageVargInt(LogMessageType messageType, const char* fmt, va_list argList)
nuclear@0 124 {
nuclear@0 125 if (OVR::System::IsInitialized() && LogSubject::GetInstance()->IsValid())
nuclear@0 126 {
nuclear@0 127 // Invoke subject
nuclear@0 128 char buffer[MaxLogBufferMessageSize];
nuclear@0 129 char* pBuffer = buffer;
nuclear@0 130 char* pAllocated = NULL;
nuclear@0 131
nuclear@0 132 #if !defined(OVR_CC_MSVC) // Non-Microsoft compilers require you to save a copy of the va_list.
nuclear@0 133 va_list argListSaved;
nuclear@0 134 va_copy(argListSaved, argList);
nuclear@0 135 #endif
nuclear@0 136
nuclear@0 137 int result = FormatLog(pBuffer, MaxLogBufferMessageSize, Log_Text, fmt, argList);
nuclear@0 138
nuclear@0 139 if(result >= MaxLogBufferMessageSize) // If there was insufficient capacity...
nuclear@0 140 {
nuclear@0 141 pAllocated = (char*)OVR_ALLOC(result + 1); // We assume C++ exceptions are disabled. FormatLog will handle the case that pAllocated is NULL.
nuclear@0 142 pBuffer = pAllocated;
nuclear@0 143
nuclear@0 144 #if !defined(OVR_CC_MSVC)
nuclear@0 145 va_end(argList); // The caller owns argList and will call va_end on it.
nuclear@0 146 va_copy(argList, argListSaved);
nuclear@0 147 #endif
nuclear@0 148
nuclear@0 149 FormatLog(pBuffer, (size_t)result + 1, Log_Text, fmt, argList);
nuclear@0 150 }
nuclear@0 151
nuclear@0 152 Lock::Locker locker(&LogSubject::GetInstance()->logSubjectLock);
nuclear@0 153 LogSubject::GetInstance()->logSubject.GetPtr()->Call(pBuffer, messageType);
nuclear@0 154
nuclear@0 155 delete[] pAllocated;
nuclear@0 156 }
nuclear@0 157 }
nuclear@0 158
nuclear@0 159 void Log::LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList)
nuclear@0 160 {
nuclear@0 161 if ((messageType & LoggingMask) == 0)
nuclear@0 162 return;
nuclear@0 163 #ifndef OVR_BUILD_DEBUG
nuclear@0 164 if (IsDebugMessage(messageType))
nuclear@0 165 return;
nuclear@0 166 #endif
nuclear@0 167
nuclear@0 168 char buffer[MaxLogBufferMessageSize];
nuclear@0 169 char* pBuffer = buffer;
nuclear@0 170 char* pAllocated = NULL;
nuclear@0 171
nuclear@0 172 #if !defined(OVR_CC_MSVC) // Non-Microsoft compilers require you to save a copy of the va_list.
nuclear@0 173 va_list argListSaved;
nuclear@0 174 va_copy(argListSaved, argList);
nuclear@0 175 #endif
nuclear@0 176
nuclear@0 177 int result = FormatLog(pBuffer, MaxLogBufferMessageSize, messageType, fmt, argList);
nuclear@0 178
nuclear@0 179 if(result >= MaxLogBufferMessageSize) // If there was insufficient capacity...
nuclear@0 180 {
nuclear@0 181 pAllocated = (char*)OVR_ALLOC(result + 1); // We assume C++ exceptions are disabled. FormatLog will handle the case that pAllocated is NULL.
nuclear@0 182 pBuffer = pAllocated;
nuclear@0 183
nuclear@0 184 #if !defined(OVR_CC_MSVC)
nuclear@0 185 va_end(argList); // The caller owns argList and will call va_end on it.
nuclear@0 186 va_copy(argList, argListSaved);
nuclear@0 187 #endif
nuclear@0 188
nuclear@0 189 FormatLog(pBuffer, (size_t)result + 1, messageType, fmt, argList);
nuclear@0 190 }
nuclear@0 191
nuclear@0 192 DefaultLogOutput(pBuffer, messageType, result);
nuclear@0 193 delete[] pAllocated;
nuclear@0 194 }
nuclear@0 195
nuclear@0 196 void OVR::Log::LogMessage(LogMessageType messageType, const char* pfmt, ...)
nuclear@0 197 {
nuclear@0 198 va_list argList;
nuclear@0 199 va_start(argList, pfmt);
nuclear@0 200 LogMessageVarg(messageType, pfmt, argList);
nuclear@0 201 va_end(argList);
nuclear@0 202 }
nuclear@0 203
nuclear@0 204
nuclear@0 205 // Return behavior is the same as ISO C vsnprintf: returns the required strlen of buffer (which will
nuclear@0 206 // be >= bufferSize if bufferSize is insufficient) or returns a negative value because the input was bad.
nuclear@0 207 int Log::FormatLog(char* buffer, size_t bufferSize, LogMessageType messageType,
nuclear@0 208 const char* fmt, va_list argList)
nuclear@0 209 {
nuclear@0 210 OVR_ASSERT(buffer && (bufferSize >= 10)); // Need to be able to at least print "Assert: \n" to it.
nuclear@0 211 if(!buffer || (bufferSize < 10))
nuclear@0 212 return -1;
nuclear@0 213
nuclear@0 214 int addNewline = 1;
nuclear@0 215 int prefixLength = 0;
nuclear@0 216
nuclear@0 217 switch(messageType)
nuclear@0 218 {
nuclear@0 219 case Log_Error: OVR_strcpy(buffer, bufferSize, "Error: "); prefixLength = 7; break;
nuclear@0 220 case Log_Debug: OVR_strcpy(buffer, bufferSize, "Debug: "); prefixLength = 7; break;
nuclear@0 221 case Log_Assert: OVR_strcpy(buffer, bufferSize, "Assert: "); prefixLength = 8; break;
nuclear@0 222 case Log_Text: buffer[0] = 0; addNewline = 0; break;
nuclear@0 223 case Log_DebugText: buffer[0] = 0; addNewline = 0; break;
nuclear@0 224 default: buffer[0] = 0; addNewline = 0; break;
nuclear@0 225 }
nuclear@0 226
nuclear@0 227 char* buffer2 = buffer + prefixLength;
nuclear@0 228 size_t size2 = bufferSize - (size_t)prefixLength;
nuclear@0 229 int messageLength = OVR_vsnprintf(buffer2, size2, fmt, argList);
nuclear@0 230
nuclear@0 231 if (addNewline)
nuclear@0 232 {
nuclear@0 233 if (messageLength < 0) // If there was a format error...
nuclear@0 234 {
nuclear@0 235 // To consider: append <format error> to the buffer here.
nuclear@0 236 buffer2[0] = '\n'; // We are guaranteed to have capacity for this.
nuclear@0 237 buffer2[1] = '\0';
nuclear@0 238 }
nuclear@0 239 else
nuclear@0 240 {
nuclear@0 241 // If the printed string used all of the capacity or required more than the capacity,
nuclear@0 242 // Chop the output by one character so we can append the \n safely.
nuclear@0 243 int messageEnd = (messageLength >= (int)(size2 - 1)) ? (int)(size2 - 2) : messageLength;
nuclear@0 244 buffer2[messageEnd + 0] = '\n';
nuclear@0 245 buffer2[messageEnd + 1] = '\0';
nuclear@0 246 }
nuclear@0 247 }
nuclear@0 248
nuclear@0 249 if (messageLength >= 0) // If the format was OK...
nuclear@0 250 return prefixLength + messageLength + addNewline; // Return the required strlen of buffer.
nuclear@0 251
nuclear@0 252 return messageLength; // Else we cannot know what the required strlen is and return the error to the caller.
nuclear@0 253 }
nuclear@0 254
nuclear@0 255 void Log::DefaultLogOutput(const char* formattedText, LogMessageType messageType, int bufferSize)
nuclear@0 256 {
nuclear@0 257 bool debug = IsDebugMessage(messageType);
nuclear@0 258 OVR_UNUSED(bufferSize);
nuclear@0 259
nuclear@0 260 #if defined(OVR_OS_WIN32)
nuclear@0 261 // Under Win32, output regular messages to console if it exists; debug window otherwise.
nuclear@0 262 static DWORD dummyMode;
nuclear@0 263 static bool hasConsole = (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) &&
nuclear@0 264 (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummyMode));
nuclear@0 265
nuclear@0 266 if (!hasConsole || debug)
nuclear@0 267 {
nuclear@0 268 ::OutputDebugStringA(formattedText);
nuclear@0 269 }
nuclear@0 270
nuclear@0 271 fputs(formattedText, stdout);
nuclear@0 272
nuclear@0 273 #elif defined(OVR_OS_MS) // Any other Microsoft OSs
nuclear@0 274
nuclear@0 275 ::OutputDebugStringA(formattedText);
nuclear@0 276
nuclear@0 277 #elif defined(OVR_OS_ANDROID)
nuclear@0 278 // To do: use bufferSize to deal with the case that Android has a limited output length.
nuclear@0 279 __android_log_write(ANDROID_LOG_INFO, "OVR", formattedText);
nuclear@0 280
nuclear@0 281 #else
nuclear@0 282 fputs(formattedText, stdout);
nuclear@0 283
nuclear@0 284 #endif
nuclear@0 285
nuclear@0 286 if (messageType == Log_Error)
nuclear@0 287 {
nuclear@0 288 #if defined(OVR_OS_WIN32)
nuclear@0 289 if (!ReportEventA(hEventSource, EVENTLOG_ERROR_TYPE, 0, 0, NULL, 1, 0, &formattedText, NULL))
nuclear@0 290 {
nuclear@0 291 OVR_ASSERT(false);
nuclear@0 292 }
nuclear@0 293 #elif defined(OVR_OS_MS) // Any other Microsoft OSs
nuclear@0 294 // TBD
nuclear@0 295 #elif defined(OVR_OS_ANDROID)
nuclear@0 296 // TBD
nuclear@0 297 #elif defined(OVR_OS_MAC) || defined(OVR_OS_LINUX)
nuclear@0 298 syslog(LOG_ERR, "%s", formattedText);
nuclear@0 299 #else
nuclear@0 300 // TBD
nuclear@0 301 #endif
nuclear@0 302 }
nuclear@0 303
nuclear@0 304 // Just in case.
nuclear@0 305 OVR_UNUSED2(formattedText, debug);
nuclear@0 306 }
nuclear@0 307
nuclear@0 308
nuclear@0 309 //static
nuclear@0 310 void Log::SetGlobalLog(Log *log)
nuclear@0 311 {
nuclear@0 312 OVR_GlobalLog = log;
nuclear@0 313 }
nuclear@0 314 //static
nuclear@0 315 Log* Log::GetGlobalLog()
nuclear@0 316 {
nuclear@0 317 // No global log by default?
nuclear@0 318 // if (!OVR_GlobalLog)
nuclear@0 319 // OVR_GlobalLog = GetDefaultLog();
nuclear@0 320 return OVR_GlobalLog;
nuclear@0 321 }
nuclear@0 322
nuclear@0 323 //static
nuclear@0 324 Log* Log::GetDefaultLog()
nuclear@0 325 {
nuclear@0 326 // Create default log pointer statically so that it can be used
nuclear@0 327 // even during startup.
nuclear@0 328 static Log defaultLog;
nuclear@0 329 return &defaultLog;
nuclear@0 330 }
nuclear@0 331
nuclear@0 332
nuclear@0 333 //-----------------------------------------------------------------------------------
nuclear@0 334 // ***** Global Logging functions
nuclear@0 335
nuclear@0 336 #if !defined(OVR_CC_MSVC)
nuclear@0 337 // The reason for va_copy is because you can't use va_start twice on Linux
nuclear@0 338 #define OVR_LOG_FUNCTION_IMPL(Name) \
nuclear@0 339 void Log##Name(const char* fmt, ...) \
nuclear@0 340 { \
nuclear@0 341 if (OVR_GlobalLog) \
nuclear@0 342 { \
nuclear@0 343 va_list argList1; \
nuclear@0 344 va_start(argList1, fmt); \
nuclear@0 345 va_list argList2; \
nuclear@0 346 va_copy(argList2, argList1); \
nuclear@0 347 OVR_GlobalLog->LogMessageVargInt(Log_##Name, fmt, argList2); \
nuclear@0 348 va_end(argList2); \
nuclear@0 349 OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList1); \
nuclear@0 350 va_end(argList1); \
nuclear@0 351 } \
nuclear@0 352 }
nuclear@0 353 #else
nuclear@0 354 #define OVR_LOG_FUNCTION_IMPL(Name) \
nuclear@0 355 void Log##Name(const char* fmt, ...) \
nuclear@0 356 { \
nuclear@0 357 if (OVR_GlobalLog) \
nuclear@0 358 { \
nuclear@0 359 va_list argList1; \
nuclear@0 360 va_start(argList1, fmt); \
nuclear@0 361 OVR_GlobalLog->LogMessageVargInt(Log_##Name, fmt, argList1); \
nuclear@0 362 OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList1); \
nuclear@0 363 va_end(argList1); \
nuclear@0 364 } \
nuclear@0 365 }
nuclear@0 366 #endif // #if !defined(OVR_OS_WIN32)
nuclear@0 367
nuclear@0 368 OVR_LOG_FUNCTION_IMPL(Text)
nuclear@0 369 OVR_LOG_FUNCTION_IMPL(Error)
nuclear@0 370
nuclear@0 371 #ifdef OVR_BUILD_DEBUG
nuclear@0 372 OVR_LOG_FUNCTION_IMPL(DebugText)
nuclear@0 373 OVR_LOG_FUNCTION_IMPL(Debug)
nuclear@0 374 OVR_LOG_FUNCTION_IMPL(Assert)
nuclear@0 375 #endif
nuclear@0 376
nuclear@0 377
nuclear@0 378
nuclear@0 379 // Assertion handler support
nuclear@0 380 // To consider: Move this to an OVR_Types.cpp or OVR_Assert.cpp source file.
nuclear@0 381
nuclear@0 382 static OVRAssertionHandler sOVRAssertionHandler = OVR::DefaultAssertionHandler;
nuclear@0 383 static intptr_t sOVRAssertionHandlerUserParameter = 0;
nuclear@0 384
nuclear@0 385 OVRAssertionHandler GetAssertionHandler(intptr_t* userParameter)
nuclear@0 386 {
nuclear@0 387 if(userParameter)
nuclear@0 388 *userParameter = sOVRAssertionHandlerUserParameter;
nuclear@0 389 return sOVRAssertionHandler;
nuclear@0 390 }
nuclear@0 391
nuclear@0 392 void SetAssertionHandler(OVRAssertionHandler assertionHandler, intptr_t userParameter)
nuclear@0 393 {
nuclear@0 394 sOVRAssertionHandler = assertionHandler;
nuclear@0 395 sOVRAssertionHandlerUserParameter = userParameter;
nuclear@0 396 }
nuclear@0 397
nuclear@0 398 intptr_t DefaultAssertionHandler(intptr_t /*userParameter*/, const char* title, const char* message)
nuclear@0 399 {
nuclear@0 400 if(OVRIsDebuggerPresent())
nuclear@0 401 {
nuclear@0 402 OVR_DEBUG_BREAK;
nuclear@0 403 }
nuclear@0 404 else
nuclear@0 405 {
nuclear@0 406 #if defined(OVR_BUILD_DEBUG)
nuclear@0 407 // Print a stack trace of all threads.
nuclear@0 408 OVR::String s;
nuclear@0 409 OVR::String threadListOutput;
nuclear@0 410 static OVR::SymbolLookup symbolLookup;
nuclear@0 411
nuclear@0 412 s = "Failure: ";
nuclear@0 413 s += message;
nuclear@0 414
nuclear@0 415 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 416 {
nuclear@0 417 s += "\r\n\r\n";
nuclear@0 418 s += threadListOutput;
nuclear@0 419 }
nuclear@0 420
nuclear@0 421 OVR::Util::DisplayMessageBox(title, s.ToCStr());
nuclear@0 422 #else
nuclear@0 423 OVR::Util::DisplayMessageBox(title, message);
nuclear@0 424 #endif
nuclear@0 425 }
nuclear@0 426
nuclear@0 427 return 0;
nuclear@0 428 }
nuclear@0 429
nuclear@0 430
nuclear@0 431
nuclear@0 432 } // OVR