#pragma once #include "optick.config.h" #if USE_OPTICK #include #include #include "optick_common.h" #include "optick_memory.h" #include "optick_message.h" #include "optick_serialization.h" #include "optick_gpu.h" #include // We expect to have 1k unique strings going through Optick at once // The chances to hit a collision are 1 in 10 trillion (odds of a meteor landing on your house) // We should be quite safe here :) // https://preshing.com/20110504/hash-collision-probabilities/ // Feel free to add a seed and wait for another strike if armageddon starts namespace Optick { struct StringHash { uint64 hash; StringHash(size_t h) : hash(h) {} StringHash(const char* str) : hash(CalcHash(str)) {} bool operator==(const StringHash& other) const { return hash == other.hash; } bool operator<(const StringHash& other) const { return hash < other.hash; } static uint64 CalcHash(const char* str); }; } // Overriding default hash function to return hash value directly namespace std { template<> struct hash { size_t operator()(const Optick::StringHash& x) const { return (size_t)x.hash; } }; } namespace Optick { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct Trace; struct SymbolEngine; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct ScopeHeader { EventTime event; uint32 boardNumber; int32 threadNumber; int32 fiberNumber; ScopeHeader(); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// OutputDataStream& operator << ( OutputDataStream& stream, const ScopeHeader& ob); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct ScopeData { ScopeHeader header; vector categories; vector events; void AddEvent(const EventData& data) { events.push_back(data); if (data.description->color != Color::Null) { categories.push_back(data); } } void InitRootEvent(const EventData& data) { header.event = data; AddEvent(data); } void Send(); void Clear(); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #if defined(OPTICK_MSVC) #pragma warning( push ) #pragma warning( disable : 4996 ) #endif //OPTICK_MSVC template struct OptickString { char data[N]; OptickString() {} OptickString& operator=(const char* text) { strncpy(data, text ? text : "null", N - 1); data[N - 1] = 0; return *this; } OptickString(const char* text) { *this = text; } }; #if defined(OPTICK_MSVC) #pragma warning( pop ) #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct Point { float x, y, z; Point() {} Point(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} Point(float pos[3]) : x(pos[0]), y(pos[1]), z(pos[2]) {} }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template OutputDataStream& operator<<(OutputDataStream &stream, const OptickString& ob) { size_t length = strnlen(ob.data, N); stream << (uint32)length; return stream.Write(ob.data, length); } OutputDataStream& operator<<(OutputDataStream& stream, const Point& ob); OutputDataStream& operator<<(OutputDataStream& stream, const ScopeData& ob); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// typedef MemoryPool EventBuffer; typedef MemoryPool CategoryBuffer; typedef MemoryPool SynchronizationBuffer; typedef MemoryPool FiberSyncBuffer; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// typedef OptickString<32> ShortString; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// typedef TagData TagFloat; typedef TagData TagS32; typedef TagData TagU32; typedef TagData TagU64; typedef TagData TagPoint; typedef TagData TagString; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// typedef MemoryPool TagFloatBuffer; typedef MemoryPool TagS32Buffer; typedef MemoryPool TagU32Buffer; typedef MemoryPool TagU64Buffer; typedef MemoryPool TagPointBuffer; typedef MemoryPool TagStringBuffer; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Base64 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// string base64_decode(string const& encoded_string); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Board //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// typedef MemoryPool EventDescriptionList; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class EventDescriptionBoard { // List of stored Event Descriptions EventDescriptionList boardDescriptions; // Shared Descriptions typedef unordered_map DescriptionMap; DescriptionMap sharedDescriptions; MemoryBuffer<64 * 1024> sharedNames; std::mutex sharedLock; public: EventDescription* CreateDescription(const char* name, const char* file = nullptr, uint32_t line = 0, uint32_t color = Color::Null, uint32_t filter = 0); EventDescription* CreateSharedDescription(const char* name, const char* file = nullptr, uint32_t line = 0, uint32_t color = Color::Null, uint32_t filter = 0); static EventDescriptionBoard& Get(); const EventDescriptionList& GetEvents() const; friend OutputDataStream& operator << (OutputDataStream& stream, const EventDescriptionBoard& ob); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct EventStorage { Mode::Type currentMode; EventBuffer eventBuffer; FiberSyncBuffer fiberSyncBuffer; TagFloatBuffer tagFloatBuffer; TagS32Buffer tagS32Buffer; TagU32Buffer tagU32Buffer; TagU64Buffer tagU64Buffer; TagPointBuffer tagPointBuffer; TagStringBuffer tagStringBuffer; struct GPUStorage { static const int MAX_GPU_NODES = 2; array, MAX_GPU_NODES> gpuBuffer; GPUContext context; void Clear(bool preserveMemory); EventData* Start(const EventDescription& desc); void Stop(EventData& data); }; GPUStorage gpuStorage; uint32 pushPopEventStackIndex; array pushPopEventStack; bool isFiberStorage; EventStorage(); OPTICK_INLINE EventData& NextEvent() { return eventBuffer.Add(); } // Free all temporary memory void Clear(bool preserveContent) { currentMode = Mode::OFF; eventBuffer.Clear(preserveContent); fiberSyncBuffer.Clear(preserveContent); gpuStorage.Clear(preserveContent); ClearTags(preserveContent); while (pushPopEventStackIndex) { pushPopEventStack[--pushPopEventStackIndex] = nullptr; } } void ClearTags(bool preserveContent) { tagFloatBuffer.Clear(preserveContent); tagS32Buffer.Clear(preserveContent); tagU32Buffer.Clear(preserveContent); tagU64Buffer.Clear(preserveContent); tagPointBuffer.Clear(preserveContent); tagStringBuffer.Clear(preserveContent); } void Reset() { Clear(true); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct ProcessDescription { string name; ProcessID processID; uint64 uniqueKey; ProcessDescription(const char* processName, ProcessID pid, uint64 key); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct ThreadDescription { string name; ThreadID threadID; ProcessID processID; int32 maxDepth; int32 priority; uint32 mask; ThreadDescription(const char* threadName, ThreadID tid, ProcessID pid, int32 maxDepth = 1, int32 priority = 0, uint32 mask = 0); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct FiberDescription { uint64 id; FiberDescription(uint64 _id) : id(_id) {} }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct ThreadEntry { ThreadDescription description; EventStorage storage; EventStorage** threadTLS; bool isAlive; ThreadEntry(const ThreadDescription& desc, EventStorage** tls) : description(desc), threadTLS(tls), isAlive(true) {} void Activate(Mode::Type mode); void Sort(); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct FiberEntry { FiberDescription description; EventStorage storage; FiberEntry(const FiberDescription& desc) : description(desc) {} }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// typedef vector ThreadList; typedef vector FiberList; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct SysCallData : EventData { uint64 id; uint64 threadID; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// OutputDataStream &operator << (OutputDataStream &stream, const SysCallData &ob); //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class SysCallCollector { typedef MemoryPool SysCallPool; public: SysCallPool syscallPool; SysCallData& Add(); void Clear(); bool Serialize(OutputDataStream& stream); }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct CallstackDesc { uint64 threadID; uint64 timestamp; uint64* callstack; uint8 count; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class CallstackCollector { // Packed callstack list: {ThreadID, Timestamp, Count, Callstack[Count]} typedef MemoryPool CallstacksPool; CallstacksPool callstacksPool; public: void Add(const CallstackDesc& desc); void Clear(); bool SerializeModules(OutputDataStream& stream); bool SerializeSymbols(OutputDataStream& stream); bool SerializeCallstacks(OutputDataStream& stream); bool IsEmpty() const; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct SwitchContextDesc { int64_t timestamp; uint64 oldThreadId; uint64 newThreadId; uint8 cpuId; uint8 reason; }; ////////////////////////////////////////////////////////////////////////// OutputDataStream &operator << (OutputDataStream &stream, const SwitchContextDesc &ob); ////////////////////////////////////////////////////////////////////////// class SwitchContextCollector { typedef MemoryPool SwitchContextPool; SwitchContextPool switchContextPool; public: void Add(const SwitchContextDesc& desc); void Clear(); bool Serialize(OutputDataStream& stream); }; ////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct CaptureStatus { enum Type { OK = 0, ERR_TRACER_ALREADY_EXISTS = 1, ERR_TRACER_ACCESS_DENIED = 2, ERR_TRACER_FAILED = 3, ERR_TRACER_INVALID_PASSWORD = 4, }; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class Core { std::recursive_mutex coreLock; std::recursive_mutex threadsLock; ThreadList threads; FiberList fibers; int64 progressReportedLastTimestampMS; vector frames; uint32 boardNumber; CallstackCollector callstackCollector; SwitchContextCollector switchContextCollector; vector> summary; std::atomic frameNumber; struct Attachment { string name; vector data; File::Type type; Attachment(File::Type t, const char* n) : name(n), type(t) {} }; list attachments; StateCallback stateCallback; vector processDescs; vector threadDescs; array frameDescriptions; State::Type currentState; State::Type pendingState; CaptureSettings settings; void UpdateEvents(); uint32_t Update(); bool UpdateState(); Core(); ~Core(); static Core notThreadSafeInstance; void DumpCapturingProgress(); void SendHandshakeResponse(CaptureStatus::Type status); void DumpEvents(EventStorage& entry, const EventTime& timeSlice, ScopeData& scope); void DumpTags(EventStorage& entry, ScopeData& scope); void DumpThread(ThreadEntry& entry, const EventTime& timeSlice, ScopeData& scope); void DumpFiber(FiberEntry& entry, const EventTime& timeSlice, ScopeData& scope); void CleanupThreadsAndFibers(); void DumpBoard(uint32 mode, EventTime timeSlice, uint32 mainThreadIndex); void GenerateCommonSummary(); public: void Activate(Mode::Type mode); volatile Mode::Type currentMode; // Active Frame (is used as buffer) static OPTICK_THREAD_LOCAL EventStorage* storage; // Resolves symbols SymbolEngine* symbolEngine; // Controls GPU activity // Graphics graphics; // System scheduler trace Trace* tracer; // SysCall Collector SysCallCollector syscallCollector; // GPU Profiler GPUProfiler* gpuProfiler; // Returns thread collection const vector& GetThreads() const; // Request to start a new capture void StartCapture(); // Request to stop an active capture void StopCapture(); // Request to stop an active capture void CancelCapture(); // Requests to dump current capture void DumpCapture(); // Report switch context event bool ReportSwitchContext(const SwitchContextDesc& desc); // Report switch context event bool ReportStackWalk(const CallstackDesc& desc); // Serialize and send current profiling progress void DumpProgress(const char* message = ""); // Too much time from last report bool IsTimeToReportProgress() const; // Serialize and send frames void DumpFrames(uint32 mode = Mode::DEFAULT); // Serialize and send frames void DumpSummary(); // Registers thread and create EventStorage ThreadEntry* RegisterThread(const ThreadDescription& description, EventStorage** slot); // UnRegisters thread bool UnRegisterThread(ThreadID threadId, bool keepAlive = false); // Check is registered thread bool IsRegistredThread(ThreadID id); // Registers finer and create EventStorage bool RegisterFiber(const FiberDescription& description, EventStorage** slot); // Registers ProcessDescription bool RegisterProcessDescription(const ProcessDescription& description); // Registers ThreaDescription (used for threads from other processes) bool RegisterThreadDescription(const ThreadDescription& description); // Sets state change callback bool SetStateChangedCallback(StateCallback cb); // Attaches a key-value pair to the next capture bool AttachSummary(const char* key, const char* value); // Attaches a screenshot to the current capture bool AttachFile(File::Type type, const char* name, const uint8_t* data, uint32_t size); bool AttachFile(File::Type type, const char* name, std::istream& stream); bool AttachFile(File::Type type, const char* name, const char* path); bool AttachFile(File::Type type, const char* name, const wchar_t* path); // Initalizes GPU profiler void InitGPUProfiler(GPUProfiler* profiler); // Initializes root password for the device bool SetSettings(const CaptureSettings& settings); // Current Frame Number (since the game started) uint32_t GetCurrentFrame() const { return frameNumber; } // Returns Frame Description const EventDescription* GetFrameDescription(FrameType::Type frame) const; // NOT Thread Safe singleton (performance) static Core& Get(); // Main Update Function static uint32_t NextFrame() { return Get().Update(); } }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } #endif //USE_OPTICK