diff options
Diffstat (limited to 'external/optick')
-rw-r--r-- | external/optick/optick.config.h | 51 | ||||
-rw-r--r-- | external/optick/optick.h | 872 | ||||
-rw-r--r-- | external/optick/optick_common.h | 142 | ||||
-rw-r--r-- | external/optick/optick_core.cpp | 1657 | ||||
-rw-r--r-- | external/optick/optick_core.h | 568 | ||||
-rw-r--r-- | external/optick/optick_core.linux.h | 410 | ||||
-rw-r--r-- | external/optick/optick_core.macos.h | 289 | ||||
-rw-r--r-- | external/optick/optick_core.platform.h | 92 | ||||
-rw-r--r-- | external/optick/optick_core.win.h | 1664 | ||||
-rw-r--r-- | external/optick/optick_gpu.cpp | 136 | ||||
-rw-r--r-- | external/optick/optick_gpu.d3d12.cpp | 382 | ||||
-rw-r--r-- | external/optick/optick_gpu.h | 129 | ||||
-rw-r--r-- | external/optick/optick_gpu.vulkan.cpp | 365 | ||||
-rw-r--r-- | external/optick/optick_memory.h | 419 | ||||
-rw-r--r-- | external/optick/optick_message.cpp | 172 | ||||
-rw-r--r-- | external/optick/optick_message.h | 130 | ||||
-rw-r--r-- | external/optick/optick_serialization.cpp | 178 | ||||
-rw-r--r-- | external/optick/optick_serialization.h | 120 | ||||
-rw-r--r-- | external/optick/optick_server.cpp | 338 | ||||
-rw-r--r-- | external/optick/optick_server.h | 42 |
20 files changed, 8156 insertions, 0 deletions
diff --git a/external/optick/optick.config.h b/external/optick/optick.config.h new file mode 100644 index 0000000..dcc6e98 --- /dev/null +++ b/external/optick/optick.config.h @@ -0,0 +1,51 @@ +#pragma once + +#define OPTICK_ENABLE_GPU_D3D12 false +#define OPTICK_ENABLE_GPU_VULKAN false + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// GLOBAL SETTINGS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// [x] USE_OPTICK - (Master Switch) +// [x] OPTICK_ENABLE_TRACING - (Enable Kernel-level tracing) +// [x] OPTICK_ENABLE_GPU_D3D12 - (GPU D3D12) +// [ ] OPTICK_ENABLE_GPU_VULKAN - (GPU VULKAN) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// MASTER SWITCH - use it for disabling profiler in final builds // +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if !defined(USE_OPTICK) +#define USE_OPTICK (1) +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Enable Low-level platform-specific tracing (Switch Contexts, Autosampling, etc.) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if !defined(OPTICK_ENABLE_TRACING) +#define OPTICK_ENABLE_TRACING (USE_OPTICK /*&& 0*/) +#endif //OPTICK_ENABLE_TRACING +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// GPU Counters +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if !defined(OPTICK_ENABLE_GPU) +#define OPTICK_ENABLE_GPU (USE_OPTICK /*&& 0*/) +#endif //OPTICK_ENABLE_GPU + +// D3D12 +#if !defined(OPTICK_ENABLE_GPU_D3D12) +#if defined(_MSC_VER) +#define OPTICK_ENABLE_GPU_D3D12 (OPTICK_ENABLE_GPU /*&& 0*/) +#else +#define OPTICK_ENABLE_GPU_D3D12 (0) +#endif +#endif + +// VUKLAN +#if !defined(OPTICK_ENABLE_GPU_VULKAN) +#define OPTICK_ENABLE_GPU_VULKAN (OPTICK_ENABLE_GPU && 0) +#endif + diff --git a/external/optick/optick.h b/external/optick/optick.h new file mode 100644 index 0000000..e3eb512 --- /dev/null +++ b/external/optick/optick.h @@ -0,0 +1,872 @@ +#pragma once +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Config +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "optick.config.h" + +#if USE_OPTICK +#include <stdint.h> + +#if defined(__clang__) || defined(__GNUC__) +# define OPTICK_GCC (1) +# if defined(__APPLE_CC__) +# define OPTICK_OSX (1) +# elif defined(__linux__) +# define OPTICK_LINUX (1) +# elif defined(__ORBIS__) +# define OPTICK_PS4 (1) +# endif +#elif defined(_MSC_VER) +# define OPTICK_MSVC (1) +# if defined(_DURANGO) +# define OPTICK_XBOX (1) +# else +# define OPTICK_PC (1) +#endif +#else +#error Compiler not supported +#endif + +//////////////////////////////////////////////////////////////////////// +// Target Platform +//////////////////////////////////////////////////////////////////////// + +#if defined(OPTICK_GCC) +#define OPTICK_FUNC __PRETTY_FUNCTION__ +#elif defined(OPTICK_MSVC) +#define OPTICK_FUNC __FUNCSIG__ +#else +#error Compiler not supported +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// EXPORTS +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef OPTICK_EXPORTS +#define OPTICK_API __declspec(dllexport) +#else +#define OPTICK_API //__declspec(dllimport) +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define OPTICK_CONCAT_IMPL(x, y) x##y +#define OPTICK_CONCAT(x, y) OPTICK_CONCAT_IMPL(x, y) + +#if defined(OPTICK_MSVC) +#define OPTICK_INLINE __forceinline +#elif defined(OPTICK_GCC) +#define OPTICK_INLINE __attribute__((always_inline)) inline +#else +#error Compiler is not supported +#endif + + +// Vulkan Forward Declarations +#define OPTICK_DEFINE_HANDLE(object) typedef struct object##_T* object; +OPTICK_DEFINE_HANDLE(VkDevice); +OPTICK_DEFINE_HANDLE(VkPhysicalDevice); +OPTICK_DEFINE_HANDLE(VkQueue); +OPTICK_DEFINE_HANDLE(VkCommandBuffer); + +// D3D12 Forward Declarations +struct ID3D12CommandList; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace Optick +{ + // Source: http://msdn.microsoft.com/en-us/library/system.windows.media.colors(v=vs.110).aspx + // Image: http://i.msdn.microsoft.com/dynimg/IC24340.png + struct Color + { + enum + { + Null = 0x00000000, + AliceBlue = 0xFFF0F8FF, + AntiqueWhite = 0xFFFAEBD7, + Aqua = 0xFF00FFFF, + Aquamarine = 0xFF7FFFD4, + Azure = 0xFFF0FFFF, + Beige = 0xFFF5F5DC, + Bisque = 0xFFFFE4C4, + Black = 0xFF000000, + BlanchedAlmond = 0xFFFFEBCD, + Blue = 0xFF0000FF, + BlueViolet = 0xFF8A2BE2, + Brown = 0xFFA52A2A, + BurlyWood = 0xFFDEB887, + CadetBlue = 0xFF5F9EA0, + Chartreuse = 0xFF7FFF00, + Chocolate = 0xFFD2691E, + Coral = 0xFFFF7F50, + CornflowerBlue = 0xFF6495ED, + Cornsilk = 0xFFFFF8DC, + Crimson = 0xFFDC143C, + Cyan = 0xFF00FFFF, + DarkBlue = 0xFF00008B, + DarkCyan = 0xFF008B8B, + DarkGoldenRod = 0xFFB8860B, + DarkGray = 0xFFA9A9A9, + DarkGreen = 0xFF006400, + DarkKhaki = 0xFFBDB76B, + DarkMagenta = 0xFF8B008B, + DarkOliveGreen = 0xFF556B2F, + DarkOrange = 0xFFFF8C00, + DarkOrchid = 0xFF9932CC, + DarkRed = 0xFF8B0000, + DarkSalmon = 0xFFE9967A, + DarkSeaGreen = 0xFF8FBC8F, + DarkSlateBlue = 0xFF483D8B, + DarkSlateGray = 0xFF2F4F4F, + DarkTurquoise = 0xFF00CED1, + DarkViolet = 0xFF9400D3, + DeepPink = 0xFFFF1493, + DeepSkyBlue = 0xFF00BFFF, + DimGray = 0xFF696969, + DodgerBlue = 0xFF1E90FF, + FireBrick = 0xFFB22222, + FloralWhite = 0xFFFFFAF0, + ForestGreen = 0xFF228B22, + Fuchsia = 0xFFFF00FF, + Gainsboro = 0xFFDCDCDC, + GhostWhite = 0xFFF8F8FF, + Gold = 0xFFFFD700, + GoldenRod = 0xFFDAA520, + Gray = 0xFF808080, + Green = 0xFF008000, + GreenYellow = 0xFFADFF2F, + HoneyDew = 0xFFF0FFF0, + HotPink = 0xFFFF69B4, + IndianRed = 0xFFCD5C5C, + Indigo = 0xFF4B0082, + Ivory = 0xFFFFFFF0, + Khaki = 0xFFF0E68C, + Lavender = 0xFFE6E6FA, + LavenderBlush = 0xFFFFF0F5, + LawnGreen = 0xFF7CFC00, + LemonChiffon = 0xFFFFFACD, + LightBlue = 0xFFADD8E6, + LightCoral = 0xFFF08080, + LightCyan = 0xFFE0FFFF, + LightGoldenRodYellow = 0xFFFAFAD2, + LightGray = 0xFFD3D3D3, + LightGreen = 0xFF90EE90, + LightPink = 0xFFFFB6C1, + LightSalmon = 0xFFFFA07A, + LightSeaGreen = 0xFF20B2AA, + LightSkyBlue = 0xFF87CEFA, + LightSlateGray = 0xFF778899, + LightSteelBlue = 0xFFB0C4DE, + LightYellow = 0xFFFFFFE0, + Lime = 0xFF00FF00, + LimeGreen = 0xFF32CD32, + Linen = 0xFFFAF0E6, + Magenta = 0xFFFF00FF, + Maroon = 0xFF800000, + MediumAquaMarine = 0xFF66CDAA, + MediumBlue = 0xFF0000CD, + MediumOrchid = 0xFFBA55D3, + MediumPurple = 0xFF9370DB, + MediumSeaGreen = 0xFF3CB371, + MediumSlateBlue = 0xFF7B68EE, + MediumSpringGreen = 0xFF00FA9A, + MediumTurquoise = 0xFF48D1CC, + MediumVioletRed = 0xFFC71585, + MidnightBlue = 0xFF191970, + MintCream = 0xFFF5FFFA, + MistyRose = 0xFFFFE4E1, + Moccasin = 0xFFFFE4B5, + NavajoWhite = 0xFFFFDEAD, + Navy = 0xFF000080, + OldLace = 0xFFFDF5E6, + Olive = 0xFF808000, + OliveDrab = 0xFF6B8E23, + Orange = 0xFFFFA500, + OrangeRed = 0xFFFF4500, + Orchid = 0xFFDA70D6, + PaleGoldenRod = 0xFFEEE8AA, + PaleGreen = 0xFF98FB98, + PaleTurquoise = 0xFFAFEEEE, + PaleVioletRed = 0xFFDB7093, + PapayaWhip = 0xFFFFEFD5, + PeachPuff = 0xFFFFDAB9, + Peru = 0xFFCD853F, + Pink = 0xFFFFC0CB, + Plum = 0xFFDDA0DD, + PowderBlue = 0xFFB0E0E6, + Purple = 0xFF800080, + Red = 0xFFFF0000, + RosyBrown = 0xFFBC8F8F, + RoyalBlue = 0xFF4169E1, + SaddleBrown = 0xFF8B4513, + Salmon = 0xFFFA8072, + SandyBrown = 0xFFF4A460, + SeaGreen = 0xFF2E8B57, + SeaShell = 0xFFFFF5EE, + Sienna = 0xFFA0522D, + Silver = 0xFFC0C0C0, + SkyBlue = 0xFF87CEEB, + SlateBlue = 0xFF6A5ACD, + SlateGray = 0xFF708090, + Snow = 0xFFFFFAFA, + SpringGreen = 0xFF00FF7F, + SteelBlue = 0xFF4682B4, + Tan = 0xFFD2B48C, + Teal = 0xFF008080, + Thistle = 0xFFD8BFD8, + Tomato = 0xFFFF6347, + Turquoise = 0xFF40E0D0, + Violet = 0xFFEE82EE, + Wheat = 0xFFF5DEB3, + White = 0xFFFFFFFF, + WhiteSmoke = 0xFFF5F5F5, + Yellow = 0xFFFFFF00, + YellowGreen = 0xFF9ACD32, + }; + }; + + struct Filter + { + enum Type : uint32_t + { + None, + + // CPU + AI, + Animation, + Audio, + Debug, + Camera, + Cloth, + GameLogic, + Input, + Navigation, + Network, + Physics, + Rendering, + Scene, + Script, + Streaming, + UI, + VFX, + Visibility, + Wait, + + // IO + IO, + + // GPU + GPU_Cloth, + GPU_Lighting, + GPU_PostFX, + GPU_Reflections, + GPU_Scene, + GPU_Shadows, + GPU_UI, + GPU_VFX, + GPU_Water, + + }; + }; + + #define OPTICK_MAKE_CATEGORY(filter, color) (((uint64_t)(1ull) << (filter + 32)) | (uint64_t)color) + + struct Category + { + enum Type : uint64_t + { + // CPU + None = OPTICK_MAKE_CATEGORY(Filter::None, Color::Null), + AI = OPTICK_MAKE_CATEGORY(Filter::AI, Color::Purple), + Animation = OPTICK_MAKE_CATEGORY(Filter::Animation, Color::LightSkyBlue), + Audio = OPTICK_MAKE_CATEGORY(Filter::Audio, Color::HotPink), + Debug = OPTICK_MAKE_CATEGORY(Filter::Debug, Color::Black), + Camera = OPTICK_MAKE_CATEGORY(Filter::Camera, Color::Black), + Cloth = OPTICK_MAKE_CATEGORY(Filter::Cloth, Color::DarkGreen), + GameLogic = OPTICK_MAKE_CATEGORY(Filter::GameLogic, Color::RoyalBlue), + Input = OPTICK_MAKE_CATEGORY(Filter::Input, Color::Ivory), + Navigation = OPTICK_MAKE_CATEGORY(Filter::Navigation, Color::Magenta), + Network = OPTICK_MAKE_CATEGORY(Filter::Network, Color::Olive), + Physics = OPTICK_MAKE_CATEGORY(Filter::Physics, Color::LawnGreen), + Rendering = OPTICK_MAKE_CATEGORY(Filter::Rendering, Color::BurlyWood), + Scene = OPTICK_MAKE_CATEGORY(Filter::Scene, Color::RoyalBlue), + Script = OPTICK_MAKE_CATEGORY(Filter::Script, Color::Plum), + Streaming = OPTICK_MAKE_CATEGORY(Filter::Streaming, Color::Gold), + UI = OPTICK_MAKE_CATEGORY(Filter::UI, Color::PaleTurquoise), + VFX = OPTICK_MAKE_CATEGORY(Filter::VFX, Color::SaddleBrown), + Visibility = OPTICK_MAKE_CATEGORY(Filter::Visibility, Color::Snow), + Wait = OPTICK_MAKE_CATEGORY(Filter::Wait, Color::Tomato), + WaitEmpty = OPTICK_MAKE_CATEGORY(Filter::Wait, Color::White), + // IO + IO = OPTICK_MAKE_CATEGORY(Filter::IO, Color::Khaki), + // GPU + GPU_Cloth = OPTICK_MAKE_CATEGORY(Filter::GPU_Cloth, Color::DarkGreen), + GPU_Lighting = OPTICK_MAKE_CATEGORY(Filter::GPU_Lighting, Color::Khaki), + GPU_PostFX = OPTICK_MAKE_CATEGORY(Filter::GPU_PostFX, Color::Maroon), + GPU_Reflections = OPTICK_MAKE_CATEGORY(Filter::GPU_Reflections, Color::CadetBlue), + GPU_Scene = OPTICK_MAKE_CATEGORY(Filter::GPU_Scene, Color::RoyalBlue), + GPU_Shadows = OPTICK_MAKE_CATEGORY(Filter::GPU_Shadows, Color::LightSlateGray), + GPU_UI = OPTICK_MAKE_CATEGORY(Filter::GPU_UI, Color::PaleTurquoise), + GPU_VFX = OPTICK_MAKE_CATEGORY(Filter::GPU_VFX, Color::SaddleBrown), + GPU_Water = OPTICK_MAKE_CATEGORY(Filter::GPU_Water, Color::SteelBlue), + }; + + static uint32_t GetMask(Type t) { return (uint32_t)(t >> 32); } + static uint32_t GetColor(Type t) { return (uint32_t)(t); } + }; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} + + +namespace Optick +{ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct Mode +{ + enum Type + { + OFF = 0x0, + INSTRUMENTATION_CATEGORIES = (1 << 0), + INSTRUMENTATION_EVENTS = (1 << 1), + INSTRUMENTATION = (INSTRUMENTATION_CATEGORIES | INSTRUMENTATION_EVENTS), + SAMPLING = (1 << 2), + TAGS = (1 << 3), + AUTOSAMPLING = (1 << 4), + SWITCH_CONTEXT = (1 << 5), + IO = (1 << 6), + GPU = (1 << 7), + END_SCREENSHOT = (1 << 8), + RESERVED_0 = (1 << 9), + RESERVED_1 = (1 << 10), + HW_COUNTERS = (1 << 11), + LIVE = (1 << 12), + RESERVED_2 = (1 << 13), + RESERVED_3 = (1 << 14), + RESERVED_4 = (1 << 15), + SYS_CALLS = (1 << 16), + OTHER_PROCESSES = (1 << 17), + + TRACER = AUTOSAMPLING | SWITCH_CONTEXT | SYS_CALLS, + DEFAULT = INSTRUMENTATION | TAGS | AUTOSAMPLING | SWITCH_CONTEXT | IO | GPU | SYS_CALLS | OTHER_PROCESSES, + }; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPTICK_API int64_t GetHighPrecisionTime(); +OPTICK_API int64_t GetHighPrecisionFrequency(); +OPTICK_API uint32_t NextFrame(); +OPTICK_API bool IsActive(Mode::Type mode = Mode::INSTRUMENTATION_EVENTS); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct EventStorage; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPTICK_API bool RegisterFiber(uint64_t fiberId, EventStorage** slot); +OPTICK_API bool RegisterThread(const char* name); +OPTICK_API bool RegisterThread(const wchar_t* name); +OPTICK_API bool UnRegisterThread(bool keepAlive); +OPTICK_API EventStorage** GetEventStorageSlotForCurrentThread(); +OPTICK_API bool IsFiberStorage(EventStorage* fiberStorage); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct ThreadMask +{ + enum Type + { + None = 0, + Main = 1 << 0, + GPU = 1 << 1, + IO = 1 << 2, + Idle = 1 << 3, + }; +}; + +OPTICK_API EventStorage* RegisterStorage(const char* name, uint64_t threadID = uint64_t(-1), ThreadMask::Type type = ThreadMask::None); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct State +{ + enum Type + { + // Starting a new capture + START_CAPTURE, + + // Stopping current capture + STOP_CAPTURE, + + // Dumping capture to the GUI + // Useful for attaching summary and screenshot to the capture + DUMP_CAPTURE, + + // Cancel current capture + CANCEL_CAPTURE, + }; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Sets a state change callback +typedef bool (*StateCallback)(State::Type state); +OPTICK_API bool SetStateChangedCallback(StateCallback cb); + +// Attaches a key-value pair to the capture's summary +// Example: AttachSummary("Version", "v12.0.1"); +// AttachSummary("Platform", "Windows"); +// AttachSummary("Config", "Release_x64"); +// AttachSummary("Settings", "Ultra"); +// AttachSummary("Map", "Atlantida"); +// AttachSummary("Position", "123.0,120.0,41.1"); +// AttachSummary("CPU", "Intel(R) Xeon(R) CPU E5410@2.33GHz"); +// AttachSummary("GPU", "NVIDIA GeForce GTX 980 Ti"); +OPTICK_API bool AttachSummary(const char* key, const char* value); + +struct File +{ + enum Type + { + // Supported formats: PNG, JPEG, BMP, TIFF + OPTICK_IMAGE, + + // Text file + OPTICK_TEXT, + + // Any other type + OPTICK_OTHER, + }; +}; +// Attaches a file to the current capture +OPTICK_API bool AttachFile(File::Type type, const char* name, const uint8_t* data, uint32_t size); +OPTICK_API bool AttachFile(File::Type type, const char* name, const char* path); +OPTICK_API bool AttachFile(File::Type type, const char* name, const wchar_t* path); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct EventDescription; +struct Frame; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct EventTime +{ + static const int64_t INVALID_TIMESTAMP = (int64_t)-1; + + int64_t start; + int64_t finish; + + OPTICK_INLINE void Start() { start = Optick::GetHighPrecisionTime(); } + OPTICK_INLINE void Stop() { finish = Optick::GetHighPrecisionTime(); } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct EventData : public EventTime +{ + const EventDescription* description; + + bool operator<(const EventData& other) const + { + if (start != other.start) + return start < other.start; + + // Reversed order for finish intervals (parent first) + return finish > other.finish; + } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct OPTICK_API SyncData : public EventTime +{ + uint64_t newThreadId; + uint64_t oldThreadId; + uint8_t core; + int8_t reason; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct OPTICK_API FiberSyncData : public EventTime +{ + uint64_t threadId; + + static void AttachToThread(EventStorage* storage, uint64_t threadId); + static void DetachFromThread(EventStorage* storage); +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +template<class T> +struct TagData +{ + const EventDescription* description; + int64_t timestamp; + T data; + TagData() {} + TagData(const EventDescription& desc, T d) : description(&desc), timestamp(Optick::GetHighPrecisionTime()), data(d) {} +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct OPTICK_API EventDescription +{ + // HOT \\ + // Have to place "hot" variables at the beginning of the class (here will be some padding) + // COLD // + + const char* name; + const char* file; + uint32_t line; + uint32_t index; + uint32_t color; + uint32_t filter; + float budget; + + static EventDescription* Create(const char* eventName, const char* fileName, const unsigned long fileLine, const unsigned long eventColor = Color::Null, const unsigned long filter = 0); + static EventDescription* CreateShared(const char* eventName, const char* fileName = nullptr, const unsigned long fileLine = 0, const unsigned long eventColor = Color::Null, const unsigned long filter = 0); + + EventDescription(); +private: + friend class EventDescriptionBoard; + EventDescription& operator=(const EventDescription&); +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct OPTICK_API Event +{ + EventData* data; + + static EventData* Start(const EventDescription& description); + static void Stop(EventData& data); + + static void Push(const char* name); + static void Push(const EventDescription& description); + static void Pop(); + + static void Add(EventStorage* storage, const EventDescription* description, int64_t timestampStart, int64_t timestampFinish); + static void Push(EventStorage* storage, const EventDescription* description, int64_t timestampStart); + static void Pop(EventStorage* storage, int64_t timestampStart); + + + Event(const EventDescription& description) + { + data = Start(description); + } + + ~Event() + { + if (data) + Stop(*data); + } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPTICK_INLINE Optick::EventDescription* CreateDescription(const char* functionName, const char* fileName, int fileLine, const char* eventName = nullptr, const ::Optick::Category::Type category = ::Optick::Category::None) +{ + return ::Optick::EventDescription::Create(eventName != nullptr ? eventName : functionName, fileName, (unsigned long)fileLine, ::Optick::Category::GetColor(category), ::Optick::Category::GetMask(category)); +} +OPTICK_INLINE Optick::EventDescription* CreateDescription(const char* functionName, const char* fileName, int fileLine, const ::Optick::Category::Type category) +{ + return ::Optick::EventDescription::Create(functionName, fileName, (unsigned long)fileLine, ::Optick::Category::GetColor(category), ::Optick::Category::GetMask(category)); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct OPTICK_API GPUEvent +{ + EventData* data; + + static EventData* Start(const EventDescription& description); + static void Stop(EventData& data); + + GPUEvent(const EventDescription& description) + { + data = Start(description); + } + + ~GPUEvent() + { + if (data) + Stop(*data); + } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct OPTICK_API Tag +{ + static void Attach(const EventDescription& description, float val); + static void Attach(const EventDescription& description, int32_t val); + static void Attach(const EventDescription& description, uint32_t val); + static void Attach(const EventDescription& description, uint64_t val); + static void Attach(const EventDescription& description, float val[3]); + static void Attach(const EventDescription& description, const char* val); + + // Derived + static void Attach(const EventDescription& description, float x, float y, float z) + { + float p[3] = { x, y, z }; Attach(description, p); + } + +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct ThreadScope +{ + bool keepAlive; + + ThreadScope(const char* name, bool bKeepAlive = false) : keepAlive(bKeepAlive) + { + RegisterThread(name); + } + + ThreadScope(const wchar_t* name) + { + RegisterThread(name); + } + + ~ThreadScope() + { + UnRegisterThread(keepAlive); + } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +enum OPTICK_API GPUQueueType +{ + GPU_QUEUE_GRAPHICS, + GPU_QUEUE_COMPUTE, + GPU_QUEUE_TRANSFER, + GPU_QUEUE_VSYNC, + + GPU_QUEUE_COUNT, +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct OPTICK_API GPUContext +{ + void* cmdBuffer; + GPUQueueType queue; + int node; + GPUContext(void* c = nullptr, GPUQueueType q = GPU_QUEUE_GRAPHICS, int n = 0) : cmdBuffer(c), queue(q), node(n) {} +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPTICK_API void InitGpuD3D12(void* device, void** cmdQueues, uint32_t numQueues); +OPTICK_API void InitGpuVulkan(void* vkDevices, void* vkPhysicalDevices, void* vkQueues, uint32_t* cmdQueuesFamily, uint32_t numQueues); +OPTICK_API void GpuFlip(void* swapChain); +OPTICK_API GPUContext SetGpuContext(GPUContext context); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct OPTICK_API GPUContextScope +{ + GPUContext prevContext; + + GPUContextScope(ID3D12CommandList* cmdList, GPUQueueType queue = GPU_QUEUE_GRAPHICS, int node = 0) + { + prevContext = SetGpuContext(GPUContext(cmdList, queue, node)); + } + + GPUContextScope(VkCommandBuffer cmdBuffer, GPUQueueType queue = GPU_QUEUE_GRAPHICS, int node = 0) + { + prevContext = SetGpuContext(GPUContext(cmdBuffer, queue, node)); + } + + ~GPUContextScope() + { + SetGpuContext(prevContext); + } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct FrameType +{ + enum Type + { + CPU, + GPU, + Render, + COUNT, + }; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPTICK_API const EventDescription* GetFrameDescription(FrameType::Type frame); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} + +#define OPTICK_UNUSED(x) (void)(x) +// Workaround for gcc compiler +#define OPTICK_VA_ARGS(...) , ##__VA_ARGS__ + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Scoped profiling event which automatically grabs current function name. +// Use tis macro 95% of the time. +// Example A: +// void Function() +// { +// OPTICK_EVENT(); +// ... code ... +// } +// or +// void Function() +// { +// OPTICK_EVENT("CustomFunctionName"); +// ... code ... +// } +// Notes: +// Optick captures full name of the function including name space and arguments. +// Full name is usually shortened in the Optick GUI in order to highlight the most important bits. +#define OPTICK_EVENT(...) static ::Optick::EventDescription* OPTICK_CONCAT(autogen_description_, __LINE__) = nullptr; \ + if (OPTICK_CONCAT(autogen_description_, __LINE__) == nullptr) OPTICK_CONCAT(autogen_description_, __LINE__) = ::Optick::CreateDescription(OPTICK_FUNC, __FILE__, __LINE__ OPTICK_VA_ARGS(__VA_ARGS__)); \ + ::Optick::Event OPTICK_CONCAT(autogen_event_, __LINE__)( *(OPTICK_CONCAT(autogen_description_, __LINE__)) ); + +// Backward compatibility with previous versions of Optick +//#if !defined(PROFILE) +//#define PROFILE OPTICK_EVENT() +//#endif + +// Scoped profiling macro with predefined color. +// Use this macro for high-level function calls (e.g. AI, Physics, Audio, Render etc.). +// Example: +// void UpdateAI() +// { +// OPTICK_CATEGORY("UpdateAI", Optick::Category::AI); +// ... code ... +// } +// +// Macro could automatically capture current function name: +// void UpdateAI() +// { +// OPTICK_CATEGORY(OPTICK_FUNC, Optick::Category::AI); +// ... code ... +// } +#define OPTICK_CATEGORY(NAME, CATEGORY) OPTICK_EVENT(NAME, CATEGORY) + +// Profiling event for Main Loop update. +// You need to call this function in the beginning of the each new frame. +// Example: +// while (true) +// { +// OPTICK_FRAME("MainThread"); +// ... code ... +// } +#define OPTICK_FRAME(FRAME_NAME) static ::Optick::ThreadScope mainThreadScope(FRAME_NAME); \ + OPTICK_UNUSED(mainThreadScope); \ + uint32_t frameNumber = ::Optick::NextFrame(); \ + ::Optick::Event OPTICK_CONCAT(autogen_event_, __LINE__)(*::Optick::GetFrameDescription(::Optick::FrameType::CPU)); \ + OPTICK_TAG("Frame", frameNumber); + + +// Thread registration macro. +// Example: +// void WorkerThread(...) +// { +// OPTICK_THREAD("Worker"); +// while (isRunning) +// { +// ... +// } +// } +#define OPTICK_THREAD(THREAD_NAME) ::Optick::ThreadScope brofilerThreadScope(THREAD_NAME); \ + OPTICK_UNUSED(brofilerThreadScope); \ + + +// Thread registration macros. +// Useful for integration with custom job-managers. +#define OPTICK_START_THREAD(FRAME_NAME) ::Optick::RegisterThread(FRAME_NAME); +#define OPTICK_STOP_THREAD() ::Optick::UnRegisterThread(false); + +// Attaches a custom data-tag. +// Supported types: int32, uint32, uint64, vec3, string (cut to 32 characters) +// Example: +// OPTICK_TAG("PlayerName", name[index]); +// OPTICK_TAG("Health", 100); +// OPTICK_TAG("Score", 0x80000000u); +// OPTICK_TAG("Height(cm)", 176.3f); +// OPTICK_TAG("Address", (uint64)*this); +// OPTICK_TAG("Position", 123.0f, 456.0f, 789.0f); +#define OPTICK_TAG(NAME, ...) static ::Optick::EventDescription* OPTICK_CONCAT(autogen_tag_, __LINE__) = nullptr; \ + if (OPTICK_CONCAT(autogen_tag_, __LINE__) == nullptr) OPTICK_CONCAT(autogen_tag_, __LINE__) = ::Optick::EventDescription::Create( NAME, __FILE__, __LINE__ ); \ + ::Optick::Tag::Attach(*OPTICK_CONCAT(autogen_tag_, __LINE__), __VA_ARGS__); \ + +// Scoped macro with DYNAMIC name. +// Optick holds a copy of the provided name. +// Each scope does a search in hashmap for the name. +// Please use variations with STATIC names where it's possible. +// Use this macro for quick prototyping or intergratoin with other profiling systems (e.g. UE4) +// Example: +// const char* name = ... ; +// OPTICK_EVENT_DYNAMIC(name); +#define OPTICK_EVENT_DYNAMIC(NAME) OPTICK_CUSTOM_EVENT(::Optick::EventDescription::CreateShared(NAME, __FILE__, __LINE__)); +// Push\Pop profiling macro with DYNAMIC name. +#define OPTICK_PUSH_DYNAMIC(NAME) ::Optick::Event::Push(NAME); + +// Push\Pop profiling macro with STATIC name. +// Please avoid using Push\Pop approach in favor for scoped macros. +// For backward compatibility with some engines. +// Example: +// OPTICK_PUSH("ScopeName"); +// ... +// OPTICK_POP(); +#define OPTICK_PUSH(NAME) static ::Optick::EventDescription* OPTICK_CONCAT(autogen_description_, __LINE__) = nullptr; \ + if (OPTICK_CONCAT(autogen_description_, __LINE__) == nullptr) OPTICK_CONCAT(autogen_description_, __LINE__) = ::Optick::EventDescription::Create( NAME, __FILE__, __LINE__ ); \ + ::Optick::Event::Push(*OPTICK_CONCAT(autogen_description_, __LINE__)); +#define OPTICK_POP() ::Optick::Event::Pop(); + + +// Scoped macro with predefined Optick::EventDescription. +// Use these events instead of DYNAMIC macros to minimize overhead. +// Common use-case: integrating Optick with internal script languages (e.g. Lua, Actionscript(Scaleform), etc.). +// Example: +// Generating EventDescription once during initialization: +// Optick::EventDescription* description = Optick::EventDescription::CreateShared("FunctionName"); +// +// Then we could just use a pointer to cached description later for profiling: +// OPTICK_CUSTOM_EVENT(description); +#define OPTICK_CUSTOM_EVENT(DESCRIPTION) ::Optick::Event OPTICK_CONCAT(autogen_event_, __LINE__)( *DESCRIPTION ); \ + +// Registration of a custom EventStorage (e.g. GPU, IO, etc.) +// Use it to present any extra information on the timeline. +// Example: +// Optick::EventStorage* IOStorage = Optick::RegisterStorage("I/O"); +// Notes: +// Registration of a new storage is thread-safe. +#define OPTICK_STORAGE_REGISTER(STORAGE_NAME) ::Optick::RegisterStorage(STORAGE_NAME); + +// Adding events to the custom storage. +// Helps to integrate Optick into already existing profiling systems (e.g. GPU Profiler, I/O profiler, etc.). +// Example: +// //Registering a storage - should be done once during initialization +// static Optick::EventStorage* IOStorage = Optick::RegisterStorage("I/O"); +// +// int64_t cpuTimestampStart = Optick::GetHighPrecisionTime(); +// ... +// int64_t cpuTimestampFinish = Optick::GetHighPrecisionTime(); +// +// //Creating a shared event-description +// static Optick::EventDescription* IORead = Optick::EventDescription::CreateShared("IO Read"); +// +// OPTICK_STORAGE_EVENT(IOStorage, IORead, cpuTimestampStart, cpuTimestampFinish); +// Notes: +// It's not thread-safe to add events to the same storage from multiple threads. +// Please guarantee thread-safety on the higher level if access from multiple threads to the same storage is required. +#define OPTICK_STORAGE_EVENT(STORAGE, DESCRIPTION, CPU_TIMESTAMP_START, CPU_TIMESTAMP_FINISH) if (::Optick::IsActive()) { ::Optick::Event::Add(STORAGE, DESCRIPTION, CPU_TIMESTAMP_START, CPU_TIMESTAMP_FINISH); } +#define OPTICK_STORAGE_PUSH(STORAGE, DESCRIPTION, CPU_TIMESTAMP_START) if (::Optick::IsActive()) { ::Optick::Event::Push(STORAGE, DESCRIPTION, CPU_TIMESTAMP_START); } +#define OPTICK_STORAGE_POP(STORAGE, CPU_TIMESTAMP_FINISH) if (::Optick::IsActive()) { ::Optick::Event::Pop(STORAGE, CPU_TIMESTAMP_FINISH); } + + +// Registers state change callback +// If callback returns false - the call is repeated the next frame +#define OPTICK_SET_STATE_CHANGED_CALLBACK(CALLBACK) ::Optick::SetStateChangedCallback(CALLBACK); + + +// GPU events +#define OPTICK_GPU_INIT_D3D12(DEVICE, CMD_QUEUES, NUM_CMD_QUEUS) ::Optick::InitGpuD3D12(DEVICE, CMD_QUEUES, NUM_CMD_QUEUS); +#define OPTICK_GPU_INIT_VULKAN(DEVICES, PHYSICAL_DEVICES, CMD_QUEUES, CMD_QUEUES_FAMILY, NUM_CMD_QUEUS) ::Optick::InitGpuVulkan(DEVICES, PHYSICAL_DEVICES, CMD_QUEUES, CMD_QUEUES_FAMILY, NUM_CMD_QUEUS); + +// Setup GPU context: +// Params: +// (CommandBuffer\CommandList, [Optional] Optick::GPUQueue queue, [Optional] int NodeIndex) +// Examples: +// OPTICK_GPU_CONTEXT(cmdBuffer); - all OPTICK_GPU_EVENT will use the same command buffer within the scope +// OPTICK_GPU_CONTEXT(cmdBuffer, Optick::GPU_QUEUE_COMPUTE); - all events will use the same command buffer and queue for the scope +// OPTICK_GPU_CONTEXT(cmdBuffer, Optick::GPU_QUEUE_COMPUTE, gpuIndex); - all events will use the same command buffer and queue for the scope +#define OPTICK_GPU_CONTEXT(...) ::Optick::GPUContextScope OPTICK_CONCAT(gpu_autogen_context_, __LINE__)(__VA_ARGS__); \ + (void)OPTICK_CONCAT(gpu_autogen_context_, __LINE__); + +#define OPTICK_GPU_EVENT(NAME) OPTICK_EVENT(NAME); \ + static ::Optick::EventDescription* OPTICK_CONCAT(gpu_autogen_description_, __LINE__) = nullptr; \ + if (OPTICK_CONCAT(gpu_autogen_description_, __LINE__) == nullptr) OPTICK_CONCAT(gpu_autogen_description_, __LINE__) = ::Optick::EventDescription::Create( NAME, __FILE__, __LINE__ ); \ + ::Optick::GPUEvent OPTICK_CONCAT(gpu_autogen_event_, __LINE__)( *(OPTICK_CONCAT(gpu_autogen_description_, __LINE__)) ); \ + +#define OPTICK_GPU_FLIP(SWAP_CHAIN) ::Optick::GpuFlip(SWAP_CHAIN); + +#else +#define OPTICK_EVENT(...) +#define OPTICK_CATEGORY(NAME, COLOR) +#define OPTICK_FRAME(NAME) +#define OPTICK_THREAD(FRAME_NAME) +#define OPTICK_START_THREAD(FRAME_NAME) +#define OPTICK_STOP_THREAD() +#define OPTICK_TAG(NAME, DATA) +#define OPTICK_EVENT_DYNAMIC(NAME) +#define OPTICK_PUSH_DYNAMIC(NAME) +#define OPTICK_PUSH(NAME) +#define OPTICK_POP() +#define OPTICK_CUSTOM_EVENT(DESCRIPTION) +#define OPTICK_STORAGE_REGISTER(STORAGE_NAME) +#define OPTICK_STORAGE_EVENT(STORAGE, DESCRIPTION, CPU_TIMESTAMP_START, CPU_TIMESTAMP_FINISH) +#define OPTICK_STORAGE_PUSH(STORAGE, DESCRIPTION, CPU_TIMESTAMP_START) +#define OPTICK_STORAGE_POP(STORAGE, CPU_TIMESTAMP_FINISH) +#define OPTICK_SET_STATE_CHANGED_CALLBACK(CALLBACK) +#define OPTICK_GPU_INIT_D3D12(DEVICE, CMD_QUEUES, NUM_CMD_QUEUS) +#define OPTICK_GPU_INIT_VULKAN(DEVICES, PHYSICAL_DEVICES, CMD_QUEUES, CMD_QUEUES_FAMILY, NUM_CMD_QUEUS) +#define OPTICK_GPU_CONTEXT(...) +#define OPTICK_GPU_EVENT(NAME) +#define OPTICK_GPU_FLIP(SWAP_CHAIN) +#endif diff --git a/external/optick/optick_common.h b/external/optick/optick_common.h new file mode 100644 index 0000000..4468911 --- /dev/null +++ b/external/optick/optick_common.h @@ -0,0 +1,142 @@ +#pragma once + +#include "optick.config.h" + +#if USE_OPTICK + +#include "optick.h" + +#include <cstdio> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> + +#if defined(OPTICK_MSVC) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#define NOMINMAX +#include <windows.h> +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Types +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef signed char int8; +typedef unsigned char uint8; +typedef unsigned char byte; +typedef short int16; +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +#if defined(OPTICK_MSVC) +typedef __int64 int64; +typedef unsigned __int64 uint64; +#elif defined(OPTICK_GCC) +typedef int64_t int64; +typedef uint64_t uint64; +#else +#error Compiler is not supported +#endif +static_assert(sizeof(int8) == 1, "Invalid type size, int8"); +static_assert(sizeof(uint8) == 1, "Invalid type size, uint8"); +static_assert(sizeof(byte) == 1, "Invalid type size, byte"); +static_assert(sizeof(int16) == 2, "Invalid type size, int16"); +static_assert(sizeof(uint16) == 2, "Invalid type size, uint16"); +static_assert(sizeof(int32) == 4, "Invalid type size, int32"); +static_assert(sizeof(uint32) == 4, "Invalid type size, uint32"); +static_assert(sizeof(int64) == 8, "Invalid type size, int64"); +static_assert(sizeof(uint64) == 8, "Invalid type size, uint64"); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef uint64 ThreadID; +static const ThreadID INVALID_THREAD_ID = (ThreadID)-1; +typedef uint32 ProcessID; +static const ProcessID INVALID_PROCESS_ID = (ProcessID)-1; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Memory +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if defined(OPTICK_MSVC) +#define OPTICK_ALIGN(N) __declspec( align( N ) ) +#elif defined(OPTICK_GCC) +#define OPTICK_ALIGN(N) __attribute__((aligned(N))) +#else +#error Can not define OPTICK_ALIGN. Unknown platform. +#endif +#define OPTICK_CACHE_LINE_SIZE 64 +#define OPTICK_ALIGN_CACHE OPTICK_ALIGN(OPTICK_CACHE_LINE_SIZE) +#define OPTICK_ARRAY_SIZE(ARR) (sizeof(ARR)/sizeof((ARR)[0])) +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if defined(OPTICK_MSVC) +#define OPTICK_NOINLINE __declspec(noinline) +#elif defined(OPTICK_GCC) +#define OPTICK_NOINLINE __attribute__((__noinline__)) +#else +#error Compiler is not supported +#endif +//////////////////////////////////////////////////////////////////////// +// OPTICK_THREAD_LOCAL +//////////////////////////////////////////////////////////////////////// +#if defined(OPTICK_MSVC) +#define OPTICK_THREAD_LOCAL __declspec(thread) +#elif defined(OPTICK_GCC) +#define OPTICK_THREAD_LOCAL __thread +#else +#error Can not define OPTICK_THREAD_LOCAL. Unknown platform. +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Asserts +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if defined(OPTICK_MSVC) +#define OPTICK_DEBUG_BREAK __debugbreak() +#elif defined(OPTICK_GCC) +#define OPTICK_DEBUG_BREAK __builtin_trap() +#else + #error Can not define OPTICK_DEBUG_BREAK. Unknown platform. +#endif +#define OPTICK_UNUSED(x) (void)(x) +#ifdef _DEBUG + #define OPTICK_ASSERT(arg, description) if (!(arg)) { OPTICK_DEBUG_BREAK; } + #define OPTICK_FAILED(description) { OPTICK_DEBUG_BREAK; } +#else + #define OPTICK_ASSERT(arg, description) + #define OPTICK_FAILED(description) +#endif +#define OPTICK_VERIFY(arg, description, operation) if (!(arg)) { OPTICK_DEBUG_BREAK; operation; } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Safe functions +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if defined(OPTICK_LINUX) || defined(OPTICK_OSX) +template<size_t sizeOfBuffer> +inline int sprintf_s(char(&buffer)[sizeOfBuffer], const char* format, ...) +{ + va_list ap; + va_start(ap, format); + int result = vsnprintf(buffer, sizeOfBuffer, format, ap); + va_end(ap); + return result; +} +#endif + +#if defined(OPTICK_GCC) +template<size_t sizeOfBuffer> +inline int wcstombs_s(char(&buffer)[sizeOfBuffer], const wchar_t* src, size_t maxCount) +{ + return wcstombs(buffer, src, maxCount); +} +#endif + +#if defined(OPTICK_MSVC) +template<size_t sizeOfBuffer> +inline int wcstombs_s(char(&buffer)[sizeOfBuffer], const wchar_t* src, size_t maxCount) +{ + size_t converted = 0; + return wcstombs_s(&converted, buffer, src, maxCount); +} +#endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif //USE_OPTICK
\ No newline at end of file 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 diff --git a/external/optick/optick_core.h b/external/optick/optick_core.h new file mode 100644 index 0000000..9ddf46b --- /dev/null +++ b/external/optick/optick_core.h @@ -0,0 +1,568 @@ +#pragma once +#include "optick.config.h" + +#if USE_OPTICK + +#include <mutex> +#include <thread> + +#include "optick_common.h" + +#include "optick_memory.h" +#include "optick_message.h" +#include "optick_serialization.h" + +#include "optick_gpu.h" + +#include <atomic> + +// 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<Optick::StringHash> + { + 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<EventData> categories; + vector<EventData> 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<int N> +struct OptickString +{ + char data[N]; + OptickString() {} + OptickString<N>& 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<int N> +OutputDataStream& operator<<(OutputDataStream &stream, const OptickString<N>& 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<EventData, 1024> EventBuffer; +typedef MemoryPool<const EventData*, 32> CategoryBuffer; +typedef MemoryPool<SyncData, 1024> SynchronizationBuffer; +typedef MemoryPool<FiberSyncData, 1024> FiberSyncBuffer; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef OptickString<32> ShortString; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef TagData<float> TagFloat; +typedef TagData<int32> TagS32; +typedef TagData<uint32> TagU32; +typedef TagData<uint64> TagU64; +typedef TagData<Point> TagPoint; +typedef TagData<ShortString> TagString; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef MemoryPool<TagFloat, 1024> TagFloatBuffer; +typedef MemoryPool<TagS32, 1024> TagS32Buffer; +typedef MemoryPool<TagU32, 1024> TagU32Buffer; +typedef MemoryPool<TagU64, 1024> TagU64Buffer; +typedef MemoryPool<TagPoint, 64> TagPointBuffer; +typedef MemoryPool<TagString, 1024> TagStringBuffer; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Base64 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +string base64_decode(string const& encoded_string); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Board +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef MemoryPool<EventDescription, 4096> EventDescriptionList; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class EventDescriptionBoard +{ + // List of stored Event Descriptions + EventDescriptionList boardDescriptions; + + // Shared Descriptions + typedef unordered_map<StringHash, EventDescription*> 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<array<EventBuffer, GPU_QUEUE_COUNT>, MAX_GPU_NODES> gpuBuffer; + GPUContext context; + + void Clear(bool preserveMemory); + + EventData* Start(const EventDescription& desc); + void Stop(EventData& data); + }; + GPUStorage gpuStorage; + + uint32 pushPopEventStackIndex; + array<EventData*, 32> 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<ThreadEntry*> ThreadList; +typedef vector<FiberEntry*> FiberList; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct SysCallData : EventData +{ + uint64 id; + uint64 threadID; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OutputDataStream &operator << (OutputDataStream &stream, const SysCallData &ob); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class SysCallCollector +{ + typedef MemoryPool<SysCallData, 1024 * 32> 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<uint64, 1024 * 32> 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<SwitchContextDesc, 1024 * 32> 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<EventTime> frames; + uint32 boardNumber; + + CallstackCollector callstackCollector; + SwitchContextCollector switchContextCollector; + + vector<std::pair<string, string>> summary; + + std::atomic<uint32_t> frameNumber; + + struct Attachment + { + string name; + vector<uint8_t> data; + File::Type type; + Attachment(File::Type t, const char* n) : name(n), type(t) {} + }; + list<Attachment> attachments; + + StateCallback stateCallback; + + vector<ProcessDescription> processDescs; + vector<ThreadDescription> threadDescs; + + array<const EventDescription*, FrameType::COUNT> 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<ThreadEntry*>& 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
\ No newline at end of file diff --git a/external/optick/optick_core.linux.h b/external/optick/optick_core.linux.h new file mode 100644 index 0000000..e0f4b49 --- /dev/null +++ b/external/optick/optick_core.linux.h @@ -0,0 +1,410 @@ +#pragma once +#if defined(__linux__) + +#include "optick.config.h" +#if USE_OPTICK + +#include "optick_core.platform.h" + +#include <sys/syscall.h> +#include <sys/time.h> +#include <sys/types.h> +#include <pthread.h> +#include <unistd.h> + +namespace Optick +{ + const char* Platform::GetName() + { + return "Linux"; + } + + ThreadID Platform::GetThreadID() + { + return syscall(SYS_gettid); + } + + ProcessID Platform::GetProcessID() + { + return (ProcessID)getpid(); + } + + int64 Platform::GetFrequency() + { + return 1000000000; + } + + int64 Platform::GetTime() + { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; + } +} + +#if OPTICK_ENABLE_TRACING + +#include "optick_memory.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +namespace ft +{ + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + struct base_event + { + int64_t timestamp; + short common_type; + uint8_t cpu_id; + base_event(short type) : timestamp(-1), common_type(type), cpu_id(uint8_t(-1)) {} +}; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + template<short TYPE> + struct event : public base_event + { + static const short type = TYPE; + event() : base_event(TYPE) {} + }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + struct process_state + { + enum type + { + Unknown, + //D Uninterruptible sleep(usually IO) + UninterruptibleSleep, + //R Running or runnable(on run queue) + Running, + //S Interruptible sleep(waiting for an event to complete) + InterruptibleSleep, + //T Stopped, either by a job control signal or because it is being traced. + Stopped, + //X dead(should never be seen) + Dead, + //Z Defunct(“zombie”) process, terminated but not reaped by its parent. + Zombie, + }; + }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + struct sched_switch : public event<305> + { + char prev_comm[16]; + pid_t prev_pid; + int prev_prio; + process_state::type prev_state; + char next_comm[16]; + pid_t next_pid; + int next_prio; + }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} // namespace ft +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace Optick +{ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static const char* KERNEL_TRACING_PATH = "/sys/kernel/debug/tracing"; +static const char* FTRACE_TRACE = "trace"; +static const char* FTRACE_TRACING_ON = "tracing_on"; +static const char* FTRACE_TRACE_CLOCK = "trace_clock"; +static const char* FTRACE_OPTIONS_IRQ_INFO = "options/irq-info"; +static const char* FTRACE_SCHED_SWITCH = "events/sched/sched_switch/enable"; +static const uint8_t PROCESS_STATE_REASON_START = 38; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class FTrace : public Trace +{ + bool isActive; + string password; + + bool Parse(const char* line); + bool ProcessEvent(const ft::base_event& ev); + + void Set(const char* name, bool value); + void Set(const char* name, const char* value); + void Exec(const char* cmd); +public: + + FTrace(); + ~FTrace(); + + virtual void SetPassword(const char* pwd) override { password = pwd; } + virtual CaptureStatus::Type Start(Mode::Type mode, int frequency, const ThreadList& threads) override; + virtual bool Stop() override; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +FTrace g_FTrace; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct Parser +{ + const char* cursor; + const char* finish; + size_t length; + + Parser(const char* b) : cursor(b), finish(b + strlen(b)) {} + + bool Skip(size_t count) + { + if ((size_t)(finish - cursor) > count) + { + cursor += count; + return true; + } + return false; + } + + bool Skip(const char* text, char* output = nullptr, size_t size = 0) + { + if (const char* ptr = strstr(cursor, text)) + { + if (output != nullptr) + { + size_t count = std::min(size - 1, (size_t)(ptr - cursor)); + strncpy(output, cursor, count); + output[count] = '\0'; + } + cursor = ptr + strlen(text); + return true; + } + return false; + } + + void SkipSpaces() + { + while (cursor != finish && (*cursor == ' ' || *cursor == '\t' || *cursor == '\n')) + ++cursor; + } + + bool Starts(const char* text) const + { + return strncmp(cursor, text, strlen(text)) == 0; + } + + int GetInt() const + { + return atoi(cursor); + } + + char GetChar() const + { + return *cursor; + } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +CaptureStatus::Type FTrace::Start(Mode::Type mode, int /*frequency*/, const ThreadList& /*threads*/) +{ + if (!isActive) + { + // Disable tracing + Set(FTRACE_TRACING_ON, false); + // Cleanup old data + Set(FTRACE_TRACE, ""); + // Set clock type + Set(FTRACE_TRACE_CLOCK, "mono"); + // Disable irq info + Set(FTRACE_OPTIONS_IRQ_INFO, false); + // Enable switch events + Set(FTRACE_SCHED_SWITCH, (mode & Mode::SWITCH_CONTEXT) != 0); + + // Enable tracing + Set(FTRACE_TRACING_ON, true); + + isActive = true; + } + + return CaptureStatus::OK; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool FTrace::Stop() +{ + if (!isActive) + { + return false; + } + + // Reset variables + Set(FTRACE_TRACING_ON, false); + Set(FTRACE_SCHED_SWITCH, false); + + // Parsing the output + char buffer[256] = { 0 }; + sprintf_s(buffer, "echo \'%s\' | sudo -S sh -c \'cat %s/%s\'", password.c_str(), KERNEL_TRACING_PATH, FTRACE_TRACE); + if (FILE* pipe = popen(buffer, "r")) + { + char* line = NULL; + size_t len = 0; + while ((getline(&line, &len, pipe)) != -1) + Parse(line); + fclose(pipe); + } + + // Cleanup data + Set(FTRACE_TRACE, ""); + + isActive = false; + + return true; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool FTrace::Parse(const char * line) +{ + // sched_switch: + // ConsoleApp-8687 [000] 181944.352057: sched_switch: prev_comm=ConsoleApp prev_pid=8687 prev_prio=120 prev_state=S ==> next_comm=ConsoleApp next_pid=8686 next_prio=120 + + Parser p(line); + if (p.Starts("#")) + return true; + + if (!p.Skip(16)) + return false; + + if (!p.Skip("[")) + return false; + + int cpu = p.GetInt(); + if (!p.Skip("]")) + return false; + + int64 timestampInt = p.GetInt(); + if (!p.Skip(".")) + return false; + + int64 timestampFraq = p.GetInt(); + if (!p.Skip(": ")) + return false; + + int64 timestamp = ((timestampInt * 1000000) + timestampFraq) * 1000; + + if (p.Starts("sched_switch:")) + { + ft::sched_switch ev; + ev.cpu_id = cpu; + ev.timestamp = timestamp; + + if (!p.Skip("prev_comm=")) + return false; + + if (!p.Skip(" prev_pid=", ev.prev_comm, OPTICK_ARRAY_SIZE(ev.prev_comm))) + return false; + + ev.prev_pid = p.GetInt(); + + if (!p.Skip(" prev_prio=")) + return false; + + ev.prev_prio = p.GetInt(); + + if (!p.Skip(" prev_state=")) + return false; + + switch (p.GetChar()) + { + case 'D': + ev.prev_state = ft::process_state::UninterruptibleSleep; + break; + + case 'R': + ev.prev_state = ft::process_state::Running; + break; + + case 'S': + ev.prev_state = ft::process_state::InterruptibleSleep; + break; + + case 'T': + ev.prev_state = ft::process_state::Stopped; + break; + + case 'X': + ev.prev_state = ft::process_state::Dead; + break; + + case 'Z': + ev.prev_state = ft::process_state::Zombie; + break; + + default: + ev.prev_state = ft::process_state::Unknown; + break; + } + + if (!p.Skip("==> next_comm=")) + return false; + + if (!p.Skip(" next_pid=", ev.next_comm, OPTICK_ARRAY_SIZE(ev.prev_comm))) + return false; + + ev.next_pid = p.GetInt(); + + if (!p.Skip(" next_prio=")) + return false; + + ev.next_prio = p.GetInt(); + + return ProcessEvent(ev); + } + return true; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool FTrace::ProcessEvent(const ft::base_event& ev) +{ + switch (ev.common_type) + { + case ft::sched_switch::type: + { + const ft::sched_switch& switchEv = (const ft::sched_switch&)ev; + SwitchContextDesc desc; + desc.reason = switchEv.prev_state + PROCESS_STATE_REASON_START; + desc.cpuId = switchEv.cpu_id; + desc.oldThreadId = (uint64)switchEv.prev_pid; + desc.newThreadId = (uint64)switchEv.next_pid; + desc.timestamp = switchEv.timestamp; + Core::Get().ReportSwitchContext(desc); + return true; + } + break; + } + + return false; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void FTrace::Set(const char * name, bool value) +{ + Set(name, value ? "1" : "0"); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void FTrace::Set(const char* name, const char* value) +{ + char buffer[256] = { 0 }; + sprintf_s(buffer, "echo %s > %s/%s", value, KERNEL_TRACING_PATH, name); + Exec(buffer); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void FTrace::Exec(const char* cmd) +{ + char buffer[256] = { 0 }; + sprintf_s(buffer, "echo \'%s\' | sudo -S sh -c \'%s\'", password.c_str(), cmd); + std::system(buffer); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +FTrace::FTrace() : isActive(false) +{ +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +FTrace::~FTrace() +{ + Stop(); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Trace* Platform::GetTrace() +{ + return &g_FTrace; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SymbolEngine* Platform::GetSymbolEngine() +{ + return nullptr; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} +#endif //OPTICK_ENABLE_TRACING +#endif //USE_OPTICK +#endif //__linux__
\ No newline at end of file diff --git a/external/optick/optick_core.macos.h b/external/optick/optick_core.macos.h new file mode 100644 index 0000000..3d19bfd --- /dev/null +++ b/external/optick/optick_core.macos.h @@ -0,0 +1,289 @@ +#pragma once +#if defined(__APPLE_CC__) + +#include "optick.config.h" +#if USE_OPTICK + +#include "optick_core.platform.h" + +#include <mach/mach_time.h> +#include <sys/time.h> +#include <sys/types.h> +#include <pthread.h> +#include <unistd.h> + +namespace Optick +{ + const char* Platform::GetName() + { + return "MacOS"; + } + + ThreadID Platform::GetThreadID() + { + uint64_t tid; + pthread_threadid_np(pthread_self(), &tid); + return tid; + } + + ProcessID Platform::GetProcessID() + { + return (ProcessID)getpid(); + } + + int64 Platform::GetFrequency() + { + return 1000000000; + } + + int64 Platform::GetTime() + { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; + } +} + +#if OPTICK_ENABLE_TRACING + +#include "optick_core.h" + +namespace Optick +{ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class DTrace : public Trace +{ + static const bool isSilent = true; + + std::thread processThread; + string password; + + enum State + { + STATE_IDLE, + STATE_RUNNING, + STATE_ABORT, + }; + + volatile State state; + volatile int64 timeout; + + struct CoreState + { + ProcessID pid; + ThreadID tid; + int prio; + bool IsValid() const { return tid != INVALID_THREAD_ID; } + CoreState() : pid(INVALID_PROCESS_ID), tid(INVALID_THREAD_ID), prio(0) {} + }; + static const int MAX_CPU_CORES = 256; + array<CoreState, MAX_CPU_CORES> cores; + + static void AsyncProcess(DTrace* trace); + void Process(); + + bool CheckRootAccess(); + + enum ParseResult + { + PARSE_OK, + PARSE_TIMEOUT, + PARSE_FAILED, + }; + ParseResult Parse(const char* line); +public: + + DTrace(); + + virtual void SetPassword(const char* pwd) override { password = pwd; } + virtual CaptureStatus::Type Start(Mode::Type mode, int frequency, const ThreadList& threads) override; + virtual bool Stop() override; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +DTrace g_DTrace; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +DTrace::DTrace() : state(STATE_IDLE), timeout(0) +{ +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool DTrace::CheckRootAccess() +{ + char cmd[256] = { 0 }; + sprintf_s(cmd, "echo \'%s\' | sudo -S echo %s", password.c_str(), isSilent ? "2> /dev/null" : ""); + return system(cmd) == 0; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +CaptureStatus::Type DTrace::Start(Mode::Type mode, int /*frequency*/, const ThreadList& /*threads*/) +{ + if (state == STATE_IDLE && (mode & Mode::SWITCH_CONTEXT) != 0) + { + if (!CheckRootAccess()) + return CaptureStatus::ERR_TRACER_INVALID_PASSWORD; + + state = STATE_RUNNING; + timeout = INT64_MAX; + cores.fill(CoreState()); + processThread = std::thread(AsyncProcess, this); + } + + return CaptureStatus::OK; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool DTrace::Stop() +{ + if (state != STATE_RUNNING) + { + return false; + } + + timeout = Platform::GetTime(); + processThread.join(); + state = STATE_IDLE; + + return true; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +FILE* popen2(const char *program, const char *type, pid_t* outPid) +{ + FILE *iop; + int pdes[2]; + pid_t pid; + if ((*type != 'r' && *type != 'w') || type[1] != '\0') { + errno = EINVAL; + return (NULL); + } + + if (pipe(pdes) < 0) { + return (NULL); + } + + switch (pid = fork()) { + case -1: /* Error. */ + (void)close(pdes[0]); + (void)close(pdes[1]); + return (NULL); + /* NOTREACHED */ + case 0: /* Child. */ + { + if (*type == 'r') { + (void)close(pdes[0]); + if (pdes[1] != STDOUT_FILENO) { + (void)dup2(pdes[1], STDOUT_FILENO); + (void)close(pdes[1]); + } + } + else { + (void)close(pdes[1]); + if (pdes[0] != STDIN_FILENO) { + (void)dup2(pdes[0], STDIN_FILENO); + (void)close(pdes[0]); + } + } + execl("/bin/sh", "sh", "-c", program, NULL); + perror("execl"); + exit(1); + /* NOTREACHED */ + } + } + /* Parent; assume fdopen can't fail. */ + if (*type == 'r') { + iop = fdopen(pdes[0], type); + (void)close(pdes[1]); + } + else { + iop = fdopen(pdes[1], type); + (void)close(pdes[0]); + } + + if (outPid) + *outPid = pid; + + return (iop); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void DTrace::Process() +{ + const char* command = "dtrace -n fbt::thread_dispatch:return'\\''{printf(\"@%d %d %d %d\", pid, tid, curthread->sched_pri, walltimestamp)}'\\''"; + + char buffer[256] = { 0 }; + sprintf_s(buffer, "echo \'%s\' | sudo -S sh -c \'%s\' %s", password.c_str(), command, isSilent ? "2> /dev/null" : ""); + pid_t pid; + if (FILE* pipe = popen2(buffer, "r", &pid)) + { + char* line = NULL; + size_t len = 0; + while (state == STATE_RUNNING && (getline(&line, &len, pipe)) != -1) + { + if (Parse(line) == PARSE_TIMEOUT) + break; + } + fclose(pipe); + + int internal_stat; + waitpid(pid, &internal_stat, 0); + } + else + { + OPTICK_FAILED("Failed to open communication pipe!"); + } +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +DTrace::ParseResult DTrace::Parse(const char* line) +{ + if (const char* cmd = strchr(line, '@')) + { + int cpu = atoi(line); + + CoreState currState; + + currState.pid = atoi(cmd + 1); + cmd = strchr(cmd, ' ') + 1; + + currState.tid = atoi(cmd); + cmd = strchr(cmd, ' ') + 1; + + currState.prio = atoi(cmd); + cmd = strchr(cmd, ' ') + 1; + + int64_t timestamp = (int64_t)atoll(cmd); + + if (timestamp > timeout) + return PARSE_TIMEOUT; + + const CoreState& prevState = cores[cpu]; + + if (prevState.IsValid()) + { + SwitchContextDesc desc; + desc.reason = 0; + desc.cpuId = cpu; + desc.oldThreadId = prevState.tid; + desc.newThreadId = currState.tid; + desc.timestamp = timestamp; + Core::Get().ReportSwitchContext(desc); + } + + cores[cpu] = currState; + } + return PARSE_FAILED; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void DTrace::AsyncProcess(DTrace *trace) { + trace->Process(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Trace* Platform::GetTrace() +{ + return &g_DTrace; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SymbolEngine* Platform::GetSymbolEngine() +{ + return nullptr; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} +#endif //OPTICK_ENABLE_TRACING +#endif //USE_OPTICK +#endif //__APPLE_CC__
\ No newline at end of file diff --git a/external/optick/optick_core.platform.h b/external/optick/optick_core.platform.h new file mode 100644 index 0000000..683376d --- /dev/null +++ b/external/optick/optick_core.platform.h @@ -0,0 +1,92 @@ +#pragma once +#include "optick.config.h" + +#if USE_OPTICK + +#include "optick_common.h" +#include "optick_memory.h" + +////////////////////////////////////////////////////////////////////////// +// Platform-specific stuff +////////////////////////////////////////////////////////////////////////// +namespace Optick +{ + struct Trace; + struct Module; + struct Symbol; + struct SymbolEngine; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Platform API + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + struct Platform + { + // Platform Name + static OPTICK_INLINE const char* GetName(); + // Thread ID (system thread id) + static OPTICK_INLINE ThreadID GetThreadID(); + // Process ID + static OPTICK_INLINE ProcessID GetProcessID(); + // CPU Frequency + static OPTICK_INLINE int64 GetFrequency(); + // CPU Time (Ticks) + static OPTICK_INLINE int64 GetTime(); + // System Tracer + static OPTICK_INLINE Trace* GetTrace(); + // Symbol Resolver + static OPTICK_INLINE SymbolEngine* GetSymbolEngine(); + }; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Tracing API + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + struct Trace + { + virtual void SetPassword(const char* /*pwd*/) {}; + virtual CaptureStatus::Type Start(Mode::Type mode, int frequency, const ThreadList& threads) = 0; + virtual bool Stop() = 0; + virtual ~Trace() {}; + }; + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Symbol API + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + struct Module + { + string path; + void* address; + size_t size; + Module(const char* p, void* a, size_t s) : path(p), address(a), size(s) {} + }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + struct Symbol + { + uint64 address; + uint64 offset; + wstring file; + wstring function; + uint32 line; + Symbol() + : address(0) + , offset(0) + , line(0) + {} + }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + struct SymbolEngine + { + // Get list of loaded modules + virtual const vector<Module>& GetModules() = 0; + + // Get Symbol from address + virtual const Symbol* GetSymbol(uint64 dwAddress) = 0; + + virtual ~SymbolEngine() {}; + }; +} +////////////////////////////////////////////////////////////////////////// + +#endif //USE_OPTICK
\ No newline at end of file diff --git a/external/optick/optick_core.win.h b/external/optick/optick_core.win.h new file mode 100644 index 0000000..0d8a11a --- /dev/null +++ b/external/optick/optick_core.win.h @@ -0,0 +1,1664 @@ +#pragma once +#if defined(_MSC_VER) + +#include "optick.config.h" + +#if USE_OPTICK + +#include "optick_core.platform.h" + +namespace Optick +{ + const char* Platform::GetName() + { + #if defined(OPTICK_PC) + return "Windows"; + #else + return "XBox"; + #endif + } + + ThreadID Platform::GetThreadID() + { + return GetCurrentThreadId(); + } + + ProcessID Platform::GetProcessID() + { + return GetCurrentProcessId(); + } + + int64 Platform::GetFrequency() + { + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + return frequency.QuadPart; + } + + int64 Platform::GetTime() + { + LARGE_INTEGER largeInteger; + QueryPerformanceCounter(&largeInteger); + return largeInteger.QuadPart; + } +} + +#if OPTICK_ENABLE_TRACING +#include <psapi.h> +#include "optick_core.h" + +/* +Event Tracing Functions - API +https://msdn.microsoft.com/en-us/library/windows/desktop/aa363795(v=vs.85).aspx +*/ + +#define DECLARE_ETW (!OPTICK_PC) + +#if DECLARE_ETW +// Copied from Windows SDK +#ifndef WMIAPI +#ifndef MIDL_PASS +#ifdef _WMI_SOURCE_ +#define WMIAPI __stdcall +#else +#define WMIAPI DECLSPEC_IMPORT __stdcall +#endif // _WMI_SOURCE +#endif // MIDL_PASS +#endif // WMIAPI +#define INITGUID +#include <guiddef.h> +#if defined(_NTDDK_) || defined(_NTIFS_) || defined(_WMIKM_) +#define _EVNTRACE_KERNEL_MODE +#endif +#if !defined(_EVNTRACE_KERNEL_MODE) +#include <wmistr.h> +#endif + +#if _MSC_VER <= 1600 +#define EVENT_DESCRIPTOR_DEF +#define EVENT_HEADER_DEF +#define EVENT_HEADER_EXTENDED_DATA_ITEM_DEF +#define EVENT_RECORD_DEF +#endif + +#ifndef _TRACEHANDLE_DEFINED +#define _TRACEHANDLE_DEFINED +typedef ULONG64 TRACEHANDLE, *PTRACEHANDLE; +#endif + +// +// EventTraceGuid is used to identify a event tracing session +// +DEFINE_GUID( /* 68fdd900-4a3e-11d1-84f4-0000f80464e3 */ + EventTraceGuid, + 0x68fdd900, + 0x4a3e, + 0x11d1, + 0x84, 0xf4, 0x00, 0x00, 0xf8, 0x04, 0x64, 0xe3 +); + +// +// SystemTraceControlGuid. Used to specify event tracing for kernel +// +DEFINE_GUID( /* 9e814aad-3204-11d2-9a82-006008a86939 */ + SystemTraceControlGuid, + 0x9e814aad, + 0x3204, + 0x11d2, + 0x9a, 0x82, 0x00, 0x60, 0x08, 0xa8, 0x69, 0x39 +); + +// +// EventTraceConfigGuid. Used to report system configuration records +// +DEFINE_GUID( /* 01853a65-418f-4f36-aefc-dc0f1d2fd235 */ + EventTraceConfigGuid, + 0x01853a65, + 0x418f, + 0x4f36, + 0xae, 0xfc, 0xdc, 0x0f, 0x1d, 0x2f, 0xd2, 0x35 +); + +// +// DefaultTraceSecurityGuid. Specifies the default event tracing security +// +DEFINE_GUID( /* 0811c1af-7a07-4a06-82ed-869455cdf713 */ + DefaultTraceSecurityGuid, + 0x0811c1af, + 0x7a07, + 0x4a06, + 0x82, 0xed, 0x86, 0x94, 0x55, 0xcd, 0xf7, 0x13 +); + + +/////////////////////////////////////////////////////////////////////////////// +#define PROCESS_TRACE_MODE_REAL_TIME 0x00000100 +#define PROCESS_TRACE_MODE_RAW_TIMESTAMP 0x00001000 +#define PROCESS_TRACE_MODE_EVENT_RECORD 0x10000000 +/////////////////////////////////////////////////////////////////////////////// +#define EVENT_HEADER_FLAG_EXTENDED_INFO 0x0001 +#define EVENT_HEADER_FLAG_PRIVATE_SESSION 0x0002 +#define EVENT_HEADER_FLAG_STRING_ONLY 0x0004 +#define EVENT_HEADER_FLAG_TRACE_MESSAGE 0x0008 +#define EVENT_HEADER_FLAG_NO_CPUTIME 0x0010 +#define EVENT_HEADER_FLAG_32_BIT_HEADER 0x0020 +#define EVENT_HEADER_FLAG_64_BIT_HEADER 0x0040 +#define EVENT_HEADER_FLAG_CLASSIC_HEADER 0x0100 +#define EVENT_HEADER_FLAG_PROCESSOR_INDEX 0x0200 +/////////////////////////////////////////////////////////////////////////////// +#define KERNEL_LOGGER_NAMEW L"NT Kernel Logger" +/////////////////////////////////////////////////////////////////////////////// +#define EVENT_TRACE_REAL_TIME_MODE 0x00000100 // Real time mode on +/////////////////////////////////////////////////////////////////////////////// +#define EVENT_TRACE_CONTROL_STOP 1 +/////////////////////////////////////////////////////////////////////////////// + +// +// Enable flags for Kernel Events +// +#define EVENT_TRACE_FLAG_PROCESS 0x00000001 // process start & end +#define EVENT_TRACE_FLAG_THREAD 0x00000002 // thread start & end +#define EVENT_TRACE_FLAG_IMAGE_LOAD 0x00000004 // image load + +#define EVENT_TRACE_FLAG_DISK_IO 0x00000100 // physical disk IO +#define EVENT_TRACE_FLAG_DISK_FILE_IO 0x00000200 // requires disk IO + +#define EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS 0x00001000 // all page faults +#define EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS 0x00002000 // hard faults only + +#define EVENT_TRACE_FLAG_NETWORK_TCPIP 0x00010000 // tcpip send & receive + +#define EVENT_TRACE_FLAG_REGISTRY 0x00020000 // registry calls +#define EVENT_TRACE_FLAG_DBGPRINT 0x00040000 // DbgPrint(ex) Calls + +// +// Enable flags for Kernel Events on Vista and above +// +#define EVENT_TRACE_FLAG_PROCESS_COUNTERS 0x00000008 // process perf counters +#define EVENT_TRACE_FLAG_CSWITCH 0x00000010 // context switches +#define EVENT_TRACE_FLAG_DPC 0x00000020 // deffered procedure calls +#define EVENT_TRACE_FLAG_INTERRUPT 0x00000040 // interrupts +#define EVENT_TRACE_FLAG_SYSTEMCALL 0x00000080 // system calls + +#define EVENT_TRACE_FLAG_DISK_IO_INIT 0x00000400 // physical disk IO initiation +#define EVENT_TRACE_FLAG_ALPC 0x00100000 // ALPC traces +#define EVENT_TRACE_FLAG_SPLIT_IO 0x00200000 // split io traces (VolumeManager) + +#define EVENT_TRACE_FLAG_DRIVER 0x00800000 // driver delays +#define EVENT_TRACE_FLAG_PROFILE 0x01000000 // sample based profiling +#define EVENT_TRACE_FLAG_FILE_IO 0x02000000 // file IO +#define EVENT_TRACE_FLAG_FILE_IO_INIT 0x04000000 // file IO initiation + +#define EVENT_TRACE_FLAG_PMC_PROFILE 0x80000000 // sample based profiling (PMC) - NOT CONFIRMED! + +// +// Enable flags for Kernel Events on Win7 and above +// +#define EVENT_TRACE_FLAG_DISPATCHER 0x00000800 // scheduler (ReadyThread) +#define EVENT_TRACE_FLAG_VIRTUAL_ALLOC 0x00004000 // VM operations + +// +// Enable flags for Kernel Events on Win8 and above +// +#define EVENT_TRACE_FLAG_VAMAP 0x00008000 // map/unmap (excluding images) +#define EVENT_TRACE_FLAG_NO_SYSCONFIG 0x10000000 // Do not do sys config rundown + +/////////////////////////////////////////////////////////////////////////////// + +#pragma warning(push) +#pragma warning (disable:4201) + +#ifndef EVENT_DESCRIPTOR_DEF +#define EVENT_DESCRIPTOR_DEF +typedef struct _EVENT_DESCRIPTOR { + + USHORT Id; + UCHAR Version; + UCHAR Channel; + UCHAR Level; + UCHAR Opcode; + USHORT Task; + ULONGLONG Keyword; + +} EVENT_DESCRIPTOR, *PEVENT_DESCRIPTOR; +typedef const EVENT_DESCRIPTOR *PCEVENT_DESCRIPTOR; +#endif +/////////////////////////////////////////////////////////////////////////////// +#ifndef EVENT_HEADER_DEF +#define EVENT_HEADER_DEF +typedef struct _EVENT_HEADER { + + USHORT Size; + USHORT HeaderType; + USHORT Flags; + USHORT EventProperty; + ULONG ThreadId; + ULONG ProcessId; + LARGE_INTEGER TimeStamp; + GUID ProviderId; + EVENT_DESCRIPTOR EventDescriptor; + union { + struct { + ULONG KernelTime; + ULONG UserTime; + } DUMMYSTRUCTNAME; + ULONG64 ProcessorTime; + + } DUMMYUNIONNAME; + GUID ActivityId; + +} EVENT_HEADER, *PEVENT_HEADER; +#endif +/////////////////////////////////////////////////////////////////////////////// +#ifndef EVENT_HEADER_EXTENDED_DATA_ITEM_DEF +#define EVENT_HEADER_EXTENDED_DATA_ITEM_DEF +typedef struct _EVENT_HEADER_EXTENDED_DATA_ITEM { + + USHORT Reserved1; // Reserved for internal use + USHORT ExtType; // Extended info type + struct { + USHORT Linkage : 1; // Indicates additional extended + // data item + USHORT Reserved2 : 15; + }; + USHORT DataSize; // Size of extended info data + ULONGLONG DataPtr; // Pointer to extended info data + +} EVENT_HEADER_EXTENDED_DATA_ITEM, *PEVENT_HEADER_EXTENDED_DATA_ITEM; +#endif +/////////////////////////////////////////////////////////////////////////////// +#ifndef ETW_BUFFER_CONTEXT_DEF +#define ETW_BUFFER_CONTEXT_DEF +typedef struct _ETW_BUFFER_CONTEXT { + union { + struct { + UCHAR ProcessorNumber; + UCHAR Alignment; + } DUMMYSTRUCTNAME; + USHORT ProcessorIndex; + } DUMMYUNIONNAME; + USHORT LoggerId; +} ETW_BUFFER_CONTEXT, *PETW_BUFFER_CONTEXT; +#endif +/////////////////////////////////////////////////////////////////////////////// +#ifndef EVENT_RECORD_DEF +#define EVENT_RECORD_DEF +typedef struct _EVENT_RECORD { + EVENT_HEADER EventHeader; + ETW_BUFFER_CONTEXT BufferContext; + USHORT ExtendedDataCount; + + USHORT UserDataLength; + PEVENT_HEADER_EXTENDED_DATA_ITEM ExtendedData; + PVOID UserData; + PVOID UserContext; +} EVENT_RECORD, *PEVENT_RECORD; +#endif +/////////////////////////////////////////////////////////////////////////////// +typedef struct _EVENT_TRACE_PROPERTIES { + WNODE_HEADER Wnode; + // + // data provided by caller + ULONG BufferSize; // buffer size for logging (kbytes) + ULONG MinimumBuffers; // minimum to preallocate + ULONG MaximumBuffers; // maximum buffers allowed + ULONG MaximumFileSize; // maximum logfile size (in MBytes) + ULONG LogFileMode; // sequential, circular + ULONG FlushTimer; // buffer flush timer, in seconds + ULONG EnableFlags; // trace enable flags + union { + LONG AgeLimit; // unused + LONG FlushThreshold; // Number of buffers to fill before flushing + } DUMMYUNIONNAME; + + // data returned to caller + ULONG NumberOfBuffers; // no of buffers in use + ULONG FreeBuffers; // no of buffers free + ULONG EventsLost; // event records lost + ULONG BuffersWritten; // no of buffers written to file + ULONG LogBuffersLost; // no of logfile write failures + ULONG RealTimeBuffersLost; // no of rt delivery failures + HANDLE LoggerThreadId; // thread id of Logger + ULONG LogFileNameOffset; // Offset to LogFileName + ULONG LoggerNameOffset; // Offset to LoggerName +} EVENT_TRACE_PROPERTIES, *PEVENT_TRACE_PROPERTIES; + +typedef struct _EVENT_TRACE_HEADER { // overlays WNODE_HEADER + USHORT Size; // Size of entire record + union { + USHORT FieldTypeFlags; // Indicates valid fields + struct { + UCHAR HeaderType; // Header type - internal use only + UCHAR MarkerFlags; // Marker - internal use only + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME; + union { + ULONG Version; + struct { + UCHAR Type; // event type + UCHAR Level; // trace instrumentation level + USHORT Version; // version of trace record + } Class; + } DUMMYUNIONNAME2; + ULONG ThreadId; // Thread Id + ULONG ProcessId; // Process Id + LARGE_INTEGER TimeStamp; // time when event happens + union { + GUID Guid; // Guid that identifies event + ULONGLONG GuidPtr; // use with WNODE_FLAG_USE_GUID_PTR + } DUMMYUNIONNAME3; + union { + struct { + ULONG KernelTime; // Kernel Mode CPU ticks + ULONG UserTime; // User mode CPU ticks + } DUMMYSTRUCTNAME; + ULONG64 ProcessorTime; // Processor Clock + struct { + ULONG ClientContext; // Reserved + ULONG Flags; // Event Flags + } DUMMYSTRUCTNAME2; + } DUMMYUNIONNAME4; +} EVENT_TRACE_HEADER, *PEVENT_TRACE_HEADER; + +typedef struct _EVENT_TRACE { + EVENT_TRACE_HEADER Header; // Event trace header + ULONG InstanceId; // Instance Id of this event + ULONG ParentInstanceId; // Parent Instance Id. + GUID ParentGuid; // Parent Guid; + PVOID MofData; // Pointer to Variable Data + ULONG MofLength; // Variable Datablock Length + union { + ULONG ClientContext; + ETW_BUFFER_CONTEXT BufferContext; + } DUMMYUNIONNAME; +} EVENT_TRACE, *PEVENT_TRACE; + +typedef struct _TRACE_LOGFILE_HEADER { + ULONG BufferSize; // Logger buffer size in Kbytes + union { + ULONG Version; // Logger version + struct { + UCHAR MajorVersion; + UCHAR MinorVersion; + UCHAR SubVersion; + UCHAR SubMinorVersion; + } VersionDetail; + } DUMMYUNIONNAME; + ULONG ProviderVersion; // defaults to NT version + ULONG NumberOfProcessors; // Number of Processors + LARGE_INTEGER EndTime; // Time when logger stops + ULONG TimerResolution; // assumes timer is constant!!! + ULONG MaximumFileSize; // Maximum in Mbytes + ULONG LogFileMode; // specify logfile mode + ULONG BuffersWritten; // used to file start of Circular File + union { + GUID LogInstanceGuid; // For RealTime Buffer Delivery + struct { + ULONG StartBuffers; // Count of buffers written at start. + ULONG PointerSize; // Size of pointer type in bits + ULONG EventsLost; // Events losts during log session + ULONG CpuSpeedInMHz; // Cpu Speed in MHz + } DUMMYSTRUCTNAME; + } DUMMYUNIONNAME2; +#if defined(_WMIKM_) + PWCHAR LoggerName; + PWCHAR LogFileName; + RTL_TIME_ZONE_INFORMATION TimeZone; +#else + LPWSTR LoggerName; + LPWSTR LogFileName; + TIME_ZONE_INFORMATION TimeZone; +#endif + LARGE_INTEGER BootTime; + LARGE_INTEGER PerfFreq; // Reserved + LARGE_INTEGER StartTime; // Reserved + ULONG ReservedFlags; // ClockType + ULONG BuffersLost; +} TRACE_LOGFILE_HEADER, *PTRACE_LOGFILE_HEADER; + +typedef enum _TRACE_QUERY_INFO_CLASS { + TraceGuidQueryList, + TraceGuidQueryInfo, + TraceGuidQueryProcess, + TraceStackTracingInfo, // Win7 + TraceSystemTraceEnableFlagsInfo, + TraceSampledProfileIntervalInfo, + TraceProfileSourceConfigInfo, + TraceProfileSourceListInfo, + TracePmcEventListInfo, + TracePmcCounterListInfo, + MaxTraceSetInfoClass +} TRACE_QUERY_INFO_CLASS, TRACE_INFO_CLASS; + +typedef struct _CLASSIC_EVENT_ID { + GUID EventGuid; + UCHAR Type; + UCHAR Reserved[7]; +} CLASSIC_EVENT_ID, *PCLASSIC_EVENT_ID; + +typedef struct _TRACE_PROFILE_INTERVAL { + ULONG Source; + ULONG Interval; +} TRACE_PROFILE_INTERVAL, *PTRACE_PROFILE_INTERVAL; + +typedef struct _EVENT_TRACE_LOGFILEW +EVENT_TRACE_LOGFILEW, *PEVENT_TRACE_LOGFILEW; + +typedef ULONG(WINAPI * PEVENT_TRACE_BUFFER_CALLBACKW) +(PEVENT_TRACE_LOGFILEW Logfile); + +typedef VOID(WINAPI *PEVENT_CALLBACK)(PEVENT_TRACE pEvent); + +typedef struct _EVENT_RECORD +EVENT_RECORD, *PEVENT_RECORD; + +typedef VOID(WINAPI *PEVENT_RECORD_CALLBACK) (PEVENT_RECORD EventRecord); + +struct _EVENT_TRACE_LOGFILEW { + LPWSTR LogFileName; // Logfile Name + LPWSTR LoggerName; // LoggerName + LONGLONG CurrentTime; // timestamp of last event + ULONG BuffersRead; // buffers read to date + union { + // Mode of the logfile + ULONG LogFileMode; + // Processing flags used on Vista and above + ULONG ProcessTraceMode; + } DUMMYUNIONNAME; + EVENT_TRACE CurrentEvent; // Current Event from this stream. + TRACE_LOGFILE_HEADER LogfileHeader; // logfile header structure + PEVENT_TRACE_BUFFER_CALLBACKW // callback before each buffer + BufferCallback; // is read + // + // following variables are filled for BufferCallback. + // + ULONG BufferSize; + ULONG Filled; + ULONG EventsLost; + // + // following needs to be propaged to each buffer + // + union { + // Callback with EVENT_TRACE + PEVENT_CALLBACK EventCallback; + // Callback with EVENT_RECORD on Vista and above + PEVENT_RECORD_CALLBACK EventRecordCallback; + } DUMMYUNIONNAME2; + + ULONG IsKernelTrace; // TRUE for kernel logfile + + PVOID Context; // reserved for internal use +}; + +#pragma warning(pop) + +#define PEVENT_TRACE_BUFFER_CALLBACK PEVENT_TRACE_BUFFER_CALLBACKW +#define EVENT_TRACE_LOGFILE EVENT_TRACE_LOGFILEW +#define PEVENT_TRACE_LOGFILE PEVENT_TRACE_LOGFILEW +#define KERNEL_LOGGER_NAME KERNEL_LOGGER_NAMEW +#define GLOBAL_LOGGER_NAME GLOBAL_LOGGER_NAMEW +#define EVENT_LOGGER_NAME EVENT_LOGGER_NAMEW + +EXTERN_C +ULONG +WMIAPI +ProcessTrace( + _In_reads_(HandleCount) PTRACEHANDLE HandleArray, + _In_ ULONG HandleCount, + _In_opt_ LPFILETIME StartTime, + _In_opt_ LPFILETIME EndTime +); + +EXTERN_C +ULONG +WMIAPI +StartTraceW( + _Out_ PTRACEHANDLE TraceHandle, + _In_ LPCWSTR InstanceName, + _Inout_ PEVENT_TRACE_PROPERTIES Properties +); + +EXTERN_C +ULONG +WMIAPI +ControlTraceW( + _In_ TRACEHANDLE TraceHandle, + _In_opt_ LPCWSTR InstanceName, + _Inout_ PEVENT_TRACE_PROPERTIES Properties, + _In_ ULONG ControlCode +); + +EXTERN_C +TRACEHANDLE +WMIAPI +OpenTraceW( + _Inout_ PEVENT_TRACE_LOGFILEW Logfile +); + +EXTERN_C +ULONG +WMIAPI +CloseTrace( + _In_ TRACEHANDLE TraceHandle +); + +EXTERN_C +ULONG +WMIAPI +TraceSetInformation( + _In_ TRACEHANDLE SessionHandle, + _In_ TRACE_INFO_CLASS InformationClass, + _In_reads_bytes_(InformationLength) PVOID TraceInformation, + _In_ ULONG InformationLength +); + +EXTERN_C +ULONG +WMIAPI +TraceQueryInformation( + _In_ TRACEHANDLE SessionHandle, + _In_ TRACE_INFO_CLASS InformationClass, + _Out_writes_bytes_(InformationLength) PVOID TraceInformation, + _In_ ULONG InformationLength, + _Out_opt_ PULONG ReturnLength +); + +////////////////////////////////////////////////////////////////////////// +#define RegisterTraceGuids RegisterTraceGuidsW +#define StartTrace StartTraceW +#define ControlTrace ControlTraceW +#define StopTrace StopTraceW +#define QueryTrace QueryTraceW +#define UpdateTrace UpdateTraceW +#define FlushTrace FlushTraceW +#define QueryAllTraces QueryAllTracesW +#define OpenTrace OpenTraceW +////////////////////////////////////////////////////////////////////////// +#else +#define INITGUID // Causes definition of SystemTraceControlGuid in evntrace.h. +#include <wmistr.h> +#include <evntrace.h> +#include <strsafe.h> +#include <evntcons.h> +#endif //DECLARE_ETW + +namespace Optick +{ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class ETW : public Trace +{ + static const int ETW_BUFFER_SIZE = 1024 << 10; // 1Mb + static const int ETW_BUFFER_COUNT = 32; + static const int ETW_MAXIMUM_SESSION_NAME = 1024; + + EVENT_TRACE_PROPERTIES *traceProperties; + EVENT_TRACE_LOGFILE logFile; + TRACEHANDLE traceSessionHandle; + TRACEHANDLE openedHandle; + + HANDLE processThreadHandle; + DWORD currentProcessId; + + bool isActive; + + static DWORD WINAPI RunProcessTraceThreadFunction(LPVOID parameter); + static void AdjustPrivileges(); + + unordered_map<uint64_t, const EventDescription*> syscallDescriptions; + + void ResolveSysCalls(); +public: + + unordered_set<uint64> activeThreadsIDs; + + ETW(); + ~ETW(); + + virtual CaptureStatus::Type Start(Mode::Type mode, int frequency, const ThreadList& threads) override; + virtual bool Stop() override; + + DWORD GetProcessID() const { return currentProcessId; } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct CSwitch +{ + // New thread ID after the switch. + uint32 NewThreadId; + + // Previous thread ID. + uint32 OldThreadId; + + // Thread priority of the new thread. + int8 NewThreadPriority; + + // Thread priority of the previous thread. + int8 OldThreadPriority; + + //The index of the C-state that was last used by the processor. A value of 0 represents the lightest idle state with higher values representing deeper C-states. + uint8 PreviousCState; + + // Not used. + int8 SpareByte; + + // Wait reason for the previous thread. The following are the possible values: + // 0 Executive + // 1 FreePage + // 2 PageIn + // 3 PoolAllocation + // 4 DelayExecution + // 5 Suspended + // 6 UserRequest + // 7 WrExecutive + // 8 WrFreePage + // 9 WrPageIn + // 10 WrPoolAllocation + // 11 WrDelayExecution + // 12 WrSuspended + // 13 WrUserRequest + // 14 WrEventPair + // 15 WrQueue + // 16 WrLpcReceive + // 17 WrLpcReply + // 18 WrVirtualMemory + // 19 WrPageOut + // 20 WrRendezvous + // 21 WrKeyedEvent + // 22 WrTerminated + // 23 WrProcessInSwap + // 24 WrCpuRateControl + // 25 WrCalloutStack + // 26 WrKernel + // 27 WrResource + // 28 WrPushLock + // 29 WrMutex + // 30 WrQuantumEnd + // 31 WrDispatchInt + // 32 WrPreempted + // 33 WrYieldExecution + // 34 WrFastMutex + // 35 WrGuardedMutex + // 36 WrRundown + // 37 MaximumWaitReason + int8 OldThreadWaitReason; + + // Wait mode for the previous thread. The following are the possible values: + // 0 KernelMode + // 1 UserMode + int8 OldThreadWaitMode; + + // State of the previous thread. The following are the possible state values: + // 0 Initialized + // 1 Ready + // 2 Running + // 3 Standby + // 4 Terminated + // 5 Waiting + // 6 Transition + // 7 DeferredReady (added for Windows Server 2003) + int8 OldThreadState; + + // Ideal wait time of the previous thread. + int8 OldThreadWaitIdealProcessor; + + // Wait time for the new thread. + uint32 NewThreadWaitTime; + + // Reserved. + uint32 Reserved; + + static const byte OPCODE = 36; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct StackWalk_Event +{ + // Original event time stamp from the event header + uint64 EventTimeStamp; + + // The process identifier of the original event + uint32 StackProcess; + + // The thread identifier of the original event + uint32 StackThread; + + // Callstack head + uint64 Stack0; + + static const byte OPCODE = 32; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct Thread_TypeGroup1 +{ + // Process identifier of the thread involved in the event. + uint32 ProcessId; + // Thread identifier of the thread involved in the event. + uint32 TThreadId; + // Base address of the thread's stack. + uint64 StackBase; + // Limit of the thread's stack. + uint64 StackLimit; + // Base address of the thread's user-mode stack. + uint64 UserStackBase; + // Limit of the thread's user-mode stack. + uint64 UserStackLimit; + // The set of processors on which the thread is allowed to run. + uint32 Affinity; + // Starting address of the function to be executed by this thread. + uint64 Win32StartAddr; + // Thread environment block base address. + uint64 TebBase; + // Identifies the service if the thread is owned by a service; otherwise, zero. + uint32 SubProcessTag; + // The scheduler priority of the thread + uint8 BasePriority; + // A memory page priority hint for memory pages accessed by the thread. + uint8 PagePriority; + // An IO priority hint for scheduling IOs generated by the thread. + uint8 IoPriority; + // Not used. + uint8 ThreadFlags; + + enum struct Opcode : uint8 + { + Start = 1, + End = 2, + DCStart = 3, + DCEnd = 4, + }; +}; + +size_t GetSIDSize(uint8* ptr) +{ + size_t result = 0; + + int sid = *((int*)ptr); + + if (sid != 0) + { + size_t tokenSize = 16; + ptr += tokenSize; + result += tokenSize; + result += 8 + (4 * ((SID*)ptr)->SubAuthorityCount); + } + else + { + result = 4; + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// https://github.com/Microsoft/perfview/blob/688a8564062d51321bbab53cd71d9e174a77d2ce/src/TraceEvent/TraceEvent.cs +struct Process_TypeGroup1 +{ + // The address of the process object in the kernel. + uint64 UniqueProcessKey; + // Global process identifier that you can use to identify a process. + uint32 ProcessId; + // Unique identifier of the process that creates this process. + uint32 ParentId; + // Unique identifier that an operating system generates when it creates a new session. + uint32 SessionId; + // Exit status of the stopped process. + int32 ExitStatus; + // The physical address of the page table of the process. + uint64 DirectoryTableBase; + // (?) uint8 Flags; + // object UserSID; + // string ImageFileName; + // wstring CommandLine; + + static size_t GetSIDOffset(PEVENT_RECORD pEvent) + { + if (pEvent->EventHeader.EventDescriptor.Version >= 4) + return 36; + + if (pEvent->EventHeader.EventDescriptor.Version == 3) + return 32; + + return 24; + } + + const char* GetProcessName(PEVENT_RECORD pEvent) const + { + OPTICK_ASSERT((pEvent->EventHeader.Flags & EVENT_HEADER_FLAG_64_BIT_HEADER) != 0, "32-bit is not supported! Disable OPTICK_ENABLE_TRACING on 32-bit platform if needed!"); + size_t sidOffset = GetSIDOffset(pEvent); + size_t sidSize = GetSIDSize((uint8*)this + sidOffset); + return (char*)this + sidOffset + sidSize; + } + + enum struct Opcode + { + Start = 1, + End = 2, + DCStart = 3, + DCEnd = 4, + Defunct = 39, + }; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct SampledProfile +{ + uint32 InstructionPointer; + uint32 ThreadId; + uint32 Count; + + static const byte OPCODE = 46; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct SysCallEnter +{ + uintptr_t SysCallAddress; + + static const byte OPCODE = 51; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct SysCallExit +{ + uint32 SysCallNtStatus; + + static const byte OPCODE = 52; +}; + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ce1dbfb4-137e-4da6-87b0-3f59aa102cbc +DEFINE_GUID(SampledProfileGuid, 0xce1dbfb4, 0x137e, 0x4da6, 0x87, 0xb0, 0x3f, 0x59, 0xaa, 0x10, 0x2c, 0xbc); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 3d6fa8d1-fe05-11d0-9dda-00c04fd7ba7c +// https://docs.microsoft.com/en-us/windows/desktop/etw/thread +DEFINE_GUID(ThreadGuid, 0x3d6fa8d1, 0xfe05, 0x11d0, 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// 3d6fa8d0-fe05-11d0-9dda-00c04fd7ba7c +// https://docs.microsoft.com/en-us/windows/desktop/etw/process +DEFINE_GUID(ProcessGuid, 0x3d6fa8d0, 0xfe05, 0x11d0, 0x9d, 0xda, 0x00, 0xc0, 0x4f, 0xd7, 0xba, 0x7c); + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const int MAX_CPU_CORES = 256; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct ETWRuntime +{ + array<ThreadID, MAX_CPU_CORES> activeCores; + vector<std::pair<uint8_t, SysCallData*>> activeSyscalls; + + ETWRuntime() + { + Reset(); + } + + void Reset() + { + activeCores.fill(INVALID_THREAD_ID); + activeSyscalls.resize(0);; + } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +ETWRuntime g_ETWRuntime; +ETW g_ETW; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void WINAPI OnRecordEvent(PEVENT_RECORD eventRecord) +{ + //static uint8 cpuCoreIsExecutingThreadFromOurProcess[256] = { 0 }; + + const byte opcode = eventRecord->EventHeader.EventDescriptor.Opcode; + + if (opcode == CSwitch::OPCODE) + { + if (sizeof(CSwitch) == eventRecord->UserDataLength) + { + CSwitch* pSwitchEvent = (CSwitch*)eventRecord->UserData; + + SwitchContextDesc desc; + desc.reason = pSwitchEvent->OldThreadWaitReason; + desc.cpuId = eventRecord->BufferContext.ProcessorNumber; + desc.oldThreadId = (uint64)pSwitchEvent->OldThreadId; + desc.newThreadId = (uint64)pSwitchEvent->NewThreadId; + desc.timestamp = eventRecord->EventHeader.TimeStamp.QuadPart; + Core::Get().ReportSwitchContext(desc); + + // Assign ThreadID to the cores + if (g_ETW.activeThreadsIDs.find(desc.newThreadId) != g_ETW.activeThreadsIDs.end()) + { + g_ETWRuntime.activeCores[desc.cpuId] = desc.newThreadId; + } + else if (g_ETW.activeThreadsIDs.find(desc.oldThreadId) != g_ETW.activeThreadsIDs.end()) + { + g_ETWRuntime.activeCores[desc.cpuId] = INVALID_THREAD_ID; + } + } + } + else if (opcode == StackWalk_Event::OPCODE) + { + if (eventRecord->UserData && eventRecord->UserDataLength >= sizeof(StackWalk_Event)) + { + //TODO: Support x86 windows kernels + const size_t osKernelPtrSize = sizeof(uint64); + + StackWalk_Event* pStackWalkEvent = (StackWalk_Event*)eventRecord->UserData; + uint32 count = 1 + (eventRecord->UserDataLength - sizeof(StackWalk_Event)) / osKernelPtrSize; + + if (count && pStackWalkEvent->StackThread != 0) + { + if (pStackWalkEvent->StackProcess == g_ETW.GetProcessID()) + { + CallstackDesc desc; + desc.threadID = pStackWalkEvent->StackThread; + desc.timestamp = pStackWalkEvent->EventTimeStamp; + + static_assert(osKernelPtrSize == sizeof(uint64), "Incompatible types!"); + desc.callstack = &pStackWalkEvent->Stack0; + + desc.count = (uint8)count; + Core::Get().ReportStackWalk(desc); + } + } + } + } + else if (opcode == SampledProfile::OPCODE) + { + SampledProfile* pEvent = (SampledProfile*)eventRecord->UserData; + OPTICK_UNUSED(pEvent); + } + else if (opcode == SysCallEnter::OPCODE) + { + if (eventRecord->UserDataLength >= sizeof(SysCallEnter)) + { + uint8_t cpuId = eventRecord->BufferContext.ProcessorNumber; + uint64_t threadId = g_ETWRuntime.activeCores[cpuId]; + + if (threadId != INVALID_THREAD_ID) + { + SysCallEnter* pEventEnter = (SysCallEnter*)eventRecord->UserData; + + SysCallData& sysCall = Core::Get().syscallCollector.Add(); + sysCall.start = eventRecord->EventHeader.TimeStamp.QuadPart; + sysCall.finish = EventTime::INVALID_TIMESTAMP; + sysCall.threadID = threadId; + sysCall.id = pEventEnter->SysCallAddress; + sysCall.description = nullptr; + + g_ETWRuntime.activeSyscalls.push_back(std::make_pair(cpuId, &sysCall)); + } + } + } + else if (opcode == SysCallExit::OPCODE) + { + if (eventRecord->UserDataLength >= sizeof(SysCallExit)) + { + uint8_t cpuId = eventRecord->BufferContext.ProcessorNumber; + if (g_ETWRuntime.activeCores[cpuId] != INVALID_THREAD_ID) + { + for (int i = (int)g_ETWRuntime.activeSyscalls.size() - 1; i >= 0; --i) + { + if (g_ETWRuntime.activeSyscalls[i].first == cpuId) + { + g_ETWRuntime.activeSyscalls[i].second->finish = eventRecord->EventHeader.TimeStamp.QuadPart; + g_ETWRuntime.activeSyscalls.erase(g_ETWRuntime.activeSyscalls.begin() + i); + break; + } + } + } + } + } + else + { + // VS TODO: We might have a situation where a thread was deleted and the new thread was created with the same threadID + // Ignoring for now - profiling sessions are quite short - not critical + if (IsEqualGUID(eventRecord->EventHeader.ProviderId, ThreadGuid)) + { + if (eventRecord->UserDataLength >= sizeof(Thread_TypeGroup1)) + { + const Thread_TypeGroup1* pThreadEvent = (const Thread_TypeGroup1*)eventRecord->UserData; + Core::Get().RegisterThreadDescription(ThreadDescription("", pThreadEvent->TThreadId, pThreadEvent->ProcessId, 1, pThreadEvent->BasePriority)); + } + + } + else if (IsEqualGUID(eventRecord->EventHeader.ProviderId, ProcessGuid)) + { + if (eventRecord->UserDataLength >= sizeof(Process_TypeGroup1)) + { + const Process_TypeGroup1* pProcessEvent = (const Process_TypeGroup1*)eventRecord->UserData; + Core::Get().RegisterProcessDescription(ProcessDescription(pProcessEvent->GetProcessName(eventRecord), pProcessEvent->ProcessId, pProcessEvent->UniqueProcessKey)); + } + } + } +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static ULONG WINAPI OnBufferRecord(_In_ PEVENT_TRACE_LOGFILE Buffer) +{ + OPTICK_UNUSED(Buffer); + return true; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const TRACEHANDLE INVALID_TRACEHANDLE = (TRACEHANDLE)-1; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +DWORD WINAPI ETW::RunProcessTraceThreadFunction(LPVOID parameter) +{ + Core::Get().RegisterThreadDescription(ThreadDescription("[Optick] ETW", GetCurrentThreadId(), GetCurrentProcessId())); + ETW* etw = (ETW*)parameter; + ULONG status = ProcessTrace(&etw->openedHandle, 1, 0, 0); + OPTICK_UNUSED(status); + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void ETW::AdjustPrivileges() +{ +#if OPTICK_PC + HANDLE token = 0; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) + { + TOKEN_PRIVILEGES tokenPrivileges; + memset(&tokenPrivileges, 0, sizeof(tokenPrivileges)); + tokenPrivileges.PrivilegeCount = 1; + tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + LookupPrivilegeValue(NULL, SE_SYSTEM_PROFILE_NAME, &tokenPrivileges.Privileges[0].Luid); + + AdjustTokenPrivileges(token, FALSE, &tokenPrivileges, 0, (PTOKEN_PRIVILEGES)NULL, 0); + CloseHandle(token); + } +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void ETW::ResolveSysCalls() +{ + if (SymbolEngine* symEngine = Platform::GetSymbolEngine()) + { + Core::Get().syscallCollector.syscallPool.ForEach([this, symEngine](SysCallData& data) + { + auto it = syscallDescriptions.find(data.id); + if (it == syscallDescriptions.end()) + { + const Symbol* symbol = symEngine->GetSymbol(data.id); + if (symbol != nullptr) + { + string name(symbol->function.begin(), symbol->function.end()); + + data.description = EventDescription::CreateShared(name.c_str(), "SysCall", (long)data.id); + syscallDescriptions.insert(std::pair<const uint64_t, const EventDescription *>(data.id, data.description)); + } + } + else + { + data.description = it->second; + } + }); + } +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +ETW::ETW() + : isActive(false) + , traceSessionHandle(INVALID_TRACEHANDLE) + , openedHandle(INVALID_TRACEHANDLE) + , processThreadHandle(INVALID_HANDLE_VALUE) + , traceProperties(nullptr) +{ + currentProcessId = GetCurrentProcessId(); +} + +CaptureStatus::Type ETW::Start(Mode::Type mode, int frequency, const ThreadList& threads) +{ + if (!isActive) + { + AdjustPrivileges(); + + g_ETWRuntime.Reset(); + + activeThreadsIDs.clear(); + for (auto it = threads.begin(); it != threads.end(); ++it) + { + ThreadEntry* entry = *it; + if (entry->isAlive) + { + activeThreadsIDs.insert(entry->description.threadID); + } + } + + + ULONG bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + (ETW_MAXIMUM_SESSION_NAME + MAX_PATH) * sizeof(WCHAR); + if (traceProperties == nullptr) + traceProperties = (EVENT_TRACE_PROPERTIES*)Memory::Alloc(bufferSize); + ZeroMemory(traceProperties, bufferSize); + traceProperties->Wnode.BufferSize = bufferSize; + traceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + StringCchCopyW((LPWSTR)((PCHAR)traceProperties + traceProperties->LoggerNameOffset), ETW_MAXIMUM_SESSION_NAME, KERNEL_LOGGER_NAMEW); + traceProperties->EnableFlags = 0; + + traceProperties->BufferSize = ETW_BUFFER_SIZE; + traceProperties->MinimumBuffers = ETW_BUFFER_COUNT; + + if (mode & Mode::SWITCH_CONTEXT) + { + traceProperties->EnableFlags |= EVENT_TRACE_FLAG_CSWITCH; + } + + if (mode & Mode::AUTOSAMPLING) + { + traceProperties->EnableFlags |= EVENT_TRACE_FLAG_PROFILE; + } + + if (mode & Mode::SYS_CALLS) + { + traceProperties->EnableFlags |= EVENT_TRACE_FLAG_SYSTEMCALL; + } + + if (mode & Mode::OTHER_PROCESSES) + { + traceProperties->EnableFlags |= EVENT_TRACE_FLAG_PROCESS; + traceProperties->EnableFlags |= EVENT_TRACE_FLAG_THREAD; + } + + traceProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; + traceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + // + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364160(v=vs.85).aspx + // Clock resolution = QPC + traceProperties->Wnode.ClientContext = 1; + traceProperties->Wnode.Guid = SystemTraceControlGuid; + + // ERROR_BAD_LENGTH(24): The Wnode.BufferSize member of Properties specifies an incorrect size. Properties does not have sufficient space allocated to hold a copy of SessionName. + // ERROR_ALREADY_EXISTS(183): A session with the same name or GUID is already running. + // ERROR_ACCESS_DENIED(5): Only users with administrative privileges, users in the Performance Log Users group, and services running as LocalSystem, LocalService, NetworkService can control event tracing sessions. + // ERROR_INVALID_PARAMETER(87) + // ERROR_BAD_PATHNAME(161) + // ERROR_DISK_FULL(112) + // ERROR_NO_SUCH_PRIVILEGE(1313) + int retryCount = 4; + ULONG status = CaptureStatus::OK; + + while (--retryCount >= 0) + { + status = StartTrace(&traceSessionHandle, KERNEL_LOGGER_NAME, traceProperties); + + switch (status) + { + case ERROR_NO_SUCH_PRIVILEGE: + AdjustPrivileges(); + break; + + case ERROR_ALREADY_EXISTS: + ControlTrace(0, KERNEL_LOGGER_NAME, traceProperties, EVENT_TRACE_CONTROL_STOP); + break; + + case ERROR_ACCESS_DENIED: + return CaptureStatus::ERR_TRACER_ACCESS_DENIED; + + case ERROR_SUCCESS: + retryCount = 0; + break; + + default: + return CaptureStatus::ERR_TRACER_FAILED; + } + } + + if (status != ERROR_SUCCESS) + { + return CaptureStatus::ERR_TRACER_FAILED; + } + + CLASSIC_EVENT_ID callstackSamples[4]; + int callstackCountSamplesCount = 0; + + if (mode & Mode::AUTOSAMPLING) + { + callstackSamples[callstackCountSamplesCount].EventGuid = SampledProfileGuid; + callstackSamples[callstackCountSamplesCount].Type = SampledProfile::OPCODE; + ++callstackCountSamplesCount; + } + + if (mode & Mode::SYS_CALLS) + { + callstackSamples[callstackCountSamplesCount].EventGuid = SampledProfileGuid; + callstackSamples[callstackCountSamplesCount].Type = SysCallEnter::OPCODE; + ++callstackCountSamplesCount; + } + + /* + callstackSamples[callstackCountSamplesCount].EventGuid = CSwitchProfileGuid; + callstackSamples[callstackCountSamplesCount].Type = CSwitch::OPCODE; + ++callstackCountSamplesCount; + */ + + + /* + https://msdn.microsoft.com/en-us/library/windows/desktop/dd392328%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 + Typically, on 64-bit computers, you cannot capture the kernel stack in certain contexts when page faults are not allowed. To enable walking the kernel stack on x64, set + the DisablePagingExecutive Memory Management registry value to 1. The DisablePagingExecutive registry value is located under the following registry key: + HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management + */ + if (callstackCountSamplesCount > 0) + { + status = TraceSetInformation(traceSessionHandle, TraceStackTracingInfo, &callstackSamples[0], sizeof(CLASSIC_EVENT_ID) * callstackCountSamplesCount); + if (status != ERROR_SUCCESS) + { + OPTICK_FAILED("TraceSetInformation - failed"); + return CaptureStatus::ERR_TRACER_FAILED; + } + } + + if (mode & Mode::AUTOSAMPLING) + { + TRACE_PROFILE_INTERVAL itnerval = { 0 }; + memset(&itnerval, 0, sizeof(TRACE_PROFILE_INTERVAL)); + int step = 10000 * 1000 / frequency; // 1ms = 10000 steps + itnerval.Interval = step; // std::max(1221, std::min(step, 10000)); + // The SessionHandle is irrelevant for this information class and must be zero, else the function returns ERROR_INVALID_PARAMETER. + status = TraceSetInformation(0, TraceSampledProfileIntervalInfo, &itnerval, sizeof(TRACE_PROFILE_INTERVAL)); + OPTICK_ASSERT(status == ERROR_SUCCESS, "TraceSetInformation - failed"); + } + + ZeroMemory(&logFile, sizeof(EVENT_TRACE_LOGFILE)); + logFile.LoggerName = KERNEL_LOGGER_NAME; + logFile.ProcessTraceMode = (PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP); + logFile.EventRecordCallback = OnRecordEvent; + logFile.BufferCallback = OnBufferRecord; + openedHandle = OpenTrace(&logFile); + if (openedHandle == INVALID_TRACEHANDLE) + { + OPTICK_FAILED("OpenTrace - failed"); + return CaptureStatus::ERR_TRACER_FAILED; + } + + DWORD threadID; + processThreadHandle = CreateThread(0, 0, RunProcessTraceThreadFunction, this, 0, &threadID); + + isActive = true; + } + + return CaptureStatus::OK; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool ETW::Stop() +{ + if (!isActive) + { + return false; + } + + ULONG controlTraceResult = ControlTrace(openedHandle, KERNEL_LOGGER_NAME, traceProperties, EVENT_TRACE_CONTROL_STOP); + + // ERROR_CTX_CLOSE_PENDING(7007L): The call was successful. The ProcessTrace function will stop after it has processed all real-time events in its buffers (it will not receive any new events). + // ERROR_BUSY(170L): Prior to Windows Vista, you cannot close the trace until the ProcessTrace function completes. + // ERROR_INVALID_HANDLE(6L): One of the following is true: TraceHandle is NULL. TraceHandle is INVALID_HANDLE_VALUE. + ULONG closeTraceStatus = CloseTrace(openedHandle); + + // Wait for ProcessThread to finish + WaitForSingleObject(processThreadHandle, INFINITE); + BOOL wasThreadClosed = CloseHandle(processThreadHandle); + + isActive = false; + + //VS TODO: Disabling resolving of the syscalls - we can't use then as EventDescriptions at the moment + //ResolveSysCalls(); + + activeThreadsIDs.clear(); + + return wasThreadClosed && (closeTraceStatus == ERROR_SUCCESS) && (controlTraceResult == ERROR_SUCCESS); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +ETW::~ETW() +{ + Stop(); + Memory::Free(traceProperties); + traceProperties = nullptr; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Trace* Platform::GetTrace() +{ + return &g_ETW; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Symbol Resolving +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define USE_DBG_HELP (OPTICK_PC) + +#if USE_DBG_HELP +#include <DbgHelp.h> +#pragma comment( lib, "DbgHelp.Lib" ) +#endif + +#include "optick_serialization.h" + +#if OPTICK_PC +#include <psapi.h> +#else +// Forward declare kernel functions +#pragma pack(push,8) +typedef struct _MODULEINFO { + LPVOID lpBaseOfDll; + DWORD SizeOfImage; + LPVOID EntryPoint; +} MODULEINFO, *LPMODULEINFO; +#pragma pack(pop) +#ifndef EnumProcessModulesEx +#define EnumProcessModulesEx K32EnumProcessModulesEx +EXTERN_C DWORD WINAPI K32EnumProcessModulesEx(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded, DWORD dwFilterFlag); +#endif +#ifndef GetModuleInformation +#define GetModuleInformation K32GetModuleInformation +EXTERN_C DWORD WINAPI K32GetModuleInformation(HANDLE hProcess, HMODULE hModule, LPMODULEINFO lpmodinfo, DWORD cb); +#endif + +#ifndef GetModuleFileNameExA +#define GetModuleFileNameExA K32GetModuleFileNameExA +EXTERN_C DWORD WINAPI K32GetModuleFileNameExA(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize); +#endif +#endif + +namespace Optick +{ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//void ReportLastError() +//{ +// LPVOID lpMsgBuf; +// DWORD dw = GetLastError(); +// +// FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, +// NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), +// (LPTSTR)&lpMsgBuf, 0, NULL); +// +// MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error"), MB_OK); +// LocalFree(lpMsgBuf); +//} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef array<uintptr_t, 512> CallStackBuffer; +typedef unordered_map<uint64, Symbol> SymbolCache; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class WinSymbolEngine : public SymbolEngine +{ + HANDLE hProcess; + + bool isInitialized; + + bool needRestorePreviousSettings; + uint32 previousOptions; + static const size_t MAX_SEARCH_PATH_LENGTH = 2048; + char previousSearchPath[MAX_SEARCH_PATH_LENGTH]; + + SymbolCache cache; + vector<Module> modules; + + void InitSystemModules(); + void InitApplicationModules(); +public: + WinSymbolEngine(); + ~WinSymbolEngine(); + + void Init(); + void Close(); + + // Get Symbol from PDB file + virtual const Symbol * GetSymbol(uint64 dwAddress) override; + virtual const vector<Module>& GetModules() override; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +WinSymbolEngine::WinSymbolEngine() : isInitialized(false), hProcess(GetCurrentProcess()), needRestorePreviousSettings(false), previousOptions(0) +{ +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +WinSymbolEngine::~WinSymbolEngine() +{ + Close(); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const Symbol* WinSymbolEngine::GetSymbol(uint64 address) +{ + if (address == 0) + return nullptr; + + Init(); + + Symbol& symbol = cache[address]; + + if (symbol.address != 0) + return &symbol; + + if (!isInitialized) + return nullptr; + + symbol.address = address; + +#if USE_DBG_HELP + DWORD64 dwAddress = static_cast<DWORD64>(address); + + // FileName and Line + IMAGEHLP_LINEW64 lineInfo; + memset(&lineInfo, 0, sizeof(IMAGEHLP_LINEW64)); + lineInfo.SizeOfStruct = sizeof(lineInfo); + DWORD dwDisp; + if (SymGetLineFromAddrW64(hProcess, dwAddress, &dwDisp, &lineInfo)) + { + symbol.file = lineInfo.FileName; + symbol.line = lineInfo.LineNumber; + } + + const size_t length = (sizeof(SYMBOL_INFOW) + MAX_SYM_NAME * sizeof(WCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64) + 1; + + // Function Name + ULONG64 buffer[length]; + PSYMBOL_INFOW dbgSymbol = (PSYMBOL_INFOW)buffer; + memset(dbgSymbol, 0, sizeof(buffer)); + dbgSymbol->SizeOfStruct = sizeof(SYMBOL_INFOW); + dbgSymbol->MaxNameLen = MAX_SYM_NAME; + + DWORD64 offset = 0; + if (SymFromAddrW(hProcess, dwAddress, &offset, dbgSymbol)) + { + symbol.function.resize(dbgSymbol->NameLen); + memcpy(&symbol.function[0], &dbgSymbol->Name[0], sizeof(WCHAR) * dbgSymbol->NameLen); + } + + symbol.offset = static_cast<uintptr_t>(offset); +#endif + + return &symbol; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const vector<Module>& WinSymbolEngine::GetModules() +{ + if (modules.empty()) + { + InitSystemModules(); + InitApplicationModules(); + } + return modules; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// const char* USER_SYMBOL_SEARCH_PATH = "http://msdl.microsoft.com/download/symbols"; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void WinSymbolEngine::Init() +{ + if (!isInitialized) + { +#if USE_DBG_HELP + previousOptions = SymGetOptions(); + + memset(previousSearchPath, 0, MAX_SEARCH_PATH_LENGTH); + SymGetSearchPath(hProcess, previousSearchPath, MAX_SEARCH_PATH_LENGTH); + + SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_INCLUDE_32BIT_MODULES | SYMOPT_LOAD_ANYTHING); + if (!SymInitialize(hProcess, NULL, TRUE)) + { + needRestorePreviousSettings = true; + SymCleanup(hProcess); + + if (SymInitialize(hProcess, NULL, TRUE)) + isInitialized = true; + } + else + { + isInitialized = true; + } + + const vector<Module>& loadedModules = GetModules(); + for (size_t i = 0; i < loadedModules.size(); ++i) + { + const Module& module = loadedModules[i]; + SymLoadModule64(hProcess, NULL, module.path.c_str(), NULL, (DWORD64)module.address, (DWORD)module.size); + } + +#else + isInitialized = true; +#endif + } +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef DWORD(__stdcall *pZwQuerySystemInformation)(DWORD, LPVOID, DWORD, DWORD*); +#define SystemModuleInformation 11 // SYSTEMINFOCLASS +#define MAXIMUM_FILENAME_LENGTH 256 + +struct SYSTEM_MODULE_INFORMATION +{ + DWORD reserved1; + DWORD reserved2; + PVOID mappedBase; + PVOID imageBase; + DWORD imageSize; + DWORD flags; + WORD loadOrderIndex; + WORD initOrderIndex; + WORD loadCount; + WORD moduleNameOffset; + CHAR imageName[MAXIMUM_FILENAME_LENGTH]; +}; + +#pragma warning (push) +#pragma warning(disable : 4200) +struct MODULE_LIST +{ + DWORD dwModules; + SYSTEM_MODULE_INFORMATION pModulesInfo[]; +}; +#pragma warning (pop) + +void WinSymbolEngine::InitSystemModules() +{ + ULONG returnLength = 0; + ULONG systemInformationLength = 0; + MODULE_LIST* pModuleList = nullptr; + +#pragma warning (push) +#pragma warning(disable : 4191) + pZwQuerySystemInformation ZwQuerySystemInformation = (pZwQuerySystemInformation)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "ZwQuerySystemInformation"); +#pragma warning (pop) + + ZwQuerySystemInformation(SystemModuleInformation, pModuleList, systemInformationLength, &returnLength); + systemInformationLength = returnLength; + pModuleList = (MODULE_LIST*)Memory::Alloc(systemInformationLength); + DWORD status = ZwQuerySystemInformation(SystemModuleInformation, pModuleList, systemInformationLength, &returnLength); + if (status == ERROR_SUCCESS) + { + char systemRootPath[MAXIMUM_FILENAME_LENGTH] = { 0 }; +#if OPTICK_PC + ExpandEnvironmentStringsA("%SystemRoot%", systemRootPath, MAXIMUM_FILENAME_LENGTH); +#else + strcpy_s(systemRootPath, "C:\\Windows"); +#endif + + const char* systemRootPattern = "\\SystemRoot"; + + modules.reserve(modules.size() + pModuleList->dwModules); + + for (uint32_t i = 0; i < pModuleList->dwModules; ++i) + { + SYSTEM_MODULE_INFORMATION& module = pModuleList->pModulesInfo[i]; + + char path[MAXIMUM_FILENAME_LENGTH] = { 0 }; + + if (strstr(module.imageName, systemRootPattern) == module.imageName) + { + strcpy_s(path, systemRootPath); + strcat_s(path, module.imageName + strlen(systemRootPattern)); + } + else + { + strcpy_s(path, module.imageName); + } + + modules.push_back(Module(path, (void*)module.imageBase, module.imageSize)); + } + } + else + { + OPTICK_FAILED("Can't query System Module Information!"); + } + + if (pModuleList) + { + Memory::Free(pModuleList); + } +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void WinSymbolEngine::InitApplicationModules() +{ + HANDLE processHandle = GetCurrentProcess(); + HMODULE hModules[256]; + DWORD modulesSize = 0; + EnumProcessModulesEx(processHandle, hModules, sizeof(hModules), &modulesSize, 0); + + int moduleCount = modulesSize / sizeof(HMODULE); + + modules.reserve(modules.size() + moduleCount); + + for (int i = 0; i < moduleCount; ++i) + { + MODULEINFO info = { 0 }; + if (GetModuleInformation(processHandle, hModules[i], &info, sizeof(MODULEINFO))) + { + char name[MAX_PATH] = "UnknownModule"; + GetModuleFileNameExA(processHandle, hModules[i], name, MAX_PATH); + + modules.push_back(Module(name, info.lpBaseOfDll, info.SizeOfImage)); + } + } +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void WinSymbolEngine::Close() +{ + if (isInitialized) + { +#if USE_DBG_HELP + SymCleanup(hProcess); + if (needRestorePreviousSettings) + { + HANDLE currentProcess = GetCurrentProcess(); + + SymSetOptions(previousOptions); + SymSetSearchPath(currentProcess, previousSearchPath); + SymInitialize(currentProcess, NULL, TRUE); + + needRestorePreviousSettings = false; + } +#endif + modules.clear(); + isInitialized = false; + } +} +////////////////////////////////////////////////////////////////////////// +SymbolEngine* Platform::GetSymbolEngine() +{ + static WinSymbolEngine pdbSymbolEngine; + return &pdbSymbolEngine; +} +////////////////////////////////////////////////////////////////////////// +} +#endif //OPTICK_ENABLE_TRACING +#endif //USE_OPTICK +#endif //_MSC_VER
\ No newline at end of file diff --git a/external/optick/optick_gpu.cpp b/external/optick/optick_gpu.cpp new file mode 100644 index 0000000..d3610c3 --- /dev/null +++ b/external/optick/optick_gpu.cpp @@ -0,0 +1,136 @@ +#include "optick.config.h" + +#if USE_OPTICK +#include "optick_gpu.h" +#include "optick_core.h" +#include "optick_memory.h" + +#include <thread> + +namespace Optick +{ + static_assert((1ULL << 32) % GPUProfiler::MAX_QUERIES_COUNT == 0, "(1 << 32) should be a multiple of MAX_QUERIES_COUNT to handle query index overflow!"); + + + GPUProfiler::GPUProfiler() : currentState(STATE_OFF), currentNode(0), frameNumber(0) + { + + } + + void GPUProfiler::InitNode(const char *nodeName, uint32_t nodeIndex) + { + Node* node = Memory::New<Node>(); + for (int i = 0; i < GPU_QUEUE_COUNT; ++i) + { + char name[128] = { 0 }; + sprintf_s(name, "%s [%s]", nodeName, GetGPUQueueName((GPUQueueType)i)); + node->gpuEventStorage[i] = RegisterStorage(name, uint64_t(-1), ThreadMask::GPU); + node->name = nodeName; + } + nodes[nodeIndex] = node; + } + + void GPUProfiler::Start(uint32 /*mode*/) + { + std::lock_guard<std::recursive_mutex> lock(updateLock); + Reset(); + currentState = STATE_STARTING; + } + + void GPUProfiler::Stop(uint32 /*mode*/) + { + std::lock_guard<std::recursive_mutex> lock(updateLock); + currentState = STATE_OFF; + } + + void GPUProfiler::Dump(uint32 /*mode*/) + { + for (size_t nodeIndex = 0; nodeIndex < nodes.size(); ++nodeIndex) + { + Node* node = nodes[nodeIndex]; + + for (int queueIndex = 0; queueIndex < GPU_QUEUE_COUNT; ++queueIndex) + { + EventBuffer& gpuBuffer = node->gpuEventStorage[queueIndex]->eventBuffer; + + const vector<ThreadEntry*>& threads = Core::Get().GetThreads(); + for (size_t threadIndex = 0; threadIndex < threads.size(); ++threadIndex) + { + ThreadEntry* thread = threads[threadIndex]; + thread->storage.gpuStorage.gpuBuffer[nodeIndex][queueIndex].ForEachChunk([&gpuBuffer](const EventData* events, int count) + { + gpuBuffer.AddRange(events, count); + }); + } + } + } + } + + string GPUProfiler::GetName() const + { + return !nodes.empty() ? nodes[0]->name : string(); + } + + GPUProfiler::~GPUProfiler() + { + for (Node* node : nodes) + Memory::Delete(node); + nodes.clear(); + } + + void GPUProfiler::Reset() + { + for (uint32_t nodeIndex = 0; nodeIndex < nodes.size(); ++nodeIndex) + { + Node& node = *nodes[nodeIndex]; + node.Reset(); + node.clock = GetClockSynchronization(nodeIndex); + } + } + + EventData& GPUProfiler::AddFrameEvent() + { + static const EventDescription* GPUFrameDescription = EventDescription::Create("GPU Frame", __FILE__, __LINE__); + EventData& event = nodes[currentNode]->gpuEventStorage[GPU_QUEUE_GRAPHICS]->eventBuffer.Add(); + event.description = GPUFrameDescription; + event.start = EventTime::INVALID_TIMESTAMP; + event.finish = EventTime::INVALID_TIMESTAMP; + return event; + } + + EventData& GPUProfiler::AddVSyncEvent() + { + static const EventDescription* VSyncDescription = EventDescription::Create("VSync", __FILE__, __LINE__); + EventData& event = nodes[currentNode]->gpuEventStorage[GPU_QUEUE_VSYNC]->eventBuffer.Add(); + event.description = VSyncDescription; + event.start = EventTime::INVALID_TIMESTAMP; + event.finish = EventTime::INVALID_TIMESTAMP; + return event; + } + + TagData<uint32>& GPUProfiler::AddFrameTag() + { + static const EventDescription* FrameTagDescription = EventDescription::CreateShared("Frame"); + TagData<uint32>& tag = nodes[currentNode]->gpuEventStorage[GPU_QUEUE_GRAPHICS]->tagU32Buffer.Add(); + tag.description = FrameTagDescription; + tag.timestamp = EventTime::INVALID_TIMESTAMP; + tag.data = Core::Get().GetCurrentFrame(); + return tag; + } + + const char * GetGPUQueueName(GPUQueueType queue) + { + const char* GPUQueueToName[GPU_QUEUE_COUNT] = { "Graphics", "Compute", "Transfer", "VSync" }; + return GPUQueueToName[queue]; + } + + void GPUProfiler::Node::Reset() + { + queryIndex = 0; + + for (size_t frameIndex = 0; frameIndex < queryGpuframes.size(); ++frameIndex) + queryGpuframes[frameIndex].Reset(); + } +} +#endif //USE_OPTICK + diff --git a/external/optick/optick_gpu.d3d12.cpp b/external/optick/optick_gpu.d3d12.cpp new file mode 100644 index 0000000..1ee4dd9 --- /dev/null +++ b/external/optick/optick_gpu.d3d12.cpp @@ -0,0 +1,382 @@ +#include "optick.config.h" +#if USE_OPTICK +#if OPTICK_ENABLE_GPU_D3D12 + +#include "optick_common.h" +#include "optick_memory.h" +#include "optick_core.h" +#include "optick_gpu.h" + +#include <atomic> +#include <thread> + +#include <d3d12.h> +#include <dxgi.h> +#include <dxgi1_4.h> + + +#define OPTICK_CHECK(args) do { HRESULT __hr = args; (void)__hr; OPTICK_ASSERT(__hr == S_OK, "Failed check"); } while(false); + +namespace Optick +{ + class GPUProfilerD3D12 : public GPUProfiler + { + struct Frame + { + ID3D12CommandAllocator* commandAllocator; + ID3D12GraphicsCommandList* commandList; + + Frame() : commandAllocator(nullptr), commandList(nullptr) + { + Reset(); + } + + void Reset() + { + } + + void Shutdown(); + + ~Frame() + { + Shutdown(); + } + }; + + struct NodePayload + { + ID3D12CommandQueue* commandQueue; + ID3D12QueryHeap* queryHeap; + ID3D12Fence* syncFence; + array<Frame, NUM_FRAMES_DELAY> frames; + + NodePayload() : commandQueue(nullptr), queryHeap(nullptr), syncFence(nullptr) {} + ~NodePayload(); + }; + vector<NodePayload*> nodePayloads; + + ID3D12Resource* queryBuffer; + ID3D12Device* device; + + // VSync Stats + DXGI_FRAME_STATISTICS prevFrameStatistics; + + //void UpdateRange(uint32_t start, uint32_t finish) + void InitNodeInternal(const char* nodeName, uint32_t nodeIndex, ID3D12CommandQueue* pCmdQueue); + + void ResolveTimestamps(uint32_t startIndex, uint32_t count); + + void WaitForFrame(uint64_t frameNumber); + + public: + GPUProfilerD3D12(); + ~GPUProfilerD3D12(); + + void InitDevice(ID3D12Device* pDevice, ID3D12CommandQueue** pCommandQueues, uint32_t numCommandQueues); + + void QueryTimestamp(ID3D12GraphicsCommandList* context, int64_t* outCpuTimestamp); + + void Flip(IDXGISwapChain* swapChain); + + + // Interface implementation + ClockSynchronization GetClockSynchronization(uint32_t nodeIndex) override; + + void QueryTimestamp(void* context, int64_t* outCpuTimestamp) override + { + QueryTimestamp((ID3D12GraphicsCommandList*)context, outCpuTimestamp); + } + + void Flip(void* swapChain) override + { + Flip(static_cast<IDXGISwapChain*>(swapChain)); + } + }; + + template <class T> void SafeRelease(T **ppT) + { + if (*ppT) + { + (*ppT)->Release(); + *ppT = NULL; + } + } + + void InitGpuD3D12(void* device, void** cmdQueues, uint32_t numQueues) + { + GPUProfilerD3D12* gpuProfiler = Memory::New<GPUProfilerD3D12>(); + gpuProfiler->InitDevice((ID3D12Device*)device, (ID3D12CommandQueue**)cmdQueues, numQueues); + Core::Get().InitGPUProfiler(gpuProfiler); + } + + GPUProfilerD3D12::GPUProfilerD3D12() : queryBuffer(nullptr), device(nullptr) + { + prevFrameStatistics = { 0 }; + } + + GPUProfilerD3D12::~GPUProfilerD3D12() + { + WaitForFrame(frameNumber - 1); + + for (NodePayload* payload : nodePayloads) + Memory::Delete(payload); + nodePayloads.clear(); + + for (Node* node : nodes) + Memory::Delete(node); + nodes.clear(); + + SafeRelease(&queryBuffer); + } + + void GPUProfilerD3D12::InitDevice(ID3D12Device* pDevice, ID3D12CommandQueue** pCommandQueues, uint32_t numCommandQueues) + { + device = pDevice; + + uint32_t nodeCount = numCommandQueues; // device->GetNodeCount(); + + nodes.resize(nodeCount); + nodePayloads.resize(nodeCount); + + D3D12_HEAP_PROPERTIES heapDesc; + heapDesc.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapDesc.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapDesc.CreationNodeMask = 0; + heapDesc.VisibleNodeMask = (1u << nodeCount) - 1u; + heapDesc.Type = D3D12_HEAP_TYPE_READBACK; + + D3D12_RESOURCE_DESC resourceDesc; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = MAX_QUERIES_COUNT * sizeof(int64_t); + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc.Count = 1; + resourceDesc.SampleDesc.Quality = 0; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + OPTICK_CHECK(device->CreateCommittedResource( + &heapDesc, + D3D12_HEAP_FLAG_NONE, + &resourceDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, + IID_PPV_ARGS(&queryBuffer))); + + // Get Device Name + LUID adapterLUID = pDevice->GetAdapterLuid(); + + IDXGIFactory4* factory; + OPTICK_CHECK(CreateDXGIFactory2(0, IID_PPV_ARGS(&factory))); + + IDXGIAdapter1* adapter; + factory->EnumAdapterByLuid(adapterLUID, IID_PPV_ARGS(&adapter)); + + DXGI_ADAPTER_DESC1 desc; + adapter->GetDesc1(&desc); + + adapter->Release(); + factory->Release(); + + char deviceName[128] = { 0 }; + wcstombs_s(deviceName, desc.Description, OPTICK_ARRAY_SIZE(deviceName) - 1); + + for (uint32_t nodeIndex = 0; nodeIndex < nodeCount; ++nodeIndex) + InitNodeInternal(deviceName, nodeIndex, pCommandQueues[nodeIndex]); + } + + void GPUProfilerD3D12::InitNodeInternal(const char* nodeName, uint32_t nodeIndex, ID3D12CommandQueue* pCmdQueue) + { + GPUProfiler::InitNode(nodeName, nodeIndex); + + NodePayload* node = Memory::New<NodePayload>(); + nodePayloads[nodeIndex] = node; + node->commandQueue = pCmdQueue; + + D3D12_QUERY_HEAP_DESC queryHeapDesc; + queryHeapDesc.Count = MAX_QUERIES_COUNT; + queryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; + queryHeapDesc.NodeMask = 1u << nodeIndex; + OPTICK_CHECK(device->CreateQueryHeap(&queryHeapDesc, IID_PPV_ARGS(&node->queryHeap))); + + OPTICK_CHECK(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&node->syncFence))); + + for (Frame& frame : node->frames) + { + OPTICK_CHECK(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&frame.commandAllocator))); + OPTICK_CHECK(device->CreateCommandList(1u << nodeIndex, D3D12_COMMAND_LIST_TYPE_DIRECT, frame.commandAllocator, nullptr, IID_PPV_ARGS(&frame.commandList))); + OPTICK_CHECK(frame.commandList->Close()); + } + } + + void GPUProfilerD3D12::QueryTimestamp(ID3D12GraphicsCommandList* context, int64_t* outCpuTimestamp) + { + if (currentState == STATE_RUNNING) + { + uint32_t index = nodes[currentNode]->QueryTimestamp(outCpuTimestamp); + context->EndQuery(nodePayloads[currentNode]->queryHeap, D3D12_QUERY_TYPE_TIMESTAMP, index); + } + } + + void GPUProfilerD3D12::ResolveTimestamps(uint32_t startIndex, uint32_t count) + { + if (count) + { + Node* node = nodes[currentNode]; + + D3D12_RANGE range = { sizeof(uint64_t)*startIndex, sizeof(uint64_t)*(startIndex + count) }; + void* pData = nullptr; + queryBuffer->Map(0, &range, &pData); + memcpy(&node->queryGpuTimestamps[startIndex], (uint64_t*)pData + startIndex, sizeof(uint64_t) * count); + queryBuffer->Unmap(0, 0); + + // Convert GPU timestamps => CPU Timestamps + for (uint32_t index = startIndex; index < startIndex + count; ++index) + *node->queryCpuTimestamps[index] = node->clock.GetCPUTimestamp(node->queryGpuTimestamps[index]); + } + } + + void GPUProfilerD3D12::WaitForFrame(uint64_t frameNumberToWait) + { + OPTICK_EVENT(); + + NodePayload* payload = nodePayloads[currentNode]; + while (frameNumberToWait > payload->syncFence->GetCompletedValue()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + } + + void GPUProfilerD3D12::Flip(IDXGISwapChain* swapChain) + { + OPTICK_CATEGORY("GPUProfilerD3D12::Flip", Category::Debug); + + std::lock_guard<std::recursive_mutex> lock(updateLock); + + if (currentState == STATE_STARTING) + currentState = STATE_RUNNING; + + if (currentState == STATE_RUNNING) + { + Node& node = *nodes[currentNode]; + NodePayload& payload = *nodePayloads[currentNode]; + + uint32_t currentFrameIndex = frameNumber % NUM_FRAMES_DELAY; + uint32_t nextFrameIndex = (frameNumber + 1) % NUM_FRAMES_DELAY; + + //Frame& currentFrame = frames[frameNumber % NUM_FRAMES_DELAY]; + //Frame& nextFrame = frames[(frameNumber + 1) % NUM_FRAMES_DELAY]; + + QueryFrame& currentFrame = node.queryGpuframes[currentFrameIndex]; + QueryFrame& nextFrame = node.queryGpuframes[nextFrameIndex]; + + ID3D12GraphicsCommandList* commandList = payload.frames[currentFrameIndex].commandList; + ID3D12CommandAllocator* commandAllocator = payload.frames[currentFrameIndex].commandAllocator; + commandAllocator->Reset(); + commandList->Reset(commandAllocator, nullptr); + + if (EventData* frameEvent = currentFrame.frameEvent) + QueryTimestamp(commandList, &frameEvent->finish); + + // Generate GPU Frame event for the next frame + EventData& event = AddFrameEvent(); + QueryTimestamp(commandList, &event.start); + QueryTimestamp(commandList, &AddFrameTag().timestamp); + nextFrame.frameEvent = &event; + + uint32_t queryBegin = currentFrame.queryIndexStart; + uint32_t queryEnd = node.queryIndex; + + if (queryBegin != (uint32_t)-1) + { + OPTICK_ASSERT(queryEnd - queryBegin <= MAX_QUERIES_COUNT, "Too many queries in one frame? Increase GPUProfiler::MAX_QUERIES_COUNT to fix the problem!"); + currentFrame.queryIndexCount = queryEnd - queryBegin; + + uint32_t startIndex = queryBegin % MAX_QUERIES_COUNT; + uint32_t finishIndex = queryEnd % MAX_QUERIES_COUNT; + + if (startIndex < finishIndex) + { + commandList->ResolveQueryData(payload.queryHeap, D3D12_QUERY_TYPE_TIMESTAMP, startIndex, queryEnd - queryBegin, queryBuffer, startIndex * sizeof(int64_t)); + } + else + { + commandList->ResolveQueryData(payload.queryHeap, D3D12_QUERY_TYPE_TIMESTAMP, startIndex, MAX_QUERIES_COUNT - startIndex, queryBuffer, startIndex * sizeof(int64_t)); + commandList->ResolveQueryData(payload.queryHeap, D3D12_QUERY_TYPE_TIMESTAMP, 0, finishIndex, queryBuffer, 0); + } + } + + commandList->Close(); + + payload.commandQueue->ExecuteCommandLists(1, (ID3D12CommandList*const*)&commandList); + payload.commandQueue->Signal(payload.syncFence, frameNumber); + + // Preparing Next Frame + // Try resolve timestamps for the current frame + if (frameNumber >= NUM_FRAMES_DELAY && nextFrame.queryIndexCount) + { + WaitForFrame(frameNumber + 1 - NUM_FRAMES_DELAY); + + uint32_t resolveStart = nextFrame.queryIndexStart % MAX_QUERIES_COUNT; + uint32_t resolveFinish = resolveStart + nextFrame.queryIndexCount; + ResolveTimestamps(resolveStart, std::min<uint32_t>(resolveFinish, MAX_QUERIES_COUNT) - resolveStart); + if (resolveFinish > MAX_QUERIES_COUNT) + ResolveTimestamps(0, resolveFinish - MAX_QUERIES_COUNT); + } + + nextFrame.queryIndexStart = queryEnd; + nextFrame.queryIndexCount = 0; + + // Process VSync + DXGI_FRAME_STATISTICS currentFrameStatistics = { 0 }; + HRESULT result = swapChain->GetFrameStatistics(¤tFrameStatistics); + if ((result == S_OK) && (prevFrameStatistics.PresentCount + 1 == currentFrameStatistics.PresentCount)) + { + EventData& data = AddVSyncEvent(); + data.start = prevFrameStatistics.SyncQPCTime.QuadPart; + data.finish = currentFrameStatistics.SyncQPCTime.QuadPart; + } + prevFrameStatistics = currentFrameStatistics; + } + + ++frameNumber; + } + + GPUProfiler::ClockSynchronization GPUProfilerD3D12::GetClockSynchronization(uint32_t nodeIndex) + { + ClockSynchronization clock; + clock.frequencyCPU = GetHighPrecisionFrequency(); + nodePayloads[nodeIndex]->commandQueue->GetTimestampFrequency((uint64_t*)&clock.frequencyGPU); + nodePayloads[nodeIndex]->commandQueue->GetClockCalibration((uint64_t*)&clock.timestampGPU, (uint64_t*)&clock.timestampCPU); + return clock; + } + + GPUProfilerD3D12::NodePayload::~NodePayload() + { + SafeRelease(&queryHeap); + SafeRelease(&syncFence); + } + + void GPUProfilerD3D12::Frame::Shutdown() + { + SafeRelease(&commandAllocator); + SafeRelease(&commandList); + } +} + +#else +#include "optick_common.h" + +namespace Optick +{ + void InitGpuD3D12(void* /*device*/, void** /*cmdQueues*/, uint32_t /*numQueues*/) + { + OPTICK_FAILED("OPTICK_ENABLE_GPU_D3D12 is disabled! Can't initialize GPU Profiler!"); + } +} + +#endif //OPTICK_ENABLE_GPU_D3D12 +#endif //USE_OPTICK
\ No newline at end of file diff --git a/external/optick/optick_gpu.h b/external/optick/optick_gpu.h new file mode 100644 index 0000000..f028f8a --- /dev/null +++ b/external/optick/optick_gpu.h @@ -0,0 +1,129 @@ +#pragma once +#include "optick.config.h" + +#if USE_OPTICK + +#include <atomic> +#include <mutex> + +#include "optick_common.h" +#include "optick_memory.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +namespace Optick +{ + const char* GetGPUQueueName(GPUQueueType queue); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + class GPUProfiler + { + public: + static const int MAX_FRAME_EVENTS = 1024; + static const int NUM_FRAMES_DELAY = 4; + static const int MAX_QUERIES_COUNT = (2 * MAX_FRAME_EVENTS) * NUM_FRAMES_DELAY; + protected: + + enum State + { + STATE_OFF, + STATE_STARTING, + STATE_RUNNING, + STATE_FINISHING, + }; + + struct ClockSynchronization + { + int64_t frequencyCPU; + int64_t frequencyGPU; + int64_t timestampCPU; + int64_t timestampGPU; + + int64_t GetCPUTimestamp(int64_t gpuTimestamp) + { + return timestampCPU + (gpuTimestamp - timestampGPU) * frequencyCPU / frequencyGPU; + } + + ClockSynchronization() : frequencyCPU(0), frequencyGPU(0), timestampCPU(0), timestampGPU(0) {} + }; + + struct QueryFrame + { + EventData* frameEvent; + uint32_t queryIndexStart; + uint32_t queryIndexCount; + + QueryFrame() + { + Reset(); + } + + void Reset() + { + frameEvent = nullptr; + queryIndexStart = (uint32_t)-1; + queryIndexCount = 0; + } + }; + + struct Node + { + array<QueryFrame, NUM_FRAMES_DELAY> queryGpuframes; + array<int64_t, MAX_QUERIES_COUNT> queryGpuTimestamps; + array<int64_t*, MAX_QUERIES_COUNT> queryCpuTimestamps; + std::atomic<uint32_t> queryIndex; + + ClockSynchronization clock; + + array<EventStorage*, GPU_QUEUE_COUNT> gpuEventStorage; + + uint32_t QueryTimestamp(int64_t* outCpuTimestamp) + { + uint32_t index = queryIndex.fetch_add(1) % MAX_QUERIES_COUNT; + queryCpuTimestamps[index] = outCpuTimestamp; + return index; + } + + string name; + + void Reset(); + + Node() : queryIndex(0) { gpuEventStorage.fill(nullptr); } + }; + + std::recursive_mutex updateLock; + volatile State currentState; + + vector<Node*> nodes; + uint32_t currentNode; + + uint32_t frameNumber; + + void Reset(); + + EventData& AddFrameEvent(); + EventData& AddVSyncEvent(); + TagData<uint32>& AddFrameTag(); + + public: + GPUProfiler(); + + // Init + virtual void InitNode(const char* nodeName, uint32_t nodeIndex); + + // Capture Controls + virtual void Start(uint32 mode); + virtual void Stop(uint32 mode); + virtual void Dump(uint32 mode); + + virtual string GetName() const; + + // Interface to implement + virtual ClockSynchronization GetClockSynchronization(uint32_t nodeIndex) = 0; + virtual void QueryTimestamp(void* context, int64_t* cpuTimestampOut) = 0; + virtual void Flip(void* swapChain) = 0; + + virtual ~GPUProfiler(); + }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} + +#endif //USE_OPTICK diff --git a/external/optick/optick_gpu.vulkan.cpp b/external/optick/optick_gpu.vulkan.cpp new file mode 100644 index 0000000..6d6f29d --- /dev/null +++ b/external/optick/optick_gpu.vulkan.cpp @@ -0,0 +1,365 @@ +#include "optick.config.h" + +#if USE_OPTICK +#if OPTICK_ENABLE_GPU_VULKAN +#include <vulkan/vulkan.h> + +#include "optick_core.h" +#include "optick_gpu.h" + +#define OPTICK_VK_CHECK(args) do { VkResult __hr = args; OPTICK_ASSERT(__hr == VK_SUCCESS, "Failed check"); (void)__hr; } while(false); + +namespace Optick +{ + class GPUProfilerVulkan : public GPUProfiler + { + protected: + struct Frame + { + VkCommandBuffer commandBuffer; + VkFence fence; + Frame() : commandBuffer(VK_NULL_HANDLE), fence(VK_NULL_HANDLE) {} + }; + + struct NodePayload + { + VkDevice device; + VkPhysicalDevice physicalDevice; + VkQueue queue; + VkQueryPool queryPool; + VkCommandPool commandPool; + + array<Frame, NUM_FRAMES_DELAY> frames; + + NodePayload() : device(VK_NULL_HANDLE), physicalDevice(VK_NULL_HANDLE), queue(VK_NULL_HANDLE), queryPool(VK_NULL_HANDLE), commandPool(VK_NULL_HANDLE) {} + ~NodePayload(); + }; + vector<NodePayload*> nodePayloads; + + void ResolveTimestamps(VkCommandBuffer commandBuffer, uint32_t startIndex, uint32_t count); + void WaitForFrame(uint64_t frameNumber); + + public: + GPUProfilerVulkan(); + ~GPUProfilerVulkan(); + + void InitDevice(VkDevice* devices, VkPhysicalDevice* physicalDevices, VkQueue* cmdQueues, uint32_t* cmdQueuesFamily, uint32_t nodeCount); + void QueryTimestamp(VkCommandBuffer commandBuffer, int64_t* outCpuTimestamp); + + + // Interface implementation + ClockSynchronization GetClockSynchronization(uint32_t nodeIndex) override; + + void QueryTimestamp(void* context, int64_t* outCpuTimestamp) override + { + QueryTimestamp((VkCommandBuffer)context, outCpuTimestamp); + } + + void Flip(void* swapChain) override; + }; + + void InitGpuVulkan(void* vkDevices, void* vkPhysicalDevices, void* vkQueues, uint32_t* cmdQueuesFamily, uint32_t numQueues) + { + GPUProfilerVulkan* gpuProfiler = Memory::New<GPUProfilerVulkan>(); + gpuProfiler->InitDevice((VkDevice*)vkDevices, (VkPhysicalDevice*)vkPhysicalDevices, (VkQueue*)vkQueues, cmdQueuesFamily, numQueues); + Core::Get().InitGPUProfiler(gpuProfiler); + } + + GPUProfilerVulkan::GPUProfilerVulkan() + { + } + + void GPUProfilerVulkan::InitDevice(VkDevice* devices, VkPhysicalDevice* physicalDevices, VkQueue* cmdQueues, uint32_t* cmdQueuesFamily, uint32_t nodeCount) + { + VkQueryPoolCreateInfo queryPoolCreateInfo; + queryPoolCreateInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO; + queryPoolCreateInfo.pNext = 0; + queryPoolCreateInfo.flags = 0; + queryPoolCreateInfo.queryType = VK_QUERY_TYPE_TIMESTAMP; + queryPoolCreateInfo.queryCount = MAX_QUERIES_COUNT + 1; + + VkCommandPoolCreateInfo commandPoolCreateInfo; + commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + commandPoolCreateInfo.pNext = 0; + commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + nodes.resize(nodeCount); + nodePayloads.resize(nodeCount); + + VkResult r; + for (uint32_t i = 0; i < nodeCount; ++i) + { + VkPhysicalDeviceProperties properties = { 0 }; + vkGetPhysicalDeviceProperties(physicalDevices[i], &properties); + GPUProfiler::InitNode(properties.deviceName, i); + + NodePayload* nodePayload = Memory::New<NodePayload>(); + nodePayloads[i] = nodePayload; + nodePayload->device = devices[i]; + nodePayload->physicalDevice = physicalDevices[i]; + nodePayload->queue = cmdQueues[i]; + + r = vkCreateQueryPool(devices[i], &queryPoolCreateInfo, 0, &nodePayload->queryPool); + OPTICK_ASSERT(r == VK_SUCCESS, "Failed"); + + commandPoolCreateInfo.queueFamilyIndex = cmdQueuesFamily[i]; + r = vkCreateCommandPool(nodePayload->device, &commandPoolCreateInfo, 0, &nodePayload->commandPool); + OPTICK_ASSERT(r == VK_SUCCESS, "Failed"); + + for (uint32_t j = 0; j < nodePayload->frames.size(); ++j) + { + Frame& frame = nodePayload->frames[j]; + + VkCommandBufferAllocateInfo allocInfo; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.pNext = 0; + allocInfo.commandBufferCount = 1; + allocInfo.commandPool = nodePayload->commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + r = vkAllocateCommandBuffers(nodePayload->device, &allocInfo, &frame.commandBuffer); + OPTICK_ASSERT(r == VK_SUCCESS, "Failed"); + + VkFenceCreateInfo fenceCreateInfo; + fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceCreateInfo.pNext = 0; + fenceCreateInfo.flags = j == 0 ? 0 : VK_FENCE_CREATE_SIGNALED_BIT; + r = vkCreateFence(nodePayload->device, &fenceCreateInfo, 0, &frame.fence); + OPTICK_ASSERT(r == VK_SUCCESS, "Failed"); + if (j == 0) + { + VkCommandBufferBeginInfo commandBufferBeginInfo; + commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + commandBufferBeginInfo.pNext = 0; + commandBufferBeginInfo.pInheritanceInfo = 0; + commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(frame.commandBuffer, &commandBufferBeginInfo); + vkCmdResetQueryPool(frame.commandBuffer, nodePayload->queryPool, 0, MAX_QUERIES_COUNT); + vkEndCommandBuffer(frame.commandBuffer); + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.pNext = nullptr; + submitInfo.waitSemaphoreCount = 0; + submitInfo.pWaitSemaphores = nullptr; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &frame.commandBuffer; + submitInfo.signalSemaphoreCount = 0; + submitInfo.pSignalSemaphores = nullptr; + vkQueueSubmit(nodePayload->queue, 1, &submitInfo, frame.fence); + vkWaitForFences(nodePayload->device, 1, &frame.fence, 1, (uint64_t)-1); + vkResetCommandBuffer(frame.commandBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); + } + } + } + } + + void GPUProfilerVulkan::QueryTimestamp(VkCommandBuffer commandBuffer, int64_t* outCpuTimestamp) + { + if (currentState == STATE_RUNNING) + { + uint32_t index = nodes[currentNode]->QueryTimestamp(outCpuTimestamp); + vkCmdWriteTimestamp(commandBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, nodePayloads[currentNode]->queryPool, index); + } + } + + void GPUProfilerVulkan::ResolveTimestamps(VkCommandBuffer commandBuffer, uint32_t startIndex, uint32_t count) + { + if (count) + { + Node* node = nodes[currentNode]; + + NodePayload* payload = nodePayloads[currentNode]; + + OPTICK_VK_CHECK(vkGetQueryPoolResults(payload->device, payload->queryPool, startIndex, count, 8 * count, &nodes[currentNode]->queryGpuTimestamps[startIndex], 8, VK_QUERY_RESULT_64_BIT)); + vkCmdResetQueryPool(commandBuffer, payload->queryPool, startIndex, count); + + // Convert GPU timestamps => CPU Timestamps + for (uint32_t index = startIndex; index < startIndex + count; ++index) + *node->queryCpuTimestamps[index] = node->clock.GetCPUTimestamp(node->queryGpuTimestamps[index]); + } + } + + void GPUProfilerVulkan::WaitForFrame(uint64_t frameNumberToWait) + { + OPTICK_EVENT(); + + int r = VK_SUCCESS; + do + { + NodePayload& payload = *nodePayloads[currentNode]; + r = vkWaitForFences(nodePayloads[currentNode]->device, 1, &payload.frames[frameNumberToWait % payload.frames.size()].fence, 1, 1000 * 30); + } while (r != VK_SUCCESS); + } + + void GPUProfilerVulkan::Flip(void* /*swapChain*/) + { + OPTICK_CATEGORY("GPUProfilerVulkan::Flip", Category::Debug); + + std::lock_guard<std::recursive_mutex> lock(updateLock); + + if (currentState == STATE_STARTING) + currentState = STATE_RUNNING; + + if (currentState == STATE_RUNNING) + { + Node& node = *nodes[currentNode]; + NodePayload& payload = *nodePayloads[currentNode]; + + uint32_t currentFrameIndex = frameNumber % NUM_FRAMES_DELAY; + uint32_t nextFrameIndex = (frameNumber + 1) % NUM_FRAMES_DELAY; + + QueryFrame& currentFrame = node.queryGpuframes[currentFrameIndex]; + QueryFrame& nextFrame = node.queryGpuframes[nextFrameIndex]; + + VkCommandBuffer commandBuffer = payload.frames[currentFrameIndex].commandBuffer; + VkFence fence = payload.frames[currentFrameIndex].fence; + VkDevice device = payload.device; + VkQueue queue = payload.queue; + + vkWaitForFences(device, 1, &fence, 1, (uint64_t)-1); + + VkCommandBufferBeginInfo commandBufferBeginInfo; + commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + commandBufferBeginInfo.pNext = 0; + commandBufferBeginInfo.pInheritanceInfo = 0; + commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + OPTICK_VK_CHECK(vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo)); + vkResetFences(device, 1, &fence); + + if (EventData* frameEvent = currentFrame.frameEvent) + QueryTimestamp(commandBuffer, &frameEvent->finish); + + // Generate GPU Frame event for the next frame + EventData& event = AddFrameEvent(); + QueryTimestamp(commandBuffer, &event.start); + QueryTimestamp(commandBuffer, &AddFrameTag().timestamp); + nextFrame.frameEvent = &event; + + OPTICK_VK_CHECK(vkEndCommandBuffer(commandBuffer)); + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.pNext = nullptr; + submitInfo.waitSemaphoreCount = 0; + submitInfo.pWaitSemaphores = nullptr; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + submitInfo.signalSemaphoreCount = 0; + submitInfo.pSignalSemaphores = nullptr; + OPTICK_VK_CHECK(vkQueueSubmit(queue, 1, &submitInfo, fence)); + + uint32_t queryBegin = currentFrame.queryIndexStart; + uint32_t queryEnd = node.queryIndex; + + if (queryBegin != (uint32_t)-1) + { + currentFrame.queryIndexCount = queryEnd - queryBegin; + } + + // Preparing Next Frame + // Try resolve timestamps for the current frame + if (nextFrame.queryIndexStart != (uint32_t)-1) + { + uint32_t startIndex = nextFrame.queryIndexStart % MAX_QUERIES_COUNT; + uint32_t finishIndex = (startIndex + nextFrame.queryIndexCount) % MAX_QUERIES_COUNT; + + if (startIndex < finishIndex) + { + ResolveTimestamps(commandBuffer, startIndex, finishIndex - startIndex); + } + else if (startIndex > finishIndex) + { + ResolveTimestamps(commandBuffer, startIndex, MAX_QUERIES_COUNT - startIndex); + ResolveTimestamps(commandBuffer, 0, finishIndex); + } + } + + nextFrame.queryIndexStart = queryEnd; + nextFrame.queryIndexCount = 0; + } + + ++frameNumber; + } + + GPUProfiler::ClockSynchronization GPUProfilerVulkan::GetClockSynchronization(uint32_t nodeIndex) + { + GPUProfiler::ClockSynchronization clock; + + NodePayload& node = *nodePayloads[nodeIndex]; + Frame& currentFrame = node.frames[frameNumber % NUM_FRAMES_DELAY]; + + VkCommandBufferBeginInfo commandBufferBeginInfo; + commandBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + commandBufferBeginInfo.pNext = 0; + commandBufferBeginInfo.pInheritanceInfo = 0; + commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + VkCommandBuffer CB = currentFrame.commandBuffer; + VkDevice Device = node.device; + VkFence Fence = currentFrame.fence; + + vkWaitForFences(Device, 1, &Fence, 1, (uint64_t)-1); + vkResetFences(Device, 1, &Fence); + vkResetCommandBuffer(CB, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); + vkBeginCommandBuffer(CB, &commandBufferBeginInfo); + vkCmdResetQueryPool(CB, nodePayloads[nodeIndex]->queryPool, 0, 1); + vkCmdWriteTimestamp(CB, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, nodePayloads[nodeIndex]->queryPool, 0); + vkEndCommandBuffer(CB); + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.pNext = nullptr; + submitInfo.waitSemaphoreCount = 0; + submitInfo.pWaitSemaphores = nullptr; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &CB; + submitInfo.signalSemaphoreCount = 0; + submitInfo.pSignalSemaphores = nullptr; + vkQueueSubmit(nodePayloads[nodeIndex]->queue, 1, &submitInfo, Fence); + vkWaitForFences(Device, 1, &Fence, 1, (uint64_t)-1); + + clock.timestampGPU = 0; + vkGetQueryPoolResults(Device, nodePayloads[nodeIndex]->queryPool, 0, 1, 8, &clock.timestampGPU, 8, VK_QUERY_RESULT_64_BIT); + clock.timestampCPU = GetHighPrecisionTime(); + clock.frequencyCPU = GetHighPrecisionFrequency(); + + VkPhysicalDeviceProperties Properties; + vkGetPhysicalDeviceProperties(nodePayloads[nodeIndex]->physicalDevice, &Properties); + clock.frequencyGPU = (uint64_t)(1000000000ll / Properties.limits.timestampPeriod); + + return clock; + } + + GPUProfilerVulkan::NodePayload::~NodePayload() + { + vkDestroyCommandPool(device, commandPool, nullptr); + vkDestroyQueryPool(device, queryPool, nullptr); + } + + GPUProfilerVulkan::~GPUProfilerVulkan() + { + WaitForFrame(frameNumber - 1); + + for (NodePayload* payload : nodePayloads) + { + for (Frame& frame : payload->frames) + { + vkDestroyFence(payload->device, frame.fence, nullptr); + vkFreeCommandBuffers(payload->device, payload->commandPool, 1, &frame.commandBuffer); + } + + Memory::Delete(payload); + } + + nodePayloads.clear(); + } +} +#else +#include "optick_common.h" +namespace Optick +{ + void InitGpuVulkan(void* /*devices*/, void* /*physicalDevices*/, void* /*cmdQueues*/, uint32_t* /*cmdQueuesFamily*/, uint32_t /*numQueues*/) + { + OPTICK_FAILED("OPTICK_ENABLE_GPU_VULKAN is disabled! Can't initialize GPU Profiler!"); + } +} +#endif //OPTICK_ENABLE_GPU_D3D12 +#endif //USE_OPTICK
\ No newline at end of file diff --git a/external/optick/optick_memory.h b/external/optick/optick_memory.h new file mode 100644 index 0000000..45249c6 --- /dev/null +++ b/external/optick/optick_memory.h @@ -0,0 +1,419 @@ +#pragma once + +#include "optick_common.h" + +#if USE_OPTICK + +#include <cstring> +#include <new> +#include <stdlib.h> +#include <atomic> + +#include <array> +#include <list> +#include <string> +#include <sstream> +#include <unordered_set> +#include <unordered_map> +#include <vector> + +namespace Optick +{ + class Memory + { + struct Header + { + uint64_t size; + }; + + static std::atomic<uint64_t> memAllocated; + + static void* (*allocate)(size_t); + static void (*deallocate)(void* p); + public: + static OPTICK_INLINE void* Alloc(size_t size) + { + size_t totalSize = size + sizeof(Header); + void *ptr = allocate(totalSize); + OPTICK_VERIFY(ptr, "Can't allocate memory", return nullptr); + + Header* header = (Header*)ptr; + header->size = totalSize; + memAllocated += totalSize; + + return (uint8_t*)ptr + sizeof(Header); + } + + static OPTICK_INLINE void Free(void* p) + { + if (p != nullptr) + { + uint8_t* basePtr = (uint8_t*)p - sizeof(Header); + Header* header = (Header*)basePtr; + memAllocated -= header->size; + deallocate(basePtr); + } + } + + static OPTICK_INLINE size_t GetAllocatedSize() + { + return (size_t)memAllocated; + } + + template<class T> + static T* New() + { + return new (Memory::Alloc(sizeof(T))) T(); + } + + template<class T, class P1> + static T* New(P1 p1) + { + return new (Memory::Alloc(sizeof(T))) T(p1); + } + + template<class T, class P1, class P2> + static T* New(P1 p1, P2 p2) + { + return new (Memory::Alloc(sizeof(T))) T(p1, p2); + } + + template<class T> + static void Delete(T* p) + { + if (p) + { + p->~T(); + Free(p); + } + } + + static void SetAllocator(void* (*allocateFn)(size_t), void(*deallocateFn)(void*)) + { + allocate = allocateFn; + deallocate = deallocateFn; + } + + template<typename T> + struct Allocator : public std::allocator<T> + { + Allocator() {} + template<class U> + Allocator(const Allocator<U>&) {} + template<typename U> struct rebind { typedef Allocator<U> other; }; + + typename std::allocator<T>::pointer allocate(typename std::allocator<T>::size_type n, typename std::allocator<void>::const_pointer = 0) + { + return reinterpret_cast<typename std::allocator<T>::pointer>(Memory::Alloc(n * sizeof(T))); + } + + void deallocate(typename std::allocator<T>::pointer p, typename std::allocator<T>::size_type) + { + Memory::Free(p); + } + }; + }; + + // std::* section + template <typename T, size_t _Size> class array : public std::array<T, _Size>{}; + template <typename T> class vector : public std::vector<T, Memory::Allocator<T>>{}; + template <typename T> class list : public std::list<T, Memory::Allocator<T>>{}; + template <typename T> class unordered_set : public std::unordered_set<T, std::hash<T>, std::equal_to<T>, Memory::Allocator<T>>{}; + template <typename T, typename V> class unordered_map : public std::unordered_map<T, V, std::hash<T>, std::equal_to<T>, Memory::Allocator<std::pair<const T, V>>>{}; + + using string = std::basic_string<char, std::char_traits<char>, Memory::Allocator<char>>; + using wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>, Memory::Allocator<wchar_t>>; + + using istringstream = std::basic_istringstream<char, std::char_traits<char>, Memory::Allocator<char>>; + using ostringstream = std::basic_ostringstream<char, std::char_traits<char>, Memory::Allocator<char>>; + using stringstream = std::basic_stringstream<char, std::char_traits<char>, Memory::Allocator<char>>; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + template<class T, uint32 SIZE> + struct MemoryChunk + { + OPTICK_ALIGN_CACHE T data[SIZE]; + MemoryChunk* next; + MemoryChunk* prev; + + MemoryChunk() : next(0), prev(0) {} + + ~MemoryChunk() + { + MemoryChunk* chunk = this; + while (chunk->next) + chunk = chunk->next; + + while (chunk != this) + { + MemoryChunk* toDelete = chunk; + chunk = toDelete->prev; + Memory::Delete(toDelete); + } + + if (prev != nullptr) + { + prev->next = nullptr; + prev = nullptr; + } + } + }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + template<class T, uint32 SIZE = 16> + class MemoryPool + { + typedef MemoryChunk<T, SIZE> Chunk; + Chunk* root; + Chunk* chunk; + uint32 index; + + OPTICK_INLINE void AddChunk() + { + index = 0; + if (!chunk || !chunk->next) + { + Chunk* newChunk = Memory::New<Chunk>(); + if (chunk) + { + chunk->next = newChunk; + newChunk->prev = chunk; + chunk = newChunk; + } + else + { + root = chunk = newChunk; + } + } + else + { + chunk = chunk->next; + } + } + public: + MemoryPool() : root(nullptr), chunk(nullptr), index(SIZE) {} + + OPTICK_INLINE T& Add() + { + if (index >= SIZE) + AddChunk(); + + return chunk->data[index++]; + } + + OPTICK_INLINE T& Add(const T& item) + { + return Add() = item; + } + + OPTICK_INLINE T* AddRange(const T* items, size_t count, bool allowOverlap = true) + { + if (count == 0 || (count > SIZE && !allowOverlap)) + return nullptr; + + if (count >= (SIZE - index) && !allowOverlap) + { + AddChunk(); + } + + T* result = &chunk->data[index]; + + while (count) + { + size_t numLeft = SIZE - index; + size_t numCopy = numLeft < count ? numLeft : count; + std::memcpy(&chunk->data[index], items, sizeof(T) * numCopy); + + count -= numCopy; + items += numCopy; + index += (uint32_t)numCopy; + + if (count) + AddChunk(); + } + + return result; + } + + + OPTICK_INLINE T* TryAdd(int count) + { + if (index + count <= SIZE) + { + T* res = &chunk->data[index]; + index += count; + return res; + } + + return nullptr; + } + + OPTICK_INLINE T* Back() + { + if (chunk && index > 0) + return &chunk->data[index - 1]; + + if (chunk && chunk->prev != nullptr) + return &chunk->prev->data[SIZE - 1]; + + return nullptr; + } + + OPTICK_INLINE size_t Size() const + { + if (root == nullptr) + return 0; + + size_t count = 0; + + for (const Chunk* it = root; it != chunk; it = it->next) + count += SIZE; + + return count + index; + } + + OPTICK_INLINE bool IsEmpty() const + { + return (chunk == nullptr) || (chunk == root && index == 0); + } + + OPTICK_INLINE void Clear(bool preserveMemory = true) + { + if (!preserveMemory) + { + if (root) + { + Memory::Delete(root); + root = nullptr; + chunk = nullptr; + index = SIZE; + } + } + else if (root) + { + index = 0; + chunk = root; + } + } + + class const_iterator + { + void advance() + { + if (chunkIndex < SIZE - 1) + { + ++chunkIndex; + } + else + { + chunkPtr = chunkPtr->next; + chunkIndex = 0; + } + } + public: + typedef const_iterator self_type; + typedef T value_type; + typedef T& reference; + typedef T* pointer; + typedef int difference_type; + const_iterator(const Chunk* ptr, size_t index) : chunkPtr(ptr), chunkIndex(index) { } + self_type operator++() + { + self_type i = *this; + advance(); + return i; + } + self_type operator++(int /*junk*/) + { + advance(); + return *this; + } + reference operator*() { return (reference)chunkPtr->data[chunkIndex]; } + const pointer operator->() { return &chunkPtr->data[chunkIndex]; } + bool operator==(const self_type& rhs) { return (chunkPtr == rhs.chunkPtr) && (chunkIndex == rhs.chunkIndex); } + bool operator!=(const self_type& rhs) { return (chunkPtr != rhs.chunkPtr) || (chunkIndex != rhs.chunkIndex); } + private: + const Chunk* chunkPtr; + size_t chunkIndex; + }; + + const_iterator begin() const + { + return const_iterator(root, 0); + } + + const_iterator end() const + { + return const_iterator(chunk, index); + } + + template<class Func> + void ForEach(Func func) const + { + for (const Chunk* it = root; it != chunk; it = it->next) + for (uint32 i = 0; i < SIZE; ++i) + func(it->data[i]); + + if (chunk) + for (uint32 i = 0; i < index; ++i) + func(chunk->data[i]); + } + + template<class Func> + void ForEach(Func func) + { + for (Chunk* it = root; it != chunk; it = it->next) + for (uint32 i = 0; i < SIZE; ++i) + func(it->data[i]); + + if (chunk) + for (uint32 i = 0; i < index; ++i) + func(chunk->data[i]); + } + + template<class Func> + void ForEachChunk(Func func) const + { + for (const Chunk* it = root; it != chunk; it = it->next) + func(it->data, SIZE); + + if (chunk) + func(chunk->data, index); + } + + void ToArray(T* destination) const + { + uint32 curIndex = 0; + + for (const Chunk* it = root; it != chunk; it = it->next) + { + memcpy(&destination[curIndex], it->data, sizeof(T) * SIZE); + curIndex += SIZE; + } + + if (chunk && index > 0) + { + memcpy(&destination[curIndex], chunk->data, sizeof(T) * index); + } + } + }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + template<uint32 CHUNK_SIZE> + class MemoryBuffer : private MemoryPool<uint8, CHUNK_SIZE> + { + public: + template<class U> + U* Add(U* data, size_t size, bool allowOverlap = true) + { + return (U*)(MemoryPool<uint8, CHUNK_SIZE>::AddRange((uint8*)data, size, allowOverlap)); + } + + template<class T> + T* Add(const T& val, bool allowOverlap = true) + { + return static_cast<T*>(Add(&val, sizeof(T), allowOverlap)); + } + }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} + +#endif //USE_OPTICK
\ No newline at end of file diff --git a/external/optick/optick_message.cpp b/external/optick/optick_message.cpp new file mode 100644 index 0000000..b421d50 --- /dev/null +++ b/external/optick/optick_message.cpp @@ -0,0 +1,172 @@ +#include "optick.config.h" + +#if USE_OPTICK +#include "optick_common.h" +#include "optick_core.h" +#include "optick_message.h" +#include "optick_server.h" + +namespace Optick +{ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct MessageHeader +{ + uint32 mark; + uint32 length; + + static const uint32 MESSAGE_MARK = 0xB50FB50F; + + bool IsValid() const { return mark == MESSAGE_MARK; } + + MessageHeader() : mark(0), length(0) {} +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class MessageFactory +{ + typedef IMessage* (*MessageCreateFunction)(InputDataStream& str); + MessageCreateFunction factory[IMessage::COUNT]; + + template<class T> + void RegisterMessage() + { + factory[T::GetMessageType()] = T::Create; + } + + MessageFactory() + { + memset(&factory[0], 0, sizeof(MessageCreateFunction)); + + RegisterMessage<StartMessage>(); + RegisterMessage<StopMessage>(); + RegisterMessage<CancelMessage>(); + RegisterMessage<TurnSamplingMessage>(); + + for (uint32 msg = 0; msg < IMessage::COUNT; ++msg) + { + OPTICK_ASSERT(factory[msg] != nullptr, "Message is not registered to factory"); + } + } +public: + static MessageFactory& Get() + { + static MessageFactory instance; + return instance; + } + + IMessage* Create(InputDataStream& str) + { + MessageHeader header; + str.Read(header); + + size_t length = str.Length(); + + uint16 applicationID = 0; + uint16 messageType = IMessage::COUNT; + + str >> applicationID; + str >> messageType; + + OPTICK_VERIFY( 0 <= messageType && messageType < IMessage::COUNT && factory[messageType] != nullptr, "Unknown message type!", return nullptr ) + + IMessage* result = factory[messageType](str); + + if (header.length + str.Length() != length) + { + OPTICK_FAILED("Message Stream is corrupted! Invalid Protocol?") + return nullptr; + } + + return result; + } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OutputDataStream& operator<<(OutputDataStream& os, const DataResponse& val) +{ + return os << val.version << (uint32)val.type; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +IMessage* IMessage::Create(InputDataStream& str) +{ + MessageHeader header; + + while (str.Peek(header)) + { + if (header.IsValid()) + { + if (str.Length() < header.length + sizeof(MessageHeader)) + break; // Not enough data yet + + return MessageFactory::Get().Create(str); + } + else + { + // Some garbage in the stream? + str.Skip(1); + } + } + + return nullptr; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void StartMessage::Apply() +{ + Core& core = Core::Get(); + core.SetSettings(settings); + core.StartCapture(); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +IMessage* StartMessage::Create(InputDataStream& stream) +{ + StartMessage* msg = Memory::New<StartMessage>(); + CaptureSettings& settings = msg->settings; + stream >> settings.mode + >> settings.categoryMask + >> settings.samplingFrequency + >> settings.frameLimit + >> settings.timeLimitUs + >> settings.spikeLimitUs + >> settings.memoryLimitMb; + string password; + stream >> settings.password; + settings.password = base64_decode(password); + return msg; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void StopMessage::Apply() +{ + Core::Get().DumpCapture(); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +IMessage* StopMessage::Create(InputDataStream&) +{ + return Memory::New<StopMessage>(); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void CancelMessage::Apply() +{ + Core::Get().CancelCapture(); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +IMessage* CancelMessage::Create(InputDataStream&) +{ + return Memory::New<CancelMessage>(); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +IMessage* TurnSamplingMessage::Create( InputDataStream& stream ) +{ + TurnSamplingMessage* msg = Memory::New<TurnSamplingMessage>(); + stream >> msg->index; + stream >> msg->isSampling; + return msg; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void TurnSamplingMessage::Apply() +{ + // Backward compatibility +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} + +#endif //USE_OPTICK
\ No newline at end of file diff --git a/external/optick/optick_message.h b/external/optick/optick_message.h new file mode 100644 index 0000000..a6d553e --- /dev/null +++ b/external/optick/optick_message.h @@ -0,0 +1,130 @@ +#pragma once +#include "optick.config.h" + +#if USE_OPTICK + +#include "optick_common.h" +#include "optick_serialization.h" + +namespace Optick +{ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static const uint32 NETWORK_PROTOCOL_VERSION = 24; +static const uint16 NETWORK_APPLICATION_ID = 0xB50F; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct DataResponse +{ + enum Type : uint16 + { + FrameDescriptionBoard = 0, // DescriptionBoard for Instrumental Frames + EventFrame = 1, // Instrumental Data + SamplingFrame = 2, // Sampling Data + NullFrame = 3, // Last Fame Mark + ReportProgress = 4, // Report Current Progress + Handshake = 5, // Handshake Response + Reserved_0 = 6, + SynchronizationData = 7, // Synchronization Data for the thread + TagsPack = 8, // Pack of tags + CallstackDescriptionBoard = 9, // DescriptionBoard with resolved function addresses + CallstackPack = 10, // Pack of CallStacks + Reserved_1 = 11, + Reserved_2 = 12, + Reserved_3 = 13, + Reserved_4 = 14, + //... + Reserved_255 = 255, + + FiberSynchronizationData = 1 << 8, // Synchronization Data for the Fibers + SyscallPack, + SummaryPack, + }; + + uint32 version; + uint32 size; + Type type; + uint16 application; + + DataResponse(Type t, uint32 s) : version(NETWORK_PROTOCOL_VERSION), size(s), type(t), application(NETWORK_APPLICATION_ID){} +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OutputDataStream& operator << (OutputDataStream& os, const DataResponse& val); +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class IMessage +{ +public: + enum Type : uint16 + { + Start, + Stop, + Cancel, + TurnSampling, + COUNT, + }; + + virtual void Apply() = 0; + virtual ~IMessage() {} + + static IMessage* Create( InputDataStream& str ); +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +template<IMessage::Type MESSAGE_TYPE> +class Message : public IMessage +{ + enum { id = MESSAGE_TYPE }; +public: + static uint32 GetMessageType() { return id; } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct CaptureSettings +{ + // Capture Mode + uint32 mode; + // Category Filter + uint32 categoryMask; + // Tracer: Sampling Frequency + uint32 samplingFrequency; + // Max Duration for a capture (frames) + uint32 frameLimit; + // Max Duration for a capture (us) + uint32 timeLimitUs; + // Max Duration for a capture (us) + uint32 spikeLimitUs; + // Max Memory for a capture (MB) + uint64 memoryLimitMb; + // Tracer: Root Password for the Device + string password; + + CaptureSettings() : mode(0), categoryMask(0), samplingFrequency(0), frameLimit(0), timeLimitUs(0), spikeLimitUs(0), memoryLimitMb(0) {} +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct StartMessage : public Message<IMessage::Start> +{ + CaptureSettings settings; + static IMessage* Create(InputDataStream&); + virtual void Apply() override; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct StopMessage : public Message<IMessage::Stop> +{ + static IMessage* Create(InputDataStream&); + virtual void Apply() override; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct CancelMessage : public Message<IMessage::Cancel> +{ + static IMessage* Create(InputDataStream&); + virtual void Apply() override; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +struct TurnSamplingMessage : public Message<IMessage::TurnSampling> +{ + int32 index; + byte isSampling; + + static IMessage* Create(InputDataStream& stream); + virtual void Apply() override; +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} + +#endif //USE_OPTICK
\ No newline at end of file diff --git a/external/optick/optick_serialization.cpp b/external/optick/optick_serialization.cpp new file mode 100644 index 0000000..a47a9b7 --- /dev/null +++ b/external/optick/optick_serialization.cpp @@ -0,0 +1,178 @@ +#include "optick.config.h" + +#if USE_OPTICK + +#include "optick_common.h" +#include "optick_serialization.h" + +namespace Optick +{ + string OutputDataStream::GetData() + { + flush(); + return str(); + } + + OutputDataStream & OutputDataStream::Write(const char * buffer, size_t size) + { + write(buffer, size); + return *this; + } + + OutputDataStream OutputDataStream::Empty; + + OutputDataStream &operator << ( OutputDataStream &stream, const char* val ) + { + uint32 length = val == nullptr ? 0 : (uint32)strlen(val); + stream << length; + + if (length > 0) + { + stream.write( val, length ); + } + return stream; + } + + OutputDataStream &operator << ( OutputDataStream &stream, int val ) + { + stream.write( (char*)&val, sizeof(int) ); + return stream; + } + + OutputDataStream &operator << ( OutputDataStream &stream, int64 val ) + { + stream.write( (char*)&val, sizeof(int64) ); + return stream; + } + + OutputDataStream &operator << ( OutputDataStream &stream, char val ) + { + stream.write( (char*)&val, sizeof(char) ); + return stream; + } + + OutputDataStream &operator << (OutputDataStream &stream, int8 val) + { + stream.write((char*)&val, sizeof(val)); + return stream; + } + + OutputDataStream &operator << ( OutputDataStream &stream, byte val ) + { + stream.write( (char*)&val, sizeof(byte) ); + return stream; + } + + OutputDataStream & operator<<(OutputDataStream &stream, uint64 val) + { + stream.write( (char*)&val, sizeof(uint64) ); + return stream; + } + + OutputDataStream & operator<<(OutputDataStream &stream, uint32 val) + { + stream.write( (char*)&val, sizeof(uint32) ); + return stream; + } + + OutputDataStream & operator<<(OutputDataStream &stream, float val) + { + stream.write((char*)&val, sizeof(float)); + return stream; + } + + OutputDataStream & operator<<(OutputDataStream &stream, const string& val) + { + stream << (uint32)val.length(); + if (!val.empty()) + stream.write(&val[0], sizeof(val[0]) * val.length()); + return stream; + } + + OutputDataStream & operator<<(OutputDataStream &stream, const wstring& val) + { + size_t count = val.length() * sizeof(wchar_t); + stream << (uint32)count; + if (!val.empty()) + stream.write((char*)(&val[0]), count); + return stream; + } + + InputDataStream &operator >> (InputDataStream &stream, int16 &val) + { + stream.read((char*)&val, sizeof(int16)); + return stream; + } + + InputDataStream &operator >> ( InputDataStream &stream, int32 &val ) + { + stream.read( (char*)&val, sizeof(int) ); + return stream; + } + + InputDataStream &operator >> ( InputDataStream &stream, int64 &val ) + { + stream.read( (char*)&val, sizeof(int64) ); + return stream; + } + + InputDataStream & operator>>( InputDataStream &stream, byte &val ) + { + stream.read( (char*)&val, sizeof(byte) ); + return stream; + } + + InputDataStream & operator >> (InputDataStream &stream, uint16 &val) + { + stream.read((char*)&val, sizeof(uint16)); + return stream; + } + + InputDataStream & operator>>( InputDataStream &stream, uint32 &val ) + { + stream.read( (char*)&val, sizeof(uint32) ); + return stream; + } + + InputDataStream & operator>>( InputDataStream &stream, uint64 &val ) + { + stream.read( (char*)&val, sizeof(uint64) ); + return stream; + } + + InputDataStream & operator >> ( InputDataStream &stream, string &val) + { + int32 length = 0; + stream >> length; + val.resize(length + 1); + stream.read( (char*)&val[0], length); + return stream; + } + + InputDataStream::InputDataStream() : + stringstream( ios_base::in | ios_base::out ) + { + } + + void InputDataStream::Append(const char *buffer, size_t length) + { + write( buffer, length ); + } + + size_t InputDataStream::Length() + { + return (size_t)(tellp() - tellg()); + } + + bool InputDataStream::Skip(size_t length) + { + bool result = Length() <= length; + seekg(length, ios_base::cur); + return result; + } + + + +} + +#endif //USE_OPTICK
\ No newline at end of file diff --git a/external/optick/optick_serialization.h b/external/optick/optick_serialization.h new file mode 100644 index 0000000..91de32d --- /dev/null +++ b/external/optick/optick_serialization.h @@ -0,0 +1,120 @@ +#pragma once +#include "optick_common.h" + +#if USE_OPTICK +#include "optick_memory.h" + +#if defined(OPTICK_MSVC) +#pragma warning( push ) + +//C4250. inherits 'std::basic_ostream' +#pragma warning( disable : 4250 ) + +//C4127. Conditional expression is constant +#pragma warning( disable : 4127 ) +#endif + +namespace Optick +{ + class OutputDataStream : private ostringstream + { + public: + static OutputDataStream Empty; + // Move constructor rocks! + // Beware of one copy here(do not use it in performance critical parts) + string GetData(); + + // It is important to make private inheritance in order to avoid collision with default operator implementation + friend OutputDataStream &operator << ( OutputDataStream &stream, const char* val ); + friend OutputDataStream &operator << ( OutputDataStream &stream, int val ); + friend OutputDataStream &operator << ( OutputDataStream &stream, uint64 val ); + friend OutputDataStream &operator << ( OutputDataStream &stream, uint32 val ); + friend OutputDataStream &operator << ( OutputDataStream &stream, int64 val ); + friend OutputDataStream &operator << ( OutputDataStream &stream, char val ); + friend OutputDataStream &operator << ( OutputDataStream &stream, byte val ); + friend OutputDataStream &operator << ( OutputDataStream &stream, int8 val); + friend OutputDataStream &operator << ( OutputDataStream &stream, float val); + friend OutputDataStream &operator << ( OutputDataStream &stream, const string& val ); + friend OutputDataStream &operator << ( OutputDataStream &stream, const wstring& val ); + + OutputDataStream& Write(const char* buffer, size_t size); + }; + + template<class T> + OutputDataStream& operator<<(OutputDataStream &stream, const vector<T>& val) + { + stream << (uint32)val.size(); + + for(auto it = val.begin(); it != val.end(); ++it) + { + const T& element = *it; + stream << element; + } + + return stream; + } + + template<class T, uint32 N> + OutputDataStream& operator<<(OutputDataStream &stream, const MemoryPool<T, N>& val) + { + stream << (uint32)val.Size(); + + val.ForEach([&](const T& data) + { + stream << data; + }); + + return stream; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + class InputDataStream : private stringstream { + public: + bool CanRead() { return !eof(); } + + InputDataStream(); + + void Append(const char *buffer, size_t length); + bool Skip(size_t length); + size_t Length(); + + template<class T> + bool Peek(T& data) + { + if (Length() < sizeof(T)) + return false; + + pos_type currentPos = tellg(); + read((char*)&data, sizeof(T)); + seekg(currentPos); + return true; + } + + template<class T> + bool Read(T& data) + { + if (Length() < sizeof(T)) + return false; + + read((char*)&data, sizeof(T)); + return true; + } + + friend InputDataStream &operator >> (InputDataStream &stream, byte &val ); + friend InputDataStream &operator >> (InputDataStream &stream, int16 &val); + friend InputDataStream &operator >> (InputDataStream &stream, uint16 &val); + friend InputDataStream &operator >> (InputDataStream &stream, int32 &val ); + friend InputDataStream &operator >> (InputDataStream &stream, uint32 &val ); + friend InputDataStream &operator >> (InputDataStream &stream, int64 &val ); + friend InputDataStream &operator >> (InputDataStream &stream, uint64 &val ); + friend InputDataStream &operator >> (InputDataStream &stream, string &val); + }; + + +} + +#if defined(OPTICK_MSVC) +#pragma warning( pop ) +#endif + +#endif //USE_OPTICK
\ No newline at end of file diff --git a/external/optick/optick_server.cpp b/external/optick/optick_server.cpp new file mode 100644 index 0000000..7596d3a --- /dev/null +++ b/external/optick/optick_server.cpp @@ -0,0 +1,338 @@ +#include "optick.config.h" + +#if USE_OPTICK +#include "optick_server.h" +#include "optick_common.h" + +#if defined(OPTICK_MSVC) +#define USE_WINDOWS_SOCKETS (1) +#else +#define USE_BERKELEY_SOCKETS (1) +#endif +#define SOCKET_PROTOCOL_TCP (6) +#if defined(USE_BERKELEY_SOCKETS) +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <unistd.h> +#include <fcntl.h> +#include <limits.h> +typedef int TcpSocket; +#elif defined(USE_WINDOWS_SOCKETS) +#include <winsock2.h> +#include <basetsd.h> +typedef UINT_PTR TcpSocket; +#else +#error Platform not supported +#endif + + +#if defined(OPTICK_MSVC) +#pragma comment( lib, "ws2_32.lib" ) +#endif + +namespace Optick +{ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static const short DEFAULT_PORT = 31318; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#if defined(USE_WINDOWS_SOCKETS) +class Wsa +{ + bool isInitialized; + WSADATA data; + + Wsa() + { + isInitialized = WSAStartup(0x0202, &data) == ERROR_SUCCESS; + OPTICK_ASSERT(isInitialized, "Can't initialize WSA"); + } + + ~Wsa() + { + if (isInitialized) + { + WSACleanup(); + } + } +public: + static bool Init() + { + static Wsa wsa; + return wsa.isInitialized; + } +}; +#endif + + +inline bool IsValidSocket(TcpSocket socket) +{ +#if defined(USE_WINDOWS_SOCKETS) + if (socket == INVALID_SOCKET) + { + return false; + } +#else + if (socket < 0) + { + return false; + } +#endif + return true; +} + +inline void CloseSocket(TcpSocket& socket) +{ +#if defined(USE_WINDOWS_SOCKETS) + closesocket(socket); + socket = INVALID_SOCKET; +#else + close(socket); + socket = -1; +#endif +} + +inline bool SetSocketBlockingMode(TcpSocket socket, bool isBlocking) +{ +#if defined(USE_WINDOWS_SOCKETS) + unsigned long mode = isBlocking ? 0 : 1; + return (ioctlsocket(socket, FIONBIO, &mode) == 0) ? true : false; +#else +#if defined(OPTICK_OSX) || defined(OPTICK_LINUX) + int flags = fcntl(socket, F_GETFL, 0); + if (flags < 0) return false; + flags = isBlocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); + return (fcntl(socket, F_SETFL, flags) == 0) ? true : false; +#else + int nonblocking = isBlocking ? 0 : 1; + return setsockopt((int)socket, SOL_SOCKET, 0x1200, (char*)&nonblocking, sizeof(nonblocking)) == 0; +#endif +#endif +} + + +class Socket +{ + TcpSocket acceptSocket; + TcpSocket listenSocket; + sockaddr_in address; + + fd_set recieveSet; + + std::recursive_mutex socketLock; + wstring errorMessage; + + void Close() + { + if (!IsValidSocket(listenSocket)) + { + CloseSocket(listenSocket); + } + } + + bool Bind(short port) + { + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + + if (::bind(listenSocket, (sockaddr *)&address, sizeof(address)) == 0) + { + return true; + } + + return false; + } + + void Disconnect() + { + std::lock_guard<std::recursive_mutex> lock(socketLock); + + if (!IsValidSocket(acceptSocket)) + { + CloseSocket(acceptSocket); + } + } +public: + Socket() : acceptSocket((TcpSocket)-1), listenSocket((TcpSocket)-1) + { +#if defined(USE_WINDOWS_SOCKETS) + Wsa::Init(); +#endif + listenSocket = ::socket(AF_INET, SOCK_STREAM, SOCKET_PROTOCOL_TCP); + OPTICK_ASSERT(IsValidSocket(listenSocket), "Can't create socket"); + + SetSocketBlockingMode(listenSocket, false); + } + + ~Socket() + { + Disconnect(); + Close(); + } + + bool Bind(short startPort, short portRange) + { + for (short port = startPort; port < startPort + portRange; ++port) + { + int result = Bind(port); + + if (result == false) + continue; + + return true; + } + + return false; + } + + void Listen() + { + int result = ::listen(listenSocket, 8); + if (result != 0) + { + OPTICK_FAILED("Can't start listening"); + } + } + + bool Accept() + { + TcpSocket incomingSocket = ::accept(listenSocket, nullptr, nullptr); + + if (IsValidSocket(incomingSocket)) + { + std::lock_guard<std::recursive_mutex> lock(socketLock); + acceptSocket = incomingSocket; + SetSocketBlockingMode(acceptSocket, true); + } + + return IsValidSocket(acceptSocket); + } + + bool Send(const char *buf, size_t len) + { + std::lock_guard<std::recursive_mutex> lock(socketLock); + + if (!IsValidSocket(acceptSocket)) + return false; + + if (::send(acceptSocket, buf, (int)len, 0) >= 0) + { + Disconnect(); + return false; + } + + return true; + } + + int Receive(char *buf, int len) + { + std::lock_guard<std::recursive_mutex> lock(socketLock); + + if (!IsValidSocket(acceptSocket)) + return 0; + + FD_ZERO(&recieveSet); + FD_SET(acceptSocket, &recieveSet); + + static timeval lim = { 0, 0 }; + +#if defined(USE_BERKELEY_SOCKETS) + if (::select(acceptSocket + 1, &recieveSet, nullptr, nullptr, &lim) == 1) +#elif defined(USE_WINDOWS_SOCKETS) + if (::select(0, &recieveSet, nullptr, nullptr, &lim) == 1) +#else +#error Platform not supported +#endif + { + return ::recv(acceptSocket, buf, len, 0); + } + + return 0; + } +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Server::Server(short port) : socket(Memory::New<Socket>()) +{ + if (!socket->Bind(port, 4)) + { + OPTICK_FAILED("Failed to bind a socket! Most probably the port is blocked by anti-virus! Change the port and verify that your game has enough permissions to communicate over the TCP\IP."); + } + else + { + socket->Listen(); + } +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Server::Update() +{ + std::lock_guard<std::recursive_mutex> lock(socketLock); + + if (!InitConnection()) + return; + + int length = -1; + while ( (length = socket->Receive( buffer, BIFFER_SIZE ) ) > 0 ) + { + networkStream.Append(buffer, length); + } + + while (IMessage *message = IMessage::Create(networkStream)) + { + message->Apply(); + Memory::Delete(message); + } +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Server::Send(DataResponse::Type type, OutputDataStream& stream) +{ + std::lock_guard<std::recursive_mutex> lock(socketLock); + + string data = stream.GetData(); + + DataResponse response(type, (uint32)data.size()); + socket->Send((char*)&response, sizeof(response)); + socket->Send(data.c_str(), data.size()); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Server::InitConnection() +{ + return socket->Accept(); +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +string Server::GetHostName() const +{ + const uint32 HOST_NAME_LENGTH = 256; + char hostname[HOST_NAME_LENGTH] = { 0 }; + +#if defined(USE_BERKELEY_SOCKETS) +#if defined(OPTICK_LINUX) || defined(OPTICK_OSX) + gethostname(hostname, HOST_NAME_LENGTH); +#endif +#elif defined(OPTICK_PC) + DWORD length = HOST_NAME_LENGTH; + GetComputerNameA(hostname, &length); +#endif + + return hostname; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Server::~Server() +{ + if (socket) + { + Memory::Delete(socket); + socket = nullptr; + } +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Server & Server::Get() +{ + static Server instance(DEFAULT_PORT); + return instance; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +} + +#endif //USE_OPTICK
\ No newline at end of file diff --git a/external/optick/optick_server.h b/external/optick/optick_server.h new file mode 100644 index 0000000..b44153e --- /dev/null +++ b/external/optick/optick_server.h @@ -0,0 +1,42 @@ +#pragma once +#include "optick.config.h" + +#if USE_OPTICK +#include "optick_message.h" + +#include <mutex> +#include <thread> + +namespace Optick +{ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class Socket; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +class Server +{ + InputDataStream networkStream; + + static const int BIFFER_SIZE = 1024; + char buffer[BIFFER_SIZE]; + + Socket* socket; + + std::recursive_mutex socketLock; + + Server( short port ); + ~Server(); + + bool InitConnection(); + +public: + void Send(DataResponse::Type type, OutputDataStream& stream = OutputDataStream::Empty); + void Update(); + + string GetHostName() const; + + static Server &Get(); +}; +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} + +#endif //USE_OPTICK
\ No newline at end of file |