Skip to content

Commit 8080e87

Browse files
authored
Enable standalone GWP-ASan unit tests (#4)
1 parent cc51fd6 commit 8080e87

10 files changed

Lines changed: 1867 additions & 69 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,13 @@ jobs:
3030
- name: Install dependencies
3131
run: |
3232
sudo apt-get update
33-
sudo apt-get install -y cmake ninja-build libjemalloc-dev
33+
sudo apt-get install -y cmake ninja-build libjemalloc-dev libgtest-dev
3434
3535
- name: Configure
36-
run: cmake -S . -B build -G Ninja -DBUILD_EXAMPLE=ON
36+
run: cmake -S . -B build -G Ninja -DGWP_ASAN_BUILD_EXAMPLE=ON -DGWP_ASAN_BUILD_TESTING=ON
3737

3838
- name: Build
3939
run: cmake --build build --parallel
40+
41+
- name: Test
42+
run: ctest --test-dir build --output-on-failure

CMakeLists.txt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,28 @@ cmake_minimum_required(VERSION 3.17)
22
project(gwp-asan)
33

44
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
5-
option(BUILD_EXAMPLE "Build examples" OFF)
5+
6+
if (DEFINED BUILD_EXAMPLE AND NOT DEFINED GWP_ASAN_BUILD_EXAMPLE)
7+
set(GWP_ASAN_BUILD_EXAMPLE "${BUILD_EXAMPLE}" CACHE BOOL "Build examples")
8+
endif ()
9+
if (DEFINED BUILD_EXAMPLE)
10+
message(DEPRECATION
11+
"BUILD_EXAMPLE is deprecated; use GWP_ASAN_BUILD_EXAMPLE instead.")
12+
endif ()
13+
14+
option(GWP_ASAN_BUILD_EXAMPLE "Build examples" OFF)
15+
option(GWP_ASAN_BUILD_TESTING "Build tests" OFF)
16+
set(GWP_ASAN_GTEST_SOURCE_DIR "" CACHE PATH
17+
"Path to a googletest checkout used when GTest is not installed")
618

719
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
820

21+
if (GWP_ASAN_BUILD_TESTING)
22+
enable_testing()
23+
endif ()
24+
925
add_subdirectory(gwp_asan)
1026

11-
if (BUILD_EXAMPLE)
27+
if (GWP_ASAN_BUILD_EXAMPLE)
1228
add_subdirectory(example)
1329
endif ()

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,32 @@ This repo contains a standalone gwp-asan, you can easily use it with CMake.
1616

1717
Check the documents here: [gwp-asan](https://llvm.org/docs/GwpAsan.html)
1818

19+
## Build
20+
21+
```bash
22+
cmake -S . -B build -G Ninja
23+
cmake --build build --parallel
24+
```
25+
26+
Build the example:
27+
28+
```bash
29+
cmake -S . -B build -G Ninja -DGWP_ASAN_BUILD_EXAMPLE=ON
30+
cmake --build build --parallel
31+
```
32+
33+
Run the upstream GWP-ASan unit tests:
34+
35+
```bash
36+
cmake -S . -B build -G Ninja -DGWP_ASAN_BUILD_TESTING=ON
37+
cmake --build build --parallel
38+
ctest --test-dir build --output-on-failure
39+
```
40+
41+
The tests require GoogleTest. Install `libgtest-dev` on Ubuntu or
42+
`gtest-devel` on Fedora/RHEL. If GoogleTest is not installed system-wide, pass
43+
`-DGWP_ASAN_GTEST_SOURCE_DIR=/path/to/googletest`.
44+
1945
## Integration
2046

2147
TL;DR read the [example](https://github.com/fanyang89/gwp-asan/blob/main/example/helloworld/main.cc)

gwp_asan/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,6 @@ add_library(RTGwpAsanSegvHandler STATIC
7777
optional/segv_handler_posix.cpp
7878
)
7979

80-
if (COMPILER_RT_INCLUDE_TESTS)
80+
if (GWP_ASAN_BUILD_TESTING)
8181
add_subdirectory(tests)
8282
endif ()

gwp_asan/optional/backtrace_linux_libc.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength,
4545

4646
for (size_t i = 0; i < TraceLength; ++i) {
4747
if (!BacktraceSymbols)
48-
Printf(" #%zu %p\n", i, Trace[i]);
48+
Printf(" #%zu 0x%zx\n", i, Trace[i]);
4949
else
5050
Printf(" #%zu %s\n", i, BacktraceSymbols[i]);
5151
}

gwp_asan/tests/CMakeLists.txt

Lines changed: 54 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,5 @@
1-
include(CompilerRTCompile)
2-
3-
set(GWP_ASAN_UNITTEST_CFLAGS
4-
${COMPILER_RT_UNITTEST_CFLAGS}
5-
${COMPILER_RT_GTEST_CFLAGS}
6-
${SANITIZER_TEST_CXX_CFLAGS}
7-
-std=c++17
8-
-I${COMPILER_RT_SOURCE_DIR}/lib/
9-
-O2
10-
-g
11-
-fno-omit-frame-pointer)
12-
13-
file(GLOB GWP_ASAN_HEADERS ../*.h)
141
set(GWP_ASAN_UNITTESTS
15-
platform_specific/printf_sanitizer_common.cpp
2+
platform_specific/printf_standalone.cpp
163
alignment.cpp
174
backtrace.cpp
185
basic.cpp
@@ -32,53 +19,63 @@ set(GWP_ASAN_UNITTESTS
3219
utilities.cpp
3320
)
3421

35-
set(GWP_ASAN_UNIT_TEST_HEADERS
36-
${GWP_ASAN_HEADERS}
37-
harness.h)
38-
39-
add_custom_target(GwpAsanUnitTests)
40-
set_target_properties(GwpAsanUnitTests PROPERTIES FOLDER "Compiler-RT/Tests")
22+
if (GWP_ASAN_GTEST_SOURCE_DIR)
23+
set(GWP_ASAN_GTEST_ROOT "${GWP_ASAN_GTEST_SOURCE_DIR}")
24+
if (EXISTS "${GWP_ASAN_GTEST_ROOT}/googletest")
25+
set(GWP_ASAN_GTEST_ROOT "${GWP_ASAN_GTEST_ROOT}/googletest")
26+
endif ()
27+
if (NOT EXISTS "${GWP_ASAN_GTEST_ROOT}/include/gtest/gtest.h" OR
28+
NOT EXISTS "${GWP_ASAN_GTEST_ROOT}/src/gtest-all.cc")
29+
message(FATAL_ERROR
30+
"GWP_ASAN_GTEST_SOURCE_DIR must point to a googletest checkout")
31+
endif ()
4132

42-
set(GWP_ASAN_UNITTEST_LINK_FLAGS
43-
${COMPILER_RT_UNITTEST_LINK_FLAGS} -ldl
44-
${COMPILER_RT_UNWINDER_LINK_LIBS}
45-
${SANITIZER_TEST_CXX_LIBRARIES})
46-
list(APPEND GWP_ASAN_UNITTEST_LINK_FLAGS --driver-mode=g++)
47-
if(NOT WIN32)
48-
list(APPEND GWP_ASAN_UNITTEST_LINK_FLAGS -pthread)
49-
endif()
33+
add_library(gwp_asan_gtest STATIC
34+
"${GWP_ASAN_GTEST_ROOT}/src/gtest-all.cc"
35+
)
36+
target_include_directories(gwp_asan_gtest PUBLIC
37+
"${GWP_ASAN_GTEST_ROOT}/include"
38+
"${GWP_ASAN_GTEST_ROOT}"
39+
)
40+
set(GWP_ASAN_GTEST_TARGET gwp_asan_gtest)
41+
else ()
42+
find_package(GTest REQUIRED)
43+
if (TARGET GTest::gtest)
44+
set(GWP_ASAN_GTEST_TARGET GTest::gtest)
45+
elseif (TARGET GTest::GTest)
46+
set(GWP_ASAN_GTEST_TARGET GTest::GTest)
47+
else ()
48+
message(FATAL_ERROR "Could not find a usable GTest target")
49+
endif ()
50+
endif ()
5051

51-
if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST GWP_ASAN_SUPPORTED_ARCH)
52-
# GWP-ASan unit tests are only run on the host machine.
53-
set(arch ${COMPILER_RT_DEFAULT_TARGET_ARCH})
52+
add_executable(gwp_asan_tests
53+
${GWP_ASAN_UNITTESTS}
54+
)
5455

55-
set(GWP_ASAN_TEST_RUNTIME RTGwpAsanTest.${arch})
56+
target_link_libraries(gwp_asan_tests PRIVATE
57+
RTGwpAsan
58+
RTGwpAsanOptionsParser
59+
RTGwpAsanBacktraceLibc
60+
RTGwpAsanSegvHandler
61+
${GWP_ASAN_GTEST_TARGET}
62+
)
5663

57-
set(GWP_ASAN_TEST_RUNTIME_OBJECTS
58-
$<TARGET_OBJECTS:RTGwpAsan.${arch}>
59-
$<TARGET_OBJECTS:RTGwpAsanBacktraceSanitizerCommon.${arch}>
60-
$<TARGET_OBJECTS:RTGwpAsanSegvHandler.${arch}>
61-
$<TARGET_OBJECTS:RTGwpAsanOptionsParser.${arch}>
62-
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
63-
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
64-
$<TARGET_OBJECTS:RTSanitizerCommonSymbolizer.${arch}>
65-
$<TARGET_OBJECTS:RTSanitizerCommonSymbolizerInternal.${arch}>)
64+
target_compile_features(gwp_asan_tests PRIVATE cxx_std_17)
65+
target_compile_options(gwp_asan_tests PRIVATE
66+
-O0
67+
-g
68+
-fno-omit-frame-pointer
69+
-fno-optimize-sibling-calls
70+
)
6671

67-
add_library(${GWP_ASAN_TEST_RUNTIME} STATIC
68-
${GWP_ASAN_TEST_RUNTIME_OBJECTS})
72+
if (NOT WIN32)
73+
target_link_libraries(gwp_asan_tests PRIVATE dl pthread)
74+
endif ()
6975

70-
set_target_properties(${GWP_ASAN_TEST_RUNTIME} PROPERTIES
71-
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
72-
FOLDER "Compiler-RT/Tests/Runtime")
76+
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
77+
target_link_options(gwp_asan_tests PRIVATE -rdynamic)
78+
endif ()
7379

74-
set(GwpAsanTestObjects)
75-
generate_compiler_rt_tests(GwpAsanTestObjects
76-
GwpAsanUnitTests "GwpAsan-${arch}-Test" ${arch}
77-
SOURCES ${GWP_ASAN_UNITTESTS} ${COMPILER_RT_GTEST_SOURCE}
78-
RUNTIME ${GWP_ASAN_TEST_RUNTIME}
79-
DEPS ${GWP_ASAN_UNIT_TEST_HEADERS}
80-
CFLAGS ${GWP_ASAN_UNITTEST_CFLAGS}
81-
LINK_FLAGS ${GWP_ASAN_UNITTEST_LINK_FLAGS})
82-
set_target_properties(GwpAsanUnitTests PROPERTIES
83-
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
84-
endif()
80+
include(GoogleTest)
81+
gtest_discover_tests(gwp_asan_tests)

gwp_asan/tests/harness.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,33 @@
1010

1111
#include <string>
1212

13-
// Optnone to ensure that the calls to these functions are not optimized away,
14-
// as we're looking for them in the backtraces.
15-
__attribute__((optnone)) char *
13+
#if defined(__clang__)
14+
#define GWP_ASAN_TEST_NOINLINE __attribute__((noinline, optnone))
15+
#elif defined(__GNUC__)
16+
#define GWP_ASAN_TEST_NOINLINE __attribute__((noinline))
17+
#else
18+
#define GWP_ASAN_TEST_NOINLINE
19+
#endif
20+
21+
// Keep these calls visible in backtraces.
22+
GWP_ASAN_TEST_NOINLINE char *
1623
AllocateMemory(gwp_asan::GuardedPoolAllocator &GPA) {
1724
return static_cast<char *>(GPA.allocate(1));
1825
}
19-
__attribute__((optnone)) void
26+
GWP_ASAN_TEST_NOINLINE void
2027
DeallocateMemory(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr) {
2128
GPA.deallocate(Ptr);
2229
}
23-
__attribute__((optnone)) void
30+
GWP_ASAN_TEST_NOINLINE void
2431
DeallocateMemory2(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr) {
2532
GPA.deallocate(Ptr);
2633
}
27-
__attribute__((optnone)) void TouchMemory(void *Ptr) {
34+
GWP_ASAN_TEST_NOINLINE void TouchMemory(void *Ptr) {
2835
*(reinterpret_cast<volatile char *>(Ptr)) = 7;
2936
}
3037

38+
#undef GWP_ASAN_TEST_NOINLINE
39+
3140
void CheckOnlyOneGwpAsanCrash(const std::string &OutputBuffer) {
3241
const char *kGwpAsanErrorString = "GWP-ASan detected a memory error";
3342
size_t FirstIndex = OutputBuffer.find(kGwpAsanErrorString);
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//===-- printf_standalone.cpp ----------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "gwp_asan/optional/printf.h"
10+
11+
#include <errno.h>
12+
#include <stdarg.h>
13+
#include <stddef.h>
14+
#include <unistd.h>
15+
16+
#define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1
17+
#define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1
18+
#define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 0
19+
#define NANOPRINTF_USE_FLOAT_HEX_FORMAT_SPECIFIER 0
20+
#define NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS 0
21+
#define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 1
22+
#define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0
23+
#define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0
24+
#define NANOPRINTF_USE_ALT_FORM_FLAG 0
25+
#define NANOPRINTF_VISIBILITY_STATIC
26+
#define NANOPRINTF_IMPLEMENTATION
27+
#include "third_party/nanoprintf/nanoprintf.h"
28+
29+
namespace gwp_asan {
30+
namespace test {
31+
namespace {
32+
33+
constexpr size_t kWriteBufferSize = 256;
34+
35+
struct BufferedWriter {
36+
char Buffer[kWriteBufferSize];
37+
size_t Length = 0;
38+
};
39+
40+
void writeAll(const char *Data, size_t Length) {
41+
while (Length > 0) {
42+
ssize_t Written = write(STDERR_FILENO, Data, Length);
43+
if (Written > 0) {
44+
Data += Written;
45+
Length -= static_cast<size_t>(Written);
46+
continue;
47+
}
48+
49+
if (Written < 0 && errno == EINTR)
50+
continue;
51+
52+
return;
53+
}
54+
}
55+
56+
void flushBufferedWriter(BufferedWriter *Writer) {
57+
writeAll(Writer->Buffer, Writer->Length);
58+
Writer->Length = 0;
59+
}
60+
61+
void bufferedPutc(int C, void *Ctx) {
62+
auto *Writer = static_cast<BufferedWriter *>(Ctx);
63+
Writer->Buffer[Writer->Length++] = static_cast<char>(C);
64+
if (Writer->Length == sizeof(Writer->Buffer))
65+
flushBufferedWriter(Writer);
66+
}
67+
68+
void TestPrintf(const char *Format, ...) {
69+
BufferedWriter Writer = {};
70+
va_list AP;
71+
va_start(AP, Format);
72+
npf_vpprintf(bufferedPutc, &Writer, Format, AP);
73+
va_end(AP);
74+
75+
flushBufferedWriter(&Writer);
76+
}
77+
78+
} // anonymous namespace
79+
80+
Printf_t getPrintfFunction() { return TestPrintf; }
81+
82+
} // namespace test
83+
} // namespace gwp_asan

0 commit comments

Comments
 (0)