summaryrefslogtreecommitdiffhomepage
path: root/ovr_sdk_win_23.0.0/Logging
diff options
context:
space:
mode:
Diffstat (limited to 'ovr_sdk_win_23.0.0/Logging')
-rw-r--r--ovr_sdk_win_23.0.0/Logging/Logging/Logging-fwd.h58
-rw-r--r--ovr_sdk_win_23.0.0/Logging/Logging/Logging_Library.h1262
-rw-r--r--ovr_sdk_win_23.0.0/Logging/Logging/Logging_OutputPlugins.h105
-rw-r--r--ovr_sdk_win_23.0.0/Logging/Logging/Logging_Tools.h230
-rw-r--r--ovr_sdk_win_23.0.0/Logging/PCSDK_Logging_dependency.props21
-rw-r--r--ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging.vcxproj170
-rw-r--r--ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging.vcxproj.filters40
-rw-r--r--ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging_internal.props8
-rw-r--r--ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging.vcxproj171
-rw-r--r--ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging.vcxproj.filters43
-rw-r--r--ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging_internal.props8
-rw-r--r--ovr_sdk_win_23.0.0/Logging/src/Logging_Library.cpp1537
-rw-r--r--ovr_sdk_win_23.0.0/Logging/src/Logging_OutputPlugins.cpp289
-rw-r--r--ovr_sdk_win_23.0.0/Logging/src/Logging_Tools.cpp255
14 files changed, 4197 insertions, 0 deletions
diff --git a/ovr_sdk_win_23.0.0/Logging/Logging/Logging-fwd.h b/ovr_sdk_win_23.0.0/Logging/Logging/Logging-fwd.h
new file mode 100644
index 0000000..26537f0
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/Logging/Logging-fwd.h
@@ -0,0 +1,58 @@
+/************************************************************************************
+
+Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
+
+Licensed under the Oculus Master SDK License Version 1.0 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+https://developer.oculus.com/licenses/oculusmastersdk-1.0
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#pragma once
+
+#include <stdint.h>
+
+namespace ovrlog {
+
+struct LogStringBuffer;
+class Channel;
+class Configurator;
+
+typedef uint32_t Log_Level_t;
+typedef uint32_t Write_Option_t;
+
+enum class Level : Log_Level_t;
+
+class Name;
+
+struct LogStringBuffer;
+
+template <typename T>
+void LogStringize(LogStringBuffer& buffer, const T& thing);
+
+class OutputPlugin;
+
+struct ChannelNode;
+
+class OutputWorker;
+
+class ErrorSilencer;
+
+class Channel;
+
+class ConfiguratorPlugin;
+
+class Configurator;
+
+} // namespace ovrlog
diff --git a/ovr_sdk_win_23.0.0/Logging/Logging/Logging_Library.h b/ovr_sdk_win_23.0.0/Logging/Logging/Logging_Library.h
new file mode 100644
index 0000000..594d8f3
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/Logging/Logging_Library.h
@@ -0,0 +1,1262 @@
+/************************************************************************************
+
+Filename : Logging_Library.h
+Content : Logging system
+Created : Oct 26, 2015
+Authors : Chris Taylor
+
+Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
+
+Licensed under the Oculus Master SDK License Version 1.0 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+https://developer.oculus.com/licenses/oculusmastersdk-1.0
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#pragma once
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4530) // C++ exception handler used, but unwind semantics are not enabled
+#endif // _MSC_VER
+
+#include <time.h>
+#include <array>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <set>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#endif // _MSC_VER
+
+#include "Logging/Logging-fwd.h"
+#include "Logging/Logging_Tools.h"
+
+#if !defined(_WIN32)
+#include <chrono>
+#include <condition_variable>
+#include <thread>
+#endif // !defined(_WIN32)
+
+namespace ovrlog {
+
+struct LogStringBuffer;
+class Channel;
+class Configurator;
+
+typedef uint32_t Log_Level_t;
+typedef uint32_t Write_Option_t;
+
+//-----------------------------------------------------------------------------
+// Log Level
+//
+// Log message priority is indicated by its level. The level can inform how
+// prominently it is displayed on the console window or whether or not a
+// message is displayed at all.
+
+enum class Level : Log_Level_t {
+ // No logging occurs.
+ Disabled,
+
+ // Trace message. This is a message that can potentially happen once per
+ // camera/HMD frame and are probably being reviewed after they are recorded
+ // since they will scroll by too fast otherwise.
+ Trace,
+
+ // Debug message. This is a verbose log level that can be selectively
+ // turned on by the user in the case of perceived problems to help
+ // root-cause the problems. This log level is intended to be used for
+ // messages that happen less often than once per camera/HMD frame,
+ // such that they can be human readable.
+ Debug,
+
+ // Info messages, which should be the default message type for infrequent
+ // messages used during subsystem initialization and/or shutdown. This
+ // log level is fairly visible so should be used sparingly. Expect users to
+ // have these turned on, so avoid printing anything here that would obscure
+ // Warning and Error messages.
+ Info,
+
+ // Warning message, which is also displayed almost everywhere. For most
+ // purposes it is as visible as an Error message, so it should also be used
+ // very selectively. The main difference from Error level is informational
+ // as it is just as visible.
+ Warning,
+
+ // Highest level of logging. If any logging is going on it will include this
+ // message. For this reason, please avoid using the Error level unless the
+ // message should be displayed absolutely everywhere.
+ Error,
+
+ // Number of error levels
+ Count, // Used to static assert where updates must happen in code.
+};
+
+//-----------------------------------------------------------------------------
+// LOGGING_LOC
+//
+// C++11 Trait that can be used to insert the file and line number into a log
+// message. Often times it is a good idea to put these at the start of a log
+// message so that the user can click on them to have the code editor IDE jump
+// to that line of code.
+
+// Log Line of Code FileLineInfo object.
+#if defined(LOGGING_DEBUG)
+#define LOGGING_FILE_LINE_STRING_ __FILE__ "(" LOGGING_STRINGIZE(__LINE__) ")"
+#define LOGGING_LOC LOGGING_FILE_LINE_STRING_
+#else
+#define LOGGING_LOC "(no LOC)"
+#endif
+
+//-----------------------------------------------------------------------------
+// LOGGING_LOCATION_HASH
+//
+// Provides a compile-time constexpr hash of the file/line combination, which is
+// likely unique across all locations within a source code base. The primary weakness
+// of this is a compiler environment in which __FILE__ represents just a file name
+// and not a path, and there are two files with the same name and this is used on
+// the same line within those files.
+//
+// Example usage:
+// printf("%llu", LOGGING_LOCATION_HASH());
+//
+#if defined(_WIN64) || defined(__x64__)
+#define LOGGING_FILE_LINE_FNV_HASH_PRIME 1099511628211ULL
+#define LOGGING_FILE_LINE_FNV_HASH_OFFSET 14695981039346656037ULL
+#else
+#define LOGGING_FILE_LINE_FNV_HASH_PRIME 16777619
+#define LOGGING_FILE_LINE_FNV_HASH_OFFSET 2166136261
+#endif
+
+// Implementation function for calculating a hash that's ~unique for a given file/line combination.
+constexpr uint64_t LoggingFileLineHash(const char* const path, uint32_t index, uint64_t hash) {
+ return path[index]
+ ? ovrlog::LoggingFileLineHash(
+ path, index + 1, (((uint8_t)path[index] ^ hash) * LOGGING_FILE_LINE_FNV_HASH_PRIME))
+ : hash;
+}
+
+#ifdef _MSC_VER
+#define LOGGING_LOCATION_HASH() \
+ __pragma(warning(push)) __pragma(warning(disable : 4307)) ovrlog::LoggingFileLineHash( \
+ __FILE__, \
+ 0, \
+ (uint64_t)( \
+ (__LINE__ ^ LOGGING_FILE_LINE_FNV_HASH_OFFSET) * LOGGING_FILE_LINE_FNV_HASH_PRIME)) \
+ __pragma(warning(pop))
+#else
+#define LOGGING_LOCATION_HASH() \
+ ovrlog::LoggingFileLineHash( \
+ __FILE__, \
+ 0, \
+ (uint64_t)( \
+ (__LINE__ ^ LOGGING_FILE_LINE_FNV_HASH_OFFSET) * LOGGING_FILE_LINE_FNV_HASH_PRIME))
+#endif
+
+//-----------------------------------------------------------------------------
+// LogTime
+//
+// Our time type
+//
+#if defined(WIN32)
+typedef SYSTEMTIME LogTime;
+#else
+typedef std::chrono::system_clock::time_point LogTime;
+#endif
+
+// Gets the current time in LogTime format.
+// May be called from multiple threads concurrently.
+LogTime GetCurrentLogTime();
+
+//-----------------------------------------------------------------------------
+// Name
+//
+// A fixed length name which avoids allocation and is safe to share across DLL
+// boundaries.
+
+class Name {
+ public:
+ // Longest string not including '\0' termination
+ static const size_t MaxLength = 63;
+
+ Name(const char* init) {
+ // Maximum portability vs. ::strncpy_s
+ for (size_t i = 0; i < MaxLength; ++i) {
+ if ((name[i] = init[i]) == '\0')
+ return;
+ }
+ name[MaxLength] = '\0';
+ }
+ Name(std::string init) : Name(init.c_str()) {}
+ const char* Get() const {
+ return name.data();
+ }
+ int cmp(const Name& rhs) {
+ // Maximum portability vs. std::strncmp
+ int diff = 0;
+ for (size_t i = 0; i < MaxLength; ++i) {
+ // this < rhs => (-), this > rhs => (+)
+ diff = int(name[i]) - int(rhs.name[i]);
+ // If strings are equal it is sufficent to terminate on either '\0'
+ if ((diff != 0) || (name[i] == '\0')) // => (rhs.name[i] == '\0')
+ break;
+ }
+ return diff;
+ }
+ bool operator==(const Name& rhs) {
+ return cmp(rhs) == 0;
+ }
+ bool operator!=(const Name& rhs) {
+ return cmp(rhs) != 0;
+ }
+
+ private:
+ // '\0'-terminated
+ std::array<char, MaxLength + 1> name;
+};
+
+//-----------------------------------------------------------------------------
+// LogStringBuffer
+//
+// Thread-local buffer for constructing a log message.
+
+struct LogStringBuffer {
+ const Name SubsystemName;
+ const Level MessageLogLevel;
+
+ // Buffer containing string as it is constructed
+ std::stringstream Stream;
+ // TBD: We can optimize this better than std::string
+ // TBD: We can remember the last log string size to avoid extra allocations.
+
+ // Flag indicating that the message is being relogged.
+ // This is useful to prevent double-logging messages.
+ bool Relogged;
+
+ // Ctor
+ LogStringBuffer(const char* subsystem, Level level)
+ : SubsystemName(subsystem), MessageLogLevel(level), Stream(), Relogged(false) {}
+};
+
+//-----------------------------------------------------------------------------
+// LogStringize Override
+//
+// This is the function that user code can override to control how special types
+// are serialized into the log messages.
+
+// Delete logging a shared_ptr
+template <typename T>
+LOGGING_INLINE void LogStringize(LogStringBuffer& buffer, const std::shared_ptr<T>& thing) {
+ (void)buffer;
+ (void)thing;
+#if !defined(__clang__)
+ static_assert(
+ false, "Don't log a shared_ptr, log *ptr (or ptr.get() for the raw pointer value).");
+#endif
+}
+
+// Delete logging a unique_ptr
+template <typename T, typename Deleter>
+LOGGING_INLINE void LogStringize(
+ LogStringBuffer& buffer,
+ const std::unique_ptr<T, Deleter>& thing) {
+ (void)buffer;
+ (void)thing;
+#if !defined(__clang__)
+ static_assert(
+ false, "Don't log a unique_ptr, log *ptr (or ptr.get() for the raw pointer value).");
+#endif
+}
+
+template <typename T>
+LOGGING_INLINE void LogStringize(LogStringBuffer& buffer, const T& first) {
+ buffer.Stream << first;
+}
+
+// Overrides for various types we want to handle specially:
+
+template <>
+LOGGING_INLINE void LogStringize(LogStringBuffer& buffer, const bool& first) {
+ buffer.Stream << (first ? "true" : "false");
+}
+
+template <>
+void LogStringize(LogStringBuffer& buffer, wchar_t const* const& first);
+
+template <int N>
+LOGGING_INLINE void LogStringize(LogStringBuffer& buffer, const wchar_t (&first)[N]) {
+ const wchar_t* str = first;
+ LogStringize(buffer, str);
+}
+
+template <>
+LOGGING_INLINE void LogStringize(LogStringBuffer& buffer, const std::wstring& first) {
+ const wchar_t* str = first.c_str();
+ LogStringize(buffer, str);
+}
+
+//-----------------------------------------------------------------------------
+// Log Output Worker Thread
+//
+// Worker thread that produces the output.
+// Call AddPlugin() to register an output plugin.
+
+// User-defined output plugin
+class OutputPlugin {
+ public:
+ virtual ~OutputPlugin() {}
+
+ // Return a unique string naming this output plugin.
+ virtual const char* GetUniquePluginName() = 0;
+
+ // Write data to output.
+ virtual void
+ Write(Level level, const char* subsystem, const char* header, const char* utf8msg) = 0;
+};
+
+//-----------------------------------------------------------------------------
+// Used by channel to specify how to write
+enum class WriteOption : Write_Option_t {
+ // Default log write. If the output message queue size limit is reached, then the write
+ // is discarded. This is intended to be an unusual case that occurs only if the message
+ // generation is very rapid compared to queue processing.
+ Default,
+
+ // If the Write would require adding an entry that goes over the max queue size, usually we
+ // drop the write. But this flag indicates to exceed the queue size, which can user more memory
+ // than expected and possibly exhaust memory if it is used very much.
+ DangerouslyIgnoreQueueLimit
+};
+
+//--------------------------------------------------------------------------------------------------
+// RepeatedMessageManager
+//
+// We have a problem in which sometimes a subsystem will spam the log with essentially the
+// same message repeatedly.
+//
+// This class handles the ownership of repeated messages, which it aggregates and counts.
+// The logging system can use an instance of this class to aggregate redundant messages.
+//
+// Example usage:
+// RepeatedMessageManager gRepeatedMessageManager; // Some kind of singleton.
+//
+// void OutputWorker::Write(const char* subsystemName, Level messageLogLevel,
+// const char* stream, bool relogged, WriteOption option)
+// {
+// if (gRepeatedMessageManager.HandleMessage(subsystemName, messageLogLevel, stream) ==
+// HandleMessage::Aggregated)
+// return;
+// }
+//
+// void OutputWorker::ProcessQueuedMessages() {
+// while(...) {
+// [...]
+// gRepeatedMessageManager.Poll();
+// }
+// }
+//
+// Design
+// In our HandleMessage function:
+// For every message we receive, we look at the last N messages received and see if this message
+// appears to be a duplicate of any of the previous ones. We make that decision by first checking
+// if the text of the message matches an entry in a hash table of known currently repeating
+// messages. If not present then we make that decision based on the text of the message being
+// similar to message text of the last N received messages and being received within some period
+// of time since the last similar message. It it appears to be a repeat, then we add it to the
+// hash table.
+//
+// In our Poll function:
+// Each message in the hash table has a repeat count, which is the number of times
+// the message has been previously seen since it was last printed. When that count gets above
+// some value, we print the message and set the count to zero for further accumulation.
+// Messages that have been in the hash table without having been repeated recently are printed
+// a final time and removed from the hash table. Since this final printing occurs only after a
+// timeout, it will be occurring after the last message was received and thus be delayed by some
+// amount of time.
+//
+// Performance considerations:
+// The list of N previously received messages represents the biggest potential performance cost
+// to this functionality. We need to check the currently incoming message against each of the
+// last N messages. That check needs to be fast, and the maintenance of the data structure
+// which holds that data (e.g. container heap usage) needs to be fast. One fairly fast solution
+// is to maintain the last N messages as a hash table of string hashes, and so when a new message
+// is received, we do an O(1) hash table lookup. But we need to continuously prune the hash
+// table, which is an O(n) operation unless we have some kind of priority_queue alongside the
+// hash table which tells us how to quickly find the oldest entries in the hash table. A simpler
+// solution is to prune the hash table only when it gets beyond N*2 messages. So we only do
+// this O(n) pass periodically.
+//
+class RepeatedMessageManager {
+ public:
+ RepeatedMessageManager();
+ ~RepeatedMessageManager() = default;
+
+ enum class HandleResult {
+ Aggregated, // The message was consumed for aggregation (it was a repeat). Don't print it.
+ Passed // The message didn't appear to be a repeat and should be printed.
+ };
+
+ // If the message appears to be a repeat of a recent message then we add it to our repeated
+ // message database and return HandleResult::Aggregated. If HandleMessage returns Aggreggated
+ // then the caller should not write the message to the stream.
+ HandleResult HandleMessage(const char* subsystemName, Level messageLogLevel, const char* stream);
+
+ // This function should be called at least once every maxDeferrableDetectionTimeMs.
+ // It causes us to look at the list of repeated messages and possibly print an aggregated
+ // form of any of them that has expired. For example, if the message "hello world" was repeated
+ // 100 times in the last five seconds, we print a single aggregate message such as:
+ // "[repeated 100 times] hello world". We also do housekeeping on the recent message list.
+ // The outputWorker is where any deferred aggregated messages should be written.
+ void Poll(OutputWorker* outputWorker);
+
+ // Causes messages with the given prefix to not be subject to aggregated deferral.
+ // This is useful in case you want a specific message to be repeated, regardless of the
+ // frequency or apparent redundancy it may have. The MessagePrefix needs to be at least
+ // messagePrefixLength in length, so that it can be matched internally accurately.
+ // Strings are case-sensitive.
+ void AddRepeatedMessageException(const char* messagePrefix);
+
+ // Removes messages previously added with AddRepeatedMessageException.
+ // Strings are case-sensitive.
+ void RemoveRepeatedMessageException(const char* messagePrefix);
+
+ // Causes messages with the given subsystemName to not be subject to aggregated deferral.
+ // This is useful in case you want a specific subsystem's message to be repeated,
+ // regardless of the frequency or apparent redundancy it may have.
+ // Strings are case-sensitive.
+ void AddRepeatedMessageSubsystemException(const char* subsystemName);
+
+ // Removes messages previously added with AddRepeatedMessageSubsystemException.
+ void RemoveRepeatedMessageSubsytemException(const char* subsystemName);
+
+ protected:
+ // Keep the last <recentMessageCount> messages to see if any of them are repeated.
+ static const uint32_t recentMessageCount = 40;
+
+ // The number of leading characters in a message which we consider for comparisons.
+ static const uint32_t messagePrefixLength = 36;
+
+ // If a message is a repeat of a previous message, we still print it in the log a few times
+ // before we start silencing it and holding it for later aggregation.
+ static const uint32_t printedRepeatCount = 8;
+
+ // Max number of messages that are held for deferral before we print an aggregate message.
+ static const uint32_t maxDeferredMessages = 40;
+
+ // If a repeating message wasn't encountered in the last <maxDeferrableDetectionTimeMs> time,
+ // then we don't consider it repeating any more.
+ static const uint32_t maxDeferrableDetectionTimeMs = 1000;
+
+ // Every <purgeDeferredMessageTimeMs> time period, we do a sweep of the repeated message map to
+ // prune any old entries that don't look like they are repeating any more.
+ static const uint32_t purgeDeferredMessageTimeMs = 100;
+
+ // String hash used to identify similar messages.
+ typedef uint32_t PrefixHashType;
+
+ // For our uses we don't want LogTime, which is a calendar time that's hard and slow to work
+ // with. Instead we want to compare milliseconds in an absolute way. So we define a type that
+ // is absolute milliseconds which can derived from a LogTime.
+ typedef int64_t LogTimeMs;
+
+ // Stores a recently generated message. It doesn't store the entire message string,
+ // as we care only about the first N characters of messages. We store the last M messages
+ // ever received in a list of RecentMessages. That way we can tell if there was a recent repeat.
+ // To consider: we don't currently consider the subsystem name as part of the uniqueness of the
+ // string. We may want or need to do this, but it's very unlikely two strings would be the same
+ // while coming from separate subsystems.
+ struct RecentMessage {
+ LogTimeMs timeMs; // Time that the message was generated.
+ // Don't need to store the message or message prefix itself, though it may
+ // help debugging.
+ };
+ typedef std::unordered_map<PrefixHashType, RecentMessage> RecentMessageMapType;
+
+ // Represents a message which has been identified as being repeated. This struct allows us to
+ // know how many times the message was repeated, when it was first seen, etc.
+ struct RepeatedMessage {
+ std::string subsystemName; // To consider: Convert to char [16]
+ Level messageLogLevel; // log level, e.g. Level::Trace.
+ std::string stream; // The first message of the repeated set.
+ LogTimeMs initialTimeMs; // Time the message was first seen.
+ LogTimeMs lastTimeMs; // Time the most recent repeat of the message was seen.
+ uint32_t aggregatedCount; // Number of times this message was aggregated for later.
+ uint32_t printedCount; // Number of times this message has been 'printed';
+ // aggregate printing counts multiple times towards this.
+ RepeatedMessage()
+ : subsystemName(),
+ messageLogLevel(),
+ stream(),
+ initialTimeMs(),
+ lastTimeMs(),
+ aggregatedCount(),
+ printedCount() {}
+ RepeatedMessage(
+ const char* subsystemName_,
+ Level messageLogLevel_,
+ const char* stream_,
+ LogTimeMs initialTimeMs_,
+ LogTimeMs lastTimeMs_,
+ uint32_t aggregatedCount_)
+ : subsystemName(subsystemName_),
+ messageLogLevel(messageLogLevel_),
+ stream(stream_),
+ initialTimeMs(initialTimeMs_),
+ lastTimeMs(lastTimeMs_),
+ aggregatedCount(aggregatedCount_),
+ printedCount() {}
+ };
+ typedef std::unordered_map<PrefixHashType, RepeatedMessage> RepeatedMessageMapType;
+
+ // Prints a message that's an aggregate deferred printing.
+ void PrintDeferredAggregateMessage(OutputWorker* outputWorker, RepeatedMessage& repeatedMessage);
+
+ // Calculates a hash for the given string for at most <messagePrefixLength> characters.
+ static PrefixHashType GetHash(const char* str);
+
+ // Gets the current LogTime in LogTimeMs.
+ static LogTimeMs GetCurrentLogMillisecondTime();
+
+ // Converts LogTime to LogTimeMs.
+ static LogTimeMs LogTimeToMillisecondTime(const LogTime& logTime);
+
+ // Returns end - begin, possibly with some fixup.
+ static int64_t GetLogMillisecondTimeDifference(LogTimeMs begin, LogTimeMs end);
+
+ protected:
+ // Mutex to which covers all our member data.
+ std::recursive_mutex Mutex;
+
+ // Used to protect against re-entrancy. Our Poll function calls external code, and we want to
+ // prevent there being a problem if that external code unexpectedly calls us back.
+ bool BusyInWrite;
+
+ // To do: Get a fixed-size node allocator working with this container for low overhead.
+ RecentMessageMapType RecentMessageMap;
+
+ // To consider: Get a fixed-size node allocator working with this container for low overhead.
+ RepeatedMessageMapType RepeatedMessageMap;
+
+ // We don't need to store the string, just the string hash.
+ std::unordered_set<PrefixHashType> RepeatedMessageExceptionSet;
+
+ // We don't need to store the string, just the string hash.
+ std::unordered_set<PrefixHashType> RepeatedMessageSubsystemExceptionSet;
+};
+
+// Iterate through the list of channels before the CRT has initialized
+#pragma pack(push, 1)
+struct ChannelNode {
+ const char* SubsystemName; // This is always a pointer to a Channel's SubsystemName.Get()
+ Log_Level_t* Level;
+ bool* UserOverrodeMinimumOutputLevel;
+ ChannelNode* Next;
+};
+#pragma pack(pop)
+
+// Export the function to access OutputWorker::Write(). This is used by the Channel class
+// to allow writing with OutputWorker possibly in a separate module.
+void OutputWorkerOutputFunctionC(
+ const char* subsystemName,
+ Log_Level_t messageLogLevel,
+ const char* stream,
+ bool relogged,
+ Write_Option_t option);
+
+typedef void (*OutputWorkerOutputFunctionType)(
+ const char* subsystemName,
+ Log_Level_t messageLogLevel,
+ const char* stream,
+ bool relogged,
+ Write_Option_t option);
+
+// Shutdown the logging system and release memory
+void ShutdownLogging();
+
+// Restart the logging system
+void RestartLogging();
+
+// Log Output Worker Thread
+class OutputWorker {
+ OutputWorker(); // Use GetInstance() to get the singleton instance.
+
+ public:
+ static OutputWorker* GetInstance();
+
+ ~OutputWorker();
+
+ void InstallDefaultOutputPlugins();
+
+ // Start/stop logging output (started automatically)
+ void Start();
+ void Stop();
+
+ // Blocks until all log messages before this function call are completed.
+ void Flush();
+
+ // Write a log buffer to the output
+ // relogged indicates that the message is being relogged, which is useful to prevent
+ // double-logging messages.
+
+ void Write(
+ const char* subsystemName,
+ Level messageLogLevel,
+ const char* stream,
+ bool relogged,
+ WriteOption option);
+
+ // Plugin management
+ void AddPlugin(std::shared_ptr<OutputPlugin> plugin);
+ void RemovePlugin(std::shared_ptr<OutputPlugin> plugin);
+ std::shared_ptr<OutputPlugin> GetPlugin(const char* const pluginName);
+
+ // Disable all output
+ void DisableAllPlugins();
+
+ // Get the lock used for the channels.
+ Lock* GetChannelsLock();
+
+ // Add an exception to the RepeatedMessageManager
+ void AddRepeatedMessageSubsystemException(const char* subsystemName);
+
+ // Removes messages previously added with AddRepeatedMessageException
+ void RemoveRepeatedMessageSubsystemException(const char* subsystemName);
+
+ // Sets the set of channels that can write to the given output.
+ // By default, all outputs are written to by all channels. But if you provide a set of
+ // channels with this function, then the output is written to only by the given channels.
+ // A typical use case of this is to associate a given channel 1:1 a given output,
+ // by combining usage of this function with the SetChannelOutputPlugins function.
+ //
+ // Example usage, which sets a 1:1 correspondence between a TimingData channel and a
+ // single log file for its output:
+ // ow->SetOutputPluginChannels("TimingDataLog", {"TimingData"});
+ // ow->SetChannelOutputPlugins("TimingData", {"TimingDataLog"});
+ // or alternatively:
+ // ow->SetChannelSingleOutput("TimingData", "TimingDataLog");
+ //
+ void SetOutputPluginChannels(
+ const char* outputPluginName,
+ const std::vector<std::string>& channelNames);
+
+ // Sets the set of outputs that a given channel can write to.
+ void SetChannelOutputPlugins(
+ const char* channelName,
+ const std::vector<std::string>& outputPluginNames);
+
+ // This combines usage of SetOutputPluginChannels and SetChannelOutputPlugins to associate a
+ // channel to exclusively the given output. No other channels will write to the given output,
+ // and the channel will write to no other outputs.
+ void SetChannelSingleOutput(const char* channelName, const char* outputPluginName);
+
+ private:
+ // Is the logger running in a debugger?
+ bool IsInDebugger;
+
+ bool DefaultPluginsDisabled;
+
+ // It's here so we know it is valid in the scope of ~OutputWorker
+ Lock ChannelsLock;
+
+ // Plugins
+ Lock PluginsLock;
+ std::set<std::shared_ptr<OutputPlugin>> Plugins;
+
+ // Output -> channel filtering
+ // This is a case-sensitive map of output name to a set of channel names.
+ // Normally a given channel write is sent to all output plugins. But we support being explicit
+ // about the output than that. Behavior:
+ // If an output isn't present in this map then its writable by all channels.
+ // If an output is present, then it is writable only by the channels specified by the map.
+ // Threaded access to this is protected by PluginsLock.
+ std::unordered_map<std::string, std::unordered_set<std::string>> OutputPluginChannelFiltering;
+
+ // Channel -> output filtering
+ // This is a case-sensitive map of channel name to a set of output plugins.
+ // Normally a given channel write is sent to all output plugins. But we support being explicit
+ // about the output than that. Behavior:
+ // If a channel isn't present in this map then the channel is written to all outputs.
+ // If a channel is present, then it is written only to the outputs specified by the map.
+ // Threaded access to this is protected by PluginsLock.
+ std::unordered_map<std::string, std::unordered_set<std::string>> ChannelOutputPluginFiltering;
+
+ // Worker Log Buffer
+ struct QueuedLogMessage {
+ const Name SubsystemName;
+ const Level MessageLogLevel;
+ std::string Buffer;
+ LogTime Time;
+ QueuedLogMessage* Next;
+#if defined(_WIN32)
+ OvrLogHandle FlushEvent;
+#else
+ // Is this a fake event, just for the purpose of flushing the queue?
+ bool FlushEvent;
+#endif // defined(_WIN32)
+
+ QueuedLogMessage(
+ const char* subsystemName,
+ Level messageLogLevel,
+ const char* stream,
+ const LogTime& time);
+ };
+
+ // Maximum number of logs that we allow in the queue at a time.
+ // If we go beyond this limit, we keep a count of additional logs that were lost.
+ static const int WorkQueueLimit = 1000;
+
+ Lock WorkQueueLock; // Lock guarding the work queue
+ QueuedLogMessage* WorkQueueHead; // Head of linked list of work that is queued
+ QueuedLogMessage* WorkQueueTail; // Tail of linked list of work that is queued
+ int WorkQueueSize; // Size of the linked list of queued work
+ int WorkQueueOverrun; // Number of log messages that exceeded the limit
+ // The work queue size is used to avoid overwhelming the logging thread, since it takes 1-2
+ // milliseconds to log out each message it can easily fall behind a large amount of logs. Lost
+ // log messages are added to the WorkQueueOverrun count so that they can be reported as "X logs
+ // were lost".
+
+ inline void WorkQueueAdd(QueuedLogMessage* msg) {
+ if (WorkQueueTail) {
+ WorkQueueTail->Next = msg;
+ } else {
+ WorkQueueHead = msg;
+ }
+ WorkQueueTail = msg;
+ ++WorkQueueSize;
+ }
+
+#if defined(_WIN32)
+#define OVR_THREAD_FUNCTION_TYPE DWORD WINAPI
+#else
+#define OVR_THREAD_FUNCTION_TYPE uint32_t
+#endif
+
+ static OVR_THREAD_FUNCTION_TYPE WorkerThreadEntrypoint_(void* worker);
+
+ void WorkerThreadEntrypoint();
+
+ Lock StartStopLock;
+#if defined(_WIN32)
+ // Event letting the worker thread know the queue is not empty
+ AutoHandle WorkerWakeEvent;
+ Terminator WorkerTerminator;
+ AutoHandle LoggingThread;
+#else
+ std::atomic<bool> Terminated;
+ std::mutex WorkerCvMutex;
+ std::condition_variable WorkerCv;
+ std::thread LoggingThread;
+#endif // defined(_WIN32)
+ RepeatedMessageManager RepeatedMessageManagerInstance;
+
+ // Append level and subsystem name to timestamp buffer
+ // The buffer should point to the ending null terminator of
+ // the timestamp string.
+ static void
+ AppendHeader(char* buffer, size_t bufferBytes, Level level, const char* subsystemName);
+
+ void ProcessQueuedMessages();
+
+ void
+ FlushDbgViewLogImmediately(const char* subsystemName, Level messageLogLevel, const char* stream);
+};
+
+//-----------------------------------------------------------------------------
+// ErrorSilencer
+//
+// This will demote errors to warnings in the log until it goes out of scope.
+// Helper class that allows error silencing to be done several function calls
+// up the stack and checked down the stack.
+class ErrorSilencer {
+ public:
+ // Returns a bitfield of SilenceOptions that are currently in effect
+ static int GetSilenceOptions();
+
+ // Start silencing errors.
+ ErrorSilencer(int options = DemoteErrorsToWarnings);
+
+ enum SilenceOptions {
+ // Error logs will be demoted to the warning log level
+ DemoteErrorsToWarnings = 1,
+
+ // All Log* methods will be silenced
+ CompletelySilenceLogs = 2,
+
+ // OVR::MakeError will not assert when errors are set
+ PreventErrorAsserts = 4,
+
+ // All logs at a level > Debug will be set to Debug level
+ DemoteToDebug = 8
+ };
+
+ // Stop silencing errors.
+ ~ErrorSilencer();
+
+ private:
+ // Start silencing errors. This is done automatically be the constructor.
+ void Silence();
+
+ // Stop silencing errors. This is done automatically be the deconstructor.
+ void Unsilence();
+
+ int Options = 0;
+};
+
+//-----------------------------------------------------------------------------
+// Channel
+//
+// One named logging channel.
+
+class Channel {
+ public:
+ Channel(const char* nameString);
+ Channel(const Channel& other);
+ ~Channel();
+
+ // This is the initial default minimum level for a newly constructed Channel.
+ const Level DefaultMinimumOutputLevel = Level::Info;
+
+ // Same thing as channel name.
+ // To do: rename all usage of SubsystemName to ChannelName.
+ const Name SubsystemName;
+
+ // Deprecated, use SubsystemName.Get() instead
+ const char* GetName() const {
+ return SubsystemName.Get();
+ }
+
+ // Add an extra prefix to all log messages generated by the channel.
+ // This function is *not* thread-safe. Logging from another thread while changing
+ // the prefix can cause crashes.
+ std::string GetPrefix() const;
+ void SetPrefix(const std::string& prefix);
+
+ // Set the minimum output level permitted from this channel.
+ void SetMinimumOutputLevel(Level newLevel);
+
+ // Set the output level temporarily for this session without remembering that setting.
+ void SetMinimumOutputLevelNoSave(Level newLevel);
+
+ Level GetMinimumOutputLevel() const;
+
+ LOGGING_INLINE bool Active(Level level) const {
+ return MinimumOutputLevel <= (uint32_t)level;
+ }
+
+ // Target of doLog function
+ static void SetOutputWorkerOutputFunction(OutputWorkerOutputFunctionType function) {
+ OutputWorkerOutputFunction = function;
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void Log(Level level, Args&&... args) const {
+ if (Active(level)) {
+ doLog(level, std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void LogError(Args&&... args) const {
+ if (Active(Level::Error)) {
+ doLog(Level::Error, std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void LogWarning(Args&&... args) const {
+ if (Active(Level::Warning)) {
+ doLog(Level::Warning, std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void LogInfo(Args&&... args) const {
+ if (Active(Level::Info)) {
+ doLog(Level::Info, std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void LogDebug(Args&&... args) const {
+ if (Active(Level::Debug)) {
+ doLog(Level::Debug, std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void LogTrace(Args&&... args) const {
+ if (Active(Level::Trace)) {
+ doLog(Level::Trace, std::forward<Args>(args)...);
+ }
+ }
+
+ // printf style log functions
+ template <typename... Args>
+ LOGGING_INLINE void LogF(Level level, Args&&... args) const {
+ if (Active(level)) {
+ doLogF(level, std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void LogErrorF(Args&&... args) const {
+ if (Active(Level::Error)) {
+ doLogF(Level::Error, std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void LogWarningF(Args&&... args) const {
+ if (Active(Level::Warning)) {
+ doLogF(Level::Warning, std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void LogInfoF(Args&&... args) const {
+ if (Active(Level::Info)) {
+ doLogF(Level::Info, std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void LogDebugF(Args&&... args) const {
+ if (Active(Level::Debug)) {
+ doLogF(Level::Debug, std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ LOGGING_INLINE void LogTraceF(Args&&... args) const {
+ if (Active(Level::Trace)) {
+ doLogF(Level::Trace, std::forward<Args>(args)...);
+ }
+ }
+
+ // DANGER DANGER DANGER
+ // This function forces a log message to be recorded even if the log queue is full.
+ // This is dangerous because the caller can run far ahead of the output writer thread
+ // and cause a large amount of memory to be allocated and logging tasks can take many
+ // minutes to flush afterwards. It should only be used when the data is critical.
+ template <typename... Args>
+ LOGGING_INLINE void DangerousForceLog(Level level, Args&&... args) const {
+ if (Active(level)) {
+ int silenceOptions = ErrorSilencer::GetSilenceOptions();
+ if (silenceOptions & ErrorSilencer::CompletelySilenceLogs) {
+ return;
+ }
+
+ if (level > Level::Debug && (silenceOptions & ErrorSilencer::DemoteToDebug)) {
+ // Demote to debug
+ level = Level::Debug;
+ } else if (
+ level == Level::Error && (silenceOptions & ErrorSilencer::DemoteErrorsToWarnings)) {
+ // Demote to warning
+ level = Level::Warning;
+ }
+
+ LogStringBuffer buffer(SubsystemName.Get(), level);
+
+ writeLogBuffer(buffer, Prefix, args...);
+
+ // Submit buffer to logging subsystem
+ const std::string& tmp = buffer.Stream.str();
+ OutputWorkerOutputFunction(
+ buffer.SubsystemName.Get(),
+ (Log_Level_t)buffer.MessageLogLevel,
+ tmp.c_str(),
+ buffer.Relogged,
+ (Write_Option_t)WriteOption::DangerouslyIgnoreQueueLimit);
+ }
+ }
+ // DANGER DANGER DANGER
+
+ private:
+ //-------------------------------------------------------------------------
+ // Internal Implementation
+
+ Channel() = delete;
+ Channel(Channel&& other) = delete;
+ Channel& operator=(const Channel& other) = delete;
+ Channel& operator=(Channel&& other) = delete;
+
+ friend class Configurator;
+
+ // Used to iterate through a linked list of Channel objects
+ // A linked list is used to avoid CRT new / delete during startup as this is called from the
+ // constructor
+ ChannelNode Node;
+ void registerNode();
+
+ // Level at which this channel will log.
+ Log_Level_t MinimumOutputLevel;
+
+ // Optional prefix
+ std::string Prefix;
+
+ // So changing Prefix is threadsafe
+ mutable Lock PrefixLock;
+
+ bool UserOverrodeMinimumOutputLevel;
+
+ // Target of doLog function
+ static OutputWorkerOutputFunctionType OutputWorkerOutputFunction;
+
+ // Target of OnChannelLevelChange
+ static void ConfiguratorOnChannelLevelChange(
+ const char* channelName,
+ Log_Level_t minimumOutputLevel);
+
+ // Target of Register
+ static void ConfiguratorRegister(ChannelNode* channelNode);
+
+ // Target of Unregister
+ static void ConfiguratorUnregister(ChannelNode* channelNode);
+
+ template <typename T>
+ LOGGING_INLINE void writeLogBuffer(LogStringBuffer& buffer, T&& arg) const {
+ LogStringize(buffer, arg);
+ }
+
+ template <typename T, typename... Args>
+ LOGGING_INLINE void writeLogBuffer(LogStringBuffer& buffer, T&& arg, Args&&... args) const {
+ writeLogBuffer(buffer, arg);
+ writeLogBuffer(buffer, args...);
+ }
+
+ // Unroll arguments
+ template <typename... Args>
+ LOGGING_INLINE void doLog(Level level, Args&&... args) const {
+ int silenceOptions = ErrorSilencer::GetSilenceOptions();
+ if (silenceOptions & ErrorSilencer::CompletelySilenceLogs) {
+ return;
+ }
+
+ if (level > Level::Debug && (silenceOptions & ErrorSilencer::DemoteToDebug)) {
+ // Demote to debug
+ level = Level::Debug;
+ } else if (level == Level::Error && (silenceOptions & ErrorSilencer::DemoteErrorsToWarnings)) {
+ // Demote to warning
+ level = Level::Warning;
+ }
+
+ LogStringBuffer buffer(SubsystemName.Get(), level);
+
+ writeLogBuffer(buffer, Prefix, args...);
+
+ // Submit buffer to logging subsystem
+ const std::string& tmp = buffer.Stream.str();
+ OutputWorkerOutputFunction(
+ buffer.SubsystemName.Get(),
+ (Log_Level_t)buffer.MessageLogLevel,
+ tmp.c_str(),
+ buffer.Relogged,
+ (Write_Option_t)WriteOption::Default);
+ }
+
+ // Returns the buffer capacity required to printf the given format+arguments.
+ // Returns -1 if the format is invalid.
+ static int GetPrintfLengthV(const char* format, va_list argList) {
+ int size;
+
+#if defined( \
+ _MSC_VER) // Microsoft doesn't support C99-Standard vsnprintf, so need to use _vscprintf.
+ size = _vscprintf(format, argList); // Returns the required strlen, or -1 if format error.
+#else
+ size = vsnprintf(
+ nullptr, 0, format, argList); // Returns the required strlen, or negative if format error.
+#endif
+
+ if (size > 0) // If we can 0-terminate the output...
+ ++size; // Add one to account for terminating null.
+ else
+ size = -1;
+
+ return size;
+ }
+
+ static int GetPrintfLength(const char* format, ...);
+
+ template <typename... Args>
+ LOGGING_INLINE void doLogF(Level level, Args&&... args) const {
+ int silenceOptions = ErrorSilencer::GetSilenceOptions();
+ if (silenceOptions & ErrorSilencer::CompletelySilenceLogs) {
+ return;
+ }
+
+ if (level > Level::Debug && (silenceOptions & ErrorSilencer::DemoteToDebug)) {
+ // Demote to debug
+ level = Level::Debug;
+ } else if (level == Level::Error && (silenceOptions & ErrorSilencer::DemoteErrorsToWarnings)) {
+ // Demote to warning
+ level = Level::Warning;
+ }
+
+ LogStringBuffer buffer(SubsystemName.Get(), level);
+
+ char logCharsLocal[1024];
+ char* logChars = logCharsLocal;
+ char* logCharsAllocated = nullptr;
+
+#if defined(_MSC_VER)
+ int result = _snprintf_s(logCharsLocal, sizeof(logCharsLocal), _TRUNCATE, args...);
+#else
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#pragma clang diagnostic ignored "-Wformat-security"
+#endif // defined(__clang__)
+ int result = snprintf(logCharsLocal, sizeof(logCharsLocal), args...);
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // defined(__clang__)
+#endif
+
+ if ((result < 0) || ((size_t)result >= sizeof(logCharsLocal))) {
+ int requiredSize = GetPrintfLength(args...);
+
+ if ((requiredSize < 0) || (requiredSize > (1024 * 1024))) {
+ LOGGING_DEBUG_BREAK(); // This call should be converted to the new log system.
+ return;
+ }
+
+ logCharsAllocated = new char[requiredSize];
+ logChars = logCharsAllocated;
+
+#if defined(_MSC_VER)
+ _snprintf_s(logChars, (size_t)requiredSize, _TRUNCATE, args...);
+#else
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#pragma clang diagnostic ignored "-Wformat-security"
+#endif // defined(__clang__)
+ snprintf(logChars, (size_t)requiredSize, args...);
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif // defined(__clang__)
+#endif
+ }
+
+ writeLogBuffer(buffer, Prefix, logChars);
+
+ // Submit buffer to logging subsystem
+ const std::string& tmp = buffer.Stream.str();
+ OutputWorkerOutputFunction(
+ buffer.SubsystemName.Get(),
+ (Log_Level_t)buffer.MessageLogLevel,
+ tmp.c_str(),
+ buffer.Relogged,
+ (Write_Option_t)WriteOption::Default);
+
+ delete[] logCharsAllocated;
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Log Configurator
+//
+// Centralized object that can configure and enumerate all the channels.
+
+class ConfiguratorPlugin {
+ public:
+ ConfiguratorPlugin();
+ virtual ~ConfiguratorPlugin();
+
+ // Modify the channel level if it is set, otherwise leave it as-is.
+ virtual void RestoreChannelLevel(const char* name, Level& level) = 0;
+
+ // Sets the channel level
+ virtual void SaveChannelLevel(const char* name, Level level) = 0;
+};
+
+class Configurator {
+ friend class Channel;
+ friend class OutputWorker;
+
+ Configurator(); // Call GetInstance() to get the singleton instance.
+
+ public:
+ // Get singleton instance for logging configurator
+ static Configurator* GetInstance();
+
+ ~Configurator();
+
+ void SetGlobalMinimumLogLevel(Level level);
+
+ inline void SilenceLogging() {
+ // Set the minimum logging level higher than any actual message.
+ SetGlobalMinimumLogLevel(Level::Count);
+ }
+
+ // Sets the ConfiguratorPlugin for this Configurator to use.
+ void SetPlugin(std::shared_ptr<ConfiguratorPlugin> plugin);
+
+ // Get all channels - note channels do not necessarily have unique names
+ void GetChannels(std::vector<std::pair<std::string, Level>>& channels);
+
+ // Set all channels with channelName to level.
+ // Cann disable a channel by using Level::Disabled.
+ void SetChannelLevel(const std::string& channelName, Level level);
+
+ // Internal: Invoked through callbacks
+ void OnChannelLevelChange(const char* channelName, Log_Level_t level);
+
+ // Internal: Load log level for a channel from disk, set all channels with this name to this level
+ void RestoreChannelLogLevel(const char* channelName);
+
+ // Internal: Load log level for a channel from disk, set this channel to this level
+ void RestoreChannelLogLevel(ChannelNode* channelNode);
+
+ // Internal: Iterate through all channels and store them
+ void RestoreAllChannelLogLevels();
+
+ // Maps to OutputWorker::SetOutputPluginChannels. See that for documentation.
+ void SetOutputPluginChannels(
+ const char* outputPluginName,
+ const std::vector<std::string>& channelNames);
+
+ // Maps to OutputWorker::SetChannelOutputPlugins. See that for documentation.
+ void SetChannelOutputPlugins(
+ const char* channelName,
+ const std::vector<std::string>& outputPluginNames);
+
+ // Maps to OutputWorker::SetChannelSingleOutput. See that for documentation.
+ void SetChannelSingleOutput(const char* channelName, const char* outputPluginName);
+
+ private:
+ void RestoreAllChannelLogLevelsNoLock();
+
+ uint32_t GlobalMinimumLogLevel;
+ std::shared_ptr<ConfiguratorPlugin> Plugin;
+
+ void SetChannelLevelNoLock(const std::string& channelName, Level level, bool overrideUser);
+};
+
+// Convenience function: ovrlog::Flush();
+inline void Flush() {
+ OutputWorker::GetInstance()->Flush();
+}
+
+} // namespace ovrlog
diff --git a/ovr_sdk_win_23.0.0/Logging/Logging/Logging_OutputPlugins.h b/ovr_sdk_win_23.0.0/Logging/Logging/Logging_OutputPlugins.h
new file mode 100644
index 0000000..6a1d726
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/Logging/Logging_OutputPlugins.h
@@ -0,0 +1,105 @@
+/************************************************************************************
+
+Filename : Logging_OutputPlugins.h
+Content : Logging output plugins
+Created : Oct 26, 2015
+Authors : Chris Taylor
+
+Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
+
+Licensed under the Oculus Master SDK License Version 1.0 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+https://developer.oculus.com/licenses/oculusmastersdk-1.0
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef Logging_OutputPlugins_h
+#define Logging_OutputPlugins_h
+
+#include "Logging/Logging_Library.h"
+
+namespace ovrlog {
+
+//-----------------------------------------------------------------------------
+// Console
+//
+// Console window output (colorized)
+// Prints at stdout level, even for errors.
+// This takes about 3 milliseconds per message in debug mode.
+class OutputConsole : public OutputPlugin {
+ public:
+ OutputConsole(bool useStdio = false);
+ ~OutputConsole();
+
+ // If true then use stdio for output instead of platform-savvy calls.
+ void SetStdioUsage(bool enable);
+
+ protected:
+ virtual const char* GetUniquePluginName() override;
+ virtual void Write(Level level, const char* subsystem, const char* header, const char* utf8msg)
+ override;
+
+ // If enabled then we use stdio instead of platform-specific calls. By default we
+ // use direct platform calls because they are lower overhead and because (for Windows)
+ // they are UTF8-savvy (unlike stdio on Windows). Defaults to false.
+ bool UseStdio;
+};
+
+//-----------------------------------------------------------------------------
+// DbgView
+//
+// This is the MSVC / DbgView log
+// This takes about 150 microseconds per message in debug mode.
+
+class OutputDbgView : public OutputPlugin {
+ public:
+ OutputDbgView();
+ ~OutputDbgView();
+
+ private:
+ virtual const char* GetUniquePluginName() override;
+ virtual void Write(Level level, const char* subsystem, const char* header, const char* utf8msg)
+ override;
+};
+
+//-----------------------------------------------------------------------------
+// System Application Event Log
+//
+// Windows Event Viewer Application Log
+// This takes about 1 millisecond per message in debug mode.
+
+class OutputEventLog : public OutputPlugin {
+ public:
+ OutputEventLog();
+ ~OutputEventLog();
+
+ private:
+#if defined(_WIN32)
+ typedef HANDLE EventSourceHandle;
+#else
+ typedef void* EventSourceHandle;
+#endif
+
+ // Event source handle initialized in constructor and used for logging
+ EventSourceHandle hEventSource;
+ Level MinReportEventLevel;
+
+ virtual const char* GetUniquePluginName() override;
+ virtual void Write(Level level, const char* subsystem, const char* header, const char* utf8msg)
+ override;
+};
+
+} // namespace ovrlog
+
+#endif // Logging_OutputPlugins_h
diff --git a/ovr_sdk_win_23.0.0/Logging/Logging/Logging_Tools.h b/ovr_sdk_win_23.0.0/Logging/Logging/Logging_Tools.h
new file mode 100644
index 0000000..4b067c4
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/Logging/Logging_Tools.h
@@ -0,0 +1,230 @@
+/************************************************************************************
+
+Filename : Logging_Tools.h
+Content : Tools for Logging
+Created : Oct 26, 2015
+Authors : Chris Taylor
+
+Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
+
+Licensed under the Oculus Master SDK License Version 1.0 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+https://developer.oculus.com/licenses/oculusmastersdk-1.0
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef Logging_Tools_h
+#define Logging_Tools_h
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4265) // disable the virtual destructor warning generated by <mutex>
+#endif
+
+#include <string>
+
+//-----------------------------------------------------------------------------
+// Platform-specific macros
+
+#ifdef _MSC_VER
+#define LOGGING_INLINE __forceinline
+#else
+#define LOGGING_INLINE inline
+#endif
+
+#if defined(_DEBUG) || defined(DEBUG)
+#define LOGGING_DEBUG
+#endif
+
+#define LOGGING_STRINGIZEIMPL(x) #x
+#define LOGGING_STRINGIZE(x) LOGGING_STRINGIZEIMPL(x)
+
+#if defined(_WIN32)
+// It is common practice to define WIN32_LEAN_AND_MEAN to reduce compile times.
+// However this then requires us to define our own NTSTATUS data type and other
+// irritations throughout our code-base.
+#ifndef WIN32_LEAN_AND_MEAN
+//#define WIN32_LEAN_AND_MEAN
+#endif
+
+// Prevents <Windows.h> from #including <Winsock.h>, as we use <Winsock2.h> instead.
+#ifndef _WINSOCKAPI_
+#define DID_DEFINE_WINSOCKAPI
+#define _WINSOCKAPI_
+#endif
+
+// Prevents <Windows.h> from defining min() and max() macro symbols.
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#define WIN
+#include <windows.h>
+#endif
+
+// Work around some broken Windows headers:
+#ifdef DID_DEFINE_WINSOCKAPI
+#undef _WINSOCKAPI_
+#undef DID_DEFINE_WINSOCKAPI
+#endif
+
+#if defined(LOGGING_DEBUG)
+#if defined(_MSC_VER)
+#include <intrin.h>
+#define LOGGING_DEBUG_BREAK() __debugbreak()
+#else
+#define LOGGING_DEBUG_BREAK() \
+ { __asm__("int $3\n" : :); }
+#endif
+#else
+#define LOGGING_DEBUG_BREAK()
+#endif
+
+#include <atomic>
+#include <mutex>
+
+namespace ovrlog {
+
+#if defined(_WIN32)
+typedef HANDLE OvrLogHandle;
+#endif // defined(_WIN32)
+
+//-----------------------------------------------------------------------------
+// Lock
+//
+// Critical section wrapper.
+//
+class Lock {
+ public:
+ Lock();
+ ~Lock();
+
+ // Returns true if lock could be held.
+ bool TryEnter();
+
+ void Enter();
+ void Leave();
+
+ private:
+#if defined(_WIN32)
+ // Until the behavior of std::recursive_mutex is vetted, we use CRITICAL_SECTION on Windows.
+ CRITICAL_SECTION cs;
+#else
+ std::recursive_mutex m;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Locker
+//
+// Scoped lock wrapper.
+// To do: Replace this class with direct std::lock usage.
+//
+class Locker {
+ public:
+ Locker(Lock* lock = nullptr);
+ Locker(Lock& lock);
+ ~Locker();
+
+ // Returns true if lock could be held.
+ bool TrySet(Lock* lock);
+ bool TrySet(Lock& lock);
+
+ // Lock the given lock. Unlocks previously held lock.
+ void Set(Lock* lock);
+ void Set(Lock& lock);
+
+ // Unlock any previously held lock.
+ void Clear();
+
+ private:
+ Lock* TheLock;
+};
+
+#if defined(_WIN32)
+//-----------------------------------------------------------------------------
+// AutoHandle
+//
+// Auto-close wrapper for a HANDLE that is invalid when NULL.
+// For example, ::OpenProcess() returns NULL on failure.
+class AutoHandle {
+ public:
+ AutoHandle(OvrLogHandle handle = nullptr);
+ ~AutoHandle();
+
+ void operator=(OvrLogHandle handle);
+
+ OvrLogHandle Get() const {
+ return TheHandle;
+ }
+
+ bool IsValid() const {
+ return TheHandle != nullptr;
+ }
+
+ void Clear();
+
+ protected:
+ OvrLogHandle TheHandle;
+};
+
+//-----------------------------------------------------------------------------
+// Terminator
+//
+// Helper class that allows for signaled exits to an infinite event wait.
+// The main purpose is the WaitOn() function.
+class Terminator {
+ public:
+ Terminator();
+ ~Terminator();
+
+ bool IsTerminated() const {
+ return Terminated;
+ }
+
+ // Setup
+ bool Initialize();
+
+ // Flag terminated
+ void Terminate();
+
+ // Returns true if the event signaled and false on termination or timeout.
+ // Call IsTerminated() to differentiate termination from timeout.
+ // Passing INFINITE for timeout will only return false on termination.
+ bool WaitOn(OvrLogHandle hEvent, uint32_t timeoutMsec = INFINITE);
+
+ // Returns true if the sleep interval exceeded or false on termination.
+ bool WaitSleep(int milliseconds);
+
+ private:
+ // Should we terminate?
+ std::atomic<bool> Terminated;
+
+ // Event to wake up during waits
+ AutoHandle TerminateEvent;
+};
+#endif // defined(_WIN32)
+
+//-----------------------------------------------------------------------------
+// Tools
+
+bool IsDebuggerAttached();
+
+} // namespace ovrlog
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif // Logging_Tools_h
diff --git a/ovr_sdk_win_23.0.0/Logging/PCSDK_Logging_dependency.props b/ovr_sdk_win_23.0.0/Logging/PCSDK_Logging_dependency.props
new file mode 100644
index 0000000..aebdc8a
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/PCSDK_Logging_dependency.props
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <!-- Add required include roots here, separated by semicolon -->
+ <!-- Eg. $(MSBuildThisFileDirectory); -->
+ <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="$(MSBuildThisFileDirectory)\Projects\Windows\$(VSDIR)\PCSDK_Logging.vcxproj">
+ <Project>{08ea9e99-1abe-41b3-9498-51a7824bfca5}</Project>
+ <Private>false</Private>
+ </ProjectReference>
+ </ItemGroup>
+ <!-- Public dependencies -->
+ <!-- /Public dependencies -->
+ <PropertyGroup>
+ <__PCSDK_Logging_dependency>true</__PCSDK_Logging_dependency>
+ </PropertyGroup>
+</Project>
diff --git a/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging.vcxproj b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging.vcxproj
new file mode 100644
index 0000000..6e097e8
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging.vcxproj
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug_DllCrt|Win32">
+ <Configuration>Debug_DllCrt</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release_DllCrt|Win32">
+ <Configuration>Release_DllCrt</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug_DllCrt|x64">
+ <Configuration>Debug_DllCrt</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release_DllCrt|x64">
+ <Configuration>Release_DllCrt</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{08ea9e99-1abe-41b3-9498-51a7824bfca5}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>PCSDK_Logging</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ItemGroup>
+ <None Include="..\..\..\PCSDK_Logging_dependency.props" />
+ <None Include="PCSDK_Logging_internal.props" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\Logging\Logging_Library.h" />
+ <ClInclude Include="..\..\..\Logging\Logging_OutputPlugins.h" />
+ <ClInclude Include="..\..\..\Logging\Logging_Tools.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\src\Logging_Tools.cpp" />
+ <ClCompile Include="..\..\..\src\Logging_Library.cpp" />
+ <ClCompile Include="..\..\..\src\Logging_OutputPlugins.cpp" />
+ </ItemGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), .ovrsource_root.props))\.ovrsource_root.props" />
+ <Import Project="$(ovrsource_root)/.msvc/common.props" />
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), PCSDK_Logging_internal.props))\PCSDK_Logging_internal.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|Win32'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|x64'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|Win32'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|x64'">
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging.vcxproj.filters b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging.vcxproj.filters
new file mode 100644
index 0000000..5476403
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging.vcxproj.filters
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <None Include="..\..\..\PCSDK_Logging_dependency.props" />
+ <None Include="PCSDK_Logging_internal.props" />
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Logging">
+ <UniqueIdentifier>{1131b44d-3c37-4e67-98be-d3820108c671}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src">
+ <UniqueIdentifier>{8090b7c1-d4a9-450b-ab8c-eeca877db86c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\internal">
+ <UniqueIdentifier>{d213a0aa-e383-41d3-8d35-2e48239d33af}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\Logging\Logging_Library.h">
+ <Filter>Logging</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Logging\Logging_OutputPlugins.h">
+ <Filter>Logging</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Logging\Logging_Tools.h">
+ <Filter>Logging</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\src\Logging_Library.cpp">
+ <Filter>src</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\src\Logging_OutputPlugins.cpp">
+ <Filter>src</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\src\Logging_Tools.cpp">
+ <Filter>src\internal</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
diff --git a/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging_internal.props b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging_internal.props
new file mode 100644
index 0000000..c56a6f0
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2015/PCSDK_Logging_internal.props
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)\..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ </ItemDefinitionGroup>
+</Project>
diff --git a/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging.vcxproj b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging.vcxproj
new file mode 100644
index 0000000..7ae24ea
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging.vcxproj
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug_DllCrt|Win32">
+ <Configuration>Debug_DllCrt</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release_DllCrt|Win32">
+ <Configuration>Release_DllCrt</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug_DllCrt|x64">
+ <Configuration>Debug_DllCrt</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release_DllCrt|x64">
+ <Configuration>Release_DllCrt</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{08ea9e99-1abe-41b3-9498-51a7824bfca5}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>PCSDK_Logging</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|x64'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v141</PlatformToolset>
+ <PreferredToolArchitecture>x64</PreferredToolArchitecture>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </ImportGroup>
+ <ItemGroup>
+ <None Include="..\..\..\PCSDK_Logging_dependency.props" />
+ <None Include="PCSDK_Logging_internal.props" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\Logging\Logging-fwd.h" />
+ <ClInclude Include="..\..\..\Logging\Logging_Library.h" />
+ <ClInclude Include="..\..\..\Logging\Logging_OutputPlugins.h" />
+ <ClInclude Include="..\..\..\Logging\Logging_Tools.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\src\Logging_Tools.cpp" />
+ <ClCompile Include="..\..\..\src\Logging_Library.cpp" />
+ <ClCompile Include="..\..\..\src\Logging_OutputPlugins.cpp" />
+ </ItemGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), .ovrsource_root.props))\.ovrsource_root.props" />
+ <Import Project="$(ovrsource_root)/.msvc/common.props" />
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), PCSDK_Logging_internal.props))\PCSDK_Logging_internal.props" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|Win32'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DllCrt|x64'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|Win32'">
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_DllCrt|x64'">
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging.vcxproj.filters b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging.vcxproj.filters
new file mode 100644
index 0000000..5ad9ef8
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging.vcxproj.filters
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <None Include="..\..\..\PCSDK_Logging_dependency.props" />
+ <None Include="PCSDK_Logging_internal.props" />
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Logging">
+ <UniqueIdentifier>{1131b44d-3c37-4e67-98be-d3820108c671}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src">
+ <UniqueIdentifier>{8090b7c1-d4a9-450b-ab8c-eeca877db86c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src\internal">
+ <UniqueIdentifier>{d213a0aa-e383-41d3-8d35-2e48239d33af}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\Logging\Logging_Library.h">
+ <Filter>Logging</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Logging\Logging_OutputPlugins.h">
+ <Filter>Logging</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Logging\Logging_Tools.h">
+ <Filter>Logging</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Logging\Logging-fwd.h">
+ <Filter>Logging</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\src\Logging_Library.cpp">
+ <Filter>src</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\src\Logging_OutputPlugins.cpp">
+ <Filter>src</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\src\Logging_Tools.cpp">
+ <Filter>src\internal</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project>
diff --git a/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging_internal.props b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging_internal.props
new file mode 100644
index 0000000..c56a6f0
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/Projects/Windows/VS2017/PCSDK_Logging_internal.props
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemDefinitionGroup>
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)\..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ </ItemDefinitionGroup>
+</Project>
diff --git a/ovr_sdk_win_23.0.0/Logging/src/Logging_Library.cpp b/ovr_sdk_win_23.0.0/Logging/src/Logging_Library.cpp
new file mode 100644
index 0000000..c1f56f1
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/src/Logging_Library.cpp
@@ -0,0 +1,1537 @@
+/************************************************************************************
+
+Filename : Logging_Library.cpp
+Content : Logging system
+Created : Oct 26, 2015
+Authors : Chris Taylor
+
+Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
+
+Licensed under the Oculus Master SDK License Version 1.0 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+https://developer.oculus.com/licenses/oculusmastersdk-1.0
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Logging/Logging_Library.h"
+#include "Logging/Logging_OutputPlugins.h"
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4530) // C++ exception handler used, but unwind semantics are not enabled
+#endif // _MSC_VER
+
+#include <assert.h>
+#include <inttypes.h>
+#include <string.h>
+#include <time.h>
+#include <algorithm>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#endif // _MSC_VER
+
+#ifndef _WIN32
+#include <codecvt>
+#include <ctime>
+#include <locale>
+#endif // !_WIN32
+
+namespace ovrlog {
+
+//--------------------------------------------------------------------------------------------------
+// LogTime
+//--------------------------------------------------------------------------------------------------
+
+LogTime GetCurrentLogTime() {
+#if defined(_WIN32)
+ SYSTEMTIME t;
+ ::GetLocalTime(&t);
+ return t;
+#else
+ return std::chrono::system_clock::now();
+#endif
+}
+
+//--------------------------------------------------------------------------------------------------
+// RepeatedMessageManager
+//--------------------------------------------------------------------------------------------------
+
+RepeatedMessageManager::RepeatedMessageManager()
+ : Mutex(),
+ BusyInWrite(false),
+ RecentMessageMap(),
+ RepeatedMessageMap(),
+ RepeatedMessageExceptionSet() {}
+
+void RepeatedMessageManager::PrintDeferredAggregateMessage(
+ OutputWorker* outputWorker,
+ RepeatedMessage& repeatedMessage) {
+ // Don't lock Mutex, as it's expected to already be locked.
+
+ // Add the prefix to repeatedMessage->stream instead of writing it separately because a race
+ // condition could otherwise cause another message to be printed between the two.
+ char prefixMessage[64]; // Impossible to overflow below.
+ size_t prefixLength = snprintf(
+ prefixMessage,
+ sizeof(prefixMessage),
+ "[Aggregated %d times] ",
+ repeatedMessage.aggregatedCount);
+ repeatedMessage.stream.insert(0, prefixMessage, prefixLength);
+
+ // We use WriteOption::DangerouslyIgnoreQueueLimit because it is very unlikely that these
+ // messages could be generated in a runaway fashion. But they are an aggregate and so are more
+ // important that their non-aggregated versions would be.
+ BusyInWrite = true;
+ outputWorker->Write(
+ repeatedMessage.subsystemName.c_str(),
+ repeatedMessage.messageLogLevel,
+ repeatedMessage.stream.c_str(),
+ false,
+ WriteOption::DangerouslyIgnoreQueueLimit);
+ BusyInWrite = false;
+}
+
+RepeatedMessageManager::LogTimeMs RepeatedMessageManager::GetCurrentLogMillisecondTime() {
+ const LogTime currentLogTime = GetCurrentLogTime();
+ const LogTimeMs currentLogTimeMs = LogTimeToMillisecondTime(currentLogTime);
+ return currentLogTimeMs;
+}
+
+RepeatedMessageManager::LogTimeMs RepeatedMessageManager::LogTimeToMillisecondTime(
+ const LogTime& logTime) {
+#if defined(_WIN32)
+ // Time is represented by SYSTEMTIME, which is a calendar time, with 1ms granularity.
+ // We need to quickly subtract two SYSTEMTIMEs, which is hard to do because it contains
+ // year, month, day, hour, minute, second, millisecond components. We could use the Windows
+ // SystemTimeToFileTime function to convert SYSTEMTIME to FILETIME, which is an absolute
+ // time with a single value, but that's an expensive conversion.
+ LogTimeMs logTimeMs = (logTime.wHour * 3600000) + (logTime.wMinute * 60000) +
+ (logTime.wSecond * 1000) + logTime.wMilliseconds;
+ return logTimeMs;
+#else
+ return std::chrono::duration_cast<std::chrono::milliseconds>(logTime.time_since_epoch()).count();
+#endif
+}
+
+int64_t RepeatedMessageManager::GetLogMillisecondTimeDifference(LogTimeMs begin, LogTimeMs end) {
+#if defined(_WIN32)
+ if (end >= begin) // If the day didn't roll over between begin and end...
+ return (end - begin);
+ return (86400000 + (end - begin)); // Else assume exactly one day rolled over.
+#else
+ return (end - begin);
+#endif
+}
+
+RepeatedMessageManager::PrefixHashType RepeatedMessageManager::GetHash(const char* p) {
+ // Fowler / Noll / Vo (FNV) Hash
+ // FNV is a great string hash for reduction of collisions, but the cost benefit is high.
+ // We can get away with a fairly poor string hash here which is very fast.
+ //
+ PrefixHashType hash(2166136261U);
+ const char* pEnd = (p + messagePrefixLength);
+ while (*p && (p < pEnd)) {
+ hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
+ hash ^= (uint8_t)*p++;
+ }
+ return hash;
+
+ // uint32_t hash(0); // To do: See if this hash is too poor.
+ // const char* pEnd = (p + messagePrefixLength);
+ // while(*p && (p < pEnd))
+ // hash += (uint8_t)*p++;
+ // return hash;
+}
+
+RepeatedMessageManager::HandleResult RepeatedMessageManager::HandleMessage(
+ const char* subsystemName,
+ Level messageLogLevel,
+ const char* stream) {
+ std::lock_guard<std::recursive_mutex> lock(Mutex);
+
+ if (BusyInWrite) // If we are here due to our own call of OutputWorker::Write from our Poll func..
+ return HandleResult::Passed;
+
+ PrefixHashType prefixHash = GetHash(stream);
+
+ // Check to see if we have this particular message in an exception list.
+ if (RepeatedMessageExceptionSet.find(prefixHash) != RepeatedMessageExceptionSet.end()) {
+ return HandleResult::Passed;
+ }
+
+ PrefixHashType subsystemNameHash = GetHash(subsystemName);
+ if (RepeatedMessageSubsystemExceptionSet.find(subsystemNameHash) !=
+ RepeatedMessageSubsystemExceptionSet.end()) {
+ return HandleResult::Passed;
+ }
+
+ // We will need the current time below for all pathways.
+ const LogTimeMs currentLogTimeMs = GetCurrentLogMillisecondTime();
+
+ // First look at our repeated messages. This is a container of known repeating messages.
+ auto itRepeated = RepeatedMessageMap.find(prefixHash);
+
+ if (itRepeated != RepeatedMessageMap.end()) { // If this is a message that's already repeating...
+ RepeatedMessage& repeatedMessage = itRepeated->second;
+
+ // Assume subsystemName == repeatedMessage->subsystemName, though theoretically it's possible
+ // that two subsystems generate the same prefix string. Let's worry about that if we see it.
+ //
+ // Assume messageLogLevel == repeatedMessage->messageLogLevel for the purposes of handling
+ // repeated messages. It's possible that a subsystem may generate the same message string
+ // but with different log levels, but we've never seen that, and it may not be significant
+ // with respect to handling repeating anyway. Let's worry about that if we see it.
+ const LogTimeMs logTimeDifferenceMs =
+ GetLogMillisecondTimeDifference(repeatedMessage.lastTimeMs, currentLogTimeMs);
+
+ if (logTimeDifferenceMs < maxDeferrableDetectionTimeMs) { // If this message was soon after
+ repeatedMessage.lastTimeMs = currentLogTimeMs; // the last one...
+
+ // We actually print the first few seemingly repeated messages before deferring them.
+ if (repeatedMessage.printedCount < printedRepeatCount) {
+ repeatedMessage.printedCount++;
+ return HandleResult::Passed;
+ }
+
+ // Else we aggregate it, and won't print it until later with a summary printing.
+ // If repeatedMessage.aggregatedCount >= maxDeferredMessages, then copy stream to
+ // repeatedMessage.stream, in order to print the most recent variation of this repeat when
+ // the aggregated print is done.
+ if (++repeatedMessage.aggregatedCount >= maxDeferredMessages)
+ repeatedMessage.stream = stream;
+
+ return HandleResult::Aggregated;
+ }
+ // Else the repeated message was old and we don't don't consider this a repeat.
+ // Don't erase the entry from RepeatedMessageMap here, as we still need to do a final
+ // print of the aggregated message before removing it. We'll handle that in the Poll function.
+ } else {
+ // Else this message wasn't known to be previously repeating, but maybe it's the first repeat
+ // we are encountering. Check the RecentMessageMap for this.
+ auto itRecent = RecentMessageMap.find(prefixHash);
+
+ if (itRecent != RecentMessageMap.end()) { // If it looks like a repeat of something recent...
+ RepeatedMessageMap[prefixHash] = RepeatedMessage(
+ subsystemName, messageLogLevel, stream, currentLogTimeMs, currentLogTimeMs, 0);
+
+ // No need to keep it in the RecentMessageMap any more, since it's now classified as repeat.
+ RecentMessageMap.erase(itRecent);
+ } else {
+ // Else add it to RecentMessageMap. Old RecentMessageMap entries will be removed by Poll().
+ RecentMessageMap[prefixHash] = RecentMessage{currentLogTimeMs};
+ }
+ }
+
+ return HandleResult::Passed;
+}
+
+void RepeatedMessageManager::Poll(OutputWorker* outputWorker) {
+ std::vector<RepeatedMessage> messagesToPrint;
+
+ {
+ std::lock_guard<std::recursive_mutex> lock(Mutex);
+
+ if (RecentMessageMap.size() > (recentMessageCount * 2)) {
+ // Prune the oldest messages out of RecentMessageMap until
+ // RecentMessageMap.size() == recentMessageCount. Unfortunately, RecentMessageMap is an
+ // unsorted hash container, so we can't quickly find the oldest N messages. We can solve
+ // this by doing a copy of iterators to an array, sort, erase first N iterators. A faster
+ // solution would be to keep a std::queue of iterators that were added to the map, though
+ // that results in some complicated code. Maybe we'll do that, but let's do the sort
+ // solution first.
+ const size_t arrayCapacity = (recentMessageCount * 3);
+ RecentMessageMapType::iterator itArray[arrayCapacity]; // Avoid memory allocation.
+ size_t itArraySize = 0;
+
+ for (RecentMessageMapType::iterator it = RecentMessageMap.begin();
+ (it != RecentMessageMap.end()) && (itArraySize < arrayCapacity);
+ ++it) {
+ itArray[itArraySize++] = it;
+ }
+
+ std::sort(
+ itArray,
+ itArray + itArraySize, // Put the oldest at the end of the array.
+ [](const RecentMessageMapType::iterator& it1,
+ const RecentMessageMapType::iterator& it2) -> bool {
+ return (it2->second.timeMs < it1->second.timeMs); // Sort newest to oldest.
+ });
+
+ for (size_t i = 0; i < itArraySize; ++i)
+ RecentMessageMap.erase(itArray[i]);
+ }
+
+ // Currently we go through the entire RepeatedMessageMap every time we are here, though we
+ // have a purgeDeferredMessageTimeMs constant which we have to make this more granular, for
+ // efficiency purposed. To do.
+ const LogTimeMs currentLogTimeMs = GetCurrentLogMillisecondTime();
+
+ for (auto it = RepeatedMessageMap.begin(); it != RepeatedMessageMap.end();) {
+ RepeatedMessage& repeatedMessage = it->second;
+ LogTimeMs logTimeDifferenceMs =
+ GetLogMillisecondTimeDifference(repeatedMessage.lastTimeMs, currentLogTimeMs);
+
+ // If this message hasn't repeated in a while...
+ if (logTimeDifferenceMs > maxDeferrableDetectionTimeMs) {
+ // Print an aggregate result for this entry before removing it.
+ // Since we have already printed the first <printedRepeatCount> of a given repeated message,
+ // we do an aggregate print only if further printings were called for.
+ if (repeatedMessage.aggregatedCount) { // If there are any aggregated messages deferred...
+ // This will print the first variation of the string that was encountered.
+ // We can move the message instead of copy because we won't need it any more after this.
+ messagesToPrint.emplace_back(std::move(repeatedMessage));
+ }
+
+ it = RepeatedMessageMap.erase(it);
+ continue;
+ } else if (repeatedMessage.aggregatedCount >= maxDeferredMessages) {
+ messagesToPrint.emplace_back(repeatedMessage);
+ repeatedMessage.printedCount += repeatedMessage.aggregatedCount;
+ repeatedMessage.aggregatedCount = 0; // Reset this for a new round of aggregation.
+ }
+ ++it;
+ }
+ } // lock
+
+ // We need to print these messages outside our locked Mutex because printing of these is
+ // calling out to external code which itself has mutexes and thus we need to avoid deadlock.
+ for (auto& repeatedMessage : messagesToPrint)
+ PrintDeferredAggregateMessage(outputWorker, repeatedMessage);
+}
+
+void RepeatedMessageManager::AddRepeatedMessageException(const char* messagePrefix) {
+ std::lock_guard<std::recursive_mutex> lock(Mutex);
+
+ PrefixHashType prefixHash = GetHash(messagePrefix);
+ RepeatedMessageExceptionSet.insert(prefixHash);
+}
+
+void RepeatedMessageManager::RemoveRepeatedMessageException(const char* messagePrefix) {
+ std::lock_guard<std::recursive_mutex> lock(Mutex);
+
+ PrefixHashType prefixHash = GetHash(messagePrefix);
+ auto it = RepeatedMessageExceptionSet.find(prefixHash);
+ if (it != RepeatedMessageExceptionSet.end()) {
+ RepeatedMessageExceptionSet.erase(it);
+ }
+}
+
+void RepeatedMessageManager::AddRepeatedMessageSubsystemException(const char* subsystemName) {
+ std::lock_guard<std::recursive_mutex> lock(Mutex);
+
+ PrefixHashType subsystemNameHash = GetHash(subsystemName);
+ RepeatedMessageSubsystemExceptionSet.insert(subsystemNameHash);
+}
+
+void RepeatedMessageManager::RemoveRepeatedMessageSubsytemException(const char* subsystemName) {
+ std::lock_guard<std::recursive_mutex> lock(Mutex);
+
+ PrefixHashType subsystemNameHash = GetHash(subsystemName);
+ auto it = RepeatedMessageSubsystemExceptionSet.find(subsystemNameHash);
+ if (it != RepeatedMessageExceptionSet.end()) {
+ RepeatedMessageExceptionSet.erase(it);
+ }
+}
+
+// Don't use locks or register channels until OutputWorker::Start has been called
+// Once called the first time, register all known channels and start using locks
+static volatile bool OutputWorkerInstValid = false;
+static ChannelNode* ChannelNodeHead = nullptr;
+void ChannelRegisterNoLock(ChannelNode* channelNode) {
+ if (ChannelNodeHead) {
+ channelNode->Next = ChannelNodeHead;
+ ChannelNodeHead = channelNode;
+ } else {
+ ChannelNodeHead = channelNode;
+ channelNode->Next = nullptr;
+ }
+}
+
+void ChannelRegister(ChannelNode* channelNode) {
+ Locker locker(OutputWorker::GetInstance()->GetChannelsLock());
+ ChannelRegisterNoLock(channelNode);
+ Configurator::GetInstance()->RestoreChannelLogLevel(channelNode);
+}
+
+void ChannelUnregisterNoLock(ChannelNode* channelNode) {
+ if (channelNode == ChannelNodeHead) {
+ ChannelNodeHead = channelNode->Next;
+ return;
+ } else {
+ if (ChannelNodeHead != nullptr) {
+ for (ChannelNode *prev = ChannelNodeHead, *cur = prev->Next; cur != nullptr;
+ prev = cur, cur = cur->Next) {
+ if (cur == channelNode) {
+ prev->Next = channelNode->Next;
+ return;
+ }
+ }
+ }
+ // did not find channel to unregister
+ assert(false);
+ }
+}
+
+void ChannelUnregister(ChannelNode* channelNode) {
+ Locker locker(OutputWorker::GetInstance()->GetChannelsLock());
+
+ ChannelUnregisterNoLock(channelNode);
+}
+
+//-----------------------------------------------------------------------------
+// Export Write() as C API so we can use it across DLL boundaries. Not marking it
+// as DLL visibile / DLL export. Expect it to be passed to DLLs after they are loaded.
+void OutputWorkerOutputFunctionC(
+ const char* subsystemName,
+ Log_Level_t messageLogLevel,
+ const char* stream,
+ bool relogged,
+ Write_Option_t option) {
+ OutputWorker::GetInstance()->Write(
+ subsystemName, (Level)messageLogLevel, stream, relogged, (WriteOption)option);
+}
+
+//-----------------------------------------------------------------------------
+// Channel
+
+OutputWorkerOutputFunctionType Channel::OutputWorkerOutputFunction = OutputWorkerOutputFunctionC;
+
+void Channel::ConfiguratorOnChannelLevelChange(
+ const char* channelName,
+ Log_Level_t minimumOutputLevel) {
+ Configurator::GetInstance()->OnChannelLevelChange(channelName, minimumOutputLevel);
+}
+void Channel::ConfiguratorRegister(ChannelNode* channelNode) {
+ if (OutputWorkerInstValid == false)
+ ChannelRegisterNoLock(channelNode);
+ else
+ ChannelRegister(channelNode);
+}
+void Channel::ConfiguratorUnregister(ChannelNode* channelNode) {
+ if (OutputWorkerInstValid == false)
+ ChannelUnregisterNoLock(channelNode);
+ else
+ ChannelUnregister(channelNode);
+}
+
+//-----------------------------------------------------------------------------
+// Shutdown the logging system and release memory
+void ShutdownLogging() {
+ // This function needs to be robust to multiple calls in a row
+ if (OutputWorkerInstValid) {
+ ovrlog::OutputWorker::GetInstance()->Stop();
+ }
+}
+
+void RestartLogging() {
+ if (OutputWorkerInstValid) {
+ ovrlog::OutputWorker::GetInstance()->Start();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Log Output Worker Thread
+
+OutputWorker* OutputWorker::GetInstance() {
+ static OutputWorker worker;
+ return &worker;
+}
+
+void OutputWorkerAtExit() {
+ // This function needs to be robust to multiple calls in a row
+ ShutdownLogging();
+}
+
+OutputWorker::OutputWorker()
+ : IsInDebugger(false),
+ DefaultPluginsDisabled(false),
+ PluginsLock(),
+ Plugins(),
+ OutputPluginChannelFiltering(),
+ ChannelOutputPluginFiltering(),
+ WorkQueueLock(),
+ WorkQueueOverrun(0),
+ StartStopLock()
+#if defined(_WIN32)
+ ,
+ WorkerWakeEvent(),
+ WorkerTerminator(),
+ LoggingThread()
+#else
+ ,
+ Terminated(true)
+#endif // defined(_WIN32)
+{
+#if defined(_WIN32)
+ // Create a worker wake event
+ WorkerWakeEvent = ::CreateEventW(nullptr, FALSE, FALSE, nullptr);
+#endif // defined(_WIN32)
+
+ IsInDebugger = IsDebuggerAttached();
+
+ InstallDefaultOutputPlugins();
+
+ OutputWorkerInstValid = true;
+
+ Start();
+}
+
+OutputWorker::~OutputWorker() {
+ Stop();
+ OutputWorkerInstValid = false;
+}
+
+void OutputWorker::InstallDefaultOutputPlugins() {
+ // These are the default outputs for ALL applications:
+
+ // If debugger is *not* attached,
+ if (!IsInDebugger) {
+ // Enable event log output. This logger is fairly slow, taking about 1 millisecond per log,
+ // and this is very expensive to flush after each log message for debugging. Since we almost
+ // never use the Event Log when debugging apps it is better to simply leave it out.
+#ifdef OVR_ENABLE_OS_EVENT_LOG
+ AddPlugin(std::make_shared<OutputEventLog>());
+#endif
+
+ // Do not log to the DbgView output from the worker thread. When a debugger is attached we
+ // instead flush directly to the DbgView log so that the messages are available at breakpoints.
+ AddPlugin(std::make_shared<OutputDbgView>());
+ }
+
+#if defined(_WIN32)
+ // If there is a console window,
+ if (::GetConsoleWindow() != NULL) {
+ bool outputToStdout = false;
+ if (GetEnvironmentVariableW(L"OvrOutputToStdout", NULL, 0) > 0) {
+ outputToStdout = true;
+ }
+
+ // Enable the console. This logger takes 3 milliseconds per message, so it is fairly
+ // slow and should be avoided if it is not needed (ie. console is not shown).
+ AddPlugin(std::make_shared<OutputConsole>(outputToStdout));
+ }
+#endif
+}
+
+void OutputWorker::AddPlugin(std::shared_ptr<OutputPlugin> plugin) {
+ if (!plugin) {
+ return;
+ }
+
+ Locker locker(PluginsLock);
+
+ RemovePlugin(plugin);
+
+ Plugins.insert(plugin);
+}
+
+void OutputWorker::RemovePlugin(std::shared_ptr<OutputPlugin> pluginToRemove) {
+ if (!pluginToRemove) {
+ return;
+ }
+
+ const char* nameOfPluginToRemove = pluginToRemove->GetUniquePluginName();
+
+ Locker locker(PluginsLock);
+
+ for (auto& existingPlugin : Plugins) {
+ const char* existingPluginName = existingPlugin->GetUniquePluginName();
+
+ // If the names match exactly,
+ if (0 == strcmp(nameOfPluginToRemove, existingPluginName)) {
+ Plugins.erase(existingPlugin);
+ break;
+ }
+ }
+}
+
+std::shared_ptr<OutputPlugin> OutputWorker::GetPlugin(const char* const pluginName) {
+ Locker locker(PluginsLock);
+
+ for (auto& existingPlugin : Plugins) {
+ const char* const existingPluginName = existingPlugin->GetUniquePluginName();
+
+ // If the names match exactly,
+ if (0 == strcmp(pluginName, existingPluginName)) {
+ return existingPlugin;
+ }
+ }
+
+ return nullptr;
+}
+
+void OutputWorker::DisableAllPlugins() {
+ Locker locker(PluginsLock);
+
+ DefaultPluginsDisabled = true;
+ Plugins.clear();
+}
+
+Lock* OutputWorker::GetChannelsLock() {
+ return &ChannelsLock;
+}
+
+void OutputWorker::AddRepeatedMessageSubsystemException(const char* subsystemName) {
+ return RepeatedMessageManagerInstance.AddRepeatedMessageSubsystemException(subsystemName);
+}
+
+void OutputWorker::RemoveRepeatedMessageSubsystemException(const char* subsystemName) {
+ return RepeatedMessageManagerInstance.RemoveRepeatedMessageSubsytemException(subsystemName);
+}
+
+void OutputWorker::SetOutputPluginChannels(
+ const char* outputPluginName,
+ const std::vector<std::string>& channelNames) {
+ Locker locker(PluginsLock);
+
+ if (channelNames.empty())
+ OutputPluginChannelFiltering.erase(outputPluginName);
+ else {
+ OutputPluginChannelFiltering[outputPluginName].clear();
+ OutputPluginChannelFiltering[outputPluginName].insert(channelNames.begin(), channelNames.end());
+ }
+}
+
+void OutputWorker::SetChannelOutputPlugins(
+ const char* channelName,
+ const std::vector<std::string>& outputPluginNames) {
+ Locker locker(PluginsLock);
+
+ if (outputPluginNames.empty())
+ OutputPluginChannelFiltering.erase(channelName);
+ else {
+ ChannelOutputPluginFiltering[channelName].clear();
+ ChannelOutputPluginFiltering[channelName].insert(
+ outputPluginNames.begin(), outputPluginNames.end());
+ }
+}
+
+void OutputWorker::SetChannelSingleOutput(const char* channelName, const char* outputPluginName) {
+ // We assume a recursive mutex, becasue the called functions also lock this mutex. If the
+ // mutex wasn't recursive then we would immediately find out because this function would
+ // deadlock itself.
+ Locker locker(PluginsLock);
+
+ SetChannelOutputPlugins(channelName, {outputPluginName});
+ SetOutputPluginChannels(outputPluginName, {channelName});
+}
+
+void OutputWorker::Start() {
+ // Hold start-stop lock to prevent Start() and Stop() from being called at the same time.
+ Locker startStopLocker(StartStopLock);
+
+ // If already started,
+#if defined(_WIN32)
+ if (LoggingThread.IsValid())
+#else
+ if (LoggingThread.joinable())
+#endif // defined(_WIN32)
+ {
+ return; // Nothing to do!
+ }
+
+ // RestoreAllChannelLogLevelsNoLock is used to address
+ // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm Section 6.7
+ // RestoreAllChannelLogLevelsNoLock otherwise invokes OutputWorker::GetInstance() whose
+ // constructor hasn't finished yet
+ Configurator::GetInstance()->RestoreAllChannelLogLevelsNoLock();
+
+#if defined(_WIN32)
+ if (!WorkerTerminator.Initialize()) {
+ // Unable to create worker terminator event?
+ LOGGING_DEBUG_BREAK();
+ return;
+ }
+
+ LoggingThread = ::CreateThread(
+ nullptr, // No thread security attributes
+ 0, // Default stack size
+ &OutputWorker::WorkerThreadEntrypoint_, // Thread entrypoint
+ this, // This parameter
+ 0, // No creation flags, start immediately
+ nullptr); // Do not request thread id
+
+ if (!LoggingThread.IsValid()) {
+ // Unable to create worker thread?
+ LOGGING_DEBUG_BREAK();
+ return;
+ }
+#else
+ {
+ std::unique_lock<std::mutex> lock(WorkerCvMutex);
+
+ Terminated = false;
+ LoggingThread = std::thread(&OutputWorker::WorkerThreadEntrypoint_, this);
+
+ // make sure our thread has started
+ WorkerCv.wait(lock);
+ }
+#endif // defined(_WIN32) && !defined(OVR_LOGGING_USE_CPP11)
+
+ // Note this may queue more than one OutputWorkerAtExit() call.
+ // This function needs to be robust to multiple calls in a row
+ std::atexit(&OutputWorkerAtExit);
+}
+
+void OutputWorker::Stop() {
+ // This function needs to be robust to multiple calls in a row
+
+ // Hold start-stop lock to prevent Start() and Stop() from being called at the same time.
+ Locker startStopLocker(StartStopLock);
+
+#if defined(_WIN32)
+ if (LoggingThread.IsValid()) {
+ // Flag termination
+ WorkerTerminator.Terminate();
+
+ // Wait for thread to end
+ ::WaitForSingleObject(
+ LoggingThread.Get(), // Thread handle
+ INFINITE); // Wait forever for thread to terminate
+
+ LoggingThread.Clear();
+ }
+#else
+ if (LoggingThread.joinable()) {
+ // Flag termination
+ Terminated = true;
+ {
+ std::unique_lock<std::mutex> lock(WorkerCvMutex);
+ WorkerCv.notify_one();
+ }
+
+ LoggingThread.join();
+ }
+#endif // defined(_WIN32)
+
+ // Hold scoped work queue lock:
+ {
+ // This ensures that logs are not printed out of order on Stop(), and that Flush()
+ // can use the flag to check if a flush has already occurred.
+ Locker workQueueLock(WorkQueueLock);
+
+ // Finish the last set of queued messages to avoid losing any before Stop() returns.
+ ProcessQueuedMessages();
+ }
+}
+
+static int GetTimestamp(char* buffer, int bufferBytes, const LogTime& logTime) {
+#if defined(_WIN32)
+ // GetDateFormat and GetTimeFormat returns the number of characters written to the
+ // buffer if successful, including the trailing '\0'; and return 0 on failure.
+ char dateBuffer[16];
+ int dateBufferLength;
+ int writtenChars =
+ ::GetDateFormatA(LOCALE_USER_DEFAULT, 0, &logTime, "dd/MM ", dateBuffer, sizeof(dateBuffer));
+
+ if (writtenChars <= 0) {
+ // Failure
+ buffer[0] = '\0';
+ return 0;
+ }
+ dateBufferLength = (writtenChars - 1);
+
+ char timeBuffer[32];
+ int timeBufferLength;
+ writtenChars = ::GetTimeFormatA( // Intentionally using 'A' version.
+ LOCALE_USER_DEFAULT, // User locale
+ 0, // Default flags
+ &logTime, // Time
+ "HH:mm:ss",
+ timeBuffer, // Output buffer
+ sizeof(timeBuffer)); // Size of buffer in tchars
+
+ if (writtenChars <= 0) {
+ // Failure
+ buffer[0] = '\0';
+ return 0;
+ }
+ timeBufferLength = (writtenChars - 1);
+
+ // Append milliseconds
+ const char msBuffer[5] = {(char)('.'),
+ (char)(((logTime.wMilliseconds / 100) % 10) + '0'),
+ (char)(((logTime.wMilliseconds / 10) % 10) + '0'),
+ (char)((logTime.wMilliseconds % 10) + '0'),
+ (char)('\0')};
+
+ const int writeSum = (dateBufferLength + timeBufferLength + sizeof(msBuffer));
+
+ if (bufferBytes < writeSum) {
+ buffer[0] = '\0';
+ return 0;
+ }
+
+#if defined(_MSC_VER)
+#pragma warning(push) // We are guaranteed that strcpy is safe.
+#pragma warning(disable : 4996) //'strcpy': This function or variable may be unsafe.
+#endif // defined(_MSC_VER)
+ strcpy(buffer, dateBuffer);
+ strcpy(buffer + dateBufferLength, timeBuffer);
+ strcpy(buffer + dateBufferLength + timeBufferLength, msBuffer);
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif // defined(_MSC_VER)
+
+ return (
+ writeSum -
+ 1); // -1 because we return the strlen of buffer, and don't include the trailing '\0'.
+#else
+ using namespace std::chrono;
+ size_t ms = duration_cast<milliseconds>(logTime.time_since_epoch()).count() % 1000;
+ time_t seconds = system_clock::to_time_t(logTime);
+
+ const char msBuffer[5] = {(char)('.'),
+ (char)(((ms / 100) % 10) + '0'),
+ (char)(((ms / 10) % 10) + '0'),
+ (char)((ms % 10) + '0'),
+ (char)('\0')};
+
+#if defined(_MSC_VER)
+#pragma warning(push) // We are guaranteed that strcpy is safe.
+#pragma warning(disable : 4996) //'strcpy': This function or variable may be unsafe.
+#endif // defined(_MSC_VER)
+ size_t dateTimeBufferLength =
+ std::strftime(buffer, bufferBytes, "%d/%m %H:%M:%S", std::localtime(&seconds));
+ strcpy(buffer + dateTimeBufferLength, msBuffer);
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif // defined(_MSC_VER)
+
+ return (int)(dateTimeBufferLength + sizeof(msBuffer) - 1);
+#endif // defined(_WIN32)
+}
+
+// Returns number of bytes written to buffer
+// Precondition: Buffer is large enough to hold everything,
+// so don't bother complaining there isn't enough length checking.
+static int GetTimestamp(char* buffer, int bufferBytes) {
+ LogTime time = GetCurrentLogTime();
+ return GetTimestamp(buffer, bufferBytes, time);
+}
+
+void OutputWorker::Flush() {
+#if defined(_WIN32)
+ if (!LoggingThread.IsValid())
+#else
+ if (!LoggingThread.joinable())
+#endif // defined(_WIN32)
+ {
+ LOGGING_DEBUG_BREAK(); // Must be called between Start() and Stop()
+ return;
+ }
+
+#if defined(_WIN32)
+ AutoHandle flushEvent;
+#else
+ std::unique_lock<std::mutex> lock(WorkerCvMutex);
+#endif // !defined(_WIN32)
+
+ // Scoped work queue lock:
+ {
+ Locker workQueueLock(WorkQueueLock);
+
+ LogStringBuffer buffer("Logging", ovrlog::Level::Info);
+ LogTime time = GetCurrentLogTime();
+ QueuedLogMessage* queuedBuffer = new QueuedLogMessage("Logging", ovrlog::Level::Info, "", time);
+#if defined(_WIN32)
+ // Generate a flush event
+ flushEvent = ::CreateEventW(nullptr, FALSE, FALSE, nullptr);
+ queuedBuffer->FlushEvent = flushEvent.Get();
+#else
+ queuedBuffer->FlushEvent = true;
+#endif // defined(_WIN32)
+
+ // Add queued buffer to the end of the work queue
+ WorkQueueAdd(queuedBuffer);
+
+#if defined(_WIN32)
+ // Wake the worker thread
+ ::SetEvent(WorkerWakeEvent.Get());
+#else
+ WorkerCv.notify_one();
+#endif // defined(_WIN32)
+ }
+
+ // Wait until the event signals.
+ // Since we are guaranteed to never lose log messages, as late as Stop() being called,
+ // this cannot cause a hang.
+#if defined(_WIN32)
+ ::WaitForSingleObject(flushEvent.Get(), INFINITE);
+#else
+ WorkerCv.wait(lock);
+#endif // !defined(_WIN32)
+}
+
+static void WriteAdvanceStrCpy(char*& buffer, size_t& bufferBytes, const char* str) {
+ // Get length of string to copy into buffer
+ size_t slen = strlen(str);
+
+ // If the resulting buffer cannot accommodate the string and a null terminator,
+ if (bufferBytes < slen + 1) {
+ // Do nothing
+ return;
+ }
+
+ // Copy string to buffer
+ memcpy(buffer, str, slen);
+
+ // Advance buffer by number of bytes copied
+ buffer += slen;
+ bufferBytes -= slen;
+}
+
+void OutputWorker::AppendHeader(
+ char* buffer,
+ size_t bufferBytes,
+ Level level,
+ const char* subsystemName) {
+ // Writes <L> [SubSystem] to the provided buffer.
+
+ // Based on message log level,
+ const char* initial = "";
+ switch (level) {
+ case Level::Disabled:
+ initial = " {DISABLED}[";
+ break; // This typically should not occur, but we have here for consistency.
+ case Level::Trace:
+ initial = " {TRACE} [";
+ break;
+ case Level::Debug:
+ initial = " {DEBUG} [";
+ break;
+ case Level::Info:
+ initial = " {INFO} [";
+ break;
+ case Level::Warning:
+ initial = " {WARNING} [";
+ break;
+ case Level::Error:
+ initial = " {!ERROR!} [";
+ break;
+ default:
+ initial = " {???} [";
+ break;
+ }
+ static_assert(Level::Count == static_cast<Level>(6), "Needs updating");
+
+ WriteAdvanceStrCpy(buffer, bufferBytes, initial);
+ WriteAdvanceStrCpy(buffer, bufferBytes, subsystemName);
+ WriteAdvanceStrCpy(buffer, bufferBytes, "] ");
+ buffer[0] = '\0';
+}
+
+OVR_THREAD_FUNCTION_TYPE OutputWorker::WorkerThreadEntrypoint_(void* vworker) {
+ // Invoke thread entry-point
+ OutputWorker* worker = reinterpret_cast<OutputWorker*>(vworker);
+ if (worker) {
+ worker->WorkerThreadEntrypoint();
+ }
+ return 0;
+}
+
+void OutputWorker::ProcessQueuedMessages() {
+ // Potentially trigger aggregated repeating messages.
+ RepeatedMessageManagerInstance.Poll(this);
+
+ static const int TempBufferBytes = 1024; // 1 KiB
+ char HeaderBuffer[TempBufferBytes];
+
+ QueuedLogMessage* message = nullptr;
+
+ // Pull messages off the queue
+ int lostCount = 0;
+ {
+ Locker locker(WorkQueueLock);
+ message = WorkQueueHead;
+ WorkQueueHead = WorkQueueTail = nullptr;
+ lostCount = WorkQueueOverrun;
+ WorkQueueOverrun = 0;
+ WorkQueueSize = 0;
+ }
+
+ if (message == nullptr) {
+ // No data to process
+ return;
+ }
+
+ // Log output format:
+ // TIMESTAMP <L> [SubSystem] Message
+
+ // If some messages were lost,
+ if (lostCount > 0) {
+ char str[255];
+ snprintf(
+ str,
+ sizeof(str),
+ "Lost %i log messages due to queue overrun; try to reduce the amount of logging",
+ lostCount);
+
+ // Insert at the front of the list
+ LogTime t = GetCurrentLogTime();
+ QueuedLogMessage* queuedMsg = new QueuedLogMessage("Logging", Level::Error, str, t);
+ queuedMsg->Next = message;
+ message = queuedMsg;
+ }
+
+ {
+ Locker locker(PluginsLock);
+
+ // For each log message,
+ for (QueuedLogMessage* next; message; message = next) {
+ // If the message is a flush event,
+#if defined(_WIN32)
+ if (message->FlushEvent != nullptr)
+#else
+ if (message->FlushEvent)
+#endif // defined(_WIN32)
+ {
+ // Signal it to wake up the waiting Flush() call.
+#if defined(_WIN32)
+ ::SetEvent(message->FlushEvent);
+#else
+ // we are already locked here...
+ WorkerCv.notify_one();
+#endif // defined(_WIN32)
+ } else {
+ std::size_t timestampLength =
+ GetTimestamp(HeaderBuffer, sizeof(HeaderBuffer), message->Time);
+ const char* subsystemName = message->SubsystemName.Get(); // a.k.a. channel name.
+ const char* messageBuffer = message->Buffer.c_str();
+ const Level level = message->MessageLogLevel;
+
+ // Construct header on top of timestamp buffer
+ AppendHeader(
+ HeaderBuffer + timestampLength,
+ sizeof(HeaderBuffer) - timestampLength,
+ level,
+ subsystemName);
+
+ // Write the output to potentially each output plugin. There's some logic below to check
+ // for channel writability to output plugins. The large majority of the time the cost of
+ // the checks will amount to two hash-table lookups of keys that aren't present. For the
+ // rare cases that they are present, a second lookup into the hash table for a short
+ // string occurs.
+ auto itC = ChannelOutputPluginFiltering.find(subsystemName);
+
+ for (auto& pluginIt : Plugins) {
+ const char* pluginName = pluginIt->GetUniquePluginName();
+ bool channelWritesToPlugin =
+ (itC == ChannelOutputPluginFiltering.end()); // Typically true.
+
+ if (!channelWritesToPlugin) { // 99% of the time we won't need to execute this block.
+ // In this case the channel filtering map has a customized set of output
+ // plugins to write to, which are a subset of the entire set.
+ channelWritesToPlugin = (itC->second.find(pluginName) != itC->second.end());
+ }
+
+ // If the channel is set to write to the output plugin, then let's check to see
+ // if the output plugin enables writes from the channel.
+ if (channelWritesToPlugin) { // Typically true.
+ auto itO = OutputPluginChannelFiltering.find(pluginName);
+ bool pluginAllowsChannel =
+ (itO == OutputPluginChannelFiltering.end()); // Typically true.
+
+ if (!pluginAllowsChannel) { // 99% of the time we won't need to execute this block.
+ // In this case the output filtering map has a customized set of channels
+ // it allows writing from.
+ pluginAllowsChannel = (itO->second.find(subsystemName) != itO->second.end());
+ }
+
+ if (pluginAllowsChannel)
+ pluginIt->Write(level, subsystemName, HeaderBuffer, messageBuffer);
+ }
+ }
+ }
+
+ next = message->Next;
+ delete message;
+ }
+ }
+}
+
+void OutputWorker::FlushDbgViewLogImmediately(
+ const char* subsystemName,
+ Level messageLogLevel,
+ const char* stream) {
+ static const int TempBufferBytes = 1024; // 1 KiB
+ char HeaderBuffer[TempBufferBytes];
+
+ // Get timestamp string
+ int timestampLength = GetTimestamp(HeaderBuffer, TempBufferBytes);
+ if (timestampLength <= 0) {
+ LOGGING_DEBUG_BREAK();
+ return; // Maybe bug in timestamp code?
+ }
+
+ // Construct log header on top of timestamp buffer
+ AppendHeader(
+ HeaderBuffer + timestampLength,
+ sizeof(HeaderBuffer) - timestampLength,
+ messageLogLevel,
+ subsystemName);
+
+ // Build up a single string to send to OutputDebugStringA so it
+ // all appears on the same line in DbgView.
+ std::stringstream ss;
+ ss << HeaderBuffer << stream << "\n";
+
+#if defined(_WIN32)
+ ::OutputDebugStringA(ss.str().c_str());
+#else
+ fputs(ss.str().c_str(), stderr);
+#endif
+}
+
+static void SetThreadName(const char* name) {
+#if defined(_WIN32)
+ DWORD threadId = ::GetCurrentThreadId();
+
+// http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
+#pragma pack(push, 8)
+ struct THREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000
+ LPCSTR szName; // Pointer to name (in user address space)
+ DWORD dwThreadID; // Thread ID (-1 for caller thread)
+ DWORD dwFlags; // Reserved for future use; must be zero
+ };
+ union TNIUnion {
+ THREADNAME_INFO tni;
+ ULONG_PTR upArray[4];
+ };
+#pragma pack(pop)
+
+ TNIUnion tniUnion = {{0x1000, name, threadId, 0}};
+
+ __try {
+ RaiseException(0x406D1388, 0, ARRAYSIZE(tniUnion.upArray), tniUnion.upArray);
+ } __except (
+ GetExceptionCode() == 0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER) {
+ return;
+ }
+#elif defined(__APPLE__)
+ pthread_setname_np(name);
+#else
+ pthread_setname_np(pthread_self(), name);
+#endif
+}
+
+void OutputWorker::WorkerThreadEntrypoint() {
+#if !defined(_WIN32)
+ std::unique_lock<std::mutex> lock(WorkerCvMutex);
+ WorkerCv.notify_one();
+#endif // !defined(_WIN32)
+ SetThreadName("LoggingOutputWorker");
+
+#if defined(_WIN32)
+ // Lower the priority for logging.
+ ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_LOWEST);
+#else
+ // Other desktop platforms (e.g. Linux, OSX) don't let you set thread priorities.
+#endif // defined(_WIN32)
+
+#if defined(_WIN32)
+ while (!WorkerTerminator.IsTerminated()) {
+ if (WorkerTerminator.WaitOn(WorkerWakeEvent.Get())) {
+ ProcessQueuedMessages();
+ }
+ }
+#else
+ while (!Terminated.load()) {
+ WorkerCv.wait(lock);
+ ProcessQueuedMessages();
+ }
+#endif // defined(_WIN32)
+}
+
+void OutputWorker::Write(
+ const char* subsystemName,
+ Level messageLogLevel,
+ const char* stream,
+ bool relogged,
+ WriteOption option) {
+ bool dropped = false; // Flag indicates if the message was dropped due to queue overrun
+ bool needToWakeWorkerThread = false; // Flag indicates if we need to wake the worker thread
+
+ // Add work to queue.
+ {
+ Locker locker(WorkQueueLock);
+
+ // Check to see if this message looks like it's repeat message which we want to aggregate
+ // in order to avoid log spam of the same similar message repeatedly.
+ if (RepeatedMessageManagerInstance.HandleMessage(subsystemName, messageLogLevel, stream) ==
+ RepeatedMessageManager::HandleResult::Aggregated) {
+ return;
+ }
+
+ if (option != WriteOption::DangerouslyIgnoreQueueLimit && WorkQueueSize >= WorkQueueLimit) {
+ // Record drop
+ WorkQueueOverrun++;
+ dropped = true;
+ } else {
+ // Add queued buffer to the end of the work queue
+ LogTime time = GetCurrentLogTime();
+ WorkQueueAdd(new QueuedLogMessage(subsystemName, messageLogLevel, stream, time));
+
+#if defined(_WIN32)
+ // Only need to wake the worker thread on the first message
+ // The SetEvent() call takes 6 microseconds or so
+ if (WorkQueueSize <= 1) {
+ needToWakeWorkerThread = true;
+ }
+#else
+ // WorkerCv.notify_one() will only wake the worker if it is waiting
+ needToWakeWorkerThread = true;
+#endif // defined(_WIN32)
+ }
+ }
+
+ if (!dropped && needToWakeWorkerThread) {
+ // Wake the worker thread
+#if defined(_WIN32)
+ ::SetEvent(WorkerWakeEvent.Get());
+#else
+ WorkerCv.notify_one();
+#endif
+ }
+
+ // If this is the first time logging this message,
+ if (!relogged) {
+ // If we are in a debugger,
+ if (IsInDebugger) {
+ FlushDbgViewLogImmediately(subsystemName, messageLogLevel, stream);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// QueuedLogMessage
+
+OutputWorker::QueuedLogMessage::QueuedLogMessage(
+ const char* subsystemName,
+ Level messageLogLevel,
+ const char* stream,
+ const LogTime& time)
+ : SubsystemName(subsystemName),
+ MessageLogLevel(messageLogLevel),
+ Buffer(stream),
+ Time(time),
+ Next(nullptr),
+#if defined(_WIN32)
+ FlushEvent(nullptr)
+#else
+ FlushEvent(false)
+#endif // defined(_WIN32)
+{
+}
+
+void Channel::registerNode() {
+ Node.SubsystemName = SubsystemName.Get();
+ Node.Level = &MinimumOutputLevel;
+ Node.UserOverrodeMinimumOutputLevel = &UserOverrodeMinimumOutputLevel;
+
+ ConfiguratorRegister(&Node);
+}
+
+Channel::Channel(const char* nameString)
+ : SubsystemName(nameString),
+ MinimumOutputLevel((Log_Level_t)DefaultMinimumOutputLevel),
+ UserOverrodeMinimumOutputLevel(false) {
+ registerNode();
+}
+
+Channel::Channel(const Channel& other)
+ : SubsystemName(other.SubsystemName),
+ MinimumOutputLevel(other.MinimumOutputLevel),
+ Prefix(other.GetPrefix()),
+ UserOverrodeMinimumOutputLevel(other.UserOverrodeMinimumOutputLevel) {
+ registerNode();
+}
+
+Channel::~Channel() {
+ // ...We can get modified from other threads here.
+ ConfiguratorUnregister(&Node);
+}
+
+std::string Channel::GetPrefix() const {
+ Locker locker(PrefixLock);
+ return Prefix;
+}
+
+void Channel::SetPrefix(const std::string& prefix) {
+ Locker locker(PrefixLock);
+ Prefix = prefix;
+}
+
+void Channel::SetMinimumOutputLevel(Level newLevel) {
+ SetMinimumOutputLevelNoSave(newLevel);
+
+ ConfiguratorOnChannelLevelChange(SubsystemName.Get(), MinimumOutputLevel);
+}
+
+void Channel::SetMinimumOutputLevelNoSave(Level newLevel) {
+ MinimumOutputLevel = (Log_Level_t)newLevel;
+ UserOverrodeMinimumOutputLevel = true;
+}
+
+Level Channel::GetMinimumOutputLevel() const {
+ return (Level)MinimumOutputLevel;
+}
+
+int Channel::GetPrintfLength(const char* format, ...) {
+ va_list argList;
+ va_start(argList, format);
+ int size = GetPrintfLengthV(format, argList);
+ va_end(argList);
+ return size;
+}
+
+//-----------------------------------------------------------------------------
+// Conversion functions
+
+template <>
+void LogStringize(LogStringBuffer& buffer, const wchar_t* const& first) {
+#ifdef _WIN32
+
+ // Use Windows' optimized multi-byte UTF8 conversion function for performance.
+ // Returns the number of bytes used by the conversion, including the null terminator
+ // since -1 is passed in for the input string length.
+ // Returns 0 on failure.
+ int bytesUsed = ::WideCharToMultiByte(
+ CP_ACP, // Default code page
+ 0, // Default flags
+ first, // String to convert
+ -1, // Unknown string length
+ nullptr, // Null while checking length of buffer
+ 0, // 0 to request the buffer size required
+ nullptr, // Default default character
+ nullptr); // Ignore whether or not default character was used
+ // Setting the last two arguments to null is documented to execute faster via MSDN.
+
+ // If the function succeeded,
+ if (bytesUsed > 0) {
+ // Avoid allocating memory if the string is fairly small.
+ char stackBuffer[128];
+ char* dynamicBuffer = nullptr;
+ char* convertedString = stackBuffer;
+ if (bytesUsed > (int)sizeof(stackBuffer)) {
+ // (defensive coding) Add 8 bytes of slop in case of API bugs.
+ dynamicBuffer = new char[bytesUsed + 8];
+ convertedString = dynamicBuffer;
+ }
+
+ int charsWritten = ::WideCharToMultiByte(
+ CP_ACP, // Default code page
+ 0, // Default flags
+ first, // String to convert
+ -1, // Unknown string length
+ convertedString, // Output buffer
+ bytesUsed, // Request the same number of bytes
+ nullptr, // Default default character
+ nullptr); // Ignore whether or not default character was used
+ // Setting the last two arguments to null is documented to execute faster via MSDN.
+
+ if (charsWritten > 0) {
+ // Append the converted string.
+ buffer.Stream << convertedString;
+ }
+
+ delete[] dynamicBuffer;
+ }
+
+#else
+
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
+ buffer.Stream << converter.to_bytes(first);
+
+#endif // _WIN32
+}
+
+//-----------------------------------------------------------------------------
+// ConfiguratorPlugin
+
+ConfiguratorPlugin::ConfiguratorPlugin() {}
+
+ConfiguratorPlugin::~ConfiguratorPlugin() {}
+
+//-----------------------------------------------------------------------------
+// Log Configurator
+
+Configurator::Configurator() : GlobalMinimumLogLevel((Log_Level_t)Level::Debug), Plugin(nullptr) {}
+
+Configurator* Configurator::GetInstance() {
+ static Configurator configurator;
+ return &configurator;
+}
+
+Configurator::~Configurator() {}
+
+void Configurator::SetGlobalMinimumLogLevel(Level level) {
+ Locker locker(OutputWorker::GetInstance()->GetChannelsLock());
+
+ GlobalMinimumLogLevel = (Log_Level_t)level;
+
+ for (ChannelNode* channelNode = ChannelNodeHead; channelNode; channelNode = channelNode->Next) {
+ *(channelNode->Level) = (ovrlog::Log_Level_t)level;
+ }
+}
+
+// Should be locked already when calling this function!
+void Configurator::RestoreChannelLogLevel(const char* channelName) {
+ Level level = (Level)GlobalMinimumLogLevel;
+
+ // Look up the log level for this channel if we can
+ if (Plugin) {
+ Plugin->RestoreChannelLevel(channelName, level);
+ }
+
+ const std::string stdChannelName(channelName);
+
+ SetChannelLevelNoLock(stdChannelName, level, false);
+}
+
+void Configurator::RestoreChannelLogLevel(ChannelNode* channelNode) {
+ Level level = (Level)GlobalMinimumLogLevel;
+
+ // Look up the log level for this channel if we can
+ if (Plugin) {
+ Plugin->RestoreChannelLevel(channelNode->SubsystemName, level);
+ }
+
+ // Don't undo user calls to SetMinimumOutputLevelNoSave()
+ if (*(channelNode->UserOverrodeMinimumOutputLevel) == false) {
+ *(channelNode->Level) = (Log_Level_t)level;
+ }
+}
+
+void Configurator::RestoreAllChannelLogLevels() {
+ Locker locker(OutputWorker::GetInstance()->GetChannelsLock());
+ RestoreAllChannelLogLevelsNoLock();
+}
+
+void Configurator::RestoreAllChannelLogLevelsNoLock() {
+ for (ChannelNode* channelNode = ChannelNodeHead; channelNode; channelNode = channelNode->Next) {
+ RestoreChannelLogLevel(channelNode->SubsystemName);
+ }
+}
+
+void Configurator::SetPlugin(std::shared_ptr<ConfiguratorPlugin> plugin) {
+ Locker locker(OutputWorker::GetInstance()->GetChannelsLock());
+
+ Plugin = plugin;
+
+ for (ChannelNode* channelNode = ChannelNodeHead; channelNode; channelNode = channelNode->Next) {
+ RestoreChannelLogLevel(channelNode->SubsystemName);
+ }
+}
+void Configurator::GetChannels(std::vector<std::pair<std::string, Level>>& channels) {
+ channels.clear();
+
+ Locker locker(OutputWorker::GetInstance()->GetChannelsLock());
+
+ for (ChannelNode* channelNode = ChannelNodeHead; channelNode; channelNode = channelNode->Next) {
+ channels.push_back(
+ std::make_pair(std::string(channelNode->SubsystemName), (Level) * (channelNode->Level)));
+ }
+}
+void Configurator::SetChannelLevel(const std::string& channelName, Level level) {
+ Locker locker(OutputWorker::GetInstance()->GetChannelsLock());
+ SetChannelLevelNoLock(channelName, level, true);
+}
+
+// Should be locked already when calling this function!
+void Configurator::SetChannelLevelNoLock(
+ const std::string& channelName,
+ Level level,
+ bool overrideUser) {
+ for (ChannelNode* channelNode = ChannelNodeHead; channelNode; channelNode = channelNode->Next) {
+ if (std::string(channelNode->SubsystemName) == channelName) {
+ if (*(channelNode->UserOverrodeMinimumOutputLevel) == false || overrideUser) {
+ *(channelNode->Level) = (Log_Level_t)level;
+
+ // Purposely no break, channels may have duplicate names
+ }
+ }
+ }
+}
+
+void Configurator::SetOutputPluginChannels(
+ const char* outputPluginName,
+ const std::vector<std::string>& channelNames) {
+ OutputWorker::GetInstance()->SetOutputPluginChannels(outputPluginName, channelNames);
+}
+
+void Configurator::SetChannelOutputPlugins(
+ const char* channelName,
+ const std::vector<std::string>& outputPluginNames) {
+ OutputWorker::GetInstance()->SetChannelOutputPlugins(channelName, outputPluginNames);
+}
+
+void Configurator::SetChannelSingleOutput(const char* channelName, const char* outputPluginName) {
+ OutputWorker::GetInstance()->SetChannelSingleOutput(channelName, outputPluginName);
+}
+
+void Configurator::OnChannelLevelChange(const char* channelName, Log_Level_t minimumOutputLevel) {
+ Locker locker(OutputWorker::GetInstance()->GetChannelsLock());
+
+ if (Plugin) {
+ // Save channel level
+ Plugin->SaveChannelLevel(channelName, (Level)minimumOutputLevel);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ErrorSilencer
+#if defined(_MSC_VER)
+#if (_MSC_VER < 1300)
+__declspec(thread) int ThreadErrorSilencedOptions = 0;
+#else
+#pragma data_seg(".tls$")
+__declspec(thread) int ThreadErrorSilencedOptions = 0;
+#pragma data_seg(".rwdata")
+#endif
+#else
+thread_local int ThreadErrorSilencedOptions = 0;
+#endif
+
+int ErrorSilencer::GetSilenceOptions() {
+ return ThreadErrorSilencedOptions;
+}
+
+ErrorSilencer::ErrorSilencer(int options) : Options(options) {
+ Silence();
+}
+
+ErrorSilencer::~ErrorSilencer() {
+ Unsilence();
+}
+
+void ErrorSilencer::Silence() {
+ // We do not currently support recursive silencers
+ assert(!GetSilenceOptions());
+ ThreadErrorSilencedOptions = Options;
+}
+
+void ErrorSilencer::Unsilence() {
+ // We do not currently support recursive silencers
+ assert(GetSilenceOptions());
+ ThreadErrorSilencedOptions = 0;
+}
+
+} // namespace ovrlog
+
+#ifdef OVR_STRINGIZE
+#error "This code must remain independent of LibOVR"
+#endif
diff --git a/ovr_sdk_win_23.0.0/Logging/src/Logging_OutputPlugins.cpp b/ovr_sdk_win_23.0.0/Logging/src/Logging_OutputPlugins.cpp
new file mode 100644
index 0000000..3b6ccc2
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/src/Logging_OutputPlugins.cpp
@@ -0,0 +1,289 @@
+/************************************************************************************
+
+Filename : Logging_OutputPlugins.cpp
+Content : Logging output plugins
+Created : Oct 26, 2015
+Authors : Chris Taylor
+
+Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
+
+Licensed under the Oculus Master SDK License Version 1.0 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+https://developer.oculus.com/licenses/oculusmastersdk-1.0
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Logging/Logging_OutputPlugins.h"
+#include "Logging/Logging_Tools.h"
+
+#include <time.h>
+#include <iostream>
+
+namespace ovrlog {
+
+//-----------------------------------------------------------------------------
+// Console
+
+OutputConsole::OutputConsole(bool useStdio) : UseStdio(useStdio) {}
+
+OutputConsole::~OutputConsole() {}
+
+void OutputConsole::SetStdioUsage(bool enable) {
+ UseStdio = enable;
+}
+
+const char* OutputConsole::GetUniquePluginName() {
+ return "DefaultOutputConsole";
+}
+
+void OutputConsole::Write(
+ Level level,
+ const char* /*subsystem*/,
+ const char* header,
+ const char* utf8msg) {
+#if defined(_WIN32)
+ HANDLE hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
+
+ // Save current console attributes
+ CONSOLE_SCREEN_BUFFER_INFO bufInfo{};
+ BOOL oldAttrValid = ::GetConsoleScreenBufferInfo(hConsole, &bufInfo);
+ WORD attr = 0;
+
+ switch (level) {
+ case Level::Disabled: // Shouldn't occur, but we handle for consistency.
+ attr |= FOREGROUND_BLUE;
+ break;
+ case Level::Trace:
+ attr |= FOREGROUND_BLUE | FOREGROUND_RED;
+ break;
+ case Level::Debug:
+ attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
+ break;
+ case Level::Info:
+ attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
+ break;
+ case Level::Warning:
+ attr |= FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY;
+ break;
+ case Level::Error:
+ attr |= FOREGROUND_RED | FOREGROUND_INTENSITY;
+ break;
+ default:
+ break;
+ }
+ static_assert(Level::Count == static_cast<Level>(6), "Needs updating");
+
+ ::SetConsoleTextAttribute(hConsole, attr & ~FOREGROUND_INTENSITY);
+
+ if (UseStdio)
+ std::cout << header;
+ else {
+ DWORD dwCount;
+ DWORD headerLength = (DWORD)strlen(header);
+
+ WriteConsoleA(hConsole, header, headerLength, &dwCount, nullptr);
+ }
+
+ if ((attr & FOREGROUND_INTENSITY) != 0) {
+ ::SetConsoleTextAttribute(hConsole, attr);
+ }
+
+ if (UseStdio)
+ std::cout << utf8msg << std::endl;
+ else {
+ DWORD dwCount;
+ DWORD msgLength = (DWORD)strlen(utf8msg);
+
+ WriteConsoleA(hConsole, utf8msg, msgLength, &dwCount, nullptr);
+ WriteConsoleA(hConsole, "\n", 1, &dwCount, nullptr);
+ }
+
+ // Restore original attributes, if saved
+ if (TRUE == oldAttrValid) {
+ ::SetConsoleTextAttribute(hConsole, bufInfo.wAttributes);
+ }
+#else
+
+ FILE* output = stdout;
+
+ switch (level) {
+ case Level::Trace:
+ break;
+ case Level::Debug:
+ break;
+ case Level::Info:
+ break;
+ case Level::Warning:
+ output = stderr;
+ fprintf(output, "\e[38;5;255m\e[48;5;208m");
+ break;
+ case Level::Error:
+ output = stderr;
+ fprintf(output, "\e[38;5;255m\e[48;5;196m");
+ break;
+ default:
+ break;
+ }
+
+ fprintf(output, "%s", header);
+ fprintf(output, "\e[0m ");
+
+ fprintf(output, "%s\n", utf8msg);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// System Application Event Log
+
+#ifndef OVR_SYSLOG_NAME
+#define OVR_SYSLOG_NAME L"OculusVR"
+#endif // OVR_SYSLOG_NAME
+
+OutputEventLog::OutputEventLog() : hEventSource(nullptr), MinReportEventLevel(Level::Error) {
+#if defined(_WIN32)
+ hEventSource = ::RegisterEventSourceW(
+ nullptr, // No server name
+ OVR_SYSLOG_NAME); // Syslog event source name
+#else
+ // To do.
+#endif
+
+ if (!hEventSource) {
+ // Unable to register event source
+ LOGGING_DEBUG_BREAK();
+ }
+}
+
+OutputEventLog::~OutputEventLog() {}
+
+const char* OutputEventLog::GetUniquePluginName() {
+ return "DefaultOutputEventLog";
+}
+
+void OutputEventLog::Write(
+ Level level,
+ const char* subsystem,
+ const char* header,
+ const char* utf8msg) {
+ (void)subsystem; // unused
+
+ if (level < MinReportEventLevel) {
+ return;
+ }
+
+ if (!hEventSource) {
+ return;
+ }
+
+#if defined(_WIN32)
+ WORD mType = 0;
+
+ switch (level) {
+ default:
+ case Level::Disabled:
+ case Level::Trace:
+ case Level::Debug:
+ case Level::Info:
+ mType = EVENTLOG_INFORMATION_TYPE;
+ return; // Do not log at this level.
+
+ case Level::Warning:
+ mType = EVENTLOG_WARNING_TYPE;
+ break; // Log at this level.
+
+ case Level::Error:
+ mType = EVENTLOG_ERROR_TYPE;
+ break; // Log at this level.
+ }
+ static_assert(Level::Count == static_cast<Level>(6), "Needs updating");
+
+ const size_t MAX_REPORT_EVENT_A_LEN = 31839;
+
+ std::vector<const char*> cstrVtr;
+ cstrVtr.push_back(header);
+ std::vector<std::string> splitStrings;
+ size_t longStringLen = strlen(utf8msg);
+ if (longStringLen >= MAX_REPORT_EVENT_A_LEN) {
+ std::string longStr(utf8msg);
+ for (size_t x = 0; x < longStringLen; x += MAX_REPORT_EVENT_A_LEN) {
+ size_t remaining = longStringLen - x;
+ std::string thisSubStr = longStr.substr(
+ x, (remaining > MAX_REPORT_EVENT_A_LEN) ? MAX_REPORT_EVENT_A_LEN : remaining);
+ splitStrings.push_back(thisSubStr);
+ }
+
+ for (size_t i = 0; i < splitStrings.size(); i++) {
+ cstrVtr.push_back(splitStrings[i].c_str());
+ }
+ } else {
+ cstrVtr.push_back(utf8msg);
+ }
+
+ if (!::ReportEventA(
+ hEventSource, // Event source
+ mType, // Event log level
+ 0, // Default category
+ 0, // Default event id
+ nullptr, // No security identifier
+ (WORD)cstrVtr.size(), // Number of strings
+ 0, // No bytes of event-specific binary data attached
+ &cstrVtr[0], // String array
+ nullptr)) // No binary data attached
+ {
+ // Unable to write event log
+ LOGGING_DEBUG_BREAK();
+ }
+#else
+ (void)header;
+ (void)utf8msg;
+ // To do.
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// DbgView
+
+OutputDbgView::OutputDbgView() {}
+
+OutputDbgView::~OutputDbgView() {}
+
+const char* OutputDbgView::GetUniquePluginName() {
+ return "DefaultOutputDbgView";
+}
+
+void OutputDbgView::Write(
+ Level level,
+ const char* subsystem,
+ const char* header,
+ const char* utf8msg) {
+ (void)subsystem; // unused
+ (void)level; // unused
+
+ // Build up a single string to send to OutputDebugStringA so it
+ // all appears on the same line in DbgView.
+ std::stringstream ss;
+ ss << header << utf8msg << "\n";
+
+#if defined(_WIN32)
+ ::OutputDebugStringA(ss.str().c_str());
+#else
+ fputs(ss.str().c_str(), stderr);
+#endif
+}
+
+} // namespace ovrlog
+
+#ifdef OVR_STRINGIZE
+#error "This code must remain independent of LibOVR"
+#endif
diff --git a/ovr_sdk_win_23.0.0/Logging/src/Logging_Tools.cpp b/ovr_sdk_win_23.0.0/Logging/src/Logging_Tools.cpp
new file mode 100644
index 0000000..ad3d03f
--- /dev/null
+++ b/ovr_sdk_win_23.0.0/Logging/src/Logging_Tools.cpp
@@ -0,0 +1,255 @@
+/************************************************************************************
+
+Filename : Logging_Tools.cpp
+Content : Tools for Logging
+Created : Oct 26, 2015
+Authors : Chris Taylor
+
+Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
+
+Licensed under the Oculus Master SDK License Version 1.0 (the "License");
+you may not use the Oculus VR Rift SDK except in compliance with the License,
+which is provided at the time of installation or download, or which
+otherwise accompanies this software in either electronic or hard copy form.
+
+You may obtain a copy of the License at
+
+https://developer.oculus.com/licenses/oculusmastersdk-1.0
+
+Unless required by applicable law or agreed to in writing, the Oculus VR SDK
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4530) // C++ exception handler used, but unwind semantics are not enabled
+#endif
+
+#include "Logging/Logging_Tools.h"
+
+#include <assert.h>
+#include <time.h>
+
+#if defined(__APPLE__)
+#include <libproc.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+#include <locale>
+#endif
+
+namespace ovrlog {
+
+#if defined(_WIN32)
+//-----------------------------------------------------------------------------
+// Terminator
+
+Terminator::Terminator() : Terminated(false), TerminateEvent() {}
+
+Terminator::~Terminator() {}
+
+bool Terminator::Initialize() {
+ Terminated = false;
+
+ if (TerminateEvent.IsValid()) {
+ ::ResetEvent(TerminateEvent.Get());
+ return true;
+ }
+
+ TerminateEvent = ::CreateEventW(nullptr, TRUE, FALSE, nullptr);
+
+ return TerminateEvent.IsValid();
+}
+
+void Terminator::Terminate() {
+ Terminated = true;
+
+ if (TerminateEvent.IsValid()) {
+ ::SetEvent(TerminateEvent.Get());
+ }
+}
+
+// Returns true if the event signaled and false on termination.
+bool Terminator::WaitOn(OvrLogHandle hEvent, uint32_t timeoutMsec) {
+ if (Terminated || !TerminateEvent.IsValid())
+ return false;
+
+ HANDLE events[2] = {hEvent, TerminateEvent.Get()};
+
+ DWORD result = ::WaitForMultipleObjects(2, events, FALSE, timeoutMsec);
+
+ if (Terminated)
+ return false;
+
+ if (result == WAIT_TIMEOUT)
+ return false;
+ if (result == WAIT_OBJECT_0)
+ return true;
+
+ return false;
+}
+
+// Returns true if the sleep interval exceeded or false on termination.
+bool Terminator::WaitSleep(int milliseconds) {
+ if (Terminated || !TerminateEvent.IsValid())
+ return false;
+
+ ::WaitForSingleObject(TerminateEvent.Get(), milliseconds); // Ignore return value
+
+ return !Terminated;
+}
+#endif // defined(_WIN32)
+
+//-----------------------------------------------------------------------------
+// Lock
+
+Lock::Lock()
+ :
+#if defined(_WIN32)
+ cs {
+}
+#else
+ m()
+#endif
+{
+#if defined(_WIN32)
+ static const DWORD kSpinCount = 1000;
+ ::InitializeCriticalSectionAndSpinCount(&cs, kSpinCount);
+#endif
+}
+
+Lock::~Lock() {
+#if defined(_WIN32)
+ ::DeleteCriticalSection(&cs);
+#endif
+}
+
+bool Lock::TryEnter() {
+#if defined(_WIN32)
+ return ::TryEnterCriticalSection(&cs) != FALSE;
+#else
+ return m.try_lock();
+#endif
+}
+
+void Lock::Enter() {
+#if defined(_WIN32)
+ ::EnterCriticalSection(&cs);
+#else
+ m.lock();
+#endif
+}
+
+void Lock::Leave() {
+#if defined(_WIN32)
+ ::LeaveCriticalSection(&cs);
+#else
+ m.unlock();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Locker
+
+Locker::Locker(Lock* lock) : TheLock(lock) {
+ if (TheLock)
+ TheLock->Enter();
+}
+
+Locker::Locker(Lock& lock) : TheLock(&lock) {
+ if (TheLock)
+ TheLock->Enter();
+}
+
+Locker::~Locker() {
+ Clear();
+}
+
+bool Locker::TrySet(Lock* lock) {
+ Clear();
+
+ if (!lock || !lock->TryEnter())
+ return false;
+
+ TheLock = lock;
+ return true;
+}
+
+bool Locker::TrySet(Lock& lock) {
+ return TrySet(&lock);
+}
+
+void Locker::Set(Lock* lock) {
+ Clear();
+
+ if (lock) {
+ lock->Enter();
+ TheLock = lock;
+ }
+}
+
+void Locker::Set(Lock& lock) {
+ return Set(&lock);
+}
+
+void Locker::Clear() {
+ if (TheLock) {
+ TheLock->Leave();
+ TheLock = nullptr;
+ }
+}
+
+#if defined(_WIN32)
+//-----------------------------------------------------------------------------
+// AutoHandle
+
+AutoHandle::AutoHandle(OvrLogHandle handle) : TheHandle(handle) {}
+
+AutoHandle::~AutoHandle() {
+ Clear();
+}
+
+void AutoHandle::operator=(OvrLogHandle handle) {
+ Clear();
+ TheHandle = handle;
+}
+
+void AutoHandle::Clear() {
+ if (TheHandle) {
+ ::CloseHandle(TheHandle);
+ TheHandle = nullptr;
+ }
+}
+#endif // defined(_WIN32)
+
+//-----------------------------------------------------------------------------
+// Tools
+
+bool IsDebuggerAttached() {
+#if defined(_WIN32)
+ return ::IsDebuggerPresent() != FALSE;
+#elif defined(__APPLE__)
+ int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
+ struct kinfo_proc info;
+ size_t size = sizeof(info);
+
+ info.kp_proc.p_flag = 0;
+ sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0);
+
+ return ((info.kp_proc.p_flag & P_TRACED) != 0);
+
+#elif defined(PT_TRACE_ME) && !defined(__android__)
+ return (ptrace(PT_TRACE_ME, 0, 1, 0) < 0);
+#else
+ // We have some platform-specific code elsewhere.
+ return false;
+#endif
+}
+
+} // namespace ovrlog
+
+#ifdef OVR_STRINGIZE
+#error "This code must remain independent of LibOVR"
+#endif