Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 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
7 changes: 7 additions & 0 deletions agent/native/ext/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,7 @@ ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( optionalBoolValue, asyncBackendComm )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, bootstrapPhpPartFile )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, breakdownMetrics )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, captureErrors )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, captureExceptions )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, devInternal )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, devInternalBackendCommLogVerbose )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, disableInstrumentations )
Expand Down Expand Up @@ -1008,6 +1009,12 @@ static void initOptionsMetadata( OptionMetadata* optsMeta )
ELASTIC_APM_CFG_OPT_NAME_CAPTURE_ERRORS,
/* defaultValue: */ true );

ELASTIC_APM_INIT_METADATA(
buildBoolOptionMetadata,
captureExceptions,
ELASTIC_APM_CFG_OPT_NAME_CAPTURE_EXCEPTIONS,
/* defaultValue: */ true );

ELASTIC_APM_INIT_METADATA(
buildStringOptionMetadata,
devInternal,
Expand Down
5 changes: 2 additions & 3 deletions agent/native/ext/ConfigManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ enum OptionId
optionId_bootstrapPhpPartFile,
optionId_breakdownMetrics,
optionId_captureErrors,
optionId_captureExceptions,
optionId_devInternal,
optionId_devInternalBackendCommLogVerbose,
optionId_disableInstrumentations,
Expand Down Expand Up @@ -257,10 +258,8 @@ const ConfigSnapshot* getGlobalCurrentConfigSnapshot();
#define ELASTIC_APM_CFG_OPT_NAME_BOOTSTRAP_PHP_PART_FILE "bootstrap_php_part_file"
#define ELASTIC_APM_CFG_OPT_NAME_BREAKDOWN_METRICS "breakdown_metrics"

/**
* Internal configuration option (not included in public documentation)
*/
#define ELASTIC_APM_CFG_OPT_NAME_CAPTURE_ERRORS "capture_errors"
#define ELASTIC_APM_CFG_OPT_NAME_CAPTURE_EXCEPTIONS "capture_exceptions"

/**
* Internal configuration option (not included in public documentation)
Expand Down
1 change: 1 addition & 0 deletions agent/native/ext/ConfigSnapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct ConfigSnapshot
String bootstrapPhpPartFile = nullptr;
bool breakdownMetrics = false;
bool captureErrors = false;
bool captureExceptions = false;
String devInternal = nullptr;
bool devInternalBackendCommLogVerbose = false;
String disableInstrumentations = nullptr;
Expand Down
54 changes: 1 addition & 53 deletions agent/native/ext/Hooking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,51 +17,6 @@

namespace elasticapm::php {

#if PHP_VERSION_ID < 80000
void elastic_apm_error_cb(int type, const char *error_filename, const Hooking::zend_error_cb_lineno_t error_lineno, const char *format, va_list args) { //<8.0
#elif PHP_VERSION_ID < 80100
void elastic_apm_error_cb(int type, const char *error_filename, const uint32_t error_lineno, zend_string *message) { // 8.0
#else
void elastic_apm_error_cb(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message) { // 8.1+
#endif
using namespace std::string_view_literals;

if (ELASTICAPM_G(captureErrors)) {
#if PHP_VERSION_ID < 80000
char * message = nullptr;
va_list messageArgsCopy;
va_copy(messageArgsCopy, args);
vspprintf(/* out */ &message, 0, format, messageArgsCopy); // vspprintf allocates memory for the resulted string buffer and it needs to be freed with efree()
va_end(messageArgsCopy);

ELASTICAPM_G(lastErrorData) = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? error_filename : ""sv, error_lineno, message ? message : ""sv);

if (message) {
efree(message);
}
#elif PHP_VERSION_ID < 80100
ELASTICAPM_G(lastErrorData) = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? error_filename : ""sv, error_lineno, message ? std::string_view{ZSTR_VAL(message), ZSTR_LEN(message)} : ""sv);
#else
ELASTICAPM_G(lastErrorData) = nullptr;
ELASTICAPM_G(lastErrorData) = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? std::string_view{ZSTR_VAL(error_filename), ZSTR_LEN(error_filename)} : ""sv, error_lineno, message ? std::string_view{ZSTR_VAL(message), ZSTR_LEN(message)} : ""sv);
#endif
}

