summaryrefslogtreecommitdiffstats
path: root/external/optick/optick_core.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--external/optick/optick_core.cpp1657
1 files changed, 1657 insertions, 0 deletions
diff --git a/external/optick/optick_core.cpp b/external/optick/optick_core.cpp
new file mode 100644
index 0000000..1d533d0
--- /dev/null
+++ b/external/optick/optick_core.cpp
@@ -0,0 +1,1657 @@
+#include "optick.config.h"
+
+#if USE_OPTICK
+
+#include "optick_core.h"
+#include "optick_server.h"
+
+#include <algorithm>
+#include <fstream>
+
+//////////////////////////////////////////////////////////////////////////
+// Start of the Platform-specific stuff
+//////////////////////////////////////////////////////////////////////////
+#if defined(OPTICK_MSVC)
+#include "optick_core.win.h"
+#endif
+#if defined(OPTICK_LINUX)
+#include "optick_core.linux.h"
+#endif
+#if defined(OPTICK_OSX)
+#include "optick_core.macos.h"
+#endif
+#if defined(OPTICK_PS4)
+#include "optick_core.ps4.h"
+#endif
+//////////////////////////////////////////////////////////////////////////
+// End of the Platform-specific stuff
+//////////////////////////////////////////////////////////////////////////
+
+extern "C" Optick::EventData* NextEvent()
+{
+ if (Optick::EventStorage* storage = Optick::Core::storage)
+ {
+ return &storage->NextEvent();
+ }
+
+ return nullptr;
+}
+
+namespace Optick
+{
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void* (*Memory::allocate)(size_t) = operator new;
+void (*Memory::deallocate)(void* p) = operator delete;
+std::atomic<uint64_t> Memory::memAllocated;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+uint64_t MurmurHash64A(const void * key, int len, uint64_t seed)
+{
+ const uint64_t m = 0xc6a4a7935bd1e995;
+ const int r = 47;
+
+ uint64_t h = seed ^ (len * m);
+
+ const uint64_t * data = (const uint64_t *)key;
+ const uint64_t * end = data + (len / 8);
+
+ while (data != end)
+ {
+ uint64_t k = *data++;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h ^= k;
+ h *= m;
+ }
+
+ const unsigned char * data2 = (const unsigned char*)data;
+
+ switch (len & 7)
+ {
+ case 7: h ^= uint64_t(data2[6]) << 48;
+ case 6: h ^= uint64_t(data2[5]) << 40;
+ case 5: h ^= uint64_t(data2[4]) << 32;
+ case 4: h ^= uint64_t(data2[3]) << 24;
+ case 3: h ^= uint64_t(data2[2]) << 16;
+ case 2: h ^= uint64_t(data2[1]) << 8;
+ case 1: h ^= uint64_t(data2[0]);
+ h *= m;
+ };
+
+ h ^= h >> r;
+ h *= m;
+ h ^= h >> r;
+
+ return h;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+uint64_t StringHash::CalcHash(const char* str)
+{
+ return MurmurHash64A(str, (int)strlen(str), 0);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Base 64
+// https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static inline bool is_base64(unsigned char c) {
+ return (isalnum(c) || (c == '+') || (c == '/'));
+}
+string base64_decode(string const& encoded_string) {
+ static string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int in_len = (int)encoded_string.size();
+ int i = 0;
+ int j = 0;
+ int in_ = 0;
+ unsigned char char_array_4[4], char_array_3[3];
+ string ret;
+
+ while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+ char_array_4[i++] = encoded_string[in_]; in_++;
+ if (i == 4) {
+ for (i = 0; i < 4; i++)
+ char_array_4[i] = (unsigned char)base64_chars.find(char_array_4[i]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (i = 0; (i < 3); i++)
+ ret += char_array_3[i];
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j < 4; j++)
+ char_array_4[j] = 0;
+
+ for (j = 0; j < 4; j++)
+ char_array_4[j] = (unsigned char)base64_chars.find(char_array_4[j]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+ }
+
+ return ret;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Get current time in milliseconds
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+int64 GetTimeMilliSeconds()
+{
+ return Platform::GetTime() * 1000 / Platform::GetFrequency();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+int64 TicksToMs(int64 ticks)
+{
+ return ticks * 1000 / Platform::GetFrequency();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+int64 TicksToUs(int64 ticks)
+{
+ return ticks * 1000000 / Platform::GetFrequency();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+template<class T>
+OutputDataStream& operator<<(OutputDataStream& stream, const TagData<T>& ob)
+{
+ return stream << ob.timestamp << ob.description->index << ob.data;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& os, const Symbol * const symbol)
+{
+ OPTICK_VERIFY(symbol, "Can't serialize NULL symbol!", return os);
+ return os << symbol->address << symbol->function << symbol->file << symbol->line;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& os, const Module& module)
+{
+ return os << module.path << (uint64)module.address << (uint64)module.size;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// VS TODO: Replace with random access iterator for MemoryPool
+template<class T, uint32 SIZE>
+void SortMemoryPool(MemoryPool<T, SIZE>& memoryPool)
+{
+ size_t count = memoryPool.Size();
+ if (count == 0)
+ return;
+
+ vector<T> memoryArray;
+ memoryArray.resize(count);
+ memoryPool.ToArray(&memoryArray[0]);
+
+ std::sort(memoryArray.begin(), memoryArray.end());
+
+ memoryPool.Clear(true);
+
+ for (const T& item : memoryArray)
+ memoryPool.Add(item);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventDescription* EventDescription::Create(const char* eventName, const char* fileName, const unsigned long fileLine, const unsigned long eventColor /*= Color::Null*/, const unsigned long filter /*= 0*/)
+{
+ return EventDescriptionBoard::Get().CreateDescription(eventName, fileName, fileLine, eventColor, filter);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventDescription* EventDescription::CreateShared(const char* eventName, const char* fileName, const unsigned long fileLine, const unsigned long eventColor /*= Color::Null*/, const unsigned long filter /*= 0*/)
+{
+ return EventDescriptionBoard::Get().CreateSharedDescription(eventName, fileName, fileLine, eventColor, filter);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventDescription::EventDescription() : name(""), file(""), line(0), color(0)
+{
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventDescription& EventDescription::operator=(const EventDescription&)
+{
+ OPTICK_FAILED("It is pointless to copy EventDescription. Please, check you logic!"); return *this;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventData* Event::Start(const EventDescription& description)
+{
+ EventData* result = nullptr;
+
+ if (EventStorage* storage = Core::storage)
+ {
+ result = &storage->NextEvent();
+ result->description = &description;
+ result->Start();
+ }
+ return result;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Event::Stop(EventData& data)
+{
+ if (EventStorage* storage = Core::storage)
+ {
+ data.Stop();
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OPTICK_INLINE PushEvent(EventStorage* pStorage, const EventDescription* description, int64_t timestampStart)
+{
+ if (EventStorage* storage = pStorage)
+ {
+ EventData& result = storage->NextEvent();
+ result.description = description;
+ result.start = timestampStart;
+ result.finish = EventTime::INVALID_TIMESTAMP;
+ storage->pushPopEventStack[storage->pushPopEventStackIndex++] = &result;
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void OPTICK_INLINE PopEvent(EventStorage* pStorage, int64_t timestampFinish)
+{
+ if (EventStorage* storage = pStorage)
+ if (storage->pushPopEventStackIndex > 0)
+ storage->pushPopEventStack[--storage->pushPopEventStackIndex]->finish = timestampFinish;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Event::Push(const char* name)
+{
+ if (EventStorage* storage = Core::storage)
+ {
+ EventDescription* desc = EventDescription::CreateShared(name);
+ Push(*desc);
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Event::Push(const EventDescription& description)
+{
+ PushEvent(Core::storage, &description, GetHighPrecisionTime());
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Event::Pop()
+{
+ PopEvent(Core::storage, GetHighPrecisionTime());
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Event::Add(EventStorage* storage, const EventDescription* description, int64_t timestampStart, int64_t timestampFinish)
+{
+ EventData& data = storage->eventBuffer.Add();
+ data.description = description;
+ data.start = timestampStart;
+ data.finish = timestampFinish;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Event::Push(EventStorage* storage, const EventDescription* description, int64_t timestampStart)
+{
+ PushEvent(storage, description, timestampStart);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Event::Pop(EventStorage* storage, int64_t timestampFinish)
+{
+ PopEvent(storage, timestampFinish);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventData* GPUEvent::Start(const EventDescription& description)
+{
+ EventData* result = nullptr;
+
+ if (EventStorage* storage = Core::storage)
+ result = storage->gpuStorage.Start(description);
+
+ return result;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void GPUEvent::Stop(EventData& data)
+{
+ if (EventStorage* storage = Core::storage)
+ storage->gpuStorage.Stop(data);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void FiberSyncData::AttachToThread(EventStorage* storage, uint64_t threadId)
+{
+ if (storage)
+ {
+ FiberSyncData& data = storage->fiberSyncBuffer.Add();
+ data.Start();
+ data.finish = EventTime::INVALID_TIMESTAMP;
+ data.threadId = threadId;
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void FiberSyncData::DetachFromThread(EventStorage* storage)
+{
+ if (storage)
+ {
+ if (FiberSyncData* syncData = storage->fiberSyncBuffer.Back())
+ {
+ syncData->Stop();
+ }
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Tag::Attach(const EventDescription& description, float val)
+{
+ if (EventStorage* storage = Core::storage)
+ if (storage->currentMode & Mode::TAGS)
+ storage->tagFloatBuffer.Add(TagFloat(description, val));
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Tag::Attach(const EventDescription& description, int32_t val)
+{
+ if (EventStorage* storage = Core::storage)
+ if (storage->currentMode & Mode::TAGS)
+ storage->tagS32Buffer.Add(TagS32(description, val));
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Tag::Attach(const EventDescription& description, uint32_t val)
+{
+ if (EventStorage* storage = Core::storage)
+ if (storage->currentMode & Mode::TAGS)
+ storage->tagU32Buffer.Add(TagU32(description, val));
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Tag::Attach(const EventDescription& description, uint64_t val)
+{
+ if (EventStorage* storage = Core::storage)
+ if (storage->currentMode & Mode::TAGS)
+ storage->tagU64Buffer.Add(TagU64(description, val));
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Tag::Attach(const EventDescription& description, float val[3])
+{
+ if (EventStorage* storage = Core::storage)
+ if (storage->currentMode & Mode::TAGS)
+ storage->tagPointBuffer.Add(TagPoint(description, val));
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Tag::Attach(const EventDescription& description, const char* val)
+{
+ if (EventStorage* storage = Core::storage)
+ if (storage->currentMode & Mode::TAGS)
+ storage->tagStringBuffer.Add(TagString(description, val));
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream & operator<<(OutputDataStream &stream, const EventDescription &ob)
+{
+ byte flags = 0;
+ return stream << ob.name << ob.file << ob.line << ob.filter << ob.color << (float)0.0f << flags;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const EventTime& ob)
+{
+ return stream << ob.start << ob.finish;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const EventData& ob)
+{
+ return stream << (EventTime)(ob) << (ob.description ? ob.description->index : (uint32)-1);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const SyncData& ob)
+{
+ return stream << (EventTime)(ob) << ob.core << ob.reason << ob.newThreadId;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const FiberSyncData& ob)
+{
+ return stream << (EventTime)(ob) << ob.threadId;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static std::mutex& GetBoardLock()
+{
+ // Initialize as static local variable to prevent problems with static initialization order
+ static std::mutex lock;
+ return lock;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventDescriptionBoard& EventDescriptionBoard::Get()
+{
+ static EventDescriptionBoard instance;
+ return instance;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const EventDescriptionList& EventDescriptionBoard::GetEvents() const
+{
+ return boardDescriptions;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventDescription* EventDescriptionBoard::CreateDescription(const char* name, const char* file /*= nullptr*/, uint32_t line /*= 0*/, uint32_t color /*= Color::Null*/, uint32_t filter /*= 0*/)
+{
+ std::lock_guard<std::mutex> lock(GetBoardLock());
+
+ size_t index = boardDescriptions.Size();
+
+ EventDescription& desc = boardDescriptions.Add();
+ desc.index = (uint32)index;
+ desc.name = name;
+ desc.file = file;
+ desc.line = line;
+ desc.color = color;
+ desc.filter = filter;
+
+ return &desc;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventDescription* EventDescriptionBoard::CreateSharedDescription(const char* name, const char* file /*= nullptr*/, uint32_t line /*= 0*/, uint32_t color /*= Color::Null*/, uint32_t filter /*= 0*/)
+{
+ StringHash nameHash(name);
+
+ std::lock_guard<std::mutex> lock(sharedLock);
+
+ std::pair<DescriptionMap::iterator, bool> cached = sharedDescriptions.insert({ nameHash, nullptr });
+
+ if (cached.second)
+ {
+ const char* nameCopy = sharedNames.Add(name, strlen(name) + 1, false);
+ cached.first->second = CreateDescription(nameCopy, file, line, color, filter);
+ }
+
+ return cached.first->second;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator << (OutputDataStream& stream, const EventDescriptionBoard& ob)
+{
+ std::lock_guard<std::mutex> lock(GetBoardLock());
+ stream << ob.GetEvents();
+ return stream;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ProcessDescription::ProcessDescription(const char* processName, ProcessID pid, uint64 key) : name(processName), processID(pid), uniqueKey(key)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ThreadDescription::ThreadDescription(const char* threadName, ThreadID tid, ProcessID pid, int32 _maxDepth /*= 1*/, int32 _priority /*= 0*/, uint32 _mask /*= 0*/)
+ : name(threadName), threadID(tid), processID(pid), maxDepth(_maxDepth), priority(_priority), mask(_mask)
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+int64_t GetHighPrecisionTime()
+{
+ return Platform::GetTime();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+int64_t GetHighPrecisionFrequency()
+{
+ return Platform::GetFrequency();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream & operator<<(OutputDataStream &stream, const SysCallData &ob)
+{
+ return stream << (const EventData&)ob << ob.threadID << ob.id;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+SysCallData& SysCallCollector::Add()
+{
+ return syscallPool.Add();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SysCallCollector::Clear()
+{
+ syscallPool.Clear(false);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool SysCallCollector::Serialize(OutputDataStream& stream)
+{
+ stream << syscallPool;
+
+ if (!syscallPool.IsEmpty())
+ {
+ syscallPool.Clear(false);
+ return true;
+ }
+
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void CallstackCollector::Add(const CallstackDesc& desc)
+{
+ if (uint64* storage = callstacksPool.TryAdd(desc.count + 3))
+ {
+ storage[0] = desc.threadID;
+ storage[1] = desc.timestamp;
+ storage[2] = desc.count;
+
+ for (uint64 i = 0; i < desc.count; ++i)
+ {
+ storage[3 + i] = desc.callstack[desc.count - i - 1];
+ }
+ }
+ else
+ {
+ uint64& item0 = callstacksPool.Add();
+ uint64& item1 = callstacksPool.Add();
+ uint64& item2 = callstacksPool.Add();
+
+ item0 = desc.threadID;
+ item1 = desc.timestamp;
+ item2 = desc.count;
+
+ for (uint64 i = 0; i < desc.count; ++i)
+ {
+ callstacksPool.Add() = desc.callstack[desc.count - i - 1];
+ }
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void CallstackCollector::Clear()
+{
+ callstacksPool.Clear(false);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool CallstackCollector::SerializeModules(OutputDataStream& stream)
+{
+ if (SymbolEngine* symEngine = Core::Get().symbolEngine)
+ {
+ stream << symEngine->GetModules();
+ return true;
+ }
+ else
+ {
+ stream << (int)0;
+ }
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool CallstackCollector::SerializeSymbols(OutputDataStream& stream)
+{
+ typedef unordered_set<uint64> SymbolSet;
+ SymbolSet symbolSet;
+
+ for (CallstacksPool::const_iterator it = callstacksPool.begin(); it != callstacksPool.end();)
+ {
+ CallstacksPool::const_iterator startIt = it;
+ OPTICK_UNUSED(startIt);
+
+ uint64 threadID = *it;
+ OPTICK_UNUSED(threadID);
+ ++it; //Skip ThreadID
+ uint64 timestamp = *it;
+ OPTICK_UNUSED
+ (timestamp);
+ ++it; //Skip Timestamp
+ uint64 count = *it;
+ count = (count & 0xFF);
+ ++it; //Skip Count
+
+ bool isBadAddrFound = false;
+
+ for (uint64 i = 0; i < count; ++i)
+ {
+ uint64 address = *it;
+ ++it;
+
+ if (address == 0)
+ {
+ isBadAddrFound = true;
+ }
+
+ if (!isBadAddrFound)
+ {
+ symbolSet.insert(address);
+ }
+ }
+ }
+
+ SymbolEngine* symEngine = Core::Get().symbolEngine;
+
+ vector<const Symbol*> symbols;
+ symbols.reserve(symbolSet.size());
+
+ size_t callstackIndex = 0;
+
+ Core::Get().DumpProgress("Resolving addresses... ");
+
+ if (symEngine)
+ {
+ for (auto it = symbolSet.begin(); it != symbolSet.end(); ++it)
+ {
+ callstackIndex++;
+
+ uint64 address = *it;
+ if (const Symbol* symbol = symEngine->GetSymbol(address))
+ {
+ symbols.push_back(symbol);
+ }
+ }
+ }
+
+ stream << symbols;
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool CallstackCollector::SerializeCallstacks(OutputDataStream& stream)
+{
+ stream << callstacksPool;
+
+ if (!callstacksPool.IsEmpty())
+ {
+ callstacksPool.Clear(false);
+ return true;
+ }
+
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool CallstackCollector::IsEmpty() const
+{
+ return callstacksPool.IsEmpty();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream & operator<<(OutputDataStream &stream, const SwitchContextDesc &ob)
+{
+ return stream << ob.timestamp << ob.oldThreadId << ob.newThreadId << ob.cpuId << ob.reason;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SwitchContextCollector::Add(const SwitchContextDesc& desc)
+{
+ switchContextPool.Add() = desc;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void SwitchContextCollector::Clear()
+{
+ switchContextPool.Clear(false);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool SwitchContextCollector::Serialize(OutputDataStream& stream)
+{
+ stream << switchContextPool;
+
+ if (!switchContextPool.IsEmpty())
+ {
+ switchContextPool.Clear(false);
+ return true;
+ }
+
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#if defined(OPTICK_MSVC)
+#define CPUID(INFO, ID) __cpuid(INFO, ID)
+#include <intrin.h>
+#elif defined(OPTICK_GCC)
+#include <cpuid.h>
+#define CPUID(INFO, ID) __cpuid(ID, INFO[0], INFO[1], INFO[2], INFO[3])
+#else
+#error Platform is not supported!
+#endif
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+string GetCPUName()
+{
+ int cpuInfo[4] = { -1 };
+ char cpuBrandString[0x40] = { 0 };
+ CPUID(cpuInfo, 0x80000000);
+ unsigned nExIds = cpuInfo[0];
+ for (unsigned i = 0x80000000; i <= nExIds; ++i)
+ {
+ CPUID(cpuInfo, i);
+ if (i == 0x80000002)
+ memcpy(cpuBrandString, cpuInfo, sizeof(cpuInfo));
+ else if (i == 0x80000003)
+ memcpy(cpuBrandString + 16, cpuInfo, sizeof(cpuInfo));
+ else if (i == 0x80000004)
+ memcpy(cpuBrandString + 32, cpuInfo, sizeof(cpuInfo));
+ }
+ return string(cpuBrandString);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Core& Core::Get()
+{
+ static Core instance;
+ return instance;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::StartCapture()
+{
+ pendingState = State::START_CAPTURE;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::StopCapture()
+{
+ pendingState = State::STOP_CAPTURE;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::CancelCapture()
+{
+ pendingState = State::CANCEL_CAPTURE;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::DumpCapture()
+{
+ pendingState = State::DUMP_CAPTURE;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::DumpProgress(const char* message)
+{
+ progressReportedLastTimestampMS = GetTimeMilliSeconds();
+
+ OutputDataStream stream;
+ stream << message;
+
+ Server::Get().Send(DataResponse::ReportProgress, stream);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::DumpEvents(EventStorage& entry, const EventTime& timeSlice, ScopeData& scope)
+{
+ if (!entry.eventBuffer.IsEmpty())
+ {
+ const EventData* rootEvent = nullptr;
+
+ entry.eventBuffer.ForEach([&](const EventData& data)
+ {
+ if (data.finish >= data.start && data.start >= timeSlice.start && timeSlice.finish >= data.finish)
+ {
+ if (!rootEvent)
+ {
+ rootEvent = &data;
+ scope.InitRootEvent(*rootEvent);
+ }
+ else if (rootEvent->finish < data.finish)
+ {
+ scope.Send();
+
+ rootEvent = &data;
+ scope.InitRootEvent(*rootEvent);
+ }
+ else
+ {
+ scope.AddEvent(data);
+ }
+ }
+ });
+
+ scope.Send();
+
+ entry.eventBuffer.Clear(false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::DumpTags(EventStorage& entry, ScopeData& scope)
+{
+ if (!entry.tagFloatBuffer.IsEmpty() ||
+ !entry.tagS32Buffer.IsEmpty() ||
+ !entry.tagU32Buffer.IsEmpty() ||
+ !entry.tagU64Buffer.IsEmpty() ||
+ !entry.tagPointBuffer.IsEmpty() ||
+ !entry.tagStringBuffer.IsEmpty())
+ {
+ OutputDataStream tagStream;
+ tagStream << scope.header.boardNumber << scope.header.threadNumber;
+ tagStream
+ << (uint32)0
+ << entry.tagFloatBuffer
+ << entry.tagU32Buffer
+ << entry.tagS32Buffer
+ << entry.tagU64Buffer
+ << entry.tagPointBuffer
+ << (uint32)0
+ << (uint32)0
+ << entry.tagStringBuffer;
+ Server::Get().Send(DataResponse::TagsPack, tagStream);
+
+ entry.ClearTags(false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::DumpThread(ThreadEntry& entry, const EventTime& timeSlice, ScopeData& scope)
+{
+ // We need to sort events for all the custom thread storages
+ if (entry.description.threadID == INVALID_THREAD_ID)
+ entry.Sort();
+
+ // Events
+ DumpEvents(entry.storage, timeSlice, scope);
+ DumpTags(entry.storage, scope);
+ OPTICK_ASSERT(entry.storage.fiberSyncBuffer.IsEmpty(), "Fiber switch events in native threads?");
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::DumpFiber(FiberEntry& entry, const EventTime& timeSlice, ScopeData& scope)
+{
+ // Events
+ DumpEvents(entry.storage, timeSlice, scope);
+
+ if (!entry.storage.fiberSyncBuffer.IsEmpty())
+ {
+ OutputDataStream fiberSynchronizationStream;
+ fiberSynchronizationStream << scope.header.boardNumber;
+ fiberSynchronizationStream << scope.header.fiberNumber;
+ fiberSynchronizationStream << entry.storage.fiberSyncBuffer;
+ Server::Get().Send(DataResponse::FiberSynchronizationData, fiberSynchronizationStream);
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventTime CalculateRange(const ThreadEntry& entry, const EventDescription* rootDescription)
+{
+ EventTime timeSlice = { INT64_MAX, INT64_MIN };
+ entry.storage.eventBuffer.ForEach([&](const EventData& data)
+ {
+ if (data.description == rootDescription)
+ {
+ timeSlice.start = std::min(timeSlice.start, data.start);
+ timeSlice.finish = std::max(timeSlice.finish, data.finish);
+ }
+ });
+ return timeSlice;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::DumpFrames(uint32 mode)
+{
+ std::lock_guard<std::recursive_mutex> lock(threadsLock);
+
+ if (frames.empty() || threads.empty())
+ return;
+
+ ++boardNumber;
+
+ DumpProgress("Generating summary...");
+
+ GenerateCommonSummary();
+ DumpSummary();
+
+ DumpProgress("Collecting Frame Events...");
+
+
+ ThreadID mainThreadID = Platform::GetThreadID();
+ uint32 mainThreadIndex = 0;
+ for (size_t i = 0; i < threads.size(); ++i)
+ if (threads[i]->description.threadID == mainThreadID)
+ mainThreadIndex = (uint32)i;
+
+ EventTime timeSlice = CalculateRange(*threads[mainThreadIndex], GetFrameDescription(FrameType::CPU));
+ if (timeSlice.start >= timeSlice.finish)
+ {
+ timeSlice.start = frames.front().start;
+ timeSlice.finish = frames.back().finish;
+ }
+
+ DumpBoard(mode, timeSlice, mainThreadIndex);
+
+ ScopeData threadScope;
+ threadScope.header.boardNumber = boardNumber;
+ threadScope.header.fiberNumber = -1;
+
+ if (gpuProfiler)
+ gpuProfiler->Dump(mode);
+
+ for (size_t i = 0; i < threads.size(); ++i)
+ {
+ threadScope.header.threadNumber = (uint32)i;
+ DumpThread(*threads[i], timeSlice, threadScope);
+ }
+
+ ScopeData fiberScope;
+ fiberScope.header.boardNumber = (uint32)boardNumber;
+ fiberScope.header.threadNumber = -1;
+ for (size_t i = 0; i < fibers.size(); ++i)
+ {
+ fiberScope.header.fiberNumber = (uint32)i;
+ DumpFiber(*fibers[i], timeSlice, fiberScope);
+ }
+
+ frames.clear();
+ CleanupThreadsAndFibers();
+
+ {
+ DumpProgress("Serializing SwitchContexts");
+ OutputDataStream switchContextsStream;
+ switchContextsStream << boardNumber;
+ switchContextCollector.Serialize(switchContextsStream);
+ Server::Get().Send(DataResponse::SynchronizationData, switchContextsStream);
+ }
+
+ {
+ DumpProgress("Serializing SysCalls");
+ OutputDataStream callstacksStream;
+ callstacksStream << boardNumber;
+ syscallCollector.Serialize(callstacksStream);
+ Server::Get().Send(DataResponse::SyscallPack, callstacksStream);
+ }
+
+ if (!callstackCollector.IsEmpty())
+ {
+ DumpProgress("Resolving callstacks");
+ OutputDataStream symbolsStream;
+ symbolsStream << boardNumber;
+ callstackCollector.SerializeModules(symbolsStream);
+ callstackCollector.SerializeSymbols(symbolsStream);
+ Server::Get().Send(DataResponse::CallstackDescriptionBoard, symbolsStream);
+
+ DumpProgress("Serializing callstacks");
+ OutputDataStream callstacksStream;
+ callstacksStream << boardNumber;
+ callstackCollector.SerializeCallstacks(callstacksStream);
+ Server::Get().Send(DataResponse::CallstackPack, callstacksStream);
+ }
+
+ Server::Get().Send(DataResponse::NullFrame, OutputDataStream::Empty);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::DumpSummary()
+{
+ OutputDataStream stream;
+
+ // Board Number
+ stream << boardNumber;
+
+ // Frames
+ double frequency = (double)Platform::GetFrequency();
+ stream << (uint32_t)frames.size();
+ for (const EventTime& frame : frames)
+ {
+ double frameTimeMs = 1000.0 * (frame.finish - frame.start) / frequency;
+ stream << (float)frameTimeMs;
+ }
+
+ // Summary
+ stream << (uint32_t)summary.size();
+ for (size_t i = 0; i < summary.size(); ++i)
+ stream << summary[i].first << summary[i].second;
+ summary.clear();
+
+ // Attachments
+ stream << (uint32_t)attachments.size();
+ for (const Attachment& att : attachments)
+ stream << (uint32_t)att.type << att.name << att.data;
+ attachments.clear();
+
+ // Send
+ Server::Get().Send(DataResponse::SummaryPack, stream);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::CleanupThreadsAndFibers()
+{
+ std::lock_guard<std::recursive_mutex> lock(threadsLock);
+
+ for (ThreadList::iterator it = threads.begin(); it != threads.end();)
+ {
+ if (!(*it)->isAlive)
+ {
+ Memory::Delete(*it);
+ it = threads.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+void Core::DumpBoard(uint32 mode, EventTime timeSlice, uint32 mainThreadIndex)
+{
+ OutputDataStream boardStream;
+
+ boardStream << boardNumber;
+ boardStream << Platform::GetFrequency();
+ boardStream << (uint64)0; // Origin
+ boardStream << (uint32)0; // Precision
+ boardStream << timeSlice;
+ boardStream << threads;
+ boardStream << fibers;
+ boardStream << mainThreadIndex;
+ boardStream << EventDescriptionBoard::Get();
+ boardStream << (uint32)0; // Tags
+ boardStream << (uint32)0; // Run
+ boardStream << (uint32)0; // Filters
+ boardStream << (uint32)0; // ThreadDescs
+ boardStream << mode; // Mode
+ boardStream << processDescs;
+ boardStream << threadDescs;
+ boardStream << (uint32)Platform::GetProcessID();
+ boardStream << (uint32)std::thread::hardware_concurrency();
+ Server::Get().Send(DataResponse::FrameDescriptionBoard, boardStream);
+
+ // Cleanup
+ processDescs.clear();
+ threadDescs.clear();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::GenerateCommonSummary()
+{
+ AttachSummary("Platform", Platform::GetName());
+ AttachSummary("CPU", GetCPUName().c_str());
+ if (gpuProfiler)
+ AttachSummary("GPU", gpuProfiler->GetName().c_str());
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Core::Core()
+ : progressReportedLastTimestampMS(0)
+ , boardNumber(0)
+ , frameNumber(0)
+ , stateCallback(nullptr)
+ , currentState(State::DUMP_CAPTURE)
+ , pendingState(State::DUMP_CAPTURE)
+ , currentMode(Mode::OFF)
+ , symbolEngine(nullptr)
+ , tracer(nullptr)
+ , gpuProfiler(nullptr)
+{
+#if OPTICK_ENABLE_TRACING
+ tracer = Platform::GetTrace();
+ symbolEngine = Platform::GetSymbolEngine();
+#endif
+
+ frameDescriptions[FrameType::CPU] = EventDescription::Create("CPU Frame", __FILE__, __LINE__);
+ frameDescriptions[FrameType::GPU] = EventDescription::Create("GPU Frame", __FILE__, __LINE__);
+ frameDescriptions[FrameType::Render] = EventDescription::Create("Render Frame", __FILE__, __LINE__);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::UpdateState()
+{
+ if (currentState != pendingState)
+ {
+ State::Type nextState = pendingState;
+ if (pendingState == State::DUMP_CAPTURE && currentState == State::START_CAPTURE)
+ nextState = State::STOP_CAPTURE;
+
+ if ((stateCallback != nullptr) && !stateCallback(nextState))
+ return false;
+
+ switch (nextState)
+ {
+ case State::START_CAPTURE:
+ Activate((Mode::Type)settings.mode);
+ break;
+
+ case State::STOP_CAPTURE:
+ case State::CANCEL_CAPTURE:
+ Activate(Mode::OFF);
+ break;
+
+ case State::DUMP_CAPTURE:
+ DumpFrames();
+ break;
+ }
+ currentState = nextState;
+ return true;
+ }
+ return false;
+}
+
+
+uint32_t Core::Update()
+{
+ std::lock_guard<std::recursive_mutex> lock(coreLock);
+
+ if (currentMode != Mode::OFF)
+ {
+ if (!frames.empty())
+ frames.back().Stop();
+
+ if (settings.frameLimit > 0 && frames.size() >= settings.frameLimit)
+ DumpCapture();
+
+ if (settings.timeLimitUs > 0)
+ {
+ if (TicksToUs(frames.back().finish - frames.front().start) >= settings.timeLimitUs)
+ DumpCapture();
+ }
+
+ if (settings.spikeLimitUs > 0)
+ {
+ if (TicksToUs(frames.back().finish - frames.back().start) >= settings.spikeLimitUs)
+ DumpCapture();
+ }
+
+ if (IsTimeToReportProgress())
+ DumpCapturingProgress();
+ }
+
+ UpdateEvents();
+
+ while (UpdateState()) {}
+
+ if (currentMode != Mode::OFF)
+ {
+ frames.push_back(EventTime());
+ frames.back().Start();
+ }
+
+ return ++frameNumber;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::UpdateEvents()
+{
+ Server::Get().Update();
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::ReportSwitchContext(const SwitchContextDesc& desc)
+{
+ switchContextCollector.Add(desc);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::ReportStackWalk(const CallstackDesc& desc)
+{
+ callstackCollector.Add(desc);
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::Activate(Mode::Type mode)
+{
+ if (mode != currentMode)
+ {
+ Mode::Type prevMode = currentMode;
+ currentMode = mode;
+
+ {
+ std::lock_guard<std::recursive_mutex> lock(threadsLock);
+ for(auto it = threads.begin(); it != threads.end(); ++it)
+ {
+ ThreadEntry* entry = *it;
+ entry->Activate(mode);
+ }
+ }
+
+
+ if (mode != Mode::OFF)
+ {
+ CaptureStatus::Type status = CaptureStatus::ERR_TRACER_FAILED;
+
+ if (tracer && (mode & Mode::TRACER))
+ {
+ std::lock_guard<std::recursive_mutex> lock(threadsLock);
+ tracer->SetPassword(settings.password.c_str());
+ status = tracer->Start(mode, settings.samplingFrequency, threads);
+ // Let's retry with more narrow setup
+ if (status != CaptureStatus::OK && (mode & Mode::AUTOSAMPLING))
+ status = tracer->Start((Mode::Type)(mode & ~Mode::AUTOSAMPLING), settings.samplingFrequency, threads);
+ }
+
+ if (gpuProfiler && (mode & Mode::GPU))
+ gpuProfiler->Start(mode);
+
+ SendHandshakeResponse(status);
+ }
+ else
+ {
+ if (tracer)
+ tracer->Stop();
+
+ if (gpuProfiler)
+ gpuProfiler->Stop(prevMode);
+ }
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::DumpCapturingProgress()
+{
+ stringstream stream;
+
+ if (currentMode != Mode::OFF)
+ {
+ size_t memUsedKb = Memory::GetAllocatedSize() >> 10;
+ float memUsedMb = memUsedKb / 1024.0f;
+ // VS TODO: Format to 3 digits
+ stream << "Capturing Frame " << (uint32)frames.size() << "..." << std::endl << "Memory Used: " << memUsedMb << " Mb";
+ }
+
+ DumpProgress(stream.str().c_str());
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::IsTimeToReportProgress() const
+{
+ return GetTimeMilliSeconds() > progressReportedLastTimestampMS + 200;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::SendHandshakeResponse(CaptureStatus::Type status)
+{
+ OutputDataStream stream;
+ stream << (uint32)status;
+ stream << Platform::GetName();
+ stream << Server::Get().GetHostName();
+ Server::Get().Send(DataResponse::Handshake, stream);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::IsRegistredThread(ThreadID id)
+{
+ std::lock_guard<std::recursive_mutex> lock(threadsLock);
+
+ for (ThreadList::iterator it = threads.begin(); it != threads.end(); ++it)
+ {
+ ThreadEntry* entry = *it;
+ if (entry->description.threadID == id)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ThreadEntry* Core::RegisterThread(const ThreadDescription& description, EventStorage** slot)
+{
+ std::lock_guard<std::recursive_mutex> lock(threadsLock);
+
+ ThreadEntry* entry = Memory::New<ThreadEntry>(description, slot);
+ threads.push_back(entry);
+
+ if ((currentMode != Mode::OFF) && slot != nullptr)
+ *slot = &entry->storage;
+
+ return entry;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::UnRegisterThread(ThreadID threadID, bool keepAlive)
+{
+ std::lock_guard<std::recursive_mutex> lock(threadsLock);
+
+ for (ThreadList::iterator it = threads.begin(); it != threads.end(); ++it)
+ {
+ ThreadEntry* entry = *it;
+ if (entry->description.threadID == threadID && entry->isAlive)
+ {
+ if ((currentMode == Mode::OFF) && !keepAlive)
+ {
+ Memory::Delete(entry);
+ threads.erase(it);
+ return true;
+ }
+ else
+ {
+ entry->isAlive = false;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::RegisterFiber(const FiberDescription& description, EventStorage** slot)
+{
+ std::lock_guard<std::recursive_mutex> lock(coreLock);
+ FiberEntry* entry = Memory::New<FiberEntry>(description);
+ fibers.push_back(entry);
+ entry->storage.isFiberStorage = true;
+ *slot = &entry->storage;
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::RegisterProcessDescription(const ProcessDescription& description)
+{
+ processDescs.push_back(description);
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::RegisterThreadDescription(const ThreadDescription& description)
+{
+ threadDescs.push_back(description);
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::SetStateChangedCallback(StateCallback cb)
+{
+ stateCallback = cb;
+ return stateCallback != nullptr;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::AttachSummary(const char* key, const char* value)
+{
+ summary.push_back(make_pair(string(key), string(value)));
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::AttachFile(File::Type type, const char* name, const uint8_t* data, uint32_t size)
+{
+ if (size > 0)
+ {
+ attachments.push_back(Attachment(type, name));
+ Attachment& attachment = attachments.back();
+ attachment.data.resize(size);
+ memcpy(&attachment.data[0], data, size);
+ return true;
+ }
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::AttachFile(File::Type type, const char* name, std::istream& stream)
+{
+ std::streampos beg = stream.tellg();
+ stream.seekg(0, std::ios::end);
+ std::streampos end = stream.tellg();
+ stream.seekg(beg, std::ios::beg);
+
+ size_t size =(size_t)(end - beg);
+ void* buffer = Memory::Alloc(size);
+
+ stream.read((char*)buffer, size);
+ bool result = AttachFile(type, name, (uint8*)buffer, (uint32_t)size);
+
+ Memory::Free(buffer);
+ return result;
+
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::AttachFile(File::Type type, const char* name, const char* path)
+{
+ std::ifstream stream(path, std::ios::binary);
+ return AttachFile(type, name, stream);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::AttachFile(File::Type type, const char* name, const wchar_t* path)
+{
+#if defined(OPTICK_MSVC)
+ std::ifstream stream(path, std::ios::binary);
+ return AttachFile(type, name, stream);
+#else
+ char p[256] = { 0 };
+ wcstombs(p, path, sizeof(p));
+ std::ifstream stream(p, std::ios::binary);
+ return AttachFile(type, name, stream);
+#endif
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void Core::InitGPUProfiler(GPUProfiler* profiler)
+{
+ OPTICK_ASSERT(gpuProfiler == nullptr, "Can't reinitialize GPU profiler! Not supported yet!");
+ Memory::Delete<GPUProfiler>(gpuProfiler);
+ gpuProfiler = profiler;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool Core::SetSettings(const CaptureSettings& captureSettings)
+{
+ settings = captureSettings;
+
+ //if (tracer)
+ //{
+ // string decoded = base64_decode(encodedPassword);
+ // tracer->SetPassword(decoded.c_str());
+ // return true;
+ //}
+ return false;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const EventDescription* Core::GetFrameDescription(FrameType::Type frame) const
+{
+ return frameDescriptions[frame];
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+Core::~Core()
+{
+ std::lock_guard<std::recursive_mutex> lock(threadsLock);
+
+ for (ThreadList::iterator it = threads.begin(); it != threads.end(); ++it)
+ {
+ Memory::Delete(*it);
+ }
+ threads.clear();
+
+ for (FiberList::iterator it = fibers.begin(); it != fibers.end(); ++it)
+ {
+ Memory::Delete(*it);
+ }
+ fibers.clear();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+const vector<ThreadEntry*>& Core::GetThreads() const
+{
+ return threads;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_THREAD_LOCAL EventStorage* Core::storage = nullptr;
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ScopeHeader::ScopeHeader() : boardNumber(0), threadNumber(0)
+{
+
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const ScopeHeader& header)
+{
+ return stream << header.boardNumber << header.threadNumber << header.fiberNumber << header.event;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const ScopeData& ob)
+{
+ return stream << ob.header << ob.categories << ob.events;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const ThreadDescription& description)
+{
+ return stream << description.threadID << description.processID << description.name << description.maxDepth << description.priority << description.mask;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const ThreadEntry* entry)
+{
+ return stream << entry->description;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const FiberDescription& description)
+{
+ return stream << description.id;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const FiberEntry* entry)
+{
+ return stream << entry->description;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const ProcessDescription& description)
+{
+ return stream << description.processID << description.name << description.uniqueKey;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool SetStateChangedCallback(StateCallback cb)
+{
+ return Core::Get().SetStateChangedCallback(cb);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool AttachSummary(const char* key, const char* value)
+{
+ return Core::Get().AttachSummary(key, value);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool AttachFile(File::Type type, const char* name, const uint8_t* data, uint32_t size)
+{
+ return Core::Get().AttachFile(type, name, data, size);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool AttachFile(File::Type type, const char* name, const char* path)
+{
+ return Core::Get().AttachFile(type, name, path);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool AttachFile(File::Type type, const char* name, const wchar_t* path)
+{
+ return Core::Get().AttachFile(type, name, path);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OutputDataStream& operator<<(OutputDataStream& stream, const Point& ob)
+{
+ return stream << ob.x << ob.y << ob.z;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API uint32_t NextFrame()
+{
+ return Core::NextFrame();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool IsActive(Mode::Type mode /*= Mode::INSTRUMENTATION_EVENTS*/)
+{
+ return (Core::Get().currentMode & mode) != 0;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API EventStorage** GetEventStorageSlotForCurrentThread()
+{
+ return &Core::Get().storage;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool IsFiberStorage(EventStorage* fiberStorage)
+{
+ return fiberStorage->isFiberStorage;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool RegisterThread(const char* name)
+{
+ return Core::Get().RegisterThread(ThreadDescription(name, Platform::GetThreadID(), Platform::GetProcessID()), &Core::storage) != nullptr;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool RegisterThread(const wchar_t* name)
+{
+ const int THREAD_NAME_LENGTH = 128;
+ char mbName[THREAD_NAME_LENGTH];
+ wcstombs_s(mbName, name, THREAD_NAME_LENGTH);
+
+ return Core::Get().RegisterThread(ThreadDescription(mbName, Platform::GetThreadID(), Platform::GetProcessID()), &Core::storage) != nullptr;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool UnRegisterThread(bool keepAlive)
+{
+ return Core::Get().UnRegisterThread(Platform::GetThreadID(), keepAlive);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API bool RegisterFiber(uint64 fiberId, EventStorage** slot)
+{
+ return Core::Get().RegisterFiber(FiberDescription(fiberId), slot);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API EventStorage* RegisterStorage(const char* name, uint64_t threadID, ThreadMask::Type type)
+{
+ ThreadEntry* entry = Core::Get().RegisterThread(ThreadDescription(name, threadID, Platform::GetProcessID(), 1, 0, type), nullptr);
+ return entry ? &entry->storage : nullptr;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API void GpuFlip(void* swapChain)
+{
+ if (GPUProfiler* gpuProfiler = Core::Get().gpuProfiler)
+ gpuProfiler->Flip(swapChain);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API GPUContext SetGpuContext(GPUContext context)
+{
+ if (EventStorage* storage = Core::storage)
+ {
+ GPUContext prevContext = storage->gpuStorage.context;
+ storage->gpuStorage.context = context;
+ return prevContext;
+ }
+ return GPUContext();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+OPTICK_API const EventDescription* GetFrameDescription(FrameType::Type frame)
+{
+ return Core::Get().GetFrameDescription(frame);
+
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventStorage::EventStorage(): currentMode(Mode::OFF), pushPopEventStackIndex(0), isFiberStorage(false)
+{
+
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void ThreadEntry::Activate(Mode::Type mode)
+{
+ if (!isAlive)
+ return;
+
+ if (mode != Mode::OFF)
+ storage.Clear(true);
+
+ if (threadTLS != nullptr)
+ {
+ storage.currentMode = mode;
+ *threadTLS = mode != Mode::OFF ? &storage : nullptr;
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void ThreadEntry::Sort()
+{
+ SortMemoryPool(storage.eventBuffer);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool IsSleepOnlyScope(const ScopeData& scope)
+{
+ //if (!scope.categories.empty())
+ // return false;
+
+ const vector<EventData>& events = scope.events;
+ for(auto it = events.begin(); it != events.end(); ++it)
+ {
+ const EventData& data = *it;
+
+ if (data.description->color != Color::White)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void ScopeData::Send()
+{
+ if (!events.empty() || !categories.empty())
+ {
+ if (!IsSleepOnlyScope(*this))
+ {
+ OutputDataStream frameStream;
+ frameStream << *this;
+ Server::Get().Send(DataResponse::EventFrame, frameStream);
+ }
+ }
+
+ Clear();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void ScopeData::Clear()
+{
+ events.clear();
+ categories.clear();
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void EventStorage::GPUStorage::Clear(bool preserveMemory)
+{
+ for (size_t i = 0; i < gpuBuffer.size(); ++i)
+ for (int j = 0; j < GPU_QUEUE_COUNT; ++j)
+ gpuBuffer[i][j].Clear(preserveMemory);
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+EventData* EventStorage::GPUStorage::Start(const EventDescription &desc)
+{
+ if (GPUProfiler* gpuProfiler = Core::Get().gpuProfiler)
+ {
+ EventData& result = gpuBuffer[context.node][context.queue].Add();
+ result.description = &desc;
+ result.start = EventTime::INVALID_TIMESTAMP;
+ result.finish = EventTime::INVALID_TIMESTAMP;
+ gpuProfiler->QueryTimestamp(context.cmdBuffer, &result.start);
+ return &result;
+ }
+ return nullptr;
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void EventStorage::GPUStorage::Stop(EventData& data)
+{
+ if (GPUProfiler* gpuProfiler = Core::Get().gpuProfiler)
+ {
+ gpuProfiler->QueryTimestamp(context.cmdBuffer, &data.finish);
+ }
+}
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+}
+
+#endif //USE_OPTICK \ No newline at end of file