Skip to content

Commit d41a80f

Browse files
authored
Disable agent when opcache is in reset pending state (#988) (#1000)
* Disable agent when opcache is in reset pending state (#988) * Better handling of tracer state
1 parent 68cad53 commit d41a80f

File tree

5 files changed

+107
-20
lines changed

5 files changed

+107
-20
lines changed

src/ext/lifecycle.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,8 @@ void elasticApmRequestInit()
625625
{
626626
requestCounter++;
627627

628+
tracerPhpPartOnRequestInitSetInitialTracerState();
629+
628630
TimePoint requestInitStartTime;
629631
getCurrentTime( &requestInitStartTime );
630632

@@ -652,6 +654,12 @@ void elasticApmRequestInit()
652654
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
653655
}
654656

657+
if (detectOpcacheRestartPending()) {
658+
ELASTIC_APM_LOG_ERROR("Detected that opcache reset is in a pending state. Instrumentation has been disabled for this request. There may be warnings or errors logged for this request.");
659+
resultCode = resultSuccess;
660+
goto finally;
661+
}
662+
655663
if ( ! config->enabled )
656664
{
657665
ELASTIC_APM_LOG_DEBUG( "Not enabled" );

src/ext/tracer_PHP_part.c

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,23 @@ String tracerPhpPartStateToString( TracerPhpPartState value )
7575

7676
static TracerPhpPartState g_tracerPhpPartState = numberOfTracerPhpPartState;
7777

78-
void switchTracerPhpPartStateToFailed( String reason, String dbgCalledFromFunc )
78+
bool canInvokeTracerPhpPart() {
79+
return g_tracerPhpPartState == tracerPhpPartState_after_bootstrap;
80+
}
81+
82+
// returns true if state was changed or false it was same before
83+
bool switchTracerPhpPartStateToFailed( String reason, String dbgCalledFromFunc )
7984
{
8085
if ( g_tracerPhpPartState == tracerPhpPartState_failed )
8186
{
82-
return;
87+
return false;
8388
}
8489

8590
ELASTIC_APM_LOG_ERROR( "Switching tracer PHP part state to failed; reason: %s, current state: %s, called from %s"
8691
, reason, tracerPhpPartStateToString( g_tracerPhpPartState ), dbgCalledFromFunc );
8792

8893
g_tracerPhpPartState = tracerPhpPartState_failed;
94+
return true;
8995
}
9096

9197
ResultCode bootstrapTracerPhpPart( const ConfigSnapshot* config, const TimePoint* requestInitStartTime )
@@ -201,10 +207,12 @@ bool tracerPhpPartInternalFuncCallPreHook( uint32_t interceptRegistrationId, zen
201207
ZVAL_UNDEF( &interceptRegistrationIdAsZval );
202208
zval phpPartArgs[ g_maxInterceptedCallArgsCount + 2 ];
203209

204-
if ( g_tracerPhpPartState != tracerPhpPartState_after_bootstrap )
205-
{
206-
switchTracerPhpPartStateToFailed( /* reason */ "Unexpected current tracer PHP part state", __FUNCTION__ );
207-
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
210+
if (!canInvokeTracerPhpPart()) {
211+
if (switchTracerPhpPartStateToFailed( /* reason */ "Unexpected current tracer PHP part state", __FUNCTION__ )) {
212+
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
213+
} else {
214+
ELASTIC_APM_SET_RESULT_CODE_TO_SUCCESS_AND_GOTO_FINALLY();
215+
}
208216
}
209217

210218
// The first argument to PHP part's interceptedCallPreHook() is $interceptRegistrationId
@@ -260,10 +268,12 @@ void tracerPhpPartInternalFuncCallPostHook( uint32_t dbgInterceptRegistrationId,
260268
ResultCode resultCode;
261269
zval phpPartArgs[ 2 ];
262270

263-
if ( g_tracerPhpPartState != tracerPhpPartState_after_bootstrap )
264-
{
265-
switchTracerPhpPartStateToFailed( /* reason */ "Unexpected current tracer PHP part state", __FUNCTION__ );
266-
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
271+
if (!canInvokeTracerPhpPart()) {
272+
if (switchTracerPhpPartStateToFailed( /* reason */ "Unexpected current tracer PHP part state", __FUNCTION__ )) {
273+
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
274+
} else {
275+
ELASTIC_APM_SET_RESULT_CODE_TO_SUCCESS_AND_GOTO_FINALLY();
276+
}
267277
}
268278

269279
// The first argument to PHP part's interceptedCallPostHook() is $hasExitedByException (bool)
@@ -301,10 +311,12 @@ void tracerPhpPartInterceptedCallEmptyMethod()
301311
zval phpPartDummyArgs[ 1 ];
302312
ZVAL_UNDEF( &( phpPartDummyArgs[ 0 ] ) );
303313

304-
if ( g_tracerPhpPartState != tracerPhpPartState_after_bootstrap )
305-
{
306-
switchTracerPhpPartStateToFailed( /* reason */ "Unexpected current tracer PHP part state", __FUNCTION__ );
307-
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
314+
if (!canInvokeTracerPhpPart()) {
315+
if (switchTracerPhpPartStateToFailed( /* reason */ "Unexpected current tracer PHP part state", __FUNCTION__ )) {
316+
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
317+
} else {
318+
ELASTIC_APM_SET_RESULT_CODE_TO_SUCCESS_AND_GOTO_FINALLY();
319+
}
308320
}
309321

310322
ELASTIC_APM_CALL_IF_FAILED_GOTO(
@@ -344,18 +356,22 @@ void tracerPhpPartLogArguments( LogLevel logLevel, uint32_t argsCount, zval args
344356

345357
void tracerPhpPartForwardCall( StringView phpFuncName, zend_execute_data* execute_data, /* out */ zval* retVal, String dbgCalledFrom )
346358
{
347-
ResultCode resultCode;
359+
ResultCode resultCode = resultFailure;
360+
ZVAL_NULL(retVal);
348361
uint32_t callArgsCount;
349362
zval callArgs[ g_maxInterceptedCallArgsCount ];
350363

351364
ELASTIC_APM_LOG_TRACE_FUNCTION_ENTRY_MSG( "phpFuncName: %s, dbgCalledFrom: %s", phpFuncName.begin, dbgCalledFrom );
352365

353-
if ( g_tracerPhpPartState != tracerPhpPartState_after_bootstrap )
354-
{
355-
switchTracerPhpPartStateToFailed( /* reason */ "Unexpected current tracer PHP part state", __FUNCTION__ );
356-
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
366+
if (!canInvokeTracerPhpPart()) {
367+
if (switchTracerPhpPartStateToFailed( /* reason */ "Unexpected current tracer PHP part state", __FUNCTION__ )) {
368+
ELASTIC_APM_SET_RESULT_CODE_AND_GOTO_FAILURE();
369+
} else {
370+
ELASTIC_APM_SET_RESULT_CODE_TO_SUCCESS_AND_GOTO_FINALLY();
371+
}
357372
}
358373

374+
359375
getArgsFromZendExecuteData( execute_data, g_maxInterceptedCallArgsCount, &( callArgs[ 0 ] ), &callArgsCount );
360376
tracerPhpPartLogArguments( logLevel_trace, callArgsCount, callArgs );
361377

@@ -385,9 +401,12 @@ void tracerPhpPartAstInstrumentationDirectCall( zend_execute_data* execute_data
385401
tracerPhpPartForwardCall( ELASTIC_APM_STRING_LITERAL_TO_VIEW( ELASTIC_APM_PHP_PART_AST_INSTRUMENTATION_DIRECT_CALL_FUNC ), execute_data, /* out */ &unusedRetVal, __FUNCTION__ );
386402
}
387403

404+
void tracerPhpPartOnRequestInitSetInitialTracerState() {
405+
g_tracerPhpPartState = tracerPhpPartState_before_bootstrap;
406+
}
407+
388408
ResultCode tracerPhpPartOnRequestInit( const ConfigSnapshot* config, const TimePoint* requestInitStartTime )
389409
{
390-
g_tracerPhpPartState = tracerPhpPartState_before_bootstrap;
391410
return bootstrapTracerPhpPart( config, requestInitStartTime );
392411
}
393412

src/ext/tracer_PHP_part.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ void tracerPhpPartInterceptedCallEmptyMethod();
3434

3535
void tracerPhpPartAstInstrumentationCallPreHook( zend_execute_data* execute_data, zval* return_value );
3636
void tracerPhpPartAstInstrumentationDirectCall( zend_execute_data* execute_data );
37+
void tracerPhpPartOnRequestInitSetInitialTracerState();

src/ext/util_for_PHP.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,64 @@ bool isPhpRunningAsCliScript()
274274
return strcmp( sapi_module.name, "cli" ) == 0;
275275
}
276276

277+
int call_internal_function(zval *object, const char *functionName, zval parameters[], int32_t parametersCount, zval *returnValue) {
278+
zval funcName;
279+
ZVAL_STRING(&funcName, functionName);
280+
281+
int result = resultFailure;
282+
zend_try {
283+
#if PHP_VERSION_ID >= 80000
284+
result = _call_user_function_impl(object, &funcName, returnValue, parametersCount, parameters, NULL);
285+
#else
286+
result = _call_user_function_ex(object, &funcName, returnValue, parametersCount, parameters, 0);
287+
#endif
288+
} zend_catch {
289+
ELASTIC_APM_LOG_ERROR("Call of '%s' failed", functionName);
290+
} zend_end_try();
291+
292+
zval_ptr_dtor(&funcName);
293+
return result;
294+
}
295+
296+
297+
bool detectOpcacheRestartPending() {
298+
bool opcacheEnabled = isPhpRunningAsCliScript() ? INI_BOOL("opcache.enable_cli") : INI_BOOL("opcache.enable");
299+
if (!opcacheEnabled) {
300+
return false;
301+
}
302+
if (EG(function_table) && !zend_hash_str_find_ptr(EG(function_table), ZEND_STRL("opcache_get_status"))) {
303+
return false;
304+
}
305+
306+
zval rv;
307+
ZVAL_NULL(&rv);
308+
zval parameters[1];
309+
ZVAL_BOOL(&parameters[0], false);
310+
311+
int result = call_internal_function(NULL, "opcache_get_status", parameters, 1, &rv);
312+
if (result == resultFailure) {
313+
zval_ptr_dtor(&rv);
314+
return false;
315+
}
316+
317+
if (Z_TYPE(rv) != IS_ARRAY) {
318+
zval_ptr_dtor(&rv);
319+
return false;
320+
}
321+
322+
zval *restartPending = zend_hash_str_find(Z_ARRVAL(rv), ZEND_STRL("restart_pending"));
323+
if (restartPending && Z_TYPE_P(restartPending) == IS_TRUE) {
324+
zval_ptr_dtor(&rv);
325+
return true;
326+
} else if (!restartPending || Z_TYPE_P(restartPending) != IS_FALSE) {
327+
ELASTIC_APM_LOG_ERROR("opcache_get_status returned unexpected data ptr: %p t:%d", restartPending, restartPending ? Z_TYPE_P(restartPending) : -1);
328+
}
329+
330+
zval_ptr_dtor(&rv);
331+
return false;
332+
}
333+
334+
277335
bool detectOpcachePreload() {
278336
if (PHP_VERSION_ID < 70400) {
279337
return false;

src/ext/util_for_PHP.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ void getArgsFromZendExecuteData( zend_execute_data *execute_data, size_t dstArra
8787

8888
bool isPhpRunningAsCliScript();
8989
bool detectOpcachePreload();
90+
bool detectOpcacheRestartPending();
9091
void enableAccessToServerGlobal();
9192

9293
#define ELASTIC_APM_ZEND_ADD_ASSOC( map, key, valueType, value ) ELASTIC_APM_PP_CONCAT( ELASTIC_APM_PP_CONCAT( add_assoc_, valueType ), _ex)( (map), (key), sizeof( key ) - 1, (value) )

0 commit comments

Comments
 (0)