Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ vendor*
.vs
.vscode
docker-sync.yml
/third_party/libbpf
/third_party/bpftool

#==============================================================================#
# Directories to ignore only in root directory
Expand Down
54 changes: 44 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ add_link_options("LINKER:--build-id=sha1")
# Define the include path of cmake scripts
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/util")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/third_party")

include(ExtendBuildTypes)

Expand Down Expand Up @@ -86,16 +87,49 @@ find_package(absl REQUIRED)
# libdatadog_profiling
include(Findlibdatadog)

# ---- Static analysis ----
include(ClangTidy)
include(Format)

# Third parties
add_subdirectory(third_party)

# todo remove hardcode
set(BPFOBJECT_CLANG_EXE "clang-17")

# Path to the bpftool executable
set(BPFOBJECT_BPFTOOL_EXE ${CMAKE_CURRENT_BINARY_DIR}/third_party/bpftool/bootstrap/bpftool)

set(LIBBPF_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/third_party/libbpf)
set(LIBBPF_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/third_party/libbpf/libbpf.a)

# Path where vmlinux.h will be generated
set(BPFOBJECT_VMLINUX_DIR ${CMAKE_CURRENT_BINARY_DIR}/third_party/vmlinux/${ARCH})
set(BPFOBJECT_VMLINUX_H ${BPFOBJECT_VMLINUX_DIR}/vmlinux.h)

# Ensure the target directory exists
file(MAKE_DIRECTORY ${BPFOBJECT_VMLINUX_DIR})

# Custom command to generate vmlinux.h
add_custom_command(
OUTPUT ${BPFOBJECT_VMLINUX_H}
COMMAND ${BPFOBJECT_BPFTOOL_EXE} btf dump file /sys/kernel/btf/vmlinux format c >
${BPFOBJECT_VMLINUX_H}
DEPENDS ${BPFOBJECT_BPFTOOL_EXE} ${BPFOBJECT_VMLINUX_DIR}
COMMENT "Generating vmlinux.h")
add_custom_target(generate_vmlinux_h ALL DEPENDS ${BPFOBJECT_VMLINUX_H} bpftool-build libbpf-build)

find_package(BpfObject REQUIRED)

# bpf
add_subdirectory(src/bpf)

# Event Parser
add_subdirectory(src/event_parser)

# elfutils
include(Findelfutils)

# ---- Static analysis ----
include(ClangTidy)
include(Format)

# Generated code needs to be available for cppcheck to work
include(CppcheckConfig)
add_dependencies(cppcheck DDProf::Parser)
Expand All @@ -112,9 +146,6 @@ endif()
# Install lib cap to retrieve capabilities
include(Findlibcap)

# Third parties
add_subdirectory(third_party)

# ---- Benchmarks ----
option(BUILD_BENCHMARKS "Enable benchmarks" OFF)
if(${BUILD_BENCHMARKS})
Expand All @@ -124,7 +155,7 @@ endif()
# ---- Declaration of DDProf ----
# Compile time definitions
string(TOLOWER ${CMAKE_PROJECT_NAME} CMAKE_PROJECT_NAME_LC)
list(APPEND DDPROF_DEFINITION_LIST "MYNAME=\"${CMAKE_PROJECT_NAME_LC}\"")
list(APPEND DDPROF_DEFINITION_LIST "MYNAME=\"${CMAKE_PROJECT_NAME_LC}\"" "EBPF_UNWINDING")

if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
list(APPEND DDPROF_DEFINITION_LIST "DDPROF_OPTIM=1")
Expand All @@ -136,7 +167,7 @@ include(Version)
string(APPEND CMAKE_C_FLAGS " ${FRAME_PTR_FLAG}")
string(APPEND CMAKE_CXX_FLAGS " ${FRAME_PTR_FLAG}")

list(APPEND DDPROF_INCLUDE_LIST ${CMAKE_SOURCE_DIR}/include)
list(APPEND DDPROF_INCLUDE_LIST ${CMAKE_SOURCE_DIR}/include ${BPFOBJECT_VMLINUX_DIR})

