diff --git a/Bloaty.cmake b/Bloaty.cmake new file mode 100644 index 0000000..ced9dda --- /dev/null +++ b/Bloaty.cmake @@ -0,0 +1,165 @@ +# +# OVERVIEW +# +# Bloaty is a static memory profiling tool which measures the size utilization +# of a target binary and outputs the result to a file. +# +# USAGE +# +# swift_add_bloaty( +# [OPTIONS] +# [WORKING_DIRECTORY working_directory] +# ) +# +# Call this function to create a new cmake target which runs the `target`'s +# executable binary with bloaty applied. All created cmake targets can be +# invoked by calling the common target 'do-all-bloaty'. +# +# BLOATY OPTIONS +# +# * SEGMENTS: Outputs what the run-time loader uses to determine what +# parts of the binary needs to be loaded/mapped into memory. +# * SECTIONS: Outputs the binary in finer details structured in different sections. +# * SYMBOLS: Outputs individual functions or variables. +# * COMPILEUNITS: Outputs what compile unit (and corresponding source file) +# each bit of the binary came from. +# +# * NUM: Set how many rows to show per level before collapsing into '[other]'. +# Set to '0' for unlimited, default: '20'. +# +# * SORT: +# vm - Sort the vm size column from largest to smallest and tells you +# how much space the binary will take when loaded into memory. +# file - Sort the file size column from largest to smallest and tells you +# how much space the binary is taking on disk. +# both - Default, sorts by max(vm, file). +# +# WORKING_DIRECTORY +# This variable changes the output directory for the tool from the default folder +# `${CMAKE_BINARY_DIR}/profiling/bloaty-reports` to the given argument. +# Example, using argument `/tmp`, outputs the results to `/tmp/bloaty-reports/ +# +# NOTE +# +# * A cmake option is available to control whether targets should be built, +# with the name ${PROJECT_NAME}_ENABLE_MEMORY_PROFILING. +# +# Running +# +# cmake -D_ENABLE_MEMORY_PROFILING=ON .. +# +# will explicitly enable these targets from the command line at configure time +# + +option(${PROJECT_NAME}_ENABLE_MEMORY_PROFILING "Builds targets with memory profiling" OFF) + +find_package(Bloaty) + +if (NOT Bloaty_FOUND AND ${PROJECT_NAME}_ENABLE_MEMORY_PROFILING) + message(STATUS "Bloaty is not installed on system, will fetch content from source") + + cmake_minimum_required(VERSION 3.14.0) + include(FetchContent) + + FetchContent_Declare( + bloaty + GIT_REPOSITORY https://github.com/google/bloaty.git + GIT_TAG v1.1 + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(bloaty) +endif() + +macro(eval_bloaty_target target) + if (NOT TARGET ${target}) + message(FATAL_ERROR "Specified target \"${target}\" does not exist") + endif() + + get_target_property(target_type ${target} TYPE) + if (NOT target_type STREQUAL EXECUTABLE) + message(FATAL_ERROR "Specified target \"${target}\" must be an executable type to register for profiling with bloaty") + endif() + + if (NOT ${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME}) + return() + endif() + + if (NOT ${PROJECT_NAME}_ENABLE_MEMORY_PROFILING) + return() + endif() +endmacro() + +function(swift_add_bloaty target) + eval_bloaty_target(${target}) + + set(argOption SEGMENTS SECTIONS SYMBOLS COMPILEUNITS) + set(argSingle NUM SORT WORKING_DIRECTORY) + set(argMulti "") + + cmake_parse_arguments(x "${argOption}" "${argSingle}" "${argMulti}" ${ARGN}) + + if (x_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unparsed arguments ${x_UNPARSED_ARGUMENTS}") + endif() + + set(target_name bloaty-${target}) + set(working_directory ${CMAKE_BINARY_DIR}/profiling) + if (x_WORKING_DIRECTORY) + set(working_directory ${x_WORKING_DIRECTORY}) + endif() + set(reports_directory ${working_directory}/bloaty-reports) + set(output_file ${target_name}.txt) + + unset(resource_options) + if (x_SEGMENTS) + list(APPEND resource_options segments) + endif() + + if (x_SECTIONS) + list(APPEND resource_options sections) + endif() + + if (x_SYMBOLS) + list(APPEND resource_options symbols) + endif() + + if (x_COMPILEUNITS) + list(APPEND resource_options compileunits) + endif() + + if (DEFINED resource_options) + string(REPLACE ";" "," resource_options "${resource_options}") + set(resource_options -d ${resource_options}) + endif() + + if (DEFINED x_NUM) + list(APPEND resource_options -n ${x_NUM}) + endif() + + if (x_SORT) + list(APPEND resource_options -s ${x_SORT}) + endif() + + if (NOT Bloaty_FOUND) + add_custom_target(${target_name} + COMMENT "bloaty is running on ${target}\ (output: \"${reports_directory}/${output_file}\")" + COMMAND ${CMAKE_COMMAND} -E make_directory ${reports_directory} + COMMAND ${CMAKE_COMMAND} -E chdir ${reports_directory} echo \"bloaty with options: ${resource_options}\" > ${reports_directory}/${output_file} + COMMAND ${CMAKE_COMMAND} -E env $ ${resource_options} $ >> ${reports_directory}/${output_file} + DEPENDS ${target} + ) + else() + add_custom_target(${target_name} + COMMENT "bloaty is running on ${target}\ (output: \"${reports_directory}/${output_file}\")" + COMMAND ${CMAKE_COMMAND} -E make_directory ${reports_directory} + COMMAND ${CMAKE_COMMAND} -E chdir ${reports_directory} echo \"bloaty with options: ${resource_options}\" > ${reports_directory}/${output_file} + COMMAND ${CMAKE_COMMAND} -E env ${Bloaty_EXECUTABLE} ${resource_options} $ >> ${reports_directory}/${output_file} + DEPENDS ${target} + ) + endif() + + if (NOT TARGET do-all-bloaty) + add_custom_target(do-all-bloaty) + endif() + add_dependencies(do-all-bloaty ${target_name}) +endfunction() diff --git a/FindBloaty.cmake b/FindBloaty.cmake new file mode 100644 index 0000000..fc6ddf6 --- /dev/null +++ b/FindBloaty.cmake @@ -0,0 +1,11 @@ +if(NOT Bloaty_FOUND) + + find_program(Bloaty_EXECUTABLE bloaty) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Bloaty DEFAULT_MSG Bloaty_EXECUTABLE) + + set(Bloaty_FOUND ${Bloaty_FOUND} CACHE BOOL "Flag whether Bloaty package was found") + mark_as_advanced(Bloaty_FOUND Bloaty_EXECUTABLE) + +endif() diff --git a/FindHeaptrack.cmake b/FindHeaptrack.cmake new file mode 100644 index 0000000..bb90c31 --- /dev/null +++ b/FindHeaptrack.cmake @@ -0,0 +1,11 @@ +if(NOT Heaptrack_FOUND) + + find_program(Heaptrack_EXECUTABLE NAMES heaptrack) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Heaptrack DEFAULT_MSG Heaptrack_EXECUTABLE) + + set(Heaptrack_FOUND ${Heaptrack_FOUND} CACHE BOOL "Flag whether Heaptrack package was found") + mark_as_advanced(Heaptrack_FOUND Heaptrack_EXECUTABLE) + +endif() diff --git a/FindStackusage.cmake b/FindStackusage.cmake new file mode 100644 index 0000000..f4a23a9 --- /dev/null +++ b/FindStackusage.cmake @@ -0,0 +1,11 @@ +if(NOT Stackusage_FOUND) + + find_program(Stackusage_EXECUTABLE NAMES stackusage) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Stackusage DEFAULT_MSG Stackusage_EXECUTABLE) + + set(Stackusage_FOUND ${Stackusage_FOUND} CACHE BOOL "Flag whether Stackusage package was found") + mark_as_advanced(Stackusage_FOUND Stackusage_EXECUTABLE) + +endif() diff --git a/Heaptrack.cmake b/Heaptrack.cmake new file mode 100644 index 0000000..167289a --- /dev/null +++ b/Heaptrack.cmake @@ -0,0 +1,140 @@ +# +# OVERVIEW +# +# Heaptrack is a dynamic memory profiling tool which measures the heap utilization +# of a target binary and outputs the result to a file. +# +# USAGE +# +# swift_add_heaptrack( +# [NAME name] +# [WORKING_DIRECTORY working_directory] +# [PROGRAM_ARGS arg1 arg2 ...] +# ) +# +# Call this function to create a new cmake target which runs the `target`'s +# executable binary with heaptrack applied. All created cmake targets can be +# invoked by calling the common target 'do-all-heaptrack'. +# +# NAME +# This variable makes it possible to choose a custom name for the target, which +# is useful in situations using Google Test. +# +# WORKING_DIRECTORY +# This variable changes the output directory for the tool from the default folder +# `${CMAKE_BINARY_DIR}/profiling/heaptrack-reports` to the given argument. +# Example, using argument `/tmp`, outputs the results to `/tmp/heaptrack-reports/ +# +# PROGRAM_ARGS +# This variable specifies target arguments. Example, using a yaml-config +# with "--config example.yaml" +# +# NOTE +# +# * Target needs to be run with a config-file. +# * A cmake option is available to control whether targets should be built, +# with the name ${PROJECT_NAME}_ENABLE_MEMORY_PROFILING. +# +# Running +# +# cmake -D_ENABLE_MEMORY_PROFILING=ON .. +# +# will explicitly enable these targets from the command line at configure time +# + +option(${PROJECT_NAME}_ENABLE_MEMORY_PROFILING "Builds targets with memory profiling" OFF) + +find_package(Heaptrack) + +if (NOT Heaptrack_FOUND AND ${PROJECT_NAME}_ENABLE_MEMORY_PROFILING) + message(STATUS "Heaptrack is not installed on system, will fetch content from source") + + cmake_minimum_required(VERSION 3.11.0) + include(FetchContent) + + FetchContent_Declare( + heaptrack + GIT_REPOSITORY https://github.com/KDE/heaptrack.git + GIT_TAG v1.1.0 + GIT_SHALLOW TRUE + ) + FetchContent_GetProperties(heaptrack) + if(NOT heaptrack_POPULATED) + FetchContent_Populate(heaptrack) + set(current_build_test_state ${BUILD_TESTING}) + set(BUILD_TESTING OFF) + add_subdirectory(${heaptrack_SOURCE_DIR} ${heaptrack_BINARY_DIR}) + set(BUILD_TESTING ${current_build_test_state}) + endif() +endif() + +macro(eval_heaptrack_target target) + if (NOT TARGET ${target}) + message(FATAL_ERROR "Specified target \"${target}\" does not exist") + endif() + + get_target_property(target_type ${target} TYPE) + if (NOT target_type STREQUAL EXECUTABLE) + message(FATAL_ERROR "Specified target \"${target}\" must be an executable type to register for profiling with heaptrack") + endif() + + if (NOT ${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME}) + return() + endif() + + if (CMAKE_CROSSCOMPILING) + return() + endif() + + if (NOT ${PROJECT_NAME}_ENABLE_MEMORY_PROFILING) + return() + endif() +endmacro() + +function(swift_add_heaptrack target) + eval_heaptrack_target(${target}) + + set(argOption "") + set(argSingle NAME WORKING_DIRECTORY) + set(argMulti PROGRAM_ARGS) + + cmake_parse_arguments(x "${argOption}" "${argSingle}" "${argMulti}" ${ARGN}) + + if (x_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unparsed arguments ${x_UNPARSED_ARGUMENTS}") + endif() + + set(target_name heaptrack-${target}) + if (x_NAME) + set(target_name heaptrack-${x_NAME}) + endif() + + set(working_directory ${CMAKE_BINARY_DIR}/profiling) + if (x_WORKING_DIRECTORY) + set(working_directory ${x_WORKING_DIRECTORY}) + endif() + set(reports_directory ${working_directory}/heaptrack-reports) + + if (NOT Heaptrack_FOUND) + add_custom_target(${target_name} + COMMENT "heaptrack is running on ${target}\ (output: \"${reports_directory}\")" + COMMAND $(MAKE) + WORKING_DIRECTORY ${heaptrack_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${reports_directory} + COMMAND ${CMAKE_COMMAND} -E chdir ${reports_directory} ${heaptrack_BINARY_DIR}/bin/heaptrack $ ${x_PROGRAM_ARGS} + DEPENDS ${target} + ) + else() + add_custom_target(${target_name} + COMMENT "heaptrack is running on ${target}\ (output: \"${reports_directory}\")" + COMMAND ${CMAKE_COMMAND} -E make_directory ${reports_directory} + COMMAND ${CMAKE_COMMAND} -E chdir ${reports_directory} ${Heaptrack_EXECUTABLE} $ ${x_PROGRAM_ARGS} + DEPENDS ${target} + ) + endif() + + if (NOT TARGET do-all-heaptrack) + add_custom_target(do-all-heaptrack) + endif() + add_dependencies(do-all-heaptrack ${target_name}) +endfunction() diff --git a/Stackusage.cmake b/Stackusage.cmake new file mode 100644 index 0000000..e576f13 --- /dev/null +++ b/Stackusage.cmake @@ -0,0 +1,137 @@ +# +# OVERVIEW +# +# Stackusage is a dynamic memory profiling tool which measures the stack +# utilization of a running target and outputs the result to a file. +# +# USAGE +# +# swift_add_stackusage( +# [NAME name] +# [WORKING_DIRECTORY working_directory] +# [PROGRAM_ARGS arg1 arg2 ...] +# ) +# +# Call this function to create a new cmake target which runs the `target`'s +# executable binary with stackusage applied. All created cmake targets can be +# invoked by calling the common target 'do-all-stackusage'. +# +# NAME +# This variable makes it possible to choose a custom name for the target, which +# is useful in situations using Google Test. +# +# WORKING_DIRECTORY +# This variable changes the output directory for the tool from the default folder +# `${CMAKE_BINARY_DIR}/profiling/stackusage-reports` to the given argument. +# Example, using argument `/tmp`, outputs the results to `/tmp/stackusage-reports/ +# +# PROGRAM_ARGS +# This variable specifies target arguments. Example, using a yaml-config +# with "--config example.yaml" +# +# NOTE +# +# * Target needs to be run with a config-file. +# * A cmake option is available to control whether targets should be built, +# with the name ${PROJECT_NAME}_ENABLE_MEMORY_PROFILING. +# +# Running +# +# cmake -D_ENABLE_MEMORY_PROFILING=ON .. +# +# will explicitly enable these targets from the command line at configure time +# + +option(${PROJECT_NAME}_ENABLE_MEMORY_PROFILING "Builds targets with memory profiling" OFF) + +find_package(Stackusage) + +if (NOT Stackusage_FOUND AND ${PROJECT_NAME}_ENABLE_MEMORY_PROFILING) + message(STATUS "Stackusage is not installed on system, will fetch content from source") + + cmake_minimum_required(VERSION 3.14.0) + include(FetchContent) + + FetchContent_Declare( + stackusage + GIT_REPOSITORY https://github.com/d99kris/stackusage.git + GIT_TAG v1.11 + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(stackusage) +endif() + +macro(eval_stackusage_target target) + if (NOT TARGET ${target}) + message(FATAL_ERROR "Specified target \"${target}\" does not exist") + endif() + + get_target_property(target_type ${target} TYPE) + if (NOT target_type STREQUAL EXECUTABLE) + message(FATAL_ERROR "Specified target \"${target}\" must be an executable type to register for profiling with stackusage") + endif() + + if (NOT ${PROJECT_NAME} STREQUAL ${CMAKE_PROJECT_NAME}) + return() + endif() + + if (CMAKE_CROSSCOMPILING) + return() + endif() + + if (NOT ${PROJECT_NAME}_ENABLE_MEMORY_PROFILING) + return() + endif() +endmacro() + +function(swift_add_stackusage target) + eval_stackusage_target(${target}) + + set(argOption "") + set(argSingle NAME WORKING_DIRECTORY) + set(argMulti PROGRAM_ARGS) + + cmake_parse_arguments(x "${argOption}" "${argSingle}" "${argMulti}" ${ARGN}) + + if (x_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unparsed arguments ${x_UNPARSED_ARGUMENTS}") + endif() + + set(target_name stackusage-${target}) + if (x_NAME) + set(target_name stackusage-${x_NAME}) + endif() + + set(working_directory ${CMAKE_BINARY_DIR}/profiling) + if (x_WORKING_DIRECTORY) + set(working_directory ${x_WORKING_DIRECTORY}) + endif() + set(reports_directory ${working_directory}/stackusage-reports) + set(output_file ${target_name}.txt) + + unset(resource_options) + list(APPEND resource_options -o ${reports_directory}/${output_file}) + + if (NOT Stackusage_FOUND) + add_custom_target(${target_name} + COMMENT "stackusage is running on ${target}\ (output: \"${reports_directory}/${output_file}\")" + COMMAND $(MAKE) + WORKING_DIRECTORY ${stackusage_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${reports_directory} + COMMAND ${CMAKE_COMMAND} -E chdir ${reports_directory} ${stackusage_BINARY_DIR}/stackusage ${resource_options} $ ${x_PROGRAM_ARGS} + DEPENDS ${target} + ) + else() + add_custom_target(${target_name} + COMMENT "stackusage is running on ${target}\ (output: \"${reports_directory}/${output_file}\")" + COMMAND ${CMAKE_COMMAND} -E make_directory ${reports_directory} + COMMAND ${CMAKE_COMMAND} -E chdir ${reports_directory} ${Stackusage_EXECUTABLE} ${resource_options} $ ${x_PROGRAM_ARGS} + DEPENDS ${target} + ) + endif() + + if (NOT TARGET do-all-stackusage) + add_custom_target(do-all-stackusage) + endif() + add_dependencies(do-all-stackusage ${target_name}) +endfunction()