Skip to content

Commit

Permalink
Agent bootstraper/loader (#1115)
Browse files Browse the repository at this point in the history
  • Loading branch information
intuibase authored Jan 15, 2024
1 parent 4f2bc45 commit 10be2bd
Show file tree
Hide file tree
Showing 24 changed files with 380 additions and 44 deletions.
7 changes: 6 additions & 1 deletion .ci/component-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ echo "BUILD ARCHITECTURE: $BUILD_ARCHITECTURE"


AGENT_EXTENSION_DIR=$APP_FOLDER/agent/native/_build/$BUILD_ARCHITECTURE-release/ext
AGENT_EXTENSION=$AGENT_EXTENSION_DIR/elastic_apm-$PHP_API.so
AGENT_LOADER_DIR=$APP_FOLDER/agent/native/_build/$BUILD_ARCHITECTURE-release/loader/code

# copy loader library into extensions
cp ${AGENT_LOADER_DIR}/elastic_apm_loader.so ${AGENT_EXTENSION_DIR}/

AGENT_EXTENSION=$AGENT_EXTENSION_DIR/elastic_apm_loader.so

mkdir -p $APP_FOLDER/build

Expand Down
9 changes: 7 additions & 2 deletions .ci/run-phpt-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ cd $APP_FOLDER/agent/native/ext
mkdir -p $APP_FOLDER/build

AGENT_EXTENSION_DIR=$APP_FOLDER/agent/native/_build/$BUILD_ARCHITECTURE-release/ext
AGENT_EXTENSION=$AGENT_EXTENSION_DIR/elastic_apm-$PHP_API.so
AGENT_LOADER_DIR=$APP_FOLDER/agent/native/_build/$BUILD_ARCHITECTURE-release/loader/code

# copy loader library
cp ${AGENT_LOADER_DIR}/elastic_apm_loader.so ${AGENT_EXTENSION_DIR}/

AGENT_EXTENSION=$AGENT_EXTENSION_DIR/elastic_apm_loader.so

ls -al $AGENT_EXTENSION_DIR

Expand All @@ -65,7 +70,7 @@ for phptFile in ./tests/*.phpt; do

INI_FILE=`php -d 'display_errors=stderr' -r 'echo php_ini_loaded_file();' 2> /dev/null`

if test "$INI_FILE"; then
if test "$INI_FILE"; then
egrep -h -v $PHP_DEPRECATED_DIRECTIVES_REGEX "$INI_FILE" > /tmp/tmp-php.ini
else
echo > /tmp/tmp-php.ini
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: package-parts-linux-x86-64
path: agent/native/_build/linux-x86-64-release/ext
path: agent/native/_build/linux-x86-64-release/
- uses: actions/download-artifact@v3
with:
name: package-parts-linuxmusl-x86-64
path: agent/native/_build/linuxmusl-x86-64-release/ext
path: agent/native/_build/linuxmusl-x86-64-release/
- name: package
run: make -C packaging package
- name: package info
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ jobs:
path: |
agent/native/_build/${{ matrix.arch }}-release/ext/elastic_apm*.so
agent/native/_build/${{ matrix.arch }}-release/ext/elastic_apm*.debug
agent/native/_build/${{ matrix.arch }}-release/loader/code/elastic_apm_loader.so
agent/native/_build/${{ matrix.arch }}-release/loader/code/elastic_apm_loader.debug
8 changes: 5 additions & 3 deletions .github/workflows/phpt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: package-parts-linux-x86-64
path: agent/native/_build/linux-x86-64-release/ext/
path: |
agent/native/_build/linux-x86-64-release/ext/
agent/native/_build/linux-x86-64-release/loader/code/
- name: Run phpt tests from PHP source code
continue-on-error: true
run: |
Expand All @@ -51,9 +53,9 @@ jobs:
docker-compose up -d elasticsearch apm-server
docker-compose run phpt_${VERSION_SHORT}
docker-compose stop
# exit 0
# exit 0
- if: success() || failure()
name: Change results ownership
name: Change results ownership
continue-on-error: true
run: |
sudo chmod -R 777 agent/extension_phpt_test/results
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: package-parts-linux-x86-64
path: agent/native/_build/linux-x86-64-release/ext
path: agent/native/_build/linux-x86-64-release

- uses: actions/download-artifact@v3
with:
name: package-parts-linuxmusl-x86-64
path: agent/native/_build/linuxmusl-x86-64-release/ext
path: agent/native/_build/linuxmusl-x86-64-release

- if: ${{ env.TESTING_TYPE == 'lifecycle' }}
name: lifecycle test
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ jobs:
- uses: actions/download-artifact@v3
with:
name: package-parts-${{ matrix.arch }}
path: agent/native/_build/${{ matrix.arch }}-release/ext/
path: agent/native/_build/${{ matrix.arch }}-release/
- name: Display structure of downloaded files
run: ls -R
- name: phpt-unit-tests
run: make -f .ci/Makefile run-phpt-tests

Expand Down
2 changes: 2 additions & 0 deletions agent/native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ include(elastic_set_default_build_options)
message(STATUS "Detected ${CMAKE_CXX_COMPILER_ID} compiler version: ${CMAKE_CXX_COMPILER_VERSION}")

include(elastic_conan_installer)
include(elastic_conan_debugsymbols)

# Install project dependencies

Expand Down Expand Up @@ -119,5 +120,6 @@ enable_testing()

add_subdirectory(libcommon)
add_subdirectory(libphpbridge)
add_subdirectory(loader)

add_subdirectory(ext)
20 changes: 20 additions & 0 deletions agent/native/building/cmake/elastic_conan_debugsymbols.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

function(copy_debug_symbols target)
block(SCOPE_FOR VARIABLES)
get_target_property(_TargetType ${target} TYPE)

add_custom_command(TARGET ${target}
POST_BUILD
COMMAND
${CMAKE_OBJCOPY} "--only-keep-debug" "$<TARGET_FILE:${target}>" "$<TARGET_FILE_DIR:${target}>/$<TARGET_PROPERTY:${target},DEBUG_SYMBOL_FILE>"

COMMAND
${CMAKE_OBJCOPY}
"--add-gnu-debuglink=$<TARGET_FILE_DIR:${target}>/$<TARGET_PROPERTY:${target},DEBUG_SYMBOL_FILE>"
"--strip-debug" "--strip-unneeded"
"$<TARGET_FILE:${target}>"
COMMENT "Striped debug symbols from ${target}"
)

endblock()
endfunction()
4 changes: 2 additions & 2 deletions agent/native/building/cmake/elastic_conan_installer.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ configure_file("${CMAKE_SOURCE_DIR}/building/conan/conan_profile.in" "${_CONAN_P


if(_VENV_CREATED)
# Installing conan and required dependencies
# Installing conan and required dependencies
execute_process(
COMMAND ${python_pip} install -U pip
COMMAND_ERROR_IS_FATAL ANY
Expand All @@ -97,7 +97,7 @@ if(_VENV_CREATED)
)

execute_process(
COMMAND ${python_pip} install -U conan==1.60.0
COMMAND ${python_pip} install -U conan==1.62.0
COMMAND_ERROR_IS_FATAL ANY
)

Expand Down
21 changes: 0 additions & 21 deletions agent/native/ext/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,6 @@
#scan for source files
AUX_SOURCE_DIRECTORY(. SrcFiles)

function(copy_debug_symbols target)
block(SCOPE_FOR VARIABLES)
get_target_property(_TargetType ${target} TYPE)

add_custom_command(TARGET ${target}
POST_BUILD
COMMAND
${CMAKE_OBJCOPY} "--only-keep-debug" "$<TARGET_FILE:${target}>" "$<TARGET_FILE_DIR:${target}>/$<TARGET_PROPERTY:${target},DEBUG_SYMBOL_FILE>"

COMMAND
${CMAKE_OBJCOPY}
"--add-gnu-debuglink=$<TARGET_FILE_DIR:${target}>/$<TARGET_PROPERTY:${target},DEBUG_SYMBOL_FILE>"
"--strip-debug" "--strip-unneeded"
"$<TARGET_FILE:${target}>"
COMMENT "Striped debug symbols from ${target}"
)

endblock()
endfunction()


foreach(_php_version ${_supported_php_versions})
set (_Target elasticapm_${_php_version})

Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions agent/native/loader/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

add_subdirectory(code)
22 changes: 22 additions & 0 deletions agent/native/loader/code/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

#scan for source files
AUX_SOURCE_DIRECTORY(. SrcFiles)

set (_Target elasticapm_loader)

add_library (${_Target} SHARED ${SrcFiles})

target_link_libraries(${_Target}
PRIVATE libcommon
)

set_target_properties(${_Target}
PROPERTIES OUTPUT_NAME elastic_apm_loader
PREFIX ""
DEBUG_SYMBOL_FILE "elastic_apm_loader.debug"
)

if (RELEASE_BUILD)
copy_debug_symbols(${_Target})
endif()

171 changes: 171 additions & 0 deletions agent/native/loader/code/loader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#include "phpdetection.h"

#include <dlfcn.h>
#include <stddef.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <filesystem>


#include "elastic_apm_version.h"

#define LOG_TO_SYSLOG_AND_STDERR(fmt, ... ) \
do { \
fprintf(stderr, \
"[elastic_apm_loader][PID: %d]" \
"[TID: %d] " \
fmt \
,(int)getpid() \
,(int)syscall( SYS_gettid ) \
, ##__VA_ARGS__ ); \
syslog( \
LOG_WARNING, \
"[elastic_apm_loader][PID: %d]" \
"[TID: %d] " \
fmt \
, (int)(getpid()) \
, (int)(syscall( SYS_gettid )) \
, ##__VA_ARGS__ ); \
} \
while(0);

namespace elasticapm::loader {

namespace phpdata {

#define INIT_FUNC_ARGS int type, int module_number
#define INIT_FUNC_ARGS_PASSTHRU type, module_number
#define SHUTDOWN_FUNC_ARGS int type, int module_number
#define SHUTDOWN_FUNC_ARGS_PASSTHRU type, module_number
#define ZEND_MODULE_INFO_FUNC_ARGS zend_module_entry *zend_module
#define ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU zend_module

struct zend_module_entry {
unsigned short size;
unsigned int zend_api;
unsigned char zend_debug;
unsigned char zts;
const void *ini_entry;
const void *deps;
const char *name;
const void *functions;
int (*module_startup_func)(INIT_FUNC_ARGS);
int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);
int (*request_startup_func)(INIT_FUNC_ARGS);
int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);
void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);
const char *version;
size_t globals_size;
#ifdef ZTS
ts_rsrc_id* globals_id_ptr;
#else
void* globals_ptr;
#endif
void (*globals_ctor)(void *global);
void (*globals_dtor)(void *global);
int (*post_deactivate_func)(void);
int module_started;
unsigned char type;
void *handle;
int module_number;
const char *build_id;
};

}

}

elasticapm::loader::phpdata::zend_module_entry elastic_apm_loader_module_entry = {
sizeof(elasticapm::loader::phpdata::zend_module_entry),
0, // API, f.ex.20220829
0, // DEBUG
0, // USING_ZTS
nullptr,
nullptr,
"elastic_apm_loader", /* Extension name */
nullptr, /* zend_function_entry */
nullptr, /* PHP_MINIT - Module initialization */
nullptr, /* PHP_MSHUTDOWN - Module shutdown */
nullptr, /* PHP_RINIT - Request initialization */
nullptr, /* PHP_RSHUTDOWN - Request shutdown */
nullptr, /* PHP_MINFO - Module info */
PHP_ELASTIC_APM_VERSION, /* Version */
0, /* globals_size */
nullptr, /* globals ptr */
nullptr, /* PHP_GINIT */
nullptr, /* PHP_GSHUTDOWN */
nullptr, /* post deactivate */
0,
0,
nullptr,
0,
"" // API20220829,NTS ..
};

