diff --git a/BUILD.gn b/BUILD.gn index 2a54be3e5715e66a19576b5e6651f6c5254c5a3e..99f3714035ab9cbbe320de7e582a8dff7a2f9143 100755 --- a/BUILD.gn +++ b/BUILD.gn @@ -113,6 +113,8 @@ config("glib_config") { "glib/deprecated", "//third_party/gettext/gettext-runtime/intl", "//third_party/pcre2/pcre2/src", + "//foundation/multimedia/media_standard/services/utils", + "glibmemdfx", ] cflags = [ "-DG_LOG_DOMAIN=\"GLib\"", @@ -137,6 +139,8 @@ config("glib_config") { "-Wno-unused-value", "-Wno-unused-function", "-Wno-int-conversion", + "-DG_MEM_DFX", + "-DOHOS_OPT_PERFORMANCE", ] } @@ -241,6 +245,7 @@ ohos_source_set("glib_source") { } ohos_shared_library("glib") { deps = [ + ":g_mem_dfx", ":glib_source", "//third_party/gettext:libgettext", "//third_party/glib:glibpcre", @@ -698,3 +703,50 @@ ohos_static_library("ginotify") { part_name = "glib" subsystem_name = "thirdparty" } + +config("g_mem_dfx_config") { + include_dirs = [ + "glibmemdfx", + "//commonlibrary/c_utils/base/include", + "//foundation/multimedia/media_standard/interfaces/inner_api/native", + "//base/hiviewdfx/hisysevent/interfaces/native/innerkits/hisysevent/include", + ] +} + +ohos_shared_library("g_mem_dfx") { + sources = [ "glibmemdfx/gmemdfx.cpp" ] + + include_dirs = [ "//commonlibrary/c_utils/base/include" ] + + cflags = [ + "-std=c++17", + "-fno-rtti", + "-fno-exceptions", + "-Wall", + "-fno-common", + "-fstack-protector-strong", + "-Wshadow", + "-FPIC", + "-FS", + "-O2", + "-D_FORTIFY_SOURCE=2", + "-fvisibility=hidden", + "-Wformat=2", + "-Wfloat-equal", + "-Wdate-time", + ] + + configs = [ ":g_mem_dfx_config" ] + + external_deps = [ + "c_utils:utils", + "faultloggerd:lib_dfx_dump_catcher", + "hisysevent_native:libhisysevent", + "hitrace_native:hitrace_meter", + "hiviewdfx_hilog_native:libhilog", + "init:libbegetutil", + ] + + subsystem_name = "thirdparty" + part_name = "glib" +} diff --git a/glib/gslice.c b/glib/gslice.c index d6335c9dda15cab744f1cbe165a6deec70359e7b..fae233410d5b689f24e4fab4f083b9d8cf467e77 100644 --- a/glib/gslice.c +++ b/glib/gslice.c @@ -51,6 +51,18 @@ #include "gvalgrind.h" +#include "gmemdfx.h" + +#if defined(G_MEM_DFX) + +#define DFX_TRACE(probe) probe + +#else + +#define DFX_TRACE(probe) + +#endif + /** * SECTION:memory_slices * @title: Memory Slices @@ -675,6 +687,64 @@ magazine_chain_prepare_fields (ChunkLink *magazine_chunks) #define magazine_chain_next(mc) ((mc)->next->next->data) #define magazine_chain_count(mc) ((mc)->next->next->next->data) +#ifdef OHOS_OPT_PERFORMANCE +/* + * ohos.opt.performance.0004 + * fix glib cache too large problem. when thread exit, release mem no user. + */ +static void +magazine_cache_trim (Allocator *allocator, + guint ix, + guint stamp, + gboolean release) +{ + /* g_mutex_lock (allocator->mutex); done by caller */ + /* trim magazine cache from tail */ + ChunkLink *current = magazine_chain_prev (allocator->magazines[ix]); + ChunkLink *trash = NULL; + while (!G_APPROX_VALUE(stamp, magazine_chain_uint_stamp (current), + allocator->config.working_set_msecs) || release) + { + /* unlink */ + ChunkLink *prev = magazine_chain_prev (current); + ChunkLink *next = magazine_chain_next (current); + magazine_chain_next (prev) = next; + magazine_chain_prev (next) = prev; + /* clear special fields, put on trash stack */ + magazine_chain_next (current) = NULL; + magazine_chain_count (current) = NULL; + magazine_chain_stamp (current) = NULL; + magazine_chain_prev (current) = trash; + trash = current; + /* fixup list head if required */ + if (current == allocator->magazines[ix]) + { + allocator->magazines[ix] = NULL; + break; + } + current = prev; + } + g_mutex_unlock (&allocator->magazine_mutex); + /* free trash */ + if (trash) + { + const gsize chunk_size = SLAB_CHUNK_SIZE (allocator, ix); + g_mutex_lock (&allocator->slab_mutex); + while (trash) + { + current = trash; + trash = magazine_chain_prev (current); + magazine_chain_prev (current) = NULL; /* clear special field */ + while (current) + { + ChunkLink *chunk = magazine_chain_pop_head (¤t); + slab_allocator_free_chunk (chunk_size, chunk); + } + } + g_mutex_unlock (&allocator->slab_mutex); + } +} +#else static void magazine_cache_trim (Allocator *allocator, guint ix, @@ -726,7 +796,42 @@ magazine_cache_trim (Allocator *allocator, g_mutex_unlock (&allocator->slab_mutex); } } +#endif +#ifdef OHOS_OPT_PERFORMANCE +/* + * ohos.opt.performance.0004 + * fix glib cache too large problem. when thread exit, release mem no user. + */ +static void +magazine_cache_push_magazine (guint ix, + ChunkLink *magazine_chunks, + gsize count, + gboolean release) /* must be >= MIN_MAGAZINE_SIZE */ +{ + ChunkLink *current = magazine_chain_prepare_fields (magazine_chunks); + ChunkLink *next, *prev; + g_mutex_lock (&allocator->magazine_mutex); + /* add magazine at head */ + next = allocator->magazines[ix]; + if (next) + prev = magazine_chain_prev (next); + else + next = prev = current; + magazine_chain_next (prev) = current; + magazine_chain_prev (next) = current; + magazine_chain_prev (current) = prev; + magazine_chain_next (current) = next; + magazine_chain_count (current) = (gpointer) count; + /* stamp magazine */ + magazine_cache_update_stamp(); + magazine_chain_stamp (current) = GUINT_TO_POINTER (allocator->last_stamp); + allocator->magazines[ix] = current; + /* free old magazines beyond a certain threshold */ + magazine_cache_trim (allocator, ix, allocator->last_stamp, release); + /* g_mutex_unlock (allocator->mutex); was done by magazine_cache_trim() */ +} +#else static void magazine_cache_push_magazine (guint ix, ChunkLink *magazine_chunks, @@ -754,7 +859,7 @@ magazine_cache_push_magazine (guint ix, magazine_cache_trim (allocator, ix, allocator->last_stamp); /* g_mutex_unlock (allocator->mutex); was done by magazine_cache_trim() */ } - +#endif static ChunkLink* magazine_cache_pop_magazine (guint ix, gsize *countp) @@ -818,7 +923,15 @@ private_thread_memory_cleanup (gpointer data) { Magazine *mag = mags[j]; if (mag->count >= MIN_MAGAZINE_SIZE) +#ifdef OHOS_OPT_PERFORMANCE +/* + * ohos.opt.performance.0004 + * fix glib cache too large problem. when thread exit, release mem no user. + */ + magazine_cache_push_magazine (ix, mag->chunks, mag->count, TRUE); +#else magazine_cache_push_magazine (ix, mag->chunks, mag->count); +#endif else { const gsize chunk_size = SLAB_CHUNK_SIZE (allocator, ix); @@ -850,7 +963,15 @@ thread_memory_magazine2_unload (ThreadMemory *tmem, guint ix) { Magazine *mag = &tmem->magazine2[ix]; +#ifdef OHOS_OPT_PERFORMANCE +/* + * ohos.opt.performance.0004 + * fix glib cache too large problem. when thread exit, release mem no user. + */ + magazine_cache_push_magazine (ix, mag->chunks, mag->count, FALSE); +#else magazine_cache_push_magazine (ix, mag->chunks, mag->count); +#endif mag->chunks = NULL; mag->count = 0; } @@ -1071,7 +1192,7 @@ g_slice_alloc (gsize mem_size) smc_notify_alloc (mem, mem_size); TRACE (GLIB_SLICE_ALLOC((void*)mem, mem_size)); - + DFX_TRACE(GMemAllocDfx((void *)mem, (unsigned int)mem_size)); return mem; } @@ -1180,6 +1301,7 @@ g_slice_free1 (gsize mem_size, g_free (mem_block); } TRACE (GLIB_SLICE_FREE((void*)mem_block, mem_size)); + DFX_TRACE(GMemFreeDfx((void *)mem_block)); } /** @@ -1207,6 +1329,7 @@ g_slice_free_chain_with_offset (gsize mem_size, gpointer mem_chain, gsize next_offset) { + DFX_TRACE(GChainMemFreeDfx((void *)mem_chain, next_offset)); gpointer slice = mem_chain; /* while the thread magazines and the magazine cache are implemented so that * they can easily be extended to allow for free lists containing more free @@ -1454,6 +1577,7 @@ allocator_memalign (gsize alignment, gint err = ENOMEM; #if HAVE_POSIX_MEMALIGN err = posix_memalign (&aligned_memory, alignment, memsize); + DFX_TRACE(GMemPoolAllocDfx(aligned_memory, alignment, memsize)); #elif HAVE_MEMALIGN errno = 0; aligned_memory = memalign (alignment, memsize); @@ -1497,6 +1621,7 @@ allocator_memfree (gsize memsize, gpointer mem) { #if HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC + DFX_TRACE(GMemPoolFreeDfx(mem)); free (mem); #else mem_assert (memsize <= sys_page_size); diff --git a/glibmemdfx/gmemdfx.cpp b/glibmemdfx/gmemdfx.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c36613d1cd71497c9288980458fc3907e55b9c4 --- /dev/null +++ b/glibmemdfx/gmemdfx.cpp @@ -0,0 +1,224 @@ +/* + * 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 "gmemdfx.h" +#include +#include +#include +#include +#include "gmemdfxdump.h" +#include "dfx_dump_catcher.h" +#include "param_wrapper.h" +#include "string_ex.h" + +#undef LOG_DOMAIN +#define LOG_DOMAIN 0xD002B00 + +#define __LOG(func, fmt, args...) \ + do { \ + (void)func(LABEL, "{%{public}s():%{public}d} " fmt, __FUNCTION__, __LINE__, ##args); \ + } while (0) + +#define LOGE(fmt, ...) __LOG(::OHOS::HiviewDFX::HiLog::Error, fmt, ##__VA_ARGS__) + +#define POINTER_MASK 0x00FFFFFF +#define FAKE_POINTER(addr) (POINTER_MASK & reinterpret_cast(addr)) + +struct MemInfo { + uint64_t count = 0; + uint64_t size = 0; + std::string str; + intptr_t mem; +}; + +struct PoolInfo { + uint64_t count = 0; + uint64_t size = 0; + uint64_t alignment = 0; + uint64_t lastTid = 0; + intptr_t mem; +}; + +namespace { + constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN, "AVGlibMemDfx"}; + static std::unordered_map memMap; + static std::unordered_map poolMap; + static uint64_t memCount = 0; + static uint64_t poolCount = 0; + static std::mutex mutex; + static bool enableDump = false; + static unsigned int dumpSize = 0; + static unsigned int dumpStart = 0; + static unsigned int dumpCount = 0; + static bool dumpOpen = false; +} + +void GMemPoolAllocDfx(void *mem, unsigned int alignment, unsigned int size) +{ + std::lock_guard lock(mutex); + if (!dumpOpen || mem == nullptr) { + return; + } + if (poolMap.find(mem) != poolMap.end()) { + LOGE("the mem 0x%{public}06" PRIXPTR " is already allocated", FAKE_POINTER(mem)); + return; + } + + poolMap[mem] = {poolCount++, size, alignment, gettid(), (intptr_t)mem}; +} + +void GMemPoolFreeDfx(void *mem) +{ + std::lock_guard lock(mutex); + if (!dumpOpen || mem == nullptr) { + return; + } + if (mem != nullptr && poolMap.erase(mem) == 0) { + LOGE("the mem 0x%{public}06" PRIXPTR " is already free", FAKE_POINTER(mem)); + } +} + +void GMemAllocDfx(void *mem, unsigned int size) +{ + std::lock_guard lock(mutex); + if (!dumpOpen || mem == nullptr) { + return; + } + if (memMap.find(mem) != memMap.end()) { + LOGE("the mem 0x%{public}06" PRIXPTR " is already allocated", FAKE_POINTER(mem)); + return; + } + std::string str; + if (enableDump && size == dumpSize && (memCount - dumpStart) % dumpCount == 0) { + OHOS::HiviewDFX::DfxDumpCatcher dumpLog; + bool ret = dumpLog.DumpCatch(getpid(), gettid(), str); + if (!ret) { + LOGE("dump error"); + } + } + + memMap[mem] = {memCount++, size, str, (intptr_t)mem}; +} + +void GChainMemFreeDfx(void *mem_chain, unsigned long next_offset) +{ + std::lock_guard lock(mutex); + if (!dumpOpen || mem_chain == nullptr) { + return; + } + void *next = mem_chain; + while (next) { + uint8_t *current = (uint8_t *)next; + next = *(void **)(current + next_offset); + if (current != nullptr && memMap.erase(current) == 0) { + LOGE("the mem 0x%{public}06" PRIXPTR " is already free", FAKE_POINTER(current)); + } + } +} + +void GMemFreeDfx(void *mem) +{ + std::lock_guard lock(mutex); + if (!dumpOpen || mem == nullptr) { + return; + } + if (mem != nullptr && memMap.erase(mem) == 0) { + LOGE("the mem 0x%{public}06" PRIXPTR " is already free", FAKE_POINTER(mem)); + } +} + +void InitParameter() +{ + std::string dumpSizeStr; + std::string dumpStartStr; + std::string dumpCountStr; + std::string dumpOpenStr; + int32_t size; + int32_t start; + int32_t count; + int32_t res = OHOS::system::GetStringParameter("sys.media.dump.mem.size", dumpSizeStr, ""); + if (res == 0 && !dumpSizeStr.empty()) { + OHOS::StrToInt(dumpSizeStr, size); + dumpSize = size; + enableDump = dumpSize == 0 ? false :true; + } else { + enableDump = false; + } + res = OHOS::system::GetStringParameter("sys.media.dump.mem.start", dumpStartStr, ""); + if (res == 0 && !dumpStartStr.empty()) { + OHOS::StrToInt(dumpStartStr, start); + dumpStart = start; + } else { + dumpStart = 0; + } + res = OHOS::system::GetStringParameter("sys.media.dump.mem.count", dumpCountStr, ""); + if (res == 0 && !dumpCountStr.empty()) { + OHOS::StrToInt(dumpCountStr, count); + dumpCount = count; + } else { + dumpCount = 1; + } + res = OHOS::system::GetStringParameter("sys.media.dump.mem.open", dumpOpenStr, ""); + if (res == 0 && !dumpOpenStr.empty()) { + dumpOpen = dumpOpenStr == "TRUE" ? true : false; + } else { + dumpOpen = false; + } +} + +void GetGMemDump(std::string &str) +{ + std::unordered_map memMapCopy; + { + std::lock_guard lock(mutex); + InitParameter(); + memMapCopy = memMap; + } + std::vector> memInfoVec(memMapCopy.begin(), memMapCopy.end()); + std::sort(memInfoVec.begin(), memInfoVec.end(), [&](auto &left, auto &right) { + return left.second.count < right.second.count; + }); + for (auto iter = memInfoVec.begin(); iter != memInfoVec.end(); iter++) { + str += "count:"; + str += std::to_string(iter->second.count) + ";"; + str += "size:"; + str += std::to_string(iter->second.size) + "\n"; + str += iter->second.str + "\n"; + } +} + +void GetGMemPoolDump(std::string &str) +{ + std::unordered_map poolMapCopy; + { + std::lock_guard lock(mutex); + InitParameter(); + poolMapCopy = poolMap; + } + std::vector> poolInfoVec(poolMapCopy.begin(), poolMapCopy.end()); + std::sort(poolInfoVec.begin(), poolInfoVec.end(), [&](auto &left, auto &right) { + return left.second.count < right.second.count; + }); + for (auto iter = poolInfoVec.begin(); iter != poolInfoVec.end(); iter++) { + str += "count:"; + str += std::to_string(iter->second.count) + ";"; + str += "size:"; + str += std::to_string(iter->second.size) + "\n"; + str += "alignment:"; + str += std::to_string(iter->second.alignment) + "\n"; + str += "lastTid:"; + str += std::to_string(iter->second.lastTid) + "\n"; + } +} \ No newline at end of file diff --git a/glibmemdfx/gmemdfx.h b/glibmemdfx/gmemdfx.h new file mode 100644 index 0000000000000000000000000000000000000000..fd0e9df8123dbf130e0a138f9ca01bec8add3d0a --- /dev/null +++ b/glibmemdfx/gmemdfx.h @@ -0,0 +1,31 @@ +/* + * 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 G_MEM_DFX_H +#define G_MEM_DFX_H + +#ifdef __cplusplus +extern "C" { +#endif + +void __attribute__((visibility("default"))) GMemAllocDfx(void *mem, unsigned int size); +void __attribute__((visibility("default"))) GChainMemFreeDfx(void *mem_chain, unsigned long next_offset); +void __attribute__((visibility("default"))) GMemFreeDfx(void *mem); + +void __attribute__((visibility("default"))) GMemPoolAllocDfx(void *mem, unsigned int alignment, unsigned int size); +void __attribute__((visibility("default"))) GMemPoolFreeDfx(void *mem); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/glibmemdfx/gmemdfxdump.h b/glibmemdfx/gmemdfxdump.h new file mode 100644 index 0000000000000000000000000000000000000000..edf58e350941ace1df4e6a56035ae7bfde3c85b1 --- /dev/null +++ b/glibmemdfx/gmemdfxdump.h @@ -0,0 +1,23 @@ +/* + * 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 G_MEM_DFX_DUMP_H +#define G_MEM_DFX_DUMP_H + +#include + +void __attribute__((visibility("default"))) GetGMemDump(std::string &str); +void __attribute__((visibility("default"))) GetGMemPoolDump(std::string &str); + +#endif \ No newline at end of file