# Find the source files
aux_source_directory(src COMMON_SRC)
Expand Down Expand Up @@ -243,6 +274,7 @@ target_include_directories(dd_profiling-embedded PUBLIC ${CMAKE_SOURCE_DIR}/incl
${CMAKE_SOURCE_DIR}/include)
set_target_properties(dd_profiling-embedded
PROPERTIES PUBLIC_HEADER "${CMAKE_SOURCE_DIR}/include/lib/dd_profiling.h")
# TODO: does EBPF make sense in embedded mode ?

# Link libstdc++/libgcc statically and export only profiler API
target_static_libcxx(dd_profiling-embedded)
Expand Down Expand Up @@ -302,8 +334,10 @@ add_exe(
ddprof ${DDPROF_GLOBAL_SRC}
LIBRARIES ${DDPROF_LIBRARY_LIST}
DEFINITIONS ${DDPROF_DEFINITION_LIST})
add_dependencies(ddprof sample_processor_skel_build)
target_link_libraries(ddprof PRIVATE libddprofiling_embedded_object CLI11 absl::base
absl::str_format)
absl::str_format sample_processor_skel)

if(USE_LOADER)
target_compile_definitions(ddprof PRIVATE "DDPROF_USE_LOADER")
target_link_libraries(ddprof PRIVATE libdd_loader_object)
Expand Down
204 changes: 204 additions & 0 deletions cmake/FindBpfObject.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause

#[=======================================================================[.rst:
FindBpfObject
--------

Find BpfObject

This module finds if all the dependencies for eBPF Compile-Once-Run-Everywhere
programs are available and where all the components are located.

The caller may set the following variables to disable automatic
search/processing for the associated component:

``BPFOBJECT_BPFTOOL_EXE``
Path to ``bpftool`` binary

``BPFOBJECT_CLANG_EXE``
Path to ``clang`` binary

``LIBBPF_INCLUDE_DIRS``
Path to ``libbpf`` development headers

``LIBBPF_LIBRARIES``
Path to `libbpf` library

``BPFOBJECT_VMLINUX_H``
Path to ``vmlinux.h`` generated by ``bpftool``. If unset, this module will
attempt to automatically generate a copy.

This module sets the following result variables:

::

BpfObject_FOUND = TRUE if all components are found


This module also provides the ``bpf_object()`` macro. This macro generates a
cmake interface library for the BPF object's generated skeleton as well
as the associated dependencies.

.. code-block:: cmake

bpf_object(<name> <source>)

Given an abstract ``<name>`` for a BPF object and the associated ``<source>``
file, generates an interface library target, ``<name>_skel``, that may be
linked against by other cmake targets.

Example Usage:

::

find_package(BpfObject REQUIRED)
bpf_object(myobject myobject.bpf.c)
add_executable(myapp myapp.c)
target_link_libraries(myapp myobject_skel)

#]=======================================================================]

if(NOT BPFOBJECT_BPFTOOL_EXE)
find_program(
BPFOBJECT_BPFTOOL_EXE
HINTS ${CMAKE_BINARY_DIR}/third_party/bpftool/bootstrap
NAMES bpftool
DOC "Path to bpftool executable")
endif()

if(NOT BPFOBJECT_CLANG_EXE)
find_program(
BPFOBJECT_CLANG_EXE
NAMES clang
DOC "Path to clang executable")

execute_process(
COMMAND ${BPFOBJECT_CLANG_EXE} --version
OUTPUT_VARIABLE CLANG_version_output
ERROR_VARIABLE CLANG_version_error
RESULT_VARIABLE CLANG_version_result
OUTPUT_STRIP_TRAILING_WHITESPACE)

# Check that clang is new enough
if(${CLANG_version_result} EQUAL 0)
if("${CLANG_version_output}" MATCHES "clang version ([^\n]+)\n")
# Transform X.Y.Z into X;Y;Z which can then be interpreted as a list
set(CLANG_VERSION "${CMAKE_MATCH_1}")
string(REPLACE "." ";" CLANG_VERSION_LIST ${CLANG_VERSION})
list(GET CLANG_VERSION_LIST 0 CLANG_VERSION_MAJOR)

