Skip to content
Merged
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
19 changes: 16 additions & 3 deletions config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,15 @@ if test "$PHP_DDTRACE" != "no"; then

PHP_VERSION=$($PHP_CONFIG --vernum)

if test $PHP_VERSION -lt 70000; then
if test $PHP_VERSION -lt 50500; then
DD_TRACE_PHP_VERSION_SPECIFIC_SOURCES="\
src/ext/php5_4/auto_flush.c \
src/ext/php5_4/blacklist.c \
src/ext/php5_4/dispatch.c \
src/ext/php5_4/engine_hooks.c \
src/ext/php5_4/handlers_curl.c \
"
elif test $PHP_VERSION -lt 70000; then
DD_TRACE_PHP_VERSION_SPECIFIC_SOURCES="\
src/ext/php5/auto_flush.c \
src/ext/php5/blacklist.c \
Expand Down Expand Up @@ -96,7 +104,12 @@ if test "$PHP_DDTRACE" != "no"; then
PHP_ADD_INCLUDE([$ext_srcdir/src/dogstatsd])
PHP_ADD_BUILD_DIR([$ext_builddir/src/dogstatsd])

PHP_ADD_BUILD_DIR([$ext_builddir/src/ext/php5])
PHP_ADD_BUILD_DIR([$ext_builddir/src/ext/php7])
if test $PHP_VERSION -lt 50500; then
PHP_ADD_BUILD_DIR([$ext_builddir/src/ext/php5_4])
elif test $PHP_VERSION -lt 70000; then
PHP_ADD_BUILD_DIR([$ext_builddir/src/ext/php5])
elif test $PHP_VERSION -lt 80000; then
PHP_ADD_BUILD_DIR([$ext_builddir/src/ext/php7])
fi
PHP_ADD_BUILD_DIR([$ext_builddir/src/ext/third-party])
fi
7 changes: 7 additions & 0 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@
<file name="mpack.c" role="src" />
<file name="mpack.h" role="src" />
</dir>
<dir name="php5_4">
<file name="auto_flush.c" role="src" />
<file name="blacklist.c" role="src" />
<file name="dispatch.c" role="src" />
<file name="engine_hooks.c" role="src" />
<file name="handlers_curl.c" role="src" />
</dir>
<dir name="php5">
<file name="auto_flush.c" role="src" />
<file name="blacklist.c" role="src" />
Expand Down
6 changes: 0 additions & 6 deletions src/ext/php5/auto_flush.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
#include "auto_flush.h"

#include <php.h>

#if PHP_VERSION_ID < 50500
int ddtrace_flush_tracer(void) { return SUCCESS; }
#else
ZEND_RESULT_CODE ddtrace_flush_tracer(void) { return SUCCESS; }
#endif
3 changes: 0 additions & 3 deletions src/ext/php5/blacklist.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#include "blacklist.h"

#include <php.h>
#include <stdbool.h>

#include "logging.h"