extern "C" {

__attribute__ ((visibility("default"))) elasticapm::loader::phpdata::zend_module_entry *get_module(void) {
using namespace std::string_view_literals;
using namespace std::string_literals;

auto zendVersion = elasticapm::loader::getMajorMinorZendVersion();
if (zendVersion.empty()) {
LOG_TO_SYSLOG_AND_STDERR( "Can't find Zend/PHP Engine version\n");
return &elastic_apm_loader_module_entry;
}

auto [zendEngineVersion, zendModuleApiVersion, isVersionSupported] = elasticapm::loader::getZendModuleApiVersion(zendVersion);

bool isThreadSafe = elasticapm::loader::isThreadSafe();

static std::string zendBuildId{"API"s};
zendBuildId.append(std::to_string(zendModuleApiVersion));
zendBuildId.append(isThreadSafe ? ",TS"sv : ",NTS"sv);

elastic_apm_loader_module_entry.zend_api = zendModuleApiVersion;
elastic_apm_loader_module_entry.build_id = zendBuildId.c_str();
elastic_apm_loader_module_entry.zts = isThreadSafe;

if (!isVersionSupported) {
LOG_TO_SYSLOG_AND_STDERR( "Zend Engine version %s is not supported by Elastic APM Agent\n", std::string(zendVersion).c_str());
return &elastic_apm_loader_module_entry;
}

if (isThreadSafe) {
LOG_TO_SYSLOG_AND_STDERR( "Thread Safe mode (ZTS) is not supported by Elastic APM Agent\n");
return &elastic_apm_loader_module_entry; // unsupported thread safe mode
}

// get path to libraries
Dl_info dl_info;
dladdr((void *)get_module, &dl_info);
if (!dl_info.dli_fname) {
LOG_TO_SYSLOG_AND_STDERR( "Unable to resolve path to Elastic PHP Agent libraries\n");
return &elastic_apm_loader_module_entry;
}

auto elasticAgentPath = std::filesystem::path(dl_info.dli_fname).parent_path();

auto agentLibrary = (elasticAgentPath/"elastic_apm-"sv);
agentLibrary += std::to_string(zendModuleApiVersion);
agentLibrary += ".so"sv;

void *agentHandle = dlopen(agentLibrary.c_str(), RTLD_LAZY | RTLD_GLOBAL);
if (!agentHandle) {
LOG_TO_SYSLOG_AND_STDERR( "Unable to load agent library from path: %s\n", agentLibrary.c_str());
return &elastic_apm_loader_module_entry;
}

auto agentGetModule = reinterpret_cast<elasticapm::loader::phpdata::zend_module_entry *(*)(void)>(dlsym(agentHandle, "get_module"));
if (!agentGetModule) {
LOG_TO_SYSLOG_AND_STDERR( "Unable to resolve agent entry point from library: %s\n", agentLibrary.c_str());
return &elastic_apm_loader_module_entry;
}

return agentGetModule(); // or we can call zend_register_module_ex(agentGetModule())) and have both fully loaded
}


}
Loading

0 comments on commit 10be2bd

Please sign in to comment.