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