1. 程式人生 > >cmake:gperftools效能分析工具find libprofiler 指令碼暨profiler的靜態連線問題

cmake:gperftools效能分析工具find libprofiler 指令碼暨profiler的靜態連線問題

gperftools是一個很好用的效能分析工具,但沒有提供官方的用於查詢profiler庫的cmake指令碼,所以在基於cmake管理的專案中如果要在系統查詢libprofiler庫就就要自己寫FindPROFILER.cmake指令碼。
將指令碼所在的資料夾加入CMAKE_MODULE_PATH,呼叫find_package(PROFILER),就會找到系統中安裝的gperftools的libprofiler庫,
指令碼不僅按傳統方式輸出LIBPROFILER_xxx系列變數,還會建立gperftools::profiler INTERFACE target.方便在專案中引用。

FindPROFILER.cmake

# - Find libprofiler
# - This module determines the libprofiler library of the system
# the vairable can be set
#   LIBPROFILER_STATIC_LINK set true for static link library 為TRUE是要求靜態連線
# The following variables are set if the library found:
# LIBPROFILER_FOUND - If false do nnt try to use libprofiler.
# LIBPROFILER_INCLUDE_DIRS - where to find the headfile of library.include資料夾位置
# LIBPROFILER_LIBRARY_DIRS - where to find the libprofiler library.profiler庫所在位置
# LIBPROFILER_LIBRARIES, the library file name needed to use libprofiler.profiler庫及所有依賴庫列表
# LIBPROFILER_LIBRARY - the library needed to use libprofiler. profiler庫全路徑
# imported target
#   gperftools::profiler

if(LIBPROFILER_FOUND)
    return()
endif()
include (depcommon)
# linux系統下呼叫pkg-config查詢profiler
if (NOT WIN32)
    include(FindPkgConfig)
    unset(_verexp)
    if(LIBPROFILER_FIND_VERSION)
        if(LIBPROFILER_FIND_VERSION_EXACT)        
              set(_verexp "=${LIBPROFILER_FIND_VERSION}")
        else()
              set(_verexp ">=${LIBPROFILER_FIND_VERSION}")
        endif()
    endif()
    pkg_check_modules (LIBPROFILER libprofiler${_verexp})
endif()

if (NOT LIBPROFILER_FOUND)
	# windows系統下通過查詢標頭檔案 gperftools/profiler.h和find_library 查詢profiler來實現
    # find the headfile of library
    set (PROFILER_HEADS gperftools/profiler.h)
    find_path (LIBPROFILER_INCLUDE_DIRS ${PROFILER_HEADS})

    set (PROFILER_NAMES ${PROFILER_NAMES} profiler)
    find_library (LIBPROFILER_LIBRARY NAMES ${PROFILER_NAMES})

    # just find one of dependency, guess other one.
    if (NOT LIBPROFILER_LIBRARY AND LIBPROFILER_INCLUDE_DIRS)
        message ("We just find the headfile, try to guess the library location.")
        set (LIBPROFILER_LIBRARY_DIRS "${LIBPROFILER_INCLUDE_DIRS}/../lib")
        find_library (LIBPROFILER_LIBRARY NAMES ${PROFILER_NAMES} PATHS ${LIBPROFILER_LIBRARY_DIRS})
    elseif (NOT LIBPROFILER_INCLUDE_DIRS AND LIBPROFILER_LIBRARY)
        message ("We just find the lib file, try to guess the include location.")
                get_filename_component(LIBPROFILER_LIBRARY_DIRS ${LIBPROFILER_LIBRARY} DIRECTORY)
        find_path (LIBPROFILER_INCLUDE_DIRS ${PROFILER_HEADS} "${LIBPROFILER_LIBRARY_DIRS}../included")
    endif()

    # find the library.
    if (LIBPROFILER_INCLUDE_DIRS AND LIBPROFILER_LIBRARY)
        if (NOT LIBPROFILER_LIBRARY_DIRS)                        
            get_filename_component(LIBPROFILER_LIBRARY_DIRS ${LIBPROFILER_LIBRARY} DIRECTORY)
        endif ()
        list(APPEND profiler pthread)
    endif()
