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
32 changes: 27 additions & 5 deletions loader/dd_library_loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ static void *libdatadog_php_handle = NULL;
static unsigned int php_api_no = 0;
static const char *runtime_version = "unknown";
static bool injection_forced = false;
static bool ddtrace_disabled_by_incompatible_runtime = false;
static char ddtrace_disabled_result_reason[384] = {0};

static bool already_done = false;

Expand Down Expand Up @@ -73,6 +75,19 @@ PHP_INI_END()

static void ddloader_telemetryf(telemetry_reason reason, injected_ext *config, const char *error, const char *format, ...);

static void ddloader_set_ddtrace_disabled_by_incompatible_runtime(const char *format, ...) {
if (ddtrace_disabled_by_incompatible_runtime) {
return;
}

ddtrace_disabled_by_incompatible_runtime = true;

va_list va;
va_start(va, format);
vsnprintf(ddtrace_disabled_result_reason, sizeof(ddtrace_disabled_result_reason), format, va);
va_end(va);
}

static char *ddtrace_pre_load_hook(injected_ext *config) {
// Load libdatadog_php.so, on which ddtrace.so implicitly depends. Implicit
// because there's no DT_NEEDED(libdatadog_php.so) entry in ddtrace.so.
Expand Down Expand Up @@ -225,16 +240,17 @@ static void ddtrace_pre_minit_hook(injected_ext *config, zend_module_entry *modu
}

// Load, but disable the tracer if runtime configuration is not safe for auto-injection
bool disable_tracer = false;

char *incompatible_exts[] = {"Xdebug", "the ionCube PHP Loader", "ionCube Loader", "the ionCube PHP Loader + ionCube24", "newrelic", "blackfire", "pcov"};
for (size_t i = 0; i < sizeof(incompatible_exts) / sizeof(incompatible_exts[0]); ++i) {
if (ddloader_is_ext_loaded(incompatible_exts[i])) {
if (force_load) {
LOG(config, WARN, "Potentially incompatible extension detected: %s. Ignoring as DD_INJECT_FORCE is enabled", incompatible_exts[i]);
} else {
LOG(config, WARN, "Potentially incompatible extension detected: %s. ddtrace will be disabled unless the environment DD_INJECT_FORCE is set to '1', 'true', 'yes' or 'on'", incompatible_exts[i]);
disable_tracer = true;
ddloader_set_ddtrace_disabled_by_incompatible_runtime(
"The PHP tracer was disabled because potentially incompatible extension '%s' is loaded. Set DD_INJECT_FORCE to force tracing.",
incompatible_exts[i]
);
}
}
}
Expand All @@ -244,11 +260,13 @@ static void ddtrace_pre_minit_hook(injected_ext *config, zend_module_entry *modu
LOG(config, WARN, "OPcache JIT is enabled and may cause instability. Ignoring as DD_INJECT_FORCE is enabled");
} else {
LOG(config, WARN, "OPcache JIT is enabled and may cause instability. ddtrace will be disabled unless the environment DD_INJECT_FORCE is set to '1', 'true', 'yes' or 'on'");
disable_tracer = true;
ddloader_set_ddtrace_disabled_by_incompatible_runtime(
"The PHP tracer was disabled because OPcache JIT is enabled. Set DD_INJECT_FORCE to force tracing."
);
}
}

