diff --git a/libraries/libstratosphere/include/stratosphere/ncm.hpp b/libraries/libstratosphere/include/stratosphere/ncm.hpp index a43a872fc..e87fdfa2c 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp index 94cdd4317..9f4fd8ae2 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_content_manager_impl.hpp @@ -30,14 +30,32 @@ namespace ams::ncm { - class ContentMetaMemoryResource { + class ContentMetaMemoryResource : public MemoryResource { private: mem::StandardAllocator allocator; - sf::StandardAllocatorMemoryResource memory_resource; + size_t peak_total_alloc_size; + size_t peak_alloc_size; public: - ContentMetaMemoryResource(void *heap, size_t heap_size) : allocator(heap, heap_size), memory_resource(std::addressof(allocator)) { /* ... */ } + explicit ContentMetaMemoryResource(void *heap, size_t heap_size) : allocator(heap, heap_size) { /* ... */ } - MemoryResource *Get() { return std::addressof(this->memory_resource); } + mem::StandardAllocator *GetAllocator() { return std::addressof(this->allocator); } + size_t GetPeakTotalAllocationSize() const { return this->peak_total_alloc_size; } + size_t GetPeakAllocationSize() const { return this->peak_alloc_size; } + private: + virtual void *AllocateImpl(size_t size, size_t alignment) override { + void *mem = this->allocator.Allocate(size, alignment); + this->peak_total_alloc_size = std::max(this->allocator.Hash().allocated_size, this->peak_total_alloc_size); + this->peak_alloc_size = std::max(size, this->peak_alloc_size); + return mem; + } + + virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override { + return this->allocator.Free(buffer); + } + + virtual bool IsEqualImpl(const MemoryResource &resource) const override { + return this == std::addressof(resource); + } }; struct SystemSaveDataInfo { @@ -127,6 +145,7 @@ namespace ams::ncm { virtual Result ActivateContentMetaDatabase(StorageId storage_id) override; virtual Result InactivateContentMetaDatabase(StorageId storage_id) override; virtual Result InvalidateRightsIdCache() override; + virtual Result GetMemoryReport(sf::Out out) override; }; } diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_manager.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_manager.hpp index 7561dfb0f..1ddfd6aa3 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_manager.hpp @@ -16,6 +16,7 @@ #pragma once #include #include +#include namespace ams::ncm { @@ -36,6 +37,7 @@ namespace ams::ncm { ActivateContentMetaDatabase = 11, InactivateContentMetaDatabase = 12, InvalidateRightsIdCache = 13, + GetMemoryReport = 14, }; public: virtual Result CreateContentStorage(StorageId storage_id) = 0; @@ -52,6 +54,7 @@ namespace ams::ncm { virtual Result ActivateContentMetaDatabase(StorageId storage_id) = 0; virtual Result InactivateContentMetaDatabase(StorageId storage_id) = 0; virtual Result InvalidateRightsIdCache() = 0; + virtual Result GetMemoryReport(sf::Out out) = 0; public: DEFINE_SERVICE_DISPATCH_TABLE { MAKE_SERVICE_COMMAND_META(CreateContentStorage), @@ -68,6 +71,7 @@ namespace ams::ncm { MAKE_SERVICE_COMMAND_META(ActivateContentMetaDatabase, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(InactivateContentMetaDatabase, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(InvalidateRightsIdCache, hos::Version_9_0_0), + MAKE_SERVICE_COMMAND_META(GetMemoryReport, hos::Version_10_0_0), }; }; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp index ab77a4dfe..98b001c21 100644 --- a/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_i_content_meta_database.hpp @@ -43,6 +43,8 @@ namespace ams::ncm { GetAttributes = 18, GetRequiredApplicationVersion = 19, GetContentIdByTypeAndIdOffset = 20, + GetCount = 21, + GetOwnerApplicationId = 22, }; public: /* Actual commands. */ @@ -67,6 +69,8 @@ namespace ams::ncm { virtual Result GetAttributes(sf::Out out_attributes, const ContentMetaKey &key) = 0; virtual Result GetRequiredApplicationVersion(sf::Out out_version, const ContentMetaKey &key) = 0; virtual Result GetContentIdByTypeAndIdOffset(sf::Out out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) = 0; + virtual Result GetCount(sf::Out out_count) = 0; + virtual Result GetOwnerApplicationId(sf::Out out_id, const ContentMetaKey &key) = 0; public: DEFINE_SERVICE_DISPATCH_TABLE { MAKE_SERVICE_COMMAND_META(Set), @@ -90,6 +94,8 @@ namespace ams::ncm { MAKE_SERVICE_COMMAND_META(GetAttributes), MAKE_SERVICE_COMMAND_META(GetRequiredApplicationVersion, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(GetContentIdByTypeAndIdOffset, hos::Version_5_0_0), + MAKE_SERVICE_COMMAND_META(GetCount, hos::Version_10_0_0), + MAKE_SERVICE_COMMAND_META(GetOwnerApplicationId, hos::Version_10_0_0), }; }; diff --git a/libraries/libstratosphere/include/stratosphere/ncm/ncm_memory_report.hpp b/libraries/libstratosphere/include/stratosphere/ncm/ncm_memory_report.hpp new file mode 100644 index 000000000..a825fb177 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ncm/ncm_memory_report.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::ncm { + + struct MemoryResourceState { + size_t peak_total_alloc_size; + size_t peak_alloc_size; + size_t allocatable_size; + size_t total_free_size; + }; + + static_assert(sizeof(MemoryResourceState) == 0x20); + + struct MemoryReport { + MemoryResourceState system_content_meta_resource_state; + MemoryResourceState sd_and_user_content_meta_resource_state; + MemoryResourceState gamecard_content_meta_resource_state; + MemoryResourceState heap_resource_state; + }; + + static_assert(sizeof(MemoryReport) == 0x80); + + class HeapState { + private: + os::Mutex mutex; + lmem::HeapHandle heap_handle; + size_t total_alloc_size; + size_t peak_total_alloc_size; + size_t peak_alloc_size; + public: + constexpr HeapState() : mutex(false), heap_handle(nullptr), total_alloc_size(0), peak_total_alloc_size(0), peak_alloc_size(0) { /* ... */ } + + void Initialize(lmem::HeapHandle heap_handle); + void Allocate(size_t size); + void Free(size_t size); + void GetMemoryResourceState(MemoryResourceState *out); + }; + + HeapState &GetHeapState(); + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp index 13ad772a4..2e1f9d2a9 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_manager_impl.cpp @@ -614,7 +614,7 @@ namespace ams::ncm { if (storage_id == StorageId::GameCard) { /* Initialize the key value store. */ - R_TRY(root->kvs->Initialize(root->max_content_metas, root->memory_resource->Get())); + R_TRY(root->kvs->Initialize(root->max_content_metas, root->memory_resource)); /* Create an on memory content meta database for game cards. */ root->content_meta_database = std::make_shared(std::addressof(*root->kvs)); @@ -626,7 +626,7 @@ namespace ams::ncm { auto mount_guard = SCOPE_GUARD { fs::Unmount(root->mount_name); }; /* Initialize and load the key value store from the filesystem. */ - R_TRY(root->kvs->Initialize(root->path, root->max_content_metas, root->memory_resource->Get())); + R_TRY(root->kvs->Initialize(root->path, root->max_content_metas, root->memory_resource)); R_TRY(root->kvs->Load()); /* Create the content meta database. */ @@ -665,4 +665,36 @@ namespace ams::ncm { return ResultSuccess(); } + Result ContentManagerImpl::GetMemoryReport(sf::Out out) { + /* Populate content meta resource states. */ + MemoryReport report = { + .system_content_meta_resource_state = { + .peak_total_alloc_size = g_system_content_meta_memory_resource.GetPeakTotalAllocationSize(), + .peak_alloc_size = g_system_content_meta_memory_resource.GetPeakAllocationSize(), + .allocatable_size = g_system_content_meta_memory_resource.GetAllocator()->GetAllocatableSize(), + .total_free_size = g_system_content_meta_memory_resource.GetAllocator()->GetTotalFreeSize(), + }, + .sd_and_user_content_meta_resource_state { + .peak_total_alloc_size = g_sd_and_user_content_meta_memory_resource.GetPeakTotalAllocationSize(), + .peak_alloc_size = g_sd_and_user_content_meta_memory_resource.GetPeakAllocationSize(), + .allocatable_size = g_sd_and_user_content_meta_memory_resource.GetAllocator()->GetAllocatableSize(), + .total_free_size = g_sd_and_user_content_meta_memory_resource.GetAllocator()->GetTotalFreeSize(), + }, + .gamecard_content_meta_resource_state { + .peak_total_alloc_size = g_gamecard_content_meta_memory_resource.GetPeakTotalAllocationSize(), + .peak_alloc_size = g_gamecard_content_meta_memory_resource.GetPeakAllocationSize(), + .allocatable_size = g_gamecard_content_meta_memory_resource.GetAllocator()->GetAllocatableSize(), + .total_free_size = g_gamecard_content_meta_memory_resource.GetAllocator()->GetTotalFreeSize(), + }, + .heap_resource_state = {}, + }; + + /* Populate heap memory resource state. */ + GetHeapState().GetMemoryResourceState(std::addressof(report.heap_resource_state)); + + /* Output the report. */ + out.SetValue(report); + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp index bd7a7d06b..a9cad8536 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.cpp @@ -440,4 +440,47 @@ namespace ams::ncm { return this->GetContentIdImpl(out_content_id.GetPointer(), key, type, std::make_optional(id_offset)); } + Result ContentMetaDatabaseImpl::GetCount(sf::Out out_count) { + R_TRY(this->EnsureEnabled()); + out_count.SetValue(this->kvs->GetCount()); + return ResultSuccess(); + } + + Result ContentMetaDatabaseImpl::GetOwnerApplicationId(sf::Out out_id, const ContentMetaKey &key) { + R_TRY(this->EnsureEnabled()); + + /* Ensure this type of key has an owner. */ + R_UNLESS(key.type == ContentMetaType::Application || key.type == ContentMetaType::Patch || key.type == ContentMetaType::AddOnContent, ncm::ResultInvalidContentMetaKey()); + + /* Applications are their own owner. */ + if (key.type == ContentMetaType::Application) { + out_id.SetValue({key.id}); + return ResultSuccess(); + } + + /* Obtain the content meta for the key. */ + const void *meta; + size_t meta_size; + R_TRY(this->GetContentMetaPointer(&meta, &meta_size, key)); + + /* Create a reader. */ + ContentMetaReader reader(meta, meta_size); + + /* Get the owner application id. */ + ApplicationId owner_application_id; + switch (key.type) { + case ContentMetaType::Patch: + owner_application_id = reader.GetExtendedHeader()->application_id; + break; + case ContentMetaType::AddOnContent: + owner_application_id = reader.GetExtendedHeader()->application_id; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Set the output value. */ + out_id.SetValue(owner_application_id); + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp index 3ccfaab46..67b806e27 100644 --- a/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp +++ b/libraries/libstratosphere/source/ncm/ncm_content_meta_database_impl.hpp @@ -49,6 +49,8 @@ namespace ams::ncm { virtual Result GetAttributes(sf::Out out_attributes, const ContentMetaKey &key) override; virtual Result GetRequiredApplicationVersion(sf::Out out_version, const ContentMetaKey &key) override; virtual Result GetContentIdByTypeAndIdOffset(sf::Out out_content_id, const ContentMetaKey &key, ContentType type, u8 id_offset) override; + virtual Result GetCount(sf::Out out_count) override; + virtual Result GetOwnerApplicationId(sf::Out out_id, const ContentMetaKey &key) override; }; } diff --git a/libraries/libstratosphere/source/ncm/ncm_memory_report.cpp b/libraries/libstratosphere/source/ncm/ncm_memory_report.cpp new file mode 100644 index 000000000..36763a0b2 --- /dev/null +++ b/libraries/libstratosphere/source/ncm/ncm_memory_report.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019-2020 Adubbz, Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ncm { + + void HeapState::Initialize(lmem::HeapHandle heap_handle) { + std::scoped_lock lk(this->mutex); + this->heap_handle = heap_handle; + } + + void HeapState::Allocate(size_t size) { + std::scoped_lock lk(this->mutex); + this->total_alloc_size += size; + this->peak_total_alloc_size = std::max(this->total_alloc_size, this->peak_total_alloc_size); + this->peak_alloc_size = std::max(size, this->peak_alloc_size); + } + + void HeapState::Free(size_t size) { + std::scoped_lock lk(this->mutex); + this->total_alloc_size -= size; + } + + void HeapState::GetMemoryResourceState(MemoryResourceState *out) { + *out = {}; + std::scoped_lock lk(this->mutex); + out->peak_total_alloc_size = this->peak_total_alloc_size; + out->peak_alloc_size = this->peak_alloc_size; + out->total_free_size = lmem::GetExpHeapTotalFreeSize(this->heap_handle); + out->allocatable_size = lmem::GetExpHeapAllocatableSize(this->heap_handle, alignof(s32)); + } + + HeapState &GetHeapState() { + static HeapState s_heap_state = {}; + return s_heap_state; + } + +} diff --git a/libraries/libstratosphere/source/ncm/ncm_remote_content_manager_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_remote_content_manager_impl.hpp index ac96d40bd..5ff6ca8d5 100644 --- a/libraries/libstratosphere/source/ncm/ncm_remote_content_manager_impl.hpp +++ b/libraries/libstratosphere/source/ncm/ncm_remote_content_manager_impl.hpp @@ -89,6 +89,11 @@ namespace ams::ncm { virtual Result InvalidateRightsIdCache() override { return ::ncmInvalidateRightsIdCache(); } + + virtual Result GetMemoryReport(sf::Out out) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } }; } diff --git a/libraries/libstratosphere/source/ncm/ncm_remote_content_meta_database_impl.hpp b/libraries/libstratosphere/source/ncm/ncm_remote_content_meta_database_impl.hpp index 9d8c72be4..98fc25068 100644 --- a/libraries/libstratosphere/source/ncm/ncm_remote_content_meta_database_impl.hpp +++ b/libraries/libstratosphere/source/ncm/ncm_remote_content_meta_database_impl.hpp @@ -156,6 +156,16 @@ namespace ams::ncm { return ncmContentMetaDatabaseGetContentIdByTypeAndIdOffset(std::addressof(this->srv), Convert(out_content_id.GetPointer()), Convert(key), static_cast<::NcmContentType>(type), id_offset); } + virtual Result GetCount(sf::Out out_count) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + virtual Result GetOwnerApplicationId(sf::Out out_id, const ContentMetaKey &key) override { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + }; } diff --git a/stratosphere/ncm/source/ncm_main.cpp b/stratosphere/ncm/source/ncm_main.cpp index debd92326..e9cc07427 100644 --- a/stratosphere/ncm/source/ncm_main.cpp +++ b/stratosphere/ncm/source/ncm_main.cpp @@ -55,15 +55,19 @@ namespace { lmem::HeapHandle g_heap_handle; void *Allocate(size_t size) { - return lmem::AllocateFromExpHeap(g_heap_handle, size); + void *mem = lmem::AllocateFromExpHeap(g_heap_handle, size); + GetHeapState().Allocate(size); + return mem; } void Deallocate(void *p, size_t size) { + GetHeapState().Free(size); lmem::FreeToExpHeap(g_heap_handle, p); } void InitializeHeap() { g_heap_handle = lmem::CreateExpHeap(g_heap_memory, sizeof(g_heap_memory), lmem::CreateOption_None); + GetHeapState().Initialize(g_heap_handle); } }