From f4d950f4e243c34136ca1fda7ae46995c6272fc5 Mon Sep 17 00:00:00 2001 From: Vlad Firoiu Date: Fri, 3 May 2019 20:46:37 +0100 Subject: [PATCH] Revert "Core: Remove MemoryWatcher" This reverts commit 0c02e77eee0a12b178b11676ca548aeb1cbb7116. --- CMakeLists.txt | 2 + Source/Core/Common/CommonPaths.h | 5 ++ Source/Core/Common/FileUtil.cpp | 6 ++ Source/Core/Common/FileUtil.h | 3 + Source/Core/Core/CMakeLists.txt | 4 + Source/Core/Core/Core.cpp | 12 +++ Source/Core/Core/MemoryWatcher.cpp | 130 +++++++++++++++++++++++++++++ Source/Core/Core/MemoryWatcher.h | 47 +++++++++++ 8 files changed, 209 insertions(+) create mode 100644 Source/Core/Core/MemoryWatcher.cpp create mode 100644 Source/Core/Core/MemoryWatcher.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3577234bb5..315f75591c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -481,6 +481,8 @@ endif() if(UNIX) message(STATUS "Using named pipes as controller inputs") add_definitions(-DUSE_PIPES=1) + message(STATUS "Watching game memory for changes") + add_definitions(-DUSE_MEMORYWATCHER=1) endif() if(ENABLE_ANALYTICS) diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 97213f6448..7137dab3f6 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -63,6 +63,7 @@ #define STYLES_DIR "Styles" #define ANAGLYPH_DIR "Anaglyph" #define PIPES_DIR "Pipes" +#define MEMORYWATCHER_DIR "MemoryWatcher" #define WFSROOT_DIR "WFS" #define BACKUP_DIR "Backup" #define RESOURCEPACK_DIR "ResourcePacks" @@ -95,6 +96,10 @@ #define ARAM_DUMP "aram.raw" #define FAKEVMEM_DUMP "fakevmem.raw" +// Files in the directory returned by GetUserPath(D_MEMORYWATCHER_IDX) +#define MEMORYWATCHER_LOCATIONS "Locations.txt" +#define MEMORYWATCHER_SOCKET "MemoryWatcher" + // Sys files #define TOTALDB "totaldb.dsy" diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index 72d441906e..7030c9dc25 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -801,6 +801,12 @@ static void RebuildUserDirectories(unsigned int dir_index) s_user_paths[F_GCSRAM_IDX] = s_user_paths[D_GCUSER_IDX] + GC_SRAM; s_user_paths[F_WIISDCARD_IDX] = s_user_paths[D_WIIROOT_IDX] + DIR_SEP WII_SDCARD; + s_user_paths[D_MEMORYWATCHER_IDX] = s_user_paths[D_USER_IDX] + MEMORYWATCHER_DIR DIR_SEP; + s_user_paths[F_MEMORYWATCHERLOCATIONS_IDX] = + s_user_paths[D_MEMORYWATCHER_IDX] + MEMORYWATCHER_LOCATIONS; + s_user_paths[F_MEMORYWATCHERSOCKET_IDX] = + s_user_paths[D_MEMORYWATCHER_IDX] + MEMORYWATCHER_SOCKET; + // The shader cache has moved to the cache directory, so remove the old one. // TODO: remove that someday. File::DeleteDirRecursively(s_user_paths[D_USER_IDX] + SHADERCACHE_LEGACY_DIR DIR_SEP); diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index dadee2ae23..71e9709da3 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -48,6 +48,7 @@ enum D_THEMES_IDX, D_STYLES_IDX, D_PIPES_IDX, + D_MEMORYWATCHER_IDX, D_WFSROOT_IDX, D_BACKUP_IDX, D_RESOURCEPACK_IDX, @@ -64,6 +65,8 @@ enum F_ARAMDUMP_IDX, F_FAKEVMEMDUMP_IDX, F_GCSRAM_IDX, + F_MEMORYWATCHERLOCATIONS_IDX, + F_MEMORYWATCHERSOCKET_IDX, F_WIISDCARD_IDX, NUM_PATH_INDICES }; diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 3ca82051a7..df3fd09162 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -382,3 +382,7 @@ endif() if(GDBSTUB) target_sources(core PRIVATE PowerPC/GDBStub.cpp) endif() + +if(UNIX) + target_sources(core PRIVATE MemoryWatcher.cpp) +endif() diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 9f2f6669f5..49348fd078 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -64,6 +64,10 @@ #include "Core/PowerPC/GDBStub.h" #endif +#ifdef USE_MEMORYWATCHER +#include "Core/MemoryWatcher.h" +#endif + #include "InputCommon/ControllerInterface/ControllerInterface.h" #include "InputCommon/GCAdapter.h" @@ -266,6 +270,10 @@ void Stop() // - Hammertime! } ResetRumble(); + +#ifdef USE_MEMORYWATCHER + MemoryWatcher::Shutdown(); +#endif } void DeclareAsCPUThread() @@ -308,6 +316,10 @@ static void CpuThread(const std::optional& savestate_path, bool del if (_CoreParameter.bFastmem) EMM::InstallExceptionHandler(); // Let's run under memory watch +#ifdef USE_MEMORYWATCHER + MemoryWatcher::Init(); +#endif + if (savestate_path) { ::State::LoadAs(*savestate_path); diff --git a/Source/Core/Core/MemoryWatcher.cpp b/Source/Core/Core/MemoryWatcher.cpp new file mode 100644 index 0000000000..e4bd3d0318 --- /dev/null +++ b/Source/Core/Core/MemoryWatcher.cpp @@ -0,0 +1,130 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include + +#include "Common/FileUtil.h" +#include "Core/CoreTiming.h" +#include "Core/HW/Memmap.h" +#include "Core/HW/SystemTimers.h" +#include "Core/MemoryWatcher.h" + +static std::unique_ptr s_memory_watcher; +static CoreTiming::EventType* s_event; +static const int MW_RATE = 600; // Steps per second + +static void MWCallback(u64 userdata, s64 cyclesLate) +{ + s_memory_watcher->Step(); + CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond() / MW_RATE - cyclesLate, s_event); +} + +void MemoryWatcher::Init() +{ + s_memory_watcher = std::make_unique(); + s_event = CoreTiming::RegisterEvent("MemoryWatcher", MWCallback); + CoreTiming::ScheduleEvent(0, s_event); +} + +void MemoryWatcher::Shutdown() +{ + CoreTiming::RemoveEvent(s_event); + s_memory_watcher.reset(); +} + +MemoryWatcher::MemoryWatcher() +{ + m_running = false; + if (!LoadAddresses(File::GetUserPath(F_MEMORYWATCHERLOCATIONS_IDX))) + return; + if (!OpenSocket(File::GetUserPath(F_MEMORYWATCHERSOCKET_IDX))) + return; + m_running = true; +} + +MemoryWatcher::~MemoryWatcher() +{ + if (!m_running) + return; + + m_running = false; + close(m_fd); +} + +bool MemoryWatcher::LoadAddresses(const std::string& path) +{ + std::ifstream locations; + File::OpenFStream(locations, path, std::ios_base::in); + if (!locations) + return false; + + std::string line; + while (std::getline(locations, line)) + ParseLine(line); + + return !m_values.empty(); +} + +void MemoryWatcher::ParseLine(const std::string& line) +{ + m_values[line] = 0; + m_addresses[line] = std::vector(); + + std::stringstream offsets(line); + offsets >> std::hex; + u32 offset; + while (offsets >> offset) + m_addresses[line].push_back(offset); +} + +bool MemoryWatcher::OpenSocket(const std::string& path) +{ + memset(&m_addr, 0, sizeof(m_addr)); + m_addr.sun_family = AF_UNIX; + strncpy(m_addr.sun_path, path.c_str(), sizeof(m_addr.sun_path) - 1); + + m_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + return m_fd >= 0; +} + +u32 MemoryWatcher::ChasePointer(const std::string& line) +{ + u32 value = 0; + for (u32 offset : m_addresses[line]) + value = Memory::Read_U32(value + offset); + return value; +} + +std::string MemoryWatcher::ComposeMessage(const std::string& line, u32 value) +{ + std::stringstream message_stream; + message_stream << line << '\n' << std::hex << value; + return message_stream.str(); +} + +void MemoryWatcher::Step() +{ + if (!m_running) + return; + + for (auto& entry : m_values) + { + std::string address = entry.first; + u32& current_value = entry.second; + + u32 new_value = ChasePointer(address); + if (new_value != current_value) + { + // Update the value + current_value = new_value; + std::string message = ComposeMessage(address, new_value); + sendto(m_fd, message.c_str(), message.size() + 1, 0, reinterpret_cast(&m_addr), + sizeof(m_addr)); + } + } +} diff --git a/Source/Core/Core/MemoryWatcher.h b/Source/Core/Core/MemoryWatcher.h new file mode 100644 index 0000000000..e2dc4790bc --- /dev/null +++ b/Source/Core/Core/MemoryWatcher.h @@ -0,0 +1,47 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include + +// MemoryWatcher reads a file containing in-game memory addresses and outputs +// changes to those memory addresses to a unix domain socket as the game runs. +// +// The input file is a newline-separated list of hex memory addresses, without +// the "0x". To follow pointers, separate addresses with a space. For example, +// "ABCD EF" will watch the address at (*0xABCD) + 0xEF. +// The output to the socket is two lines. The first is the address from the +// input file, and the second is the new value in hex. +class MemoryWatcher final +{ +public: + MemoryWatcher(); + ~MemoryWatcher(); + void Step(); + + static void Init(); + static void Shutdown(); + +private: + bool LoadAddresses(const std::string& path); + bool OpenSocket(const std::string& path); + + void ParseLine(const std::string& line); + u32 ChasePointer(const std::string& line); + std::string ComposeMessage(const std::string& line, u32 value); + + bool m_running; + + int m_fd; + sockaddr_un m_addr; + + // Address as stored in the file -> list of offsets to follow + std::map> m_addresses; + // Address as stored in the file -> current value + std::map m_values; +};