diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.h @@ -41,6 +41,7 @@ void StackDepotLockAll(); void StackDepotUnlockAll(); +void StackDepotPrintAll(); // Instantiating this class creates a snapshot of StackDepot which can be // efficiently queried with StackDepotGet(). You can use it concurrently with diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepot.cpp @@ -115,6 +115,12 @@ theDepot.UnlockAll(); } +void StackDepotPrintAll() { +#if !SANITIZER_GO + theDepot.PrintAll(); +#endif +} + bool StackDepotReverseMap::IdDescPair::IdComparator( const StackDepotReverseMap::IdDescPair &a, const StackDepotReverseMap::IdDescPair &b) { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepotbase.h b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepotbase.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_stackdepotbase.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stackdepotbase.h @@ -13,9 +13,11 @@ #ifndef SANITIZER_STACKDEPOTBASE_H #define SANITIZER_STACKDEPOTBASE_H +#include + +#include "sanitizer_atomic.h" #include "sanitizer_internal_defs.h" #include "sanitizer_mutex.h" -#include "sanitizer_atomic.h" #include "sanitizer_persistent_allocator.h" namespace __sanitizer { @@ -34,6 +36,7 @@ void LockAll(); void UnlockAll(); + void PrintAll(); private: static Node *find(Node *s, args_type args, u32 hash); @@ -172,6 +175,21 @@ } } +template +void StackDepotBase::PrintAll() { + for (int i = 0; i < kTabSize; ++i) { + atomic_uintptr_t *p = &tab[i]; + lock(p); + uptr v = atomic_load(p, memory_order_relaxed); + Node *s = (Node *)(v & ~1UL); + for (; s; s = s->link) { + Printf("Stack for id %u:\n", s->id); + s->load().Print(); + } + unlock(p, s); + } +} + } // namespace __sanitizer #endif // SANITIZER_STACKDEPOTBASE_H diff --git a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt --- a/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt +++ b/compiler-rt/lib/sanitizer_common/tests/CMakeLists.txt @@ -53,6 +53,7 @@ set(SANITIZER_TEST_CFLAGS_COMMON ${COMPILER_RT_UNITTEST_CFLAGS} ${COMPILER_RT_GTEST_CFLAGS} + ${COMPILER_RT_GMOCK_CFLAGS} -I${COMPILER_RT_SOURCE_DIR}/include -I${COMPILER_RT_SOURCE_DIR}/lib -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common @@ -151,7 +152,7 @@ generate_compiler_rt_tests(SANITIZER_TEST_OBJECTS SanitizerUnitTests "Sanitizer-${arch}-Test" ${arch} RUNTIME "${SANITIZER_COMMON_LIB}" - SOURCES ${SANITIZER_UNITTESTS} ${COMPILER_RT_GTEST_SOURCE} + SOURCES ${SANITIZER_UNITTESTS} ${COMPILER_RT_GTEST_SOURCE} ${COMPILER_RT_GMOCK_SOURCE} COMPILE_DEPS ${SANITIZER_TEST_HEADERS} DEPS gtest CFLAGS ${SANITIZER_TEST_CFLAGS_COMMON} ${extra_flags} @@ -207,6 +208,7 @@ add_executable(SanitizerTest ${SANITIZER_UNITTESTS} ${COMPILER_RT_GTEST_SOURCE} + ${COMPILER_RT_GMOCK_SOURCE} $ $ $) diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cpp b/compiler-rt/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cpp --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cpp +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cpp @@ -10,12 +10,62 @@ // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_stackdepot.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_file.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_libc.h" -#include "gtest/gtest.h" + +#if SANITIZER_WINDOWS +#define NOMINMAX +#include +#undef NOMINMAX +#endif +#if SANITIZER_POSIX +#include "sanitizer_common/sanitizer_posix.h" +#endif namespace __sanitizer { +static void temp_file_name(char *buf, size_t bufsize, const char *prefix) { +#if SANITIZER_WINDOWS + buf[0] = '\0'; + char tmp_dir[MAX_PATH]; + if (!::GetTempPathA(MAX_PATH, tmp_dir)) + return; + // GetTempFileNameA needs a MAX_PATH buffer. + char tmp_path[MAX_PATH]; + if (!::GetTempFileNameA(tmp_dir, prefix, 0, tmp_path)) + return; + internal_strncpy(buf, tmp_path, bufsize); +#else + const char *tmpdir = "/tmp"; +#if SANITIZER_ANDROID + // I don't know a way to query temp directory location on Android without + // going through Java interfaces. The code below is not ideal, but should + // work. May require "adb root", but it is needed for almost any use of ASan + // on Android already. + tmpdir = GetEnv("EXTERNAL_STORAGE"); +#endif + internal_snprintf(buf, bufsize, "%s/%sXXXXXX", tmpdir, prefix); + ASSERT_TRUE(mkstemp(buf)); +#endif +} + +static void Unlink(const char *path) { +#if SANITIZER_WINDOWS + // No sanitizer needs to delete a file on Windows yet. If we ever do, we can + // add a portable wrapper and test it from here. + ::DeleteFileA(&path[0]); +#else + internal_unlink(path); +#endif +} + TEST(SanitizerCommon, StackDepotBasic) { uptr array[] = {1, 2, 3, 4, 5}; StackTrace s1(array, ARRAY_SIZE(array)); @@ -64,6 +114,32 @@ EXPECT_NE(i1, i2); } +TEST(SanitizerCommon, StackDepotPrint) { + uptr array1[] = {1, 2, 3, 4, 7}; + StackTrace s1(array1, ARRAY_SIZE(array1)); + u32 i1 = StackDepotPut(s1); + uptr array2[] = {1, 2, 3, 4, 8, 9}; + StackTrace s2(array2, ARRAY_SIZE(array2)); + u32 i2 = StackDepotPut(s2); + EXPECT_NE(i1, i2); + char tmpfile[128]; + temp_file_name(tmpfile, sizeof(tmpfile), + "sanitizer_common.stackdepotprint.tmp."); + fd_t fd = OpenFile(tmpfile, WrOnly); + __sanitizer_set_report_fd(reinterpret_cast(fd)); + StackDepotPrintAll(); + CloseFile(fd); + std::ifstream t(tmpfile); + std::stringstream buffer; + buffer << t.rdbuf(); + EXPECT_THAT( + buffer.str(), + testing::MatchesRegex( + "Stack for id .*#0 0x0.*#1 0x1.*#2 0x2.*#3 0x3.*#4 0x6.*Stack for id " + ".*#0 0x0.*#1 0x1.*#2 0x2.*#3 0x3.*#4 0x7.*#5 0x8.*")); + Unlink(tmpfile); +} + TEST(SanitizerCommon, StackDepotReverseMap) { uptr array1[] = {1, 2, 3, 4, 5}; uptr array2[] = {7, 1, 3, 0};