# Anything older than clang 10 doesn't really work
string(COMPARE LESS ${CLANG_VERSION_MAJOR} 10 CLANG_VERSION_MAJOR_LT10)
if(${CLANG_VERSION_MAJOR_LT10})
message(FATAL_ERROR "clang ${CLANG_VERSION} is too old for BPF CO-RE")
endif()

message(STATUS "Found clang version: ${CLANG_VERSION}")
else()
message(FATAL_ERROR "Failed to parse clang version string: ${CLANG_version_output}")
endif()
else()
message(
FATAL_ERROR
"Command \"${BPFOBJECT_CLANG_EXE} --version\" failed with output:\n${CLANG_version_error}")
endif()
endif()

if(NOT LIBBPF_INCLUDE_DIRS OR NOT LIBBPF_LIBRARIES)
find_package(LibBpf)
endif()

if(BPFOBJECT_VMLINUX_H)
get_filename_component(GENERATED_VMLINUX_DIR ${BPFOBJECT_VMLINUX_H} DIRECTORY)
elseif(BPFOBJECT_BPFTOOL_EXE)
# Generate vmlinux.h
set(GENERATED_VMLINUX_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(BPFOBJECT_VMLINUX_H ${GENERATED_VMLINUX_DIR}/vmlinux.h)
execute_process(
COMMAND ${BPFOBJECT_BPFTOOL_EXE} btf dump file /sys/kernel/btf/vmlinux format c
OUTPUT_FILE ${BPFOBJECT_VMLINUX_H}
ERROR_VARIABLE VMLINUX_error
RESULT_VARIABLE VMLINUX_result)
if(${VMLINUX_result} EQUAL 0)
set(VMLINUX ${BPFOBJECT_VMLINUX_H})
else()
message(FATAL_ERROR "Failed to dump vmlinux.h from BTF: ${VMLINUX_error}")
endif()
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
BpfObject REQUIRED_VARS BPFOBJECT_BPFTOOL_EXE BPFOBJECT_CLANG_EXE LIBBPF_INCLUDE_DIRS
LIBBPF_LIBRARIES GENERATED_VMLINUX_DIR)

