From 7e3102d9b7ae9a4f78a18d2169ce1a2ccdd2a60b Mon Sep 17 00:00:00 2001 From: lightaooii Date: Thu, 4 Jul 2024 15:59:53 +0800 Subject: [PATCH 1/2] [OHOS][libunwind]Add lock for FrameHeaderCache itself. Isuue: IAAMK0 Signed-off-by: gaohongtao --- libunwind/src/FrameHeaderCache.hpp | 47 +++++++++++++++++++++++++++++- libunwind/src/libunwind.cpp | 5 +++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/libunwind/src/FrameHeaderCache.hpp b/libunwind/src/FrameHeaderCache.hpp index 54d5d33c3cd7..544ab28cee60 100644 --- a/libunwind/src/FrameHeaderCache.hpp +++ b/libunwind/src/FrameHeaderCache.hpp @@ -14,6 +14,11 @@ #include "config.h" #include +// OHOS_LOCAL begin +#if defined(__OHOS__) + #include +#endif +// OHOS_LOCAL end #ifdef _LIBUNWIND_DEBUG_FRAMEHEADER_CACHE #define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x) _LIBUNWIND_LOG0(x) @@ -51,6 +56,15 @@ class _LIBUNWIND_HIDDEN FrameHeaderCache { CacheEntry *MostRecentlyUsed; CacheEntry *Unused; + // OHOS_LOCAL begin + #if defined(__OHOS__) + // For thead-safety. FrameHeaderCache is used when findUnwindSectionsByPhdr in + // dl_iterate_phdr callback, and there's a lock for the callback. But actually, + // we only need to add a lock to do the synchronization in FrameHeaderCache. + pthread_mutex_t lock; + #endif + // OHOS_LOCAL end + void resetCache() { _LIBUNWIND_FRAMEHEADERCACHE_TRACE0("FrameHeaderCache reset"); MostRecentlyUsed = nullptr; @@ -86,8 +100,19 @@ class _LIBUNWIND_HIDDEN FrameHeaderCache { public: bool find(dl_phdr_info *PInfo, size_t, void *data) { - if (cacheNeedsReset(PInfo) || MostRecentlyUsed == nullptr) + // OHOS_LOCAL begin + #if defined(__OHOS__) + pthread_mutex_lock(&lock); + #endif + // OHOS_LOCAL end + if (cacheNeedsReset(PInfo) || MostRecentlyUsed == nullptr) { + // OHOS_LOCAL begin + #if defined(__OHOS__) + pthread_mutex_unlock(&lock); + #endif + // OHOS_LOCAL end return false; + } auto *CBData = static_cast(data); CacheEntry *Current = MostRecentlyUsed; @@ -109,11 +134,21 @@ public: MostRecentlyUsed = Current; } *CBData->sects = Current->Info; + // OHOS_LOCAL begin + #if defined(__OHOS__) + pthread_mutex_unlock(&lock); + #endif + // OHOS_LOCAL end return true; } Previous = Current; Current = Current->Next; } + // OHOS_LOCAL begin + #if defined(__OHOS__) + pthread_mutex_unlock(&lock); + #endif + // OHOS_LOCAL end _LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache miss for address %lx", CBData->targetAddr); return false; @@ -122,6 +157,11 @@ public: void add(const UnwindInfoSections *UIS) { CacheEntry *Current = nullptr; + // OHOS_LOCAL begin + #if defined(__OHOS__) + pthread_mutex_lock(&lock); + #endif + // OHOS_LOCAL end if (Unused != nullptr) { Current = Unused; Unused = Unused->Next; @@ -140,6 +180,11 @@ public: Current->Info = *UIS; Current->Next = MostRecentlyUsed; MostRecentlyUsed = Current; + // OHOS_LOCAL begin + #if defined(__OHOS__) + pthread_mutex_unlock(&lock); + #endif + // OHOS_LOCAL end _LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache add [%lx - %lx)", MostRecentlyUsed->LowPC(), MostRecentlyUsed->HighPC()); diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp index e0e98c04aabe..343b511eaeca 100644 --- a/libunwind/src/libunwind.cpp +++ b/libunwind/src/libunwind.cpp @@ -27,8 +27,11 @@ #endif #if !defined(__USING_SJLJ_EXCEPTIONS__) -#include "AddressSpace.hpp" +// OHOS_LOCAL begin +// change include sequence to fix pid_t compile issue #include "UnwindCursor.hpp" +#include "AddressSpace.hpp" +// OHOS_LOCAL end using namespace libunwind; -- Gitee From 0a7def8816528dff5667e5d0d18f4f7bafd68ee6 Mon Sep 17 00:00:00 2001 From: lightaooii Date: Tue, 23 Jul 2024 10:57:43 +0800 Subject: [PATCH 2/2] [OHOS][libunwind]Add multi-threads unwind test Isuue: IAAMK0 Signed-off-by: gaohongtao --- libunwind/test/multi-threads-unwind.pass.cpp | 113 +++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 libunwind/test/multi-threads-unwind.pass.cpp diff --git a/libunwind/test/multi-threads-unwind.pass.cpp b/libunwind/test/multi-threads-unwind.pass.cpp new file mode 100644 index 000000000000..96b6f7556dc0 --- /dev/null +++ b/libunwind/test/multi-threads-unwind.pass.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2024 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 + +#define NUM_WORKER_THREADS 128 + // 3 minutes timeout, normaly it'll finish in 1m +#define TIMEOUT_SECONDS 3 * 60 + +// Thread completion counter +int completed_threads = 0; +// For synchronization +pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER; + +// Task for worker threads +void* worker_task(void* ) { + int loop_count = NUM_WORKER_THREADS; + while (loop_count > 0) { + try { + throw "test for multi-threads unwinding"; + } + catch (...) { + loop_count--; + } + } + + // Update the completed thread counter + pthread_mutex_lock(&count_mutex); + completed_threads++; + pthread_mutex_unlock(&count_mutex); + + return NULL; +} + +// Task for the monitor thread: check if all the theads can exit normaly. +// The unwinding worker thread can be blocked when lacking of synchronization, +// as FrameHeaderCache::find method use LRU cache replacement principle which +// may changes the link CacheEntry *Next pointing to the CacheEntry itself. +void* monitor_task(void* arg) { + struct timeval start_time = *(struct timeval*)arg; + while (true) { + pthread_mutex_lock(&count_mutex); + // Check if all worker threads have completed + if (completed_threads == NUM_WORKER_THREADS) { + pthread_mutex_unlock(&count_mutex); + break; // no blocking worker_task, just end the monitor + } + pthread_mutex_unlock(&count_mutex); + + struct timeval current_time; + gettimeofday(¤t_time, NULL); + long seconds = current_time.tv_sec - start_time.tv_sec; + // Check for timeout, regard as failure + if(seconds > TIMEOUT_SECONDS) { + _exit(-1); + } + + sleep(1); // Check once per second + } + return NULL; +} + +int main() { + pthread_t threads[NUM_WORKER_THREADS]; + pthread_t monitor_thread; + + // Record the start time of the program + struct timeval start_time; + gettimeofday(&start_time, NULL); + + // Create and start worker threads + for (int i = 0; i < NUM_WORKER_THREADS; ++i) { + if(pthread_create(&threads[i], NULL, worker_task, NULL) != 0) { + _exit(-1); + } + } + + // Create and start the monitor thread + if(pthread_create(&monitor_thread, NULL, monitor_task, &start_time) != 0) { + _exit(-1); + } + + // Wait for all worker threads to complete + for (int i = 0; i < NUM_WORKER_THREADS; ++i) { + if(pthread_join(threads[i], NULL) != 0) { + _exit(-1); + } + } + + // Wait for monitor thread to complete + if(pthread_join(monitor_thread, NULL) != 0) { + _exit(-1); + } + + // Clean up resources + pthread_mutex_destroy(&count_mutex); + + return 0; +} -- Gitee