agent/native/ext/platform.cpp (516 lines of code) (raw):

/* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include "platform.h" #include "elastic_apm_version.h" #include <limits.h> #include <string.h> #include <stdlib.h> #ifdef PHP_WIN32 # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # ifndef VC_EXTRALEAN # define VC_EXTRALEAN # endif # include <windows.h> # include <process.h> #else # include <unistd.h> # include <sys/syscall.h> # include <syslog.h> # include <signal.h> # include <errno.h> # ifndef __USE_GNU # define __USE_GNU 1 # endif # include <dlfcn.h> #endif #if defined( ELASTIC_APM_PLATFORM_HAS_LIBUNWIND ) # define UNW_LOCAL_ONLY # include <libunwind.h> #endif // if defined( ELASTIC_APM_PLATFORM_HAS_LIBUNWIND ) #include "util.h" #include "log.h" #include "TextOutputStream.h" #define ELASTIC_APM_CURRENT_LOG_CATEGORY ELASTIC_APM_LOG_CATEGORY_PLATFORM pid_t getCurrentProcessId() { #ifdef PHP_WIN32 return _getpid(); #else return getpid(); #endif } pid_t getCurrentThreadId() { #ifdef PHP_WIN32 return (pid_t) GetCurrentThreadId(); #else return (pid_t) syscall( SYS_gettid ); #endif } pid_t getParentProcessId() { #ifdef PHP_WIN32 return (pid_t)( -1 ); #else return getppid(); #endif } #ifdef PHP_WIN32 void writeToWindowsSystemDebugger( String msg ) { ELASTIC_APM_ASSERT_VALID_STRING( msg ); OutputDebugStringA( msg ); } #endif #ifdef PHP_WIN32 bool getTimeZoneShiftOnWindows( long* secondsAheadUtc ) { ELASTIC_APM_ASSERT_VALID_PTR( secondsAheadUtc ); TIME_ZONE_INFORMATION timeZoneInformation = { 0 }; if ( GetTimeZoneInformation( &timeZoneInformation ) == TIME_ZONE_ID_INVALID ) return false; // https://docs.microsoft.com/en-ca/windows/win32/api/timezoneapi/ns-timezoneapi-time_zone_information // Bias is the difference, in minutes, between Coordinated Universal Time (UTC) and local time. // All translations between UTC and local time are based on the following formula: // UTC = local time + bias // So negative value in Bias means that we are to the east of UTC, so it is positive for our format *secondsAheadUtc = -( timeZoneInformation.Bias * 60 ); return true; } #endif String streamErrNo( int errnoValue, TextOutputStream* txtOutStream ) { TextOutputStreamState txtOutStreamStateOnEntryStart; if ( ! textOutputStreamStartEntry( txtOutStream, &txtOutStreamStateOnEntryStart ) ) return ELASTIC_APM_TEXT_OUTPUT_STREAM_NOT_ENOUGH_SPACE_MARKER; const size_t freeSpaceSize = textOutputStreamGetFreeSpaceSize( txtOutStream ); if ( freeSpaceSize == 0 ) return textOutputStreamEndEntryAsOverflowed( &txtOutStreamStateOnEntryStart, txtOutStream ); // +1 to detect overflow and +1 for terminating '\0' const size_t freeSizePlus = freeSpaceSize + 2; ELASTIC_APM_ASSERT_VALID_OBJ( assertValidEndPtrIntoTextOutputStream( txtOutStreamStateOnEntryStart.freeSpaceBegin + freeSizePlus, txtOutStream ) ); // Write terminating zero to detect if anything was written to the buffer // by the function converting errno to string *( txtOutStreamStateOnEntryStart.freeSpaceBegin ) = '\0'; #ifdef PHP_WIN32 // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strerror-s-strerror-s-wcserror-s-wcserror-s?view=vs-2019 // strerror_s( char *buffer, size_t numberOfElements, int errnum ); strerror_s( txtOutStreamStateOnEntryStart.freeSpaceBegin, freeSizePlus, errnoValue ); #else // http://man7.org/linux/man-pages/man3/strerror.3.html // strerror_r( int errnum, char *buf, size_t buflen ); //TODO missing error check - it can contain trash from buffer auto errorStr = strerror(errnoValue); auto len = strlen(errorStr); if (len < freeSizePlus) { strcpy(txtOutStreamStateOnEntryStart.freeSpaceBegin, errorStr); } else { strncpy(txtOutStreamStateOnEntryStart.freeSpaceBegin, errorStr, freeSizePlus -1); txtOutStreamStateOnEntryStart.freeSpaceBegin[freeSizePlus -1] = 0; } #endif const size_t numberOfContentChars = strlen( txtOutStreamStateOnEntryStart.freeSpaceBegin ); const bool isOverflowed = ( numberOfContentChars > freeSpaceSize ); textOutputStreamSkipNChars( txtOutStream, isOverflowed ? freeSpaceSize : numberOfContentChars ); return ( isOverflowed ) ? textOutputStreamEndEntryAsOverflowed( &txtOutStreamStateOnEntryStart, txtOutStream ) : textOutputStreamEndEntry( &txtOutStreamStateOnEntryStart, txtOutStream ); } #ifdef PHP_WIN32 size_t captureStackTraceWindows( void** addressesBuffer, size_t addressesBufferSize ) { ELASTIC_APM_UNUSED( addressesBuffer ); ELASTIC_APM_UNUSED( addressesBufferSize ); return 0; } String streamStackTraceWindows( void* const* addresses, size_t addressesCount, String linePrefix, TextOutputStream* txtOutStream ) { ELASTIC_APM_UNUSED( addresses ); ELASTIC_APM_UNUSED( addressesCount ); ELASTIC_APM_UNUSED( linePrefix ); return streamString( "", txtOutStream ); } #else // #ifdef PHP_WIN32 String streamStackTraceLinux( void* const* addresses, size_t addressesCount, String linePrefix, TextOutputStream* txtOutStream ) { TextOutputStreamState txtOutStreamStateOnEntryStart; if ( ! textOutputStreamStartEntry( txtOutStream, &txtOutStreamStateOnEntryStart ) ) return ELASTIC_APM_TEXT_OUTPUT_STREAM_NOT_ENOUGH_SPACE_MARKER; #ifdef ELASTIC_APM_PLATFORM_HAS_BACKTRACE char** addressesAsSymbols = backtrace_symbols( addresses, (int)addressesCount ); if ( addressesAsSymbols == NULL ) { streamPrintf( txtOutStream, "backtrace_symbols returned NULL (i.e., failed to resolve addresses to symbols). Addresses:\n" ); ELASTIC_APM_FOR_EACH_INDEX( i, addressesCount ) streamPrintf( txtOutStream, "%s%p\n", linePrefix, addresses[ i ] ); } else { ELASTIC_APM_FOR_EACH_INDEX( i, addressesCount ) streamPrintf( txtOutStream, "%s%s\n", linePrefix, addressesAsSymbols[ i ] ); free( addressesAsSymbols ); } #else streamPrintf( txtOutStream, "Could not obtain stack trace because execinfo/backtrace is not supported on this platform" ); #endif return textOutputStreamEndEntry( &txtOutStreamStateOnEntryStart, txtOutStream ); } #endif // #else // #ifdef PHP_WIN32 String streamStackTrace( void* const* addresses, size_t addressesCount, String linePrefix, TextOutputStream* txtOutStream ) { ELASTIC_APM_ASSERT_VALID_PTR( addresses ); if ( addressesCount == 0 ) return streamString( "<stack is empty>", txtOutStream ); return #ifdef PHP_WIN32 streamStackTraceWindows( addresses, addressesCount, linePrefix, txtOutStream ) #else streamStackTraceLinux( addresses, addressesCount, linePrefix, txtOutStream ) #endif ; } #ifndef PHP_WIN32 static String g_procSelfCmdLineFileName = "/proc/self/cmdline"; void streamCharUpToMaxLength( TextOutputStream* txtOutStream, char value, size_t maxLength, size_t* numberOfCharsProcessed ) { ELASTIC_APM_ASSERT_VALID_PTR( txtOutStream ); ELASTIC_APM_ASSERT_VALID_PTR( numberOfCharsProcessed ); if ( *numberOfCharsProcessed < maxLength ) { streamChar( value, txtOutStream ); } ++(*numberOfCharsProcessed); } void streamCurrentProcessCommandLineImpl( TextOutputStream* txtOutStream, size_t maxLength, FILE* procSelfCmdLineFile ) { ELASTIC_APM_ASSERT_VALID_PTR( txtOutStream ); ELASTIC_APM_ASSERT_VALID_PTR( procSelfCmdLineFile ); enum { auxBufferSize = 100 }; char auxBuffer[ auxBufferSize ]; bool reachedEndOfFile = false; bool isRightAfterSeparator = false; size_t numberOfCharsProcessed = 0; while ( ! reachedEndOfFile ) { size_t actuallyReadBytes = fread( auxBuffer, /* data item size: */ 1, /* max data items count: */ auxBufferSize, procSelfCmdLineFile ); if ( actuallyReadBytes < auxBufferSize ) { if ( ferror( procSelfCmdLineFile ) != 0 ) { streamPrintf( txtOutStream, "<Failed to read from %s>", g_procSelfCmdLineFileName ); return; } reachedEndOfFile = ( feof( procSelfCmdLineFile ) != 0 ); if ( ! reachedEndOfFile ) { streamPrintf( txtOutStream, "<fread did not read full buffer from %s but feof() returned false>", g_procSelfCmdLineFileName ); return; } } ELASTIC_APM_FOR_EACH_INDEX( i, actuallyReadBytes ) { if ( auxBuffer[ i ] == '\0' ) { isRightAfterSeparator = true; continue; } if ( isRightAfterSeparator ) { streamCharUpToMaxLength( txtOutStream, ' ', maxLength, &numberOfCharsProcessed ); isRightAfterSeparator = false; } streamCharUpToMaxLength( txtOutStream, auxBuffer[ i ], maxLength, &numberOfCharsProcessed ); } } if ( numberOfCharsProcessed > maxLength ) { streamPrintf( txtOutStream, " <skipped remaining %" PRIu64 " characters>", (UInt64)( numberOfCharsProcessed - maxLength ) ); } } #endif String streamCurrentProcessCommandLine( TextOutputStream* txtOutStream, size_t maxLength ) { if ( maxLength == 0 ) { return ""; } ELASTIC_APM_ASSERT_VALID_PTR( txtOutStream ); #ifdef PHP_WIN32 return ELASTIC_APM_STRING_LITERAL_TO_VIEW( "<Not implemented on Windows>" ); #else TextOutputStreamState txtOutStreamStateOnEntryStart; FILE* procSelfCmdLineFile = NULL; if ( ! textOutputStreamStartEntry( txtOutStream, &txtOutStreamStateOnEntryStart ) ) { return ELASTIC_APM_TEXT_OUTPUT_STREAM_NOT_ENOUGH_SPACE_MARKER; } txtOutStream->autoTermZero = false; int openFileErrNo = openFile( g_procSelfCmdLineFileName, "rb", /* out */ &procSelfCmdLineFile ); if ( openFileErrNo != 0 ) { char auxTxtOutStreamBuf[ ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE ]; TextOutputStream auxTxtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( auxTxtOutStreamBuf ); streamPrintf( txtOutStream, "<Failed to open %s, errno: %s>", g_procSelfCmdLineFileName, streamErrNo( openFileErrNo, &auxTxtOutStream ) ); goto finally; } streamCurrentProcessCommandLineImpl( txtOutStream, maxLength, procSelfCmdLineFile ); finally: if ( procSelfCmdLineFile != NULL ) { fclose( procSelfCmdLineFile ); } return textOutputStreamEndEntry( &txtOutStreamStateOnEntryStart, txtOutStream ); #endif } #ifdef ELASTIC_APM_PLATFORM_HAS_LIBUNWIND void iterateOverCStackTraceLibUnwind( size_t numberOfFramesToSkip, IterateOverCStackTraceCallback callback, IterateOverCStackTraceLogErrorCallback logErrorCallback, void* callbackCtx ) { char txtOutStreamBuf[ ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE ]; TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf ); # define ELASTIC_APM_LIBUNWIND_CALL_RETURN_ON_ERROR( expr ) \ do { \ int temp_libUnwindRetVal = (expr); \ if ( temp_libUnwindRetVal < 0 ) \ { \ textOutputStreamRewind( &txtOutStream ); \ logErrorCallback( streamPrintf( &txtOutStream, "%s call failed (return value: %d)", ELASTIC_APM_PP_STRINGIZE( expr ), temp_libUnwindRetVal ), callbackCtx ); \ return; \ } \ } while ( 0 ) unw_cursor_t unwindCursor; unw_context_t unwindContext; enum { funcNameBufferSize = 100 }; char funcNameBuffer[ funcNameBufferSize ]; unw_word_t offsetInsideFunc; size_t frameIndex = 0; ELASTIC_APM_LIBUNWIND_CALL_RETURN_ON_ERROR( unw_getcontext( &unwindContext ) ); ELASTIC_APM_LIBUNWIND_CALL_RETURN_ON_ERROR( unw_init_local( &unwindCursor, &unwindContext ) ); for (;; ++frameIndex) { // +1 is for this function frame if ( frameIndex >= numberOfFramesToSkip + 1 ) { textOutputStreamRewind( &txtOutStream ); unw_proc_info_t pi; if (unw_get_proc_info(&unwindCursor, &pi) == 0) { *funcNameBuffer = 0; offsetInsideFunc = 0; int getProcNameRetVal = unw_get_proc_name( &unwindCursor, funcNameBuffer, funcNameBufferSize, &offsetInsideFunc ); if (getProcNameRetVal != UNW_ESUCCESS && getProcNameRetVal != -UNW_ENOMEM) { strcpy(funcNameBuffer, "???"); unw_word_t pc; unw_get_reg(&unwindCursor, UNW_REG_IP, &pc); offsetInsideFunc = pc - pi.start_ip; } Dl_info dlInfo; if (dladdr((const void *)pi.gp, &dlInfo)) { callback( streamPrintf( &txtOutStream, "%s(%s+0x%lx) ModuleBase: %p FuncStart: 0x%lx FuncEnd: 0x%lx FuncStartRelative: 0x%lx FuncOffsetRelative: 0x%lx\n\t'addr2line -afCp -e \"%s\" %lx'\n", dlInfo.dli_fname ? dlInfo.dli_fname : "???", dlInfo.dli_sname ? dlInfo.dli_sname : funcNameBuffer, offsetInsideFunc, dlInfo.dli_fbase, pi.start_ip, pi.end_ip, pi.start_ip - reinterpret_cast<unw_word_t>(dlInfo.dli_fbase), pi.start_ip - reinterpret_cast<unw_word_t>(dlInfo.dli_fbase) + offsetInsideFunc, dlInfo.dli_fname ? dlInfo.dli_fname : "???", pi.start_ip - reinterpret_cast<unw_word_t>(dlInfo.dli_fbase) + offsetInsideFunc ), callbackCtx ); } else { logErrorCallback( streamPrintf( &txtOutStream, "dladdr failed on frame %zu", frameIndex), callbackCtx ); } } else { logErrorCallback( streamPrintf( &txtOutStream, "unw_get_proc_info failed on frame %zu", frameIndex), callbackCtx ); } } int unwindStepRetVal = 0; ELASTIC_APM_LIBUNWIND_CALL_RETURN_ON_ERROR( unwindStepRetVal = unw_step( &unwindCursor ) ); if ( unwindStepRetVal == 0 ) { break; } } # undef ELASTIC_APM_LIBUNWIND_CALL_RETURN_ON_ERROR } #endif // #ifdef ELASTIC_APM_PLATFORM_HAS_LIBUNWIND #ifdef ELASTIC_APM_PLATFORM_HAS_BACKTRACE void iterateOverCStackTraceBacktrace( size_t numberOfFramesToSkip, IterateOverCStackTraceCallback callback, IterateOverCStackTraceLogErrorCallback logErrorCallback, void* callbackCtx ) { char txtOutStreamBuf[ ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE ]; TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf ); enum { maxStackTraceAddressesCount = 100 }; void* stackTraceAddresses[ maxStackTraceAddressesCount ]; int stackTraceAddressesCount = backtrace( stackTraceAddresses, maxStackTraceAddressesCount ); if ( stackTraceAddressesCount == 0 ) { textOutputStreamRewind( &txtOutStream ); logErrorCallback( streamPrintf( &txtOutStream, "backtrace returned 0 as stackTraceAddressesCount (i.e., failed to get any address on the stack)" ), callbackCtx ); return; } char** stackTraceAddressesAsSymbols = backtrace_symbols( stackTraceAddresses, stackTraceAddressesCount ); if ( stackTraceAddressesAsSymbols == NULL ) { textOutputStreamRewind( &txtOutStream ); logErrorCallback( streamPrintf( &txtOutStream, "backtrace_symbols returned NULL (i.e., failed to resolve addresses to symbols). Returning raw addresses as hex strings" ), callbackCtx ); ELASTIC_APM_FOR_EACH_INDEX( frameIndex, stackTraceAddressesCount ) { // +1 is for this function frame if ( frameIndex < numberOfFramesToSkip + 1 ) { continue; } textOutputStreamRewind( &txtOutStream ); callback( streamPrintf( &txtOutStream, "%p", stackTraceAddresses[ frameIndex ] ), callbackCtx ); } return; } ELASTIC_APM_FOR_EACH_INDEX( frameIndex, stackTraceAddressesCount ) { // +1 is for this function frame if ( frameIndex < numberOfFramesToSkip + 1 ) { continue; } textOutputStreamRewind( &txtOutStream ); callback( streamPrintf( &txtOutStream, "%s", stackTraceAddressesAsSymbols[ frameIndex ] ), callbackCtx ); } free( stackTraceAddressesAsSymbols ); stackTraceAddressesAsSymbols = NULL; } #endif // #ifdef ELASTIC_APM_PLATFORM_HAS_BACKTRACE #ifdef ELASTIC_APM_CAN_CAPTURE_C_STACK_TRACE void iterateOverCStackTrace( size_t numberOfFramesToSkip, IterateOverCStackTraceCallback callback, IterateOverCStackTraceLogErrorCallback logErrorCallback, void* callbackCtx ) { # if defined( ELASTIC_APM_PLATFORM_HAS_LIBUNWIND ) iterateOverCStackTraceLibUnwind( numberOfFramesToSkip + 1, callback, logErrorCallback, callbackCtx ); # elif defined( ELASTIC_APM_PLATFORM_HAS_BACKTRACE ) iterateOverCStackTraceBacktrace( numberOfFramesToSkip + 1, callback, logErrorCallback, callbackCtx ); # endif } #endif // #ifdef ELASTIC_APM_CAN_CAPTURE_C_STACK_TRACE #ifndef PHP_WIN32 static String osSignalIdToName( int signalId ) { #define ELASTIC_APM_OS_SIGNAL_ID_TO_NAME_SWITCH_CASE( sigIdForSwitchCase ) case sigIdForSwitchCase: return #sigIdForSwitchCase; switch ( signalId ) { ELASTIC_APM_OS_SIGNAL_ID_TO_NAME_SWITCH_CASE( SIGQUIT ) ELASTIC_APM_OS_SIGNAL_ID_TO_NAME_SWITCH_CASE( SIGABRT ) ELASTIC_APM_OS_SIGNAL_ID_TO_NAME_SWITCH_CASE( SIGBUS ) ELASTIC_APM_OS_SIGNAL_ID_TO_NAME_SWITCH_CASE( SIGKILL ) ELASTIC_APM_OS_SIGNAL_ID_TO_NAME_SWITCH_CASE( SIGSEGV ) ELASTIC_APM_OS_SIGNAL_ID_TO_NAME_SWITCH_CASE( SIGTERM ) ELASTIC_APM_OS_SIGNAL_ID_TO_NAME_SWITCH_CASE( SIGSTOP ) default: return "UNKNOWN OS SIGNAL ID"; } #undef ELASTIC_APM_OS_SIGNAL_ID_TO_NAME_SWITCH_CASE } #define ELASTIC_APM_LOG_FROM_CRASH_SIGNAL_HANDLER( fmt, ... ) ELASTIC_APM_SIGNAL_SAFE_LOG_CRITICAL( fmt, ##__VA_ARGS__ ) #ifdef ELASTIC_APM_CAN_CAPTURE_C_STACK_TRACE void handleOsSignalLinux_writeStackTraceFrameToSyslog( String frameDesc, void* ctx ) { ELASTIC_APM_UNUSED( ctx ); ELASTIC_APM_LOG_FROM_CRASH_SIGNAL_HANDLER( " Call stack frame: %s", frameDesc == NULL ? "<N/A>" : frameDesc ); } void handleOsSignalLinux_writeStackTraceToSyslog_logError( String errorDesc, void* ctx ) { ELASTIC_APM_UNUSED( ctx ); ELASTIC_APM_LOG_FROM_CRASH_SIGNAL_HANDLER( "%s", errorDesc ); } #endif // #ifdef ELASTIC_APM_CAN_CAPTURE_C_STACK_TRACE void handleOsSignalLinux_writeStackTraceToSyslog() { # ifdef ELASTIC_APM_CAN_CAPTURE_C_STACK_TRACE ELASTIC_APM_LOG_FROM_CRASH_SIGNAL_HANDLER( "Call stack:" ); iterateOverCStackTrace( /* numberOfFramesToSkip */ 0, &handleOsSignalLinux_writeStackTraceFrameToSyslog, &handleOsSignalLinux_writeStackTraceToSyslog_logError, /* callbackCtx */ NULL ); # else // #ifdef ELASTIC_APM_CAN_CAPTURE_C_STACK_TRACE ELASTIC_APM_LOG_FROM_CRASH_SIGNAL_HANDLER( "C call stack capture is not supported by the platform"); # endif // #ifdef ELASTIC_APM_CAN_CAPTURE_C_STACK_TRACE } typedef void (* OsSignalHandler )( int ); bool g_isOldSignalHandlerSet = false; OsSignalHandler g_oldSignalHandler = NULL; void handleOsSignalLinux( int signalId ) { #ifdef __ELASTIC_LIBC_MUSL__ #define LIBC_IMPL "musl" #else #define LIBC_IMPL "" #endif ELASTIC_APM_LOG_FROM_CRASH_SIGNAL_HANDLER( "Received signal %d (%s). Agent version: " PHP_ELASTIC_APM_VERSION " " LIBC_IMPL, signalId, osSignalIdToName( signalId ) ); handleOsSignalLinux_writeStackTraceToSyslog(); /* Call the default signal handler to have core dump generated... */ if ( g_isOldSignalHandlerSet ) { signal( signalId, g_oldSignalHandler ); g_isOldSignalHandlerSet = false; g_oldSignalHandler = NULL; } else { signal( signalId, SIG_DFL ); } raise( signalId ); } #endif // #ifndef PHP_WIN32 void registerOsSignalHandler() { #ifndef PHP_WIN32 OsSignalHandler signal_retVal = signal( SIGSEGV, handleOsSignalLinux ); if ( signal_retVal == SIG_ERR ) { int signal_errno = errno; char txtOutStreamBuf[ ELASTIC_APM_TEXT_OUTPUT_STREAM_ON_STACK_BUFFER_SIZE ]; TextOutputStream txtOutStream = ELASTIC_APM_TEXT_OUTPUT_STREAM_FROM_STATIC_BUFFER( txtOutStreamBuf ); ELASTIC_APM_LOG_FROM_CRASH_SIGNAL_HANDLER( "Call to signal() to register handler failed - errno: %s", streamErrNo( signal_errno, &txtOutStream ) ); } else { g_isOldSignalHandlerSet = true; g_oldSignalHandler = signal_retVal; ELASTIC_APM_SIGNAL_SAFE_LOG_DEBUG( "Successfully registered signal handler" ); } #endif } void unregisterOsSignalHandler() { #ifndef PHP_WIN32 if ( g_isOldSignalHandlerSet ) { signal( SIGSEGV, g_oldSignalHandler ); g_isOldSignalHandlerSet = false; g_oldSignalHandler = NULL; ELASTIC_APM_SIGNAL_SAFE_LOG_DEBUG( "Successfully unregistered signal handler" ); } #endif } void atExitLogging() { ELASTIC_APM_LOG_DIRECT_DEBUG( "Callback registered with atexit() has been called" ); } void registerAtExitLogging() { #ifndef PHP_WIN32 int atexit_retVal = atexit( &atExitLogging ); // atexit returns 0 if successful, or a nonzero value if an error occurs if ( atexit_retVal != 0 ) { ELASTIC_APM_LOG_DIRECT_DEBUG( "Call to atexit() to register process on-exit logging func failed" ); } else { ELASTIC_APM_LOG_DIRECT_DEBUG( "Registered callback with atexit()" ); } #endif } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" int openFile( String fileName, String mode, /* out */ FILE** pFile ) { ELASTIC_APM_ASSERT_VALID_PTR( fileName ); ELASTIC_APM_ASSERT_VALID_PTR( mode ); ELASTIC_APM_ASSERT_VALID_OUT_PTR_TO_PTR( pFile ); #ifdef PHP_WIN32 return (int)fopen_s( /* out */ pFile, fileName, mode ); #else // #ifdef PHP_WIN32 FILE* file = fopen( fileName, mode ); if ( file == NULL ) { return (int)errno; } *pFile = file; return 0; #endif // #ifdef PHP_WIN32 } #pragma clang diagnostic pop