From add090bde6273f8f6c76a3d55ca56acb04d2b464 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Sat, 26 Apr 2014 00:51:38 +0200 Subject: [PATCH] DSP: Add support for PCAP logging of CPU<->DSP communications --- Source/Core/Core/CMakeLists.txt | 1 + Source/Core/Core/ConfigManager.cpp | 2 + Source/Core/Core/ConfigManager.h | 1 + Source/Core/Core/Core.vcxproj | 4 +- Source/Core/Core/Core.vcxproj.filters | 8 ++- Source/Core/Core/DSP/DSPCaptureLogger.cpp | 79 +++++++++++++++++++++++ Source/Core/Core/DSP/DSPCaptureLogger.h | 76 ++++++++++++++++++++++ Source/Core/Core/DSP/DSPCore.cpp | 5 ++ Source/Core/Core/DSP/DSPCore.h | 13 +++- Source/Core/Core/DSP/DSPHWInterface.cpp | 41 +++++++++--- Source/Core/Core/HW/DSPLLE/DSPLLE.cpp | 7 ++ 11 files changed, 224 insertions(+), 13 deletions(-) create mode 100644 Source/Core/Core/DSP/DSPCaptureLogger.cpp create mode 100644 Source/Core/Core/DSP/DSPCaptureLogger.h diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 62e7de4420..2a4915db45 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -29,6 +29,7 @@ set(SRCS ActionReplay.cpp DSP/DSPAssembler.cpp DSP/DSPDisassembler.cpp DSP/DSPAccelerator.cpp + DSP/DSPCaptureLogger.cpp DSP/DSPIntCCUtil.cpp DSP/DSPIntExtOps.cpp DSP/DSPHWInterface.cpp diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 5f9c6ce479..727b1b1f91 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -326,6 +326,7 @@ void SConfig::SaveDSPSettings(IniFile& ini) dsp->Set("DumpAudio", m_DumpAudio); dsp->Set("Backend", sBackend); dsp->Set("Volume", m_Volume); + dsp->Set("CaptureLog", m_DSPCaptureLog); } void SConfig::SaveFifoPlayerSettings(IniFile& ini) @@ -552,6 +553,7 @@ void SConfig::LoadDSPSettings(IniFile& ini) dsp->Get("Backend", &sBackend, BACKEND_NULLSOUND); #endif dsp->Get("Volume", &m_Volume, 100); + dsp->Get("CaptureLog", &m_DSPCaptureLog, false); } void SConfig::LoadFifoPlayerSettings(IniFile& ini) diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 0bda4cc132..45139caa71 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -92,6 +92,7 @@ struct SConfig : NonCopyable // DSP settings bool m_DSPEnableJIT; + bool m_DSPCaptureLog; bool m_DumpAudio; int m_Volume; std::string sBackend; diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 784e7503bd..0ea27a1b7e 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -64,6 +64,7 @@ + @@ -268,6 +269,7 @@ + @@ -468,4 +470,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index f25fbe6248..469b7783be 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -515,6 +515,9 @@ DSPCore + + DSPCore + DSPCore @@ -1041,6 +1044,9 @@ DSPCore + + DSPCore + DSPCore @@ -1205,4 +1211,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/DSP/DSPCaptureLogger.cpp b/Source/Core/Core/DSP/DSPCaptureLogger.cpp new file mode 100644 index 0000000000..b440d0c84d --- /dev/null +++ b/Source/Core/Core/DSP/DSPCaptureLogger.cpp @@ -0,0 +1,79 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include + +#include "Common/CommonTypes.h" +#include "Common/FileUtil.h" +#include "Common/PcapFile.h" + +#include "Core/DSP/DSPCaptureLogger.h" + +// Definition of the packet structures stored in PCAP capture files. + +const u8 IFX_ACCESS_PACKET_MAGIC = 0; +const u8 DMA_PACKET_MAGIC = 1; + +#pragma pack(push, 1) +struct IFXAccessPacket +{ + u8 magic; // IFX_ACCESS_PACKET_MAGIC + u8 is_read; // 0 for writes, 1 for reads. + u16 address; + u16 value; +}; + +// Followed by the bytes of the DMA. +struct DMAPacket +{ + u8 magic; // DMA_PACKET_MAGIC + u16 dma_control; // Value of the DMA control register. + u32 gc_address; // Address in the GC RAM. + u16 dsp_address; // Address in the DSP RAM. + u16 length; // Length in bytes. +}; +#pragma pack(pop) + +PCAPDSPCaptureLogger::PCAPDSPCaptureLogger(const std::string& pcap_filename) + : m_pcap(new PCAP(new File::IOFile(pcap_filename, "wb"))) +{ +} + +PCAPDSPCaptureLogger::PCAPDSPCaptureLogger(PCAP* pcap) : m_pcap(pcap) +{ +} + +PCAPDSPCaptureLogger::PCAPDSPCaptureLogger(std::unique_ptr&& pcap) + : m_pcap(std::move(pcap)) +{ +} + +void PCAPDSPCaptureLogger::LogIFXAccess(bool read, u16 address, u16 value) +{ + IFXAccessPacket pkt; + pkt.magic = IFX_ACCESS_PACKET_MAGIC; + pkt.is_read = !!read; // Make sure we actually have 0/1. + pkt.address = address; + pkt.value = value; + + m_pcap->AddPacket(pkt); +} + +void PCAPDSPCaptureLogger::LogDMA(u16 control, u32 gc_address, u16 dsp_address, + u16 length, const u8* data) +{ + // The length of a DMA cannot be above 64K, so we use a static buffer for + // the construction of the packet. + static u8 buffer[0x10000]; + + DMAPacket* pkt = reinterpret_cast(&buffer[0]); + pkt->magic = DMA_PACKET_MAGIC; + pkt->dma_control = control; + pkt->gc_address = gc_address; + pkt->dsp_address = dsp_address; + pkt->length = length; + memcpy(&buffer[sizeof (DMAPacket)], data, length); + + m_pcap->AddPacket(buffer, sizeof (DMAPacket) + length); +} diff --git a/Source/Core/Core/DSP/DSPCaptureLogger.h b/Source/Core/Core/DSP/DSPCaptureLogger.h new file mode 100644 index 0000000000..16406d4775 --- /dev/null +++ b/Source/Core/Core/DSP/DSPCaptureLogger.h @@ -0,0 +1,76 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/Common.h" + +class PCAP; + +// An interface used to capture and log structured data about internal DSP +// data transfers. +// +// Note: these calls are done within the DSP emulator in critical paths and at +// a high frequency. Implementations must try and avoid blocking for too long. +class DSPCaptureLogger +{ +public: + // Accesses (reads or writes) to memory mapped registers (external + // interface, also known as IFX). These are always 16 bits accesses. + virtual void LogIFXRead(u16 address, u16 read_value) = 0; + virtual void LogIFXWrite(u16 address, u16 written_value) = 0; + + // DMAs to/from main memory from/to DSP memory. We let the interpretation + // of the "control" field to the layer that analyze the logs and do not + // perform our own interpretation of it. Thus there is only one call which + // is used for DRAM/IRAM in any direction (to/from DSP). + // + // Length is expressed in bytes, not DSP words. + virtual void LogDMA(u16 control, u32 gc_address, u16 dsp_address, + u16 length, const u8* data) = 0; +}; + +// A dummy implementation of a capture logger that does nothing. This is the +// default implementation used by the DSP emulator. +// +// Can also be inherited from if you want to only override part of the methods. +class DefaultDSPCaptureLogger : public DSPCaptureLogger +{ +public: + virtual void LogIFXRead(u16 address, u16 read_value) override {} + virtual void LogIFXWrite(u16 address, u16 written_value) override {} + virtual void LogDMA(u16 control, u32 gc_address, u16 dsp_address, + u16 length, const u8* data) override {} +}; + +// A capture logger implementation that logs to PCAP files in a custom +// packet-based format. +class PCAPDSPCaptureLogger final : public DSPCaptureLogger, NonCopyable +{ +public: + // Automatically creates a writeable file (truncate existing file). + PCAPDSPCaptureLogger(const std::string& pcap_filename); + // Takes ownership of pcap. + PCAPDSPCaptureLogger(PCAP* pcap); + PCAPDSPCaptureLogger(std::unique_ptr&& pcap); + + virtual void LogIFXRead(u16 address, u16 read_value) override + { + LogIFXAccess(true, address, read_value); + } + virtual void LogIFXWrite(u16 address, u16 written_value) override + { + LogIFXAccess(false, address, written_value); + } + virtual void LogDMA(u16 control, u32 gc_address, u16 dsp_address, + u16 length, const u8* data) override; + +private: + void LogIFXAccess(bool read, u16 address, u16 value); + + std::unique_ptr m_pcap; +}; diff --git a/Source/Core/Core/DSP/DSPCore.cpp b/Source/Core/Core/DSP/DSPCore.cpp index 84620d9ba0..dfa483ce47 100644 --- a/Source/Core/Core/DSP/DSPCore.cpp +++ b/Source/Core/Core/DSP/DSPCore.cpp @@ -42,6 +42,7 @@ DSPCoreState core_state = DSPCORE_STOP; u16 cyclesLeft = 0; bool init_hax = false; DSPEmitter *dspjit = nullptr; +std::unique_ptr g_dsp_cap; Common::Event step_event; // Returns false if the hash fails and the user hits "Yes" @@ -171,6 +172,8 @@ bool DSPCore_Init(const DSPInitOptions& opts) if (opts.core_type == DSPInitOptions::CORE_JIT) dspjit = new DSPEmitter(); + g_dsp_cap.reset(opts.capture_logger); + core_state = DSPCORE_RUNNING; return true; } @@ -187,6 +190,8 @@ void DSPCore_Shutdown() dspjit = nullptr; } DSPCore_FreeMemoryPages(); + + g_dsp_cap.reset(); } void DSPCore_Reset() diff --git a/Source/Core/Core/DSP/DSPCore.h b/Source/Core/Core/DSP/DSPCore.h index e7ae1ca89d..eaffcc551f 100644 --- a/Source/Core/Core/DSP/DSPCore.h +++ b/Source/Core/Core/DSP/DSPCore.h @@ -26,11 +26,13 @@ #pragma once #include +#include #include #include "Common/Thread.h" #include "Core/DSP/DSPBreakpoints.h" +#include "Core/DSP/DSPCaptureLogger.h" #include "Core/DSP/DSPEmitter.h" #define DSP_IRAM_BYTE_SIZE 0x2000 @@ -270,6 +272,7 @@ extern DSPBreakpoints dsp_breakpoints; extern DSPEmitter *dspjit; extern u16 cyclesLeft; extern bool init_hax; +extern std::unique_ptr g_dsp_cap; struct DSPInitOptions { @@ -280,6 +283,7 @@ struct DSPInitOptions std::array coef_contents; // Core used to emulate the DSP. + // Default: CORE_JIT. enum CoreType { CORE_INTERPRETER, @@ -287,8 +291,13 @@ struct DSPInitOptions }; CoreType core_type; - // Provide default option values. - DSPInitOptions() : core_type(CORE_JIT) + // Optional capture logger used to log internal DSP data transfers. + // Default: dummy implementation, does nothing. + DSPCaptureLogger* capture_logger; + + DSPInitOptions() + : core_type(CORE_JIT), + capture_logger(new DefaultDSPCaptureLogger()) { } }; diff --git a/Source/Core/Core/DSP/DSPHWInterface.cpp b/Source/Core/Core/DSP/DSPHWInterface.cpp index 6125c3a414..163c776a61 100644 --- a/Source/Core/Core/DSP/DSPHWInterface.cpp +++ b/Source/Core/Core/DSP/DSPHWInterface.cpp @@ -115,6 +115,8 @@ u16 gdsp_mbox_read_l(u8 mbx) void gdsp_ifx_write(u32 addr, u32 val) { + g_dsp_cap->LogIFXWrite(addr, val); + switch (addr & 0xff) { case DSP_DIRQ: @@ -190,7 +192,7 @@ void gdsp_ifx_write(u32 addr, u32 val) } } -u16 gdsp_ifx_read(u16 addr) +static u16 _gdsp_ifx_read(u16 addr) { switch (addr & 0xff) { @@ -235,7 +237,14 @@ u16 gdsp_ifx_read(u16 addr) } } -static void gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size) +u16 gdsp_ifx_read(u16 addr) +{ + u16 retval = _gdsp_ifx_read(addr); + g_dsp_cap->LogIFXRead(addr, retval); + return retval; +} + +static const u8* gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size) { UnWriteProtectMemory(g_dsp.iram, DSP_IRAM_BYTE_SIZE, false); @@ -249,11 +258,15 @@ static void gdsp_idma_in(u16 dsp_addr, u32 addr, u32 size) DSPHost::CodeLoaded((const u8*)g_dsp.iram + dsp_addr, size); NOTICE_LOG(DSPLLE, "*** Copy new UCode from 0x%08x to 0x%04x (crc: %8x)", addr, dsp_addr, g_dsp.iram_crc); + + return dst + dsp_addr; } -static void gdsp_idma_out(u16 dsp_addr, u32 addr, u32 size) +static const u8* gdsp_idma_out(u16 dsp_addr, u32 addr, u32 size) { ERROR_LOG(DSPLLE, "*** idma_out IRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size); + + return nullptr; } #if _M_SSE >= 0x301 @@ -261,7 +274,7 @@ static const __m128i s_mask = _mm_set_epi32(0x0E0F0C0DL, 0x0A0B0809L, 0x06070405 #endif // TODO: These should eat clock cycles. -static void gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size) +static const u8* gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size) { u8* dst = ((u8*)g_dsp.dram); @@ -282,9 +295,11 @@ static void gdsp_ddma_in(u16 dsp_addr, u32 addr, u32 size) } } INFO_LOG(DSPLLE, "*** ddma_in RAM (0x%08x) -> DRAM_DSP (0x%04x) : size (0x%08x)", addr, dsp_addr / 2, size); + + return dst + dsp_addr; } -static void gdsp_ddma_out(u16 dsp_addr, u32 addr, u32 size) +static const u8* gdsp_ddma_out(u16 dsp_addr, u32 addr, u32 size) { const u8* src = ((const u8*)g_dsp.dram); @@ -306,6 +321,8 @@ static void gdsp_ddma_out(u16 dsp_addr, u32 addr, u32 size) } INFO_LOG(DSPLLE, "*** ddma_out DRAM_DSP (0x%04x) -> RAM (0x%08x) : size (0x%08x)", dsp_addr / 2, addr, size); + + return src + dsp_addr; } static void gdsp_do_dma() @@ -323,22 +340,28 @@ static void gdsp_do_dma() #if defined(_DEBUG) || defined(DEBUGFAST) DEBUG_LOG(DSPLLE, "DMA pc: %04x, Control: %04x, Address: %08x, DSP Address: %04x, Size: %04x", g_dsp.pc, ctl, addr, dsp_addr, len); #endif + + const u8* copied_data_ptr; switch (ctl & 0x3) { case (DSP_CR_DMEM | DSP_CR_TO_CPU): - gdsp_ddma_out(dsp_addr, addr, len); + copied_data_ptr = gdsp_ddma_out(dsp_addr, addr, len); break; case (DSP_CR_DMEM | DSP_CR_FROM_CPU): - gdsp_ddma_in(dsp_addr, addr, len); + copied_data_ptr = gdsp_ddma_in(dsp_addr, addr, len); break; case (DSP_CR_IMEM | DSP_CR_TO_CPU): - gdsp_idma_out(dsp_addr, addr, len); + copied_data_ptr = gdsp_idma_out(dsp_addr, addr, len); break; case (DSP_CR_IMEM | DSP_CR_FROM_CPU): - gdsp_idma_in(dsp_addr, addr, len); + copied_data_ptr = gdsp_idma_in(dsp_addr, addr, len); break; } + + if (copied_data_ptr) + g_dsp_cap->LogDMA(ctl, addr, dsp_addr, len, copied_data_ptr); + } diff --git a/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp b/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp index 607958ecfa..516117a838 100644 --- a/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp +++ b/Source/Core/Core/HW/DSPLLE/DSPLLE.cpp @@ -16,6 +16,7 @@ #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/Host.h" +#include "Core/DSP/DSPCaptureLogger.h" #include "Core/DSP/DSPCore.h" #include "Core/DSP/DSPDisassembler.h" #include "Core/DSP/DSPHost.h" @@ -146,6 +147,12 @@ static bool FillDSPInitOptions(DSPInitOptions* opts) opts->core_type = SConfig::GetInstance().m_DSPEnableJIT ? DSPInitOptions::CORE_JIT : DSPInitOptions::CORE_INTERPRETER; + if (SConfig::GetInstance().m_DSPCaptureLog) + { + const std::string pcap_path = File::GetUserPath(D_DUMPDSP_IDX) + "dsp.pcap"; + opts->capture_logger = new PCAPDSPCaptureLogger(pcap_path); + } + return true; }