else ()
        list(GET MGNCS_LIBRARIES 0 _name)
    find_library (LIBPROFILER_LIBRARY NAMES ${LIBPROFILER_LIBRARIES} PATHS ${LIBPROFILER_LIBRARY_DIRS})
endif()
# handle the QUIETLY and REQUIRED arguments and set LIBPROFILER_FOUND to TRUE if
# all listed variables are TRUE
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBPROFILER DEFAULT_MSG    LIBPROFILER_LIBRARIES LIBPROFILER_INCLUDE_DIRS)

if(LIBPROFILER_FOUND)
    set(_static_libname libtcmalloc_and_profiler.a)
    find_library (LIBPROFILER_STATIC_LIBRARY NAMES ${_static_libname} PATHS ${LIBPROFILER_LIBRARY_DIRS})
    if(NOT LIBPROFILER_FIND_QUIETLY)
        message(STATUS "  -I: ${LIBPROFILER_INCLUDE_DIRS}")
        message(STATUS "  -L: ${LIBPROFILER_LIBRARY_DIRS}")
        message(STATUS "  -l: ${LIBPROFILER_LIBRARIES}")
    endif()
    find_library (LIBPROFILER_LIBRARY NAMES ${LIBPROFILER_LIBRARIES} PATHS ${LIBPROFILER_LIBRARY_DIRS})

    # create imported target
    if (NOT TARGET gperftools::profiler)
        add_library(gperftools::profiler INTERFACE IMPORTED)
        if(LIBPROFILER_STATIC_LINK)
            # for linking static profiler,must use libtcmalloc_and_profiler.a,see also README gperftools
            set(_link_libs ${LIBPROFILER_STATIC_LDFLAGS})
            if(NOT LIBPROFILER_STATIC_LIBRARY)
                message(FATAL_ERROR "NOT FOUND static library for profiler:${_static_libname} ")
            endif()
      # 替換 profiler 為 :libtcmalloc_and_profiler.a
            string(REPLACE profiler :${_static_libname} _link_libs "${_link_libs}")
        else()
            set(_link_libs ${LIBPROFILER_LDFLAGS})
        endif()
        set_target_properties(gperftools::profiler PROPERTIES
            INTERFACE_COMPILE_OPTIONS "${LIBPROFILER_CFLAGS_OTHER}"
            INTERFACE_INCLUDE_DIRECTORIES "${LIBPROFILER_INCLUDE_DIRS}"
            INTERFACE_LINK_LIBRARIES   "${_link_libs}"
            )
        if(NOT LIBPROFILER_FIND_QUIETLY)
            message(STATUS "IMPORTED TARGET: gperftools::profiler,link libraies ${_link_libs}")
        endif()
    endif ()
    
endif(LIBPROFILER_FOUND)

靜態連線profiler

關於靜態連線profiler,之前看過不少部落格文章,都指出gperftools不支援靜態連線profiler庫。但我查看了gperftools(2.7)的官方說明,提供了靜態連線profiler庫的方法:

下面的英文說明來自gperftools的官方說明(https://github.com/gperftools/gperftools/blob/master/README)

EVERYTHING IN ONE
-----------------
If you want the CPU profiler, heap profiler, and heap leak-checker to
all be available for your application, you can do:
   gcc -o myapp ... -lprofiler -ltcmalloc

However, if you have a reason to use the static versions of the
library, this two-library linking won't work:
   gcc -o myapp ... /usr/lib/libprofiler.a /usr/lib/libtcmalloc.a  # errors!

Instead, use the special libtcmalloc_and_profiler library, which we
make for just this purpose:
   gcc -o myapp ... /usr/lib/libtcmalloc_and_profiler.a

確實直接靜態連線libprofiler.a是不行的,但這裡也明確給出了靜態連線profiler的方式:用libtcmalloc_and_profiler.a替代libprofiler.a

所以在上面的cmake指令碼中當LIBPROFILER_STATIC_LINK 為TRUE時,會替換 profiler:libtcmalloc_and_profiler.a