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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]