gperftools/CMakeLists.txt
Aliaksey Kandratsenka e78238d94d reworked heap leak checker for more portability
In most practical terms, this expands "official" heap leak checker
support to Linux/arm64 and Linux/riscv (mips-en and legacy arm are
likely to work & pass tests too now).

The code is now explicitly Linux-only, without trying to pretend
otherwise. Main goal of this change is to finally amputate
linux_syscall_support.h, which we historically had trouble maintaining
well. Biggest challenge was around thread listing facility which uses
clone (ptrace explicitly fails between threads) and that causes
difficulties around parent and child tasks sharing
errno. linux_syscall_support stuff had special feature to "redirect"
errno accesses. But it caused us for more trouble. We switched to
regular syscalls, and errno stamping avoidance is now simply via
careful programming.

A number of other cleanups is made (such us thread finding codes in
procfs which clearly was built for some ages old and odd kernels).

sem_post/sem_wait synchronization was previously potentially prone to
deadlock (if parent died at bad time). We now use pipe pair for this
synchronization and it is fully robust.
2023-07-02 22:30:00 -04:00

1466 lines
59 KiB
CMake

cmake_minimum_required(VERSION 3.12)
# Please note that cmake support is very preliminary. Autotools-based
# build is the only fully supported build for now.
# Based on configure.ac
project(gperftools VERSION 2.10.0 LANGUAGES C CXX
DESCRIPTION "Performance tools for C++"
HOMEPAGE_URL http://code.google.com/p/gperftools/)
# Update this value for every release!
set(TCMALLOC_SO_VERSION 9.10.5)
set(PROFILER_SO_VERSION 5.5.5)
set(TCMALLOC_AND_PROFILER_SO_VERSION 10.5.6)
# The user can choose not to compile in the heap-profiler, the
# heap-checker, or the cpu-profiler. There's also the possibility
# for a 'fully minimal' compile, which leaves out the stacktrace
# code as well. By default, we include all of these that the
# target system supports.
set(DEFAULT_BUILD_CPU_PROFILER ON)
set(DEFAULT_BUILD_HEAP_PROFILER ON)
set(DEFAULT_BUILD_HEAP_CHECKER OFF)
set(DEFAULT_BUILD_DEBUGALLOC ON)
set(DEFAULT_BUILD_MINIMAL OFF)
set(DEFAULT_TCMALLOC_ALIGNMENT 16)
set(NEED_NANOSLEEP ON) # Used later, to decide if to run ACX_NANOSLEEP
set(HOST string(TOLOWER "${CMAKE_SYSTEM_NAME}"))
if(MINGW OR MSVC)
set(DEFAULT_BUILD_MINIMAL ON)
set(DEFAULT_BUILD_DEBUGALLOC OFF)
set(NEED_NANOSLEEP OFF)
elseif(CYGWIN)
set(DEFAULT_BUILD_CPU_PROFILER OFF)
endif()
# Heap checker is Linux-only (and deprecated).
if(CMAKE_SYSTEM MATCHES Linux)
set(DEFAULT_BUILD_HEAP_CHECKER ON)
endif()
include(CheckCCompilerFlag)
include(CheckCSourceCompiles)
include(CheckCXXSourceCompiles)
include(CheckFunctionExists)
include(CheckIncludeFile)
include(CheckLibraryExists)
include(CheckSymbolExists)
include(CheckTypeSize)
include(CheckVariableExists)
include(CMakeDependentOption)
include(CTest)
include(CPack)
include(GNUInstallDirs)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
include(DefineTargetVariables)
include(FindObjcopyWithWeaken)
include(PCFromUContext)
define_target_variables()
# Currently only backtrace works on s390.
if(s390 OR OSX)
set(default_enable_libunwind OFF)
set(default_enable_backtrace ON)
else()
set(default_enable_libunwind ON)
set(default_enable_backtrace OFF)
endif()
# Disable libunwind linking on ppc64 by default.
if(PPC64)
set(default_enable_libunwind OFF)
set(default_tcmalloc_pagesize 64)
else()
set(default_enable_libunwind ON)
set(default_tcmalloc_pagesize 8)
endif()
cmake_dependent_option(
GPERFTOOLS_BUILD_CPU_PROFILER "Build cpu-profiler" ${DEFAULT_BUILD_CPU_PROFILER}
"NOT gperftools_build_minimal" OFF)
cmake_dependent_option(
GPERFTOOLS_BUILD_HEAP_PROFILER "Build heap-profiler" ${DEFAULT_BUILD_HEAP_PROFILER}
"NOT gperftools_build_minimal" OFF)
cmake_dependent_option(
GPERFTOOLS_BUILD_HEAP_CHECKER "Build heap-checker" ${DEFAULT_BUILD_HEAP_CHECKER}
"NOT gperftools_build_minimal" OFF)
cmake_dependent_option(
GPERFTOOLS_BUILD_DEBUGALLOC "Build debugalloc" ${DEFAULT_BUILD_DEBUGALLOC}
"NOT gperftools_build_minimal" OFF)
option(
gperftools_build_minimal
"Build only tcmalloc-minimal (and maybe tcmalloc-minimal-debug)"
${DEFAULT_BUILD_MINIMAL})
if(gperftools_build_minimal)
set(GPERFTOOLS_BUILD_CPU_PROFILER OFF)
set(GPERFTOOLS_BUILD_HEAP_PROFILER OFF)
set(GPERFTOOLS_BUILD_HEAP_CHECKER OFF)
endif()
cmake_dependent_option(
gperftools_build_benchmark "Build benchmark" ON "NOT MINGW AND NOT MSVC" OFF)
option(gperftools_enable_stacktrace_via_backtrace
"Enable use of backtrace() for stacktrace capturing (may deadlock)"
${default_enable_backtrace})
option(gperftools_enable_libunwind
"Enable libunwind linking"
${default_enable_libunwind})
set(enable_backtrace ${gperftools_enable_stacktrace_via_backtrace})
set(enable_libunwind ${gperftools_enable_libunwind})
set(gperftools_tcmalloc_pagesize ${default_tcmalloc_pagesize}
CACHE STRING "Set the tcmalloc internal page size")
set_property(CACHE gperftools_tcmalloc_pagesize PROPERTY STRINGS "8" "32" "64")
if(NOT gperftools_tcmalloc_pagesize STREQUAL "8" AND
NOT gperftools_tcmalloc_pagesize STREQUAL "32" AND
NOT gperftools_tcmalloc_pagesize STREQUAL "64")
message(WARNING
"Invalid gperftools_tcmalloc_pagesize (${gperftools_tcmalloc_pagesize}), "
"setting to default value (${default_tcmalloc_pagesize})")
set(gperftools_tcmalloc_pagesize ${default_tcmalloc_pagesize})
endif()
if (gperftools_tcmalloc_pagesize STREQUAL "32" OR
gperftools_tcmalloc_pagesize STREQUAL "64")
set(TCMALLOC_${gperftools_tcmalloc_pagesize}K_PAGES ON)
endif()
set(gperftools_tcmalloc_alignment ${DEFAULT_TCMALLOC_ALIGNMENT}
CACHE STRING "Set the tcmalloc allocation alignment")
set_property(CACHE gperftools_tcmalloc_alignment PROPERTY STRINGS "8" "16")
if(NOT gperftools_tcmalloc_alignment STREQUAL "8" AND
NOT gperftools_tcmalloc_alignment STREQUAL "16")
message(WARNING
"Invalid gperftools_tcmalloc_alignment (${gperftools_tcmalloc_alignment}), "
"setting to default value (${DEFAULT_TCMALLOC_ALIGNMENT})")
set(gperftools_tcmalloc_alignment ${DEFAULT_TCMALLOC_ALIGNMENT})
endif()
if(gperftools_tcmalloc_alignment STREQUAL "8")
set(TCMALLOC_ALIGN_8BYTES ON)
endif()
# AX_CXX_COMPILE_STDCXX(11, ext, mandatory)
if(cxx_std_17 IN_LIST CMAKE_CXX_COMPILE_FEATURES)
set(CMAKE_CXX_STANDARD 17) # std::align_val_t
else()
set(CMAKE_CXX_STANDARD 11)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)
# Check if we have an objcopy installed that supports -W
find_objcopy_with_weaken()
# AX_C___ATTRIBUTE__
check_c_source_compiles("#include <stdlib.h>
static void foo(void) __attribute__ ((unused));
void foo(void) { exit(1); }
int main() { return 0; }"
HAVE___ATTRIBUTE__)
set(CMAKE_EXTRA_INCLUDE_FILES "malloc.h")
check_type_size("struct mallinfo" STRUCT_MALLINFO LANGUAGE CXX)
set(CMAKE_EXTRA_INCLUDE_FILES "elf.h")
check_type_size("Elf32_Versym" ELF32_VERSYM LANGUAGE CXX) # for vdso_support.h
set(CMAKE_EXTRA_INCLUDE_FILES)
check_function_exists("sbrk" HAVE_SBRK) # for tcmalloc to get memory
check_function_exists("__sbrk" HAVE_SBRK) # for tcmalloc to get memory
check_function_exists("geteuid" HAVE_GETEUID) # for turning off services when run as root
check_function_exists("fork" HAVE_FORK) # for the pthread_atfork setup
check_include_file("features.h" HAVE_FEATURES_H) # for vdso_support.h, Where __GLIBC__ is defined
check_include_file("malloc.h" HAVE_MALLOC_H) # some systems define stuff there, others not
check_include_file("glob.h" HAVE_GLOB_H) # for heap-profile-table (cleaning up profiles)
check_include_file("execinfo.h" HAVE_EXECINFO_H) # for stacktrace? and heapchecker_unittest
check_include_file("unwind.h" HAVE_UNWIND_H) # for stacktrace
check_include_file("sched.h" HAVE_SCHED_H) # for being nice in our spinlock code
check_include_file("sys/syscall.h" HAVE_SYS_SYSCALL_H)
check_include_file("sys/socket.h" HAVE_SYS_SOCKET_H) # optional; for forking out to symbolizer
check_include_file("sys/wait.h" HAVE_SYS_WAIT_H) # optional; for forking out to symbolizer
check_include_file("poll.h" HAVE_POLL_H) # optional; for forking out to symbolizer
check_include_file("fcntl.h" HAVE_FCNTL_H) # for tcmalloc_unittest
check_include_file("grp.h" HAVE_GRP_H) # for heapchecker_unittest
check_include_file("pwd.h" HAVE_PWD_H) # for heapchecker_unittest
check_include_file("sys/resource.h" HAVE_SYS_RESOURCE_H) # for memalign_unittest.cc
check_include_file("sys/cdefs.h" HAVE_SYS_CDEFS_H) # Where glibc defines __THROW
check_include_file("unistd.h" HAVE_UNISTD_H)
check_include_file("inttypes.h" HAVE_INTTYPES_H)
# We also need <ucontext.h>/<sys/ucontext.h>, but we get those from
# AC_PC_FROM_UCONTEXT, below.
# We override a lot of memory allocation routines, not all of which are
# standard. For those the system doesn't declare, we'll declare ourselves.
set(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=600)
check_symbol_exists("cfree" "stdlib.h;malloc.h" HAVE_DECL_CFREE)
check_symbol_exists("posix_memalign" "stdlib.h;malloc.h" HAVE_DECL_POSIX_MEMALIGN)
check_symbol_exists("memalign" "stdlib.h;malloc.h" HAVE_DECL_MEMALIGN)
check_symbol_exists("valloc" "stdlib.h;malloc.h" HAVE_DECL_VALLOC)
check_symbol_exists("pvalloc" "stdlib.h;malloc.h" HAVE_DECL_PVALLOC)
set(CMAKE_REQUIRED_DEFINITIONS)
if(HAVE_STRUCT_MALLINFO)
set(HAVE_STRUCT_MALLINFO 1)
else()
set(HAVE_STRUCT_MALLINFO 0)
endif()
# We hardcode HAVE_MMAP to 1. There are no interesting systems anymore
# without functional mmap. And our windows (except mingw) builds
# aren't using autoconf. So we keep HAVE_MMAP define, but only to
# distingush windows and rest.
if(NOT WIN32)
set(HAVE_MMAP 1)
endif()
# We want to access the "PC" (Program Counter) register from a struct
# ucontext. Every system has its own way of doing that. We try all the
# possibilities we know about. Note REG_PC should come first (REG_RIP
# is also defined on solaris, but does the wrong thing). But don't
# bother if we're not doing cpu-profiling.
# [*] means that we've not actually tested one of these systems
if (GPERFTOOLS_BUILD_CPU_PROFILER)
pc_from_ucontext(PC_FROM_UCONTEXT_DEF)
endif ()
# Some tests test the behavior of .so files, and only make sense for dynamic.
option(GPERFTOOLS_BUILD_STATIC "Enable Static" ON)
if(gperftools_enable_libunwind)
check_include_file("libunwind.h" HAVE_LIBUNWIND_H)
if(HAVE_LIBUNWIND_H)
find_library(libunwind_location NAMES unwind)
if(libunwind_location)
check_library_exists(
unwind backtrace ${libunwind_location} have_libunwind)
endif()
if(have_libunwind)
set(unwind_libs ${libunwind_location})
set(will_use_libunwind ON)
set(USE_LIBUNWIND 1)
endif()
endif()
endif()
# On x86_64, we know that default is to omit frame pointer.
if(x86_64)
set(omit_fp_by_default ON)
endif()
# See if the compiler supports -Wno-unused-result.
# Newer ubuntu's turn on -D_FORTIFY_SOURCE=2, enabling
# __attribute__((warn_unused_result)) for things like write(),
# which we don't care about.
check_c_compiler_flag("-Wno-unused-result" have_w_no_unused_result)
option(gperftools_dynamic_sized_delete_support
"Try to build run-time switch for sized delete operator"
OFF)
if(gperftools_dynamic_sized_delete_support)
set(ENABLE_DYNAMIC_SIZED_DELETE 1)
endif()
option(gperftools_sized_delete "Build sized delete operator" OFF)
if(gperftools_sized_delete)
set(ENABLE_SIZED_DELETE 1)
endif()
if(NOT MSVC)
set(CMAKE_REQUIRED_FLAGS -fsized-deallocation)
check_cxx_source_compiles("
#include <new>
int main() { (::operator delete)(0, 256); return 0; }"
have_sized_deallocation)
set(CMAKE_REQUIRED_FLAGS)
endif()
check_cxx_source_compiles("
#include <new>
int main() { (::operator delete)((::operator new)(256, std::align_val_t(16)), std::align_val_t(16)); return 0; }"
HAVE_STD_ALIGN_VAL_T)
if(HAVE_STD_ALIGN_VAL_T)
set(HAVE_STD_ALIGN_VAL_T 1)
else()
set(HAVE_STD_ALIGN_VAL_T 0)
endif()
check_c_source_compiles("
#include <unwind.h>
int main()
{
#if __APPLE__
#error OSX _Unwind_Backtrace recurses back to malloc
#endif
&_Unwind_Backtrace;
return 0;
}"
HAVE_UNWIND_BACKTRACE)
if(enable_backtrace)
set(default_emergency_malloc ON)
else()
set(default_emergency_malloc OFF)
endif()
if(will_use_libunwind AND ARM)
set(default_emergency_malloc ON)
endif()
option(gperftools_emergency_malloc
"Build emergency malloc"
${default_emergency_malloc})
check_c_source_compiles(
"int main() { return __builtin_expect(main != 0, 1); }"
HAVE_BUILTIN_EXPECT)
check_c_source_compiles("
#include <unistd.h>
int main()
{
char** env = __environ;
return 0;
}"
HAVE___ENVIRON)
# If we support __thread, that can speed up tcmalloc a bit.
# Note, however, that our code tickles a bug in gcc < 4.1.2
# involving TLS and -fPIC (which our libraries will use) on x86:
# http://gcc.gnu.org/ml/gcc-bugs/2006-09/msg02275.html
#
# And mingw also does compile __thread but resultant code actually
# fails to work correctly at least in some not so ancient version:
# http://mingw-users.1079350.n2.nabble.com/gcc-4-4-multi-threaded-exception-handling-amp-thread-specifier-not-working-td3440749.html
#
# Also it was reported that earlier gcc versions for mips compile
# __thread but it doesn't really work
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.1.2")
message(WARNING "gcc has this bug: http://gcc.gnu.org/ml/gcc-bugs/2006-09/msg02275.html")
elseif(APPLE)
message(WARNING "OSX __thread support is known to call malloc which makes "
"it unsafe to use from malloc replacement")
elseif(MINGW)
message(WARNING "mingw doesn't really support tls")
else()
check_c_source_compiles("static __thread int p = 0; int main() {}" HAVE_TLS)
endif()
if(NEED_NANOSLEEP)
check_c_source_compiles(
"#include <time.h>
int main()
{ static struct timespec ts; nanosleep(&ts, NULL); return 0; }"
nanosleep_ok)
if(NOT nanosleep_ok)
set(CMAKE_REQUIRED_LIBRARIES rt)
check_c_source_compiles(
"#include <time.h>
int main()
{ static struct timespec ts; nanosleep(&ts, NULL); return 0; }"
nanosleep_ok)
if(nanosleep_ok)
set(nanosleep_libs rt)
else()
message(FATAL_ERROR "cannot find the nanosleep function")
endif()
set(CMAKE_REQUIRED_LIBRARIES)
endif()
endif()
# Nanosleep requires extra libraries on some architectures (solaris).
# This sets NANOSLEEP_LIBS. nanosleep doesn't exist on mingw, which
# is fine for us because we don't compile libspinlock, which uses it.
if(enable_backtrace)
check_symbol_exists("backtrace" "execinfo.h" HAVE_DECL_BACKTRACE)
check_function_exists("backtrace" backtrace_exists)
if(NOT backtrace_exists)
set(CMAKE_REQUIRED_LIBRARIES execinfo)
check_function_exists("backtrace" backtrace_exists)
set(CMAKE_REQUIRED_LIBRARIES)
if(backtrace_exists)
list(INSERT unwind_libs 0 execinfo)
endif()
endif()
endif()
find_package(Threads REQUIRED)
set(HAVE_PTHREAD ${CMAKE_USE_PTHREADS_INIT})
if(FreeBSD)
set(PTHREADS_CRASHES_IF_RUN_TOO_EARLY ON)
endif()
set(libstdcxx_la_linker_flag)
if(EXISTS /usr/sfw/lib/libstdc++.la)
file(READ /usr/sfw/lib/libstdc++.la _ch LIMIT 1)
if(string(LENGTH _ch) EQUAL 0)
set(libstdcxx_la_linker_flag "-L${CMAKE_CURRENT_SOURCE_DIR}/src/solaris")
endif()
endif()
check_cxx_source_compiles(
"#include <string>
#include <vector>
int main() { pthread_t th; pthread_join(th, 0); return 0; }"
have_pthread_despite_asking_for)
check_variable_exists("program_invocation_name" HAVE_PROGRAM_INVOCATION_NAME)
if(MINGW)
check_symbol_exists("sleep" "unistd.h" HAVE_DECL_SLEEP)
check_symbol_exists("nanosleep" "time.h" HAVE_DECL_NANOSLEEP)
endif()
if(LINUX)
check_c_source_compiles("
#include <signal.h>
#include <time.h>
int main() { return SIGEV_THREAD_ID || CLOCK_THREAD_CPUTIME_ID; }"
HAVE_LINUX_SIGEV_THREAD_ID)
endif()
configure_file(cmake/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY)
configure_file(cmake/tcmalloc.h.in
${CMAKE_CURRENT_BINARY_DIR}/gperftools/tcmalloc.h
@ONLY)
if(GPERFTOOLS_BUILD_CPU_PROFILER OR
GPERFTOOLS_BUILD_HEAP_PROFILER OR
GPERFTOOLS_BUILD_HEAP_CHECKER)
set(WITH_STACK_TRACE ON)
endif()
# The following matters only if we're not using libunwind and if we
# care about backtrace capturing, and frame pointers are not available
# to capture backtraces. The idea is to warn user about less stable or
# known bad configurations (e.g. encourage to install libunwind).
if (NOT unwind_libs AND NOT gperftools_build_minimal AND
omit_fp_by_default AND NOT gperftools_enable_frame_pointers)
if(HAVE_UNWIND_BACKTRACE)
message(WARNING "No frame pointers and no libunwind. "
"Using experimental backtrace capturing via libgcc. "
"Expect crashy cpu profiler.")
elseif(gperftools_enable_stacktrace_via_backtrace)
message(WARNING "No frame pointers and no libunwind. "
"Using experimental backtrace(). "
"Expect crashy cpu profiler.")
else()
message(FATAL_ERROR "No frame pointers and no libunwind. "
"The compilation will fail.")
endif()
endif()
# Based on Makefile.am
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# This is so we can #include <gperftools/foo>
include_directories($<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
if(NOT WITH_STACK_TRACE)
add_compile_definitions(NO_TCMALLOC_SAMPLES)
endif()
# These are good warnings to turn on by default.
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare")
# x86-es (starting from gcc-4.6 as per earlier comment) have
# -momit-leaf-frame-pointer by default. And on i386 we "traditionally"
# used to re-enable frame pointers. So lets keep doing it.
if(i386)
add_compile_options(-fno-omit-frame-pointer)
endif()
endif()
if(have_w_no_unused_result)
add_compile_options(-Wno-unused-result)
endif()
if(have_sized_deallocation)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsized-deallocation")
endif()
if(have_f_aligned_new)
add_compile_options(-faligned-new)
endif()
# LIBSTDCXX_LA_LINKER_FLAG is used to fix a Solaris bug.
add_link_options(${libstdcxx_la_linker_flag})
option(
gperftools_enable_frame_pointers
"Compile with -fno-omit-frame-pointer (see INSTALL)"
OFF)
if(gperftools_enable_frame_pointers)
add_compile_options(-fno-omit-frame-pointer)
endif()
if(omit_fp_by_default AND NOT gperftools_enable_frame_pointers)
add_compile_definitions(NO_FRAME_POINTER)
endif()
# For windows systems (at least, mingw), we need to tell all our
# tests to link in libtcmalloc using -u. This is because libtcmalloc
# accomplishes its tasks via patching, leaving no work for the linker
# to identify, so the linker will ignore libtcmalloc by default unless
# we explicitly create a dependency via -u.
set(TCMALLOC_FLAGS)
if(MINGW)
list(APPEND TCMALLOC_FLAGS "-Wl,-u__tcmalloc")
endif()
set(googleinclude_HEADERS
src/google/heap-checker.h
src/google/heap-profiler.h
src/google/malloc_extension.h
src/google/malloc_extension_c.h
src/google/malloc_hook.h
src/google/malloc_hook_c.h
src/google/profiler.h
src/google/stacktrace.h
src/google/tcmalloc.h
)
install(FILES ${googleinclude_HEADERS}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/google
)
# This is a 'convenience library' -- it's not actually installed or anything
set(LOGGING_INCLUDES
src/base/logging.h
src/base/commandlineflags.h
src/base/basictypes.h
src/base/dynamic_annotations.h)
set(liblogging_la_SOURCES src/base/logging.cc
src/base/dynamic_annotations.c
${LOGGING_INCLUDES})
add_library(logging STATIC ${liblogging_la_SOURCES})
set(SYSINFO_INCLUDES
src/base/sysinfo.h
src/getenv_safe.h
src/base/logging.h
src/base/commandlineflags.h
src/base/basictypes.h)
set(libsysinfo_la_SOURCES src/base/sysinfo.cc
${SYSINFO_INCLUDES})
set(libsysinfo_la_LIBADD ${NANOSLEEP_LIBS})
add_library(sysinfo STATIC ${libsysinfo_la_SOURCES})
target_link_libraries(sysinfo ${libsysinfo_la_LIBADD})
# For MinGW, we use also have to use libwindows Luckily, we need the
# windows.a library in exactly the same place we need spinlock.a
# (pretty much everywhere), so we can use the same variable name for
# each. We can also optimize the MinGW rule a bit by leaving out
# files we know aren't used on windows. libwindows also obsoletes the
# need for other files like system_alloc.cc.
if(MINGW OR MSVC)
set(WINDOWS_INCLUDES
src/windows/port.h
src/windows/mingw.h
src/windows/mini_disassembler.h
src/windows/mini_disassembler_types.h
src/windows/preamble_patcher.h)
set(libwindows_la_SOURCES ${WINDOWS_INCLUDES}
src/windows/port.cc
src/windows/system-alloc.cc
src/windows/ia32_modrm_map.cc
src/windows/ia32_opcode_map.cc
src/windows/mini_disassembler.cc
src/windows/patch_functions.cc
src/windows/preamble_patcher.cc
src/windows/preamble_patcher_with_stub.cc)
add_library(windows_object OBJECT ${libwindows_la_SOURCES})
add_library(windows INTERFACE)
target_sources(windows INTERFACE $<TARGET_OBJECTS:windows_object>)
# patch_functions.cc uses Psapi.lib. MSVC has a #pragma for that, but not us.
target_link_libraries(windows INTERFACE psapi)
set(SPINLOCK_INCLUDES src/base/spinlock.h
src/base/spinlock_internal.h
src/base/spinlock_win32-inl.h
src/base/spinlock_linux-inl.h
src/base/spinlock_posix-inl.h)
set(libspinlock_la_SOURCES src/base/spinlock.cc
src/base/spinlock_internal.cc
${SPINLOCK_INCLUDES})
add_library(spinlock STATIC ${libspinlock_la_SOURCES})
set(LIBSPINLOCK windows spinlock sysinfo logging)
# We also need to tell mingw that sysinfo.cc needs shlwapi.lib.
# (We do this via a #pragma for msvc, but need to do it here for mingw).
target_link_libraries(sysinfo shlwapi)
if(have_pthread_despite_asking_for)
add_library(maybe_threads STATIC src/maybe_threads.cc)
set(maybe_threads_lib maybe_threads)
endif()
else()
set(SPINLOCK_INCLUDES src/base/spinlock.h
src/base/spinlock_internal.h)
set(libspinlock_la_SOURCES src/base/spinlock.cc
src/base/spinlock_internal.cc
${SPINLOCK_INCLUDES})
add_library(spinlock STATIC ${libspinlock_la_SOURCES})
target_link_libraries(spinlock ${nanosleep_libs})
set(LIBSPINLOCK spinlock sysinfo logging)
set(TCMALLOC_CC "src/tcmalloc.cc")
set(SYSTEM_ALLOC_CC "src/system-alloc.cc")
add_library(maybe_threads STATIC src/maybe_threads.cc)
set(maybe_threads_lib maybe_threads)
endif()
if(BUILD_TESTING)
set(LOW_LEVEL_ALLOC_UNITTEST_INCLUDES
src/base/low_level_alloc.h
src/base/basictypes.h
src/gperftools/malloc_hook.h
src/gperftools/malloc_hook_c.h
src/malloc_hook-inl.h
src/malloc_hook_mmap_linux.h
src/malloc_hook_mmap_freebsd.h
${SPINLOCK_INCLUDES}
${LOGGING_INCLUDES})
set(low_level_alloc_unittest_SOURCES src/base/low_level_alloc.cc
src/malloc_hook.cc
src/tests/low_level_alloc_unittest.cc
${LOW_LEVEL_ALLOC_UNITTEST_INCLUDES})
if(MSVC OR MINGW)
list(APPEND low_level_alloc_unittest_SOURCES src/windows/port.cc)
endif()
add_executable(low_level_alloc_unittest ${low_level_alloc_unittest_SOURCES})
# By default, MallocHook takes stack traces for use by the heap-checker.
# We don't need that functionality here, so we turn it off to reduce deps.
target_compile_definitions(low_level_alloc_unittest PRIVATE NO_TCMALLOC_SAMPLES)
target_link_libraries(low_level_alloc_unittest spinlock sysinfo logging ${maybe_threads_lib})
add_test(low_level_alloc_unittest low_level_alloc_unittest)
endif()
### ------- stack trace
if(WITH_STACK_TRACE)
set(S_STACKTRACE_INCLUDES src/stacktrace_impl_setup-inl.h
src/stacktrace_generic-inl.h
src/stacktrace_libgcc-inl.h
src/stacktrace_libunwind-inl.h
src/stacktrace_arm-inl.h
src/stacktrace_powerpc-inl.h
src/stacktrace_powerpc-darwin-inl.h
src/stacktrace_powerpc-linux-inl.h
src/stacktrace_x86-inl.h
src/stacktrace_win32-inl.h
src/stacktrace_instrument-inl.h
src/base/elf_mem_image.h
src/base/vdso_support.h)
set(SG_STACKTRACE_INCLUDES src/gperftools/stacktrace.h)
set(STACKTRACE_INCLUDES ${S_STACKTRACE_INCLUDES} ${SG_STACKTRACE_INCLUDES})
list(APPEND perftoolsinclude_HEADERS ${SG_STACKTRACE_INCLUDES})
### Making the library
set(libstacktrace_la_SOURCES src/stacktrace.cc
src/base/elf_mem_image.cc
src/base/vdso_support.cc
${STACKTRACE_INCLUDES})
add_library(stacktrace INTERFACE)
add_library(stacktrace_object OBJECT ${libstacktrace_la_SOURCES})
target_link_libraries(stacktrace INTERFACE ${unwind_libs} ${LIBSPINLOCK})
target_sources(stacktrace INTERFACE $<TARGET_OBJECTS:stacktrace_object>)
set(libfake_stacktrace_scope_la_SOURCES src/fake_stacktrace_scope.cc)
add_library(fake_stacktrace_scope ${libfake_stacktrace_scope_la_SOURCES})
if(BUILD_TESTING)
set(STACKTRACE_UNITTEST_INCLUDES src/config_for_unittests.h
src/base/commandlineflags.h
${STACKTRACE_INCLUDES}
${LOGGING_INCLUDES})
set(stacktrace_unittest_SOURCES src/tests/stacktrace_unittest.cc
${STACKTRACE_UNITTEST_INCLUDES})
add_executable(stacktrace_unittest ${stacktrace_unittest_SOURCES})
target_link_libraries(stacktrace_unittest stacktrace logging fake_stacktrace_scope)
add_test(stacktrace_unittest stacktrace_unittest)
endif()
endif()
### ------- pprof
# If we are not compiling with stacktrace support, pprof is worthless
if(WITH_STACK_TRACE)
install(FILES src/pprof DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME pprof-symbolize)
if(BUILD_TESTING)
add_test(NAME pprof_unittest
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/src/pprof" -test
VERBATIM)
list(APPEND TESTS_ENVIRONMENT "PPROF_PATH=${CMAKE_CURRENT_SOURCE_DIR}/src/pprof")
endif()
if(INSTALL_PPROF)
install(FILES src/pprof DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
endif()
### ------- tcmalloc_minimal (thread-caching malloc)
### The header files we use. We divide into categories based on directory
set(S_TCMALLOC_MINIMAL_INCLUDES src/common.h
src/internal_logging.h
src/system-alloc.h
src/packed-cache-inl.h
${SPINLOCK_INCLUDES}
src/tcmalloc_guard.h
src/base/commandlineflags.h
src/base/basictypes.h
src/pagemap.h
src/sampler.h
src/central_freelist.h
src/linked_list.h
src/libc_override.h
src/libc_override_gcc_and_weak.h
src/libc_override_glibc.h
src/libc_override_osx.h
src/libc_override_redefine.h
src/page_heap.h
src/page_heap_allocator.h
src/span.h
src/static_vars.h
src/symbolize.h
src/thread_cache.h
src/stack_trace_table.h
src/base/thread_annotations.h
src/malloc_hook-inl.h
src/malloc_hook_mmap_linux.h
src/malloc_hook_mmap_freebsd.h)
set(SG_TCMALLOC_MINIMAL_INCLUDES src/gperftools/malloc_hook.h
src/gperftools/malloc_hook_c.h
src/gperftools/malloc_extension.h
src/gperftools/malloc_extension_c.h
src/gperftools/nallocx.h)
set(TCMALLOC_MINIMAL_INCLUDES ${S_TCMALLOC_MINIMAL_INCLUDES} ${SG_TCMALLOC_MINIMAL_INCLUDES} ${SG_STACKTRACE_INCLUDES})
list(APPEND perftoolsinclude_HEADERS ${SG_TCMALLOC_MINIMAL_INCLUDES})
### Making the library
set(libtcmalloc_minimal_internal_la_SOURCES src/common.cc
src/internal_logging.cc
${SYSTEM_ALLOC_CC}
src/memfs_malloc.cc
src/central_freelist.cc
src/page_heap.cc
src/sampler.cc
src/span.cc
src/stack_trace_table.cc
src/static_vars.cc
src/symbolize.cc
src/thread_cache.cc
src/malloc_hook.cc
src/malloc_extension.cc
${TCMALLOC_MINIMAL_INCLUDES})
add_library(tcmalloc_minimal_internal_object OBJECT ${libtcmalloc_minimal_internal_la_SOURCES})
# We #define NO_TCMALLOC_SAMPLES, since sampling is turned off for _minimal.
target_compile_definitions(tcmalloc_minimal_internal_object PRIVATE NO_TCMALLOC_SAMPLES NO_HEAP_CHECK NDEBUG)
add_library(tcmalloc_minimal_internal INTERFACE)
target_link_libraries(tcmalloc_minimal_internal INTERFACE ${LIBSPINLOCK} ${maybe_threads_lib})
target_sources(tcmalloc_minimal_internal INTERFACE $<TARGET_OBJECTS:tcmalloc_minimal_internal_object>)
set(libtcmalloc_minimal_la_SOURCES ${TCMALLOC_CC} ${TCMALLOC_MINIMAL_INCLUDES})
set(libtcmalloc_minimal_la_DEFINES NO_TCMALLOC_SAMPLES NDEBUG)
add_library(tcmalloc_minimal SHARED ${libtcmalloc_minimal_la_SOURCES})
target_compile_definitions(tcmalloc_minimal PRIVATE ${libtcmalloc_minimal_la_DEFINES})
set(libtcmalloc_minimal_la_LIBADD tcmalloc_minimal_internal)
target_link_libraries(tcmalloc_minimal PRIVATE tcmalloc_minimal_internal Threads::Threads)
if(MINGW)
target_link_libraries(tcmalloc_minimal PRIVATE stacktrace)
endif()
set_target_properties(tcmalloc_minimal PROPERTIES
VERSION ${TCMALLOC_SO_VERSION}
SOVERSION ${TCMALLOC_SO_VERSION})
weaken_object(tcmalloc_minimal)
install(TARGETS tcmalloc_minimal)
if(GPERFTOOLS_BUILD_STATIC)
add_library(tcmalloc_minimal_static STATIC ${libtcmalloc_minimal_internal_la_SOURCES})
target_compile_definitions(tcmalloc_minimal_static PRIVATE NO_TCMALLOC_SAMPLES NDEBUG)
target_link_libraries(tcmalloc_minimal_static PRIVATE tcmalloc_minimal_internal Threads::Threads)
if(MINGW)
target_link_libraries(tcmalloc_minimal_static PRIVATE stacktrace)
endif()
if(NOT MSVC)
set_target_properties(tcmalloc_minimal_static PROPERTIES
OUTPUT_NAME tcmalloc_minimal)
endif()
weaken_object(tcmalloc_minimal_static)
install(TARGETS tcmalloc_minimal_static)
endif()
if(BUILD_TESTING)
set(tcmalloc_minimal_unittest_SOURCES
src/tests/tcmalloc_unittest.cc
src/tests/testutil.h src/tests/testutil.cc
${TCMALLOC_UNITTEST_INCLUDES})
set(tcmalloc_minimal_unittest_LDADD
${TCMALLOC_FLAGS} Threads::Threads logging)
# We want libtcmalloc last on the link line, but due to a bug in
# libtool involving convenience libs, they need to come last on the
# link line in order to get dependency ordering right. This is ok:
# convenience libraries are .a's, so tcmalloc is still the last .so.
# We also put pthreads after tcmalloc, because some pthread
# implementations define their own malloc, and we need to go on the
# first linkline to make sure our malloc 'wins'.
add_executable(tcmalloc_minimal_unittest ${tcmalloc_minimal_unittest_SOURCES})
target_link_libraries(tcmalloc_minimal_unittest tcmalloc_minimal ${tcmalloc_minimal_unittest_LDADD})
add_test(tcmalloc_minimal_unittest tcmalloc_minimal_unittest)
if(NOT MSVC)
add_executable(tcm_min_asserts_unittest
src/tests/tcmalloc_unittest.cc
src/tests/testutil.cc)
target_compile_definitions(tcm_min_asserts_unittest PUBLIC NO_TCMALLOC_SAMPLES NO_HEAP_CHECK)
target_link_libraries(tcm_min_asserts_unittest tcmalloc_minimal Threads::Threads)
add_test(tcm_min_asserts_unittest tcm_min_asserts_unittest)
endif()
add_executable(tcmalloc_minimal_large_unittest
src/tests/tcmalloc_large_unittest.cc
src/tests/testutil.cc
src/tests/testutil.h)
target_link_libraries(tcmalloc_minimal_large_unittest tcmalloc_minimal Threads::Threads)
add_test(tcmalloc_minimal_large_unittest tcmalloc_minimal_large_unittest)
add_executable(tcmalloc_minimal_large_heap_fragmentation_unittest
src/tests/large_heap_fragmentation_unittest.cc)
target_link_libraries(
tcmalloc_minimal_large_heap_fragmentation_unittest PUBLIC tcmalloc_minimal)
add_test(tcmalloc_minimal_large_heap_fragmentation_unittest tcmalloc_minimal_large_heap_fragmentation_unittest)
if(BUILD_SHARED_LIBS AND NOT MINGW)
add_custom_target(maybe_threads_unittest
COMMAND src/tests/maybe_threads_unittest.sh
VERBATIM)
add_test(maybe_threads_unittest maybe_threads_unittest)
endif()
if(MINGW OR MSVC)
set(port_src src/windows/port.cc)
endif()
add_executable(addressmap_unittest
src/tests/addressmap_unittest.cc
src/addressmap-inl.h
${port_src})
target_link_libraries(addressmap_unittest logging)
add_test(addressmap_unittest addressmap_unittest)
if(NOT MINGW)
add_executable(system_alloc_unittest src/tests/system-alloc_unittest.cc)
target_link_libraries(system_alloc_unittest PUBLIC tcmalloc_minimal)
add_test(system_alloc_unittest system_alloc_unittest)
endif()
add_executable(packed_cache_test src/tests/packed-cache_test.cc)
target_link_libraries(packed_cache_test PUBLIC tcmalloc_minimal)
add_test(packed_cache_test packed_cache_test)
add_executable(frag_unittest src/tests/frag_unittest.cc)
target_link_libraries(frag_unittest PUBLIC tcmalloc_minimal)
add_test(frag_unittest frag_unittest)
add_executable(markidle_unittest
src/tests/markidle_unittest.cc
src/tests/testutil.cc)
target_link_libraries(markidle_unittest tcmalloc_minimal Threads::Threads)
add_test(markidle_unittest markidle_unittest)
add_executable(current_allocated_bytes_test
src/tests/current_allocated_bytes_test.cc)
target_link_libraries(current_allocated_bytes_test PUBLIC tcmalloc_minimal)
add_test(current_allocated_bytes_test current_allocated_bytes_test)
add_executable(malloc_hook_test
src/tests/malloc_hook_test.cc
src/tests/testutil.cc)
target_link_libraries(malloc_hook_test tcmalloc_minimal Threads::Threads)
add_test(malloc_hook_test malloc_hook_test)
set(malloc_extension_test_SOURCES src/tests/malloc_extension_test.cc
src/config_for_unittests.h
src/base/logging.h
src/gperftools/malloc_extension.h
src/gperftools/malloc_extension_c.h)
set(malloc_extension_test_LIBADD Threads::Threads ${TCMALLOC_FLAGS})
add_executable(malloc_extension_test ${malloc_extension_test_SOURCES})
target_link_libraries(malloc_extension_test tcmalloc_minimal ${malloc_extension_test_LIBADD})
add_test(malloc_extension_test malloc_extension_test)
if(NOT MSVC)
add_executable(malloc_extension_c_test src/tests/malloc_extension_c_test.c)
target_link_libraries(malloc_extension_c_test PUBLIC
tcmalloc_minimal stdc++ m)
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(malloc_extension_c_test PUBLIC "-ansi")
endif()
add_test(malloc_extension_c_test malloc_extension_c_test)
endif()
if(NOT MINGW AND NOT MSVC AND NOT APPLE)
set(memalign_unittest_SOURCES src/tests/memalign_unittest.cc
src/tcmalloc.h
src/config_for_unittests.h
src/tests/testutil.h src/tests/testutil.cc)
add_executable(memalign_unittest ${memalign_unittest_SOURCES})
target_link_libraries(memalign_unittest tcmalloc_minimal Threads::Threads)
add_test(memalign_unittest memalign_unittest)
endif()
add_executable(page_heap_test src/tests/page_heap_test.cc)
if(MSVC)
target_link_libraries(page_heap_test tcmalloc_minimal_static)
else()
target_link_libraries(page_heap_test tcmalloc_minimal)
endif()
add_test(page_heap_test page_heap_test)
add_executable(pagemap_unittest src/tests/pagemap_unittest.cc)
target_link_libraries(pagemap_unittest PUBLIC tcmalloc_minimal)
add_test(pagemap_unittest pagemap_unittest)
set(realloc_unittest_SOURCES src/tests/realloc_unittest.cc
src/config_for_unittests.h
src/base/logging.h)
set(realloc_unittest_LDFLAGS Threads::Threads ${TCMALLOC_FLAGS})
add_executable(realloc_unittest ${realloc_unittest_SOURCES})
target_link_libraries(realloc_unittest PUBLIC tcmalloc_minimal ${realloc_unittest_LDFLAGS})
add_test(realloc_unittest realloc_unittest)
add_executable(stack_trace_table_test src/tests/stack_trace_table_test.cc)
target_link_libraries(stack_trace_table_test PUBLIC tcmalloc_minimal)
add_test(stack_trace_table_test stack_trace_table_test)
add_executable(thread_dealloc_unittest
src/tests/thread_dealloc_unittest.cc
src/tests/testutil.cc)
target_link_libraries(thread_dealloc_unittest tcmalloc_minimal Threads::Threads)
add_test(thread_dealloc_unittest thread_dealloc_unittest)
endif()
### ------- tcmalloc_minimal_debug (thread-caching malloc with debugallocation)
if(GPERFTOOLS_BUILD_DEBUGALLOC)
set(libtcmalloc_minimal_debug_la_SOURCES src/debugallocation.cc
${TCMALLOC_MINIMAL_INCLUDES})
add_library(tcmalloc_minimal_debug SHARED ${libtcmalloc_minimal_debug_la_SOURCES})
target_compile_definitions(tcmalloc_minimal_debug PRIVATE ${libtcmalloc_minimal_la_DEFINES}
TCMALLOC_FOR_DEBUGALLOCATION)
target_link_libraries(tcmalloc_minimal_debug PRIVATE ${libtcmalloc_minimal_la_LIBADD})
weaken_object(tcmalloc_minimal_debug)
install(TARGETS tcmalloc_minimal_debug)
set_target_properties(tcmalloc_minimal_debug PROPERTIES
VERSION ${TCMALLOC_SO_VERSION}
SOVERSION ${TCMALLOC_SO_VERSION})
if(GPERFTOOLS_BUILD_STATIC)
add_library(tcmalloc_minimal_debug_static STATIC ${libtcmalloc_minimal_debug_la_SOURCES})
target_compile_definitions(tcmalloc_minimal_debug_static PRIVATE ${libtcmalloc_minimal_la_DEFINES}
TCMALLOC_FOR_DEBUGALLOCATION)
if(NOT MSVC)
set_target_properties(tcmalloc_minimal_debug_static PROPERTIES
OUTPUT_NAME tcmalloc_minimal_debug)
endif()
target_link_libraries(tcmalloc_minimal_debug_static PRIVATE ${libtcmalloc_minimal_la_LIBADD})
weaken_object(tcmalloc_minimal_debug_static)
install(TARGETS tcmalloc_minimal_debug_static)
endif()
### Unittests
if(BUILD_TESTING)
add_executable(tcmalloc_minimal_debug_unittest ${tcmalloc_minimal_unittest_SOURCES})
target_compile_definitions(tcmalloc_minimal_debug_unittest PRIVATE DEBUGALLOCATION)
target_link_libraries(tcmalloc_minimal_debug_unittest tcmalloc_minimal_debug ${tcmalloc_minimal_unittest_LDADD})
add_test(tcmalloc_minimal_debug_unittest tcmalloc_minimal_debug_unittest)
add_executable(malloc_extension_debug_test ${malloc_extension_test_SOURCES})
target_link_libraries(malloc_extension_debug_test tcmalloc_minimal_debug ${malloc_extension_test_LIBADD})
add_test(malloc_extension_debug_test malloc_extension_debug_test)
if(NOT MINGW AND NOT APPLE)
add_executable(memalign_debug_unittest ${memalign_unittest_SOURCES})
target_link_libraries(memalign_debug_unittest
tcmalloc_minimal_debug Threads::Threads)
add_test(memalign_debug_unittest memalign_debug_unittest)
endif()
add_executable(realloc_debug_unittest ${realloc_unittest_SOURCES})
target_link_libraries(realloc_debug_unittest PUBLIC tcmalloc_minimal_debug)
add_test(realloc_debug_unittest realloc_debug_unittest)
if(WITH_STACK_TRACE)
add_executable(debugallocation_test src/tests/debugallocation_test.cc)
target_link_libraries(debugallocation_test PUBLIC tcmalloc_minimal_debug Threads::Threads)
add_test(NAME debugallocation_test
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/src/tests/debugallocation_test.sh)
endif()
endif()
endif()
if(NOT MINGW AND NOT MSVC)
if(gperftools_build_benchmark)
add_library(run_benchmark benchmark/run_benchmark.c)
add_executable(malloc_bench benchmark/malloc_bench.cc)
target_link_libraries(malloc_bench run_benchmark ${TCMALLOC_FLAGS})
if(GPERFTOOLS_BUILD_STATIC)
target_link_libraries(malloc_bench tcmalloc_minimal_static)
else()
target_link_libraries(malloc_bench tcmalloc_minimal)
endif()
add_executable(malloc_bench_shared benchmark/malloc_bench.cc)
target_link_libraries(malloc_bench_shared run_benchmark tcmalloc_minimal ${TCMALLOC_FLAGS} Threads::Threads)
if(GPERFTOOLS_BUILD_HEAP_CHECKER OR GPERFTOOLS_BUILD_HEAP_PROFILER)
add_executable(malloc_bench_shared_full benchmark/malloc_bench.cc)
target_link_libraries(malloc_bench_shared_full run_benchmark tcmalloc ${TCMALLOC_FLAGS} Threads::Threads)
endif()
add_executable(binary_trees benchmark/binary_trees.cc)
target_link_libraries(binary_trees Threads::Threads ${TCMALLOC_FLAGS})
if(GPERFTOOLS_BUILD_STATIC)
target_link_libraries(binary_trees tcmalloc_minimal_static)
else()
target_link_libraries(binary_trees tcmalloc_minimal)
endif()
add_executable(binary_trees_shared benchmark/binary_trees.cc)
target_link_libraries(binary_trees_shared tcmalloc_minimal Threads::Threads ${TCMALLOC_FLAGS})
endif()
endif()
### ------- tcmalloc (thread-caching malloc + heap profiler + heap checker)
if(GPERFTOOLS_BUILD_HEAP_CHECKER OR GPERFTOOLS_BUILD_HEAP_PROFILER)
### The header files we use. We divide into categories based on directory
set(S_TCMALLOC_INCLUDES ${S_TCMALLOC_MINIMAL_INCLUDES}
${LOGGING_INCLUDES}
src/addressmap-inl.h
src/raw_printer.h
src/base/googleinit.h
src/base/linuxthreads.h
src/base/stl_allocator.h
src/base/sysinfo.h
src/heap-profile-table.h
src/heap-profile-stats.h
src/maybe_emergency_malloc.h
src/emergency_malloc.h)
set(SG_TCMALLOC_INCLUDES src/gperftools/heap-profiler.h
src/gperftools/heap-checker.h)
set(TCMALLOC_INCLUDES ${S_TCMALLOC_INCLUDES} ${SG_TCMALLOC_MINIMAL_INCLUDES}
${SG_TCMALLOC_INCLUDES} ${SG_STACKTRACE_INCLUDES})
list(APPEND perftoolsinclude_HEADERS ${SG_TCMALLOC_INCLUDES})
if(gperftools_emergency_malloc)
set(EMERGENCY_MALLOC_CC
src/emergency_malloc.cc
src/emergency_malloc_for_stacktrace.cc)
set(EMERGENCY_MALLOC_DEFINE ENABLE_EMERGENCY_MALLOC)
else()
set(EMERGENCY_MALLOC_CC src/fake_stacktrace_scope.cc)
endif()
### Making the library
set(libtcmalloc_internal_la_SOURCES ${libtcmalloc_minimal_internal_la_SOURCES}
${TCMALLOC_INCLUDES}
src/base/low_level_alloc.cc
src/heap-profile-table.cc
src/heap-profiler.cc
src/raw_printer.cc
${EMERGENCY_MALLOC_CC}
src/memory_region_map.cc)
set(libtcmalloc_internal_la_DEFINE NDEBUG ${EMERGENCY_MALLOC_DEFINE})
set(libtcmalloc_internal_la_LIBADD stacktrace Threads::Threads)
set(libtcmalloc_la_SOURCES ${TCMALLOC_CC} ${TCMALLOC_INCLUDES})
set(libtcmalloc_la_DEFINE NDEBUG ${EMERGENCY_MALLOC_DEFINE})
set(libtcmalloc_la_LIBADD tcmalloc_internal ${maybe_threads_lib} Threads::Threads)
if(GPERFTOOLS_BUILD_HEAP_CHECKER)
# heap-checker-bcad is last, in hopes its global ctor will run first.
# (Note this is added to libtcmalloc.la, not libtcmalloc_internal.la,
# but that's ok; the internal/external distinction is only useful for
# cygwin, and cygwin doesn't use HEAP_CHECKER anyway.)
set(HEAP_CHECKER_SOURCES src/base/linuxthreads.cc
src/heap-checker.cc
src/heap-checker-bcad.cc)
list(APPEND libtcmalloc_la_SOURCES ${HEAP_CHECKER_SOURCES})
else()
list(APPEND libtcmalloc_internal_la_DEFINE NO_HEAP_CHECK)
list(APPEND libtcmalloc_la_DEFINE NO_HEAP_CHECK)
endif()
add_library(tcmalloc_internal_object OBJECT ${libtcmalloc_internal_la_SOURCES})
target_compile_definitions(tcmalloc_internal_object PRIVATE ${libtcmalloc_internal_la_DEFINE})
add_library(tcmalloc_internal INTERFACE)
target_sources(tcmalloc_internal INTERFACE $<TARGET_OBJECTS:tcmalloc_internal_object>)
target_link_libraries(tcmalloc_internal INTERFACE ${libtcmalloc_internal_la_LIBADD})
add_library(tcmalloc SHARED ${libtcmalloc_la_SOURCES})
target_compile_definitions(tcmalloc PRIVATE ${libtcmalloc_la_DEFINE})
target_link_libraries(tcmalloc ${libtcmalloc_la_LIBADD})
set_target_properties(tcmalloc PROPERTIES
VERSION ${TCMALLOC_SO_VERSION}
SOVERSION ${TCMALLOC_SO_VERSION})
weaken_object(tcmalloc)
install(TARGETS tcmalloc)
if(GPERFTOOLS_BUILD_STATIC)
add_library(tcmalloc_static STATIC ${libtcmalloc_la_SOURCES})
target_compile_definitions(tcmalloc_static PRIVATE ${libtcmalloc_la_DEFINE})
if(NOT MSVC)
set_target_properties(tcmalloc_static PROPERTIES OUTPUT_NAME tcmalloc)
endif()
target_link_libraries(tcmalloc_static PRIVATE ${libtcmalloc_la_LIBADD})
weaken_object(tcmalloc_static)
install(TARGETS tcmalloc_static)
endif()
### Unittests
if(BUILD_TESTING)
set(TCMALLOC_UNITTEST_INCLUDES src/config_for_unittests.h
src/gperftools/malloc_extension.h)
set(tcmalloc_unittest_SOURCES src/tests/tcmalloc_unittest.cc
src/tcmalloc.h
src/tests/testutil.h src/tests/testutil.cc
${TCMALLOC_UNITTEST_INCLUDES})
set(tcmalloc_unittest_LIBADD ${TCMALLOC_FLAGS} logging Threads::Threads)
add_executable(tcmalloc_unittest ${tcmalloc_unittest_SOURCES})
target_link_libraries(tcmalloc_unittest tcmalloc ${tcmalloc_unittest_LIBADD})
add_test(NAME tcmalloc_unittest
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/src/tests/tcmalloc_unittest.sh")
# This makes sure it's safe to link in both tcmalloc and
# tcmalloc_minimal. (One would never do this on purpose, but perhaps
# by accident...) When we can compile libprofiler, we also link it in
# to make sure that works too. NOTE: On OS X, it's *not* safe to
# link both in (we end up with two copies of every global var, and
# the code tends to pick one arbitrarily), so don't run the test there.
set(tcmalloc_both_unittest_srcs src/tests/tcmalloc_unittest.cc
src/tests/testutil.h src/tests/testutil.cc
${TCMALLOC_UNITTEST_INCLUDES})
if(GPERFTOOLS_BUILD_CPU_PROFILER)
set(tcmalloc_both_unittest_ladd tcmalloc tcmalloc_minimal profiler logging Threads::Threads)
else()
set(tcmalloc_both_unittest_ladd tcmalloc tcmalloc_minimal logging Threads::Threads)
endif()
if(NOT APPLE)
add_executable(tcmalloc_both_unittest ${tcmalloc_both_unittest_srcs})
target_link_libraries(tcmalloc_both_unittest ${TCMALLOC_FLAGS} ${tcmalloc_both_unittest_ladd})
add_test(tcmalloc_both_unittest tcmalloc_both_unittest)
endif()
add_executable(tcmalloc_large_unittest src/tests/tcmalloc_large_unittest.cc)
target_link_libraries(tcmalloc_large_unittest tcmalloc Threads::Threads)
add_test(tcmalloc_large_unittest tcmalloc_large_unittest)
add_executable(tcmalloc_large_heap_fragmentation_unittest src/tests/large_heap_fragmentation_unittest.cc)
target_link_libraries(tcmalloc_large_heap_fragmentation_unittest tcmalloc Threads::Threads)
add_test(tcmalloc_large_heap_fragmentation_unittest tcmalloc_large_heap_fragmentation_unittest)
add_executable(raw_printer_test src/tests/raw_printer_test.cc)
target_link_libraries(raw_printer_test tcmalloc Threads::Threads)
add_test(raw_printer_test raw_printer_test)
# sampler_test and sampling_test both require sampling to be turned
# on, which it's not by default. Use the "standard" value of 2^19.
list(APPEND TESTS_ENVIRONMENT TCMALLOC_SAMPLE_PARAMETER=524288)
set(sampler_test_SOURCES src/tests/sampler_test.cc
src/config_for_unittests.h)
set(sampler_test_LIBADD ${TCMALLOC_FLAGS} Threads::Threads m)
add_executable(sampler_test ${sampler_test_SOURCES})
target_link_libraries(sampler_test tcmalloc ${sampler_test_LIBADD})
add_test(sampler_test sampler_test)
# These unittests often need to run binaries. They're in the current dir
list(APPEND TESTS_ENVIRONMENT BINDIR=. TMPDIR=/tmp/perftools)
set(SAMPLING_TEST_INCLUDES src/config_for_unittests.h
src/base/logging.h
src/gperftools/malloc_extension.h)
set(sampling_test_SOURCES src/tests/sampling_test.cc
${SAMPLING_TEST_INCLUDES})
add_executable(sampling_test ${sampling_test_SOURCES})
target_link_libraries(sampling_test ${TCMALLOC_FLAGS} tcmalloc Threads::Threads)
add_test(NAME sampling_test.sh
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/src/tests/sampling_test.sh" sampling_test)
if(GPERFTOOLS_BUILD_HEAP_PROFILER)
set(HEAP_PROFILER_UNITTEST_INCLUDES src/config_for_unittests.h
src/gperftools/heap-profiler.h)
set(heap_profiler_unittest_SOURCES src/tests/heap-profiler_unittest.cc
${HEAP_PROFILER_UNITTEST_INCLUDES})
add_executable(heap_profiler_unittest ${heap_profiler_unittest_SOURCES})
target_link_libraries(heap_profiler_unittest ${TCMALLOC_FLAGS} tcmalloc Threads::Threads)
add_test(NAME heap-profiler_unittest.sh
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/src/tests/heap-profiler_unittest.sh" heap-profiler_unittest)
# Tests the compatibility include-headers in google/. Requires a function
# defined in the heap-profiler, which is why the test lives here.
add_executable(simple_compat_test src/tests/simple_compat_test.cc
${googleinclude_HEADERS})
target_link_libraries(simple_compat_test ${TCMALLOC_FLAGS} tcmalloc)
add_test(simple_compat_test simple_compat_test)
endif()
if(GPERFTOOLS_BUILD_HEAP_CHECKER)
set(HEAP_CHECKER_UNITTEST_INCLUDES src/config_for_unittests.h
src/memory_region_map.h
src/base/commandlineflags.h
src/base/googleinit.h
src/gperftools/heap-checker.h
${LOGGING_INCLUDES})
set(heap_checker_unittest_SOURCES src/tests/heap-checker_unittest.cc
${HEAP_CHECKER_UNITTEST_INCLUDES})
add_executable(heap_checker_unittest ${heap_checker_unittest_SOURCES})
target_link_libraries(heap_checker_unittest ${TCMALLOC_FLAGS} tcmalloc logging Threads::Threads)
add_test(heap-checker_unittest heap_checker_unittest)
add_test(NAME heap-checker-death_unittest.sh
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/src/tests/heap-checker-death_unittest.sh")
endif()
endif()
endif()
### ------- tcmalloc with debugallocation
if(GPERFTOOLS_BUILD_DEBUGALLOC)
if(GPERFTOOLS_BUILD_HEAP_CHECKER OR GPERFTOOLS_BUILD_HEAP_PROFILER)
add_library(tcmalloc_debug SHARED src/debugallocation.cc ${HEAP_CHECKER_SOURCES} ${TCMALLOC_INCLUDES})
target_compile_definitions(tcmalloc_debug PRIVATE ${libtcmalloc_la_DEFINE}
TCMALLOC_FOR_DEBUGALLOCATION)
target_link_libraries(tcmalloc_debug PRIVATE ${libtcmalloc_la_LIBADD})
set_target_properties(tcmalloc_debug PROPERTIES
VERSION ${TCMALLOC_SO_VERSION}
SOVERSION ${TCMALLOC_SO_VERSION})
weaken_object(tcmalloc_debug)
install(TARGETS tcmalloc_debug)
if(GPERFTOOLS_BUILD_STATIC)
add_library(tcmalloc_debug_static STATIC src/debugallocation.cc ${HEAP_CHECKER_SOURCES} ${TCMALLOC_INCLUDES})
target_compile_definitions(tcmalloc_debug_static PRIVATE ${libtcmalloc_la_DEFINE}
TCMALLOC_FOR_DEBUGALLOCATION)
target_link_libraries(tcmalloc_debug_static PRIVATE ${libtcmalloc_la_LIBADD})
if(NOT MSVC)
set_target_properties(tcmalloc_debug_static PROPERTIES
OUTPUT_NAME tcmalloc_debug)
endif()
weaken_object(tcmalloc_debug_static)
install(TARGETS tcmalloc_debug_static)
endif()
### Unittests
if(BUILD_TESTING)
add_executable(tcmalloc_debug_unittest ${tcmalloc_unittest_SOURCES})
target_compile_definitions(tcmalloc_debug_unittest PRIVATE DEBUGALLOCATION ${tcmalloc_unittest})
target_link_libraries(tcmalloc_debug_unittest tcmalloc_debug ${tcmalloc_unittest_LIBADD})
add_test(tcmalloc_debug_unittest tcmalloc_debug_unittest)
add_executable(sampler_debug_test ${sampler_test_SOURCES})
target_link_libraries(sampler_debug_test tcmalloc_debug ${tcmalloc_unittest_LIBADD})
add_test(sampler_debug_test sampler_debug_test)
add_executable(sampling_debug_test ${sampling_test_SOURCES})
target_link_libraries(sampling_debug_test ${TCMALLOC_FLAGS} tcmalloc_debug Threads::Threads)
add_test(sampling_debug_test.sh "${CMAKE_CURRENT_SOURCE_DIR}/src/tests/sampling_test.sh" sampling_debug_test)
if(GPERFTOOLS_BUILD_HEAP_PROFILER)
add_executable(heap_profiler_debug_unittest ${heap_profiler_unittest_SOURCES})
target_link_libraries(heap_profiler_debug_unittest ${TCMALLOC_FLAGS} tcmalloc_debug Threads::Threads)
add_test(heap-profiler_debug_unittest.sh "${CMAKE_CURRENT_SOURCE_DIR}/src/tests/heap-profiler_unittest.sh" heap-profiler_debug_unittest)
endif()
if(GPERFTOOLS_BUILD_HEAP_CHECKER)
add_executable(heap_checker_debug_unittest ${heap_checker_unittest_SOURCES})
target_link_libraries(heap_checker_debug_unittest ${TCMALLOC_FLAGS} tcmalloc_debug logging Threads::Threads)
add_test(heap_checker_debug_unittest heap_checker_debug_unittest)
endif()
endif()
endif()
endif()
### ------- CPU profiler
if(GPERFTOOLS_BUILD_CPU_PROFILER)
### The header files we use. We divide into categories based on directory
set(S_CPU_PROFILER_INCLUDES src/profiledata.h
src/profile-handler.h
src/getpc.h
src/base/basictypes.h
src/base/commandlineflags.h
src/base/googleinit.h
src/base/logging.h
src/base/simple_mutex.h
src/base/sysinfo.h
${SPINLOCK_INCLUDES}
${LOGGING_INCLUDES})
set(SG_CPU_PROFILER_INCLUDES src/gperftools/profiler.h)
set(CPU_PROFILER_INCLUDES ${S_CPU_PROFILER_INCLUDES} ${SG_CPU_PROFILER_INCLUDES}
${SG_STACKTRACE_INCLUDES})
list(APPEND perftoolsinclude_HEADERS ${SG_CPU_PROFILER_INCLUDES})
### Making the library
set(libprofiler_la_SOURCES src/profiler.cc
src/profile-handler.cc
src/profiledata.cc
${CPU_PROFILER_INCLUDES})
set(libprofiler_la_LIBADD stacktrace ${maybe_threads_lib} fake_stacktrace_scope)
add_library(profiler SHARED ${libprofiler_la_SOURCES})
target_link_libraries(profiler PRIVATE ${libprofiler_la_LIBADD})
set_target_properties(profiler PROPERTIES
VERSION ${PROFILER_SO_VERSION}
SOVERSION ${PROFILER_SO_VERSION})
install(TARGETS profiler)
if(GPERFTOOLS_BUILD_STATIC)
add_library(profiler_static STATIC ${libprofiler_la_SOURCES})
target_link_libraries(profiler_static PRIVATE ${libprofiler_la_LIBADD})
if(NOT MSVC)
set_target_properties(profiler_static PROPERTIES OUTPUT_NAME profiler)
endif()
install(TARGETS profiler_static)
endif()
# See discussion above (under LIBTCMALLOC_MINIMAL) for why we do this.
# Basically it's to work around systems where --rpath doesn't work right.
set(LIBPROFILER stacktrace profiler)
if(BUILD_TESTING)
add_executable(getpc_test src/tests/getpc_test.cc src/getpc.h)
add_test(getpc_test getpc_test)
add_executable(profiledata_unittest src/tests/profiledata_unittest.cc
src/profiledata.h
src/base/commandlineflags.h
src/base/logging.h
src/base/basictypes.h)
target_link_libraries(profiledata_unittest ${LIBPROFILER})
add_test(profiledata_unittest profiledata_unittest)
add_executable(profile_handler_unittest src/tests/profile-handler_unittest.cc
src/profile-handler.h)
target_link_libraries(profile_handler_unittest ${LIBPROFILER} Threads::Threads)
add_test(profile_handler_unittest profile_handler_unittest)
add_test(NAME profiler_unittest.sh
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/src/tests/profiler_unittest.sh")
set(PROFILER_UNITTEST_INCLUDES src/config_for_unittests.h
src/gperftools/profiler.h)
set(PROFILER_UNITTEST_SRCS src/tests/profiler_unittest.cc
src/tests/testutil.h src/tests/testutil.cc
${PROFILER_UNITTEST_INCLUDES})
add_executable(profiler1_unittest ${PROFILER_UNITTEST_SRCS})
target_compile_definitions(profiler1_unittest PRIVATE NO_THREADS)
target_link_libraries(profiler1_unittest ${LIBPROFILER})
add_executable(profiler2_unittest ${PROFILER_UNITTEST_SRCS})
target_compile_definitions(profiler2_unittest PRIVATE NO_THREADS)
target_link_libraries(profiler2_unittest stacktrace profiler)
add_executable(profiler3_unittest ${PROFILER_UNITTEST_SRCS})
target_link_libraries(profiler3_unittest ${LIBPROFILER} Threads::Threads)
add_executable(profiler4_unittest ${PROFILER_UNITTEST_SRCS})
target_link_libraries(profiler4_unittest stacktrace profiler Threads::Threads)
endif()
endif()
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/gperftools/tcmalloc.h
${perftoolsinclude_HEADERS}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gperftools)
### ------- CPU profiler and heap checker, in one!
# Ideally, folks who wanted to use both tcmalloc and libprofiler,
# could just link them both into their application. But while this
# works fine for .so files, it does not for .a files. The easiest way
# around this -- and I've tried a bunch of the hard ways -- is to just
# to create another set of libraries that has both functionality in it.
if(GPERFTOOLS_BUILD_HEAP_PROFILER OR GPERFTOOLS_BUILD_HEAP_CHECKER)
if(GPERFTOOLS_BUILD_CPU_PROFILER)
add_library(tcmalloc_and_profiler SHARED ${libtcmalloc_la_SOURCES} ${libprofiler_la_SOURCES})
target_compile_definitions(tcmalloc_and_profiler PRIVATE ${libtcmalloc_la_DEFINE})
set_target_properties(tcmalloc_and_profiler PROPERTIES
VERSION ${TCMALLOC_AND_PROFILER_SO_VERSION}
SOVERSION ${TCMALLOC_AND_PROFILER_SO_VERSION})
# We don't include libprofiler_la_LIBADD here because all it adds is
# libstacktrace.la, which we already get via libtcmalloc. Trying to
# specify it twice causes link-time duplicate-definition errors. :-(
target_link_libraries(tcmalloc_and_profiler PRIVATE ${libtcmalloc_la_LIBADD})
weaken_object(tcmalloc_and_profiler)
install(TARGETS tcmalloc_and_profiler)
if(GPERFTOOLS_BUILD_STATIC)
add_library(tcmalloc_and_profiler_static STATIC ${libtcmalloc_la_SOURCES} ${libprofiler_la_SOURCES})
target_compile_definitions(tcmalloc_and_profiler_static PRIVATE ${libtcmalloc_la_DEFINE})
target_link_libraries(tcmalloc_and_profiler_static PRIVATE ${libtcmalloc_la_LIBADD})
if(NOT MSVC)
set_target_properties(tcmalloc_and_profiler_static PROPERTIES
OUTPUT_NAME tcmalloc_and_profiler)
endif()
weaken_object(tcmalloc_and_profiler_static)
install(TARGETS tcmalloc_and_profiler_static)
endif()
if(BUILD_TESTING)
add_executable(tcmalloc_and_profiler_unittest ${tcmalloc_both_unittest_srcs})
target_link_libraries(tcmalloc_and_profiler_unittest tcmalloc_and_profiler Threads::Threads)
add_test(tcmalloc_and_profiler_unittest tcmalloc_and_profiler_unittest)
endif()
endif()
endif()
if(BUILD_TESTING)
get_directory_property(tests TESTS)
message("TESTS_ENVIRONMENT:${TESTS_ENVIRONMENT}")
if(TESTS_ENVIRONMENT)
foreach(test IN LISTS tests)
set_tests_properties(${test} PROPERTIES ENVIRONMENT "${TESTS_ENVIRONMENT}")
endforeach()
endif()
endif()
if(MSVC)
add_subdirectory(src/windows)
endif()
## ^^^^ END OF RULES TO MAKE YOUR LIBRARIES, BINARIES, AND UNITTESTS
#TODO rpm deb
# http://linux.die.net/man/1/pkg-config, http://pkg-config.freedesktop.org/wiki
# I get the description and URL lines from the rpm spec. I use sed to
# try to rewrite exec_prefix, libdir, and includedir in terms of
# prefix, if possible.
set(PTHREAD_FLAGS)
foreach(flag IN ITEMS INTERFACE_LINK_LIBRARIES INTERFACE_LINK_OPTIONS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_COMPILE_OPTIONS INTERFACE_COMPILE_DEFINITIONS INTERFACE_SOURCES)
get_target_property(T Threads::Threads ${flag})
if(T)
set(PTHREAD_FLAGS "${PTHREAD_FLAGS} ${T}")
endif()
endforeach()
set(NAME tcmalloc)
configure_file(cmake/pkgconfig.pc libtcmalloc.pc @ONLY)
set(NAME tcmalloc_debug)
configure_file(cmake/pkgconfig.pc libtcmalloc_debug.pc @ONLY)
set(NAME tcmalloc_minimal)
configure_file(cmake/pkgconfig.pc libtcmalloc_minimal.pc @ONLY)
set(NAME tcmalloc_minimal_debug)
configure_file(cmake/pkgconfig.pc libtcmalloc_minimal_debug.pc @ONLY)
set(NAME profiler)
configure_file(cmake/pkgconfig.pc libprofiler.pc @ONLY)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/libtcmalloc.pc
${CMAKE_CURRENT_BINARY_DIR}/libtcmalloc_minimal.pc
${CMAKE_CURRENT_BINARY_DIR}/libtcmalloc_debug.pc
${CMAKE_CURRENT_BINARY_DIR}/libtcmalloc_minimal_debug.pc
${CMAKE_CURRENT_BINARY_DIR}/libprofiler.pc
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
#TODO @GENERATE_CHANGELOG_RULES@
#TODO dist