diff options
Diffstat (limited to 'ovr_sdk_win_23.0.0/Logging')
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 |