if (disable_tracer) {
if (ddtrace_disabled_by_incompatible_runtime) {
ddloader_ini_set_configuration(config, ZEND_STRL("ddtrace.disable"), ZEND_STRL("1"));
}

Expand Down Expand Up @@ -728,6 +746,8 @@ static PHP_MINIT_FUNCTION(ddloader_injected_extension_minit) {
zend_result ret = module->module_startup_func(INIT_FUNC_ARGS_PASSTHRU);
if (ret == FAILURE) {
TELEMETRY(REASON_ERROR, config, "error_minit", "'%s' MINIT function failed", config->ext_name);
} else if (strcmp(config->ext_name, "ddtrace") == 0 && ddtrace_disabled_by_incompatible_runtime) {
TELEMETRY(REASON_INCOMPATIBLE_RUNTIME, config, NULL, "%s", ddtrace_disabled_result_reason)
} else {
TELEMETRY(REASON_COMPLETE, config, NULL, "Application instrumentation bootstrapping complete ('%s')", config->ext_name)
}
Expand Down Expand Up @@ -1028,6 +1048,8 @@ static void ddloader_zend_extension_shutdown(zend_extension *ext) {
}

injection_forced = false;
ddtrace_disabled_by_incompatible_runtime = false;
*ddtrace_disabled_result_reason = 0;
already_done = false;
}

Expand Down
42 changes: 40 additions & 2 deletions loader/tests/functional/test_incompatibility_jit.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

$msg_disabled = "OPcache JIT is enabled and may cause instability. ddtrace will be disabled unless the environment DD_INJECT_FORCE is set to '1', 'true', 'yes' or 'on'";
$msg_forced = "OPcache JIT is enabled and may cause instability. Ignoring as DD_INJECT_FORCE is enabled";
$jitTelemetryLogPath = tempnam(sys_get_temp_dir(), 'test_loader_');

$tests = [
// OPcache disabled in CLI
Expand Down Expand Up @@ -73,6 +74,10 @@
// JIT enabled
[
"config" => "-dzend_extension=opcache -dopcache.enable_cli=1 -ddatadog.trace.cli_enabled=1 -dopcache.jit_buffer_size=32M -dopcache.jit=tracing",
"env" => [
'FAKE_FORWARDER_LOG_PATH='.$jitTelemetryLogPath,
'DD_TELEMETRY_FORWARDER_PATH='.__DIR__.'/../../bin/fake_forwarder.sh',
],
"must_not_contain" => [],
"must_contain" => [
$msg_disabled,
Expand All @@ -94,8 +99,8 @@

=> ddtrace
Version => %s
Injection success => true
Injection error =>
Injection success => false
Injection error => Incompatible runtime
Extra config => datadog.trace.sources_path=%s/trace/src
ddtrace.disable=1

Expand All @@ -104,6 +109,34 @@
%A
EOT
],
"telemetry_log_path" => $jitTelemetryLogPath,
"telemetry" => <<<EOS
{
"metadata": {
"runtime_name": "php",
"runtime_version": "%d.%d.%d%S",
"language_name": "php",
"language_version": "%d.%d.%d%S",
"tracer_version": "%s",
"pid": %d,
"result": "abort",
"result_reason": "The PHP tracer was disabled because OPcache JIT is enabled. Set DD_INJECT_FORCE to force tracing.",
"result_class": "incompatible_runtime"
},
"points": [
{
"name": "library_entrypoint.abort",
"tags": [
"reason:incompatible_runtime",
"product:ddtrace"
]
},
{
"name": "library_entrypoint.abort.runtime"
}
]
}
EOS
],
// JIT enabled + force injection via ENV
[
Expand Down Expand Up @@ -198,4 +231,9 @@
foreach ($data['must_match'] as $pattern) {
assertMatchesFormat($output, $pattern);
}
if (isset($data['telemetry'])) {
// Let time to the fork to write the telemetry log
usleep(5000);
assertTelemetry($data['telemetry_log_path'], $data['telemetry']);
}
}
72 changes: 71 additions & 1 deletion loader/tests/functional/test_incompatibility_xdebug.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,84 @@
$msg_disabled = "Potentially incompatible extension detected: Xdebug. ddtrace will be disabled unless the environment DD_INJECT_FORCE is set to '1', 'true', 'yes' or 'on'";
$msg_forced = "Potentially incompatible extension detected: Xdebug. Ignoring as DD_INJECT_FORCE is enabled";

$output = runCLI('-dzend_extension='.getenv("XDEBUG_SO_NAME").' -v', true, ['DD_TRACE_DEBUG=1']);
$telemetryLogPath = tempnam(sys_get_temp_dir(), 'test_loader_');

$output = runCLI('-dzend_extension='.getenv("XDEBUG_SO_NAME").' -v', true, [
'DD_TRACE_DEBUG=1',
'FAKE_FORWARDER_LOG_PATH='.$telemetryLogPath,
'DD_TELEMETRY_FORWARDER_PATH='.__DIR__.'/../../bin/fake_forwarder.sh',
]);
assertContains($output, 'Found extension file');
assertContains($output, $msg_disabled);
assertNotContains($output, $msg_forced);
assertContains($output, 'with dd_library_loader v');
assertContains($output, 'with Xdebug v');
assertContains($output, 'with ddtrace v');

// Let time to the fork to write the telemetry log
usleep(5000);

$metrics = [<<<EOS
{
"metadata": {
"runtime_name": "php",
"runtime_version": "%d.%d.%d%S",
"language_name": "php",
"language_version": "%d.%d.%d%S",
"tracer_version": "%s",
"pid": %d,
"result": "abort",
"result_reason": "The PHP tracer was disabled because potentially incompatible extension 'Xdebug' is loaded. Set DD_INJECT_FORCE to force tracing.",
"result_class": "incompatible_runtime"
},
"points": [
{
"name": "library_entrypoint.abort",
"tags": [
"reason:incompatible_runtime",
"product:ddtrace"
]
},
{
"name": "library_entrypoint.abort.runtime"
}
]
}
EOS
];

if ('7.0' === php_minor_version()) {
$metrics[] = <<<EOS
{
"metadata": {
"runtime_name": "php",
"runtime_version": "%d.%d.%d%S",
"language_name": "php",
"language_version": "%d.%d.%d%S",
"tracer_version": "%s",
"pid": %d,
"result": "abort",
"result_reason": "%s",
"result_class": "incompatible_runtime"
},
"points": [
{
"name": "library_entrypoint.abort",
"tags": [
"reason:incompatible_runtime",
"product:datadog-profiling"
]
},
{
"name": "library_entrypoint.abort.runtime"
}
]
}
EOS;
}

assertTelemetry($telemetryLogPath, $metrics);

$output = runCLI('-dzend_extension='.getenv("XDEBUG_SO_NAME").' -v', true, ['DD_TRACE_DEBUG=1', 'DD_INJECT_FORCE=1']);
assertContains($output, 'Found extension file');
assertNotContains($output, $msg_disabled);
Expand Down
Loading