update heap-checker_unittest to reach into malloc guts via TestingPortal

This commit is contained in:
Aliaksey Kandratsenka 2024-03-20 19:34:13 -04:00
parent d5bd0087c1
commit 02f10c15a5
2 changed files with 55 additions and 79 deletions

View File

@ -749,7 +749,7 @@ heap-checker-death_unittest.sh$(EXEEXT): $(heap_checker_death_unittest_sh_SOURCE
TESTS += heap-checker_unittest
heap_checker_unittest_SOURCES = src/tests/heap-checker_unittest.cc
heap_checker_unittest_LDFLAGS = $(TCMALLOC_FLAGS) $(AM_LDFLAGS)
heap_checker_unittest_LDADD = libtcmalloc.la
heap_checker_unittest_LDADD = libtcmalloc.la libcommon.la
endif WITH_HEAP_CHECKER
@ -815,7 +815,7 @@ TESTS += heap-checker_debug_unittest
heap_checker_debug_unittest_SOURCES = $(heap_checker_unittest_SOURCES)
heap_checker_debug_unittest_CXXFLAGS = $(heap_checker_unittest_CXXFLAGS)
heap_checker_debug_unittest_LDFLAGS = $(heap_checker_unittest_LDFLAGS)
heap_checker_debug_unittest_LDADD = libtcmalloc_debug.la
heap_checker_debug_unittest_LDADD = libtcmalloc_debug.la libcommon.la
endif WITH_HEAP_CHECKER
endif WITH_DEBUGALLOC

View File

@ -59,35 +59,26 @@
// (see the comment in our .h file).
#include "config_for_unittests.h"
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#include <stdint.h>
#include <sys/types.h>
#include <stdlib.h>
#include <errno.h> // errno
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for sleep(), geteuid()
#endif
#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif
#include <fcntl.h> // for open(), close()
#ifdef HAVE_EXECINFO_H
#include <gperftools/heap-checker.h>
#include <errno.h>
#include <execinfo.h> // backtrace
#endif
#ifdef HAVE_GRP_H
#include <fcntl.h>
#include <grp.h> // getgrent, getgrnam
#endif
#ifdef HAVE_PWD_H
#include <poll.h>
#include <pwd.h>
#endif
#include <spawn.h> // for posix_spawn
#include <sys/wait.h> // waitpid etc
#include <spawn.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <algorithm>
#include <iostream> // for cout
#include <iomanip> // for hex
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <memory>
@ -95,23 +86,14 @@
#include <string>
#include <vector>
#include "base/commandlineflags.h"
#include "base/googleinit.h"
#include "base/logging.h"
#include "base/commandlineflags.h"
#include "base/linuxthreads.h"
#include <gperftools/heap-checker.h>
#include "memory_region_map.h"
#include <gperftools/malloc_extension.h>
#include <gperftools/stacktrace.h>
// On systems (like freebsd) that don't define MAP_ANONYMOUS, use the old
// form of the name instead.
#ifndef MAP_ANONYMOUS
# define MAP_ANONYMOUS MAP_ANON
#endif
#include "base/commandlineflags.h"
#include "base/logging.h"
#include "memory_region_map.h"
using namespace std;
#include "testing_portal.h"
// ========================================================================= //
@ -155,17 +137,18 @@ DEFINE_bool(no_threads,
// This is used so we can make can_create_leaks_reliably true
// for any pthread implementation and test with that.
DECLARE_int64(heap_check_max_pointer_offset); // heap-checker.cc
DECLARE_string(heap_check); // in heap-checker.cc
#define WARN_IF(cond, msg) LOG_IF(WARNING, cond, msg)
// This is an evil macro! Be very careful using it...
#undef VLOG // and we start by evilling overriding logging.h VLOG
#define VLOG(lvl) if (FLAGS_verbose >= (lvl)) cout << "\n"
#define VLOG(lvl) if (FLAGS_verbose >= (lvl)) std::cout << "\n"
// This is, likewise, evil
#define LOGF VLOG(INFO)
std::string_view GetHeapCheckFlag() {
return tcmalloc::TestingPortal::Get()->GetHeapCheckFlag();
}
static void RunHeapBusyThreads(); // below
@ -315,7 +298,7 @@ static void UnHide(T** ptr) {
static void LogHidden(const char* message, const void* ptr) {
LOGF << message << " : "
<< ptr << " ^ " << reinterpret_cast<void*>(kHideMask) << endl;
<< ptr << " ^ " << reinterpret_cast<void*>(kHideMask) << std::endl;
}
// volatile to fool the compiler against inlining the calls to these
@ -717,7 +700,7 @@ static void TestHeapLeakCheckerDisabling() {
CHECK(check.SameHeap());
}
typedef set<int> IntSet;
typedef std::set<int> IntSet;
static int some_ints[] = { 1, 2, 3, 21, 22, 23, 24, 25 };
@ -783,7 +766,7 @@ static void TestSTLAllocInverse() {
template<class Alloc>
static void DirectTestSTLAlloc(Alloc allocator, const char* name) {
HeapLeakChecker check((string("direct_stl-") + name).c_str());
HeapLeakChecker check((std::string("direct_stl-") + name).c_str());
static const int kSize = 1000;
typename Alloc::value_type* ptrs[kSize];
for (int i = 0; i < kSize; ++i) {
@ -841,21 +824,15 @@ static void TestLibCAllocate() {
strerror(errno);
const time_t now = time(NULL);
ctime(&now);
#ifdef HAVE_EXECINFO_H
void *stack[1];
backtrace(stack, 1);
#endif
if (grplock.TryLock()) {
#ifdef HAVE_GRP_H
gid_t gid = getgid();
getgrgid(gid);
if (grp == NULL) grp = getgrent(); // a race condition here is okay
getgrnam(grp->gr_name);
#endif
#ifdef HAVE_PWD_H
getpwuid(geteuid());
#endif
grplock.Unlock();
}
}
@ -879,7 +856,7 @@ static void* HeapBusyThreadBody(void* a) {
register int** ptr;
#endif
ptr = NULL;
typedef set<int> Set;
typedef std::set<int> Set;
Set s1;
while (1) {
// TestLibCAllocate() calls libc functions that don't work so well
@ -891,7 +868,7 @@ static void* HeapBusyThreadBody(void* a) {
ptr = new(initialized) int*[1];
*ptr = new(initialized) int[1];
}
set<int>* s2 = new(initialized) set<int>[1];
std::set<int>* s2 = new(initialized) std::set<int>[1];
s1.insert(random());
s2->insert(*s1.begin());
user += *s2->begin();
@ -973,7 +950,7 @@ static void RunHeapBusyThreads() {
// returns a "weird" pointer to a new object for which
// it's worth checking that the object is reachable via that pointer.
typedef void* (*ObjMakerFunc)();
static list<ObjMakerFunc> obj_makers; // list of registered object makers
static std::list<ObjMakerFunc> obj_makers; // list of registered object makers
// Helper macro to register an object making function
// 'name' is an identifier of this object maker,
@ -995,7 +972,7 @@ struct ObjMakerRegistrar {
// List of the objects/pointers made with all the obj_makers
// to test reachability via global data pointers during leak checks.
static list<void*>* live_objects = new list<void*>;
static std::list<void*>* live_objects = new std::list<void*>;
// pointer so that it does not get destructed on exit
// Exerciser for one ObjMakerFunc.
@ -1012,7 +989,7 @@ static void TestPointerReach(ObjMakerFunc obj_maker) {
// Test all ObjMakerFunc registred via REGISTER_OBJ_MAKER.
static void TestObjMakers() {
for (list<ObjMakerFunc>::const_iterator i = obj_makers.begin();
for (std::list<ObjMakerFunc>::const_iterator i = obj_makers.begin();
i != obj_makers.end(); ++i) {
TestPointerReach(*i);
TestPointerReach(*i); // a couple more times would not hurt
@ -1086,11 +1063,12 @@ REGISTER_OBJ_MAKER(3_sized, void* p = malloc(3);)
REGISTER_OBJ_MAKER(4_sized, void* p = malloc(4);)
static int set_data[] = { 1, 2, 3, 4, 5, 6, 7, 21, 22, 23, 24, 25, 26, 27 };
static set<int> live_leak_set(set_data, set_data+7);
static const set<int> live_leak_const_set(set_data, set_data+14);
static std::set<int> live_leak_set(set_data, set_data+7);
static const std::set<int> live_leak_const_set(set_data, set_data+14);
REGISTER_OBJ_MAKER(set,
set<int>* p = new(initialized) set<int>(set_data, set_data + 13);
REGISTER_OBJ_MAKER(
set,
std::set<int>* p = new(initialized) std::set<int>(set_data, set_data + 13);
)
class ClassA {
@ -1309,16 +1287,14 @@ static void VerifyMemoryRegionMapStackGet() {
uintptr_t caller_addr_limit;
void* addr = (*mmapper_addr)(&caller_addr_limit);
uintptr_t caller = 0;
{ MemoryRegionMap::LockHolder l;
for (MemoryRegionMap::RegionIterator
i = MemoryRegionMap::BeginRegionLocked();
i != MemoryRegionMap::EndRegionLocked(); ++i) {
if (i->start_addr == reinterpret_cast<uintptr_t>(addr)) {
tcmalloc::TestingPortal::Get()->IterateMemoryRegionMap(
[&] (const void* _region) {
const MemoryRegionMap::Region* region = static_cast<const MemoryRegionMap::Region*>(_region);
if (region->start_addr == reinterpret_cast<uintptr_t>(addr)) {
CHECK_EQ(caller, 0);
caller = i->caller();
caller = region->caller();
}
}
}
});
// caller must point into Mmapper function:
if (!(GetFunctionAddress(mmapper_addr) <= caller &&
caller < caller_addr_limit)) {
@ -1423,7 +1399,7 @@ int main(int argc, char** argv) {
run_hidden_ptr = DoRunHidden;
wipe_stack_ptr = DoWipeStack;
if (!HeapLeakChecker::IsActive()) {
CHECK_EQ(FLAGS_heap_check, "");
CHECK_EQ(GetHeapCheckFlag(), "");
LOG(WARNING, "HeapLeakChecker got turned off; we won't test much...");
} else {
VerifyMemoryRegionMapStackGet();
@ -1449,7 +1425,7 @@ int main(int argc, char** argv) {
}
TestLibCAllocate();
LOGF << "In main(): heap_check=" << FLAGS_heap_check << endl;
LOGF << "In main(): heap_check=" << GetHeapCheckFlag() << std::endl;
CHECK(HeapLeakChecker::NoGlobalLeaks()); // so far, so good
@ -1550,14 +1526,14 @@ int main(int argc, char** argv) {
DTSL(std::allocator<char>());
DTSL(std::allocator<int>());
DTSL(std::string().get_allocator());
DTSL(string().get_allocator());
DTSL(vector<int>().get_allocator());
DTSL(vector<double>().get_allocator());
DTSL(vector<vector<int> >().get_allocator());
DTSL(vector<string>().get_allocator());
DTSL((map<string, string>().get_allocator()));
DTSL((map<string, int>().get_allocator()));
DTSL(set<char>().get_allocator());
DTSL(std::string().get_allocator());
DTSL(std::vector<int>().get_allocator());
DTSL(std::vector<double>().get_allocator());
DTSL(std::vector<std::vector<int> >().get_allocator());
DTSL(std::vector<std::string>().get_allocator());
DTSL((std::map<std::string, std::string>().get_allocator()));
DTSL((std::map<std::string, int>().get_allocator()));
DTSL(std::set<char>().get_allocator());
#undef DTSL
TestLibCAllocate();