From 4121a0430fd11646a9b04c97449c715172f4a493 Mon Sep 17 00:00:00 2001 From: Vadim Skipin Date: Thu, 7 May 2026 17:26:20 +0000 Subject: [PATCH 1/2] Bundle systemtap-sdt library --- CMakeLists.txt | 1 + contrib/systemtap-sdt/CMakeLists.txt | 12 + contrib/systemtap-sdt/sys/sdt-config.h | 6 + contrib/systemtap-sdt/sys/sdt.h | 509 +++++++++++++++++++++++++ 4 files changed, 528 insertions(+) create mode 100644 contrib/systemtap-sdt/CMakeLists.txt create mode 100644 contrib/systemtap-sdt/sys/sdt-config.h create mode 100644 contrib/systemtap-sdt/sys/sdt.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0381e9a..90b7ab5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,7 @@ add_subdirectory(contrib/librseq-cmake) add_subdirectory(contrib/libbacktrace-cmake) add_subdirectory(contrib/googletest-cmake) add_subdirectory(contrib/benchmark-cmake) +add_subdirectory(contrib/systemtap-sdt) option(BUILD_POCO "Build Poco library (required by http-perf)" OFF) if(BUILD_POCO) diff --git a/contrib/systemtap-sdt/CMakeLists.txt b/contrib/systemtap-sdt/CMakeLists.txt new file mode 100644 index 0000000..544b9c2 --- /dev/null +++ b/contrib/systemtap-sdt/CMakeLists.txt @@ -0,0 +1,12 @@ +# Vendored copy of systemtap's + , the two +# header files needed to emit USDT (Statically Defined Tracepoint) probes. +# Both files are dedicated to the public domain (CC0) by upstream specifically +# so user programs incorporating them are not GPL-tainted; no further attribution +# or LICENSE artifact is required. +# +# We vendor instead of relying on the systemtap-sdt-dev distro package so that +# the silk build has zero extra apt dependencies for USDT. + +add_library(systemtap-sdt INTERFACE) +target_include_directories(systemtap-sdt INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +add_library(SystemTap::Sdt ALIAS systemtap-sdt) diff --git a/contrib/systemtap-sdt/sys/sdt-config.h b/contrib/systemtap-sdt/sys/sdt-config.h new file mode 100644 index 0000000..733045a --- /dev/null +++ b/contrib/systemtap-sdt/sys/sdt-config.h @@ -0,0 +1,6 @@ +/* includes/sys/sdt-config.h. Generated from sdt-config.h.in by configure. + + This file just defines _SDT_ASM_SECTION_AUTOGROUP_SUPPORT to 0 or 1 to + indicate whether the assembler supports "?" in .pushsection directives. */ + +#define _SDT_ASM_SECTION_AUTOGROUP_SUPPORT 1 diff --git a/contrib/systemtap-sdt/sys/sdt.h b/contrib/systemtap-sdt/sys/sdt.h new file mode 100644 index 0000000..b9b4f53 --- /dev/null +++ b/contrib/systemtap-sdt/sys/sdt.h @@ -0,0 +1,509 @@ +/* - Systemtap static probe definition macros. + + This file is dedicated to the public domain, pursuant to CC0 + (https://creativecommons.org/publicdomain/zero/1.0/) +*/ + +#ifndef _SYS_SDT_H +#define _SYS_SDT_H 1 + +/* + This file defines a family of macros + + STAP_PROBEn(op1, ..., opn) + + that emit a nop into the instruction stream, and some data into an auxiliary + note section. The data in the note section describes the operands, in terms + of size and location. Each location is encoded as assembler operand string. + Consumer tools such as gdb or systemtap insert breakpoints on top of + the nop, and decode the location operand-strings, like an assembler, + to find the values being passed. + + The operand strings are selected by the compiler for each operand. + They are constrained by gcc inline-assembler codes. The default is: + + #define STAP_SDT_ARG_CONSTRAINT nor + + This is a good default if the operands tend to be integral and + moderate in number (smaller than number of registers). In other + cases, the compiler may report "'asm' requires impossible reload" or + similar. In this case, consider simplifying the macro call (fewer + and simpler operands), reduce optimization, or override the default + constraints string via: + + #define STAP_SDT_ARG_CONSTRAINT g + #include + + See also: + https://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation + https://gcc.gnu.org/onlinedocs/gcc/Constraints.html + */ + + + +#ifdef __ASSEMBLER__ +# define _SDT_PROBE(provider, name, n, arglist) \ + _SDT_ASM_BODY(provider, name, _SDT_ASM_SUBSTR_1, (_SDT_DEPAREN_##n arglist)) \ + _SDT_ASM_BASE +# define _SDT_ASM_1(x) x; +# define _SDT_ASM_2(a, b) a,b; +# define _SDT_ASM_3(a, b, c) a,b,c; +# define _SDT_ASM_5(a, b, c, d, e) a,b,c,d,e; +# define _SDT_ASM_STRING_1(x) .asciz #x; +# define _SDT_ASM_SUBSTR_1(x) .ascii #x; +# define _SDT_DEPAREN_0() /* empty */ +# define _SDT_DEPAREN_1(a) a +# define _SDT_DEPAREN_2(a,b) a b +# define _SDT_DEPAREN_3(a,b,c) a b c +# define _SDT_DEPAREN_4(a,b,c,d) a b c d +# define _SDT_DEPAREN_5(a,b,c,d,e) a b c d e +# define _SDT_DEPAREN_6(a,b,c,d,e,f) a b c d e f +# define _SDT_DEPAREN_7(a,b,c,d,e,f,g) a b c d e f g +# define _SDT_DEPAREN_8(a,b,c,d,e,f,g,h) a b c d e f g h +# define _SDT_DEPAREN_9(a,b,c,d,e,f,g,h,i) a b c d e f g h i +# define _SDT_DEPAREN_10(a,b,c,d,e,f,g,h,i,j) a b c d e f g h i j +# define _SDT_DEPAREN_11(a,b,c,d,e,f,g,h,i,j,k) a b c d e f g h i j k +# define _SDT_DEPAREN_12(a,b,c,d,e,f,g,h,i,j,k,l) a b c d e f g h i j k l +#else +#if defined _SDT_HAS_SEMAPHORES +#define _SDT_NOTE_SEMAPHORE_USE(provider, name) \ + __asm__ __volatile__ ("" :: "m" (provider##_##name##_semaphore)); +#else +#define _SDT_NOTE_SEMAPHORE_USE(provider, name) +#endif + +# define _SDT_PROBE(provider, name, n, arglist) \ + do { \ + _SDT_NOTE_SEMAPHORE_USE(provider, name); \ + __asm__ __volatile__ (_SDT_ASM_BODY(provider, name, _SDT_ASM_ARGS, (n)) \ + :: _SDT_ASM_OPERANDS_##n arglist); \ + __asm__ __volatile__ (_SDT_ASM_BASE); \ + } while (0) +# define _SDT_S(x) #x +# define _SDT_ASM_1(x) _SDT_S(x) "\n" +# define _SDT_ASM_2(a, b) _SDT_S(a) "," _SDT_S(b) "\n" +# define _SDT_ASM_3(a, b, c) _SDT_S(a) "," _SDT_S(b) "," \ + _SDT_S(c) "\n" +# define _SDT_ASM_5(a, b, c, d, e) _SDT_S(a) "," _SDT_S(b) "," \ + _SDT_S(c) "," _SDT_S(d) "," \ + _SDT_S(e) "\n" +# define _SDT_ASM_ARGS(n) _SDT_ASM_TEMPLATE_##n +# define _SDT_ASM_STRING_1(x) _SDT_ASM_1(.asciz #x) +# define _SDT_ASM_SUBSTR_1(x) _SDT_ASM_1(.ascii #x) + +# define _SDT_ARGFMT(no) _SDT_ASM_1(_SDT_SIGN %n[_SDT_S##no]) \ + _SDT_ASM_1(_SDT_SIZE %n[_SDT_S##no]) \ + _SDT_ASM_1(_SDT_TYPE %n[_SDT_S##no]) \ + _SDT_ASM_SUBSTR(_SDT_ARGTMPL(_SDT_A##no)) + + +# ifndef STAP_SDT_ARG_CONSTRAINT +# if defined __powerpc__ +# define STAP_SDT_ARG_CONSTRAINT nZr +# elif defined __ia64__ +# define STAP_SDT_ARG_CONSTRAINT nr +# elif defined __arm__ +# define STAP_SDT_ARG_CONSTRAINT g +# else +# define STAP_SDT_ARG_CONSTRAINT nor +# endif +# endif + +# define _SDT_STRINGIFY(x) #x +# define _SDT_ARG_CONSTRAINT_STRING(x) _SDT_STRINGIFY(x) +/* _SDT_S encodes the size and type as 0xSSTT which is decoded by the assembler + macros _SDT_SIZE and _SDT_TYPE */ +# define _SDT_ARG(n, x) \ + [_SDT_S##n] "n" ((_SDT_ARGSIGNED (x) ? (int)-1 : 1) * (-(((int) _SDT_ARGSIZE (x)) << 8) + (-(0x7f & __builtin_classify_type (x))))), \ + [_SDT_A##n] _SDT_ARG_CONSTRAINT_STRING (STAP_SDT_ARG_CONSTRAINT) (_SDT_ARGVAL (x)) +#endif +#define _SDT_ASM_STRING(x) _SDT_ASM_STRING_1(x) +#define _SDT_ASM_SUBSTR(x) _SDT_ASM_SUBSTR_1(x) + +#define _SDT_ARGARRAY(x) (__builtin_classify_type (x) == 14 \ + || __builtin_classify_type (x) == 5) + +#ifdef __cplusplus +# define _SDT_ARGSIGNED(x) (!_SDT_ARGARRAY (x) \ + && __sdt_type<__typeof (x)>::__sdt_signed) +# define _SDT_ARGSIZE(x) (_SDT_ARGARRAY (x) \ + ? sizeof (void *) : sizeof (x)) +# define _SDT_ARGVAL(x) (x) + +# include + +template +struct __sdt_type +{ + static const bool __sdt_signed = false; +}; + +#define __SDT_ALWAYS_SIGNED(T) \ +template<> struct __sdt_type { static const bool __sdt_signed = true; }; +#define __SDT_COND_SIGNED(T,CT) \ +template<> struct __sdt_type { static const bool __sdt_signed = ((CT)(-1) < 1); }; +__SDT_ALWAYS_SIGNED(signed char) +__SDT_ALWAYS_SIGNED(short) +__SDT_ALWAYS_SIGNED(int) +__SDT_ALWAYS_SIGNED(long) +__SDT_ALWAYS_SIGNED(long long) +__SDT_ALWAYS_SIGNED(volatile signed char) +__SDT_ALWAYS_SIGNED(volatile short) +__SDT_ALWAYS_SIGNED(volatile int) +__SDT_ALWAYS_SIGNED(volatile long) +__SDT_ALWAYS_SIGNED(volatile long long) +__SDT_ALWAYS_SIGNED(const signed char) +__SDT_ALWAYS_SIGNED(const short) +__SDT_ALWAYS_SIGNED(const int) +__SDT_ALWAYS_SIGNED(const long) +__SDT_ALWAYS_SIGNED(const long long) +__SDT_ALWAYS_SIGNED(const volatile signed char) +__SDT_ALWAYS_SIGNED(const volatile short) +__SDT_ALWAYS_SIGNED(const volatile int) +__SDT_ALWAYS_SIGNED(const volatile long) +__SDT_ALWAYS_SIGNED(const volatile long long) +__SDT_COND_SIGNED(char, char) +__SDT_COND_SIGNED(wchar_t, wchar_t) +__SDT_COND_SIGNED(volatile char, char) +__SDT_COND_SIGNED(volatile wchar_t, wchar_t) +__SDT_COND_SIGNED(const char, char) +__SDT_COND_SIGNED(const wchar_t, wchar_t) +__SDT_COND_SIGNED(const volatile char, char) +__SDT_COND_SIGNED(const volatile wchar_t, wchar_t) +#if defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) +/* __SDT_COND_SIGNED(char16_t) */ +/* __SDT_COND_SIGNED(char32_t) */ +#endif + +template +struct __sdt_type<__sdt_E[]> : public __sdt_type<__sdt_E *> {}; + +template +struct __sdt_type<__sdt_E[__sdt_N]> : public __sdt_type<__sdt_E *> {}; + +#elif !defined(__ASSEMBLER__) +__extension__ extern unsigned long long __sdt_unsp; +# define _SDT_ARGINTTYPE(x) \ + __typeof (__builtin_choose_expr (((__builtin_classify_type (x) \ + + 3) & -4) == 4, (x), 0U)) +# define _SDT_ARGSIGNED(x) \ + (!__extension__ \ + (__builtin_constant_p ((((unsigned long long) \ + (_SDT_ARGINTTYPE (x)) __sdt_unsp) \ + & ((unsigned long long)1 << (sizeof (unsigned long long) \ + * __CHAR_BIT__ - 1))) == 0) \ + || (_SDT_ARGINTTYPE (x)) -1 > (_SDT_ARGINTTYPE (x)) 0)) +# define _SDT_ARGSIZE(x) \ + (_SDT_ARGARRAY (x) ? sizeof (void *) : sizeof (x)) +# define _SDT_ARGVAL(x) (x) +#endif + +#if defined __powerpc__ || defined __powerpc64__ +# define _SDT_ARGTMPL(id) %I[id]%[id] +#elif defined __i386__ +# define _SDT_ARGTMPL(id) %k[id] /* gcc.gnu.org/PR80115 sourceware.org/PR24541 */ +#else +# define _SDT_ARGTMPL(id) %[id] +#endif + +/* NB: gdb PR24541 highlighted an unspecified corner of the sdt.h + operand note format. + + The named register may be a longer or shorter (!) alias for the + storage where the value in question is found. For example, on + i386, 64-bit value may be put in register pairs, and the register + name stored would identify just one of them. Previously, gcc was + asked to emit the %w[id] (16-bit alias of some registers holding + operands), even when a wider 32-bit value was used. + + Bottom line: the byte-width given before the @ sign governs. If + there is a mismatch between that width and that of the named + register, then a sys/sdt.h note consumer may need to employ + architecture-specific heuristics to figure out where the compiler + has actually put the complete value. +*/ + +#ifdef __LP64__ +# define _SDT_ASM_ADDR .8byte +#else +# define _SDT_ASM_ADDR .4byte +#endif + +/* The ia64 and s390 nop instructions take an argument. */ +#if defined(__ia64__) || defined(__s390__) || defined(__s390x__) +#define _SDT_NOP nop 0 +#else +#define _SDT_NOP nop +#endif + +#define _SDT_NOTE_NAME "stapsdt" +#define _SDT_NOTE_TYPE 3 + +/* If the assembler supports the necessary feature, then we can play + nice with code in COMDAT sections, which comes up in C++ code. + Without that assembler support, some combinations of probe placements + in certain kinds of C++ code may produce link-time errors. */ +#include "sdt-config.h" +#if _SDT_ASM_SECTION_AUTOGROUP_SUPPORT +# define _SDT_ASM_AUTOGROUP "?" +#else +# define _SDT_ASM_AUTOGROUP "" +#endif + +#define _SDT_DEF_MACROS \ + _SDT_ASM_1(.altmacro) \ + _SDT_ASM_1(.macro _SDT_SIGN x) \ + _SDT_ASM_1(.iflt \\x) \ + _SDT_ASM_1(.ascii "-") \ + _SDT_ASM_1(.endif) \ + _SDT_ASM_1(.endm) \ + _SDT_ASM_1(.macro _SDT_SIZE_ x) \ + _SDT_ASM_1(.ascii "\x") \ + _SDT_ASM_1(.endm) \ + _SDT_ASM_1(.macro _SDT_SIZE x) \ + _SDT_ASM_1(_SDT_SIZE_ %%((-(-\\x*((-\\x>0)-(-\\x<0))))>>8)) \ + _SDT_ASM_1(.endm) \ + _SDT_ASM_1(.macro _SDT_TYPE_ x) \ + _SDT_ASM_2(.ifc 8,\\x) \ + _SDT_ASM_1(.ascii "f") \ + _SDT_ASM_1(.endif) \ + _SDT_ASM_1(.ascii "@") \ + _SDT_ASM_1(.endm) \ + _SDT_ASM_1(.macro _SDT_TYPE x) \ + _SDT_ASM_1(_SDT_TYPE_ %%((\\x)&(0xff))) \ + _SDT_ASM_1(.endm) + +#define _SDT_UNDEF_MACROS \ + _SDT_ASM_1(.purgem _SDT_SIGN) \ + _SDT_ASM_1(.purgem _SDT_SIZE_) \ + _SDT_ASM_1(.purgem _SDT_SIZE) \ + _SDT_ASM_1(.purgem _SDT_TYPE_) \ + _SDT_ASM_1(.purgem _SDT_TYPE) + +#define _SDT_ASM_BODY(provider, name, pack_args, args, ...) \ + _SDT_DEF_MACROS \ + _SDT_ASM_1(990: _SDT_NOP) \ + _SDT_ASM_3( .pushsection .note.stapsdt,_SDT_ASM_AUTOGROUP,"note") \ + _SDT_ASM_1( .balign 4) \ + _SDT_ASM_3( .4byte 992f-991f, 994f-993f, _SDT_NOTE_TYPE) \ + _SDT_ASM_1(991: .asciz _SDT_NOTE_NAME) \ + _SDT_ASM_1(992: .balign 4) \ + _SDT_ASM_1(993: _SDT_ASM_ADDR 990b) \ + _SDT_ASM_1( _SDT_ASM_ADDR _.stapsdt.base) \ + _SDT_SEMAPHORE(provider,name) \ + _SDT_ASM_STRING(provider) \ + _SDT_ASM_STRING(name) \ + pack_args args \ + _SDT_ASM_SUBSTR(\x00) \ + _SDT_UNDEF_MACROS \ + _SDT_ASM_1(994: .balign 4) \ + _SDT_ASM_1( .popsection) + +#define _SDT_ASM_BASE \ + _SDT_ASM_1(.ifndef _.stapsdt.base) \ + _SDT_ASM_5( .pushsection .stapsdt.base,"aG","progbits", \ + .stapsdt.base,comdat) \ + _SDT_ASM_1( .weak _.stapsdt.base) \ + _SDT_ASM_1( .hidden _.stapsdt.base) \ + _SDT_ASM_1( _.stapsdt.base: .space 1) \ + _SDT_ASM_2( .size _.stapsdt.base, 1) \ + _SDT_ASM_1( .popsection) \ + _SDT_ASM_1(.endif) + +#if defined _SDT_HAS_SEMAPHORES +#define _SDT_SEMAPHORE(p,n) \ + _SDT_ASM_1( _SDT_ASM_ADDR p##_##n##_semaphore) +#else +#define _SDT_SEMAPHORE(p,n) _SDT_ASM_1( _SDT_ASM_ADDR 0) +#endif + +#define _SDT_ASM_BLANK _SDT_ASM_SUBSTR(\x20) +#define _SDT_ASM_TEMPLATE_0 /* no arguments */ +#define _SDT_ASM_TEMPLATE_1 _SDT_ARGFMT(1) +#define _SDT_ASM_TEMPLATE_2 _SDT_ASM_TEMPLATE_1 _SDT_ASM_BLANK _SDT_ARGFMT(2) +#define _SDT_ASM_TEMPLATE_3 _SDT_ASM_TEMPLATE_2 _SDT_ASM_BLANK _SDT_ARGFMT(3) +#define _SDT_ASM_TEMPLATE_4 _SDT_ASM_TEMPLATE_3 _SDT_ASM_BLANK _SDT_ARGFMT(4) +#define _SDT_ASM_TEMPLATE_5 _SDT_ASM_TEMPLATE_4 _SDT_ASM_BLANK _SDT_ARGFMT(5) +#define _SDT_ASM_TEMPLATE_6 _SDT_ASM_TEMPLATE_5 _SDT_ASM_BLANK _SDT_ARGFMT(6) +#define _SDT_ASM_TEMPLATE_7 _SDT_ASM_TEMPLATE_6 _SDT_ASM_BLANK _SDT_ARGFMT(7) +#define _SDT_ASM_TEMPLATE_8 _SDT_ASM_TEMPLATE_7 _SDT_ASM_BLANK _SDT_ARGFMT(8) +#define _SDT_ASM_TEMPLATE_9 _SDT_ASM_TEMPLATE_8 _SDT_ASM_BLANK _SDT_ARGFMT(9) +#define _SDT_ASM_TEMPLATE_10 _SDT_ASM_TEMPLATE_9 _SDT_ASM_BLANK _SDT_ARGFMT(10) +#define _SDT_ASM_TEMPLATE_11 _SDT_ASM_TEMPLATE_10 _SDT_ASM_BLANK _SDT_ARGFMT(11) +#define _SDT_ASM_TEMPLATE_12 _SDT_ASM_TEMPLATE_11 _SDT_ASM_BLANK _SDT_ARGFMT(12) +#define _SDT_ASM_OPERANDS_0() [__sdt_dummy] "g" (0) +#define _SDT_ASM_OPERANDS_1(arg1) _SDT_ARG(1, arg1) +#define _SDT_ASM_OPERANDS_2(arg1, arg2) \ + _SDT_ASM_OPERANDS_1(arg1), _SDT_ARG(2, arg2) +#define _SDT_ASM_OPERANDS_3(arg1, arg2, arg3) \ + _SDT_ASM_OPERANDS_2(arg1, arg2), _SDT_ARG(3, arg3) +#define _SDT_ASM_OPERANDS_4(arg1, arg2, arg3, arg4) \ + _SDT_ASM_OPERANDS_3(arg1, arg2, arg3), _SDT_ARG(4, arg4) +#define _SDT_ASM_OPERANDS_5(arg1, arg2, arg3, arg4, arg5) \ + _SDT_ASM_OPERANDS_4(arg1, arg2, arg3, arg4), _SDT_ARG(5, arg5) +#define _SDT_ASM_OPERANDS_6(arg1, arg2, arg3, arg4, arg5, arg6) \ + _SDT_ASM_OPERANDS_5(arg1, arg2, arg3, arg4, arg5), _SDT_ARG(6, arg6) +#define _SDT_ASM_OPERANDS_7(arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ + _SDT_ASM_OPERANDS_6(arg1, arg2, arg3, arg4, arg5, arg6), _SDT_ARG(7, arg7) +#define _SDT_ASM_OPERANDS_8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \ + _SDT_ASM_OPERANDS_7(arg1, arg2, arg3, arg4, arg5, arg6, arg7), \ + _SDT_ARG(8, arg8) +#define _SDT_ASM_OPERANDS_9(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9) \ + _SDT_ASM_OPERANDS_8(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8), \ + _SDT_ARG(9, arg9) +#define _SDT_ASM_OPERANDS_10(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10) \ + _SDT_ASM_OPERANDS_9(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9), \ + _SDT_ARG(10, arg10) +#define _SDT_ASM_OPERANDS_11(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11) \ + _SDT_ASM_OPERANDS_10(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10), \ + _SDT_ARG(11, arg11) +#define _SDT_ASM_OPERANDS_12(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12) \ + _SDT_ASM_OPERANDS_11(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11), \ + _SDT_ARG(12, arg12) + +/* These macros can be used in C, C++, or assembly code. + In assembly code the arguments should use normal assembly operand syntax. */ + +#define STAP_PROBE(provider, name) \ + _SDT_PROBE(provider, name, 0, ()) +#define STAP_PROBE1(provider, name, arg1) \ + _SDT_PROBE(provider, name, 1, (arg1)) +#define STAP_PROBE2(provider, name, arg1, arg2) \ + _SDT_PROBE(provider, name, 2, (arg1, arg2)) +#define STAP_PROBE3(provider, name, arg1, arg2, arg3) \ + _SDT_PROBE(provider, name, 3, (arg1, arg2, arg3)) +#define STAP_PROBE4(provider, name, arg1, arg2, arg3, arg4) \ + _SDT_PROBE(provider, name, 4, (arg1, arg2, arg3, arg4)) +#define STAP_PROBE5(provider, name, arg1, arg2, arg3, arg4, arg5) \ + _SDT_PROBE(provider, name, 5, (arg1, arg2, arg3, arg4, arg5)) +#define STAP_PROBE6(provider, name, arg1, arg2, arg3, arg4, arg5, arg6) \ + _SDT_PROBE(provider, name, 6, (arg1, arg2, arg3, arg4, arg5, arg6)) +#define STAP_PROBE7(provider, name, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ + _SDT_PROBE(provider, name, 7, (arg1, arg2, arg3, arg4, arg5, arg6, arg7)) +#define STAP_PROBE8(provider,name,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8) \ + _SDT_PROBE(provider, name, 8, (arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8)) +#define STAP_PROBE9(provider,name,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)\ + _SDT_PROBE(provider, name, 9, (arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9)) +#define STAP_PROBE10(provider,name,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10) \ + _SDT_PROBE(provider, name, 10, \ + (arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)) +#define STAP_PROBE11(provider,name,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11) \ + _SDT_PROBE(provider, name, 11, \ + (arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11)) +#define STAP_PROBE12(provider,name,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12) \ + _SDT_PROBE(provider, name, 12, \ + (arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11,arg12)) + +/* This STAP_PROBEV macro can be used in variadic scenarios, where the + number of probe arguments is not known until compile time. Since + variadic macro support may vary with compiler options, you must + pre-#define SDT_USE_VARIADIC to enable this type of probe. + + The trick to count __VA_ARGS__ was inspired by this post by + Laurent Deniau : + http://groups.google.com/group/comp.std.c/msg/346fc464319b1ee5 + + Note that our _SDT_NARG is called with an extra 0 arg that's not + counted, so we don't have to worry about the behavior of macros + called without any arguments. */ + +#define _SDT_NARG(...) __SDT_NARG(__VA_ARGS__, 12,11,10,9,8,7,6,5,4,3,2,1,0) +#define __SDT_NARG(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12, N, ...) N +#ifdef SDT_USE_VARIADIC +#define _SDT_PROBE_N(provider, name, N, ...) \ + _SDT_PROBE(provider, name, N, (__VA_ARGS__)) +#define STAP_PROBEV(provider, name, ...) \ + _SDT_PROBE_N(provider, name, _SDT_NARG(0, ##__VA_ARGS__), ##__VA_ARGS__) +#endif + +/* These macros are for use in asm statements. You must compile + with -std=gnu99 or -std=c99 to use the STAP_PROBE_ASM macro. + + The STAP_PROBE_ASM macro generates a quoted string to be used in the + template portion of the asm statement, concatenated with strings that + contain the actual assembly code around the probe site. + + For example: + + asm ("before\n" + STAP_PROBE_ASM(provider, fooprobe, %eax 4(%esi)) + "after"); + + emits the assembly code for "before\nafter", with a probe in between. + The probe arguments are the %eax register, and the value of the memory + word located 4 bytes past the address in the %esi register. Note that + because this is a simple asm, not a GNU C extended asm statement, these + % characters do not need to be doubled to generate literal %reg names. + + In a GNU C extended asm statement, the probe arguments can be specified + using the macro STAP_PROBE_ASM_TEMPLATE(n) for n arguments. The paired + macro STAP_PROBE_ASM_OPERANDS gives the C values of these probe arguments, + and appears in the input operand list of the asm statement. For example: + + asm ("someinsn %0,%1\n" // %0 is output operand, %1 is input operand + STAP_PROBE_ASM(provider, fooprobe, STAP_PROBE_ASM_TEMPLATE(3)) + "otherinsn %[namedarg]" + : "r" (outvar) + : "g" (some_value), [namedarg] "i" (1234), + STAP_PROBE_ASM_OPERANDS(3, some_value, some_ptr->field, 1234)); + + This is just like writing: + + STAP_PROBE3(provider, fooprobe, some_value, some_ptr->field, 1234)); + + but the probe site is right between "someinsn" and "otherinsn". + + The probe arguments in STAP_PROBE_ASM can be given as assembly + operands instead, even inside a GNU C extended asm statement. + Note that these can use operand templates like %0 or %[name], + and likewise they must write %%reg for a literal operand of %reg. */ + +#define _SDT_ASM_BODY_1(p,n,...) _SDT_ASM_BODY(p,n,_SDT_ASM_SUBSTR,(__VA_ARGS__)) +#define _SDT_ASM_BODY_2(p,n,...) _SDT_ASM_BODY(p,n,/*_SDT_ASM_STRING */,__VA_ARGS__) +#define _SDT_ASM_BODY_N2(p,n,no,...) _SDT_ASM_BODY_ ## no(p,n,__VA_ARGS__) +#define _SDT_ASM_BODY_N1(p,n,no,...) _SDT_ASM_BODY_N2(p,n,no,__VA_ARGS__) +#define _SDT_ASM_BODY_N(p,n,...) _SDT_ASM_BODY_N1(p,n,_SDT_NARG(0, __VA_ARGS__),__VA_ARGS__) + +#if __STDC_VERSION__ >= 199901L +# define STAP_PROBE_ASM(provider, name, ...) \ + _SDT_ASM_BODY_N(provider, name, __VA_ARGS__) \ + _SDT_ASM_BASE +# define STAP_PROBE_ASM_OPERANDS(n, ...) _SDT_ASM_OPERANDS_##n(__VA_ARGS__) +#else +# define STAP_PROBE_ASM(provider, name, args) \ + _SDT_ASM_BODY(provider, name, /* _SDT_ASM_STRING */, (args)) \ + _SDT_ASM_BASE +#endif +#define STAP_PROBE_ASM_TEMPLATE(n) _SDT_ASM_TEMPLATE_##n,"use _SDT_ASM_TEMPLATE_" + + +/* DTrace compatible macro names. */ +#define DTRACE_PROBE(provider,probe) \ + STAP_PROBE(provider,probe) +#define DTRACE_PROBE1(provider,probe,parm1) \ + STAP_PROBE1(provider,probe,parm1) +#define DTRACE_PROBE2(provider,probe,parm1,parm2) \ + STAP_PROBE2(provider,probe,parm1,parm2) +#define DTRACE_PROBE3(provider,probe,parm1,parm2,parm3) \ + STAP_PROBE3(provider,probe,parm1,parm2,parm3) +#define DTRACE_PROBE4(provider,probe,parm1,parm2,parm3,parm4) \ + STAP_PROBE4(provider,probe,parm1,parm2,parm3,parm4) +#define DTRACE_PROBE5(provider,probe,parm1,parm2,parm3,parm4,parm5) \ + STAP_PROBE5(provider,probe,parm1,parm2,parm3,parm4,parm5) +#define DTRACE_PROBE6(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6) \ + STAP_PROBE6(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6) +#define DTRACE_PROBE7(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7) \ + STAP_PROBE7(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7) +#define DTRACE_PROBE8(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8) \ + STAP_PROBE8(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8) +#define DTRACE_PROBE9(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9) \ + STAP_PROBE9(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9) +#define DTRACE_PROBE10(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9,parm10) \ + STAP_PROBE10(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9,parm10) +#define DTRACE_PROBE11(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9,parm10,parm11) \ + STAP_PROBE11(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9,parm10,parm11) +#define DTRACE_PROBE12(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9,parm10,parm11,parm12) \ + STAP_PROBE12(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9,parm10,parm11,parm12) + + +#endif /* sys/sdt.h */ From 5d1177d7e46acc29f91815741d3fe0cc742a102e Mon Sep 17 00:00:00 2001 From: Vadim Skipin Date: Fri, 8 May 2026 07:30:31 +0000 Subject: [PATCH 2/2] Bundle libbpf/bpftool --- .github/workflows/ci.yml | 19 +++++----- .gitmodules | 6 +++ CMakeLists.txt | 2 + contrib/bpftool | 1 + contrib/bpftool-cmake/CMakeLists.txt | 47 ++++++++++++++++++++++++ contrib/libbpf | 1 + contrib/libbpf-cmake/CMakeLists.txt | 48 ++++++++++++++++++++++++ src/profiler/CMakeLists.txt | 55 ++++++++++++++++++---------- 8 files changed, 150 insertions(+), 29 deletions(-) create mode 160000 contrib/bpftool create mode 100644 contrib/bpftool-cmake/CMakeLists.txt create mode 160000 contrib/libbpf create mode 100644 contrib/libbpf-cmake/CMakeLists.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a53b2c1..270331d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,14 +2,14 @@ name: CI on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] workflow_dispatch: jobs: fmt: - runs-on: [ self-hosted, silk-arm-small-cpu ] + runs-on: [self-hosted, silk-arm-small-cpu] steps: - uses: actions/checkout@v5 @@ -26,7 +26,7 @@ jobs: run: ./bb fmt --check test: - runs-on: [ self-hosted, "${{ matrix.arch.runner }}" ] + runs-on: [self-hosted, "${{ matrix.arch.runner }}"] strategy: fail-fast: false @@ -68,7 +68,9 @@ jobs: contrib/librseq \ contrib/liburing \ contrib/googletest \ - contrib/benchmark + contrib/benchmark \ + contrib/libbpf \ + contrib/bpftool - name: Init Poco submodule if: matrix.build.name == 'release' || matrix.build.name == 'tsan' @@ -111,16 +113,15 @@ jobs: ccache \ libboost-context-dev \ libboost-program-options-dev \ - libbpf-dev \ libdouble-conversion-dev \ + libelf-dev \ zlib1g-dev - name: Cache ccache uses: actions/cache@v5 with: path: ~/.cache/ccache - key: ${{ runner.os }}-ccache-${{ matrix.arch.name }}-${{ matrix.build.name - }}-${{ github.sha }} + key: ${{ runner.os }}-ccache-${{ matrix.arch.name }}-${{ matrix.build.name }}-${{ github.sha }} restore-keys: | ${{ runner.os }}-ccache-${{ matrix.arch.name }}-${{ matrix.build.name }}- @@ -149,7 +150,7 @@ jobs: pages: needs: test if: github.ref == 'refs/heads/main' - runs-on: [ self-hosted, silk-amd-small-cpu ] + runs-on: [self-hosted, silk-amd-small-cpu] permissions: pages: write diff --git a/.gitmodules b/.gitmodules index 80e7d0b..6b4babe 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,9 @@ [submodule "contrib/librseq"] path = contrib/librseq url = https://github.com/compudj/librseq.git +[submodule "contrib/libbpf"] + path = contrib/libbpf + url = https://github.com/libbpf/libbpf.git +[submodule "contrib/bpftool"] + path = contrib/bpftool + url = https://github.com/libbpf/bpftool.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 90b7ab5..a24334f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,8 @@ add_subdirectory(contrib/libbacktrace-cmake) add_subdirectory(contrib/googletest-cmake) add_subdirectory(contrib/benchmark-cmake) add_subdirectory(contrib/systemtap-sdt) +add_subdirectory(contrib/libbpf-cmake) +add_subdirectory(contrib/bpftool-cmake) option(BUILD_POCO "Build Poco library (required by http-perf)" OFF) if(BUILD_POCO) diff --git a/contrib/bpftool b/contrib/bpftool new file mode 160000 index 0000000..d844a27 --- /dev/null +++ b/contrib/bpftool @@ -0,0 +1 @@ +Subproject commit d844a27fc175aeae3bcb29b052813ef210c97004 diff --git a/contrib/bpftool-cmake/CMakeLists.txt b/contrib/bpftool-cmake/CMakeLists.txt new file mode 100644 index 0000000..5be566a --- /dev/null +++ b/contrib/bpftool-cmake/CMakeLists.txt @@ -0,0 +1,47 @@ +if(NOT TARGET Libbpf::Libbpf) + return() +endif() + +set(BPFTOOL_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../bpftool) + +file(GLOB BPFTOOL_SOURCES ${BPFTOOL_SRC}/src/*.c) +list(APPEND BPFTOOL_SOURCES ${BPFTOOL_SRC}/src/kernel/bpf/disasm.c) +# jit_disasm.c needs libbfd or LLVM disassembler; without either the +# inline stubs in main.h cover the API for everything else we use. +list(REMOVE_ITEM BPFTOOL_SOURCES ${BPFTOOL_SRC}/src/jit_disasm.c) + +add_executable(bpftool ${BPFTOOL_SOURCES}) + +target_compile_options(bpftool PRIVATE + -w + -Wno-unused-parameter + -Wno-missing-field-initializers + # struct list_head lives in ; libbpf's + # uses it without including types.h directly. + -include linux/types.h + # bpftool is a build-time tool; don't pay the cost of sanitizers, + # and skip false positives like qsort_r(NULL, 0, ...) tripping + # UBSan's nonnull-attribute check. + -fno-sanitize=all +) +target_link_options(bpftool PRIVATE -fno-sanitize=all) + +# BPFTOOL_WITHOUT_SKELETONS turns the prog-profile / pid-iter paths (which +# depend on bpftool's own bootstrap-built skel headers) into stubs. The +# remaining commands -- including `btf dump` and `gen skeleton` that silk +# uses -- compile without needing a bootstrap stage. +target_compile_definitions(bpftool PRIVATE + BPFTOOL_WITHOUT_SKELETONS + BPFTOOL_VERSION="7.5.0" + PACKAGE="bpftool" + __EXPORTED_HEADERS__ + _GNU_SOURCE +) + +target_include_directories(bpftool PRIVATE + ${BPFTOOL_SRC}/include + ${BPFTOOL_SRC}/include/uapi + ${BPFTOOL_SRC}/src/kernel/bpf +) + +target_link_libraries(bpftool PRIVATE Libbpf::Libbpf) diff --git a/contrib/libbpf b/contrib/libbpf new file mode 160000 index 0000000..09b9e83 --- /dev/null +++ b/contrib/libbpf @@ -0,0 +1 @@ +Subproject commit 09b9e83102eb8ab9e540d36b4559c55f3bcdb95d diff --git a/contrib/libbpf-cmake/CMakeLists.txt b/contrib/libbpf-cmake/CMakeLists.txt new file mode 100644 index 0000000..6f4cf8c --- /dev/null +++ b/contrib/libbpf-cmake/CMakeLists.txt @@ -0,0 +1,48 @@ +find_library(LIBELF_LIBRARY NAMES elf) +find_path(LIBELF_INCLUDE_DIR libelf.h) +if(NOT LIBELF_LIBRARY OR NOT LIBELF_INCLUDE_DIR) + message(STATUS "Skipping libbpf: libelf not found (install libelf-dev)") + return() +endif() +find_package(ZLIB REQUIRED) + +set(LIBBPF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../libbpf) +set(LIBBPF_BIN ${CMAKE_BINARY_DIR}/libbpf-generated) + +# Stage src/*.h under a bpf/ subdir so consumers can use . +# The headers live directly in src/ in the repo but ship under bpf/ in +# the install layout that includes assume. +file(MAKE_DIRECTORY ${LIBBPF_BIN}/include/bpf) +file(GLOB _libbpf_public_headers ${LIBBPF_SRC}/src/*.h) +foreach(_hdr IN LISTS _libbpf_public_headers) + get_filename_component(_name ${_hdr} NAME) + configure_file(${_hdr} ${LIBBPF_BIN}/include/bpf/${_name} COPYONLY) +endforeach() + +file(GLOB LIBBPF_SOURCES ${LIBBPF_SRC}/src/*.c) + +add_library(_libbpf STATIC ${LIBBPF_SOURCES}) + +target_compile_options(_libbpf PRIVATE + -w + -std=gnu89 + # Don't sanitize third-party libbpf source; lets bpftool (which we + # also build with -fno-sanitize=all) link it without pulling in the + # sanitizer runtime, and keeps silk's profiler binary slimmer. + -fno-sanitize=all +) + +target_compile_definitions(_libbpf PRIVATE + _LARGEFILE64_SOURCE + _FILE_OFFSET_BITS=64 +) + +target_include_directories(_libbpf + PRIVATE ${LIBBPF_SRC}/src + PUBLIC ${LIBBPF_BIN}/include ${LIBBPF_SRC}/include ${LIBBPF_SRC}/include/uapi +) + +target_link_libraries(_libbpf PUBLIC ZLIB::ZLIB ${LIBELF_LIBRARY}) +target_include_directories(_libbpf SYSTEM PUBLIC ${LIBELF_INCLUDE_DIR}) + +add_library(Libbpf::Libbpf ALIAS _libbpf) diff --git a/src/profiler/CMakeLists.txt b/src/profiler/CMakeLists.txt index 6994ed7..96c66c9 100644 --- a/src/profiler/CMakeLists.txt +++ b/src/profiler/CMakeLists.txt @@ -1,13 +1,17 @@ -find_program(BPFTOOL bpftool) +if(NOT TARGET Libbpf::Libbpf OR NOT TARGET bpftool) + message(STATUS "Skipping profiler: bundled libbpf/bpftool unavailable (libelf-dev missing?)") + return() +endif() + find_program(LLVM_STRIP NAMES llvm-strip llvm-strip-21) find_program(CLANG NAMES clang clang-21) -find_path(BPF_INCLUDE bpf/bpf_core_read.h) - -if(NOT BPFTOOL OR NOT LLVM_STRIP OR NOT CLANG OR NOT BPF_INCLUDE) - message(STATUS "Skipping profiler: missing bpftool=${BPFTOOL} llvm-strip=${LLVM_STRIP} clang=${CLANG} libbpf-dev=${BPF_INCLUDE}") +if(NOT LLVM_STRIP OR NOT CLANG) + message(STATUS "Skipping profiler: missing llvm-strip=${LLVM_STRIP} clang=${CLANG}") return() endif() +set(BPFTOOL "$") + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") @@ -23,28 +27,37 @@ set(BPF_OBJ ${CMAKE_CURRENT_BINARY_DIR}/profiler.bpf.o) set(BPF_SKEL ${CMAKE_CURRENT_BINARY_DIR}/profiler.skel.h) # Use cmake -P scripts to avoid shell redirect escaping issues in ninja rules. -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/gen_vmlinux.cmake" - "execute_process(\n" - " COMMAND \"${BPFTOOL}\" btf dump file /sys/kernel/btf/vmlinux format c\n" - " OUTPUT_FILE \"${VMLINUX_H}\"\n" - " COMMAND_ERROR_IS_FATAL ANY\n" - ")\n" +# file(GENERATE) (vs WRITE) so $ resolves at generate time. +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/gen_vmlinux.cmake" + CONTENT +"execute_process( + COMMAND \"${BPFTOOL}\" btf dump file /sys/kernel/btf/vmlinux format c + OUTPUT_FILE \"${VMLINUX_H}\" + COMMAND_ERROR_IS_FATAL ANY ) +") -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/gen_skeleton.cmake" - "execute_process(\n" - " COMMAND \"${BPFTOOL}\" gen skeleton \"${BPF_OBJ}\"\n" - " OUTPUT_FILE \"${BPF_SKEL}\"\n" - " COMMAND_ERROR_IS_FATAL ANY\n" - ")\n" +file(GENERATE + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/gen_skeleton.cmake" + CONTENT +"execute_process( + COMMAND \"${BPFTOOL}\" gen skeleton \"${BPF_OBJ}\" + OUTPUT_FILE \"${BPF_SKEL}\" + COMMAND_ERROR_IS_FATAL ANY ) +") add_custom_command( OUTPUT ${VMLINUX_H} COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/gen_vmlinux.cmake + DEPENDS bpftool COMMENT "Generating vmlinux.h" ) +# usdt.bpf.h transitively pulls in -> , which +# lives in the multiarch include path on Debian/Ubuntu. Probe both x86_64 and +# aarch64 multiarch dirs; the architecture mismatch path is harmless if absent. add_custom_command( OUTPUT ${BPF_OBJ} COMMAND ${CLANG} @@ -52,7 +65,9 @@ add_custom_command( -g -O2 -D__TARGET_ARCH_${BPF_ARCH} -I${CMAKE_CURRENT_BINARY_DIR} - -I/usr/include/bpf + -I${CMAKE_BINARY_DIR}/libbpf-generated/include + -I/usr/include/x86_64-linux-gnu + -I/usr/include/aarch64-linux-gnu -c ${CMAKE_CURRENT_SOURCE_DIR}/profiler.bpf.c -o ${BPF_OBJ} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/profiler.bpf.c ${VMLINUX_H} @@ -65,7 +80,7 @@ add_custom_command( # Strip DWARF in-place (keeps BTF); bpftool then derives struct name from profiler.bpf.o -> profiler_bpf. COMMAND ${LLVM_STRIP} -g ${BPF_OBJ} COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/gen_skeleton.cmake - DEPENDS ${BPF_OBJ} + DEPENDS ${BPF_OBJ} bpftool COMMENT "Generating BPF skeleton" VERBATIM ) @@ -79,6 +94,6 @@ add_executable(profiler main.cpp profiler.cpp) add_dependencies(profiler profiler-skel) target_include_directories(profiler PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -target_link_libraries(profiler PRIVATE bpf elf z symbolizer Boost::program_options) +target_link_libraries(profiler PRIVATE Libbpf::Libbpf symbolizer Boost::program_options) add_subdirectory(tests)