diff --git a/libpurgeablemem/BUILD.gn b/libpurgeablemem/BUILD.gn index 27330c8de234c359ec4b41afb4aeed3f7e1f4ff9..38bdf253084dbc7ebe841ac405591b5713002c4f 100644 --- a/libpurgeablemem/BUILD.gn +++ b/libpurgeablemem/BUILD.gn @@ -17,6 +17,7 @@ config("libpurgeable_config") { include_dirs = [ "c/include", "common/include", + "cpp/include", ] cflags_cc = [ "-fexceptions" ] } @@ -26,6 +27,9 @@ ohos_shared_library("libpurgeablemem") { "c/src/purgeable_mem_builder_c.c", "c/src/purgeable_mem_c.c", "common/src/ux_page_table_c.c", + "cpp/src/purgeable_mem.cpp", + "cpp/src/purgeable_mem_builder.cpp", + "cpp/src/ux_page_table.cpp", ] include_dirs = [ "include" ] deps = [ "//utils/native/base:utils" ] diff --git a/libpurgeablemem/c/include/purgeable_mem_c.h b/libpurgeablemem/c/include/purgeable_mem_c.h index 24468451efb3cbe1abbffb7550f302b698cf8e22..14268c3545398a337f6d8a1c7c070166a461c5cc 100644 --- a/libpurgeablemem/c/include/purgeable_mem_c.h +++ b/libpurgeablemem/c/include/purgeable_mem_c.h @@ -58,10 +58,11 @@ bool PurgMemDestroy(struct PurgMem *purgObj); * PurgMemBeginRead: begin read a PurgMem obj. * Input: @purgObj: a PurgMem obj. * Return: return true if @purgObj's content is present. - * if content is purged, system will recover its data, + * If content is purged(no present), system will recover its data, * return false if content is purged and recover failed. - * OS cannot reclaim the memory of @purgObj's content - * when this function return true until PurgMemEndRead() is called. + * While return true if content recover success. + * OS cannot reclaim the memory of @purgObj's content when this + * function return true, until PurgMemEndRead() is called. */ bool PurgMemBeginRead(struct PurgMem *purgObj); @@ -74,13 +75,14 @@ bool PurgMemBeginRead(struct PurgMem *purgObj); void PurgMemEndRead(struct PurgMem *purgObj); /* - * PurgMemBeginWrite: begin read a PurgMem obj. + * PurgMemBeginWrite: begin write a PurgMem obj. * Input: @purgObj: a PurgMem obj. * Return: return true if @purgObj's content is present. - * if content is purged, system will recover its data, + * if content is purged(no present), system will recover its data, * return false if content is purged and recover failed. - * OS cannot reclaim the memory of @purgObj's content - * when this function return true until PurgMemEndWrite() is called. + * While return true if content recover success. + * OS cannot reclaim the memory of @purgObj's content when this + * function return true, until PurgMemEndWrite() is called. */ bool PurgMemBeginWrite(struct PurgMem *purgObj); @@ -97,7 +99,7 @@ void PurgMemEndWrite(struct PurgMem *purgObj); * Input: @purgObj: a PurgMem obj. * Return: return start address of a PurgMem obj's content. * Return NULL if @purgObj is NULL. - * This function should be protect by PurgMemEndRead()/PurgMemEndRead() + * This function should be protect by PurgMemBeginRead()/PurgMemEndRead() * or PurgMemBeginWrite()/PurgMemEndWrite() */ void *PurgMemGetContent(struct PurgMem *purgObj); @@ -114,7 +116,7 @@ size_t PurgMemGetContentSize(struct PurgMem *purgObj); * PurgMemAppendModify: append a modify to a PurgMem obj. * Input: @purgObj: a PurgMem obj. * Input: @size: data size of a PurgMem obj's content. - * Input: @func: function pointer, it modifies content of @PurgMem. + * Input: @func: function pointer, it will modify content of @PurgMem. * Input: @funcPara: parameters used by @func. * Return: append result, true is success, while false is fail. */ diff --git a/libpurgeablemem/common/include/ux_page_table_c.h b/libpurgeablemem/common/include/ux_page_table_c.h index e98e15a4e45b40ce92813fb240a7faab9073dc36..5a5a21a124089aa9bcab266e2cd89dca0bcbdd01 100644 --- a/libpurgeablemem/common/include/ux_page_table_c.h +++ b/libpurgeablemem/common/include/ux_page_table_c.h @@ -46,5 +46,4 @@ bool UxpteIsPresent(UxPageTableStruct *upt, uint64_t addr, size_t len); } #endif /* End of #if __cplusplus */ #endif /* End of #ifdef __cplusplus */ - #endif /* OHOS_UTILS_MEMOEY_LIBPURGEABLEMEM_COMMON_INCLUDE_UX_PAGE_TABLE_C_H */ diff --git a/libpurgeablemem/cpp/include/pm_smartptr_util.h b/libpurgeablemem/cpp/include/pm_smartptr_util.h new file mode 100644 index 0000000000000000000000000000000000000000..ef24d24243b251351ad3c10154b6a9601e0884bc --- /dev/null +++ b/libpurgeablemem/cpp/include/pm_smartptr_util.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 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_SMARTPTR_UTIL_H +#define OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PM_SMARTPTR_UTIL_H + +#include "../../common/include/pm_ptr_util.h" + +namespace OHOS { +namespace PurgeableMem { +#define MAKE_UNIQUE(ptrSelf, classType, errLog, errAction, ...) \ + do { \ + ptrSelf = nullptr; \ + try { \ + ptrSelf = std::make_unique(__VA_ARGS__); \ + } catch (...) { \ + HILOG_ERROR(LOG_CORE, errLog); \ + } \ + if (ptrSelf == nullptr) { \ + errAction; \ + } \ + } while (0) +} /* namespace PurgeableMem */ +} /* namespace OHOS */ +#endif /* OHOS_UTILS_MEMORY_LIBPURGEABLEMEM_CPP_INCLUDE_PM_SMARTPTR_UTIL_H */ diff --git a/libpurgeablemem/cpp/include/purgeable_mem.h b/libpurgeablemem/cpp/include/purgeable_mem.h new file mode 100644 index 0000000000000000000000000000000000000000..37c774dad09002f07ff7484d06b61755ad04011a --- /dev/null +++ b/libpurgeablemem/cpp/include/purgeable_mem.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2022 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_MEMOEY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_MEM_H +#define OHOS_UTILS_MEMOEY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_MEM_H + +#include /* unique_ptr */ +#include /* shared_mutex */ +#include + +#include "purgeable_mem_builder.h" +#include "ux_page_table.h" + +namespace OHOS { +namespace PurgeableMem { +class PurgeableMem { +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 = nullptr); + + /* + * Destructor of class PurgeableMem. + * It will free the memory of builders associated with this PurgeableMem obj. + */ + ~PurgeableMem(); + + /* + * 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_; + size_t dataSizeInput_; + std::unique_ptr builder_; + std::unique_ptr pageTable_; + std::shared_mutex rwlock_; + unsigned int buildDataCount_; + + bool IsPurged_(); + bool BuildContent_(); + std::string ToString_() const; +}; +} /* namespace PurgeableMem */ +} /* namespace OHOS */ +#endif /* OHOS_UTILS_MEMOEY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_MEM_H */ diff --git a/libpurgeablemem/cpp/include/purgeable_mem_builder.h b/libpurgeablemem/cpp/include/purgeable_mem_builder.h new file mode 100644 index 0000000000000000000000000000000000000000..b8e92690bdc425471747ded5c40210bc006352c5 --- /dev/null +++ b/libpurgeablemem/cpp/include/purgeable_mem_builder.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 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_MEMOEY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_MEM_BUILDER_H +#define OHOS_UTILS_MEMOEY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_MEM_BUILDER_H + +#include /* unique_ptr */ + +namespace OHOS { +namespace PurgeableMem { +/* + * Class PurgeableMemBuilder is a base class of user's builder. + * PurgeableMem users can define their builders by inheriting this class. + * In its member func Build(), user should define how to build the content of a PurgeableMem obj. + */ +class PurgeableMemBuilder { +public: + virtual ~PurgeableMemBuilder(); + + /* + * User should define how to build the content of a PurgeableMem obj in this func. + * Input: data: data ptr, ponits to start address of a PurgeableMem obj's content. + * Input: size: data size of the content. + * Return: build content result, true means success, while false is fail. + */ + virtual bool Build(void *data, size_t size) = 0; + +private: + std::unique_ptr nextBuilder_ = nullptr; + + /* Only called by its friend */ + void AppendBuilder(std::unique_ptr builder); + bool BuildAll(void *data, size_t size); + friend class PurgeableMem; +}; +} /* namespace PurgeableMem */ +} /* namespace OHOS */ +#endif /* OHOS_UTILS_MEMOEY_LIBPURGEABLEMEM_CPP_INCLUDE_PURGEABLE_MEM_BUILDER_H */ diff --git a/libpurgeablemem/cpp/include/ux_page_table.h b/libpurgeablemem/cpp/include/ux_page_table.h new file mode 100644 index 0000000000000000000000000000000000000000..e84d6a971c4d3dcee47b0b5be23fccf218f3f079 --- /dev/null +++ b/libpurgeablemem/cpp/include/ux_page_table.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2022 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_MEMOEY_LIBPURGEABLEMEM_CPP_INCLUDE_UX_PAGE_TABLE_H +#define OHOS_UTILS_MEMOEY_LIBPURGEABLEMEM_CPP_INCLUDE_UX_PAGE_TABLE_H + +#include + +#include "../../common/include/ux_page_table_c.h" + +namespace OHOS { +namespace PurgeableMem { +class UxPageTable { +public: + UxPageTable(uint64_t startAddr, size_t size); + ~UxPageTable(); + +private: + UxPageTableStruct *uxpt_; + friend class PurgeableMem; + /* only called by its friend */ + void GetUxpte(uint64_t addr, size_t len); + void PutUxpte(uint64_t addr, size_t len); + bool CheckPresent(uint64_t addr, size_t len); + std::string ToString() const; +}; +} /* namespace PurgeableMem */ +} /* namespace OHOS */ +#endif /* OHOS_UTILS_MEMOEY_LIBPURGEABLEMEM_CPP_INCLUDE_UX_PAGE_TABLE_H */ diff --git a/libpurgeablemem/cpp/src/purgeable_mem.cpp b/libpurgeablemem/cpp/src/purgeable_mem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9100bb6163c46891d95573943a901ad137c3e110 --- /dev/null +++ b/libpurgeablemem/cpp/src/purgeable_mem.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2022 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 "../../common/include/pm_util.h" +#include "../../common/include/pm_state_c.h" +#include "pm_smartptr_util.h" +#include "purgeable_mem.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; +} + +PurgeableMem::PurgeableMem(size_t dataSize, std::unique_ptr builder) +{ + if (dataSize == 0) { + return; + } + dataSizeInput_ = dataSize; + + size_t size = RoundUp_(dataSizeInput_, PAGE_SIZE); + dataPtr_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PURGEABLE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (!dataPtr_) { + return; + } + + pageTable_ = nullptr; + builder_ = nullptr; + buildDataCount_ = 0; + + if (builder) { + builder_ = 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()); +} + +PurgeableMem::~PurgeableMem() +{ + HILOG_DEBUG(LOG_CORE, "%{public}s %{public}s", __func__, ToString_().c_str()); + if (dataPtr_) { + if (munmap(dataPtr_, RoundUp_(dataSizeInput_, PAGE_SIZE))) { + HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr fail", __func__); + } else { + if (USE_UXPT && !IsPurged_()) { + HILOG_ERROR(LOG_CORE, "%{public}s: munmap dataPtr succ, but uxpte present", __func__); + } + dataPtr_ = nullptr; + } + } + builder_.reset(); + pageTable_.reset(); +} + +bool PurgeableMem::BeginRead() +{ + 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); + + 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__, PMStateNames[err]); + pageTable_->PutUxpte((uint64_t)dataPtr_, dataSizeInput_); + } + return ret; +} + +void PurgeableMem::EndRead() +{ + 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_); +} + +bool PurgeableMem::BeginWrite() +{ + 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); + + 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__, PMStateNames[err]); + pageTable_->PutUxpte((uint64_t)dataPtr_, dataSizeInput_); + return false; +} + +void PurgeableMem::EndWrite() +{ + 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(); + 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(move(modifier)); + } else { + builder_ = move(modifier); + } + return true; +} + +bool PurgeableMem::IsPurged_() +{ + if (buildDataCount_ == 0) { + HILOG_INFO(LOG_CORE, "%{public}s, has never built, return true", __func__); + return true; + } + return USE_UXPT && (!pageTable_->CheckPresent((uint64_t)dataPtr_, dataSizeInput_)); +} + +bool PurgeableMem::BuildContent_() +{ + bool succ = true; + /* succ is true when purgObj has no builder */ + if (builder_) { + succ = builder_->BuildAll(dataPtr_, dataSizeInput_); + } + if (succ) { + buildDataCount_++; + } + return succ; +} + +inline std::string PurgeableMem::ToString_() const +{ + return "dataAddr:" + std::to_string((unsigned long long)dataPtr_) + + " dataSizeInput:" + std::to_string(dataSizeInput_) + + " " + pageTable_->ToString(); +} +} /* namespace PurgeableMem */ +} /* namespace OHOS */ diff --git a/libpurgeablemem/cpp/src/purgeable_mem_builder.cpp b/libpurgeablemem/cpp/src/purgeable_mem_builder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d7d6ff34d01acbba62bd6dc54539bbbb099c6be --- /dev/null +++ b/libpurgeablemem/cpp/src/purgeable_mem_builder.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 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 "pm_smartptr_util.h" +#include "purgeable_mem_builder.h" + +namespace OHOS { +namespace PurgeableMem { +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "PurgeableMem: Builder" + +PurgeableMemBuilder::~PurgeableMemBuilder() +{ + if (nextBuilder_) { + nextBuilder_.reset(); + } +} + +void PurgeableMemBuilder::AppendBuilder(std::unique_ptr builder) +{ + IF_NULL_LOG_ACTION(builder, "input builder is nullptr", return); + if (nextBuilder_) { + nextBuilder_->AppendBuilder(move(builder)); + } else { + nextBuilder_ = move(builder); + } +} + +bool PurgeableMemBuilder::BuildAll(void *data, size_t size) +{ + if (!Build(data, size)) { + HILOG_ERROR(LOG_CORE, "%{public}s: build(0x%{public}llx, %{public}zu) fail", + __func__, (unsigned long long)data, size); + return false; + } + if (!nextBuilder_) { + return true; + } + return nextBuilder_->BuildAll(data, size); +} +} /* namespace PurgeableMem */ +} /* namespace OHOS */ diff --git a/libpurgeablemem/cpp/src/ux_page_table.cpp b/libpurgeablemem/cpp/src/ux_page_table.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17f92e0f7a0f713a3dd9fc917813c0f045a2f6ff --- /dev/null +++ b/libpurgeablemem/cpp/src/ux_page_table.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 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 /* malloc */ + +#include "pm_smartptr_util.h" +#include "ux_page_table.h" + +namespace OHOS { +namespace PurgeableMem { +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "PurgeableMem: UPT" + +UxPageTable::UxPageTable(uint64_t addr, size_t len) +{ + uxpt_ = (UxPageTableStruct *)malloc(UxPageTableSize()); + if (!uxpt_) { + HILOG_ERROR(LOG_CORE, "%{public}s: malloc UxPageTableStruct fail", __func__); + } + PMState err = InitUxPageTable(uxpt_, addr, len); /* dataPtr is aligned */ + if (err != PM_OK) { + HILOG_ERROR(LOG_CORE, "%{public}s: InitUxPageTable fail, %{public}s", __func__, PMStateNames[err]); + free(uxpt_); + uxpt_ = nullptr; + } +} + +UxPageTable::~UxPageTable() +{ + /* unmap uxpt */ + if (uxpt_) { + PMState err = DeinitUxPageTable(uxpt_); + if (err != PM_OK) { + HILOG_ERROR(LOG_CORE, "%{public}s: deinit upt fail, %{public}s", __func__, PMStateNames[err]); + } else { + free(uxpt_); + uxpt_ = nullptr; + } + } +} + +void UxPageTable::GetUxpte(uint64_t addr, size_t len) +{ + UxpteGet(uxpt_, addr, len); +} + +void UxPageTable::PutUxpte(uint64_t addr, size_t len) +{ + UxptePut(uxpt_, addr, len); +} + + +bool UxPageTable::CheckPresent(uint64_t addr, size_t len) +{ + return UxpteIsPresent(uxpt_, addr, len); +} + +std::string UxPageTable::ToString() const +{ + return "uxptAddr: " + std::to_string((unsigned long long)uxpt_); +} +} /* namespace PurgeableMem */ +} /* namespace OHOS */ diff --git a/libpurgeablemem/test/BUILD.gn b/libpurgeablemem/test/BUILD.gn index 59fa7ad17e7e2c633139e0f4fe717394fd1867a5..a63819128d49dee7cc392a18b8961eb50da552dd 100644 --- a/libpurgeablemem/test/BUILD.gn +++ b/libpurgeablemem/test/BUILD.gn @@ -39,7 +39,23 @@ ohos_unittest("purgeable_c_test") { part_name = "utils_memory" } +ohos_unittest("purgeable_cpp_test") { + module_out_path = module_output_path + sources = [ "purgeable_cpp_test.cpp" ] + deps = purgeable_deps + if (is_standard_system) { + external_deps = purgeable_external_deps + public_deps = purgeable_public_deps + } + + subsystem_name = "utils" + part_name = "utils_memory" +} + group("libpurgeablemem_test") { testonly = true - deps = [ ":purgeable_c_test" ] + deps = [ + ":purgeable_c_test", + ":purgeable_cpp_test", + ] } diff --git a/libpurgeablemem/test/purgeable_cpp_test.cpp b/libpurgeablemem/test/purgeable_cpp_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cae1dfd6eda8a2b7a2237aa33ac569ab50cf3a2f --- /dev/null +++ b/libpurgeablemem/test/purgeable_cpp_test.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2022 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 /* unique_ptr */ + +#include "gtest/gtest.h" +#include "purgeable_mem.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(PurgeableMem *pdata, unsigned int loopCount); +bool ReclaimPurgeable(void); +void LoopReclaimPurgeable(unsigned int loopCount); +void ModifyPurgMemByBuilder(PurgeableMem *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) + { + char *str = (char *)data; + size_t len = 0; + for (char ch = start; ch <= end && len < size; ch++) { + str[len++] = ch; + } + str[len] = 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 = (char *)data; + for (size_t i = 0; str[i] && i < size; i++) { + if (str[i] == from) { + str[i] = to; + } + } + std::cout << "modify addr("<< (unsigned long long)str <<") " << + from << "->" << to << ", data=[" << str << "]" << std::endl; + return true; + } + + ~TestDataModifier() + { + std::cout << "~TestDataModifier" << std::endl; + } + +private: + char from, to; +}; + +class PurgeableCppTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void PurgeableCppTest::SetUpTestCase() +{ +} + +void PurgeableCppTest::TearDownTestCase() +{ +} + +void PurgeableCppTest::SetUp() +{ +} + +void PurgeableCppTest::TearDown() +{ +} + +HWTEST_F(PurgeableCppTest, MultiObjCreateTest, TestSize.Level1) +{ + const char alphabetFinal[] = "BBCDEFGHIJKLMNOPQRSTUVWXYZ\0"; + std::unique_ptr builder = std::make_unique('A', 'Z'); + std::unique_ptr builder2 = std::make_unique('A', 'Z'); + PurgeableMem pobj1(27, move(builder)); + std::unique_ptr mod = std::make_unique('A', 'B'); + std::unique_ptr mod2 = std::make_unique('A', 'B'); + + LoopPrintAlphabet(&pobj1, 1); + ModifyPurgMemByBuilder(&pobj1, move(mod)); + LoopPrintAlphabet(&pobj1, 1); + LoopReclaimPurgeable(1); + + PurgeableMem pobj2(27, move(builder2)); + LoopPrintAlphabet(&pobj2, 1); + ModifyPurgMemByBuilder(&pobj2, move(mod2)); + LoopPrintAlphabet(&pobj2, 1); + + if (pobj1.BeginRead()) { + ASSERT_STREQ(alphabetFinal, (char *)(pobj1.GetContent())); + pobj1.EndRead(); + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + + if (pobj2.BeginRead()) { + ASSERT_STREQ(alphabetFinal, (char *)(pobj2.GetContent())); + pobj2.EndRead(); + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } +} + +HWTEST_F(PurgeableCppTest, ReadTest, TestSize.Level1) +{ + const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\0"; + PurgeableMem *pobj = new PurgeableMem(27); + std::unique_ptr builder = std::make_unique('A', 'Z'); + ModifyPurgMemByBuilder(pobj, move(builder)); + std::thread reclaimThread(LoopReclaimPurgeable, (unsigned int)(-1)); + pthread_t reclaimPid = reclaimThread.native_handle(); + reclaimThread.detach(); + + unsigned int loopCount = 3; + /* loop read content */ + for (unsigned int i = 0; i < loopCount; i++) { + if (!pobj->BeginRead()) { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + continue; + } + ASSERT_STREQ(alphabet, (char *)(pobj->GetContent())); + pobj->EndRead(); + } + + pthread_cancel(reclaimPid); /* destroy reclaimThread */ + delete pobj; + pobj = nullptr; +} + +HWTEST_F(PurgeableCppTest, WriteTest, TestSize.Level1) +{ + const char alphabet[] = "CCCDEFGHIJKLMNOPQRSTUVWXYZ\0"; + std::unique_ptr builder = std::make_unique('A', 'Z'); + PurgeableMem *pobj = new PurgeableMem(27, move(builder)); + std::thread reclaimThread(LoopReclaimPurgeable, (unsigned int)(-1)); + pthread_t reclaimPid = reclaimThread.native_handle(); + reclaimThread.detach(); + + std::unique_ptr modA2B = std::make_unique('A', 'B'); + std::unique_ptr modB2C = std::make_unique('B', 'C'); + ModifyPurgMemByBuilder(pobj, move(modA2B)); + ModifyPurgMemByBuilder(pobj, move(modB2C)); + + if (pobj->BeginRead()) { + ASSERT_STREQ(alphabet, (char *)(pobj->GetContent())); + pobj->EndRead(); + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + + pthread_cancel(reclaimPid); /* destroy reclaimThread */ + delete pobj; + pobj = nullptr; + LoopReclaimPurgeable(3); +} + +HWTEST_F(PurgeableCppTest, ReadWriteTest, TestSize.Level1) +{ + const char alphabet[] = "DDDDEFGHIJKLMNOPQRSTUVWXYZ\0"; + std::unique_ptr builder = std::make_unique('A', 'Z'); + PurgeableMem *pobj = new PurgeableMem(27, move(builder)); + /* loop reclaim thread */ + std::thread reclaimThread(LoopReclaimPurgeable, (unsigned int)(-1)); + pthread_t reclaimPid = reclaimThread.native_handle(); + reclaimThread.detach(); + /* loop read thread */ + std::thread readThread(LoopPrintAlphabet, pobj, (unsigned int)(-1)); + pthread_t readPid = readThread.native_handle(); + readThread.detach(); + + 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, move(modA2B)); + ModifyPurgMemByBuilder(pobj, move(modB2C)); + ModifyPurgMemByBuilder(pobj, move(modC2D)); + + if (pobj->BeginRead()) { + ASSERT_STREQ(alphabet, (char *)(pobj->GetContent())); + pobj->EndRead(); + } else { + std::cout << __func__ << ": ERROR! BeginRead failed." << std::endl; + } + + pthread_cancel(readPid); /* destroy readThread */ + pthread_cancel(reclaimPid); /* destroy reclaimThread */ + std::this_thread::sleep_for(std::chrono::seconds(2 * PRINT_INTERVAL_SECONDS)); + delete pobj; + pobj = nullptr; +} + +void LoopPrintAlphabet(PurgeableMem *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; + } + std::cout << __func__ << ": " << i << ". data=[" << + (char *)(pdata->GetContent()) << "]" << std::endl; + 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/kernel/purgeable", "w"); + if (!f) { + std::cout << __func__ << ": open file failed" << std::endl; + return false; + } + bool succ = true; + if (fputs("1", 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(PurgeableMem *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)); + std::cout << __func__ << " before mod data=[" << (char *)(pdata->GetContent()) << "]" << std::endl; + pdata->ModifyContentByBuilder(move(mod)); + std::cout<< __func__ << " after mod data=[" << (char *)(pdata->GetContent()) << "]" << std::endl; + + std::cout << __func__ << " data=[" << (char *)(pdata->GetContent()) << "]" << std::endl; + pdata->EndWrite(); +} +} /* namespace PurgeableMem */ +} /* namespace OHOS */