DSP: Add support for PCAP logging of CPU<->DSP communications

This commit is contained in:
Pierre Bourdon 2014-04-26 00:51:38 +02:00
parent 7889d90633
commit add090bde6
11 changed files with 224 additions and 13 deletions

View File

@ -29,6 +29,7 @@ set(SRCS ActionReplay.cpp
DSP/DSPAssembler.cpp DSP/DSPAssembler.cpp
DSP/DSPDisassembler.cpp DSP/DSPDisassembler.cpp
DSP/DSPAccelerator.cpp DSP/DSPAccelerator.cpp
DSP/DSPCaptureLogger.cpp
DSP/DSPIntCCUtil.cpp DSP/DSPIntCCUtil.cpp
DSP/DSPIntExtOps.cpp DSP/DSPIntExtOps.cpp
DSP/DSPHWInterface.cpp DSP/DSPHWInterface.cpp

View File

@ -326,6 +326,7 @@ void SConfig::SaveDSPSettings(IniFile& ini)
dsp->Set("DumpAudio", m_DumpAudio); dsp->Set("DumpAudio", m_DumpAudio);
dsp->Set("Backend", sBackend); dsp->Set("Backend", sBackend);
dsp->Set("Volume", m_Volume); dsp->Set("Volume", m_Volume);
dsp->Set("CaptureLog", m_DSPCaptureLog);
} }
void SConfig::SaveFifoPlayerSettings(IniFile& ini) void SConfig::SaveFifoPlayerSettings(IniFile& ini)
@ -552,6 +553,7 @@ void SConfig::LoadDSPSettings(IniFile& ini)
dsp->Get("Backend", &sBackend, BACKEND_NULLSOUND); dsp->Get("Backend", &sBackend, BACKEND_NULLSOUND);
#endif #endif
dsp->Get("Volume", &m_Volume, 100); dsp->Get("Volume", &m_Volume, 100);
dsp->Get("CaptureLog", &m_DSPCaptureLog, false);
} }
void SConfig::LoadFifoPlayerSettings(IniFile& ini) void SConfig::LoadFifoPlayerSettings(IniFile& ini)

View File

@ -92,6 +92,7 @@ struct SConfig : NonCopyable
// DSP settings // DSP settings
bool m_DSPEnableJIT; bool m_DSPEnableJIT;
bool m_DSPCaptureLog;
bool m_DumpAudio; bool m_DumpAudio;
int m_Volume; int m_Volume;
std::string sBackend; std::string sBackend;

View File

@ -64,6 +64,7 @@
<ClCompile Include="DSP\DSPDisassembler.cpp" /> <ClCompile Include="DSP\DSPDisassembler.cpp" />
<ClCompile Include="DSP\DSPAccelerator.cpp" /> <ClCompile Include="DSP\DSPAccelerator.cpp" />
<ClCompile Include="DSP\DSPAnalyzer.cpp" /> <ClCompile Include="DSP\DSPAnalyzer.cpp" />
<ClCompile Include="DSP\DSPCaptureLogger.cpp" />
<ClCompile Include="DSP\DSPCodeUtil.cpp" /> <ClCompile Include="DSP\DSPCodeUtil.cpp" />
<ClCompile Include="DSP\DSPCore.cpp" /> <ClCompile Include="DSP\DSPCore.cpp" />
<ClCompile Include="DSP\DSPEmitter.cpp" /> <ClCompile Include="DSP\DSPEmitter.cpp" />
@ -268,6 +269,7 @@
<ClInclude Include="DSP\DSPAccelerator.h" /> <ClInclude Include="DSP\DSPAccelerator.h" />
<ClInclude Include="DSP\DSPAnalyzer.h" /> <ClInclude Include="DSP\DSPAnalyzer.h" />
<ClInclude Include="DSP\DSPBreakpoints.h" /> <ClInclude Include="DSP\DSPBreakpoints.h" />
<ClInclude Include="DSP\DSPCaptureLogger.h" />
<ClInclude Include="DSP\DSPCodeUtil.h" /> <ClInclude Include="DSP\DSPCodeUtil.h" />
<ClInclude Include="DSP\DSPCommon.h" /> <ClInclude Include="DSP\DSPCommon.h" />
<ClInclude Include="DSP\DSPCore.h" /> <ClInclude Include="DSP\DSPCore.h" />

View File

@ -515,6 +515,9 @@
<ClCompile Include="DSP\DSPAnalyzer.cpp"> <ClCompile Include="DSP\DSPAnalyzer.cpp">
<Filter>DSPCore</Filter> <Filter>DSPCore</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="DSP\DSPCaptureLogger.cpp">
<Filter>DSPCore</Filter>
</ClCompile>
<ClCompile Include="DSP\DSPCodeUtil.cpp"> <ClCompile Include="DSP\DSPCodeUtil.cpp">
<Filter>DSPCore</Filter> <Filter>DSPCore</Filter>
</ClCompile> </ClCompile>
@ -1041,6 +1044,9 @@
<ClInclude Include="DSP\DSPBreakpoints.h"> <ClInclude Include="DSP\DSPBreakpoints.h">
<Filter>DSPCore</Filter> <Filter>DSPCore</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="DSP\DSPCaptureLogger.h">
<Filter>DSPCore</Filter>
</ClInclude>
<ClInclude Include="DSP\DSPCodeUtil.h"> <ClInclude Include="DSP\DSPCodeUtil.h">
<Filter>DSPCore</Filter> <Filter>DSPCore</Filter>
</ClInclude> </ClInclude>

View File

@ -0,0 +1,79 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#include <memory>
#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>&& 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<DMAPacket*>(&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);
}

View File

