This is an automated email from the ASF dual-hosted git repository.

wwbmmm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 75763d4c Fix malloc deadlock caused by contention profiler (#2684)
75763d4c is described below

commit 75763d4c5064d6cc847fc93383087bd8cdbaf1be
Author: Bright Chen <chenguangmin...@foxmail.com>
AuthorDate: Tue Jul 23 10:25:56 2024 +0800

    Fix malloc deadlock caused by contention profiler (#2684)
---
 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                                |  3 +-
 src/bthread/mutex.cpp                              | 43 ++++++++---
 src/butil/debug/stack_trace.cc                     |  9 ++-
 src/butil/debug/stack_trace.h                      | 12 ++-
 src/butil/debug/stack_trace_posix.cc               | 20 ++++-
 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       | 86 +++++++++++++++-------
 src/butil/third_party/symbolize/symbolize.h        |  2 +
 test/stack_trace_unittest.cc                       | 79 +++++++++++++++++---
 15 files changed, 221 insertions(+), 68 deletions(-)

diff --git a/src/brpc/builtin/hotspots_service.cpp 
b/src/brpc/builtin/hotspots_service.cpp
index abb0bb4e..f714843d 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 eba71377..e9591698 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 33a0c0c6..99a3e085 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 40af0204..8fcfae9c 100644
--- a/src/brpc/socket.cpp
+++ b/src/brpc/socket.cpp
@@ -57,8 +57,7 @@
 #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 87a88876..8212c84c 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 <pthread.h>
-#include <execinfo.h>
 #include <dlfcn.h>                               // dlsym
 #include <fcntl.h>                               // 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/mutex.h"                       // bthread_mutex_t
 #include "bthread/sys_futex.h"
@@ -44,16 +46,12 @@
 #include "butil/debug/stack_trace.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;
@@ -468,6 +466,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;
+
 // ++tls_pthread_lock_count when pthread locking,
 // --tls_pthread_lock_count when pthread unlocking.
 // Only when it is equal to 0, it is safe for the bthread to be scheduled.
@@ -559,6 +561,26 @@ 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(true); // May lock.
+    if (0 == stack.FrameCount()) {
+        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<SampledContention>()) {
+        // In malloc call stack, can not submit contention.
+        if (stack.FindSymbol((void*)malloc)) {
+            return;
+        }
+    }
+
     auto sc = butil::get_object<SampledContention>();
     // Normalize duration_us and count so that they're addable in later
     // processings. Notice that sampling_range is adjusted periodically by
@@ -566,11 +588,10 @@ 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
+    sc->nframes = stack.CopyAddressTo(sc->stack, arraysize(sc->stack));
     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 d8d60ecb..38abede9 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_)
@@ -31,6 +28,12 @@ const void *const *StackTrace::Addresses(size_t* count) 
const {
   return NULL;
 }
 
+size_t StackTrace::CopyAddressTo(void** buffer, size_t max_nframes) const {
+    size_t nframes = std::min(count_, max_nframes);
+    memcpy(buffer, trace_, nframes * sizeof(void*));
+    return nframes;
+}
+
 std::string StackTrace::ToString() const {
   std::stringstream stream;
 #if !defined(__UCLIBC__)
diff --git a/src/butil/debug/stack_trace.h b/src/butil/debug/stack_trace.h
index 574f12d6..5c6545ad 100644
--- a/src/butil/debug/stack_trace.h
+++ b/src/butil/debug/stack_trace.h
@@ -59,12 +59,20 @@ 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;
 
+  // Gets the number of frames in the stack trace.
+  size_t FrameCount() const { return count_; }
+
+  // Copies the stack trace to |buffer|,
+  // where the size is min(max_nframes, frame_count()).
+  size_t CopyAddressTo(void** dest, size_t max_nframes) 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 df16d498..878f94a7 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 {
@@ -768,6 +768,24 @@ StackTrace::StackTrace(bool exclude_self) {
   }
 }
 
+bool StackTrace::FindSymbol(void* symbol) const {
+#if !defined(__UCLIBC__)
+  for (size_t i = 0; i < count_; ++i) {
+    uint64_t 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<char*>(trace_[i]) - 1;
+    if (!google::SymbolizeAddress(address, &saddr)) {
+      continue;
+    }
+    if ((void*)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 15356955..11353beb 100644
--- a/src/butil/iobuf_profiler.cpp
+++ b/src/butil/iobuf_profiler.cpp
@@ -24,7 +24,7 @@
 #include "butil/hash.h"
 #include <execinfo.h>
 
-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 b663fe84..c8690821 100644
--- a/src/butil/object_pool.h
+++ b/src/butil/object_pool.h
@@ -69,6 +69,11 @@ template <typename T> struct ObjectPoolValidator {
 
 namespace butil {
 
+// Whether if FreeChunk of LocalPool is empty.
+template <typename T> inline bool local_pool_free_empty() {
+    return ObjectPool<T>::singleton()->local_free_empty();
+}
+
 // Get an object typed |T|. The object should be cleared before usage.
 // NOTE: T must be default-constructible.
 template <typename T> inline T* get_object() {
diff --git a/src/butil/object_pool_inl.h b/src/butil/object_pool_inl.h
index 0371eff0..6f10e03a 100644
--- a/src/butil/object_pool_inl.h
+++ b/src/butil/object_pool_inl.h
@@ -216,6 +216,10 @@ public:
             return -1;
         }
 
+        inline bool free_empty() const {
+            return 0 == _cur_free.nfree;
+        }
+
     private:
         ObjectPool* _pool;
         Block* _cur_block;
@@ -223,6 +227,14 @@ public:
         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 a68a826b..b746203c 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 bb693a72..80a8ce3d 100644
--- a/src/butil/third_party/symbolize/symbolize.cc
+++ b/src/butil/third_party/symbolize/symbolize.cc
@@ -279,8 +279,8 @@ bool BAIDU_WEAK GetSectionHeaderByName(int fd, const char 
*name, size_t name_len
 // inlined.
 static ATTRIBUTE_NOINLINE bool
 FindSymbol(uint64_t pc, const int fd, char *out, int out_size,
-           uint64_t symbol_offset, const ElfW(Shdr) *strtab,
-           const ElfW(Shdr) *symtab) {
+           uint64_t *out_saddr, uint64_t symbol_offset,
+           const ElfW(Shdr) *strtab, const ElfW(Shdr) *symtab) {
   if (symtab == NULL) {
     return false;
   }
@@ -311,10 +311,15 @@ FindSymbol(uint64_t pc, const int fd, char *out, int 
out_size,
       if (symbol.st_value != 0 &&  // Skip null value symbols.
           symbol.st_shndx != 0 &&  // Skip undefined symbols.
           start_address <= pc && pc < end_address) {
-        ssize_t len1 = ReadFromOffset(fd, out, out_size,
-                                      strtab->sh_offset + symbol.st_name);
-        if (len1 <= 0 || memchr(out, '\0', out_size) == NULL) {
-          return false;
+        if (NULL != out) {
+          ssize_t len1 = ReadFromOffset(
+              fd, out, out_size, strtab->sh_offset + symbol.st_name);
+          if (len1 <= 0 || memchr(out, '\0', out_size) == NULL) {
+            return false;
+          }
+        }
+        if (NULL != out_saddr) {
+          *out_saddr = start_address;
         }
         return true;  // Obtained the symbol name.
       }
@@ -330,6 +335,7 @@ FindSymbol(uint64_t pc, const int fd, char *out, int 
out_size,
 // false.
 static bool GetSymbolFromObjectFile(const int fd, uint64_t pc,
                                     char *out, int out_size,
+                                    uint64_t *out_saddr,
                                     uint64_t map_start_address) {
   // Read the ELF header.
   ElfW(Ehdr) elf_header;
@@ -351,8 +357,8 @@ static bool GetSymbolFromObjectFile(const int fd, uint64_t 
pc,
                              symtab.sh_link * sizeof(symtab))) {
       return false;
     }
-    if (FindSymbol(pc, fd, out, out_size, symbol_offset,
-                   &strtab, &symtab)) {
+    if (FindSymbol(pc, fd, out, out_size, out_saddr,
+                   symbol_offset, &strtab, &symtab)) {
       return true;  // Found the symbol in a regular symbol table.
     }
   }
@@ -364,8 +370,8 @@ static bool GetSymbolFromObjectFile(const int fd, uint64_t 
pc,
                              symtab.sh_link * sizeof(symtab))) {
       return false;
     }
-    if (FindSymbol(pc, fd, out, out_size, symbol_offset,
-                   &strtab, &symtab)) {
+    if (FindSymbol(pc, fd, out, out_size, out_saddr,
+                   symbol_offset, &strtab, &symtab)) {
       return true;  // Found the symbol in a dynamic symbol table.
     }
   }
@@ -727,17 +733,21 @@ void SafeAppendHexNumber(uint64_t value, char* dest, int 
dest_size) {
 // To keep stack consumption low, we would like this function to not
 // get inlined.
 static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
-                                                    int out_size) {
+                                                    int out_size,
+                                                    uint64_t *out_saddr) {
   uint64_t pc0 = reinterpret_cast<uintptr_t>(pc);
   uint64_t start_address = 0;
   uint64_t base_address = 0;
   int object_fd = -1;
 
-  if (out_size < 1) {
+  if ((NULL == out || out_size < 1) &&
+      NULL == out_saddr) {
     return false;
   }
-  out[0] = '\0';
-  SafeAppendString("(", out, out_size);
+  if (NULL != out) {
+    out[0] = '\0';
+    SafeAppendString("(", out, out_size);
+  }
 
   if (g_symbolize_open_object_file_callback) {
     object_fd = g_symbolize_open_object_file_callback(pc0, start_address,
@@ -752,7 +762,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void 
*pc, char *out,
 
   // Check whether a file name was returned.
   if (object_fd < 0) {
-    if (out[1]) {
+    if (NULL != out && out[1] && NULL == out_saddr) {
       // The object file containing PC was determined successfully however the
       // object file was not opened successfully.  This is still considered
       // success because the object file name and offset are known and tools
@@ -785,12 +795,15 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void 
*pc, char *out,
     }
   }
   if (!GetSymbolFromObjectFile(wrapped_object_fd.get(), pc0,
-                               out, out_size, start_address)) {
+                               out, out_size, out_saddr,
+                               start_address)) {
     return false;
   }
 
-  // Symbolization succeeded.  Now we try to demangle the symbol.
-  DemangleInplace(out, out_size);
+  if (NULL != out) {
+    // Symbolization succeeded.  Now we try to demangle the symbol.
+    DemangleInplace(out, out_size);
+  }
   return true;
 }
 
@@ -804,17 +817,24 @@ _END_GOOGLE_NAMESPACE_
 _START_GOOGLE_NAMESPACE_
 
 static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
-                                                    int out_size) {
-  Dl_info info;
-  if (dladdr(pc, &info)) {
-    if ((int)strlen(info.dli_sname) < out_size) {
-      strcpy(out, info.dli_sname);
-      // Symbolization succeeded.  Now we try to demangle the symbol.
-      DemangleInplace(out, out_size);
-      return true;
+                                                    int out_size,
+                                                    uint64_t *out_saddr) {
+  Dl_info info{};
+  if (0 == dladdr(pc, &info)) {
+    return false;
+  }
+  if (NULL != out) {
+    if ((int)strlen(info.dli_sname) >= out_size) {
+      return false;
     }
+    strcpy(out, info.dli_sname);
+    // Symbolization succeeded.  Now we try to demangle the symbol.
+    DemangleInplace(out, out_size);
   }
-  return false;
+  if (NULL != out_saddr) {
+    *out_saddr = (uint64_t)info.dli_saddr;
+  }
+  return true;
 }
 
 _END_GOOGLE_NAMESPACE_
@@ -827,7 +847,12 @@ _START_GOOGLE_NAMESPACE_
 
 bool BAIDU_WEAK Symbolize(void *pc, char *out, int out_size) {
   SAFE_ASSERT(out_size >= 0);
-  return SymbolizeAndDemangle(pc, out, out_size);
+  return SymbolizeAndDemangle(pc, out, out_size, NULL);
+}
+
+bool BAIDU_WEAK SymbolizeAddress(void *pc, uint64_t *out) {
+  SAFE_ASSERT(NULL != out);
+  return SymbolizeAndDemangle(pc, NULL, 0, out);
 }
 
 _END_GOOGLE_NAMESPACE_
@@ -846,6 +871,11 @@ bool BAIDU_WEAK Symbolize(void *pc, char *out, int 
out_size) {
   return false;
 }
 
+bool BAIDU_WEAK SymbolizeAddress(void *pc, uint64_t *out) {
+  assert(0);
+  return false;
+}
+
 _END_GOOGLE_NAMESPACE_
 
 #endif
diff --git a/src/butil/third_party/symbolize/symbolize.h 
b/src/butil/third_party/symbolize/symbolize.h
index 4c857c12..7bd31e41 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 SymbolizeAddress(void *pc, uint64_t *out);
+
 _END_GOOGLE_NAMESPACE_
 
 #endif  // BUTIL_SYMBOLIZE_H_
diff --git a/test/stack_trace_unittest.cc b/test/stack_trace_unittest.cc
index e2052b6f..35a226e9 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 <gtest/gtest.h>
 
+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 {
 
@@ -107,20 +132,25 @@ TEST_F(StackTraceTest, MAYBE_OutputToStream) {
 #endif  // define(OS_MACOSX)
 }
 
-// The test is used for manual testing, e.g., to see the raw output.
-TEST_F(StackTraceTest, DebugOutputToStream) {
-  {
-    StackTrace trace;
-    std::ostringstream os;
-    trace.OutputToStream(&os);
-    VLOG(1) << os.str();
-  }
-  {
-    StackTrace trace(true);
+void CheckDebugOutputToStream(bool exclude_self) {
+    StackTrace trace(exclude_self);
+    size_t count;
+    const void* const* addrs = trace.Addresses(&count);
+    ASSERT_EQ(count, trace.FrameCount());
+    void* addr = malloc(sizeof(void*) * count);
+    size_t copied_count = trace.CopyAddressTo((void**)addr, count);
+    ASSERT_EQ(count, copied_count);
+    ASSERT_EQ(0, memcmp(addrs, addr, sizeof(void*) * count));
+
     std::ostringstream os;
     trace.OutputToStream(&os);
     VLOG(1) << os.str();
-  }
+}
+
+// The test is used for manual testing, e.g., to see the raw output.
+TEST_F(StackTraceTest, DebugOutputToStream) {
+  CheckDebugOutputToStream(false);
+  CheckDebugOutputToStream(true);
 }
 
 // The test is used for manual testing, e.g., to see the raw output.
@@ -206,5 +236,32 @@ TEST_F(StackTraceTest, itoa_r) {
 }
 #endif  // defined(OS_POSIX) && !defined(OS_ANDROID)
 
+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();
+}
+
+TEST_F(StackTraceTest, find_symbol) {
+    ::TestFindSymbol2();
+    TestFindSymbol2();
+}
+
 }  // namespace debug
 }  // namespace butil


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@brpc.apache.org
For additional commands, e-mail: dev-h...@brpc.apache.org

Reply via email to