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

guangmingchen 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 7a7d1c83 Support AddressSanitizer (#2890)
7a7d1c83 is described below

commit 7a7d1c83e54952302126f00263196ee77d35ba8f
Author: Bright Chen <chenguangmin...@foxmail.com>
AuthorDate: Wed Mar 26 20:07:45 2025 +0800

    Support AddressSanitizer (#2890)
    
    * Support AddressSanitizer
    
    * Add gperftools helper header
---
 .github/workflows/ci-linux.yml                     | 30 +++++--
 BUILD.bazel                                        |  8 +-
 CMakeLists.txt                                     |  5 ++
 bazel/config/BUILD.bazel                           |  6 ++
 config_brpc.sh                                     | 18 ++++-
 docs/cn/getting_started.md                         |  2 -
 docs/cn/sanitizers.md                              | 32 ++++++++
 docs/en/getting_started.md                         |  2 -
 src/brpc/details/http_message.h                    |  2 +-
 src/brpc/details/http_parser.cpp                   |  4 +-
 .../policy/consistent_hashing_load_balancer.cpp    |  5 +-
 src/brpc/policy/hasher.cpp                         |  6 +-
 src/brpc/policy/http_rpc_protocol.cpp              |  2 +-
 src/brpc/socket.cpp                                |  4 +-
 src/bthread/bthread.cpp                            |  9 ++-
 src/bthread/execution_queue.cpp                    |  1 -
 src/bthread/execution_queue_inl.h                  | 12 ++-
 src/bthread/stack.cpp                              |  2 +-
 src/bthread/stack.h                                |  5 +-
 src/bthread/stack_inl.h                            | 92 +++++++++++++++++++++-
 src/bthread/task_control.cpp                       |  5 +-
 src/bthread/task_group.cpp                         | 68 +++++++++++++++-
 src/bthread/task_group.h                           |  7 +-
 src/bthread/task_group_inl.h                       |  8 +-
 src/bthread/task_tracer.cpp                        |  2 +
 src/butil/compiler_specific.h                      | 15 +++-
 src/butil/debug/address_annotations.h              | 42 ++++++++++
 src/butil/debug/leak_annotations.h                 |  8 +-
 src/butil/logging.cc                               |  9 ++-
 src/butil/object_pool.h                            |  5 ++
 src/butil/object_pool_inl.h                        | 70 ++++++++++++----
 test/BUILD.bazel                                   | 10 +++
 test/Makefile                                      |  2 +-
 test/brpc_builtin_service_unittest.cpp             |  8 +-
 test/brpc_event_dispatcher_unittest.cpp            |  6 +-
 test/brpc_h2_unsent_message_unittest.cpp           |  2 +-
 test/brpc_http_rpc_protocol_unittest.cpp           | 24 ++++--
 test/brpc_hulu_pbrpc_protocol_unittest.cpp         |  1 -
 test/brpc_input_messenger_unittest.cpp             |  7 +-
 test/brpc_load_balancer_unittest.cpp               |  2 +-
 test/brpc_protobuf_json_unittest.cpp               | 20 ++---
 test/brpc_snappy_compress_unittest.cpp             |  2 +-
 test/brpc_socket_unittest.cpp                      |  2 +-
 test/bthread_cond_unittest.cpp                     |  2 +-
 test/bthread_dispatcher_unittest.cpp               |  5 +-
 test/bthread_execution_queue_unittest.cpp          |  2 +-
 test/bthread_fd_unittest.cpp                       |  5 +-
 test/bthread_mutex_unittest.cpp                    |  4 +-
 test/bthread_rwlock_unittest.cpp                   |  3 +-
 test/bthread_setconcurrency_unittest.cpp           |  4 +-
 test/bthread_unittest.cpp                          | 14 +++-
 test/bthread_work_stealing_queue_unittest.cpp      |  2 +
 test/bvar_lock_timer_unittest.cpp                  |  4 +-
 test/gperftools_helper.h                           | 34 ++++++++
 test/iobuf_unittest.cpp                            |  4 +
 test/logging_unittest.cc                           |  4 +-
 test/mpsc_queue_unittest.cc                        |  3 -
 test/popen_unittest.cpp                            |  1 +
 test/run_tests.sh                                  |  5 +-
 test/security_unittest.cc                          |  5 ++
 test/stack_trace_unittest.cc                       |  1 +
 test/thread_key_unittest.cpp                       |  2 -
 62 files changed, 554 insertions(+), 122 deletions(-)

diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml
index f87d6519..64dedb6d 100644
--- a/.github/workflows/ci-linux.yml
+++ b/.github/workflows/ci-linux.yml
@@ -61,7 +61,7 @@ jobs:
     - uses: ./.github/actions/install-all-dependences
     - uses: ./.github/actions/init-make-config
       with:
-       options: --cc=gcc --cxx=g++ --with-thrift --with-glog --with-rdma  
--with-debug-bthread-sche-safety --with-debug-lock --with-bthread-tracer 
--werror
+       options: --cc=gcc --cxx=g++ --with-thrift --with-glog --with-rdma  
--with-debug-bthread-sche-safety --with-debug-lock --with-bthread-tracer 
--werror --with-asan
     - name: compile
       run: |
         make -j ${{env.proc_num}}
@@ -76,7 +76,7 @@ jobs:
            export CC=gcc && export CXX=g++
            mkdir build
            cd build
-           cmake -DWITH_MESALINK=OFF -DWITH_GLOG=ON -DWITH_THRIFT=ON 
-DWITH_RDMA=ON -DWITH_DEBUG_BTHREAD_SCHE_SAFETY=ON -DWITH_DEBUG_LOCK=ON 
-WITH_BTHREAD_TRACER=ON ..
+           cmake -DWITH_MESALINK=OFF -DWITH_GLOG=ON -DWITH_THRIFT=ON 
-DWITH_RDMA=ON -DWITH_DEBUG_BTHREAD_SCHE_SAFETY=ON -DWITH_DEBUG_LOCK=ON 
-DWITH_BTHREAD_TRACER=ON -DWITH_ASAN=ON ..
     - name: compile
       run: |
            cd build
@@ -86,7 +86,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
     - uses: actions/checkout@v2
-    - run: bazel build --verbose_failures --define with_mesalink=false 
--define with_glog=true --define with_thrift=true --define 
with_debug_bthread_sche_safety=true --define with_debug_lock=true -- //... 
-//example/...
+    - run: bazel build --verbose_failures --define with_mesalink=false 
--define with_glog=true --define with_thrift=true --define 
with_debug_bthread_sche_safety=true --define with_debug_lock=true --define 
with_asan=true -- //... -//example/...
 
   clang-compile-with-make:
     runs-on: ubuntu-22.04
@@ -135,7 +135,7 @@ jobs:
     - uses: ./.github/actions/install-all-dependences
     - uses: ./.github/actions/init-make-config
       with:
-        options: --cc=clang --cxx=clang++ --with-thrift --with-glog 
--with-rdma  --with-debug-bthread-sche-safety --with-debug-lock 
--with-bthread-tracer --werror
+        options: --cc=clang --cxx=clang++ --with-thrift --with-glog 
--with-rdma  --with-debug-bthread-sche-safety --with-debug-lock 
--with-bthread-tracer --werror --with-asan
     - name: compile
       run: |
            make -j ${{env.proc_num}}
@@ -150,7 +150,7 @@ jobs:
            export CC=clang && export CXX=clang++
            mkdir build
            cd build
-           cmake -DWITH_MESALINK=OFF -DWITH_GLOG=ON -DWITH_THRIFT=ON 
-DWITH_RDMA=ON -DWITH_DEBUG_BTHREAD_SCHE_SAFETY=ON -DWITH_DEBUG_LOCK=ON 
-WITH_BTHREAD_TRACER=ON ..
+           cmake -DWITH_MESALINK=OFF -DWITH_GLOG=ON -DWITH_THRIFT=ON 
-DWITH_RDMA=ON -DWITH_DEBUG_BTHREAD_SCHE_SAFETY=ON -DWITH_DEBUG_LOCK=ON 
-DWITH_BTHREAD_TRACER=ON -DWITH_ASAN=ON ..
     - name: compile
       run: |
            cd build
@@ -160,7 +160,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
     - uses: actions/checkout@v2
-    - run: bazel build --verbose_failures --action_env=CC=clang --define 
with_mesalink=false --define with_glog=true --define with_thrift=true --define 
with_debug_bthread_sche_safety=true --define with_debug_lock=true -- //... 
-//example/...
+    - run: bazel build --verbose_failures --action_env=CC=clang --define 
with_mesalink=false --define with_glog=true --define with_thrift=true --define 
with_debug_bthread_sche_safety=true --define with_debug_lock=true --define 
with_asan=true -- //... -//example/...
 
   clang-unittest:
     runs-on: ubuntu-22.04
@@ -179,3 +179,21 @@ jobs:
         run: |
           cd test
           sh ./run_tests.sh
+
+  clang-unittest-asan:
+    runs-on: ubuntu-22.04
+    steps:
+      - uses: actions/checkout@v2
+      - uses: ./.github/actions/install-essential-dependences
+      - uses: ./.github/actions/init-ut-make-config
+        with:
+          options: --cc=clang-12 --cxx=clang++-12 --with-bthread-tracer 
--with-asan
+      - name: compile tests
+        run: |
+          cat config.mk
+          cd test
+          make NEED_GPERFTOOLS=0 -j ${{env.proc_num}}
+      - name: run tests
+        run: |
+          cd test
+          sh ./run_tests.sh
diff --git a/BUILD.bazel b/BUILD.bazel
index 610d01d0..4e0217fc 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -53,6 +53,9 @@ COPTS = [
 }) + select({
     "@platforms//cpu:x86_64": ["-DBRPC_BTHREAD_TRACER"],
     "//conditions:default": [],
+}) + select({
+    "//bazel/config:brpc_with_asan": ["-fsanitize=address"],
+    "//conditions:default": [""],
 })
 
 LINKOPTS = [
@@ -87,7 +90,10 @@ LINKOPTS = [
         "-libverbs",
     ],
     "//conditions:default": [],
-})
+}) + select({
+        "//bazel/config:brpc_with_asan": ["-fsanitize=address"],
+        "//conditions:default": [""],
+  })
 
 genrule(
     name = "config_h",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 19b92ba5..639d0d87 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,6 +29,7 @@ option(WITH_SNAPPY "With snappy" OFF)
 option(WITH_RDMA "With RDMA" OFF)
 option(WITH_DEBUG_BTHREAD_SCHE_SAFETY "With debugging bthread sche safety" OFF)
 option(WITH_DEBUG_LOCK "With debugging lock" OFF)
+option(WITH_ASAN "With AddressSanitizer" OFF)
 option(BUILD_UNIT_TESTS "Whether to build unit tests" OFF)
 option(BUILD_FUZZ_TESTS "Whether to build fuzz tests" OFF)
 option(BUILD_BRPC_TOOLS "Whether to build brpc tools" ON)
@@ -134,6 +135,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
 endif()
 
 set(CMAKE_CPP_FLAGS "${CMAKE_CPP_FLAGS} ${DEFINE_CLOCK_GETTIME} 
-DBRPC_WITH_GLOG=${WITH_GLOG_VAL} -DBRPC_WITH_RDMA=${WITH_RDMA_VAL} 
-DBRPC_DEBUG_BTHREAD_SCHE_SAFETY=${WITH_DEBUG_BTHREAD_SCHE_SAFETY_VAL} 
-DBRPC_DEBUG_LOCK=${WITH_DEBUG_LOCK_VAL}")
+if (WITH_ASAN)
+    set(CMAKE_CPP_FLAGS "${CMAKE_CPP_FLAGS} -fsanitize=address")
+    set(CMAKE_C_FLAGS "${CMAKE_CPP_FLAGS} -fsanitize=address")
+endif()
 if(WITH_MESALINK)
     set(CMAKE_CPP_FLAGS "${CMAKE_CPP_FLAGS} -DUSE_MESALINK")
 endif()
diff --git a/bazel/config/BUILD.bazel b/bazel/config/BUILD.bazel
index 10965747..57569dd3 100644
--- a/bazel/config/BUILD.bazel
+++ b/bazel/config/BUILD.bazel
@@ -120,4 +120,10 @@ config_setting(
     name = "brpc_with_debug_lock",
     define_values = {"with_debug_lock": "true"},
     visibility = ["//visibility:public"],
+)
+
+config_setting(
+    name = "brpc_with_asan",
+    define_values = {"with_asan": "true"},
+    visibility = ["//visibility:public"],
 )
\ No newline at end of file
diff --git a/config_brpc.sh b/config_brpc.sh
index 25f506fe..a16bab81 100755
--- a/config_brpc.sh
+++ b/config_brpc.sh
@@ -38,12 +38,13 @@ else
     LDD=ldd
 fi
 
-TEMP=`getopt -o v: --long 
headers:,libs:,cc:,cxx:,with-glog,with-thrift,with-rdma,with-mesalink,with-bthread-tracer,with-debug-bthread-sche-safety,with-debug-lock,nodebugsymbols,werror
 -n 'config_brpc' -- "$@"`
+TEMP=`getopt -o v: --long 
headers:,libs:,cc:,cxx:,with-glog,with-thrift,with-rdma,with-mesalink,with-bthread-tracer,with-debug-bthread-sche-safety,with-debug-lock,with-asan,nodebugsymbols,werror
 -n 'config_brpc' -- "$@"`
 WITH_GLOG=0
 WITH_THRIFT=0
 WITH_RDMA=0
 WITH_MESALINK=0
 WITH_BTHREAD_TRACER=0
+WITH_ASAN=0
 BRPC_DEBUG_BTHREAD_SCHE_SAFETY=0
 DEBUGSYMBOLS=-g
 WERROR=
@@ -74,6 +75,7 @@ while true; do
         --with-bthread-tracer) WITH_BTHREAD_TRACER=1; shift 1 ;;
         --with-debug-bthread-sche-safety ) BRPC_DEBUG_BTHREAD_SCHE_SAFETY=1; 