auto original = Hooking::getInstance().getOriginalZendErrorCb();
if (original == elastic_apm_error_cb) {
ELASTIC_APM_LOG_DIRECT_CRITICAL("originalZendErrorCallback == elasticApmZendErrorCallback dead loop detected");
return;
}

if (original) {
#if PHP_VERSION_ID < 80000
original(type, error_filename, error_lineno, format, args);
#else
original(type, error_filename, error_lineno, message);
#endif
}
}

static void elastic_execute_internal(INTERNAL_FUNCTION_PARAMETERS) {

zend_try {
Expand Down Expand Up @@ -92,21 +47,14 @@ static void elastic_interrupt_function(zend_execute_data *execute_data) {
} zend_end_try();
}

void Hooking::replaceHooks(bool cfgCaptureErrors, bool cfgInferredSpansEnabled) {
void Hooking::replaceHooks(bool cfgInferredSpansEnabled) {
if (cfgInferredSpansEnabled) {
zend_execute_internal = elastic_execute_internal;
zend_interrupt_function = elastic_interrupt_function;
ELASTIC_APM_LOG_DEBUG( "Replaced zend_execute_internal and zend_interrupt_function hooks" );
} else {
ELASTIC_APM_LOG_DEBUG( "NOT replacing zend_execute_internal and zend_interrupt_function hooks because profiling_inferred_spans_enabled configuration option is set to false" );
}

if (cfgCaptureErrors) {
zend_error_cb = elastic_apm_error_cb;
ELASTIC_APM_LOG_DEBUG( "Replaced zend_error_cb hook" );
} else {
ELASTIC_APM_LOG_DEBUG( "NOT replacing zend_error_cb hook because capture_errors configuration option is set to false" );
}
}

}
24 changes: 1 addition & 23 deletions agent/native/ext/Hooking.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,6 @@ namespace elasticapm::php {
class Hooking {
public:

#if PHP_VERSION_ID < 70400
using zend_error_cb_lineno_t = uint;
#else
using zend_error_cb_lineno_t = uint32_t;
#endif

#if PHP_VERSION_ID < 80000
using zend_error_cb_t = void (*)(int type, const char *error_filename, const zend_error_cb_lineno_t error_lineno, const char *format, va_list args); //<8.0
#elif PHP_VERSION_ID < 80100
using zend_error_cb_t = void (*)(int type, const char *error_filename, const uint32_t error_lineno, zend_string *message); // 8.0
#else
using zend_error_cb_t = void (*)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message); // 8.1+
#endif

using zend_execute_internal_t = void (*)(zend_execute_data *execute_data, zval *return_value);
using zend_interrupt_function_t = void (*)(zend_execute_data *execute_data);

Expand All @@ -37,16 +23,14 @@ class Hooking {
void fetchOriginalHooks() {
original_execute_internal_ = zend_execute_internal;
original_zend_interrupt_function_ = zend_interrupt_function;
original_zend_error_cb_ = zend_error_cb;
}

void restoreOriginalHooks() {
zend_execute_internal = original_execute_internal_;
zend_interrupt_function = original_zend_interrupt_function_;
zend_error_cb = original_zend_error_cb_;
}

void replaceHooks(bool cfgCaptureErrors, bool cfgInferredSpansEnabled);
void replaceHooks(bool cfgInferredSpansEnabled);

zend_execute_internal_t getOriginalExecuteInternal() {
return original_execute_internal_;
Expand All @@ -56,19 +40,13 @@ class Hooking {
return original_zend_interrupt_function_;
}

zend_error_cb_t getOriginalZendErrorCb() {
return original_zend_error_cb_;
}


private:
Hooking(Hooking const &) = delete;
void operator=(Hooking const &) = delete;
Hooking() = default;

zend_execute_internal_t original_execute_internal_ = nullptr;
zend_interrupt_function_t original_zend_interrupt_function_ = nullptr;
zend_error_cb_t original_zend_error_cb_ = nullptr;
};


Expand Down
59 changes: 1 addition & 58 deletions agent/native/ext/elastic_apm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ PHP_INI_BEGIN()
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_BOOTSTRAP_PHP_PART_FILE )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_BREAKDOWN_METRICS )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_CAPTURE_ERRORS )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_CAPTURE_EXCEPTIONS )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_DEV_INTERNAL )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_DEV_INTERNAL_BACKEND_COMM_LOG_VERBOSE )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_DISABLE_INSTRUMENTATIONS )
Expand Down Expand Up @@ -287,23 +288,13 @@ static PHP_GINIT_FUNCTION(elastic_apm)
} catch (std::exception const &e) {
ELASTIC_APM_LOG_DIRECT_CRITICAL( "Unable to allocate AgentGlobals. '%s'", e.what());
}

