diff --git a/libpurgeablemem/BUILD.gn b/libpurgeablemem/BUILD.gn index 41f682f328d0d3721439e2ae51c21c1de4fb7217..bec592f0c1d9a027474e1ed8052813e07031b512 100644 --- a/libpurgeablemem/BUILD.gn +++ b/libpurgeablemem/BUILD.gn @@ -28,7 +28,9 @@ ohos_shared_library("libpurgeablemem") { "c/src/purgeable_mem_c.c", "common/src/pm_state_c.c", "common/src/ux_page_table_c.c", + "cpp/src/purgeable_ashmem.cpp", "cpp/src/purgeable_mem.cpp", + "cpp/src/purgeable_mem_base.cpp", "cpp/src/purgeable_mem_builder.cpp", "cpp/src/ux_page_table.cpp", ] diff --git a/libpurgeablemem/c/src/purgeable_mem_builder_c.c b/libpurgeablemem/c/src/purgeable_mem_builder_c.c index 714b4d050d61730eb65836d7b16d75726808c745..43e288cc58581f7083d365d3e3d259ab986460a8 100644 --- a/libpurgeablemem/c/src/purgeable_mem_builder_c.c +++ b/libpurgeablemem/c/src/purgeable_mem_builder_c.c @@ -18,7 +18,7 @@ #include /* malloc */ #include "hilog/log_c.h" -#include "../../common/include/pm_ptr_util.h" +#include "pm_ptr_util.h" #include "purgeable_mem_builder_c.h" #undef LOG_TAG diff --git a/libpurgeablemem/c/src/purgeable_mem_c.c b/libpurgeablemem/c/src/purgeable_mem_c.c index c1789774f4301d82004512bf0907d2d00205831c..eb0c846d17f6b65f27192f026d6666d7d8938a7c 100644 --- a/libpurgeablemem/c/src/purgeable_mem_c.c +++ b/libpurgeablemem/c/src/purgeable_mem_c.c @@ -19,10 +19,10 @@ #include /* FILE */ #include "securec.h" -#include "../../common/include/pm_ptr_util.h" -#include "../../common/include/pm_util.h" -#include "../../common/include/pm_state_c.h" -#include "../../common/include/ux_page_table_c.h" +#include "pm_ptr_util.h" +#include "pm_util.h" +#include "pm_state_c.h" +#include "ux_page_table_c.h" #include "purgeable_mem_builder_c.h" #include "purgeable_mem_c.h" diff --git a/libpurgeablemem/cpp/include/pm_log.h b/libpurgeablemem/cpp/include/pm_log.h new file mode 100644 index 0000000000000000000000000000000000000000..0a8f0df5135f9f8521c001810aef5216ef827c92 --- /dev/null +++ b/libpurgeablemem/cpp/include/pm_log.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PM_LOG_H +#define OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PM_LOG_H + +#include "hilog/log.h" + +static constexpr OHOS::HiviewDFX::HiLogLabel PM_LOG_LABEL = { LOG_CORE, 0xD001799, "MemMgrPurge" }; + +#ifdef PM_HILOG_ERROR +#undef PM_HILOG_ERROR +#endif + +#ifdef PM_HILOG_INFO +#undef PM_HILOG_INFO +#endif + +#ifdef PM_HILOG_DEBUG +#undef PM_HILOG_DEBUG +#endif + +#define PM_FILENAME "purgeable" + +#define PM_HILOG_ERROR(logCore, fmt, ...) \ + (void)OHOS::HiviewDFX::HiLog::Error( \ + PM_LOG_LABEL, "[%{public}s(%{public}s:%{public}d)]" fmt, PM_FILENAME, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +#define PM_HILOG_INFO(logCore, fmt, ...) \ + (void)OHOS::HiviewDFX::HiLog::Info( \ + PM_LOG_LABEL, "[%{public}s(%{public}s:%{public}d)]" fmt, PM_FILENAME, __FUNCTION__, __LINE__, ##__VA_ARGS__) + +#define PM_HILOG_DEBUG(logCore, fmt, ...) \ + (void)OHOS::HiviewDFX::HiLog::Debug( \ + PM_LOG_LABEL, "[%{public}s(%{public}s:%{public}d)]" fmt, PM_FILENAME, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#endif \ No newline at end of file diff --git a/libpurgeablemem/cpp/include/pm_smartptr_util.h b/libpurgeablemem/cpp/include/pm_smartptr_util.h index ef24d24243b251351ad3c10154b6a9601e0884bc..ad77cdccde4bf9a61d86aa90e726a0870f8d15b6 100644 --- a/libpurgeablemem/cpp/include/pm_smartptr_util.h +++ b/libpurgeablemem/cpp/include/pm_smartptr_util.h @@ -16,7 +16,7 @@ #ifndef OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PM_SMARTPTR_UTIL_H #define OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PM_SMARTPTR_UTIL_H -#include "../../common/include/pm_ptr_util.h" +#include "pm_ptr_util.h" namespace OHOS { namespace PurgeableMem { diff --git a/libpurgeablemem/cpp/include/purgeable_ashmem.h b/libpurgeablemem/cpp/include/purgeable_ashmem.h new file mode 100644 index 0000000000000000000000000000000000000000..bf5f78e7fa24ecda85b9adc8600ad0b2521ec8a9 --- /dev/null +++ b/libpurgeablemem/cpp/include/purgeable_ashmem.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_ASHMEM_H +#define OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_ASHMEM_H + +#include +#include +#include +#include +#include +#include + +#include "ashmem.h" +#include "purgeable_mem_builder.h" +#include "purgeable_mem_base.h" +#include "ux_page_table.h" + +#ifndef ASHMEM_SET_PURGEABLE +#define ASHMEM_SET_PURGEABLE _IO(__ASHMEMIOC, 11) +#endif +#ifndef ASHMEM_GET_PURGEABLE +#define ASHMEM_GET_PURGEABLE _IO(__ASHMEMIOC, 12) +#endif +#ifndef PURGEABLE_ASHMEM_IS_PURGED +#define PURGEABLE_ASHMEM_IS_PURGED _IO(__ASHMEMIOC, 13) +#endif +#ifndef PURGEABLE_ASHMEM_REBUILD_SUCCESS +#define PURGEABLE_ASHMEM_REBUILD_SUCCESS _IO(__ASHMEMIOC, 14) +#endif + +namespace OHOS { +namespace PurgeableMem { +class PurgeableAshMem : public PurgeableMemBase { +public: + PurgeableAshMem(size_t dataSize, std::unique_ptr builder); + PurgeableAshMem(std::unique_ptr builder); + ~PurgeableAshMem() override; + int GetAshmemFd(); + void ResizeData(size_t newSize) override; + void ChangeAshmemData(size_t size, int fd, void *data); +protected: + int ashmemFd_; + int isSupport_; + ashmem_pin pin_ = { static_cast(0), static_cast(0) }; + bool Pin_() override; + bool Unpin_() override; + bool IsPurged_() override; + bool CreatePurgeableData_() override; + void AfterRebuildSucc_() override; + std::string ToString_() const override; +}; +} /* namespace PurgeableMem */ +} /* namespace OHOS */ +#endif /* OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_ASHMEM_H */ diff --git a/libpurgeablemem/cpp/include/purgeable_mem.h b/libpurgeablemem/cpp/include/purgeable_mem.h index c77e91b94111c7030feb6d6941cd6c8c2e606833..85d617d61b016d6f7c823a40e9189fe631487478 100644 --- a/libpurgeablemem/cpp/include/purgeable_mem.h +++ b/libpurgeablemem/cpp/include/purgeable_mem.h @@ -21,95 +21,26 @@ #include #include "purgeable_mem_builder.h" +#include "purgeable_mem_base.h" #include "ux_page_table.h" namespace OHOS { namespace PurgeableMem { -class PurgeableMem { +class PurgeableMem : public PurgeableMemBase { public: - /* - * Constructor of class PurgeableMem. - * Input: @dataSize: data size of this PurgeableMem obj's data content. - * Input: @builder: using @builder to recover user data when this obj's content is purged. - * @builder should be a unique_ptr to avolid memory manage chaos(memory leak). - */ PurgeableMem(size_t dataSize, std::unique_ptr builder); - - /* - * Destructor of class PurgeableMem. - * It will free the memory of builders associated with this PurgeableMem obj. - */ ~PurgeableMem(); + void ResizeData(size_t newSize) override; - /* - * BeginRead: begin read the PurgeableMem obj. - * Return: return true if the obj's content is present. - * If content is purged(no present), system will recover its data, - * return false if content is purged and recover failed. - * While return true if content recover success. - * OS cannot reclaim the memory of the obj's content when this - * function return true, until EndRead() is called. - */ - bool BeginRead(); - - /* - * EndRead: end read the PurgeableMem obj. - * OS may reclaim the memory of its content - * at a later time when this function returns. - */ - void EndRead(); - - /* - * BeginWrite: begin write the PurgeableMem obj. - * Return: return true if the obj's content is present. - * if content is purged(no present), system will recover its data, - * return false if content is purged and recover failed. - * While return true if content recover success. - * OS cannot reclaim the memory of the obj's content when this - * function return true, until EndWrite() is called. - */ - bool BeginWrite(); - - /* - * EndWrite: end write the PurgeableMem obj. - * OS may reclaim the memory of its content - * at a later time when this function returns. - */ - void EndWrite(); - /* - * GetContent: get content ptr of the PurgeableMem obj. - * Return: return the content ptr, which is start address of the obj's content. - * This function should be protected by BeginRead()/EndRead() - * or BeginWrite()/EndWrite(). - */ - void *GetContent(); - - /* - * GetContentSize: get content size of the PurgeableMem obj. - * Return: return content size of the obj's content. - */ - size_t GetContentSize(); - - /* - * ModifyContentByBuilder: append a PurgeableMemBuilder obj to the PurgeableMem obj. - * Input: @modifier: unique_ptr of PurgeableMemBuilder, it will modify content of this obj. - * Return: modify result, true is success, while false is fail. - * This function should be protected by BeginWrite()/EndWrite(). - */ - bool ModifyContentByBuilder(std::unique_ptr modifier); - -private: - void *dataPtr_ = nullptr; - size_t dataSizeInput_; - std::unique_ptr builder_ = nullptr; +protected: std::unique_ptr pageTable_ = nullptr; - std::shared_mutex rwlock_; - unsigned int buildDataCount_; - - bool IsPurged_(); - bool BuildContent_(); - std::string ToString_() const; + bool Pin_() override; + bool Unpin_() override; + bool IsPurged_() override; + bool CreatePurgeableData_() override; + void AfterRebuildSucc_() override; + std::string ToString_() const override; }; } /* namespace PurgeableMem */ } /* namespace OHOS */ diff --git a/libpurgeablemem/cpp/include/purgeable_mem_base.h b/libpurgeablemem/cpp/include/purgeable_mem_base.h new file mode 100644 index 0000000000000000000000000000000000000000..2c2a8e0cd3f9b3943124ee058c65dca69f94a337 --- /dev/null +++ b/libpurgeablemem/cpp/include/purgeable_mem_base.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_MEM_BASE_H +#define OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_MEM_BASE_H + +#include /* unique_ptr */ +#include /* shared_mutex */ +#include + +#include "purgeable_mem_builder.h" +#include "ux_page_table.h" + +namespace OHOS { +namespace PurgeableMem { +class PurgeableMemBase { +public: + /* + * BeginRead: begin read the PurgeableMem obj. + * Return: return true if the obj's content is present. + * If content is purged(no present), system will recover its data, + * return false if content is purged and recover failed. + * While return true if content recover success. + * OS cannot reclaim the memory of the obj's content when this + * function return true, until EndRead() is called. + */ + bool BeginRead(); + + /* + * EndRead: end read the PurgeableMem obj. + * OS may reclaim the memory of its content + * at a later time when this function returns. + */ + void EndRead(); + + /* + * BeginRead: begin read the PurgeableMem obj. + * Return: return true if the obj's content is present. + * If content is purged(no present), system will recover its data, + * return false if content is purged and recover failed. + * While return true if content recover success. + * OS cannot reclaim the memory of the obj's content when this + * function return true, until EndRead() is called. + */ + + bool BeginWrite(); + + /* + * EndWrite: end write the PurgeableMem obj. + * OS may reclaim the memory of its content + * at a later time when this function returns. + */ + void EndWrite(); + + /* + * ModifyContentByBuilder: append a PurgeableMemBuilder obj to the PurgeableMem obj. + * Input: @modifier: unique_ptr of PurgeableMemBuilder, it will modify content of this obj. + * Return: modify result, true is success, while false is fail. + * This function should be protected by BeginWrite()/EndWrite(). + */ + bool ModifyContentByBuilder(std::unique_ptr modifier); + + /* + * GetContent: get content ptr of the PurgeableMem obj. + * Return: return the content ptr, which is start address of the obj's content. + * This function should be protected by BeginRead()/EndRead() + * or BeginWrite()/EndWrite(). + */ + void *GetContent(); + + /* + * GetContentSize: get content size of the PurgeableMem obj. + * Return: return content size of the obj's content. + */ + size_t GetContentSize(); + + /* + * ResizeData: resize size of the PurgeableMem obj. + */ + virtual void ResizeData(size_t newSize); + + + PurgeableMemBase(); + virtual ~PurgeableMemBase(); + PurgeableMemBase(const PurgeableMemBase&) = delete; + PurgeableMemBase& operator = (PurgeableMemBase&) = delete; + PurgeableMemBase(PurgeableMemBase&&) noexcept = delete; + PurgeableMemBase& operator = (PurgeableMemBase&&) noexcept = delete; + +protected: + void *dataPtr_ = nullptr; + size_t dataSizeInput_; + std::unique_ptr builder_ = nullptr; + std::shared_mutex rwlock_; + unsigned int buildDataCount_; + bool BuildContent_(); + bool IfNeedRebuild_(); + virtual bool Pin_(); + virtual bool Unpin_(); + virtual bool IsPurged_(); + virtual bool CreatePurgeableData_(); + virtual void AfterRebuildSucc_(); + virtual std::string ToString_() const; +}; +} /* namespace PurgeableMem */ +} /* namespace OHOS */ +#endif /* OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_MEM_BASE_H */ diff --git a/libpurgeablemem/cpp/include/purgeable_mem_builder.h b/libpurgeablemem/cpp/include/purgeable_mem_builder.h index 7b09b4f560af1798c24857a8d258338211a48f77..9a3fbf004850e6f4c6936a30c1d49f7f2570851f 100644 --- a/libpurgeablemem/cpp/include/purgeable_mem_builder.h +++ b/libpurgeablemem/cpp/include/purgeable_mem_builder.h @@ -43,7 +43,7 @@ private: /* Only called by its friend */ void AppendBuilder(std::unique_ptr builder); bool BuildAll(void *data, size_t size); - friend class PurgeableMem; + friend class PurgeableMemBase; }; } /* namespace PurgeableMem */ } /* namespace OHOS */ diff --git a/libpurgeablemem/cpp/include/ux_page_table.h b/libpurgeablemem/cpp/include/ux_page_table.h index a0481be1b5cc3fe424f579bb5709ed747e4b3bff..d25985ade2bb939670ded0ce157ca478d0952c06 100644 --- a/libpurgeablemem/cpp/include/ux_page_table.h +++ b/libpurgeablemem/cpp/include/ux_page_table.h @@ -18,7 +18,7 @@ #include /* std::string */ -#include "../../common/include/ux_page_table_c.h" /* UxPageTableStruct */ +#include "ux_page_table_c.h" /* UxPageTableStruct */ namespace OHOS { namespace PurgeableMem { diff --git a/libpurgeablemem/cpp/src/purgeable_ashmem.cpp b/libpurgeablemem/cpp/src/purgeable_ashmem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8462ce287dd9d5f4a28609a53e38ea861fe466b6 --- /dev/null +++ b/libpurgeablemem/cpp/src/purgeable_ashmem.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include /* mmap */ + +#include "securec.h" +#include "pm_util.h" +#include "pm_state_c.h" +#include "pm_smartptr_util.h" +#include "purgeable_ashmem.h" +#include "pm_log.h" + +namespace OHOS { +namespace PurgeableMem { +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "PurgeableMem" + +static inline size_t RoundUp_(size_t val, size_t align) +{ + if (align == 0) { + return val; + } + return ((val + align - 1) / align) * align; +} + +PurgeableAshMem::PurgeableAshMem(std::unique_ptr builder) +{ + dataPtr_ = nullptr; + builder_ = nullptr; + ashmemFd_ = -1; + buildDataCount_ = 0; + isSupport_ = false; + IF_NULL_LOG_ACTION(builder, "%{public}s: input builder nullptr", return); + builder_ = std::move(builder); + PM_HILOG_DEBUG(LOG_CORE, "%{public}s init succ. %{public}s", __func__, ToString_().c_str()); +} + +PurgeableAshMem::PurgeableAshMem(size_t dataSize, std::unique_ptr builder) +{ + dataPtr_ = nullptr; + builder_ = nullptr; + ashmemFd_ = -1; + buildDataCount_ = 0; + isSupport_ = false; + if (dataSize == 0) { + return; + } + dataSizeInput_ = dataSize; + IF_NULL_LOG_ACTION(builder, "%{public}s: input builder nullptr", return); + + CreatePurgeableData_(); + builder_ = std::move(builder); + PM_HILOG_DEBUG(LOG_CORE, "%{public}s init succ. %{public}s", __func__, ToString_().c_str()); +} + +PurgeableAshMem::~PurgeableAshMem() +{ + PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); + if (dataPtr_) { + if (munmap(dataPtr_, RoundUp_(dataSizeInput_, PAGE_SIZE)) != 0) { + PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__); + } else { + if (UxpteIsEnabled() && !IsPurged_()) { + PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr succ, but uxpte present", __func__); + } + dataPtr_ = nullptr; + close(ashmemFd_); + } + } + builder_.reset(); +} + +int PurgeableAshMem::GetAshmemFd() +{ + return ashmemFd_; +} + +bool PurgeableAshMem::IsPurged_() +{ + if (!isSupport_) { + return false; + } + int ret = ioctl(ashmemFd_, PURGEABLE_ASHMEM_IS_PURGED); + PM_HILOG_DEBUG(LOG_CORE, "%{public}s: IsPurged_ %{public}d", __func__, ret); + return ret ? true : false; +} + +bool PurgeableAshMem::CreatePurgeableData_() +{ + PM_HILOG_DEBUG(LOG_CORE, "%{public}s", __func__); + if (dataSizeInput_ == 0) { + return false; + } + size_t size = RoundUp_(dataSizeInput_, PAGE_SIZE); + int fd = AshmemCreate("PurgeableAshmem", size); + if (fd < 0) { + return false; + } + if (AshmemSetProt(fd, PROT_READ | PROT_WRITE) < 0) { + close(fd); + return false; + } + ashmemFd_ = fd; + pin_ = { static_cast(0), static_cast(0) }; + dataPtr_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd_, 0); + if (dataPtr_ == MAP_FAILED) { + PM_HILOG_ERROR(LOG_CORE, "%{public}s: mmap fail", __func__); + dataPtr_ = nullptr; + close(ashmemFd_); + return false; + } + TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_SET_PURGEABLE)); + if (TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_GET_PURGEABLE)) == 1) { + isSupport_ = true; + } + Unpin_(); + return true; +} + +bool PurgeableAshMem::Pin_() +{ + if (!isSupport_) { + return true; + } + if (ashmemFd_ > 0) { + TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_PIN, &pin_)); + PM_HILOG_DEBUG(LOG_CORE, "%{public}s: fd:%{pubilc}d PURGEABLE_GET_PIN_STATE: %{public}d", + __func__, ashmemFd_, ioctl(ashmemFd_, ASHMEM_GET_PIN_STATUS, &pin_)); + } else { + PM_HILOG_DEBUG(LOG_CORE, "ashmemFd_ not exist!!"); + return false; + } + return true; +} + +bool PurgeableAshMem::Unpin_() +{ + if (!isSupport_) { + return true; + } + if (ashmemFd_ > 0) { + TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_UNPIN, &pin_)); + PM_HILOG_DEBUG(LOG_CORE, "%{public}s: fd:%{pubilc}d PURGEABLE_GET_PIN_STATE: %{public}d", + __func__, ashmemFd_, ioctl(ashmemFd_, ASHMEM_GET_PIN_STATUS, &pin_)); + } else { + PM_HILOG_DEBUG(LOG_CORE, "ashmemFd_ not exist!!"); + return false; + } + return true; +} + +void PurgeableAshMem::AfterRebuildSucc_() +{ + TEMP_FAILURE_RETRY(ioctl(ashmemFd_, PURGEABLE_ASHMEM_REBUILD_SUCCESS)); +} + +void PurgeableAshMem::ResizeData(size_t newSize) +{ + if (newSize <= 0) { + return; + } + if (dataPtr_) { + if (munmap(dataPtr_, RoundUp_(dataSizeInput_, PAGE_SIZE)) != 0) { + PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__); + } else { + dataPtr_ = nullptr; + if (ashmemFd_ > 0) { + close(ashmemFd_); + } + } + } + dataSizeInput_ = newSize; + CreatePurgeableData_(); +} + +void PurgeableAshMem::ChangeAshmemData(size_t size, int fd, void *data) +{ + if (dataPtr_) { + if (munmap(dataPtr_, RoundUp_(dataSizeInput_, PAGE_SIZE)) != 0) { + PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__); + } else { + dataPtr_ = nullptr; + if (ashmemFd_ > 0) { + close(ashmemFd_); + } + } + } + dataSizeInput_ = size; + ashmemFd_ = fd; + dataPtr_ = data; + buildDataCount_++; + TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_SET_PURGEABLE)); + if (TEMP_FAILURE_RETRY(ioctl(ashmemFd_, ASHMEM_GET_PURGEABLE)) == 1) { + isSupport_ = true; + } + Unpin_(); +} + +inline std::string PurgeableAshMem::ToString_() const +{ + return ""; +} +} /* namespace PurgeableMem */ +} /* namespace OHOS */ diff --git a/libpurgeablemem/cpp/src/purgeable_mem.cpp b/libpurgeablemem/cpp/src/purgeable_mem.cpp index 8a75613e441b9b6cd489a726d68e852fce4dd44b..2d9fcf276e9e44dc9ee4783618d18cb0b6b40187 100644 --- a/libpurgeablemem/cpp/src/purgeable_mem.cpp +++ b/libpurgeablemem/cpp/src/purgeable_mem.cpp @@ -16,10 +16,11 @@ #include /* mmap */ #include "securec.h" -#include "../../common/include/pm_util.h" -#include "../../common/include/pm_state_c.h" +#include "pm_util.h" +#include "pm_state_c.h" #include "pm_smartptr_util.h" #include "purgeable_mem.h" +#include "pm_log.h" namespace OHOS { namespace PurgeableMem { @@ -49,31 +50,20 @@ PurgeableMem::PurgeableMem(size_t dataSize, std::unique_ptr dataSizeInput_ = dataSize; IF_NULL_LOG_ACTION(builder, "%{public}s: input builder nullptr", return); - size_t size = RoundUp_(dataSizeInput_, PAGE_SIZE); - int type = MAP_ANONYMOUS; - type |= (UxpteIsEnabled() ? MAP_PURGEABLE : MAP_PRIVATE); - dataPtr_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, type, -1, 0); - if (dataPtr_ == MAP_FAILED) { - HILOG_ERROR(LOG_CORE, "%{public}s: mmap fail", __func__); - dataPtr_ = nullptr; - return; - } - + CreatePurgeableData_(); builder_ = std::move(builder); - - MAKE_UNIQUE(pageTable_, UxPageTable, "constructor uxpt make_unique fail", return, (uint64_t)dataPtr_, size); - HILOG_DEBUG(LOG_CORE, "%{public}s init succ. %{public}s", __func__, ToString_().c_str()); + PM_HILOG_DEBUG(LOG_CORE, "%{public}s init succ. %{public}s", __func__, ToString_().c_str()); } PurgeableMem::~PurgeableMem() { - HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); + PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); if (dataPtr_) { if (munmap(dataPtr_, RoundUp_(dataSizeInput_, PAGE_SIZE)) != 0) { - HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__); + PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__); } else { if (UxpteIsEnabled() && !IsPurged_()) { - HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr succ, but uxpte present", __func__); + PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr succ, but uxpte present", __func__); } dataPtr_ = nullptr; } @@ -82,160 +72,62 @@ PurgeableMem::~PurgeableMem() pageTable_.reset(); } -bool PurgeableMem::BeginRead() +bool PurgeableMem::IsPurged_() { - bool succ = false; - bool ret = false; - HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); - IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginRead", return false); - IF_NULL_LOG_ACTION(pageTable_, "pageTable_ is nullptr in BeginRead", return false); - IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginRead", return false); - pageTable_->GetUxpte((uint64_t)dataPtr_, dataSizeInput_); - PMState err = PM_OK; - while (true) { - try { - rwlock_.lock_shared(); - } catch (...) { - err = PM_LOCK_READ_FAIL; - break; - } - if (!IsPurged_()) { - HILOG_INFO(LOG_CORE, "%{public}s: not purged, return true. MAP_PUR=0x%{public}x", - __func__, MAP_PURGEABLE); - ret = true; - break; - } - /* data is purged, will rebuild it */ - rwlock_.unlock_shared(); - try { - rwlock_.lock(); - } catch (...) { - err = PM_LOCK_WRITE_FAIL; - break; - } - if (IsPurged_()) { - succ = BuildContent_(); - HILOG_INFO(LOG_CORE, "%{public}s: purged, built %{public}s", __func__, succ ? "succ" : "fail"); - } - rwlock_.unlock(); - if (!succ) { - err = PMB_BUILD_ALL_FAIL; - break; - } - } - - if (!ret) { - HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut.", __func__, GetPMStateName(err)); - pageTable_->PutUxpte((uint64_t)dataPtr_, dataSizeInput_); - } - return ret; + IF_NULL_LOG_ACTION(pageTable_, "pageTable_ is nullptrin BeginWrite", return false); + return !(pageTable_->CheckPresent((uint64_t)dataPtr_, dataSizeInput_)); } -void PurgeableMem::EndRead() +bool PurgeableMem::CreatePurgeableData_() { - HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); - IF_NULL_LOG_ACTION(pageTable_, "pageTable_ is nullptr in EndRead", return); - rwlock_.unlock_shared(); - pageTable_->PutUxpte((uint64_t)dataPtr_, dataSizeInput_); + PM_HILOG_DEBUG(LOG_CORE, "%{public}s", __func__); + pageTable_ = nullptr; + size_t size = RoundUp_(dataSizeInput_, PAGE_SIZE); + int type = MAP_ANONYMOUS; + type |= (UxpteIsEnabled() ? MAP_PURGEABLE : MAP_PRIVATE); + + dataPtr_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, type, -1, 0); + if (dataPtr_ == MAP_FAILED) { + PM_HILOG_ERROR(LOG_CORE, "%{public}s: mmap fail", __func__); + dataPtr_ = nullptr; + return false; + } + MAKE_UNIQUE(pageTable_, UxPageTable, "constructor uxpt make_unique fail", return false, (uint64_t)dataPtr_, size); + return true; } -bool PurgeableMem::BeginWrite() +bool PurgeableMem::Pin_() { - HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); - - IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginWrite", return false); IF_NULL_LOG_ACTION(pageTable_, "pageTable_ is nullptrin BeginWrite", return false); - IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginWrite", return false); - pageTable_->GetUxpte((uint64_t)dataPtr_, dataSizeInput_); - PMState err = PM_OK; - do { - try { - rwlock_.lock(); - } catch (...) { - err = PM_LOCK_WRITE_FAIL; - break; - } - if (!IsPurged_()) { - /* data is not purged, return true */ - break; - } - /* data purged, rebuild it */ - if (BuildContent_()) { - /* data rebuild succ, return true */ - break; - } - err = PMB_BUILD_ALL_FAIL; - } while (0); - - if (err == PM_OK) { - return true; - } - - rwlock_.unlock(); - HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut.", __func__, GetPMStateName(err)); - pageTable_->PutUxpte((uint64_t)dataPtr_, dataSizeInput_); - return false; + return true; } -void PurgeableMem::EndWrite() +bool PurgeableMem::Unpin_() { - HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); - IF_NULL_LOG_ACTION(pageTable_, "pageTable_ is nullptr in EndWrite", return); - rwlock_.unlock(); + IF_NULL_LOG_ACTION(pageTable_, "pageTable_ is nullptrin BeginWrite", return false); pageTable_->PutUxpte((uint64_t)dataPtr_, dataSizeInput_); -} - -void* PurgeableMem::GetContent() -{ - return dataPtr_; -} - -size_t PurgeableMem::GetContentSize() -{ - return dataSizeInput_; -} - -bool PurgeableMem::ModifyContentByBuilder(std::unique_ptr modifier) -{ - IF_NULL_LOG_ACTION(modifier, "input modifier is nullptr", return false); - /* apply modify */ - bool succ = modifier->Build(dataPtr_, dataSizeInput_); - if (!succ) { - return false; - } - /* log modify */ - if (builder_) { - builder_->AppendBuilder(std::move(modifier)); - } else { - builder_ = std::move(modifier); - } return true; } -bool PurgeableMem::IsPurged_() +void PurgeableMem::AfterRebuildSucc_() { - if (buildDataCount_ == 0) { - HILOG_INFO(LOG_CORE, "%{public}s, has never built, return true", __func__); - return true; - } - return !(pageTable_->CheckPresent((uint64_t)dataPtr_, dataSizeInput_)); } -bool PurgeableMem::BuildContent_() +void PurgeableMem::ResizeData(size_t newSize) { - bool succ = false; - /* clear content before rebuild */ - if (memset_s(dataPtr_, RoundUp_(dataSizeInput_, PAGE_SIZE), 0, dataSizeInput_) != EOK) { - HILOG_ERROR(LOG_CORE, "%{public}s, clear content fail", __func__); - return succ; + if (newSize <= 0) { + return; } - /* builder_ and dataPtr_ is never nullptr since it is checked by BeginAccess() before */ - succ = builder_->BuildAll(dataPtr_, dataSizeInput_); - if (succ) { - buildDataCount_++; + if (dataPtr_) { + if (munmap(dataPtr_, RoundUp_(dataSizeInput_, PAGE_SIZE)) != 0) { + PM_HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__); + } else { + dataPtr_ = nullptr; + } } - return succ; + dataSizeInput_ = newSize; + CreatePurgeableData_(); } inline std::string PurgeableMem::ToString_() const diff --git a/libpurgeablemem/cpp/src/purgeable_mem_base.cpp b/libpurgeablemem/cpp/src/purgeable_mem_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e6d541d941509e10303badf9d7334f23857f087 --- /dev/null +++ b/libpurgeablemem/cpp/src/purgeable_mem_base.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include /* mmap */ + +#include "securec.h" +#include "pm_util.h" +#include "pm_state_c.h" +#include "pm_smartptr_util.h" +#include "purgeable_mem_base.h" +#include "pm_log.h" + +namespace OHOS { +namespace PurgeableMem { +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "PurgeableMem" + +static inline size_t RoundUp_(size_t val, size_t align) +{ + if (align == 0) { + return val; + } + return ((val + align - 1) / align) * align; +} + +PurgeableMemBase::PurgeableMemBase() +{ +} + +PurgeableMemBase::~PurgeableMemBase() +{ +} + +bool PurgeableMemBase::BeginRead() +{ + bool succ = false; + bool ret = false; + + PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); + IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginRead", return false); + IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginRead", return false); + Pin_(); + PMState err = PM_OK; + while (true) { + try { + rwlock_.lock_shared(); + } catch (...) { + err = PM_LOCK_READ_FAIL; + break; + } + if (!IfNeedRebuild_()) { + PM_HILOG_DEBUG(LOG_CORE, "%{public}s: not purged, return true. MAP_PUR=0x%{public}x", + __func__, MAP_PURGEABLE); + ret = true; + break; + } + /* data is purged, will rebuild it */ + rwlock_.unlock_shared(); + try { + rwlock_.lock(); + } catch (...) { + err = PM_LOCK_WRITE_FAIL; + break; + } + if (IfNeedRebuild_()) { + succ = BuildContent_(); + if (succ) { + AfterRebuildSucc_(); + } + PM_HILOG_DEBUG(LOG_CORE, "%{public}s: purged, built %{public}s", __func__, succ ? "succ" : "fail"); + } + rwlock_.unlock(); + if (!succ) { + err = PMB_BUILD_ALL_FAIL; + break; + } + } + + if (!ret) { + PM_HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut.", __func__, GetPMStateName(err)); + Unpin_(); + } + return ret; +} + +void PurgeableMemBase::EndRead() +{ + PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); + rwlock_.unlock_shared(); + Unpin_(); +} + +bool PurgeableMemBase::BeginWrite() +{ + PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); + if (dataPtr_ == nullptr && !CreatePurgeableData_()) { + return false; + } + IF_NULL_LOG_ACTION(dataPtr_, "dataPtr is nullptr in BeginWrite", return false); + IF_NULL_LOG_ACTION(builder_, "builder_ is nullptr in BeginWrite", return false); + + Pin_(); + PMState err = PM_OK; + do { + try { + rwlock_.lock(); + } catch (...) { + err = PM_LOCK_WRITE_FAIL; + break; + } + if (!IfNeedRebuild_()) { + /* data is not purged, return true */ + break; + } + /* data purged, rebuild it */ + if (BuildContent_()) { + /* data rebuild succ, return true */ + AfterRebuildSucc_(); + break; + } + err = PMB_BUILD_ALL_FAIL; + } while (0); + + if (err == PM_OK) { + return true; + } + + rwlock_.unlock(); + PM_HILOG_ERROR(LOG_CORE, "%{public}s: err %{public}s, UxptePut.", __func__, GetPMStateName(err)); + Unpin_(); + return false; +} + +void PurgeableMemBase::EndWrite() +{ + PM_HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); + rwlock_.unlock(); + Unpin_(); +} + +bool PurgeableMemBase::ModifyContentByBuilder(std::unique_ptr modifier) +{ + IF_NULL_LOG_ACTION(modifier, "input modifier is nullptr", return false); + if (!modifier->Build(dataPtr_, dataSizeInput_)) { + PM_HILOG_ERROR(LOG_CORE, "%{public}s: modify content by builder fail!!", __func__); + return false; + } + /* log modify */ + if (builder_) { + builder_->AppendBuilder(std::move(modifier)); + } else { + builder_ = std::move(modifier); + } + return true; +} + +bool PurgeableMemBase::IfNeedRebuild_() +{ + if (buildDataCount_ == 0 || IsPurged_()) { + return true; + } + return false; +} + +void PurgeableMemBase::AfterRebuildSucc_() +{ +} + +void *PurgeableMemBase::GetContent() +{ + return dataPtr_; +} + +size_t PurgeableMemBase::GetContentSize() +{ + return dataSizeInput_; +} + +bool PurgeableMemBase::IsPurged_() +{ + return false; +} + +bool PurgeableMemBase::BuildContent_() +{ + bool succ = false; + /* clear content before rebuild */ + if (memset_s(dataPtr_, RoundUp_(dataSizeInput_, PAGE_SIZE), 0, dataSizeInput_) != EOK) { + PM_HILOG_ERROR(LOG_CORE, "%{public}s, clear content fail", __func__); + return succ; + } + /* builder_ and dataPtr_ is never nullptr since it is checked by BeginAccess() before */ + succ = builder_->BuildAll(dataPtr_, dataSizeInput_); + if (succ) { + buildDataCount_++; + } + return succ; +} + +bool PurgeableMemBase::CreatePurgeableData_() +{ + return false; +} + +void PurgeableMemBase::ResizeData(size_t newSize) +{ +} + +bool PurgeableMemBase::Pin_() +{ + return false; +} + +bool PurgeableMemBase::Unpin_() +{ + return false; +} + +inline std::string PurgeableMemBase::ToString_() const +{ + return ""; +} +} /* namespace PurgeableMem */ +} /* namespace OHOS */ diff --git a/libpurgeablemem/test/BUILD.gn b/libpurgeablemem/test/BUILD.gn index 5bc7e9d87a5ca4357ac1b8e59a64964c92b9307a..19b8474619d85e790fb91148a061cfbe417d2b9b 100644 --- a/libpurgeablemem/test/BUILD.gn +++ b/libpurgeablemem/test/BUILD.gn @@ -22,7 +22,10 @@ purgeable_deps = [ "//commonlibrary/memory_utils/libpurgeablemem:libpurgeablemem", ] -purgeable_external_deps = [ "hiviewdfx_hilog_native:libhilog" ] +purgeable_external_deps = [ + "c_utils:utils", + "hiviewdfx_hilog_native:libhilog", +] purgeable_public_deps = [ "//third_party/googletest:gtest_main" ] @@ -52,6 +55,19 @@ ohos_unittest("purgeable_cpp_test") { part_name = "memory_utils" } +ohos_unittest("purgeableashmem_test") { + module_out_path = module_output_path + sources = [ "purgeableashmem_test.cpp" ] + deps = purgeable_deps + if (is_standard_system) { + external_deps = purgeable_external_deps + public_deps = purgeable_public_deps + } + + subsystem_name = "commonlibrary" + part_name = "memory_utils" +} + group("libpurgeablemem_test") { testonly = true deps = [ diff --git a/libpurgeablemem/test/purgeableashmem_test.cpp b/libpurgeablemem/test/purgeableashmem_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1340ed1ceede11e8d7b08a3e74e35ed57b1e66d0 --- /dev/null +++ b/libpurgeablemem/test/purgeableashmem_test.cpp @@ -0,0 +1,609 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ashmem.h" +#include "gtest/gtest.h" +#include "purgeable_ashmem.h" +#include "securec.h" + +namespace OHOS { +namespace PurgeableMem { +using namespace testing; +using namespace testing::ext; + +static constexpr int PRINT_INTERVAL_SECONDS = 1; +static constexpr int RECLAIM_INTERVAL_SECONDS = 1; +static constexpr int MODIFY_INTERVAL_SECONDS = 2; +void LoopPrintAlphabet(PurgeableAshMem *pdata, unsigned int loopCount); +bool ReclaimPurgeable(void); +void LoopReclaimPurgeable(unsigned int loopCount); +void ModifyPurgMemByBuilder(PurgeableAshMem *pdata, std::unique_ptr mod); + +class TestDataBuilder : public PurgeableMemBuilder { +public: + TestDataBuilder(char start, char end) + { + this->start = start; + this->end = end; + } + + bool Build(void *data, size_t size) + { + if (size <= 0) { + return true; + } + char *str = static_cast(data); + size_t len = 0; + for (char ch = start; ch <= end && len < size; ch++) { + str[len++] = ch; + } + str[size - 1] = 0; + std::cout << "rebuild addr("<< (unsigned long long)str <<") " << + start << "~" << end << ", data=[" << str << "]" << std::endl; + return true; + } + + ~TestDataBuilder() + { + std::cout << "~TestDataBuilder" << std::endl; + } + +private: + char start, end; +}; + +class TestDataModifier : public PurgeableMemBuilder { +public: + TestDataModifier(char from, char to) + { + this->from = from; + this->to = to; + } + + bool Build(void *data, size_t size) + { + char *str = static_cast(data); + for (size_t i = 0; i < size && str[i]; i++) { + if (str[i] == from) { + str[i] = to; + } + } + return true; + } + + ~TestDataModifier() + { + std::cout << "~TestDataModifier" << std::endl; + } + +private: + char from, to; +}; + +class TestBigDataBuilder : public PurgeableMemBuilder { +public: + explicit TestBigDataBuilder(char target) + { + this->target = target; + } + + bool Build(void *data, size_t size) + { + if (size <= 0) { + return true; + } + char *str = static_cast(data); + size_t len = 0; + for (char ch = target; len < size;) { + str[len++] = ch; + } + str[size - 1] = 0; + return true; + } + + ~TestBigDataBuilder() + { + std::cout << "~TestBigDataBuilder" << std::endl; + } + +private: + char target; +}; + +class PurgeableAshmemTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void PurgeableAshmemTest::SetUpTestCase() +{ +} + +void PurgeableAshmemTest::TearDownTestCase() +{ +} + +void PurgeableAshmemTest::SetUp() +{ +} + +void PurgeableAshmemTest::TearDown() +{ +} + +HWTEST_F(PurgeableAshmemTest, KernelInterfaceTest, TestSize.Level1) +{ + size_t size = 4096 * 100; + int fd = AshmemCreate("Purgeable Ashmem", size); + EXPECT_LT(fd, 0); + if (AshmemSetProt(fd, PROT_READ | PROT_WRITE) < 0) { + close(fd); + return; + } + void *dataPtr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (dataPtr == MAP_FAILED) { + dataPtr = nullptr; + close(fd); + return; + } + char *str = static_cast(dataPtr); + for (size_t i = 0; i < size; i++) { + str[i] = 'a'; + } + str[size - 1] = '\0'; + ashmem_pin pin_ = { static_cast(0), static_cast(0) }; + EXPECT_EQ(ioctl(fd, ASHMEM_GET_PURGEABLE), -1); + EXPECT_EQ(ioctl(fd, ASHMEM_SET_PURGEABLE), 0); + EXPECT_EQ(ioctl(fd, ASHMEM_GET_PURGEABLE), 1); + EXPECT_EQ(ioctl(fd, ASHMEM_GET_PIN_STATUS, &pin_), 1); + ioctl(fd, ASHMEM_PIN, &pin_); + EXPECT_EQ(ioctl(fd, ASHMEM_GET_PIN_STATUS, &pin_), 2); + ioctl(fd, ASHMEM_PIN, &pin_); + EXPECT_EQ(ioctl(fd, ASHMEM_GET_PIN_STATUS, &pin_), 3); + ioctl(fd, ASHMEM_UNPIN, &pin_); + EXPECT_EQ(ioctl(fd, ASHMEM_GET_PIN_STATUS, &pin_), 2); + ioctl(fd, ASHMEM_UNPIN, &pin_); + EXPECT_EQ(ioctl(fd, ASHMEM_GET_PIN_STATUS, &pin_), 1); + EXPECT_EQ(ioctl(fd, ASHMEM_PURGE_ALL_CACHES), 0); + EXPECT_EQ(ioctl(fd, PURGEABLE_ASHMEM_IS_PURGED), 0); + ioctl(fd, ASHMEM_UNPIN, &pin_); + EXPECT_EQ(ioctl(fd, ASHMEM_GET_PIN_STATUS, &pin_), 0); + EXPECT_EQ(ioctl(fd, PURGEABLE_ASHMEM_IS_PURGED), 0); + ioctl(fd, ASHMEM_PURGE_ALL_CACHES); + EXPECT_EQ(ioctl(fd, PURGEABLE_ASHMEM_IS_PURGED), 1); + ioctl(fd, ASHMEM_PIN, &pin_); + EXPECT_EQ(ioctl(fd, ASHMEM_GET_PIN_STATUS, &pin_), 1); + EXPECT_EQ(ioctl(fd, PURGEABLE_ASHMEM_IS_PURGED), 1); + ioctl(fd, ASHMEM_UNPIN, &pin_); + EXPECT_EQ(ioctl(fd, ASHMEM_GET_PIN_STATUS, &pin_), 0); + ioctl(fd, PURGEABLE_ASHMEM_REBUILD_SUCCESS); + EXPECT_EQ(ioctl(fd, PURGEABLE_ASHMEM_IS_PURGED), 0); +} + +HWTEST_F(PurgeableAshmemTest, MultiObjCreateTest, TestSize.Level1) +{ + const char alphabetFinal[] = "BBCDEFGHIJKLMNOPQRSTUVWXYZ\0"; + std::unique_ptr builder1 = std::make_unique('A', 'Z'); + std::unique_ptr builder2 = std::make_unique('A', 'Z'); + std::unique_ptr mod1 = std::make_unique('A', 'B'); + std::unique_ptr mod2 = std::make_unique('A', 'B'); + + PurgeableAshMem pobj1(27, std::move(builder1)); + LoopPrintAlphabet(&pobj1, 1); + ModifyPurgMemByBuilder(&pobj1, std::move(mod1)); + LoopPrintAlphabet(&pobj1, 1); + LoopReclaimPurgeable(1); + + PurgeableAshMem pobj2(27, std::move(builder2)); + LoopPrintAlphabet(&pobj2, 1); + ModifyPurgMemByBuilder(&pobj2, std::move(mod2)); + LoopPrintAlphabet(&pobj2, 1); + LoopReclaimPurgeable(1); + + int ret1 = 1; + int ret2 = 1; + int times1 = 0; + int times2 = 0; + while (times1++ < 10) { + if (pobj1.BeginRead()) { + ret1 = strncmp(alphabetFinal, static_cast(pobj1.GetContent()), 26); + pobj1.EndRead(); + break; + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + } + + while (times2++ < 10) { + if (pobj2.BeginRead()) { + ret2 = strncmp(alphabetFinal, static_cast(pobj2.GetContent()), 26); + pobj2.EndRead(); + break; + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + } + + EXPECT_EQ(ret1, 0); + EXPECT_EQ(ret2, 0); +} + +HWTEST_F(PurgeableAshmemTest, ReadTest, TestSize.Level1) +{ + const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\0"; + std::unique_ptr builder = std::make_unique('A', 'Z'); + PurgeableAshMem *pobj = new (std::nothrow) PurgeableAshMem(27, std::move(builder)); + EXPECT_EQ(pobj, nullptr); + LoopReclaimPurgeable(1); + + int times = 0; + int ret = 1; + while (times++ < 10) { + if (pobj->BeginRead()) { + ret = strncmp(alphabet, static_cast(pobj->GetContent()), 26); + pobj->EndRead(); + break; + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + } + delete pobj; + pobj = nullptr; + EXPECT_EQ(ret, 0); +} + +HWTEST_F(PurgeableAshmemTest, WriteTest, TestSize.Level1) +{ + const char alphabet[] = "CCCDEFGHIJKLMNOPQRSTUVWXYZ\0"; + std::unique_ptr builder = std::make_unique('A', 'Z'); + PurgeableAshMem *pobj = new (std::nothrow) PurgeableAshMem(27, std::move(builder)); + EXPECT_EQ(pobj, nullptr); + LoopReclaimPurgeable(1); + + std::unique_ptr modA2B = std::make_unique('A', 'B'); + std::unique_ptr modB2C = std::make_unique('B', 'C'); + ModifyPurgMemByBuilder(pobj, std::move(modA2B)); + ModifyPurgMemByBuilder(pobj, std::move(modB2C)); + + int times = 0; + int ret = 1; + while (times++ < 10) { + if (pobj->BeginRead()) { + ret = strncmp(alphabet, static_cast(pobj->GetContent()), 26); + pobj->EndRead(); + break; + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + } + delete pobj; + pobj = nullptr; + EXPECT_EQ(ret, 0); +} + +HWTEST_F(PurgeableAshmemTest, ReadWriteTest, TestSize.Level1) +{ + const char alphabet[] = "DDDDEFGHIJKLMNOPQRSTUVWXYZ\0"; + std::unique_ptr builder = std::make_unique('A', 'Z'); + PurgeableAshMem *pobj = new (std::nothrow) PurgeableAshMem(27, std::move(builder)); + EXPECT_EQ(pobj, nullptr); + + LoopReclaimPurgeable(1); + LoopPrintAlphabet(pobj, 1); + + std::unique_ptr modA2B = std::make_unique('A', 'B'); + std::unique_ptr modB2C = std::make_unique('B', 'C'); + std::unique_ptr modC2D = std::make_unique('C', 'D'); + ModifyPurgMemByBuilder(pobj, std::move(modA2B)); + ModifyPurgMemByBuilder(pobj, std::move(modB2C)); + ModifyPurgMemByBuilder(pobj, std::move(modC2D)); + + int times = 0; + int ret = 1; + while (times++ < 10) { + if (pobj->BeginRead()) { + ret = strncmp(alphabet, static_cast(pobj->GetContent()), 26); + pobj->EndRead(); + break; + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + } + delete pobj; + pobj = nullptr; + EXPECT_EQ(ret, 0); +} + +HWTEST_F(PurgeableAshmemTest, MutiPageReadTest, TestSize.Level1) +{ + char alphabet[4098]; + size_t len = 0; + for (char ch = 'A'; len < 4098;) { + alphabet[len++] = ch; + } + alphabet[4097] = 0; + std::unique_ptr builder = std::make_unique('A'); + PurgeableAshMem *pobj = new (std::nothrow) PurgeableAshMem(4098, std::move(builder)); + EXPECT_EQ(pobj, nullptr); + + LoopReclaimPurgeable(1); + + int times = 0; + int ret = 1; + while (times++ < 10) { + if (pobj->BeginRead()) { + ret = strncmp(alphabet, static_cast(pobj->GetContent()), 4097); + pobj->EndRead(); + break; + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + } + delete pobj; + pobj = nullptr; + EXPECT_EQ(ret, 0); +} + +HWTEST_F(PurgeableAshmemTest, MutiPageWriteTest, TestSize.Level1) +{ + char alphabet[4098]; + size_t len = 0; + for (char ch = 'C'; len < 4098;) { + alphabet[len++] = ch; + } + alphabet[4097] = 0; + std::unique_ptr builder = std::make_unique('A'); + PurgeableAshMem *pobj = new (std::nothrow) PurgeableAshMem(4098, std::move(builder)); + EXPECT_EQ(pobj, nullptr); + + LoopReclaimPurgeable(1); + + std::unique_ptr modA2B = std::make_unique('A', 'B'); + std::unique_ptr modB2C = std::make_unique('B', 'C'); + ModifyPurgMemByBuilder(pobj, std::move(modA2B)); + ModifyPurgMemByBuilder(pobj, std::move(modB2C)); + + int times = 0; + int ret = 1; + while (times++ < 10) { + if (pobj->BeginRead()) { + ret = strncmp(alphabet, static_cast(pobj->GetContent()), 4097); + pobj->EndRead(); + break; + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + } + delete pobj; + pobj = nullptr; + EXPECT_EQ(ret, 0); +} + +HWTEST_F(PurgeableAshmemTest, MutiPageReadWriteTest, TestSize.Level1) +{ + char alphabet[4098]; + size_t len = 0; + for (char ch = 'D'; len < 4098;) { + alphabet[len++] = ch; + } + alphabet[4097] = 0; + std::unique_ptr builder = std::make_unique('A'); + PurgeableAshMem *pobj = new (std::nothrow) PurgeableAshMem(4098, std::move(builder)); + EXPECT_EQ(pobj, nullptr); + LoopReclaimPurgeable(1); + LoopPrintAlphabet(pobj, 1); + + std::unique_ptr modA2B = std::make_unique('A', 'B'); + std::unique_ptr modB2C = std::make_unique('B', 'C'); + std::unique_ptr modC2D = std::make_unique('C', 'D'); + ModifyPurgMemByBuilder(pobj, std::move(modA2B)); + ModifyPurgMemByBuilder(pobj, std::move(modB2C)); + ModifyPurgMemByBuilder(pobj, std::move(modC2D)); + + int times = 0; + int ret = 1; + while (times++ < 10) { + if (pobj->BeginRead()) { + ret = strncmp(alphabet, static_cast(pobj->GetContent()), 4097); + pobj->EndRead(); + break; + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + } + delete pobj; + pobj = nullptr; + EXPECT_EQ(ret, 0); +} + +HWTEST_F(PurgeableAshmemTest, MutiMorePageReadWriteTest, TestSize.Level1) +{ + size_t size = 5 * 1024 * 1024; + char *alphabet = (char *)malloc(size); + size_t len = 0; + for (char ch = 'D'; len < size;) { + alphabet[len++] = ch; + } + alphabet[size - 1] = 0; + std::unique_ptr builder = std::make_unique('A'); + PurgeableAshMem *pobj = new (std::nothrow) PurgeableAshMem(4098, std::move(builder)); + EXPECT_EQ(pobj, nullptr); + + LoopReclaimPurgeable(1); + LoopPrintAlphabet(pobj, 1); + + std::unique_ptr modA2B = std::make_unique('A', 'B'); + std::unique_ptr modB2C = std::make_unique('B', 'C'); + std::unique_ptr modC2D = std::make_unique('C', 'D'); + ModifyPurgMemByBuilder(pobj, std::move(modA2B)); + ModifyPurgMemByBuilder(pobj, std::move(modB2C)); + ModifyPurgMemByBuilder(pobj, std::move(modC2D)); + + int times = 0; + int ret = 1; + while (times++ < 10) { + if (pobj->BeginRead()) { + ret = strncmp(alphabet, static_cast(pobj->GetContent()), size - 1); + pobj->EndRead(); + break; + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + } + delete pobj; + pobj = nullptr; + free(alphabet); + alphabet = nullptr; + EXPECT_EQ(ret, 0); +} + +HWTEST_F(PurgeableAshmemTest, StableMutiMorePageReadWriteTest, TestSize.Level1) +{ + size_t size = 5 * 1024 * 1024; + char *alphabet = (char *)malloc(size); + size_t len = 0; + for (char ch = 'D'; len < size;) { + alphabet[len++] = ch; + } + alphabet[size - 1] = 0; + std::unique_ptr builder = std::make_unique('A'); + PurgeableAshMem *pobj = new (std::nothrow) PurgeableAshMem(size, std::move(builder)); + EXPECT_EQ(pobj, nullptr); + + std::thread reclaimThread(LoopReclaimPurgeable, 10); + std::thread readThread(LoopPrintAlphabet, pobj, 10); + + std::unique_ptr modA2B = std::make_unique('A', 'B'); + std::unique_ptr modB2C = std::make_unique('B', 'C'); + std::unique_ptr modC2D = std::make_unique('C', 'D'); + ModifyPurgMemByBuilder(pobj, std::move(modA2B)); + ModifyPurgMemByBuilder(pobj, std::move(modB2C)); + ModifyPurgMemByBuilder(pobj, std::move(modC2D)); + + int times = 0; + int ret = 1; + while (times++ < 10) { + if (pobj->BeginRead()) { + ret = strncmp(alphabet, static_cast(pobj->GetContent()), size - 1); + pobj->EndRead(); + break; + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + } + reclaimThread.join(); + readThread.join(); + delete pobj; + pobj = nullptr; + free(alphabet); + alphabet = nullptr; + EXPECT_EQ(ret, 0); +} + +HWTEST_F(PurgeableAshmemTest, InvalidInputSizeTest, TestSize.Level1) +{ + std::unique_ptr builder = std::make_unique('A', 'Z'); + PurgeableAshMem *pobj = new (std::nothrow) PurgeableAshMem(0, std::move(builder)); + EXPECT_EQ(pobj, nullptr); + bool ret = pobj->BeginRead(); + if (ret) { + pobj->EndRead(); + } + delete pobj; + pobj = nullptr; + EXPECT_EQ(ret, false); +} + +HWTEST_F(PurgeableAshmemTest, InvalidInputBuilderTest, TestSize.Level1) +{ + PurgeableAshMem *pobj = new (std::nothrow) PurgeableAshMem(27, std::move(builder)); + EXPECT_EQ(pobj, nullptr); + bool ret = pobj->BeginRead(); + if (ret) { + pobj->EndRead(); + } + delete pobj; + pobj = nullptr; + EXPECT_EQ(ret, false); +} + +void LoopPrintAlphabet(PurgeableAshMem *pdata, unsigned int loopCount) +{ + std::cout << "inter " << __func__ << std::endl; + for (unsigned int i = 0; i < loopCount; i++) { + if (!pdata->BeginRead()) { + std::cout << __func__ << ": " << i << ". ERROR! BeginRead failed." << std::endl; + break; + } + pdata->EndRead(); + std::this_thread::sleep_for(std::chrono::seconds(PRINT_INTERVAL_SECONDS)); + } + std::cout << "quit " << __func__ << std::endl; +} + +bool ReclaimPurgeable(void) +{ + FILE *f = fopen("/proc/sys/vm/drop_caches", "w"); + if (!f) { + std::cout << __func__ << ": kernel not support" << std::endl; + return false; + } + bool succ = true; + if (fputs("3", f) == EOF) { + succ = false; + } + + if (fclose(f) == EOF) { + std::cout << __func__ << ": close file failed" << std::endl; + } + + return succ; +} + +void LoopReclaimPurgeable(unsigned int loopCount) +{ + bool ret = false; + std::cout << "inter " << __func__ << std::endl; + for (unsigned int i = 0; i < loopCount; i++) { + ret = ReclaimPurgeable(); + std::cout << __func__ << ": " << i << ". Reclaim result=" << (ret ? "succ" : "fail") << std::endl; + std::this_thread::sleep_for(std::chrono::seconds(RECLAIM_INTERVAL_SECONDS)); /* wait reclaim finish */ + } + std::cout << "quit " << __func__ << std::endl; +} + +void ModifyPurgMemByBuilder(PurgeableAshMem *pdata, std::unique_ptr mod) +{ + if (!pdata->BeginWrite()) { + std::cout << __func__ << ": ERROR! BeginWrite failed." << std::endl; + return; + } + std::this_thread::sleep_for(std::chrono::seconds(MODIFY_INTERVAL_SECONDS)); + pdata->ModifyContentByBuilder(std::move(mod)); + pdata->EndWrite(); +} +} /* namespace PurgeableAshMem */ +} /* namespace OHOS */