static bool _dd_is_blacklisted_module(zend_module_entry *module) {
Expand Down
145 changes: 143 additions & 2 deletions src/ext/php5/engine_hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,9 @@ static int _dd_opcode_default_dispatch(zend_execute_data *execute_data TSRMLS_DC
static int _dd_begin_fcall_handler(zend_execute_data *execute_data TSRMLS_DC) {
zend_function *current_fbc = NULL;
ddtrace_dispatch_t *dispatch = NULL;
if (!_dd_should_trace_call(execute_data, &current_fbc, &dispatch TSRMLS_CC)) {
if (!_dd_should_trace_call(execute_data, &current_fbc, &dispatch TSRMLS_CC) ||
((dispatch->options & DDTRACE_DISPATCH_POSTHOOK) && current_fbc->type == ZEND_INTERNAL_FUNCTION)) {
// internal functions that use POSTHOOK will use zend_execute_internal
return _dd_opcode_default_dispatch(execute_data TSRMLS_CC);
}
int vm_retval = _dd_opcode_default_dispatch(execute_data TSRMLS_CC);
Expand Down Expand Up @@ -749,9 +751,148 @@ void ddtrace_opcode_mshutdown(void) {

zend_set_user_opcode_handler(ZEND_EXIT, NULL);
}
static ddtrace_dispatch_t *_dd_lookup_dispatch_from_fbc(zval *this, zend_function *fbc TSRMLS_DC) {
if (DDTRACE_G(disable) || DDTRACE_G(disable_in_current_request) || DDTRACE_G(class_lookup) == NULL ||
DDTRACE_G(function_lookup) == NULL) {
return FALSE;
}
if (!fbc) {
return FALSE;
}

// Don't trace closures or functions without names
if (fbc->common.fn_flags & ZEND_ACC_CLOSURE || !fbc->common.function_name) {
return FALSE;
}

zval zv, *fname;
fname = &zv;
ZVAL_STRING(fname, fbc->common.function_name, 0);

return ddtrace_find_dispatch(this ? Z_OBJCE_P(this) : fbc->common.scope, fname TSRMLS_CC);
}

static bool _dd_should_trace_dispatch(ddtrace_dispatch_t *dispatch TSRMLS_DC) {
if (dispatch->busy) {
return false;
}
if (ddtrace_tracer_is_limited(TSRMLS_C) && (dispatch->options & DDTRACE_DISPATCH_INSTRUMENT_WHEN_LIMITED) == 0) {
return false;
}

return true;
}

static void _dd_execute_end_span(zend_execute_data *call, ddtrace_span_t *span, zval *user_retval,
const zend_op *opline_before_exception TSRMLS_DC) {
ddtrace_dispatch_t *dispatch = span->dispatch;
zval *user_args;
ALLOC_INIT_ZVAL(user_args);
zval *exception = NULL, *prev_exception = NULL;

dd_trace_stop_span_time(span);

ddtrace_copy_function_args(call, user_args);
if (EG(exception)) {
exception = EG(exception);
EG(exception) = NULL;
prev_exception = EG(prev_exception);
EG(prev_exception) = NULL;
ddtrace_span_attach_exception(span, exception);
zend_clear_exception(TSRMLS_C);
}

BOOL_T keep_span = TRUE;
if (Z_TYPE(dispatch->callable) == IS_OBJECT) {
ddtrace_error_handling eh;
ddtrace_backup_error_handling(&eh, EH_SUPPRESS TSRMLS_CC);

keep_span = ddtrace_execute_tracing_closure(dispatch, span->span_data, call, user_args, user_retval,
exception TSRMLS_CC);

if (get_dd_trace_debug() && PG(last_error_message) && eh.message != PG(last_error_message)) {
const char *fname = Z_STRVAL(dispatch->function_name);
ddtrace_log_errf("Error raised in tracing closure for %s(): %s in %s on line %d", fname,
PG(last_error_message), PG(last_error_file), PG(last_error_lineno));
}

ddtrace_restore_error_handling(&eh TSRMLS_CC);
// If the tracing closure threw an exception, ignore it to not impact the original call
if (get_dd_trace_debug() && EG(exception)) {
zval *ex = EG(exception), *message = NULL;
const char *type = Z_OBJCE_P(ex)->name;
const char *name = Z_STRVAL(dispatch->function_name);
message = ddtrace_exception_get_entry(ex, ZEND_STRL("message") TSRMLS_CC);
const char *msg = message && Z_TYPE_P(message) == IS_STRING ? Z_STRVAL_P(message)
: "(internal error reading exception message)";
ddtrace_log_errf("%s thrown in tracing closure for %s: %s", type, name, msg);
}
ddtrace_maybe_clear_exception(TSRMLS_C);
}

if (keep_span == TRUE) {
ddtrace_close_span(TSRMLS_C);
} else {
ddtrace_drop_top_open_span(TSRMLS_C);
}

if (exception) {
EG(exception) = exception;
EG(prev_exception) = prev_exception;
EG(opline_before_exception) = (zend_op *)opline_before_exception;
EG(current_execute_data)->opline = EG(exception_op);
}

zval_ptr_dtor(&user_args);
}

void (*_dd_prev_execute_internal)(zend_execute_data *execute_data_ptr, zend_fcall_info *fci,
int return_value_used TSRMLS_DC);
void ddtrace_execute_internal(zend_execute_data *execute_data, zend_fcall_info *fci, int return_value_used TSRMLS_DC) {
zend_function *fbc = execute_data->function_state.function;
ddtrace_dispatch_t *dispatch = _dd_lookup_dispatch_from_fbc(EX(object), fbc TSRMLS_CC);
if (!dispatch || !_dd_should_trace_dispatch(dispatch TSRMLS_CC) ||
!(dispatch->options & DDTRACE_DISPATCH_POSTHOOK)) {
_dd_prev_execute_internal(execute_data, fci, return_value_used TSRMLS_CC);
return;
}
dispatch->busy = 1;
ddtrace_dispatch_copy(dispatch);
zend_fcall_info fci_tmp;
if (!fci) {
fci = &fci_tmp;

// Taken from execute_internal on PHP 5.5 and 5.6
zval **retval_ptr_ptr = &EX_TMP_VAR(execute_data, EX(opline)->result.var)->var.ptr;
fci->object_ptr = EX(object);
#if PHP_VERSION_ID < 50600
fci->param_count = EX(opline)->extended_value;
fci->retval_ptr_ptr =
(EX(function_state).function->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? retval_ptr_ptr : NULL;
#else
fci->param_count = EX(opline)->extended_value + EX(call)->num_additional_args;
fci->retval_ptr_ptr = retval_ptr_ptr;
#endif
}

zend_op *opline = execute_data ? execute_data->opline : NULL; // ?
ddtrace_span_t *span = ddtrace_open_span(execute_data, dispatch TSRMLS_CC);

_dd_prev_execute_internal(execute_data, fci, return_value_used TSRMLS_CC);

if (span == DDTRACE_G(open_spans_top)) {
_dd_execute_end_span(execute_data, span, *fci->retval_ptr_ptr, opline TSRMLS_CC);
} else {
if (get_dd_trace_debug()) {
const char *fname = Z_STRVAL(dispatch->function_name);
ddtrace_log_errf("Cannot run tracing closure for %s(); spans out of sync", fname);
}
}
}

void ddtrace_execute_internal_minit(void) {
// TODO
_dd_prev_execute_internal = zend_execute_internal ?: execute_internal;
zend_execute_internal = ddtrace_execute_internal;
}

void ddtrace_execute_internal_mshutdown(void) {
Expand Down
3 changes: 3 additions & 0 deletions src/ext/php5_4/auto_flush.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "auto_flush.h"

int ddtrace_flush_tracer(void) { return SUCCESS; }
27 changes: 27 additions & 0 deletions src/ext/php5_4/blacklist.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "blacklist.h"

#include "logging.h"

static bool _dd_is_blacklisted_module(zend_module_entry *module) {
if (strcmp("ionCube Loader", module->name) == 0 || strcmp("newrelic", module->name) == 0) {
ddtrace_log_debugf("Found blacklisted module: %s, disabling conflicting functionality", module->name);
return true;
}
return false;
}

void ddtrace_blacklist_startup() {
zend_module_entry *module;
HashPosition pos;

ddtrace_has_blacklisted_module = false;

zend_hash_internal_pointer_reset_ex(&module_registry, &pos);
while (zend_hash_get_current_data_ex(&module_registry, (void *)&module, &pos) != FAILURE) {
if (module && module->name && _dd_is_blacklisted_module(module)) {
ddtrace_has_blacklisted_module = true;
return;
}
zend_hash_move_forward_ex(&module_registry, &pos);
}
}
Loading