From e7f38c730d87f26f4da8b48a004c0db83bf53476 Mon Sep 17 00:00:00 2001 From: Bright Chen Date: Sun, 7 Jul 2024 10:57:13 +0800 Subject: [PATCH] Fix malloc dead lock cause by contention profiler --- .github/workflows/ci-linux.yml | 2 +- src/brpc/builtin/hotspots_service.cpp | 4 +- src/brpc/builtin/pprof_service.cpp | 4 +- src/brpc/policy/rtmp_protocol.cpp | 2 +- src/brpc/socket.cpp | 4 +- src/bthread/mutex.cpp | 46 +++- src/butil/debug/stack_trace.cc | 3 - src/butil/debug/stack_trace.h | 5 +- src/butil/debug/stack_trace_posix.cc | 21 +- src/butil/iobuf_profiler.cpp | 6 +- src/butil/object_pool.h | 5 + src/butil/object_pool_inl.h | 12 ++ .../dynamic_annotations/dynamic_annotations.c | 2 +- src/butil/third_party/symbolize/symbolize.cc | 8 + src/butil/third_party/symbolize/symbolize.h | 2 + test/Makefile | 203 +++++++++--------- test/run_tests.sh | 2 +- test/stack_trace_unittest.cc | 29 +++ 18 files changed, 228 insertions(+), 132 deletions(-) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 652631f28f..0962f19779 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -217,7 +217,7 @@ jobs: - name: compile tests run: | cd test - make -j ${{env.proc_num}} + make -j ${{env.proc_num}} test_butil - name: run tests run: | cd test diff --git a/src/brpc/builtin/hotspots_service.cpp b/src/brpc/builtin/hotspots_service.cpp index abb0bb4ea2..f714843dfd 100644 --- a/src/brpc/builtin/hotspots_service.cpp +++ b/src/brpc/builtin/hotspots_service.cpp @@ -33,8 +33,8 @@ #include "brpc/details/tcmalloc_extension.h" extern "C" { -int __attribute__((weak)) ProfilerStart(const char* fname); -void __attribute__((weak)) ProfilerStop(); +int BAIDU_WEAK ProfilerStart(const char* fname); +void BAIDU_WEAK ProfilerStop(); } namespace bthread { diff --git a/src/brpc/builtin/pprof_service.cpp b/src/brpc/builtin/pprof_service.cpp index eba7137738..e959169874 100644 --- a/src/brpc/builtin/pprof_service.cpp +++ b/src/brpc/builtin/pprof_service.cpp @@ -41,8 +41,8 @@ extern "C" { #if defined(OS_LINUX) extern char *program_invocation_name; #endif -int __attribute__((weak)) ProfilerStart(const char* fname); -void __attribute__((weak)) ProfilerStop(); +int BAIDU_WEAK ProfilerStart(const char* fname); +void BAIDU_WEAK ProfilerStop(); } namespace bthread { diff --git a/src/brpc/policy/rtmp_protocol.cpp b/src/brpc/policy/rtmp_protocol.cpp index 33a0c0c689..99a3e085ce 100644 --- a/src/brpc/policy/rtmp_protocol.cpp +++ b/src/brpc/policy/rtmp_protocol.cpp @@ -39,7 +39,7 @@ // we mark the symbol as weak. If the runtime does not have the function, // handshaking will fallback to the simple one. extern "C" { -const EVP_MD* __attribute__((weak)) EVP_sha256(void); +const EVP_MD* BAIDU_WEAK EVP_sha256(void); } diff --git a/src/brpc/socket.cpp b/src/brpc/socket.cpp index ac1c37aebe..9e42b3ab68 100644 --- a/src/brpc/socket.cpp +++ b/src/brpc/socket.cpp @@ -57,8 +57,8 @@ #endif namespace bthread { -size_t __attribute__((weak)) -get_sizes(const bthread_id_list_t* list, size_t* cnt, size_t n); +size_t BAIDU_WEAK get_sizes(const bthread_id_list_t* list, + size_t* cnt, size_t n); } diff --git a/src/bthread/mutex.cpp b/src/bthread/mutex.cpp index 357452ee7c..2450335ca9 100644 --- a/src/bthread/mutex.cpp +++ b/src/bthread/mutex.cpp @@ -20,7 +20,6 @@ // Date: Sun Aug 3 12:46:15 CST 2014 #include -#include #include // dlsym #include // O_RDONLY #include "butil/atomicops.h" @@ -34,9 +33,12 @@ #include "butil/files/file_path.h" #include "butil/file_util.h" #include "butil/unique_ptr.h" +#include "butil/memory/scope_guard.h" #include "butil/third_party/murmurhash3/murmurhash3.h" +#include "butil/third_party/symbolize/symbolize.h" #include "butil/logging.h" #include "butil/object_pool.h" +#include "butil/debug/stack_trace.h" #include "bthread/butex.h" // butex_* #include "bthread/processor.h" // cpu_relax, barrier #include "bthread/mutex.h" // bthread_mutex_t @@ -44,16 +46,12 @@ #include "bthread/log.h" extern "C" { -extern void* __attribute__((weak)) _dl_sym(void* handle, const char* symbol, void* caller); +extern void* BAIDU_WEAK _dl_sym(void* handle, const char* symbol, void* caller); } -extern int __attribute__((weak)) GetStackTrace(void** result, int max_depth, int skip_count); namespace bthread { // Warm up backtrace before main(). -void* dummy_buf[4]; -const int ALLOW_UNUSED dummy_bt = GetStackTrace - ? GetStackTrace(dummy_buf, arraysize(dummy_buf), 0) - : backtrace(dummy_buf, arraysize(dummy_buf)); +const butil::debug::StackTrace ALLOW_UNUSED dummy_bt; // For controlling contentions collected per second. static bvar::CollectorSpeedLimit g_cp_sl = BVAR_COLLECTOR_SPEED_LIMIT_INITIALIZER; @@ -457,6 +455,10 @@ inline uint64_t hash_mutex_ptr(const Mutex* m) { // code are never sampled, otherwise deadlock may occur. static __thread bool tls_inside_lock = false; +// Warn up some singleton objects used in contention profiler +// to avoid deadlock in malloc call stack. +static __thread bool tls_warn_up = false; + // Speed up with TLS: // Most pthread_mutex are locked and unlocked in the same thread. Putting // contention information in TLS avoids collisions that may occur in @@ -527,6 +529,28 @@ inline bool remove_pthread_contention_site(const Mutex* mutex, // Submit the contention along with the callsite('s stacktrace) void submit_contention(const bthread_contention_site_t& csite, int64_t now_ns) { tls_inside_lock = true; + BRPC_SCOPE_EXIT { + tls_inside_lock = false; + }; + + butil::debug::StackTrace stack; // May lock. + size_t nframes = 0; + const void* const* addrs = stack.Addresses(&nframes); + if (0 == nframes) { + return; + } + // There are two situations where we need to check whether in the + // malloc call stack: + // 1. Warn up some singleton objects used in `submit_contention' + // to avoid deadlock in malloc call stack. + // 2. LocalPool is empty, GlobalPool may allocate memory by malloc. + if (!tls_warn_up || butil::local_pool_free_empty()) { + // In malloc call stack, can not submit contention. + if (stack.FindSymbol((void*)malloc)) { + return; + } + } + auto sc = butil::get_object(); // Normalize duration_us and count so that they're addable in later // processings. Notice that sampling_range is adjusted periodically by @@ -534,11 +558,11 @@ void submit_contention(const bthread_contention_site_t& csite, int64_t now_ns) { sc->duration_ns = csite.duration_ns * bvar::COLLECTOR_SAMPLING_BASE / csite.sampling_range; sc->count = bvar::COLLECTOR_SAMPLING_BASE / (double)csite.sampling_range; - sc->nframes = GetStackTrace - ? GetStackTrace(sc->stack, arraysize(sc->stack), 0) - : backtrace(sc->stack, arraysize(sc->stack)); // may lock + memcpy(sc->stack, addrs, sizeof(void*) * nframes); + sc->nframes = nframes; sc->submit(now_ns / 1000); // may lock - tls_inside_lock = false; + // Once submit a contention, complete warn up. + tls_warn_up = true; } namespace internal { diff --git a/src/butil/debug/stack_trace.cc b/src/butil/debug/stack_trace.cc index d8d60ecb54..3032868709 100644 --- a/src/butil/debug/stack_trace.cc +++ b/src/butil/debug/stack_trace.cc @@ -21,9 +21,6 @@ StackTrace::StackTrace(const void* const* trace, size_t count) { count_ = count; } -StackTrace::~StackTrace() { -} - const void *const *StackTrace::Addresses(size_t* count) const { *count = count_; if (count_) diff --git a/src/butil/debug/stack_trace.h b/src/butil/debug/stack_trace.h index ee5172813b..4a02e62845 100644 --- a/src/butil/debug/stack_trace.h +++ b/src/butil/debug/stack_trace.h @@ -58,12 +58,13 @@ class BUTIL_EXPORT StackTrace { // Copying and assignment are allowed with the default functions. - ~StackTrace(); - // Gets an array of instruction pointer values. |*count| will be set to the // number of elements in the returned array. const void* const* Addresses(size_t* count) const; + // Whether if the given symbol is found in the stack trace. + bool FindSymbol(void* symbol) const; + // Prints the stack trace to stderr. void Print() const; diff --git a/src/butil/debug/stack_trace_posix.cc b/src/butil/debug/stack_trace_posix.cc index b96f1c8bde..81c3a9f0cd 100644 --- a/src/butil/debug/stack_trace_posix.cc +++ b/src/butil/debug/stack_trace_posix.cc @@ -45,7 +45,7 @@ #include "butil/third_party/symbolize/symbolize.h" #endif -extern int __attribute__((weak)) GetStackTrace(void** result, int max_depth, int skip_count); +extern int BAIDU_WEAK GetStackTrace(void** result, int max_depth, int skip_count); namespace butil { namespace debug { @@ -763,6 +763,25 @@ StackTrace::StackTrace() { } } +bool StackTrace::FindSymbol(void* symbol) const { +#if !defined(__UCLIBC__) + for (size_t i = 0; i < count_; ++i) { + void* saddr; + // Subtract by one as return address of function may be in the next + // function when a function is annotated as noreturn. + void* address = static_cast(trace_[i]) - 1; + if (!google::SymbolizeAndAddress(address, &saddr)) { + continue; + } + LOG(INFO) << "saddr: " << saddr << " symbol: " << symbol; + if (saddr == symbol) { + return true; + } + } +#endif + return false; +} + void StackTrace::Print() const { // NOTE: This code MUST be async-signal safe (it's used by in-process // stack dumping signal handler). NO malloc or stdio is allowed here. diff --git a/src/butil/iobuf_profiler.cpp b/src/butil/iobuf_profiler.cpp index 15356955a6..11353bebf0 100644 --- a/src/butil/iobuf_profiler.cpp +++ b/src/butil/iobuf_profiler.cpp @@ -24,7 +24,7 @@ #include "butil/hash.h" #include -extern int __attribute__((weak)) GetStackTrace(void** result, int max_depth, int skip_count); +extern int BAIDU_WEAK GetStackTrace(void** result, int max_depth, int skip_count); namespace butil { @@ -296,9 +296,7 @@ void SubmitIOBufSample(IOBuf::Block* block, int64_t ref) { auto sample = IOBufSample::New(); sample->block = block; sample->count = ref; - sample->nframes = GetStackTrace(sample->stack, - arraysize(sample->stack), - 0); // may lock + sample->nframes = GetStackTrace(sample->stack, arraysize(sample->stack), 0); IOBufProfiler::GetInstance()->Submit(sample); } diff --git a/src/butil/object_pool.h b/src/butil/object_pool.h index b663fe84e2..c8690821ef 100644 --- a/src/butil/object_pool.h +++ b/src/butil/object_pool.h @@ -69,6 +69,11 @@ template struct ObjectPoolValidator { namespace butil { +// Whether if FreeChunk of LocalPool is empty. +template inline bool local_pool_free_empty() { + return ObjectPool::singleton()->local_free_empty(); +} + // Get an object typed |T|. The object should be cleared before usage. // NOTE: T must be default-constructible. template inline T* get_object() { diff --git a/src/butil/object_pool_inl.h b/src/butil/object_pool_inl.h index 0371eff09f..6f10e03abb 100644 --- a/src/butil/object_pool_inl.h +++ b/src/butil/object_pool_inl.h @@ -216,6 +216,10 @@ class BAIDU_CACHELINE_ALIGNMENT ObjectPool { return -1; } + inline bool free_empty() const { + return 0 == _cur_free.nfree; + } + private: ObjectPool* _pool; Block* _cur_block; @@ -223,6 +227,14 @@ class BAIDU_CACHELINE_ALIGNMENT ObjectPool { FreeChunk _cur_free; }; + inline bool local_free_empty() { + LocalPool* lp = get_or_new_local_pool(); + if (BAIDU_LIKELY(lp != NULL)) { + return lp->free_empty(); + } + return true; + } + inline T* get_object() { LocalPool* lp = get_or_new_local_pool(); if (BAIDU_LIKELY(lp != NULL)) { diff --git a/src/butil/third_party/dynamic_annotations/dynamic_annotations.c b/src/butil/third_party/dynamic_annotations/dynamic_annotations.c index a68a826bad..b746203cd6 100644 --- a/src/butil/third_party/dynamic_annotations/dynamic_annotations.c +++ b/src/butil/third_party/dynamic_annotations/dynamic_annotations.c @@ -255,7 +255,7 @@ static int GetRunningOnValgrind(void) { } /* See the comments in dynamic_annotations.h */ -int __attribute__((weak)) RunningOnValgrind(void) { +int DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK RunningOnValgrind(void) { static volatile int running_on_valgrind = -1; /* C doesn't have thread-safe initialization of statics, and we don't want to depend on pthread_once here, so hack it. */ diff --git a/src/butil/third_party/symbolize/symbolize.cc b/src/butil/third_party/symbolize/symbolize.cc index bb693a7237..e52e50f950 100644 --- a/src/butil/third_party/symbolize/symbolize.cc +++ b/src/butil/third_party/symbolize/symbolize.cc @@ -830,6 +830,14 @@ bool BAIDU_WEAK Symbolize(void *pc, char *out, int out_size) { return SymbolizeAndDemangle(pc, out, out_size); } +bool BAIDU_WEAK SymbolizeAndAddress(void *pc, void **out) { + Dl_info info{}; + if (dladdr(pc, &info)) { + *out = info.dli_saddr; + } + return NULL != info.dli_saddr; +} + _END_GOOGLE_NAMESPACE_ #else /* HAVE_SYMBOLIZE */ diff --git a/src/butil/third_party/symbolize/symbolize.h b/src/butil/third_party/symbolize/symbolize.h index 4c857c129a..cb9f32ebac 100644 --- a/src/butil/third_party/symbolize/symbolize.h +++ b/src/butil/third_party/symbolize/symbolize.h @@ -150,6 +150,8 @@ _START_GOOGLE_NAMESPACE_ // returns false. bool Symbolize(void *pc, char *out, int out_size); +bool SymbolizeAndAddress(void *pc, void **out); + _END_GOOGLE_NAMESPACE_ #endif // BUTIL_SYMBOLIZE_H_ diff --git a/test/Makefile b/test/Makefile index 97bde8f534..bbe10b64da 100644 --- a/test/Makefile +++ b/test/Makefile @@ -46,107 +46,108 @@ ifeq ($(SYSTEM),Darwin) SOEXT = dylib endif -TEST_BUTIL_SOURCES = \ - at_exit_unittest.cc \ - atomicops_unittest.cc \ - base64_unittest.cc \ - base64url_unittest.cc \ - big_endian_unittest.cc \ - bits_unittest.cc \ - hash_tables_unittest.cc \ - linked_list_unittest.cc \ - mru_cache_unittest.cc \ - small_map_unittest.cc \ - stack_container_unittest.cc \ - mpsc_queue_unittest.cc \ - cpu_unittest.cc \ - crash_logging_unittest.cc \ - leak_tracker_unittest.cc \ - stack_trace_unittest.cc \ - environment_unittest.cc \ - file_util_unittest.cc \ - dir_reader_posix_unittest.cc \ - file_path_unittest.cc \ - file_unittest.cc \ - scoped_temp_dir_unittest.cc \ - guid_unittest.cc \ - hash_unittest.cc \ - lazy_instance_unittest.cc \ - aligned_memory_unittest.cc \ - linked_ptr_unittest.cc \ - ref_counted_memory_unittest.cc \ - ref_counted_unittest.cc \ - scoped_ptr_unittest.cc \ - scoped_vector_unittest.cc \ - singleton_unittest.cc \ - weak_ptr_unittest.cc \ - observer_list_unittest.cc \ - file_descriptor_shuffle_unittest.cc \ - rand_util_unittest.cc \ - safe_numerics_unittest.cc \ - scoped_clear_errno_unittest.cc \ - scoped_generic_unittest.cc \ - security_unittest.cc \ - sha1_unittest.cc \ - stl_util_unittest.cc \ - nullable_string16_unittest.cc \ - safe_sprintf_unittest.cc \ - string16_unittest.cc \ - stringprintf_unittest.cc \ - string_number_conversions_unittest.cc \ - string_piece_unittest.cc \ - string_split_unittest.cc \ - string_tokenizer_unittest.cc \ - string_util_unittest.cc \ - stringize_macros_unittest.cc \ - sys_string_conversions_unittest.cc \ - utf_offset_string_conversions_unittest.cc \ - utf_string_conversions_unittest.cc \ - cancellation_flag_unittest.cc \ - condition_variable_unittest.cc \ - lock_unittest.cc \ - waitable_event_unittest.cc \ - type_traits_unittest.cc \ - non_thread_safe_unittest.cc \ - platform_thread_unittest.cc \ - simple_thread_unittest.cc \ - thread_checker_unittest.cc \ - thread_collision_warner_unittest.cc \ - thread_id_name_manager_unittest.cc \ - thread_local_storage_unittest.cc \ - thread_local_unittest.cc \ - watchdog_unittest.cc \ - time_unittest.cc \ - version_unittest.cc \ - logging_unittest.cc \ - cacheline_unittest.cpp \ - class_name_unittest.cpp \ - endpoint_unittest.cpp \ - unique_ptr_unittest.cpp \ - errno_unittest.cpp \ - fd_guard_unittest.cpp \ - file_watcher_unittest.cpp \ - find_cstr_unittest.cpp \ - scoped_lock_unittest.cpp \ - status_unittest.cpp \ - string_printf_unittest.cpp \ - string_splitter_unittest.cpp \ - synchronous_event_unittest.cpp \ - temp_file_unittest.cpp \ - baidu_thread_local_unittest.cpp \ - thread_key_unittest.cpp \ - baidu_time_unittest.cpp \ - flat_map_unittest.cpp \ - crc32c_unittest.cc \ - iobuf_unittest.cpp \ - object_pool_unittest.cpp \ - recordio_unittest.cpp \ - test_switches.cc \ - scoped_locale.cc \ - popen_unittest.cpp \ - bounded_queue_unittest.cc \ - butil_unittest_main.cpp \ - scope_guard_unittest.cc +TEST_BUTIL_SOURCES = stack_trace_unittest.cc +#TEST_BUTIL_SOURCES = \ +# at_exit_unittest.cc \ +# atomicops_unittest.cc \ +# base64_unittest.cc \ +# base64url_unittest.cc \ +# big_endian_unittest.cc \ +# bits_unittest.cc \ +# hash_tables_unittest.cc \ +# linked_list_unittest.cc \ +# mru_cache_unittest.cc \ +# small_map_unittest.cc \ +# stack_container_unittest.cc \ +# mpsc_queue_unittest.cc \ +# cpu_unittest.cc \ +# crash_logging_unittest.cc \ +# leak_tracker_unittest.cc \ +# stack_trace_unittest.cc \ +# environment_unittest.cc \ +# file_util_unittest.cc \ +# dir_reader_posix_unittest.cc \ +# file_path_unittest.cc \ +# file_unittest.cc \ +# scoped_temp_dir_unittest.cc \ +# guid_unittest.cc \ +# hash_unittest.cc \ +# lazy_instance_unittest.cc \ +# aligned_memory_unittest.cc \ +# linked_ptr_unittest.cc \ +# ref_counted_memory_unittest.cc \ +# ref_counted_unittest.cc \ +# scoped_ptr_unittest.cc \ +# scoped_vector_unittest.cc \ +# singleton_unittest.cc \ +# weak_ptr_unittest.cc \ +# observer_list_unittest.cc \ +# file_descriptor_shuffle_unittest.cc \ +# rand_util_unittest.cc \ +# safe_numerics_unittest.cc \ +# scoped_clear_errno_unittest.cc \ +# scoped_generic_unittest.cc \ +# security_unittest.cc \ +# sha1_unittest.cc \ +# stl_util_unittest.cc \ +# nullable_string16_unittest.cc \ +# safe_sprintf_unittest.cc \ +# string16_unittest.cc \ +# stringprintf_unittest.cc \ +# string_number_conversions_unittest.cc \ +# string_piece_unittest.cc \ +# string_split_unittest.cc \ +# string_tokenizer_unittest.cc \ +# string_util_unittest.cc \ +# stringize_macros_unittest.cc \ +# sys_string_conversions_unittest.cc \ +# utf_offset_string_conversions_unittest.cc \ +# utf_string_conversions_unittest.cc \ +# cancellation_flag_unittest.cc \ +# condition_variable_unittest.cc \ +# lock_unittest.cc \ +# waitable_event_unittest.cc \ +# type_traits_unittest.cc \ +# non_thread_safe_unittest.cc \ +# platform_thread_unittest.cc \ +# simple_thread_unittest.cc \ +# thread_checker_unittest.cc \ +# thread_collision_warner_unittest.cc \ +# thread_id_name_manager_unittest.cc \ +# thread_local_storage_unittest.cc \ +# thread_local_unittest.cc \ +# watchdog_unittest.cc \ +# time_unittest.cc \ +# version_unittest.cc \ +# logging_unittest.cc \ +# cacheline_unittest.cpp \ +# class_name_unittest.cpp \ +# endpoint_unittest.cpp \ +# unique_ptr_unittest.cpp \ +# errno_unittest.cpp \ +# fd_guard_unittest.cpp \ +# file_watcher_unittest.cpp \ +# find_cstr_unittest.cpp \ +# scoped_lock_unittest.cpp \ +# status_unittest.cpp \ +# string_printf_unittest.cpp \ +# string_splitter_unittest.cpp \ +# synchronous_event_unittest.cpp \ +# temp_file_unittest.cpp \ +# baidu_thread_local_unittest.cpp \ +# thread_key_unittest.cpp \ +# baidu_time_unittest.cpp \ +# flat_map_unittest.cpp \ +# crc32c_unittest.cc \ +# iobuf_unittest.cpp \ +# object_pool_unittest.cpp \ +# recordio_unittest.cpp \ +# test_switches.cc \ +# scoped_locale.cc \ +# popen_unittest.cpp \ +# bounded_queue_unittest.cc \ +# butil_unittest_main.cpp \ +# scope_guard_unittest.cc ifeq ($(SYSTEM), Linux) TEST_BUTIL_SOURCES += test_file_util_linux.cc \ diff --git a/test/run_tests.sh b/test/run_tests.sh index ebd648402a..d318e25487 100755 --- a/test/run_tests.sh +++ b/test/run_tests.sh @@ -22,7 +22,7 @@ rm core.* test_num=0 failed_test="" rc=0 -test_bins="test_butil test_bvar bthread*unittest brpc*unittest" +test_bins="test_butil" for test_bin in $test_bins; do test_num=$((test_num + 1)) >&2 echo "[runtest] $test_bin" diff --git a/test/stack_trace_unittest.cc b/test/stack_trace_unittest.cc index 1b96161001..53ee8f3d69 100644 --- a/test/stack_trace_unittest.cc +++ b/test/stack_trace_unittest.cc @@ -8,8 +8,33 @@ #include "butil/debug/stack_trace.h" #include "butil/logging.h" +#include "butil/scoped_lock.h" #include +extern "C" { +void TestFindSymbol1(); +void TestFindSymbol2(); +void TestFindSymbol3(); + +void TestFindSymbol1() { + butil::debug::StackTrace trace; + ASSERT_TRUE(trace.ToString().find("TestFindSymbol1") != std::string::npos) << trace.ToString(); + ASSERT_TRUE(trace.ToString().find("TestFindSymbol2") != std::string::npos) << trace.ToString(); + ASSERT_TRUE(trace.ToString().find("TestFindSymbol3") == std::string::npos) << trace.ToString(); + ASSERT_TRUE(trace.FindSymbol((void*)TestFindSymbol2)) << trace.ToString(); + ASSERT_TRUE(trace.FindSymbol((void*)TestFindSymbol1)) << trace.ToString(); + ASSERT_FALSE(trace.FindSymbol((void*)TestFindSymbol3)) << trace.ToString(); +} + +void TestFindSymbol2() { + TestFindSymbol1(); +} + +void TestFindSymbol3() { + TestFindSymbol1(); +} +} + namespace butil { namespace debug { @@ -198,5 +223,9 @@ TEST_F(StackTraceTest, itoa_r) { } #endif // defined(OS_POSIX) && !defined(OS_ANDROID) +TEST_F(StackTraceTest, find) { + TestFindSymbol2(); +} + } // namespace debug } // namespace butil