// Copyright 2026 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include "Common/CommonTypes.h" namespace Common::PerformanceTrace { // Opt-in diagnostic events, never guest state. Windows provider: // Dolphin.Performance / {54ee9231-28be-4ab0-981e-b26f979c56a4}, schema 1. // Set DOLPHIN_PERF_TRACE=1 before process creation and start ETW before launching. enum class Region : u32 { Slice, ARM, Jit, Compile, Devices, NAND, Journal, JournalBuild, JournalWrite, JournalFlush, NAT, DMARead, DMAWrite, AES, SHA, Count }; enum class Counter : u32 { JournalTransactions, JournalBytes, JournalErrors, NatRXFrames, NatRXBytes, NatTXFrames, NatTXBytes, NatPollErrors, UsbRXFrames, UsbRXBytes, UsbDrops, UsbDeliveredBytes, NANDReads, NANDPrograms, NANDErases, Count }; struct Timing { u64 calls = 0; u64 inclusive = 0; u64 exclusive = 0; }; // Clock-independent accounting, also exercised with deterministic timestamps in tests. class ScopeStack { public: bool Begin(Region region, u64 now); bool End(u64 now); void Reset(); size_t Depth() const { return m_depth; } u64 Errors() const { return m_errors; } const auto& Timings() const { return m_timings; } private: struct Entry { Region region; u64 start; u64 children; }; std::array m_stack{}; std::array(Region::Count)> m_timings{}; size_t m_depth = 0; u64 m_errors = 0; }; bool Requested(); bool Enabled(); u64 Now(); u64 Frequency(); u64 NewGeneration(); void Add(Counter counter, u64 value = 1); void Snapshot(u64 core_id, u64 instructions, u64 native_instructions, u64 slow_reads, u64 slow_writes, u64 compiles, u64 fallbacks, u32 pc, bool waiting); void CodeMap(const char* domain, const void* owner, const void* begin, const void* end, u32 guest_pc, u64 identity, const char* kind); void CodeReset(const char* domain, const void* owner, const void* begin, const void* end); void Frame(const char* kind, s64 guest_ticks = 0, u32 guest_frequency = 0); void Marker(const char* name); void Queue(const void* owner, u64 frames, u64 bytes, u64 oldest_qpc, u64 peak); // Only scopes inside a randomly selected root slice read the clock. No per-access timers. class SampleRoot { public: explicit SampleRoot(bool force_for_test = false); ~SampleRoot(); SampleRoot(const SampleRoot&) = delete; SampleRoot& operator=(const SampleRoot&) = delete; private: bool m_active = false; u64 m_start = 0; u32 m_denominator = 1024; }; class Scope { public: explicit Scope(Region region); ~Scope(); Scope(const Scope&) = delete; Scope& operator=(const Scope&) = delete; private: bool m_active = false; }; } // namespace Common::PerformanceTrace