@ -0,0 +1,76 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#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>&& 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<PCAP> m_pcap;
};

View File

@ -42,6 +42,7 @@ DSPCoreState core_state = DSPCORE_STOP;
u16 cyclesLeft = 0; u16 cyclesLeft = 0;
bool init_hax = false; bool init_hax = false;
DSPEmitter *dspjit = nullptr; DSPEmitter *dspjit = nullptr;
std::unique_ptr<DSPCaptureLogger> g_dsp_cap;
Common::Event step_event; Common::Event step_event;
// Returns false if the hash fails and the user hits "Yes" // 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) if (opts.core_type == DSPInitOptions::CORE_JIT)
dspjit = new DSPEmitter(); dspjit = new DSPEmitter();
g_dsp_cap.reset(opts.capture_logger);
core_state = DSPCORE_RUNNING; core_state = DSPCORE_RUNNING;
return true; return true;
} }
@ -187,6 +190,8 @@ void DSPCore_Shutdown()
dspjit = nullptr; dspjit = nullptr;
} }
DSPCore_FreeMemoryPages(); DSPCore_FreeMemoryPages();
g_dsp_cap.reset();
} }
void DSPCore_Reset() void DSPCore_Reset()

View File

@ -26,11 +26,13 @@
#pragma once #pragma once
#include <array> #include <array>
#include <memory>
#include <string> #include <string>
#include "Common/Thread.h" #include "Common/Thread.h"
#include "Core/DSP/DSPBreakpoints.h" #include "Core/DSP/DSPBreakpoints.h"
#include "Core/DSP/DSPCaptureLogger.h"
#include "Core/DSP/DSPEmitter.h" #include "Core/DSP/DSPEmitter.h"
#define DSP_IRAM_BYTE_SIZE 0x2000 #define DSP_IRAM_BYTE_SIZE 0x2000
@ -270,6 +272,7 @@ extern DSPBreakpoints dsp_breakpoints;
extern DSPEmitter *dspjit; extern DSPEmitter *dspjit;
extern u16 cyclesLeft; extern u16 cyclesLeft;
extern bool init_hax; extern bool init_hax;
extern std::unique_ptr<DSPCaptureLogger> g_dsp_cap;
struct DSPInitOptions struct DSPInitOptions
{ {
@ -280,6 +283,7 @@ struct DSPInitOptions
std::array<u16, DSP_COEF_SIZE> coef_contents; std::array<u16, DSP_COEF_SIZE> coef_contents;
// Core used to emulate the DSP. // Core used to emulate the DSP.
// Default: CORE_JIT.
enum CoreType enum CoreType
{ {
CORE_INTERPRETER, CORE_INTERPRETER,
@ -287,8 +291,13 @@ struct DSPInitOptions
}; };
CoreType core_type; CoreType core_type;
// Provide default option values. // Optional capture logger used to log internal DSP data transfers.
DSPInitOptions() : core_type(CORE_JIT) // Default: dummy implementation, does nothing.
DSPCaptureLogger* capture_logger;
DSPInitOptions()
: core_type(CORE_JIT),
capture_logger(new DefaultDSPCaptureLogger())
{ {
} }
}; };

View File

@ -115,6 +115,8 @@ u16 gdsp_mbox_read_l(u8 mbx)
void gdsp_ifx_write(u32 addr, u32 val) void gdsp_ifx_write(u32 addr, u32 val)
{ {
g_dsp_cap->LogIFXWrite(addr, val);
switch (addr & 0xff) switch (addr & 0xff)
{ {
case DSP_DIRQ: 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) 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); 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); 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); 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); 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 #if _M_SSE >= 0x301
@ -261,7 +274,7 @@ static const __m128i s_mask = _mm_set_epi32(0x0E0F0C0DL, 0x0A0B0809L, 0x06070405
#endif #endif
// TODO: These should eat clock cycles. // 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); 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); 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); 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); 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() static void gdsp_do_dma()
@ -323,22 +340,28 @@ static void gdsp_do_dma()
#if defined(_DEBUG) || defined(DEBUGFAST) #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); DEBUG_LOG(DSPLLE, "DMA pc: %04x, Control: %04x, Address: %08x, DSP Address: %04x, Size: %04x", g_dsp.pc, ctl, addr, dsp_addr, len);
#endif #endif
const u8* copied_data_ptr;
switch (ctl & 0x3) switch (ctl & 0x3)
{ {
case (DSP_CR_DMEM | DSP_CR_TO_CPU): 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; break;
case (DSP_CR_DMEM | DSP_CR_FROM_CPU): 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; break;
case (DSP_CR_IMEM | DSP_CR_TO_CPU): 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; break;
case (DSP_CR_IMEM | DSP_CR_FROM_CPU): 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; break;
} }
if (copied_data_ptr)
g_dsp_cap->LogDMA(ctl, addr, dsp_addr, len, copied_data_ptr);
} }

View File

@ -16,6 +16,7 @@
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/Host.h" #include "Core/Host.h"
#include "Core/DSP/DSPCaptureLogger.h"
#include "Core/DSP/DSPCore.h" #include "Core/DSP/DSPCore.h"
#include "Core/DSP/DSPDisassembler.h" #include "Core/DSP/DSPDisassembler.h"
#include "Core/DSP/DSPHost.h" #include "Core/DSP/DSPHost.h"
@ -146,6 +147,12 @@ static bool FillDSPInitOptions(DSPInitOptions* opts)
opts->core_type = SConfig::GetInstance().m_DSPEnableJIT ? opts->core_type = SConfig::GetInstance().m_DSPEnableJIT ?
DSPInitOptions::CORE_JIT : DSPInitOptions::CORE_INTERPRETER; 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; return true;
} }