shift 1 ;;
         --with-debug-lock ) BRPC_DEBUG_LOCK=1; shift 1 ;;
+        --with-asan) WITH_ASAN=1; shift 1 ;;
         --nodebugsymbols ) DEBUGSYMBOLS=; shift 1 ;;
         --werror ) WERROR=-Werror; shift 1 ;;
         -- ) shift; break ;;
@@ -345,10 +347,15 @@ else
     CXXFLAGS="-std=c++0x"
 fi
 
-LEVELDB_HDR=$(find_dir_of_header_or_die leveldb/db.h)
-
 CPPFLAGS=
 
+if [ $WITH_ASAN != 0 ]; then
+  CPPFLAGS="${CPPFLAGS} -fsanitize=address"
+  DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -fsanitize=address"
+fi
+
+LEVELDB_HDR=$(find_dir_of_header_or_die leveldb/db.h)
+
 if [ $WITH_BTHREAD_TRACER != 0 ]; then
     if [ "$SYSTEM" != "Linux" ] || [ "$(uname -m)" != "x86_64" ]; then
         >&2 $ECHO "bthread tracer is only supported on Linux x86_64 platform"
@@ -425,7 +432,7 @@ append_to_output "STATIC_LINKINGS=$STATIC_LINKINGS"
 append_to_output "DYNAMIC_LINKINGS=$DYNAMIC_LINKINGS"
 
 # CPP means C PreProcessing, not C PlusPlus
-CPPFLAGS="${CPPFLAGS}  -DBRPC_WITH_GLOG=$WITH_GLOG 
-DBRPC_DEBUG_BTHREAD_SCHE_SAFETY=$BRPC_DEBUG_BTHREAD_SCHE_SAFETY 
-DBRPC_DEBUG_LOCK=$BRPC_DEBUG_LOCK"
+CPPFLAGS="${CPPFLAGS} -DBRPC_WITH_GLOG=$WITH_GLOG 
-DBRPC_DEBUG_BTHREAD_SCHE_SAFETY=$BRPC_DEBUG_BTHREAD_SCHE_SAFETY 
-DBRPC_DEBUG_LOCK=$BRPC_DEBUG_LOCK"
 
 # Avoid over-optimizations of TLS variables by GCC>=4.8
 # See: https://github.com/apache/brpc/issues/1693
@@ -523,7 +530,10 @@ append_to_output "ifeq (\$(NEED_GPERFTOOLS), 1)"
 TCMALLOC_LIB=$(find_dir_of_lib tcmalloc_and_profiler)
 if [ -z "$TCMALLOC_LIB" ]; then
     append_to_output "    \$(error \"Fail to find gperftools\")"
+elif [ $WITH_ASAN != 0 ]; then
+    append_to_output "    \$(error \"gperftools is not compatible with ASAN\")"
 else
+    append_to_output "    CPPFLAGS+=-DBRPC_ENABLE_CPU_PROFILER"
     append_to_output_libs "$TCMALLOC_LIB" "    "
     if [ -f $TCMALLOC_LIB/libtcmalloc.$SO ]; then
         append_to_output "    DYNAMIC_LINKINGS+=-ltcmalloc_and_profiler"