ZVAL_UNDEF(&elastic_apm_globals->lastException);
new (&elastic_apm_globals->lastErrorData) std::unique_ptr<elasticapm::php::PhpErrorData>;
elastic_apm_globals->captureErrors = false;
}

static PHP_GSHUTDOWN_FUNCTION(elastic_apm) {
ELASTIC_APM_LOG_DIRECT_DEBUG( "%s: GSHUTDOWN called; parent PID: %d", __FUNCTION__, (int)getParentProcessId() );
if (elastic_apm_globals->globals) {
delete elastic_apm_globals->globals;
}

if (elastic_apm_globals->lastErrorData) {
ELASTIC_APM_LOG_DIRECT_WARNING( "%s: still holding error", __FUNCTION__);
// we need to relese any dangling php error data beacause it is already freed (it was allocated in request pool)
elastic_apm_globals->lastErrorData.release();
}
}

PHP_MINIT_FUNCTION(elastic_apm)
Expand Down Expand Up @@ -603,52 +594,6 @@ PHP_FUNCTION( elastic_apm_log )
}
/* }}} */

ZEND_BEGIN_ARG_INFO_EX( elastic_apm_get_last_thrown_arginfo, /* _unused */ 0, /* return_reference: */ 0, /* required_num_args: */ 0 )
ZEND_END_ARG_INFO()
/* {{{ elastic_apm_get_last_thrown(): mixed
*/
PHP_FUNCTION( elastic_apm_get_last_thrown )
{
ResultCode resultCode;
ZVAL_NULL( /* out */ return_value );

// We SHOULD NOT log before resetting state if forked because logging might be using thread synchronization
// which might deadlock in forked child
ELASTIC_APM_CALL_IF_FAILED_GOTO( elasticApmApiEntered( __FILE__, __LINE__, __FUNCTION__ ) );

elasticApmGetLastThrown( /* out */ return_value );

finally:
return;

failure:
goto finally;
}
/* }}} */

ZEND_BEGIN_ARG_INFO_EX( elastic_apm_get_last_php_error_arginfo, /* _unused */ 0, /* return_reference: */ 0, /* required_num_args: */ 0 )
ZEND_END_ARG_INFO()
/* {{{ elastic_apm_get_last_error(): array
*/
PHP_FUNCTION( elastic_apm_get_last_php_error )
{
ResultCode resultCode;
ZVAL_NULL( /* out */ return_value );

// We SHOULD NOT log before resetting state if forked because logging might be using thread synchronization
// which might deadlock in forked child
ELASTIC_APM_CALL_IF_FAILED_GOTO( elasticApmApiEntered( __FILE__, __LINE__, __FUNCTION__ ) );

elasticApmGetLastPhpError( /* out */ return_value );

finally:
return;

failure:
goto finally;
}
/* }}} */

ZEND_BEGIN_ARG_INFO_EX( elastic_apm_before_loading_agent_php_code_arginfo, /* _unused */ 0, /* return_reference: */ 0, /* required_num_args: */ 0 )
ZEND_END_ARG_INFO()
/* {{{ elastic_apm_before_loading_agent_php_code(): void
Expand Down Expand Up @@ -717,8 +662,6 @@ static const zend_function_entry elastic_apm_functions[] =
PHP_FE( elastic_apm_intercept_calls_to_internal_function, elastic_apm_intercept_calls_to_internal_function_arginfo )
PHP_FE( elastic_apm_send_to_server, elastic_apm_send_to_server_arginfo )
PHP_FE( elastic_apm_log, elastic_apm_log_arginfo )
PHP_FE( elastic_apm_get_last_thrown, elastic_apm_get_last_thrown_arginfo )
PHP_FE( elastic_apm_get_last_php_error, elastic_apm_get_last_php_error_arginfo )
PHP_FE( elastic_apm_before_loading_agent_php_code, elastic_apm_before_loading_agent_php_code_arginfo )
PHP_FE( elastic_apm_after_loading_agent_php_code, elastic_apm_after_loading_agent_php_code_arginfo )
PHP_FE( elastic_apm_ast_instrumentation_pre_hook, elastic_apm_ast_instrumentation_pre_hook_arginfo )
Expand Down
Loading