# Get clang bpf system includes
execute_process(
COMMAND
bash -c
"${BPFOBJECT_CLANG_EXE} -v -E - < /dev/null 2>&1 |
sed -n '/<...> search starts here:/,/End of search list./{ s| \\(/.*\\)|-idirafter \\1|p }'"
OUTPUT_VARIABLE CLANG_SYSTEM_INCLUDES_output
ERROR_VARIABLE CLANG_SYSTEM_INCLUDES_error
RESULT_VARIABLE CLANG_SYSTEM_INCLUDES_result
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(${CLANG_SYSTEM_INCLUDES_result} EQUAL 0)
separate_arguments(CLANG_SYSTEM_INCLUDES UNIX_COMMAND ${CLANG_SYSTEM_INCLUDES_output})
message(STATUS "BPF system include flags: ${CLANG_SYSTEM_INCLUDES}")
else()
message(FATAL_ERROR "Failed to determine BPF system includes: ${CLANG_SYSTEM_INCLUDES_error}")
endif()

# Get target arch
execute_process(
COMMAND uname -m
COMMAND sed -e "s/x86_64/x86/" -e "s/aarch64/arm64/" -e "s/ppc64le/powerpc/" -e "s/mips.*/mips/"
-e "s/riscv64/riscv/"
OUTPUT_VARIABLE ARCH_output
ERROR_VARIABLE ARCH_error
RESULT_VARIABLE ARCH_result
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(${ARCH_result} EQUAL 0)
set(ARCH ${ARCH_output})
message(STATUS "BPF target arch: ${ARCH}")
else()
message(FATAL_ERROR "Failed to determine target architecture: ${ARCH_error}")
endif()

# Public macro
macro(bpf_object name input)
set(BPF_C_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${input})
set(BPF_O_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.bpf.o)
set(BPF_SKEL_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.skel.h)
set(OUTPUT_TARGET ${name}_skel)

message(STATUS "Start of bpf object" ${BPF_C_FILE} - clang = ${BPFOBJECT_CLANG_EXE})

# Build BPF object file
add_custom_command(
OUTPUT ${BPF_O_FILE}
COMMAND
${BPFOBJECT_CLANG_EXE} -g -O2 -target bpf -D__TARGET_ARCH_${ARCH} ${CLANG_SYSTEM_INCLUDES}
-I${GENERATED_VMLINUX_DIR} -I${CMAKE_SOURCE_DIR}/include -isystem ${LIBBPF_INCLUDE_DIRS} -c
${BPF_C_FILE} -o ${BPF_O_FILE}
COMMAND_EXPAND_LISTS VERBATIM
DEPENDS ${BPF_C_FILE}
COMMENT "[clang] Building BPF object: ${name}")

message(STATUS "Output = ${BPF_O_FILE} target = ${OUTPUT_TARGET}")

# Build BPF skeleton header
add_custom_command(
OUTPUT ${BPF_SKEL_FILE}
COMMAND bash -c "${BPFOBJECT_BPFTOOL_EXE} gen skeleton ${BPF_O_FILE} > ${BPF_SKEL_FILE}"
VERBATIM
DEPENDS ${BPF_O_FILE}
COMMENT "[skel] Building BPF skeleton: ${name}")

add_custom_target(${OUTPUT_TARGET}_build ALL DEPENDS ${BPF_O_FILE} ${BPF_SKEL_FILE})

add_library(${OUTPUT_TARGET} INTERFACE)
target_sources(${OUTPUT_TARGET} INTERFACE ${BPF_SKEL_FILE})
target_include_directories(${OUTPUT_TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(${OUTPUT_TARGET} SYSTEM INTERFACE ${LIBBPF_INCLUDE_DIRS})
target_link_libraries(${OUTPUT_TARGET} INTERFACE ${LIBBPF_LIBRARIES} -lelf -lz)
endmacro()
25 changes: 25 additions & 0 deletions cmake/FindLibBpf.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause

find_path(
LIBBPF_INCLUDE_DIRS
NAMES bpf/bpf.h bpf/btf.h bpf/libbpf.h
PATHS /usr/include /usr/local/include /opt/local/include /sw/include ENV CPATH)

find_library(
LIBBPF_LIBRARIES
NAMES bpf
PATHS /usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib
ENV
LIBRARY_PATH
ENV
LD_LIBRARY_PATH)

include(FindPackageHandleStandardArgs)

find_package_handle_standard_args(LibBpf "Please install the libbpf development package"
LIBBPF_LIBRARIES LIBBPF_INCLUDE_DIRS)

mark_as_advanced(LIBBPF_INCLUDE_DIRS LIBBPF_LIBRARIES)
26 changes: 26 additions & 0 deletions docs/eBPF_unwinding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# eBPF unwinding

## Motivation

The native profiler was initially designed on top of perf event open.
The capture of every sample causes 32kB of memory to be copied at every sample.

What would it mean to have an eBPF unwinding instead ?

## Build

Thie build does not work well
Manual target generations are still necessary
```bash
make libbpf-build
make bpftool-build
make generate_vmlinux_h
```

## Current state

The proposal is to attach a BPF sample processor

## Acknowledgements

This mostly reuses the great tools provided by libbpf.
4 changes: 4 additions & 0 deletions include/base_frame_symbol_lookup.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class BaseFrameSymbolLookup {
DsoSymbolLookup &dso_symbol_lookup,
DsoHdr &dso_hdr);

SymbolIdx_t get_or_insert_comm(const char*comm, SymbolTable &symbol_table);

// Erase symbol lookup for this pid (warning symbols still exist)
void erase(pid_t pid) {
_bin_map.erase(pid);
Expand All @@ -36,6 +38,8 @@ class BaseFrameSymbolLookup {
// holds generic symbol for this pid and a number of lookups to keep track of
// failures looking for a given binary
std::unordered_map<pid_t, PidSymbol> _pid_map;

std::unordered_map<std::string, SymbolIdx_t> _comm_map;
};

} // namespace ddprof
Loading