mirror of
https://github.com/wiiu-env/WiiUPluginLoaderBackend.git
synced 2026-03-21 17:25:21 -05:00
Initial support for tracking plugin heap usage and detect double frees or memory leaks
This commit is contained in:
parent
aadba0ea1b
commit
90d6767d11
|
|
@ -12,6 +12,7 @@
|
|||
#include "plugin/PluginMetaInformationFactory.h"
|
||||
#include "plugin/RelocationData.h"
|
||||
#include "plugin/SectionInfo.h"
|
||||
#include "plugin/TrackingPluginHeapMemoryAllocator.h"
|
||||
#include "utils/ElfUtils.h"
|
||||
#include "utils/StringTools.h"
|
||||
#include "utils/logger.h"
|
||||
|
|
@ -67,23 +68,18 @@ PluginManagement::loadPlugins(const std::vector<PluginLoadWrapper> &pluginDataLi
|
|||
|
||||
bool PluginManagement::doRelocation(const std::vector<RelocationData> &relocData,
|
||||
std::span<relocation_trampoline_entry_t> trampData,
|
||||
std::map<std::string, OSDynLoad_Module> &usedRPls) {
|
||||
std::map<std::string, OSDynLoad_Module> &usedRPls,
|
||||
const IPluginHeapMemoryAllocator &memory_allocator) {
|
||||
for (auto const &cur : relocData) {
|
||||
uint32_t functionAddress = 0;
|
||||
auto &functionName = cur.getName();
|
||||
|
||||
if (functionName == "MEMAllocFromDefaultHeap") {
|
||||
OSDynLoad_Module rplHandle;
|
||||
OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle);
|
||||
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemory", (void **) &functionAddress);
|
||||
functionAddress = reinterpret_cast<uint32_t>(memory_allocator.GetAllocFunctionAddress());
|
||||
} else if (functionName == "MEMAllocFromDefaultHeapEx") {
|
||||
OSDynLoad_Module rplHandle;
|
||||
OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle);
|
||||
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemoryEx", (void **) &functionAddress);
|
||||
functionAddress = reinterpret_cast<uint32_t>(memory_allocator.GetAllocExFunctionAddress());
|
||||
} else if (functionName == "MEMFreeToDefaultHeap") {
|
||||
OSDynLoad_Module rplHandle;
|
||||
OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle);
|
||||
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMFreeToMappedMemory", (void **) &functionAddress);
|
||||
functionAddress = reinterpret_cast<uint32_t>(memory_allocator.GetFreeFunctionAddress());
|
||||
}
|
||||
|
||||
if (functionAddress == 0) {
|
||||
|
|
@ -160,7 +156,7 @@ bool PluginManagement::doRelocations(const std::vector<PluginContainer> &plugins
|
|||
DEBUG_FUNCTION_LINE_VERBOSE("Doing relocations for plugin: %s", pluginContainer.getMetaInformation().getName().c_str());
|
||||
if (!PluginManagement::doRelocation(pluginContainer.getPluginLinkInformation().getRelocationDataList(),
|
||||
trampData,
|
||||
usedRPls)) {
|
||||
usedRPls, pluginContainer.getMemoryAllocator())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <span>
|
||||
#include <string>
|
||||
|
||||
class IPluginHeapMemoryAllocator;
|
||||
class RelocationData;
|
||||
class PluginLoadWrapper;
|
||||
class PluginContainer;
|
||||
|
|
@ -25,7 +26,8 @@ public:
|
|||
|
||||
static bool doRelocation(const std::vector<RelocationData> &relocData,
|
||||
std::span<relocation_trampoline_entry_t> trampData,
|
||||
std::map<std::string, OSDynLoad_Module> &usedRPls);
|
||||
std::map<std::string, OSDynLoad_Module> &usedRPls,
|
||||
const IPluginHeapMemoryAllocator &);
|
||||
|
||||
static bool DoFunctionPatches(std::vector<PluginContainer> &plugins);
|
||||
|
||||
|
|
|
|||
44
source/plugin/DefaultPluginHeapMemoryAllocator.cpp
Normal file
44
source/plugin/DefaultPluginHeapMemoryAllocator.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#include "DefaultPluginHeapMemoryAllocator.h"
|
||||
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <coreinit/dynload.h>
|
||||
|
||||
DefaultPluginHeapMemoryAllocator DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator;
|
||||
|
||||
DefaultPluginHeapMemoryAllocator::DefaultPluginHeapMemoryAllocator() {
|
||||
OSDynLoad_Module rplHandle;
|
||||
if (OSDynLoad_Acquire("homebrew_memorymapping", &rplHandle) == OS_DYNLOAD_OK) {
|
||||
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemory", reinterpret_cast<void **>(&mMemoryMappingModuleAllocPtr));
|
||||
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMAllocFromMappedMemoryEx", reinterpret_cast<void **>(&mMemoryMappingModuleAllocExPtr));
|
||||
OSDynLoad_FindExport(rplHandle, OS_DYNLOAD_EXPORT_DATA, "MEMFreeToMappedMemory", reinterpret_cast<void **>(&mMemoryMappingModuleFreePtr));
|
||||
}
|
||||
}
|
||||
|
||||
MEMAllocFromDefaultHeapFn *DefaultPluginHeapMemoryAllocator::GetAllocFunctionAddress() const {
|
||||
if (mMemoryMappingModuleAllocPtr == nullptr) {
|
||||
DEBUG_FUNCTION_LINE_WARN("### mMemoryMappingModuleAllocPtr was NULL!");
|
||||
return &MEMAllocFromDefaultHeap;
|
||||
}
|
||||
return mMemoryMappingModuleAllocPtr;
|
||||
}
|
||||
|
||||
MEMAllocFromDefaultHeapExFn *DefaultPluginHeapMemoryAllocator::GetAllocExFunctionAddress() const {
|
||||
if (mMemoryMappingModuleAllocExPtr == nullptr) {
|
||||
DEBUG_FUNCTION_LINE_WARN("### mMemoryMappingModuleAllocExPtr was NULL!");
|
||||
return &MEMAllocFromDefaultHeapEx;
|
||||
}
|
||||
return mMemoryMappingModuleAllocExPtr;
|
||||
}
|
||||
|
||||
MEMFreeToDefaultHeapFn *DefaultPluginHeapMemoryAllocator::GetFreeFunctionAddress() const {
|
||||
if (mMemoryMappingModuleFreePtr == nullptr) {
|
||||
DEBUG_FUNCTION_LINE_WARN("### mMemoryMappingModuleFreePtr was NULL!");
|
||||
return &MEMFreeToDefaultHeap;
|
||||
}
|
||||
return mMemoryMappingModuleFreePtr;
|
||||
}
|
||||
|
||||
std::optional<PluginMemorySnapshot> DefaultPluginHeapMemoryAllocator::GetHeapMemoryUsageSnapshot() const {
|
||||
return {};
|
||||
}
|
||||
20
source/plugin/DefaultPluginHeapMemoryAllocator.h
Normal file
20
source/plugin/DefaultPluginHeapMemoryAllocator.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
#include "IPluginHeapMemoryAllocator.h"
|
||||
|
||||
class DefaultPluginHeapMemoryAllocator : public IPluginHeapMemoryAllocator {
|
||||
public:
|
||||
~DefaultPluginHeapMemoryAllocator() override = default;
|
||||
MEMAllocFromDefaultHeapFn *GetAllocFunctionAddress() const override;
|
||||
MEMAllocFromDefaultHeapExFn *GetAllocExFunctionAddress() const override;
|
||||
MEMFreeToDefaultHeapFn *GetFreeFunctionAddress() const override;
|
||||
std::optional<PluginMemorySnapshot> GetHeapMemoryUsageSnapshot() const override;
|
||||
|
||||
static DefaultPluginHeapMemoryAllocator gDefaultPluginHeapMemoryAllocator;
|
||||
|
||||
private:
|
||||
DefaultPluginHeapMemoryAllocator();
|
||||
|
||||
MEMAllocFromDefaultHeapFn *mMemoryMappingModuleAllocPtr = nullptr;
|
||||
MEMAllocFromDefaultHeapExFn *mMemoryMappingModuleAllocExPtr = nullptr;
|
||||
MEMFreeToDefaultHeapFn *mMemoryMappingModuleFreePtr = nullptr;
|
||||
};
|
||||
46
source/plugin/IPluginHeapMemoryAllocator.h
Normal file
46
source/plugin/IPluginHeapMemoryAllocator.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
struct AllocationInfo {
|
||||
uint32_t size;
|
||||
std::vector<uint32_t> stackTrace;
|
||||
};
|
||||
|
||||
struct PluginMemorySnapshot {
|
||||
uint32_t currentAllocated = 0;
|
||||
uint32_t peakAllocated = 0;
|
||||
uint32_t allocCount = 0;
|
||||
uint32_t freeCount = 0;
|
||||
std::string pluginName = {};
|
||||
|
||||
std::map<void *, AllocationInfo> allocationMap;
|
||||
};
|
||||
|
||||
class IPluginHeapMemoryAllocator {
|
||||
public:
|
||||
virtual ~IPluginHeapMemoryAllocator() = default;
|
||||
|
||||
/**
|
||||
* The returned address needs to be valid for the whole time
|
||||
*/
|
||||
virtual MEMAllocFromDefaultHeapFn *GetAllocFunctionAddress() const = 0;
|
||||
|
||||
/**
|
||||
* The returned address needs to be valid for the whole time
|
||||
*/
|
||||
virtual MEMAllocFromDefaultHeapExFn *GetAllocExFunctionAddress() const = 0;
|
||||
|
||||
/**
|
||||
* The returned address needs to be valid for the whole time
|
||||
*/
|
||||
virtual MEMFreeToDefaultHeapFn *GetFreeFunctionAddress() const = 0;
|
||||
|
||||
virtual std::optional<PluginMemorySnapshot> GetHeapMemoryUsageSnapshot() const = 0;
|
||||
};
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include "PluginContainer.h"
|
||||
|
||||
#include "plugin/FunctionData.h"
|
||||
#include "plugin/HookData.h"
|
||||
#include "plugin/PluginConfigData.h"
|
||||
|
|
@ -6,7 +7,9 @@
|
|||
#include "plugin/PluginLinkInformation.h"
|
||||
#include "plugin/RelocationData.h"
|
||||
#include "plugin/SectionInfo.h"
|
||||
|
||||
#include "utils/buttoncombo/ButtonComboUtils.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/storage/StorageUtils.h"
|
||||
|
||||
#include <optional>
|
||||
|
|
@ -17,10 +20,15 @@ PluginContainer::PluginContainer(PluginMetaInformation metaInformation, PluginLi
|
|||
mPluginData(std::move(pluginData)) {
|
||||
// Abuse this as a stable handle that references itself and survives std::move
|
||||
*mHandle = reinterpret_cast<uint32_t>(mHandle.get());
|
||||
|
||||
if (const bool res = useTrackingPluginHeapMemoryAllocator(mMetaInformation.getHeapTrackingOptions()); !res) {
|
||||
DEBUG_FUNCTION_LINE_WARN("Failed to set heap tracking options for \"%s\"", mMetaInformation.getName().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
PluginContainer::PluginContainer(PluginContainer &&src) noexcept : mMetaInformation(std::move(src.mMetaInformation)),
|
||||
mPluginLinkInformation(std::move(src.mPluginLinkInformation)),
|
||||
mTrackingHeapAllocatorOpt(std::move(src.mTrackingHeapAllocatorOpt)),
|
||||
mPluginData(std::move(src.mPluginData)),
|
||||
mHandle(std::move(src.mHandle)),
|
||||
mPluginConfigData(std::move(src.mPluginConfigData)),
|
||||
|
|
@ -39,6 +47,7 @@ PluginContainer &PluginContainer::operator=(PluginContainer &&src) noexcept {
|
|||
this->mPluginData = std::move(src.mPluginData);
|
||||
this->mPluginConfigData = std::move(src.mPluginConfigData);
|
||||
this->mHandle = std::move(src.mHandle);
|
||||
this->mTrackingHeapAllocatorOpt = std::move(src.mTrackingHeapAllocatorOpt);
|
||||
this->mStorageRootItem = src.mStorageRootItem;
|
||||
this->mInitDone = src.mInitDone;
|
||||
this->mButtonComboManagerHandle = src.mButtonComboManagerHandle;
|
||||
|
|
@ -135,4 +144,31 @@ void PluginContainer::DeinitButtonComboData() {
|
|||
|
||||
uint32_t PluginContainer::getButtonComboManagerHandle() const {
|
||||
return mButtonComboManagerHandle;
|
||||
}
|
||||
|
||||
bool PluginContainer::useTrackingPluginHeapMemoryAllocator(PluginMetaInformation::HeapTrackingOptions options) {
|
||||
if (options != PluginMetaInformation::TRACK_HEAP_OPTIONS_NONE) {
|
||||
if (!this->mTrackingHeapAllocatorOpt) {
|
||||
uint32_t stackTraceDepth = 0;
|
||||
if (options == PluginMetaInformation::TRACK_HEAP_OPTIONS_TRACK_SIZE_AND_COLLECT_STACK_TRACES) {
|
||||
stackTraceDepth = 8;
|
||||
}
|
||||
this->mTrackingHeapAllocatorOpt = TrackingPluginHeapMemoryAllocator::Create(this->mMetaInformation.getName(), stackTraceDepth);
|
||||
}
|
||||
|
||||
return this->mTrackingHeapAllocatorOpt.has_value();
|
||||
}
|
||||
this->mTrackingHeapAllocatorOpt.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginContainer::isUsingTrackingPluginHeapMemoryAllocator() const {
|
||||
return mTrackingHeapAllocatorOpt.has_value();
|
||||
}
|
||||
|
||||
const IPluginHeapMemoryAllocator &PluginContainer::getMemoryAllocator() const {
|
||||
if (mTrackingHeapAllocatorOpt) {
|
||||
return *mTrackingHeapAllocatorOpt;
|
||||
}
|
||||
return DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator;
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include "PluginConfigData.h"
|
||||
#include "PluginLinkInformation.h"
|
||||
#include "PluginMetaInformation.h"
|
||||
#include "TrackingPluginHeapMemoryAllocator.h"
|
||||
|
||||
#include <wups/storage.h>
|
||||
|
||||
|
|
@ -69,9 +70,19 @@ public:
|
|||
|
||||
[[nodiscard]] uint32_t getButtonComboManagerHandle() const;
|
||||
|
||||
/**
|
||||
* @return Returns true if setting the value was successful
|
||||
*/
|
||||
bool useTrackingPluginHeapMemoryAllocator(PluginMetaInformation::HeapTrackingOptions options);
|
||||
|
||||
[[nodiscard]] bool isUsingTrackingPluginHeapMemoryAllocator() const;
|
||||
|
||||
[[nodiscard]] const IPluginHeapMemoryAllocator &getMemoryAllocator() const;
|
||||
|
||||
private:
|
||||
PluginMetaInformation mMetaInformation;
|
||||
PluginLinkInformation mPluginLinkInformation;
|
||||
std::optional<TrackingPluginHeapMemoryAllocator> mTrackingHeapAllocatorOpt;
|
||||
std::shared_ptr<PluginData> mPluginData;
|
||||
|
||||
std::unique_ptr<uint32_t> mHandle = std::make_unique<uint32_t>();
|
||||
|
|
|
|||
|
|
@ -38,6 +38,10 @@
|
|||
return mSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] PluginMetaInformation::HeapTrackingOptions PluginMetaInformation::getHeapTrackingOptions() const {
|
||||
return mHeapTrackingOptions;
|
||||
}
|
||||
|
||||
PluginMetaInformation::PluginMetaInformation() = default;
|
||||
|
||||
void PluginMetaInformation::setName(std::string name) {
|
||||
|
|
@ -78,4 +82,8 @@ void PluginMetaInformation::setSize(const size_t size) {
|
|||
|
||||
void PluginMetaInformation::setStorageId(std::string storageId) {
|
||||
mStorageId = std::move(storageId);
|
||||
}
|
||||
|
||||
void PluginMetaInformation::setHeapTrackingOptions(HeapTrackingOptions value) {
|
||||
mHeapTrackingOptions = value;
|
||||
}
|
||||
|
|
@ -26,7 +26,14 @@
|
|||
class WUPSVersion;
|
||||
|
||||
class PluginMetaInformation {
|
||||
|
||||
public:
|
||||
enum HeapTrackingOptions {
|
||||
TRACK_HEAP_OPTIONS_NONE = 0,
|
||||
TRACK_HEAP_OPTIONS_TRACK_SIZE = 1,
|
||||
TRACK_HEAP_OPTIONS_TRACK_SIZE_AND_COLLECT_STACK_TRACES = 2,
|
||||
};
|
||||
|
||||
[[nodiscard]] const std::string &getName() const;
|
||||
|
||||
[[nodiscard]] const std::string &getAuthor() const;
|
||||
|
|
@ -45,6 +52,8 @@ public:
|
|||
|
||||
[[nodiscard]] size_t getSize() const;
|
||||
|
||||
[[nodiscard]] HeapTrackingOptions getHeapTrackingOptions() const;
|
||||
|
||||
private:
|
||||
PluginMetaInformation();
|
||||
|
||||
|
|
@ -68,6 +77,8 @@ private:
|
|||
|
||||
void setStorageId(std::string storageId);
|
||||
|
||||
void setHeapTrackingOptions(HeapTrackingOptions value);
|
||||
|
||||
std::string mName;
|
||||
std::string mAuthor;
|
||||
std::string mVersion;
|
||||
|
|
@ -75,8 +86,9 @@ private:
|
|||
std::string mBuildTimestamp;
|
||||
std::string mDescription;
|
||||
std::string mStorageId;
|
||||
size_t mSize = {};
|
||||
WUPSVersion mWUPSVersion = WUPSVersion(0, 0, 0);
|
||||
size_t mSize = {};
|
||||
WUPSVersion mWUPSVersion = WUPSVersion(0, 0, 0);
|
||||
HeapTrackingOptions mHeapTrackingOptions = TRACK_HEAP_OPTIONS_NONE;
|
||||
|
||||
friend class PluginMetaInformationFactory;
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,12 @@ std::optional<PluginMetaInformation> PluginMetaInformationFactory::loadPlugin(st
|
|||
pluginInfo.setDescription(value);
|
||||
} else if (key == "storage_id") {
|
||||
pluginInfo.setStorageId(value);
|
||||
} else if (key == "debug") {
|
||||
if (value == "track_heap") {
|
||||
pluginInfo.setHeapTrackingOptions(PluginMetaInformation::TRACK_HEAP_OPTIONS_TRACK_SIZE);
|
||||
} else if (value == "track_heap_with_stack_trace") {
|
||||
pluginInfo.setHeapTrackingOptions(PluginMetaInformation::TRACK_HEAP_OPTIONS_TRACK_SIZE_AND_COLLECT_STACK_TRACES);
|
||||
}
|
||||
} else if (key == "wups") {
|
||||
if (value == "0.7.1") {
|
||||
pluginInfo.setWUPSVersion(0, 7, 1);
|
||||
|
|
|
|||
177
source/plugin/TrackingPluginHeapMemoryAllocator.cpp
Normal file
177
source/plugin/TrackingPluginHeapMemoryAllocator.cpp
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
#include "TrackingPluginHeapMemoryAllocator.h"
|
||||
|
||||
#include "utils/logger.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace {
|
||||
struct PluginMemoryStatsInternal {
|
||||
mutable std::mutex mutex;
|
||||
bool inUse = false;
|
||||
uint32_t stackTraceDepth = 0;
|
||||
PluginMemorySnapshot data;
|
||||
};
|
||||
|
||||
// Generate 64 different thunk functions so we can track 64 plugins at a time which should be more than enough
|
||||
constexpr int MAX_SLOTS = 64;
|
||||
template<int N>
|
||||
struct MemoryThunk {
|
||||
inline static PluginMemoryStatsInternal stats;
|
||||
|
||||
static void *Alloc(uint32_t size) {
|
||||
const auto ptr = (*DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetAllocFunctionAddress())(size);
|
||||
|
||||
if (ptr) {
|
||||
std::lock_guard lock(stats.mutex);
|
||||
if (stats.inUse) {
|
||||
stats.data.allocCount++;
|
||||
stats.data.currentAllocated += size;
|
||||
if (stats.data.currentAllocated > stats.data.peakAllocated) {
|
||||
stats.data.peakAllocated = stats.data.currentAllocated;
|
||||
}
|
||||
|
||||
stats.data.allocationMap[ptr] = {size, stats.stackTraceDepth > 0 ? CaptureStackTrace(stats.stackTraceDepth) : std::vector<uint32_t>()};
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *AllocEx(uint32_t size, int align) {
|
||||
const auto ptr = (*DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetAllocExFunctionAddress())(size, align);
|
||||
if (ptr) {
|
||||
std::lock_guard lock(stats.mutex);
|
||||
if (stats.inUse) {
|
||||
stats.data.allocCount++;
|
||||
stats.data.currentAllocated += size;
|
||||
if (stats.data.currentAllocated > stats.data.peakAllocated) {
|
||||
stats.data.peakAllocated = stats.data.currentAllocated;
|
||||
}
|
||||
stats.data.allocationMap[ptr] = {size, stats.stackTraceDepth > 0 ? CaptureStackTrace(stats.stackTraceDepth) : std::vector<uint32_t>()};
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void Free(void *ptr) {
|
||||
if (!ptr) return;
|
||||
|
||||
std::lock_guard lock(stats.mutex);
|
||||
if (stats.inUse) {
|
||||
if (const auto it = stats.data.allocationMap.find(ptr); it != stats.data.allocationMap.end()) {
|
||||
uint32_t size = it->second.size;
|
||||
if (stats.data.currentAllocated >= size) {
|
||||
stats.data.currentAllocated -= size;
|
||||
} else {
|
||||
stats.data.currentAllocated = 0;
|
||||
}
|
||||
stats.data.allocationMap.erase(it);
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("free() of for unknown ptr detected (double free?). \"%s\": %p", stats.data.pluginName.c_str(), ptr);
|
||||
auto stackTrace = CaptureStackTrace(16);
|
||||
PrintCapturedStackTrace(stackTrace);
|
||||
}
|
||||
stats.data.freeCount++;
|
||||
}
|
||||
|
||||
(*DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetFreeFunctionAddress())(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
template<int... Is>
|
||||
constexpr std::array<MEMAllocFromDefaultHeapFn, sizeof...(Is)> CreateAllocTable(std::integer_sequence<int, Is...>) { return {MemoryThunk<Is>::Alloc...}; }
|
||||
template<int... Is>
|
||||
constexpr std::array<MEMAllocFromDefaultHeapExFn, sizeof...(Is)> CreateAllocExTable(std::integer_sequence<int, Is...>) { return {MemoryThunk<Is>::AllocEx...}; }
|
||||
template<int... Is>
|
||||
constexpr std::array<MEMFreeToDefaultHeapFn, sizeof...(Is)> CreateFreeTable(std::integer_sequence<int, Is...>) { return {MemoryThunk<Is>::Free...}; }
|
||||
|
||||
template<int... Is>
|
||||
constexpr std::array<PluginMemoryStatsInternal *, sizeof...(Is)> CreateStatsTable(std::integer_sequence<int, Is...>) {
|
||||
return {&MemoryThunk<Is>::stats...};
|
||||
}
|
||||
|
||||
auto sAllocThunks = CreateAllocTable(std::make_integer_sequence<int, MAX_SLOTS>{});
|
||||
auto sAllocExThunks = CreateAllocExTable(std::make_integer_sequence<int, MAX_SLOTS>{});
|
||||
auto sFreeThunks = CreateFreeTable(std::make_integer_sequence<int, MAX_SLOTS>{});
|
||||
auto sStatsTable = CreateStatsTable(std::make_integer_sequence<int, MAX_SLOTS>{});
|
||||
} // namespace
|
||||
|
||||
TrackingPluginHeapMemoryAllocator::TrackingPluginHeapMemoryAllocator(const int32_t index) : sIndex(index) {
|
||||
}
|
||||
|
||||
std::optional<TrackingPluginHeapMemoryAllocator> TrackingPluginHeapMemoryAllocator::Create(const std::string_view pluginName, uint32_t stackTraceDepth) {
|
||||
int32_t index = 0;
|
||||
for (auto *stat : sStatsTable) {
|
||||
std::lock_guard lock(stat->mutex);
|
||||
if (!stat->inUse) {
|
||||
stat->inUse = true;
|
||||
stat->stackTraceDepth = stackTraceDepth;
|
||||
stat->data = {};
|
||||
stat->data.pluginName = pluginName;
|
||||
return TrackingPluginHeapMemoryAllocator{index};
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TrackingPluginHeapMemoryAllocator::~TrackingPluginHeapMemoryAllocator() {
|
||||
if (sIndex < 0 || sIndex >= MAX_SLOTS) {
|
||||
return;
|
||||
}
|
||||
auto *stats = sStatsTable[sIndex];
|
||||
std::lock_guard lock(stats->mutex);
|
||||
stats->inUse = false;
|
||||
}
|
||||
|
||||
TrackingPluginHeapMemoryAllocator::TrackingPluginHeapMemoryAllocator(TrackingPluginHeapMemoryAllocator &&src) noexcept {
|
||||
this->sIndex = src.sIndex;
|
||||
src.sIndex = -1;
|
||||
}
|
||||
|
||||
TrackingPluginHeapMemoryAllocator &TrackingPluginHeapMemoryAllocator::operator=(TrackingPluginHeapMemoryAllocator &&src) noexcept {
|
||||
if (this != &src) {
|
||||
// Release current slot if we have one
|
||||
if (this->sIndex >= 0 && this->sIndex < MAX_SLOTS) {
|
||||
auto *stats = sStatsTable[this->sIndex];
|
||||
std::lock_guard lock(stats->mutex);
|
||||
stats->inUse = false;
|
||||
stats->data = {};
|
||||
}
|
||||
|
||||
this->sIndex = src.sIndex;
|
||||
src.sIndex = -1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
MEMAllocFromDefaultHeapFn *TrackingPluginHeapMemoryAllocator::GetAllocFunctionAddress() const {
|
||||
if (sIndex < 0 || sIndex >= MAX_SLOTS) {
|
||||
return DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetAllocFunctionAddress();
|
||||
}
|
||||
|
||||
return &sAllocThunks[sIndex];
|
||||
}
|
||||
|
||||
MEMAllocFromDefaultHeapExFn *TrackingPluginHeapMemoryAllocator::GetAllocExFunctionAddress() const {
|
||||
if (sIndex < 0 || sIndex >= MAX_SLOTS) {
|
||||
return DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetAllocExFunctionAddress();
|
||||
}
|
||||
return &sAllocExThunks[sIndex];
|
||||
}
|
||||
|
||||
MEMFreeToDefaultHeapFn *TrackingPluginHeapMemoryAllocator::GetFreeFunctionAddress() const {
|
||||
if (sIndex < 0 || sIndex >= MAX_SLOTS) {
|
||||
return DefaultPluginHeapMemoryAllocator::gDefaultPluginHeapMemoryAllocator.GetFreeFunctionAddress();
|
||||
}
|
||||
return &sFreeThunks[sIndex];
|
||||
}
|
||||
|
||||
std::optional<PluginMemorySnapshot> TrackingPluginHeapMemoryAllocator::GetHeapMemoryUsageSnapshot() const {
|
||||
if (sIndex < 0 || sIndex >= MAX_SLOTS) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto *stats = sStatsTable[sIndex];
|
||||
std::lock_guard lock(stats->mutex);
|
||||
return stats->data;
|
||||
}
|
||||
29
source/plugin/TrackingPluginHeapMemoryAllocator.h
Normal file
29
source/plugin/TrackingPluginHeapMemoryAllocator.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
#include "DefaultPluginHeapMemoryAllocator.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class TrackingPluginHeapMemoryAllocator : public IPluginHeapMemoryAllocator {
|
||||
public:
|
||||
static std::optional<TrackingPluginHeapMemoryAllocator> Create(std::string_view pluginName, uint32_t stackTraceDepth);
|
||||
|
||||
~TrackingPluginHeapMemoryAllocator() override;
|
||||
|
||||
TrackingPluginHeapMemoryAllocator(const TrackingPluginHeapMemoryAllocator &) = delete;
|
||||
|
||||
TrackingPluginHeapMemoryAllocator(TrackingPluginHeapMemoryAllocator &&src) noexcept;
|
||||
|
||||
TrackingPluginHeapMemoryAllocator &operator=(TrackingPluginHeapMemoryAllocator &&src) noexcept;
|
||||
|
||||
MEMAllocFromDefaultHeapFn *GetAllocFunctionAddress() const override;
|
||||
MEMAllocFromDefaultHeapExFn *GetAllocExFunctionAddress() const override;
|
||||
MEMFreeToDefaultHeapFn *GetFreeFunctionAddress() const override;
|
||||
std::optional<PluginMemorySnapshot> GetHeapMemoryUsageSnapshot() const override;
|
||||
|
||||
private:
|
||||
int sIndex = -1;
|
||||
TrackingPluginHeapMemoryAllocator(int index);
|
||||
};
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include "json.hpp"
|
||||
#include "logger.h"
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/ios.h>
|
||||
|
||||
#include <wups/storage.h>
|
||||
|
|
@ -198,4 +199,70 @@ std::vector<std::string> getNonBaseAromaPluginFilenames(std::string_view basePat
|
|||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
std::vector<uint32_t>
|
||||
CaptureStackTrace(uint32_t maxDepth) {
|
||||
std::vector<uint32_t> trace;
|
||||
trace.reserve(maxDepth);
|
||||
|
||||
uint32_t *stackPointer;
|
||||
// Grab the current Stack Pointer (r1)
|
||||
asm volatile("mr %0, 1"
|
||||
: "=r"(stackPointer));
|
||||
|
||||
for (uint32_t i = 0; i < maxDepth + 1; ++i) {
|
||||
// Basic alignment check
|
||||
if (!stackPointer || (reinterpret_cast<uintptr_t>(stackPointer) & 0x3)) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t backChain = stackPointer[0];
|
||||
uint32_t returnAddr = stackPointer[1];
|
||||
|
||||
if (returnAddr == 0) break;
|
||||
if (i != 0) {
|
||||
trace.push_back(returnAddr);
|
||||
}
|
||||
|
||||
if (backChain == 0) break;
|
||||
stackPointer = reinterpret_cast<uint32_t *>(backChain);
|
||||
}
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
||||
#define SC17_FindClosestSymbol ((uint32_t(*)(uint32_t addr, int32_t * outDistance, char *symbolNameBuffer, uint32_t symbolNameBufferLength, char *moduleNameBuffer, uint32_t moduleNameBufferLength))(0x101C400 + 0x1f934))
|
||||
|
||||
void PrintCapturedStackTrace(std::span<uint32_t> trace) {
|
||||
if (trace.empty()) {
|
||||
DEBUG_FUNCTION_LINE_INFO("┌────────────────────── CAPTURED TRACE ──────────────────────┐");
|
||||
DEBUG_FUNCTION_LINE_INFO("│ <Empty Trace>");
|
||||
DEBUG_FUNCTION_LINE_INFO("└────────────────────────────────────────────────────────────┘");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE_INFO("┌────────────────────── CAPTURED TRACE ──────────────────────┐");
|
||||
for (size_t i = 0; i < trace.size(); ++i) {
|
||||
uint32_t addr = trace[i];
|
||||
int distance = 0;
|
||||
char moduleName[100] = {};
|
||||
char symbolName[100] = {};
|
||||
|
||||
if (SC17_FindClosestSymbol(addr, &distance, symbolName, sizeof(symbolName) - 1, moduleName, sizeof(moduleName) - 1) != 0) {
|
||||
DEBUG_FUNCTION_LINE_INFO("│ [%02d] 0x%08X", i, addr);
|
||||
} else {
|
||||
moduleName[sizeof(moduleName) - 1] = '\0';
|
||||
symbolName[sizeof(symbolName) - 1] = '\0';
|
||||
DEBUG_FUNCTION_LINE_INFO("│ [%02d] %s : %s + 0x%X (0x%08X)",
|
||||
i,
|
||||
moduleName,
|
||||
symbolName,
|
||||
distance,
|
||||
addr);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE_INFO("└────────────────────────────────────────────────────────────┘");
|
||||
}
|
||||
|
|
@ -161,4 +161,8 @@ UtilsIOError ParseJsonFromFile(const std::string &filePath, nlohmann::json &outJ
|
|||
|
||||
std::vector<std::string> getPluginFilePaths(std::string_view basePath);
|
||||
|
||||
std::vector<std::string> getNonBaseAromaPluginFilenames(std::string_view basePath);
|
||||
std::vector<std::string> getNonBaseAromaPluginFilenames(std::string_view basePath);
|
||||
|
||||
std::vector<uint32_t> CaptureStackTrace(uint32_t maxDepth);
|
||||
|
||||
void PrintCapturedStackTrace(std::span<uint32_t> trace);
|
||||
Loading…
Reference in New Issue
Block a user