diff --git a/docs/cn/getting_started.md b/docs/cn/getting_started.md
index 76aa6f86..1b91e15b 100644
--- a/docs/cn/getting_started.md
+++ b/docs/cn/getting_started.md
@@ -352,8 +352,6 @@ bRPC 中使用了 protobuf 内部 API,上游不保证相关 API 的兼容性
 
 [1.8.0](https://github.com/apache/brpc/releases/tag/1.8.0) 中 
[#2406](https://github.com/apache/brpc/pull/2406) 和 
[#2493](https://github.com/apache/brpc/pull/2493)引入了部分 proto3 语法,所以目前 bRPC 不再兼容 
protobuf 2.x 版本。如果你希望使用 2.x 版本,可以使用 1.8.0 之前的 bRPC 版本。
 
-pb 3.x中的Arena至今没被支持。
-
 ## gflags: 2.1-2.2.2
 
 2.1.1 
中存在一处已知问题,需要[补丁](https://github.com/gflags/gflags/commit/408061b46974cc8377a8a794a048ecae359ad887)。
diff --git a/docs/cn/sanitizers.md b/docs/cn/sanitizers.md
new file mode 100644
index 00000000..e902e101
--- /dev/null
+++ b/docs/cn/sanitizers.md
@@ -0,0 +1,32 @@
+# Sanitizers
+
+新版本的GCC/Clang支持[sanitizers](https://github.com/google/sanitizers),方便开发者排查代码中的bug。
 bRPC对sanitizers提供了一定的支持。
+
+## AddressSanitizer(ASan)
+
+ASan提供了[对协程的支持](https://reviews.llvm.org/D20913)。 
在bthread创建、切换、销毁时,让ASan知道当前bthread的栈信息,主要用于维护[fake 
stack](https://github.com/google/sanitizers/wiki/AddressSanitizerUseAfterReturn)。
+
+bRPC中启用ASan的方法:给config_brpc.sh增加`--with-glog`选项、给cmake增加`-DWITH_GLOG=ON`选项或者给bazel增加`--define
 with_asan=true`选项。
+
+另外需要注意的是,ASan没法检测非ASan分配内存或者对象池复用内存。所以我们封装了两个宏,让ASan知道内存块是否能被使用。在非ASan环境下,这两个宏什么也不做,没有开销。
+
+```c++
+#include <butil/debug/address_annotations.h>
+
+BUTIL_ASAN_POISON_MEMORY_REGION(addr, size);
+BUTIL_ASAN_UNPOISON_MEMORY_REGION(addr, size);
+```
+
+其他问题:如果ASan报告中new/delete的调用栈不完整,可以通过设置`fast_unwind_on_malloc=0`回溯出完整的调用栈了。需要注意的是`fast_unwind_on_malloc=0`很耗性能。
+
+## ThreadSanitizer(TSan)
+
+待支持(TODO)
+
+## LeakSanitizer(LSan) / MemorySanitizer(MSan) / 
UndefinedBehaviorSanitizer(UBSan) 
+
+bRPC默认支持这三种sanitizers,编译及链接时加上对应的选项即可启用对应的sanitizers:
+
+1. LSan: `-fsanitize=leak`;
+2. MSan: `-fsanitize=memory`;
+3. UBSan: `-fsanitize=undefined`;
diff --git a/docs/en/getting_started.md b/docs/en/getting_started.md
index a2ae80d4..b2363ce3 100644
--- a/docs/en/getting_started.md
+++ b/docs/en/getting_started.md
@@ -347,8 +347,6 @@ Please [submit 
issue](https://github.com/apache/brpc/issues) if you have any pro
 
 [#2406](https://github.com/apache/brpc/pull/2406) and 
[#2493](https://github.com/apache/brpc/pull/2493) in [version 
1.8.0]((https://github.com/apache/brpc/releases/tag/1.8.0)) introduce some 
proto3 syntax, so currently bRPC is no longer compatible with pb 2.x version. 
If you want to use pb 2.x version, you can use bRPC version before 1.8.0.
 
-Arena in pb 3.x is not supported yet.
-
 ## gflags: 2.0-2.2.2
 
 [gflags 
patch](https://github.com/gflags/gflags/commit/408061b46974cc8377a8a794a048ecae359ad887)
 is required when compiled with 2.1.1.
diff --git a/src/brpc/details/http_message.h b/src/brpc/details/http_message.h
index 6ff2d031..655ba1a9 100644
--- a/src/brpc/details/http_message.h
+++ b/src/brpc/details/http_message.h
@@ -88,7 +88,7 @@ public:
     bool read_body_progressively() const { return _read_body_progressively; }
 
     void set_read_body_progressively(bool read_body_progressively) {
-        this->_read_body_progressively = read_body_progressively;
+        _read_body_progressively = read_body_progressively;
     }
 
     // Send new parts of the body to the reader. If the body already has some
diff --git a/src/brpc/details/http_parser.cpp b/src/brpc/details/http_parser.cpp
index 0f9e906a..a4aa4d12 100644
--- a/src/brpc/details/http_parser.cpp
+++ b/src/brpc/details/http_parser.cpp
@@ -2191,13 +2191,13 @@ http_parser_init (http_parser *parser, enum 
http_parser_type t)
 
 const char *
 http_errno_name(enum http_errno err) {
-  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
+  assert(err < 
(http_errno)(sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
   return http_strerror_tab[err].name;
 }
 
 const char *
 http_errno_description(enum http_errno err) {
-  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
+  assert(err < 
(http_errno)(sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
   return http_strerror_tab[err].description;
 }
 
diff --git a/src/brpc/policy/consistent_hashing_load_balancer.cpp 
b/src/brpc/policy/consistent_hashing_load_balancer.cpp
index 19c8849c..c18c9a34 100644
--- a/src/brpc/policy/consistent_hashing_load_balancer.cpp
+++ b/src/brpc/policy/consistent_hashing_load_balancer.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>                                           // 
std::set_union
 #include <array>
 #include <gflags/gflags.h>
+#include <openssl/md5.h>
 #include "butil/containers/flat_map.h"
 #include "butil/errno.h"
 #include "butil/strings/string_number_conversions.h"
@@ -102,10 +103,10 @@ bool KetamaReplicaPolicy::Build(ServerId server,
     CHECK(num_replicas % points_per_hash == 0)
         << "Ketam hash replicas number(" << num_replicas << ") should be n*4";
     for (size_t i = 0; i < num_replicas / points_per_hash; ++i) {
-        char host[32];
+        char host[256];
         int len = snprintf(host, sizeof(host), "%s-%lu",
                            endpoint2str(ptr->remote_side()).c_str(), i);
-        unsigned char digest[16];
+        unsigned char digest[MD5_DIGEST_LENGTH];
         MD5HashSignature(host, len, digest);
         for (size_t j = 0; j < points_per_hash; ++j) {
             ConsistentHashingLoadBalancer::Node node;
diff --git a/src/brpc/policy/hasher.cpp b/src/brpc/policy/hasher.cpp
index f533099a..ba33eb54 100644
--- a/src/brpc/policy/hasher.cpp
+++ b/src/brpc/policy/hasher.cpp
@@ -28,12 +28,12 @@ namespace policy {
 void MD5HashSignature(const void* key, size_t len, unsigned char* results) {
     MD5_CTX my_md5;
     MD5_Init(&my_md5);
-    MD5_Update(&my_md5, (const unsigned char *)key, len);
+    MD5_Update(&my_md5, key, len);
     MD5_Final(results, &my_md5);
 }
 
 uint32_t MD5Hash32(const void* key, size_t len) {
-    unsigned char results[16];
+    unsigned char results[MD5_DIGEST_LENGTH];
     MD5HashSignature(key, len, results);
     return ((uint32_t) (results[3] & 0xFF) << 24) 
             | ((uint32_t) (results[2] & 0xFF) << 16) 
@@ -48,7 +48,7 @@ uint32_t MD5Hash32V(const butil::StringPiece* keys, size_t 
num_keys) {
         MD5_Update(&ctx, (const unsigned char *)keys[i].data(),
                    keys[i].size());
     }
-    unsigned char results[16];
+    unsigned char results[MD5_DIGEST_LENGTH];
     MD5_Final(results, &ctx);
     return ((uint32_t) (results[3] & 0xFF) << 24) 
             | ((uint32_t) (results[2] & 0xFF) << 16) 
diff --git a/src/brpc/policy/http_rpc_protocol.cpp 
b/src/brpc/policy/http_rpc_protocol.cpp
index 07fc7da8..a1353b14 100644
--- a/src/brpc/policy/http_rpc_protocol.cpp
+++ b/src/brpc/policy/http_rpc_protocol.cpp
@@ -1708,7 +1708,7 @@ void HttpContext::CheckProgressiveRead(const void* arg, 
Socket *socket) {
         header().uri().path(), (Server *)arg,
         const_cast<std::string *>(&header().unresolved_path()));
     if (sp != NULL && sp->params.enable_progressive_read) {
-        this->set_read_body_progressively(true);
+        set_read_body_progressively(true);
         socket->read_will_be_progressive(CONNECTION_TYPE_SHORT);
     }
 }
diff --git a/src/brpc/socket.cpp b/src/brpc/socket.cpp
index 056a9fca..5e969fc8 100644
--- a/src/brpc/socket.cpp
+++ b/src/brpc/socket.cpp
@@ -508,9 +508,11 @@ void 
Socket::ReturnSuccessfulWriteRequest(Socket::WriteRequest* p) {
     DCHECK(p->data.empty());
     AddOutputMessages(1);
     const bthread_id_t id_wait = p->id_wait;
+    bool is_notify_on_success = p->is_notify_on_success();
     butil::return_object(p);
+    // Do not access `p' after it is returned to ObjectPool.
     if (id_wait != INVALID_BTHREAD_ID) {
-        if (p->is_notify_on_success() && !Failed()) {
+        if (is_notify_on_success && !Failed()) {
             bthread_id_error(id_wait, 0);
         } else {
             NotifyOnFailed(id_wait);
diff --git a/src/bthread/bthread.cpp b/src/bthread/bthread.cpp
index e124f4bc..8d4e2026 100644
--- a/src/bthread/bthread.cpp
+++ b/src/bthread/bthread.cpp
@@ -350,7 +350,7 @@ bthread_t bthread_self(void) {
     bthread::TaskGroup* g = bthread::tls_task_group;
     // note: return 0 for main tasks now, which include main thread and
     // all work threads. So that we can identify main tasks from logs
-    // more easily. This is probably questionable in future.
+    // more easily. This is probably questionable in the future.
     if (g != NULL && !g->is_current_main_task()/*note*/) {
         return g->current_tid();
     }
@@ -361,6 +361,13 @@ int bthread_equal(bthread_t t1, bthread_t t2) {
     return t1 == t2;
 }
 
+#ifdef BUTIL_USE_ASAN
+// Fixme!!!
+// The noreturn `bthread_exit' may cause a warning of ASan, but does not abort 
the program.
+//
+// ==94463==WARNING: ASan is ignoring requested __asan_handle_no_return: stack 
type: default top: 0x00016dd7f000; bottom 0x00010b1a4000; size: 0x000062bdb000 
(1656598528)
+// False positive error reports may follow
+#endif // BUTIL_USE_ASAN
 void bthread_exit(void* retval) {
     bthread::TaskGroup* g = bthread::tls_task_group;
     if (g != NULL && !g->is_current_main_task()) {
diff --git a/src/bthread/execution_queue.cpp b/src/bthread/execution_queue.cpp
index bb01882c..88d96c2b 100644
--- a/src/bthread/execution_queue.cpp
+++ b/src/bthread/execution_queue.cpp
@@ -22,7 +22,6 @@
 #include "bthread/execution_queue.h"
 
 #include "butil/memory/singleton_on_pthread_once.h"
-#include "butil/object_pool.h"           // butil::get_object
 #include "butil/resource_pool.h"         // butil::get_resource
 #include "butil/threading/platform_thread.h"
 
diff --git a/src/bthread/execution_queue_inl.h 
b/src/bthread/execution_queue_inl.h
index f5998a26..cf7d2ee5 100644
--- a/src/bthread/execution_queue_inl.h
+++ b/src/bthread/execution_queue_inl.h
@@ -27,8 +27,9 @@
 #include "butil/memory/scoped_ptr.h"     // butil::scoped_ptr
 #include "butil/logging.h"               // LOG
 #include "butil/time.h"                  // butil::cpuwide_time_ns
-#include "bvar/bvar.h"                  // bvar::Adder
-#include "bthread/butex.h"              // butex_construct
+#include "butil/object_pool.h"           // butil::get_object
+#include "bvar/bvar.h"                   // bvar::Adder
+#include "bthread/butex.h"               // butex_construct
 #include "butil/synchronization/condition_variable.h"
 
 namespace bthread {
@@ -585,4 +586,11 @@ inline int ExecutionQueueBase::dereference() {
 
 }  // namespace bthread
 
+namespace butil {
+// `TaskNode::cancel' may access the TaskNode object returned to the 
ObjectPool<TaskNode>,
+// so ObjectPool<TaskNode> can not poison the memory region of TaskNode.
+template <>
+struct ObjectPoolWithASanPoison<bthread::TaskNode> : false_type {};
+} // namespace butil
+
 #endif  //BTHREAD_EXECUTION_QUEUE_INL_H
diff --git a/src/bthread/stack.cpp b/src/bthread/stack.cpp
index c312ef83..71daf9d7 100644
--- a/src/bthread/stack.cpp
+++ b/src/bthread/stack.cpp
@@ -139,7 +139,7 @@ void deallocate_stack_storage(StackStorage* s) {
         return;
     }
     s_stack_count.fetch_sub(1, butil::memory_order_relaxed);
-    if (s->guardsize <= 0) {
+    if (s->guardsize == 0) {
         free((char*)s->bottom - memsize);
     } else {
         munmap((char*)s->bottom - memsize, memsize);
diff --git a/src/bthread/stack.h b/src/bthread/stack.h
index 46cafb4a..91d1df60 100644
--- a/src/bthread/stack.h
+++ b/src/bthread/stack.h
@@ -31,8 +31,8 @@
 namespace bthread {
 
 struct StackStorage {
-     int stacksize;
-     int guardsize;
+     unsigned stacksize;
+     unsigned guardsize;
     // Assume stack grows upwards.
     // 
http://www.boost.org/doc/libs/1_55_0/libs/context/doc/html/context/stack.html
     void* bottom;
@@ -62,6 +62,7 @@ enum StackType {
 };
 
 struct ContextualStack {
+    virtual ~ContextualStack() = default;
     bthread_fcontext_t context;
     StackType stacktype;
     StackStorage storage;
diff --git a/src/bthread/stack_inl.h b/src/bthread/stack_inl.h
index 868775d4..d765dfd0 100644
--- a/src/bthread/stack_inl.h
+++ b/src/bthread/stack_inl.h
@@ -28,6 +28,83 @@ DECLARE_int32(tc_stack_normal);
 
 namespace bthread {
 
+#ifdef BUTIL_USE_ASAN
+namespace internal {
+
+BUTIL_FORCE_INLINE void ASanPoisonMemoryRegion(const StackStorage& storage) {
+    if (NULL == storage.bottom) {
+        return;
+    }
+
+    CHECK_GT((void*)storage.bottom,
+             reinterpret_cast<void*>(storage.stacksize + + storage.guardsize));
+    BUTIL_ASAN_POISON_MEMORY_REGION(
+        (char*)storage.bottom - storage.stacksize, storage.stacksize);
+}
+
+BUTIL_FORCE_INLINE void ASanUnpoisonMemoryRegion(const StackStorage& storage) {
+    if (NULL == storage.bottom) {
+        return;
+    }
+    CHECK_GT(storage.bottom,
+             reinterpret_cast<void*>(storage.stacksize + storage.guardsize));
+    BUTIL_ASAN_UNPOISON_MEMORY_REGION(
+        (char*)storage.bottom - storage.stacksize, storage.stacksize);
+}
+
+
+BUTIL_FORCE_INLINE void StartSwitchFiber(void** fake_stack_save, StackStorage& 
storage) {
+    if (NULL == storage.bottom) {
+        return;
+    }
+    RELEASE_ASSERT(storage.bottom >
+                   reinterpret_cast<void*>(storage.stacksize + 
storage.guardsize));
+    // Lowest address of this stack.
+    void* asan_stack_bottom = (char*)storage.bottom - storage.stacksize;
+    BUTIL_ASAN_START_SWITCH_FIBER(fake_stack_save, asan_stack_bottom, 
storage.stacksize);
+}
+
+BUTIL_FORCE_INLINE void FinishSwitchFiber(void* fake_stack_save) {
+    BUTIL_ASAN_FINISH_SWITCH_FIBER(fake_stack_save, NULL, NULL);
+}
+
+class ScopedASanFiberSwitcher {
+public:
+    ScopedASanFiberSwitcher(StackStorage& storage, bool ending) {
+        // If bthread will be quit here, pass NULL as `fake_stack_save',
+        // so that ASan knows it can destroy the fake stack.
+        StartSwitchFiber(ending ? NULL : &_fake_stack, storage);
+    }
+
+    ~ScopedASanFiberSwitcher() {
+        FinishSwitchFiber(_fake_stack);
+    }
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedASanFiberSwitcher);
+
+private:
+    void* _fake_stack{NULL};
+};
+
+#define BTHREAD_ASAN_POISON_MEMORY_REGION(storage) \
+    ::bthread::internal::ASanPoisonMemoryRegion(storage)
+
+#define BTHREAD_ASAN_UNPOISON_MEMORY_REGION(storage) \
+    ::bthread::internal::ASanUnpoisonMemoryRegion(storage)
+
+#define BTHREAD_SCOPED_ASAN_FIBER_SWITCHER(storage, ending) \
+    ::bthread::internal::ScopedASanFiberSwitcher switcher(storage, ending)
+
+} // namespace internal
+#else
+
+// If ASan are used, the annotations should be no-ops.
+#define BTHREAD_ASAN_POISON_MEMORY_REGION(storage) ((void)(storage))
+#define BTHREAD_ASAN_UNPOISON_MEMORY_REGION(storage) ((void)(storage))
+#define BTHREAD_SCOPED_ASAN_FIBER_SWITCHER(storage, ending) ((void)(storage), 
(void)(ending))
+
+#endif // BUTIL_USE_ASAN
+
 struct MainStackClass {};
 
 struct SmallStackClass {
@@ -57,10 +134,14 @@ template <typename StackClass> struct StackFactory {
             }
             context = bthread_make_fcontext(storage.bottom, storage.stacksize, 
entry);
             stacktype = (StackType)StackClass::stacktype;
+            // It's poisoned prior to use.
+            BTHREAD_ASAN_POISON_MEMORY_REGION(storage);
         }
         ~Wrapper() {
             if (context) {
                 context = NULL;
+                // Unpoison to avoid affecting other allocator.
+                BTHREAD_ASAN_UNPOISON_MEMORY_REGION(storage);
                 deallocate_stack_storage(&storage);
                 storage.zeroize();
             }
@@ -68,11 +149,16 @@ template <typename StackClass> struct StackFactory {
     };
     
     static ContextualStack* get_stack(void (*entry)(intptr_t)) {
-        return butil::get_object<Wrapper>(entry);
+        ContextualStack* cs = butil::get_object<Wrapper>(entry);
+        // Marks stack as addressable.
+        BTHREAD_ASAN_UNPOISON_MEMORY_REGION(cs->storage);
+        return cs;
     }
     
-    static void return_stack(ContextualStack* sc) {
-        butil::return_object(static_cast<Wrapper*>(sc));
+    static void return_stack(ContextualStack* cs) {
+        // Marks stack as unaddressable.
+        BTHREAD_ASAN_POISON_MEMORY_REGION(cs->storage);
+        butil::return_object(static_cast<Wrapper*>(cs));
     }
 };
 
diff --git a/src/bthread/task_control.cpp b/src/bthread/task_control.cpp
index 55ed1f2e..545bc322 100644
--- a/src/bthread/task_control.cpp
+++ b/src/bthread/task_control.cpp
@@ -228,7 +228,7 @@ int TaskControl::init(int concurrency) {
         const int rc = pthread_create(&_workers[i], NULL, worker_thread, arg);
         if (rc) {
             delete arg;
-            LOG(ERROR) << "Fail to create _workers[" << i << "], " << 
berror(rc);
+            PLOG(ERROR) << "Fail to create _workers[" << i << "]";
             return -1;
         }
     }
@@ -272,8 +272,7 @@ int TaskControl::add_workers(int num, bthread_tag_t tag) {
                 &_workers[i + old_concurency], NULL, worker_thread, arg);
         if (rc) {
             delete arg;
-            LOG(WARNING) << "Fail to create _workers[" << i + old_concurency
-                         << "], " << berror(rc);
+            PLOG(WARNING) << "Fail to create _workers[" << i + old_concurency 
<< "]";
             _concurrency.fetch_sub(1, butil::memory_order_release);
             break;
         }
diff --git a/src/bthread/task_group.cpp b/src/bthread/task_group.cpp
index 361efa59..0c924ac3 100644
--- a/src/bthread/task_group.cpp
+++ b/src/bthread/task_group.cpp
@@ -208,12 +208,39 @@ TaskGroup::~TaskGroup() {
     if (_main_tid) {
         TaskMeta* m = address_meta(_main_tid);
         CHECK(_main_stack == m->stack);
+#ifdef BUTIL_USE_ASAN
+        _main_stack->storage.bottom = NULL;
+        _main_stack->storage.stacksize = 0;
+#endif // BUTIL_USE_ASAN
         return_stack(m->release_stack());
         return_resource(get_slot(_main_tid));
         _main_tid = 0;
     }
 }
 
+#ifdef BUTIL_USE_ASAN
+int PthreadAttrGetStack(void*& stack_addr, size_t& stack_size) {
+#if defined(OS_MACOSX)
+    stack_addr = pthread_get_stackaddr_np(pthread_self());
+    stack_size = pthread_get_stacksize_np(pthread_self());
+    return 0;
+#else
+    pthread_attr_t attr;
+    int rc = pthread_getattr_np(pthread_self(), &attr);
+    if (0 != rc) {
+        LOG(ERROR) << "Fail to get pthread attributes: " << berror(rc);
+        return rc;
+    }
+    rc = pthread_attr_getstack(&attr, &stack_addr, &stack_size);
+    if (0 != rc) {
+        LOG(ERROR) << "Fail to get pthread stack: " << berror(rc);
+    }
+    pthread_attr_destroy(&attr);
+    return rc;
+#endif // OS_MACOSX
+}
+#endif // BUTIL_USE_ASAN
+
 int TaskGroup::init(size_t runqueue_capacity) {
     if (_rq.init(runqueue_capacity) != 0) {
         LOG(FATAL) << "Fail to init _rq";
@@ -223,6 +250,15 @@ int TaskGroup::init(size_t runqueue_capacity) {
         LOG(FATAL) << "Fail to init _remote_rq";
         return -1;
     }
+
+#ifdef BUTIL_USE_ASAN
+    void* stack_addr = NULL;
+    size_t stack_size = 0;
+    if (0 != PthreadAttrGetStack(stack_addr, stack_size)) {
+        return -1;
+    }
+#endif // BUTIL_USE_ASAN
+
     ContextualStack* stk = get_stack(STACK_TYPE_MAIN, NULL);
     if (NULL == stk) {
         LOG(FATAL) << "Fail to get main stack container";
@@ -247,6 +283,12 @@ int TaskGroup::init(size_t runqueue_capacity) {
     m->tid = make_tid(*m->version_butex, slot);
     m->set_stack(stk);
 
+#ifdef BUTIL_USE_ASAN
+    stk->storage.bottom = stack_addr;
+    stk->storage.stacksize = stack_size;
+    // No guard size required for ASan.
+#endif // BUTIL_USE_ASAN
+
     _cur_meta = m;
     _main_tid = m->tid;
     _main_stack = stk;
@@ -255,6 +297,15 @@ int TaskGroup::init(size_t runqueue_capacity) {
     return 0;
 }
 
+#ifdef BUTIL_USE_ASAN
+void TaskGroup::asan_task_runner(intptr_t) {
+    // This is a new thread, and it doesn't have the fake stack yet. ASan will
+    // create it lazily, for now just pass NULL.
+    internal::FinishSwitchFiber(NULL);
+    task_runner(0);
+}
+#endif // BUTIL_USE_ASAN
+
 void TaskGroup::task_runner(intptr_t skip_remained) {
     // NOTE: tls_task_group is volatile since tasks are moved around
     //       different groups.
@@ -564,11 +615,18 @@ void TaskGroup::ending_sched(TaskGroup** pg) {
     TaskMeta* next_meta = address_meta(next_tid);
     if (next_meta->stack == NULL) {
         if (next_meta->stack_type() == cur_meta->stack_type()) {
+            // Reuse the stack of the current ending task.
+            //
             // also works with pthread_task scheduling to pthread_task, the
             // transfered stack is just _main_stack.
             next_meta->set_stack(cur_meta->release_stack());
         } else {
+#ifdef BUTIL_USE_ASAN
+            ContextualStack* stk = get_stack(
+                next_meta->stack_type(), asan_task_runner);
+#else
             ContextualStack* stk = get_stack(next_meta->stack_type(), 
task_runner);
+#endif // BUTIL_USE_ASAN
             if (stk) {
                 next_meta->set_stack(stk);
             } else {
@@ -581,7 +639,7 @@ void TaskGroup::ending_sched(TaskGroup** pg) {
             }
         }
     }
-    sched_to(pg, next_meta);
+    sched_to(pg, next_meta, true);
 }
 
 void TaskGroup::sched(TaskGroup** pg) {
@@ -602,7 +660,7 @@ void TaskGroup::sched(TaskGroup** pg) {
 
 extern void CheckBthreadScheSafety();
 
-void TaskGroup::sched_to(TaskGroup** pg, TaskMeta* next_meta) {
+void TaskGroup::sched_to(TaskGroup** pg, TaskMeta* next_meta, bool cur_ending) 
{
     TaskGroup* g = *pg;
 #ifndef NDEBUG
     if ((++g->_sched_recursive_guard) > 1) {
@@ -657,7 +715,11 @@ void TaskGroup::sched_to(TaskGroup** pg, TaskMeta* 
next_meta) {
                 g->_control->_task_tracer.set_status(TASK_STATUS_JUMPING, 
cur_meta);
                 g->_control->_task_tracer.set_status(TASK_STATUS_JUMPING, 
next_meta);
 #endif // BRPC_BTHREAD_TRACER
-                jump_stack(cur_meta->stack, next_meta->stack);
+                {
+                    BTHREAD_SCOPED_ASAN_FIBER_SWITCHER(
+                        cur_meta->stack->storage, cur_ending);
+                    jump_stack(cur_meta->stack, next_meta->stack);
+                }
                 // probably went to another group, need to assign g again.
                 g = BAIDU_GET_VOLATILE_THREAD_LOCAL(tls_task_group);
 #ifdef BRPC_BTHREAD_TRACER
diff --git a/src/bthread/task_group.h b/src/bthread/task_group.h
index c3d2ae46..488ad492 100644
--- a/src/bthread/task_group.h
+++ b/src/bthread/task_group.h
@@ -81,7 +81,7 @@ public:
     // Suspend caller and run bthread `next_tid' in TaskGroup *pg.
     // Purpose of this function is to avoid pushing `next_tid' to _rq and
     // then being popped by sched(pg), which is not necessary.
-    static void sched_to(TaskGroup** pg, TaskMeta* next_meta);
+    static void sched_to(TaskGroup** pg, TaskMeta* next_meta, bool cur_ending);
     static void sched_to(TaskGroup** pg, bthread_t next_tid);
     static void exchange(TaskGroup** pg, TaskMeta* next_meta);
 
@@ -204,7 +204,7 @@ private:
 friend class TaskControl;
 
     // You shall use TaskControl::create_group to create new instance.
-    explicit TaskGroup(TaskControl*);
+    explicit TaskGroup(TaskControl* c);
 
     int init(size_t runqueue_capacity);
 
@@ -212,6 +212,9 @@ friend class TaskControl;
     // of groups are postponed to avoid race.
     ~TaskGroup();
 
+#ifdef BUTIL_USE_ASAN
+    static void asan_task_runner(intptr_t);
+#endif // BUTIL_USE_ASAN
     static void task_runner(intptr_t skip_remained);
 
     // Callbacks for set_remained()
diff --git a/src/bthread/task_group_inl.h b/src/bthread/task_group_inl.h
index 75c377e1..4842bf69 100644
--- a/src/bthread/task_group_inl.h
+++ b/src/bthread/task_group_inl.h
@@ -56,13 +56,17 @@ inline void TaskGroup::exchange(TaskGroup** pg, TaskMeta* 
next_meta) {
                      ? ready_to_run_in_worker_ignoresignal
                      : ready_to_run_in_worker),
                     &args);
-    TaskGroup::sched_to(pg, next_meta);
+    TaskGroup::sched_to(pg, next_meta, false);
 }
 
 inline void TaskGroup::sched_to(TaskGroup** pg, bthread_t next_tid) {
     TaskMeta* next_meta = address_meta(next_tid);
     if (next_meta->stack == NULL) {
+#ifdef BUTIL_USE_ASAN
+        ContextualStack* stk = get_stack(next_meta->stack_type(), 
asan_task_runner);
+#else
         ContextualStack* stk = get_stack(next_meta->stack_type(), task_runner);
+#endif // BUTIL_USE_ASAN
         if (stk) {
             next_meta->set_stack(stk);
         } else {
@@ -75,7 +79,7 @@ inline void TaskGroup::sched_to(TaskGroup** pg, bthread_t 
next_tid) {
         }
     }
     // Update now_ns only when wait_task did yield.
-    sched_to(pg, next_meta);
+    sched_to(pg, next_meta, false);
 }
 
 inline void TaskGroup::push_rq(bthread_t tid) {
diff --git a/src/bthread/task_tracer.cpp b/src/bthread/task_tracer.cpp
index acdf9208..af88ba64 100644
--- a/src/bthread/task_tracer.cpp
+++ b/src/bthread/task_tracer.cpp
@@ -305,6 +305,8 @@ TaskTracer::Result TaskTracer::TraceImpl(bthread_t tid) {
     return result;
 }
 
+// Instruct ASan to ignore this function.
+BUTIL_ATTRIBUTE_NO_SANITIZE_ADDRESS
 unw_cursor_t TaskTracer::MakeCursor(bthread_fcontext_t fcontext) {
     unw_cursor_t cursor;
     unw_init_local(&cursor, &_context);
diff --git a/src/butil/compiler_specific.h b/src/butil/compiler_specific.h
index 924fe439..7c1c2662 100644
--- a/src/butil/compiler_specific.h
+++ b/src/butil/compiler_specific.h
@@ -208,10 +208,21 @@
 #endif
 
 // Instruct ASan is enabled.
-#if BUTIL_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
-#define BUTIL_USE_ASAN 1
+#if defined(BUTIL_USE_ASAN)
+#error "BUTIL_USE_ASAN cannot be set directly."
+#elif BUTIL_HAS_FEATURE(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+#define BUTIL_USE_ASAN
 #endif
 
+// 
https://github.com/google/sanitizers/wiki/AddressSanitizer#turning-off-instrumentation
+// Attribute to instruct ASan to ignore a function.
+#if defined(COMPILER_GCC)
+# define BUTIL_ATTRIBUTE_NO_SANITIZE_ADDRESS 
__attribute__((no_sanitize_address))
+#else
+# define BUTIL_ATTRIBUTE_NO_SANITIZE_ADDRESS
+#endif
+
+
 // Tell the compiler a function is using a printf-style format string.
 // |format_param| is the one-based index of the format string parameter;
 // |dots_param| is the one-based index of the "..." parameter.
diff --git a/src/butil/debug/address_annotations.h 
b/src/butil/debug/address_annotations.h
new file mode 100644
index 00000000..81e7ab11
--- /dev/null
+++ b/src/butil/debug/address_annotations.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BUTIL_DEBUG_ADDRESS_ANNOTATIONS_H_
+#define BUTIL_DEBUG_ADDRESS_ANNOTATIONS_H_
+
+#include "butil/macros.h"
+
+// It provides AddressSanitizer annotations for bthread and memory management.
+// See <sanitizer/asan_interface.h> for detail of these annotations.
+
+#ifdef BUTIL_USE_ASAN
+
+#include <sanitizer/asan_interface.h>
+
+#define BUTIL_ASAN_POISON_MEMORY_REGION(addr, size) \
+    __asan_poison_memory_region(addr, size)
+
+#define BUTIL_ASAN_UNPOISON_MEMORY_REGION(addr, size) \
+    __asan_unpoison_memory_region(addr, size)
+
+#define BUTIL_ASAN_ADDRESS_IS_POISONED(addr) \
+    __asan_address_is_poisoned(addr)
+
+#define BUTIL_ASAN_START_SWITCH_FIBER(fake_stack_save, bottom, size) \
+    __sanitizer_start_switch_fiber(fake_stack_save, bottom, size)
+
+#define BUTIL_ASAN_FINISH_SWITCH_FIBER(fake_stack_save, bottom_old, size_old) \
+    __sanitizer_finish_switch_fiber(fake_stack_save, bottom_old, size_old)
+
+#else
+// If ASan is not used, these annotations are no-ops.
+#define BUTIL_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), 
(void)(size))
+#define BUTIL_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), 
(void)(size))
+#define BUTIL_ASAN_START_SWITCH_FIBER(fake_stack_save, bottom, size) \
+    ((void)(fake_stack_save), (void)(bottom), (void)(size))
+#define BUTIL_ASAN_FINISH_SWITCH_FIBER(fake_stack_save, bottom_old, size_old) \
+    ((void)(fake_stack_save), (void)(bottom_old), (void)(size_old))
+#endif // BUTIL_USE_ASAN
+
+#endif  // BUTIL_DEBUG_ADDRESS_ANNOTATIONS_H_
diff --git a/src/butil/debug/leak_annotations.h 
b/src/butil/debug/leak_annotations.h
index 231e9982..47387acb 100644
--- a/src/butil/debug/leak_annotations.h
+++ b/src/butil/debug/leak_annotations.h
@@ -32,23 +32,23 @@ void __lsan_do_leak_check();
 }  // extern "C"
 
 class ScopedLeakSanitizerDisabler {
- public:
+public:
   ScopedLeakSanitizerDisabler() { __lsan_disable(); }
   ~ScopedLeakSanitizerDisabler() { __lsan_enable(); }
- private:
+private:
   DISALLOW_COPY_AND_ASSIGN(ScopedLeakSanitizerDisabler);
 };
 
 #define ANNOTATE_SCOPED_MEMORY_LEAK \
     ScopedLeakSanitizerDisabler leak_sanitizer_disabler; static_cast<void>(0)
 
-#define ANNOTATE_LEAKING_OBJECT_PTR(X) __lsan_ignore_object(X);
+#define ANNOTATE_LEAKING_OBJECT_PTR(X) __lsan_ignore_object(X)
 
 #else
 
 // If neither HeapChecker nor LSan are used, the annotations should be no-ops.
 #define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0)
-#define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)0)
+#define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)(X))
 
 #endif
 
diff --git a/src/butil/logging.cc b/src/butil/logging.cc
index a15251b2..9b09af6d 100644
--- a/src/butil/logging.cc
+++ b/src/butil/logging.cc
@@ -464,6 +464,13 @@ TimeVal GetTimestamp() {
 }
 
 struct BAIDU_CACHELINE_ALIGNMENT LogInfo {
+    ~LogInfo() = default;
+    void clear() {
+        file.clear();
+        func.clear();
+        content.clear();
+    }
+
     std::string file;
     std::string func;
     std::string content;
@@ -751,7 +758,7 @@ bool AsyncLogger::IsLogComplete(LogRequest* old_head) {
 
 void AsyncLogger::DoLog(LogRequest* req) {
     DoLog(req->log_info);
-    req->log_info.content.clear();
+    req->log_info.clear();
 }
 
 void AsyncLogger::DoLog(const LogInfo& log_info) {
diff --git a/src/butil/object_pool.h b/src/butil/object_pool.h
index aa265dfa..92bc4036 100644
--- a/src/butil/object_pool.h
+++ b/src/butil/object_pool.h
@@ -23,6 +23,7 @@
 #define BUTIL_OBJECT_POOL_H
 
 #include <cstddef>                       // size_t
+#include "butil/type_traits.h"
 
 // ObjectPool is a derivative class of ResourcePool to allocate and
 // reuse fixed-size objects without identifiers.
@@ -63,6 +64,10 @@ template <typename T> struct ObjectPoolValidator {
     static bool validate(const T*) { return true; }
 };
 
+//
+template <typename T>
+struct ObjectPoolWithASanPoison : true_type {};
+
 }  // namespace butil
 
 #include "butil/object_pool_inl.h"
diff --git a/src/butil/object_pool_inl.h b/src/butil/object_pool_inl.h
index f4853262..20d4aea5 100644
--- a/src/butil/object_pool_inl.h
+++ b/src/butil/object_pool_inl.h
@@ -22,15 +22,16 @@
 #ifndef BUTIL_OBJECT_POOL_INL_H
 #define BUTIL_OBJECT_POOL_INL_H
 
-#include <iostream>                      // std::ostream
-#include <pthread.h>                     // pthread_mutex_t
-#include <algorithm>                     // std::max, std::min
-#include "butil/atomicops.h"             // butil::atomic
-#include "butil/macros.h"                // BAIDU_CACHELINE_ALIGNMENT
-#include "butil/scoped_lock.h"           // BAIDU_SCOPED_LOCK
-#include "butil/thread_local.h"          // BAIDU_THREAD_LOCAL
-#include "butil/memory/aligned_memory.h" // butil::AlignedMemory
+#include <iostream>                       // std::ostream
+#include <pthread.h>                      // pthread_mutex_t
+#include <algorithm>                      // std::max, std::min
 #include <vector>
+#include "butil/atomicops.h"              // butil::atomic
+#include "butil/macros.h"                 // BAIDU_CACHELINE_ALIGNMENT
+#include "butil/scoped_lock.h"            // BAIDU_SCOPED_LOCK
+#include "butil/thread_local.h"           // BAIDU_THREAD_LOCAL
+#include "butil/memory/aligned_memory.h"  // butil::AlignedMemory
+#include "butil/debug/address_annotations.h"
 
 #ifdef BUTIL_OBJECT_POOL_NEED_FREE_ITEM_NUM
 #define BAIDU_OBJECT_POOL_FREE_ITEM_NUM_ADD1                    \
@@ -54,7 +55,7 @@ template <typename T>
 struct ObjectPoolFreeChunk<T, 0> {
     size_t nfree;
     T* ptrs[0];
-}; 
+};
 
 struct ObjectPoolInfo {
     size_t local_pool_num;
@@ -85,6 +86,29 @@ public:
 
 template <typename T>
 class BAIDU_CACHELINE_ALIGNMENT ObjectPool {
+private:
+#ifdef BUTIL_USE_ASAN
+    static void asan_poison_memory_region(T* ptr) {
+        if (!ObjectPoolWithASanPoison<T>::value || NULL == ptr) {
+            return;
+        }
+        // Marks the object as addressable.
+        BUTIL_ASAN_POISON_MEMORY_REGION(ptr, sizeof(T));
+    }
+    static void asan_unpoison_memory_region(T* ptr) {
+        if (!ObjectPoolWithASanPoison<T>::value || NULL == ptr) {
+            return;
+        }
+        // Marks the object as unaddressable.
+        BUTIL_ASAN_UNPOISON_MEMORY_REGION(ptr, sizeof(T));
+    }
+#define OBJECT_POOL_ASAN_POISON_MEMORY_REGION(ptr) 
asan_poison_memory_region(ptr)
+#define OBJECT_POOL_ASAN_UNPOISON_MEMORY_REGION(ptr) 
asan_unpoison_memory_region(ptr)
+#else
+#define OBJECT_POOL_ASAN_POISON_MEMORY_REGION(ptr) ((void)(ptr))
+#define OBJECT_POOL_ASAN_UNPOISON_MEMORY_REGION(ptr) ((void)(ptr))
+#endif // BUTIL_USE_ASAN
+
 public:
     static const size_t BLOCK_NITEM = ObjectPoolBlockItemNum<T>::value;
     static const size_t FREE_CHUNK_NITEM = BLOCK_NITEM;
@@ -93,8 +117,15 @@ public:
     // global list(_free_chunks).
     typedef ObjectPoolFreeChunk<T, FREE_CHUNK_NITEM>    FreeChunk;
     typedef ObjectPoolFreeChunk<T, 0> DynamicFreeChunk;
-
+#ifdef BUTIL_USE_ASAN
+    // According to 
https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning ,
+    // The allocated chunks should start with 8-aligned addresses,
+    // so that AlignedMemory starts with at least 8-aligned addresses.
+    typedef AlignedMemory<sizeof(T), __alignof__(T) < 8 ? 8 : __alignof__(T)> 
BlockItem;
+#else
     typedef AlignedMemory<sizeof(T), __alignof__(T)> BlockItem;
+#endif
+
     // When a thread needs memory, it allocates a Block. To improve locality,
     // items in the Block are only used by the thread.
     // To support cache-aligned objects, align Block.items by cacheline.
@@ -169,6 +200,8 @@ public:
                 obj->~T();                                              \
                 return NULL;                                            \
             }                                                           \
+            /* It's poisoned prior to use. */                           \
+            OBJECT_POOL_ASAN_POISON_MEMORY_REGION(obj);                 \
             ++_cur_block->nitem;                                        \
             return obj;                                                 \
         }                                                               \
@@ -181,6 +214,8 @@ public:
                 obj->~T();                                              \
                 return NULL;                                            \
             }                                                           \
+            /* It's poisoned prior to use. */                           \
+            OBJECT_POOL_ASAN_POISON_MEMORY_REGION(obj);                 \
             ++_cur_block->nitem;                                        \
             return obj;                                                 \
         }                                                               \
@@ -199,6 +234,9 @@ public:
 #undef BAIDU_OBJECT_POOL_GET
 
         inline int return_object(T* ptr) {
+            // TODO. Refer to ASan to implement a efficient quarantine 
mechanism.
+            OBJECT_POOL_ASAN_POISON_MEMORY_REGION(ptr);
+
             // Return to local free list
             if (_cur_free.nfree < ObjectPool::free_chunk_nitem()) {
                 _cur_free.ptrs[_cur_free.nfree++] = ptr;
@@ -238,10 +276,12 @@ public:
     template <typename... Args>
     inline T* get_object(Args&&... args) {
         LocalPool* lp = get_or_new_local_pool();
+        T* ptr = NULL;
         if (BAIDU_LIKELY(lp != NULL)) {
-            return lp->get(std::forward<Args>(args)...);
+            ptr = lp->get(std::forward<Args>(args)...);
+            OBJECT_POOL_ASAN_UNPOISON_MEMORY_REGION(ptr);
         }
-        return NULL;
+        return ptr;
     }
 
     inline int return_object(T* ptr) {
@@ -432,8 +472,10 @@ private:
                     continue;
                 }
                 for (size_t k = 0; k < b->nitem; ++k) {
-                    T* const objs = (T*)b->items;
-                    objs[k].~T();
+                    T* obj = (T*)&b->items[k];
+                    // Unpoison to avoid affecting other allocator.
+                    OBJECT_POOL_ASAN_UNPOISON_MEMORY_REGION(obj);
+                    obj->~T();
                 }
                 delete b;
             }
diff --git a/test/BUILD.bazel b/test/BUILD.bazel
index 1fd937b2..05420ae3 100644
--- a/test/BUILD.bazel
+++ b/test/BUILD.bazel
@@ -177,6 +177,13 @@ cc_library(
     ],
 )
 
+cc_library(
+    name = "gperftools_helper",
+    hdrs = [
+        "gperftools_helper.h",
+    ],
+)
+
 cc_test(
     name = "butil_test",
     srcs = TEST_BUTIL_SOURCES + [
@@ -188,6 +195,7 @@ cc_test(
     deps = [
         ":cc_test_proto",
         ":sstream_workaround",
+        ":gperftools_helper",
         "//:brpc",
         "@com_google_googletest//:gtest",
     ],
@@ -208,6 +216,7 @@ cc_test(
     copts = COPTS,
     deps = [
         ":sstream_workaround",
+        ":gperftools_helper",
         "//:bvar",
         "@com_google_googletest//:gtest",
     ],
@@ -236,6 +245,7 @@ cc_test(
     copts = COPTS,
     deps = [
         ":sstream_workaround",
+        ":gperftools_helper",
         "//:brpc",
         "@com_google_googletest//:gtest",
         "@com_google_googletest//:gtest_main",
diff --git a/test/Makefile b/test/Makefile
index 874e6928..0f1534b7 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -19,7 +19,7 @@ NEED_GPERFTOOLS=1
 NEED_GTEST=1
 include ../config.mk
 CPPFLAGS+=-DBTHREAD_USE_FAST_PTHREAD_MUTEX -D_GNU_SOURCE -DUSE_SYMBOLIZE 
-DNO_TCMALLOC -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS 
-D__STDC_CONSTANT_MACROS -DUNIT_TEST -Dprivate=public -Dprotected=public 
-DBVAR_NOT_LINK_DEFAULT_VARIABLES --include sstream_workaround.h
-CXXFLAGS=$(CPPFLAGS) -pipe -Wall -W -fPIC -fstrict-aliasing 
-Wno-invalid-offsetof -Wno-unused-parameter -fno-omit-frame-pointer -std=c++0x
+CXXFLAGS+=$(CPPFLAGS) -pipe -Wall -W -fPIC -fstrict-aliasing 
-Wno-invalid-offsetof -Wno-unused-parameter -fno-omit-frame-pointer -std=c++0x
 
 #required by butil/crc32.cc to boost performance for 10x
 ifeq ($(shell test $(GCC_VERSION) -ge 40400; echo $$?),0)
diff --git a/test/brpc_builtin_service_unittest.cpp 
b/test/brpc_builtin_service_unittest.cpp
index 8ea17a47..638e5d4c 100644
--- a/test/brpc_builtin_service_unittest.cpp
+++ b/test/brpc_builtin_service_unittest.cpp
@@ -25,7 +25,7 @@
 #include <gtest/gtest.h>
 #include <gflags/gflags.h>
 #include <google/protobuf/descriptor.h>
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "butil/time.h"
 #include "butil/macros.h"
 #include "brpc/socket.h"
@@ -113,7 +113,7 @@ void MyVLogSite() {
 void CheckContent(const brpc::Controller& cntl, const char* name) {
     const std::string& content = cntl.response_attachment().to_string();
     std::size_t pos = content.find(name);
-    ASSERT_TRUE(pos != std::string::npos) << "name=" << name;
+    ASSERT_TRUE(pos != std::string::npos) << "name=" << name << " content=" << 
content;
 }
 
 void CheckErrorText(const brpc::Controller& cntl, const char* error) {
@@ -717,6 +717,7 @@ TEST_F(BuiltinServiceTest, rpcz) {
     }
 }
 
+#if defined(BRPC_ENABLE_CPU_PROFILER) || defined(BAIDU_RPC_ENABLE_CPU_PROFILER)
 TEST_F(BuiltinServiceTest, pprof) {
     brpc::PProfService service;
     {
@@ -758,6 +759,7 @@ TEST_F(BuiltinServiceTest, pprof) {
         CheckContent(cntl, "brpc_builtin_service_unittest");
     }
 }
+#endif // BRPC_ENABLE_CPU_PROFILER || BAIDU_RPC_ENABLE_CPU_PROFILER
 
 TEST_F(BuiltinServiceTest, dir) {
     brpc::DirService service;
@@ -933,6 +935,7 @@ TEST_F(BuiltinServiceTest, sockets) {
     }    
 }
 
+#if defined(BRPC_ENABLE_CPU_PROFILER) || defined(BAIDU_RPC_ENABLE_CPU_PROFILER)
 TEST_F(BuiltinServiceTest, memory) {
     brpc::MemoryService service;
     brpc::MemoryRequest req;
@@ -950,3 +953,4 @@ TEST_F(BuiltinServiceTest, memory) {
     CheckContent(cntl, "tcmalloc.pageheap_free_bytes");
     CheckContent(cntl, "tcmalloc.pageheap_unmapped_bytes");
 }
+#endif // BRPC_ENABLE_CPU_PROFILER || BAIDU_RPC_ENABLE_CPU_PROFILER
diff --git a/test/brpc_event_dispatcher_unittest.cpp 
b/test/brpc_event_dispatcher_unittest.cpp
index 185e9f2d..5c0aa064 100644
--- a/test/brpc_event_dispatcher_unittest.cpp
+++ b/test/brpc_event_dispatcher_unittest.cpp
@@ -23,7 +23,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <gtest/gtest.h>
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "butil/time.h"
 #include "butil/macros.h"
 #include "butil/fd_utility.h"
@@ -345,13 +345,11 @@ TEST_F(EventDispatcherTest, dispatch_tasks) {
     ProfilerStart("event_dispatcher.prof");
     butil::Timer tm;
     tm.start();
-    
     sleep(5);
-    
     tm.stop();
     ProfilerStop();
     LOG(INFO) << "End profiling";
-    
+
     size_t client_bytes = 0;
     size_t server_bytes = 0;
     for (size_t i = 0; i < NCLIENT; ++i) {
diff --git a/test/brpc_h2_unsent_message_unittest.cpp 
b/test/brpc_h2_unsent_message_unittest.cpp
index 79b56f55..acb79039 100644
--- a/test/brpc_h2_unsent_message_unittest.cpp
+++ b/test/brpc_h2_unsent_message_unittest.cpp
@@ -25,7 +25,7 @@
 #include "butil/atomicops.h"
 #include "brpc/policy/http_rpc_protocol.h"
 #include "brpc/policy/http2_rpc_protocol.h"
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 
 int main(int argc, char* argv[]) {
     testing::InitGoogleTest(&argc, argv);
diff --git a/test/brpc_http_rpc_protocol_unittest.cpp 
b/test/brpc_http_rpc_protocol_unittest.cpp
index cf23debd..ad631a2e 100644
--- a/test/brpc_http_rpc_protocol_unittest.cpp
+++ b/test/brpc_http_rpc_protocol_unittest.cpp
@@ -733,15 +733,19 @@ private:
     butil::Status _destroying_st;
 };
 
+#ifdef BUTIL_USE_ASAN
+static const int GENERAL_DELAY_US = 1000000; // 1s
+#else
 static const int GENERAL_DELAY_US = 300000; // 0.3s
+#endif
 
 TEST_F(HttpTest, read_long_body_progressively) {
+    DownloadServiceImpl svc(DONE_BEFORE_CREATE_PA,
+                            std::numeric_limits<size_t>::max());
     butil::intrusive_ptr<ReadBody> reader;
     {
         const int port = 8923;
         brpc::Server server;
-        DownloadServiceImpl svc(DONE_BEFORE_CREATE_PA,
-                                std::numeric_limits<size_t>::max());
         EXPECT_EQ(0, server.AddService(&svc, brpc::SERVER_DOESNT_OWN_SERVICE));
         EXPECT_EQ(0, server.Start(port, NULL));
         {
@@ -823,12 +827,12 @@ TEST_F(HttpTest, read_short_body_progressively) {
 }
 
 TEST_F(HttpTest, read_progressively_after_cntl_destroys) {
+    DownloadServiceImpl svc(DONE_BEFORE_CREATE_PA,
+                            std::numeric_limits<size_t>::max());
     butil::intrusive_ptr<ReadBody> reader;
     {
         const int port = 8923;
         brpc::Server server;
-        DownloadServiceImpl svc(DONE_BEFORE_CREATE_PA,
-                                std::numeric_limits<size_t>::max());
         EXPECT_EQ(0, server.AddService(&svc, brpc::SERVER_DOESNT_OWN_SERVICE));
         EXPECT_EQ(0, server.Start(port, NULL));
         {
@@ -870,11 +874,11 @@ TEST_F(HttpTest, read_progressively_after_cntl_destroys) {
 
 TEST_F(HttpTest, read_progressively_after_long_delay) {
     butil::intrusive_ptr<ReadBody> reader;
+    DownloadServiceImpl svc(DONE_BEFORE_CREATE_PA,
+                            std::numeric_limits<size_t>::max());
     {
         const int port = 8923;
         brpc::Server server;
-        DownloadServiceImpl svc(DONE_BEFORE_CREATE_PA,
-                                std::numeric_limits<size_t>::max());
         EXPECT_EQ(0, server.AddService(&svc, brpc::SERVER_DOESNT_OWN_SERVICE));
         EXPECT_EQ(0, server.Start(port, NULL));
         {
@@ -919,10 +923,10 @@ TEST_F(HttpTest, read_progressively_after_long_delay) {
 }
 
 TEST_F(HttpTest, skip_progressive_reading) {
-    const int port = 8923;
-    brpc::Server server;
     DownloadServiceImpl svc(DONE_BEFORE_CREATE_PA,
                             std::numeric_limits<size_t>::max());
+    const int port = 8923;
+    brpc::Server server;
     EXPECT_EQ(0, server.AddService(&svc, brpc::SERVER_DOESNT_OWN_SERVICE));
     EXPECT_EQ(0, server.Start(port, NULL));
     brpc::Channel channel;
@@ -1029,6 +1033,7 @@ TEST_F(HttpTest, broken_socket_stops_progressive_reading) 
{
     ASSERT_EQ(ECONNRESET, reader->destroying_status().error_code());
 }
 
+#ifndef BUTIL_USE_ASAN
 static const std::string TEST_PROGRESSIVE_HEADER = "Progressive";
 static const std::string TEST_PROGRESSIVE_HEADER_VAL = "Progressive-val";
 
@@ -1144,6 +1149,8 @@ TEST_F(HttpTest, 
server_end_read_short_body_progressively) {
     ASSERT_FALSE(cntl.Failed());
 }
 
+// Fixme!!! Server progressive reader has a heap-use-after-free bug detected 
by ASan.
+// For details, see 
https://github.com/apache/brpc/issues/2145#issuecomment-2329413363
 TEST_F(HttpTest, server_end_read_failed) {
     const int port = 8923;
     brpc::ServiceOptions opt;
@@ -1180,6 +1187,7 @@ TEST_F(HttpTest, server_end_read_failed) {
     channel.CallMethod(NULL, &cntl, NULL, NULL, NULL);
     ASSERT_TRUE(cntl.Failed());
 }
+#endif // BUTIL_USE_ASAN
 
 TEST_F(HttpTest, http2_sanity) {
     const int port = 8923;
diff --git a/test/brpc_hulu_pbrpc_protocol_unittest.cpp 
b/test/brpc_hulu_pbrpc_protocol_unittest.cpp
index 7c971dc2..d00b9d45 100644
--- a/test/brpc_hulu_pbrpc_protocol_unittest.cpp
+++ b/test/brpc_hulu_pbrpc_protocol_unittest.cpp
@@ -27,7 +27,6 @@
 #include <google/protobuf/descriptor.h>
 #include "butil/time.h"
 #include "butil/macros.h"
-#include "butil/gperftools_profiler.h"
 #include "brpc/socket.h"
 #include "brpc/acceptor.h"
 #include "brpc/server.h"
diff --git a/test/brpc_input_messenger_unittest.cpp 
b/test/brpc_input_messenger_unittest.cpp
index 00b14ed4..812c499a 100644
--- a/test/brpc_input_messenger_unittest.cpp
+++ b/test/brpc_input_messenger_unittest.cpp
@@ -21,9 +21,9 @@
 
 #include <sys/types.h>
 #include <sys/socket.h>
-#include <netdb.h>                   //
+#include <netdb.h>
 #include <gtest/gtest.h>
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "butil/time.h"
 #include "butil/macros.h"
 #include "butil/fd_utility.h"
@@ -180,7 +180,8 @@ TEST_F(MessengerTest, dispatch_tasks) {
     }
 
     sleep(1);
-    
+
+
     LOG(INFO) << "Begin to profile... (5 seconds)";
     ProfilerStart("input_messenger.prof");
 
diff --git a/test/brpc_load_balancer_unittest.cpp 
b/test/brpc_load_balancer_unittest.cpp
index 2abb869e..a0a62c27 100644
--- a/test/brpc_load_balancer_unittest.cpp
+++ b/test/brpc_load_balancer_unittest.cpp
@@ -23,7 +23,7 @@
 #include <map>
 #include <gtest/gtest.h>
 #include "bthread/bthread.h"
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "butil/containers/doubly_buffered_data.h"
 #include "brpc/describable.h"
 #include "brpc/socket.h"
diff --git a/test/brpc_protobuf_json_unittest.cpp 
b/test/brpc_protobuf_json_unittest.cpp
index 3565772e..5bb158d7 100644
--- a/test/brpc_protobuf_json_unittest.cpp
+++ b/test/brpc_protobuf_json_unittest.cpp
@@ -26,7 +26,7 @@
 #include "butil/strings/string_util.h"
 #include "butil/third_party/rapidjson/rapidjson.h"
 #include "butil/time.h"
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "json2pb/pb_to_json.h"
 #include "json2pb/json_to_pb.h"
 #include "json2pb/encode_decode.h"
@@ -505,8 +505,8 @@ TEST_F(ProtobufJsonTest, json_to_pb_perf_case) {
 
     printf("----------test json to pb performance------------\n\n");
 
-    std::string error; 
-  
+    std::string error;
+
     ProfilerStart("json_to_pb_perf.prof");
     butil::Timer timer;
     bool res;
@@ -542,8 +542,8 @@ TEST_F(ProtobufJsonTest, 
json_to_pb_encode_decode_perf_case) {
                           \"welcome\", 
\"enum--type\":1},\"uid*\":\"welcome\"}], \
                           \"judge\":false, \"spur\":2, \"data:array\":[]}";  
     printf("----------test json to pb encode/decode 
performance------------\n\n");
-    std::string error; 
-    
+    std::string error;
+
     ProfilerStart("json_to_pb_encode_decode_perf.prof");
     butil::Timer timer;
     bool res;
@@ -593,7 +593,7 @@ TEST_F(ProtobufJsonTest, json_to_pb_complex_perf_case) {
     json2pb::Json2PbOptions options;
     options.base64_to_bytes = false;
     ProfilerStart("json_to_pb_complex_perf.prof");
-    for (int i = 0; i < times; i++) { 
+    for (int i = 0; i < times; i++) {
         gss::message::gss_us_res_t data;
         butil::IOBufAsZeroCopyInputStream stream(buf); 
         timer.start();
@@ -625,7 +625,7 @@ TEST_F(ProtobufJsonTest, 
json_to_pb_to_string_complex_perf_case) {
     json2pb::Json2PbOptions options;
     options.base64_to_bytes = false;
     ProfilerStart("json_to_pb_to_string_complex_perf.prof");
-    for (int i = 0; i < times; i++) { 
+    for (int i = 0; i < times; i++) {
         gss::message::gss_us_res_t data;
         timer.start();
         res = json2pb::JsonToProtoMessage(info3, &data, options, &error);
@@ -1292,6 +1292,7 @@ TEST_F(ProtobufJsonTest, pb_to_json_perf_case) {
     printf("text:%s\n", text.data());
 
     printf("----------test pb to json performance------------\n\n");
+
     ProfilerStart("pb_to_json_perf.prof");
     butil::Timer timer;
     bool res;
@@ -1352,6 +1353,7 @@ TEST_F(ProtobufJsonTest, 
pb_to_json_encode_decode_perf_case) {
 
     printf("----------test pb to json encode decode 
performance------------\n\n");
     ProfilerStart("pb_to_json_encode_decode_perf.prof");
+
     butil::Timer timer;
     bool res;
     float avg_time1 = 0;
@@ -1401,7 +1403,7 @@ TEST_F(ProtobufJsonTest, pb_to_json_complex_perf_case) {
     res = JsonToProtoMessage(info3, &data, option, &error);
     ASSERT_TRUE(res) << error;
     ProfilerStart("pb_to_json_complex_perf.prof");
-    for (int i = 0; i < times; i++) { 
+    for (int i = 0; i < times; i++) {
         std::string error1;
         timer.start();
         butil::IOBuf buf;
@@ -1437,7 +1439,7 @@ TEST_F(ProtobufJsonTest, 
pb_to_json_to_string_complex_perf_case) {
     res = JsonToProtoMessage(info3, &data, option, &error);
     ASSERT_TRUE(res);
     ProfilerStart("pb_to_json_to_string_complex_perf.prof");
-    for (int i = 0; i < times; i++) { 
+    for (int i = 0; i < times; i++) {
         std::string info4;  
         std::string error1;
         timer.start();
diff --git a/test/brpc_snappy_compress_unittest.cpp 
b/test/brpc_snappy_compress_unittest.cpp
index ee6a00e6..94b54dfd 100644
--- a/test/brpc_snappy_compress_unittest.cpp
+++ b/test/brpc_snappy_compress_unittest.cpp
@@ -20,7 +20,7 @@
 // Date: 2015/01/20 19:01:06
 
 #include <gtest/gtest.h>
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "butil/third_party/snappy/snappy.h"
 #include "butil/macros.h"
 #include "butil/iobuf.h"
diff --git a/test/brpc_socket_unittest.cpp b/test/brpc_socket_unittest.cpp
index 70dd8021..78b83503 100644
--- a/test/brpc_socket_unittest.cpp
+++ b/test/brpc_socket_unittest.cpp
@@ -24,7 +24,7 @@
 #include <fcntl.h>  // F_GETFD
 #include <gtest/gtest.h>
 #include <gflags/gflags.h>
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "butil/time.h"
 #include "butil/macros.h"
 #include "butil/fd_utility.h"
diff --git a/test/bthread_cond_unittest.cpp b/test/bthread_cond_unittest.cpp
index 948b75de..035ff333 100644
--- a/test/bthread_cond_unittest.cpp
+++ b/test/bthread_cond_unittest.cpp
@@ -21,7 +21,7 @@
 #include "butil/time.h"
 #include "butil/macros.h"
 #include "butil/scoped_lock.h"
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "bthread/bthread.h"
 #include "bthread/condition_variable.h"
 #include "bthread/stack.h"
diff --git a/test/bthread_dispatcher_unittest.cpp 
b/test/bthread_dispatcher_unittest.cpp
index a2008020..669d9c4e 100644
--- a/test/bthread_dispatcher_unittest.cpp
+++ b/test/bthread_dispatcher_unittest.cpp
@@ -25,7 +25,7 @@
 #include "butil/scoped_lock.h"
 #include "butil/fd_utility.h"
 #include "butil/logging.h"
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "bthread/bthread.h"
 #include "bthread/task_control.h"
 #include "bthread/task_group.h"
@@ -192,6 +192,7 @@ void* client_thread(void* arg) {
             }
         }
     }
+    free(buf);
     return NULL;
 }
 
@@ -256,7 +257,7 @@ TEST(DispatcherTest, dispatch_tasks) {
         cm[i]->bytes = 0;
         ASSERT_EQ(0, pthread_create(&cth[i], NULL, client_thread, cm[i]));
     }
-    
+
     ProfilerStart("dispatcher.prof");
     butil::Timer tm;
     tm.start();
diff --git a/test/bthread_execution_queue_unittest.cpp 
b/test/bthread_execution_queue_unittest.cpp
index 6ecad01b..1baaa777 100644
--- a/test/bthread_execution_queue_unittest.cpp
+++ b/test/bthread_execution_queue_unittest.cpp
@@ -22,7 +22,7 @@
 #include <bthread/countdown_event.h>
 #include "butil/time.h"
 #include "butil/fast_rand.h"
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 
 namespace {
 bool stopped = false;
diff --git a/test/bthread_fd_unittest.cpp b/test/bthread_fd_unittest.cpp
index 49275c2c..91b89c21 100644
--- a/test/bthread_fd_unittest.cpp
+++ b/test/bthread_fd_unittest.cpp
@@ -16,13 +16,14 @@
 // under the License.
 
 #include "butil/compat.h"
+#include "butil/compiler_specific.h"
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/utsname.h>                           // uname
 #include <fcntl.h>
 #include <gtest/gtest.h>
 #include <pthread.h>
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "butil/time.h"
 #include "butil/macros.h"
 #include "butil/fd_utility.h"
@@ -328,7 +329,7 @@ TEST(FDTest, ping_pong) {
     }
     tm.stop();
     ProfilerStop();
-    LOG(INFO) << "tid=" << REP*NCLIENT*1000000L/tm.u_elapsed();
+    LOG(INFO) << "tid=" << REP*NCLIENT * 1000000L / tm.u_elapsed();
     stop = true;
     for (size_t i = 0; i < NEPOLL; ++i) {
 #if defined(OS_LINUX)
diff --git a/test/bthread_mutex_unittest.cpp b/test/bthread_mutex_unittest.cpp
index 3163876c..f839d063 100644
--- a/test/bthread_mutex_unittest.cpp
+++ b/test/bthread_mutex_unittest.cpp
@@ -25,7 +25,7 @@
 #include "bthread/butex.h"
 #include "bthread/task_control.h"
 #include "bthread/mutex.h"
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 
 namespace {
 inline unsigned* get_butex(bthread_mutex_t & m) {
@@ -206,7 +206,7 @@ void PerfTest(Mutex* mutex,
     }
     g_started = true;
     char prof_name[32];
-    snprintf(prof_name, sizeof(prof_name), "mutex_perf_%d.prof", 
++g_prof_name_counter); 
+    snprintf(prof_name, sizeof(prof_name), "mutex_perf_%d.prof", 
++g_prof_name_counter);
     ProfilerStart(prof_name);
     usleep(500 * 1000);
     ProfilerStop();
diff --git a/test/bthread_rwlock_unittest.cpp b/test/bthread_rwlock_unittest.cpp
index 3318956b..815494ca 100644
--- a/test/bthread_rwlock_unittest.cpp
+++ b/test/bthread_rwlock_unittest.cpp
@@ -16,7 +16,7 @@
 // under the License.
 
 #include <gtest/gtest.h>
-#include <butil/gperftools_profiler.h>
+#include "gperftools_helper.h"
 #include <bthread/rwlock.h>
 
 namespace {
@@ -447,6 +447,7 @@ TEST(RWLockTest, pthread_rdlock_performance) {
         long* res = NULL;
         pthread_join(rth[i], (void**)&res);
         printf("read thread %lu = %ldns\n", i, *res);
+        delete res;
     }
     pthread_join(wth, NULL);
 #ifdef CHECK_RWLOCK
diff --git a/test/bthread_setconcurrency_unittest.cpp 
b/test/bthread_setconcurrency_unittest.cpp
index 4d269b64..9e2e50d1 100644
--- a/test/bthread_setconcurrency_unittest.cpp
+++ b/test/bthread_setconcurrency_unittest.cpp
@@ -97,7 +97,7 @@ TEST(BthreadTest, setconcurrency_with_running_bthread) {
     *odd = 0;
     *even = 0;
     std::vector<bthread_t> tids;
-    const int N = 500;
+    const int N = 200;
     for (int i = 0; i < N; ++i) {
         bthread_t tid;
         bthread_start_background(&tid, &BTHREAD_ATTR_SMALL, odd_thread, NULL);
@@ -155,7 +155,7 @@ TEST(BthreadTest, min_concurrency) {
     ASSERT_EQ(1, set_min_concurrency(0)); // set min success
     ASSERT_EQ(0, get_min_concurrency());
     int conn = bthread_getconcurrency();
-    int add_conn = 100;
+    int add_conn = 50;
 
     ASSERT_EQ(0, set_min_concurrency(conn + 1)); // set min failed
     ASSERT_EQ(0, get_min_concurrency());
diff --git a/test/bthread_unittest.cpp b/test/bthread_unittest.cpp
index 57f4fc82..1fbefb27 100644
--- a/test/bthread_unittest.cpp
+++ b/test/bthread_unittest.cpp
@@ -20,7 +20,7 @@
 #include "butil/time.h"
 #include "butil/macros.h"
 #include "butil/logging.h"
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "bthread/bthread.h"
 #include "bthread/unstable.h"
 #include "bthread/task_meta.h"
@@ -230,6 +230,7 @@ TEST_F(BthreadTest, backtrace) {
     for (int i = 0; i < bt_cnt; ++i) {
         puts(text[i]);
     }
+    free(text);
 }
 
 void* show_self(void*) {
@@ -330,7 +331,7 @@ TEST_F(BthreadTest, small_threads) {
             LOG(INFO) << "[Round " << j + 1 << "] bthread_start_urgent takes "
                       << tm.n_elapsed()/N << "ns, sum=" << s;
             ASSERT_EQ(N * (j + 1), (size_t)s);
-        
+
             // Check uniqueness of th
             std::sort(th.begin(), th.end());
             ASSERT_EQ(th.end(), std::unique(th.begin(), th.end()));
@@ -339,9 +340,14 @@ TEST_F(BthreadTest, small_threads) {
 }
 
 void* bthread_starter(void* void_counter) {
+    std::vector<bthread_t> ths;
     while (!stop.load(butil::memory_order_relaxed)) {
         bthread_t th;
         EXPECT_EQ(0, bthread_start_urgent(&th, NULL, adding_func, 
void_counter));
+        ths.push_back(th);
+    }
+    for (size_t i = 0; i < ths.size(); ++i) {
+        EXPECT_EQ(0, bthread_join(ths[i], NULL));
     }
     return NULL;
 }
@@ -361,7 +367,7 @@ TEST_F(BthreadTest, start_bthreads_frequently) {
     bthread_t th[con];
 
     std::cout << "Perf with different parameters..." << std::endl;
-    //ProfilerStart(prof_name);
+    ProfilerStart(prof_name);
     for (int cur_con = 1; cur_con <= con; ++cur_con) {
         stop = false;
         for (int i = 0; i < cur_con; ++i) {
@@ -384,7 +390,7 @@ TEST_F(BthreadTest, start_bthreads_frequently) {
         std::cout << sum << ",";
     }
     std::cout << std::endl;
-    //ProfilerStop();
+    ProfilerStop();
     delete [] counters;
 }
 
diff --git a/test/bthread_work_stealing_queue_unittest.cpp 
b/test/bthread_work_stealing_queue_unittest.cpp
index 82897533..92fbb910 100644
--- a/test/bthread_work_stealing_queue_unittest.cpp
+++ b/test/bthread_work_stealing_queue_unittest.cpp
@@ -109,6 +109,7 @@ TEST(WSQTest, sanity) {
         for (size_t j = 0; j < res->size(); ++j, ++nstolen) {
             values.push_back((*res)[j]);
         }
+        delete res;
     }
     pthread_join(wth, NULL);
     std::vector<value_type>* res = NULL;
@@ -116,6 +117,7 @@ TEST(WSQTest, sanity) {
     for (size_t j = 0; j < res->size(); ++j, ++npopped) {
         values.push_back((*res)[j]);
     }
+    delete res;
 
     value_type val;
     while (q.pop(&val)) {
diff --git a/test/bvar_lock_timer_unittest.cpp 
b/test/bvar_lock_timer_unittest.cpp
index 52b42f9b..f69ba3a2 100644
--- a/test/bvar_lock_timer_unittest.cpp
+++ b/test/bvar_lock_timer_unittest.cpp
@@ -22,7 +22,7 @@
 #include <condition_variable>
 #endif
 #include <gtest/gtest.h>
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "bvar/utils/lock_timer.h"
 
 namespace {
@@ -220,7 +220,7 @@ TEST_F(LockTimerTest, overhead) {
     MutexWithLatencyRecorder<DummyMutex> m0(r0);
     butil::Timer timer;
     const size_t N = 1000 * 1000 * 10;
-    
+
     ProfilerStart("mutex_with_latency_recorder.prof");
     timer.start();
     for (size_t i = 0; i < N; ++i) {
diff --git a/test/gperftools_helper.h b/test/gperftools_helper.h
new file mode 100644
index 00000000..c507ade7
--- /dev/null
+++ b/test/gperftools_helper.h
@@ -0,0 +1,34 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 TEST_GPERFTOOLS_HELPER_H_
+#define TEST_GPERFTOOLS_HELPER_H_
+
+#if defined(BRPC_ENABLE_CPU_PROFILER) || defined(BAIDU_RPC_ENABLE_CPU_PROFILER)
+#include "butil/gperftools_profiler.h"
+#else
+extern "C" {
+
+// If gperftools are not used, these functions are no-ops.
+inline int ProfilerStart(const char*) { return 0; }
+inline void ProfilerStop() {}
+inline void ProfilerFlush() {}
+
+}  // extern "C"
+#endif // BRPC_ENABLE_CPU_PROFILER || BAIDU_RPC_ENABLE_CPU_PROFILER
+
+#endif // TEST_GPERFTOOLS_HELPER_H_
diff --git a/test/iobuf_unittest.cpp b/test/iobuf_unittest.cpp
index da36c180..b45a174c 100644
--- a/test/iobuf_unittest.cpp
+++ b/test/iobuf_unittest.cpp
@@ -291,6 +291,7 @@ TEST_F(IOBufTest, appendv) {
     ASSERT_EQ(0, b.appendv(vec2, arraysize(vec2)));
     ASSERT_EQ(full_len, b.size());
     ASSERT_EQ(0, memcmp(str, b.to_string().data(), full_len));
+    free(str);
 }
 
 TEST_F(IOBufTest, reserve) {
@@ -345,6 +346,8 @@ TEST_F(IOBufTest, reserve) {
     ASSERT_EQ("orang" + s2 + s1, b.to_string());
 }
 
+#ifndef BUTIL_USE_ASAN
+// ASan will detect heap-buffer-overflow error casued by FakeBlock.
 struct FakeBlock {
     int nshared;
     FakeBlock() : nshared(1) {}
@@ -451,6 +454,7 @@ TEST_F(IOBufTest, iobuf_as_queue) {
         delete blocks[i];
     }
 }
+#endif // BUTIL_USE_ASAN
 
 TEST_F(IOBufTest, iobuf_sanity) {
     install_debug_allocator();
diff --git a/test/logging_unittest.cc b/test/logging_unittest.cc
index a431e01b..3a7576ff 100644
--- a/test/logging_unittest.cc
+++ b/test/logging_unittest.cc
@@ -4,7 +4,7 @@
 
 #include "butil/basictypes.h"
 #include "butil/logging.h"
-#include "butil/gperftools_profiler.h"
+#include "gperftools_helper.h"
 #include "butil/files/temp_file.h"
 #include "butil/popen.h"
 #include <gtest/gtest.h>
@@ -540,6 +540,7 @@ TEST_F(LoggingTest, async_log) {
     FLAGS_async_log = saved_async_log;
 }
 
+#if defined(BRPC_ENABLE_CPU_PROFILER) || defined(BAIDU_RPC_ENABLE_CPU_PROFILER)
 struct BAIDU_CACHELINE_ALIGNMENT PerfArgs {
     const std::string* log;
     int64_t counter;
@@ -643,6 +644,7 @@ TEST_F(LoggingTest, performance) {
 
     FLAGS_async_log = saved_async_log;
 }
+#endif // BRPC_ENABLE_CPU_PROFILER || BAIDU_RPC_ENABLE_CPU_PROFILER
 
 }  // namespace
 
diff --git a/test/mpsc_queue_unittest.cc b/test/mpsc_queue_unittest.cc
index f57a85f6..c651e3a0 100644
--- a/test/mpsc_queue_unittest.cc
+++ b/test/mpsc_queue_unittest.cc
@@ -2,9 +2,6 @@
 #include <pthread.h>
 #include "butil/containers/mpsc_queue.h"
 
-#define BAIDU_CLEAR_OBJECT_POOL_AFTER_ALL_THREADS_QUIT
-#include "butil/object_pool.h"
-
 namespace {
 
 const uint MAX_COUNT = 1000000;
diff --git a/test/popen_unittest.cpp b/test/popen_unittest.cpp
index efd307e4..81d4cdec 100644
--- a/test/popen_unittest.cpp
+++ b/test/popen_unittest.cpp
@@ -135,6 +135,7 @@ TEST(PopenTest, does_vfork_suspend_all_threads) {
               << " as=" << counter_after_sleep
               << std::endl;
     ASSERT_EQ(cpid, waitpid(cpid, &ws, __WALL));
+    free(child_stack_mem);
 }
 
 #endif  // OS_LINUX
diff --git a/test/run_tests.sh b/test/run_tests.sh
index 14297797..2e174777 100755
--- a/test/run_tests.sh
+++ b/test/run_tests.sh
@@ -26,7 +26,10 @@ test_bins="test_butil test_bvar bthread*unittest 
brpc*unittest"
 for test_bin in $test_bins; do
     test_num=$((test_num + 1))
     >&2 echo "[runtest] $test_bin"
-    ./$test_bin
+    ASAN_OPTIONS="detect_leaks=0" ./$test_bin
+    # If ASan abort without detailed call stack of new/delete,
+    # try to disable fast_unwind_on_malloc, which would be a performance 
killer.
+    # ASAN_OPTIONS="fast_unwind_on_malloc=0:detect_leaks=0" ./$test_bin
     rc=$?
     if [ $rc -ne 0 ]; then
         failed_test="$test_bin"
diff --git a/test/security_unittest.cc b/test/security_unittest.cc
index d418ebbe..5121775d 100644
--- a/test/security_unittest.cc
+++ b/test/security_unittest.cc
@@ -22,6 +22,9 @@
 #include <sys/mman.h>
 #include <unistd.h>
 #endif
+#include "butil/compiler_specific.h"
+
+#ifdef BUTIL_USE_ASAN
 
 using std::nothrow;
 using std::numeric_limits;
@@ -298,3 +301,5 @@ TEST(SecurityTest, TCMALLOC_TEST(RandomMemoryAllocations)) {
 #endif  // defined(OS_LINUX) && defined(__x86_64__)
 
 }  // namespace
+
+#endif // BUTIL_USE_ASAN
diff --git a/test/stack_trace_unittest.cc b/test/stack_trace_unittest.cc
index 992a7404..72b1b986 100644
--- a/test/stack_trace_unittest.cc
+++ b/test/stack_trace_unittest.cc
@@ -145,6 +145,7 @@ void CheckDebugOutputToStream(bool exclude_self) {
     std::ostringstream os;
     trace.OutputToStream(&os);
     VLOG(1) << os.str();
+    free(addr);
 }
 
 // The test is used for manual testing, e.g., to see the raw output.
diff --git a/test/thread_key_unittest.cpp b/test/thread_key_unittest.cpp
index 5a95b8f0..758a6791 100644
--- a/test/thread_key_unittest.cpp
+++ b/test/thread_key_unittest.cpp
@@ -309,8 +309,6 @@ TEST(ThreadLocalTest, thread_key_multi_thread) {
     }
 }
 
-DEFINE_bool(test_pthread_key, true, "test pthread_key");
-
 struct BAIDU_CACHELINE_ALIGNMENT ThreadKeyPerfArgs {
     pthread_key_t pthread_key;
     ThreadKey* thread_key;


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

Reply via email to